Перейти к основному содержимому

Введение в GeoTools

· 7 мин. чтения

1. Обзор

В этой статье мы рассмотрим основы библиотеки Java с открытым исходным кодом GeoTools — для работы с геопространственными данными . Эта библиотека предоставляет совместимые методы для реализации географических информационных систем (ГИС), а также реализует и поддерживает многие стандарты Open Geospatial Consortium (OGC).

Поскольку OGC разрабатывает новые стандарты, они реализуются с помощью GeoTools, что делает его весьма удобным для работы с геопространственными данными.

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

Нам нужно добавить зависимости GeoTools в наш файл pom.xml . Поскольку эти зависимости не размещены в Maven Central, нам также необходимо объявить их репозитории, чтобы Maven мог их загрузить:

<repositories>
<repository>
<id>osgeo</id>
<name>Open Source Geospatial Foundation Repository</name>
<url>http://download.osgeo.org/webdav/geotools/</url>
</repository>
<repository>
<id>opengeo</id>
<name>OpenGeo Maven Repository</name>
<url>http://repo.opengeo.org</url>
</repository>
</repositories>

После этого мы можем добавить наши зависимости:

<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>15.2</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</artifactId>
<version>15.2</version>
</dependency>

3. ГИС и шейп-файлы

Чтобы иметь какое-либо практическое применение библиотеки GeoTools, нам нужно знать несколько вещей о географических информационных системах и шейп -файлах .

3.1. ГИС

Если мы хотим работать с географическими данными, нам понадобится географическая информационная система (ГИС). Эта система может использоваться для представления, сбора, хранения, обработки, анализа или управления географическими данными .

Некоторая часть географических данных является пространственной — она относится к конкретным местам на Земле. Пространственные данные обычно сопровождаются атрибутивными данными. Атрибутными данными может быть любая дополнительная информация о каждом из пространственных объектов.

Примером географических данных могут быть города. Фактическое расположение городов - это пространственные данные. Дополнительные данные, такие как название города и численность населения, будут составлять данные атрибута.

3.2. Шейп-файлы

Доступны различные форматы для работы с геопространственными данными. Растр и вектор — два основных типа данных.

В этой статье мы увидим, как работать с векторным типом данных . Этот тип данных может быть представлен точками, линиями или полигонами.

Для хранения векторных данных в файле мы будем использовать шейп -файл . Этот формат файла используется при работе с геопространственным векторным типом данных. Кроме того, он совместим с широким спектром программного обеспечения ГИС.

Мы можем использовать GeoTools для добавления таких объектов, как города, школы и ориентиры, в шейп -файлы .

4. Создание объектов

В документации GeoTools указано, что объект — это все, что можно нарисовать на карте, например, город или какой-либо ориентир. И, как мы уже упоминали, после создания функции могут быть сохранены в файлы, называемые шейп- файлами .

4.1. Хранение геопространственных данных

Перед созданием объекта нам необходимо знать его геопространственные данные или координаты долготы и широты его местоположения на Земле. Что касается данных атрибутов, нам нужно знать имя объекта, который мы хотим создать.

Эту информацию можно найти в сети. Некоторые сайты, такие как simplemaps.com или maxmind.com, предлагают бесплатные базы данных с геопространственными данными.

Когда мы знаем долготу и широту города, мы можем легко сохранить их в каком-либо объекте. Мы можем использовать объект Map , который будет содержать название города и список его координат.

Давайте создадим вспомогательный метод, чтобы упростить хранение данных внутри нашего объекта Map :

private static void addToLocationMap(
String name,
double lat,
double lng,
Map<String, List<Double>> locations) {
List<Double> coordinates = new ArrayList<>();

coordinates.add(lat);
coordinates.add(lng);
locations.put(name, coordinates);
}

Теперь давайте заполним наш объект Map :

Map<String, List<Double>> 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();

Оба типа хранят одну и ту же информацию. Расположение города сохраняется как точка , а название города сохраняется как строка .

Вы, наверное, заметили, что имена переменных типа TYPE и CITY пишутся с большой буквы, как и константы. Переменные типа должны рассматриваться как окончательные переменные и не должны изменяться после их создания , поэтому этот способ именования может использоваться именно для этого.

4.3. Создание функций и наборы функций

Как только мы определили тип объекта и у нас есть объект с данными, необходимыми для создания объектов, мы можем начать создавать их с помощью их построителя.

Давайте создадим экземпляр SimpleFeatureBuilder , предоставляющий наш тип функции:

SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(CITY);

Нам также понадобится коллекция для хранения всех созданных объектов объектов:

DefaultFeatureCollection collection = new DefaultFeatureCollection();

Поскольку мы объявили в нашем типе объекта, что он содержит точку для местоположения, нам нужно создать точки для наших городов на основе их координат . Мы можем сделать это с помощью JTSGeometryFactoryFinder GeoTools :

GeometryFactory geometryFactory
= JTSFactoryFinder.getGeometryFactory(null);

Обратите внимание, что мы также можем использовать другие классы Geometry , такие как Line и Polygon .

Мы можем создать функцию , которая поможет нам поместить функции в коллекцию:

private static Function<Map.Entry<String, List<Double>>, 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);
};
}

Когда у нас есть построитель и коллекция, с помощью ранее созданной функции мы можем создавать функции и сохранять их в нашей коллекции :

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

Коллекция теперь содержит все объекты, созданные на основе нашего объекта « Карта » , содержащего геопространственные данные.

5. Создание хранилища данных

GeoTools содержит DataStore API , который используется для представления источника геопространственных данных. Этот источник может быть файлом, базой данных или какой-либо службой, которая возвращает данные. Мы можем использовать DataStoreFactory для создания нашего хранилища данных , которое будет содержать наши функции.

Давайте установим файл, который будет содержать функции:

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

Теперь давайте установим параметры, которые мы собираемся использовать, чтобы сообщить DataStoreFactory , какой файл использовать, и указать, что нам нужно сохранить пространственный индекс при создании нашего DataStore :

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

Давайте создадим DataStoreFactory , используя только что созданные параметры, и используем эту фабрику для создания DataStore :

ShapefileDataStoreFactory dataStoreFactory
= new ShapefileDataStoreFactory();

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

6. Запись в шейп-файл

Последний шаг, который нам нужно сделать, это записать наши данные в шейп -файл . Чтобы сделать это безопасно, мы будем использовать интерфейс Transaction , который является частью API GeoTools .

Этот интерфейс дает нам возможность легко зафиксировать изменения в файле . Он также предоставляет способ выполнить откат неудачных изменений, если при записи в файл возникает какая-то проблема:

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();
}
}

SimpleFeatureSource используется для чтения функций, а SimpleFeatureStore для чтения/записи. В документации GeoTools указано , что использование метода instanceof для проверки возможности записи в файл является правильным способом сделать это. ``

Позже этот шейп-файл можно будет открыть с помощью любой программы просмотра ГИС, поддерживающей шейп-файлы .

7. Заключение

В этой статье мы увидели, как мы можем использовать библиотеку GeoTools для выполнения очень интересной геопространственной работы.

Хотя пример был простым, его можно расширить и использовать для создания богатых шейп -файлов для различных целей.

Мы должны иметь в виду, что GeoTools — это динамичная библиотека, и эта статья служит лишь базовым введением в библиотеку. Кроме того, GeoTools не ограничивается созданием только векторных типов данных — его также можно использовать для создания или работы с растровыми типами данных.

Полный пример кода, использованный в этой статье, вы можете найти в нашем проекте на GitHub . Это проект Maven, поэтому вы должны иметь возможность импортировать его и запускать как есть.