Въведение в GeoTools

1. Общ преглед

В тази статия ще разгледаме основите на Java библиотеката с отворен код GeoTools - за работа с геопространствени данни . Тази библиотека предоставя съвместими методи за внедряване на Географски информационни системи (ГИС) и внедрява и поддържа много стандарти на Open Geospatial Consortium (OGC).

Тъй като OGC разработва нови стандарти, те се прилагат от GeoTools, което го прави доста удобен за геопространствена работа.

2. Зависимости

Ще трябва да добавим зависимостите GeoTools към нашия файл pom.xml . Тъй като тези зависимости не се хостват в Maven Central, ние също трябва да декларираме техните хранилища, така че Maven да може да ги изтегли:

  osgeo Open Source Geospatial Foundation Repository //download.osgeo.org/webdav/geotools/   opengeo OpenGeo Maven Repository //repo.opengeo.org  

След това можем да добавим нашите зависимости:

 org.geotools gt-shapefile 15.2   org.geotools gt-epsg-hsql 15.2 

3. ГИС и Shapefiles

За да използваме на практика библиотеката GeoTools, ще трябва да знаем няколко неща за географските информационни системи и шейпфайловете .

3.1. ГИС

Ако искаме да работим с географски данни, ще ни трябва географска информационна система (ГИС). Тази система може да се използва за представяне, улавяне, съхраняване, манипулиране, анализ или управление на географски данни .

Част от географските данни са пространствени - в тях се посочват конкретни местоположения на земята. Пространствените данни обикновено се придружават от атрибутни данни. Данните за атрибутите могат да бъдат всякаква допълнителна информация за всяка от пространствените характеристики.

Пример за географски данни биха били градовете. Действителното местоположение на градовете са пространствените данни. Допълнителни данни като името на града и населението ще съставят данните за атрибутите.

3.2. Shapefiles

Предлагат се различни формати за работа с геопространствени данни. Растерът и векторът са двата основни типа данни.

В тази статия, ние ще видим как да работа с векторни данни Typ д . Този тип данни може да бъде представен като точки, линии или полигони.

За да съхраняваме векторни данни във файл, ще използваме шейпфайл . Този файлов формат се използва при работа с геопространствен вектор тип данни. Също така е съвместим с широк спектър от ГИС софтуер.

Можем да използваме GeoTools, за да добавяме функции като градове, училища и ориентири към шейпфайловете .

4. Създаване на функции

Документацията на GeoTools посочва, че дадена функция е всичко, което може да бъде нарисувано на карта, като град или някаква забележителност. И както споменахме, веднъж създадени, функциите след това могат да бъдат запазени във файлове, наречени shapefiles .

4.1. Съхраняване на геопространствени данни

Преди да създадем обект, трябва да знаем геопространствените му данни или координатите на географската дължина и ширина на местоположението му на земята. Що се отнася до данните за атрибутите, трябва да знаем името на функцията, която искаме да създадем.

Тази информация може да бъде намерена в мрежата. Някои сайтове като simplemaps.com или maxmind.com предлагат безплатни бази данни с геопространствени данни.

Когато знаем дължина и ширина на даден град, можем лесно да ги съхраняваме в някакъв обект. Можем да използваме обект на Карта, който ще съдържа името на града и списък с неговите координати.

Нека създадем помощен метод за улесняване на съхраняването на данни в нашия обект Map :

private static void addToLocationMap( String name, double lat, double lng, Map
    
      locations) { List coordinates = new ArrayList(); coordinates.add(lat); coordinates.add(lng); locations.put(name, coordinates); }
    

Сега нека попълним нашия обект Map :

Map
    
      locations = new HashMap(); addToLocationMap("Bangkok", 13.752222, 100.493889, locations); addToLocationMap("New York", 53.083333, -0.15, locations); addToLocationMap("Cape Town", -33.925278, 18.423889, locations); addToLocationMap("Sydney", -33.859972, 151.211111, locations); addToLocationMap("Ottawa", 45.420833, -75.69, locations); addToLocationMap("Cairo", 30.07708, 31.285909, locations);
    

Ако изтеглим някаква CSV база данни, която съдържа тези данни, можем лесно да създадем четец за извличане на данните, вместо да ги съхраняваме в обект като тук.

4.2. Определяне на типове функции

И така, сега имаме карта на градовете. За да можем да създадем функции с тези данни, първо трябва да определим техния тип. GeoTools предлага два начина за определяне на типовете функции.

