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, поэтому вы должны иметь возможность импортировать его и запускать как есть.