Contents

GeoTools 简介

1. 概述

在本文中,我们将介绍**GeoTools 开源 Java 库的基础知识——用于处理地理空间数据**。该库提供了用于实施地理信息系统 (GIS) 的兼容方法,并实施和支持许多开放地理空间联盟 (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. GIS 和 Shapefile

要实际使用 GeoTools 库,我们需要了解一些有关地理信息系统和shapefile的知识。

3.1. 地理信息系统

如果我们想处理地理数据,我们需要一个地理信息系统 (GIS)。该系统可用于呈现、捕获、存储、操作、分析或管理地理数据

地理数据的某些部分是空间数据——它引用了地球上的具体位置。空间数据通常伴随着属性数据。属性数据可以是关于每个空间特征的任何附加信息。

地理数据的一个例子是城市。城市的实际位置是空间数据。城市名称和人口等附加数据将构成属性数据。

3.2. 形状文件

不同的格式可用于处理地理空间数据。栅格和矢量是两种主要的数据类型。

在本文中,我们将了解如何使用向量数据类型e。此数据类型可以表示为点、线或多边形。

要将矢量数据存储在文件中,我们将使用shapefile。使用地理空间矢量数据类型时使用此文件格式。此外,它与各种 GIS 软件兼容。

我们可以使用 GeoTools 将城市、学校和地标等特征添加到shapefile中。

4. 创建功能

GeoTools文档指定要素是可以在地图上绘制的任何东西,例如城市或某些地标*。而且,正如我们所提到的,一旦创建,特征就可以保存到名为shapefiles*的文件中。

4.1. 保存地理空间数据

**在创建一个要素之前,我们需要知道它的地理空间数据或它在地球上的位置的经纬度坐标。**至于属性数据,我们需要知道我们要创建的特征的名称。

此信息可在网络上找到。一些网站,如simplemaps.commaxmind.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 提供了两种定义要素类型的方法。

一种方法是使用DataUtilites类的createType方法:

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

**两种类型都存储相同的信息。**城市的位置存储为Point,城市的名称存储为String

您可能注意到类型变量TYPECITY都以大写字母命名,就像常量一样。类型变量应被视为final变量,不应在创建后更改,因此可以使用这种命名方式来表明这一点。

4.3. 特征创建和特征集合

一旦我们定义了特征类型并且我们有了一个包含创建特征所需数据的对象,我们就可以开始使用它们的构建器创建它们。

让我们实例化一个SimpleFeatureBuilder提供我们的特征类型:

SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(CITY);

我们还需要一个集合来存储所有创建的特征对象:

DefaultFeatureCollection collection = new DefaultFeatureCollection();

由于我们在特征类型中声明为该位置保存一个Point,因此我们需要根据城市的坐标为我们的城市创建点。我们可以使用GeoTools 的 JTSGeometryFactoryFinder来做到这一点:

GeometryFactory geometryFactory
  = JTSFactoryFinder.getGeometryFactory(null);

请注意,我们还可以使用其他几何类,如LinePolygon

我们可以创建一个Function来帮助我们将特征放入集合中:

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

一旦我们有了构建器和集合,通过使用之前创建的Function,我们可以创建特征并将它们存储在我们的集合中

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

该集合现在包含基于保存地理空间数据的Map对象创建的所有要素。

5. 创建数据存储

GeoTools包含一个用于表示地理空间数据源的DataStore API 。此源可以是文件、数据库或某些返回数据的服务。我们可以使用DataStoreFactory来创建我们的DataStore,它将包含我们的功能。

让我们设置将包含功能的文件:

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. 写入 Shapefile

我们需要做的最后一步是将数据写入shapefile。为了安全地做到这一点,我们将使用作为GeoTools API一部分的Transaction接口。

这个界面使我们能够轻松地*commit 我们对文件的更改。如果在写入文件时出现问题*,它还提供了*一种*rollback 不成功更改的方法:

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方法检查我们是否可以写入文件是正确的方法。

以后可以使用任何支持 shapefile 的 GIS 查看器打开此shapefile