Един от начините е да се използва createType метода на DataUtilites класа:

SimpleFeatureType TYPE = DataUtilities.createType( "Location", "location:Point:srid=4326," + "name:String");

Друг начин е да се използва SimpleFeatureTypeBuilder , който осигурява по-голяма гъвкавост . Например можем да зададем координатната референтна система за типа и да зададем максимална дължина за полето с име:

SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.setName("Location"); builder.setCRS(DefaultGeographicCRS.WGS84); builder .add("Location", Point.class); .length(15) .add("Name", String.class); SimpleFeatureType CITY = builder.buildFeatureType();

И двата типа съхраняват една и съща информация. Местоположението на града се съхранява като точка , а името на града - като низ .

You probably noticed that the type variables TYPE and CITY are named with all capital letters, like constants. Type variables should be treated as final variables and should not be changed after they are created, so this way of naming can be used to indicate just that.

4.3. Feature Creation and Feature Collections

Once we have the feature type defined and we have an object that has the data needed to create features, we can start creating them with their builder.

Let's instantiate a SimpleFeatureBuilder providing our feature type:

SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(CITY);

We'll also need a collection to store all the created feature objects:

DefaultFeatureCollection collection = new DefaultFeatureCollection();

Since we declared in our feature type to hold a Point for the location, we'll need to create points for our cities based on their coordinates. We can do this with the GeoTools's JTSGeometryFactoryFinder:

GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);

Note that we can also use other Geometry classes like Line and Polygon.

We can create a function that will help us put features in the collection:

private static Function
    
     , SimpleFeature> toFeature(SimpleFeatureType CITY, GeometryFactory geometryFactory) { return location -> { Point point = geometryFactory.createPoint( new Coordinate(location.getValue() .get(0), location.getValue().get(1))); SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(CITY); featureBuilder.add(point); featureBuilder.add(location.getKey()); return featureBuilder.buildFeature(null); }; }
    

Once we have the builder and the collection, by using the previously created function, we can create features and store them in our collection:

locations.entrySet().stream() .map(toFeature(CITY, geometryFactory)) .forEach(collection::add);

The collection now contains all the features created based on our Map object that held the geospatial data.

5. Creating a DataStore

GeoTools contains a DataStore API that is used to represent a source of geospatial data. This source can be a file, a database, or some service that returns data. We can use a DataStoreFactory to create our DataStore, which will contain our features.

Let's set the file that will contain the features:

File shapeFile = new File( new File(".").getAbsolutePath() + "shapefile.shp");

Now, let's set the parameters that we are going to use to tell the DataStoreFactory which file to use and indicate that we need to store a spatial index when we create our DataStore:

Map params = new HashMap(); params.put("url", shapeFile.toURI().toURL()); params.put("create spatial index", Boolean.TRUE);

Let's create the DataStoreFactory using the parameters we just created, and use that factory to create the DataStore:

ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory(); ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params); dataStore.createSchema(CITY);

6. Writing to a Shapefile

The last step that we need to do is to write our data to a shapefile. To do this safely, we are going to use the Transaction interface that is a part of the GeoTools API.

This interface gives us the possibility to easily commit our the changes to the file. It also provides a way to perform a rollback of the unsuccessful changes if some problem occurs while writing to the file:

Transaction transaction = new DefaultTransaction("create"); String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); if (featureSource instanceof SimpleFeatureStore) { SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; featureStore.setTransaction(transaction); try { featureStore.addFeatures(collection); transaction.commit(); } catch (Exception problem) { transaction.rollback(); } finally { transaction.close(); } }

The SimpleFeatureSource is used to read features, and the SimpleFeatureStore is used for read/write access. It is specified in the GeoTools documentation that using the instanceof method for checking if we can write to the file is the right way to do so.

This shapefile can later be opened with any GIS viewer that has shapefile support.

7. Conclusion

In this article, we saw how we can make use of the GeoTools library to do some very interesting geo-spatial work.

Although the example was simple, it can be extended and used for creating rich shapefiles for various purposes.

Трябва да имаме предвид, че GeoTools е оживена библиотека и тази статия просто служи като основно въведение в библиотеката. Също така GeoTools не се ограничава само до създаването на типове векторни данни - той също може да се използва за създаване или работа с типове растерни данни.

Можете да намерите пълния примерен код, използван в тази статия в нашия проект GitHub. Това е проект на Maven, така че трябва да можете да го импортирате и да го стартирате както е.