GeoTools简介

2025/04/01

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>28.1</version>
</dependency>
<dependency>
    <groupId>org.geotools</groupId>
    <artifactId>gt-epsg-hsql</artifactId>
    <version>28.1</version>
</dependency>

3. GIS和Shapefile

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

3.1 地理信息系统

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

地理数据的一部分是空间-它指的是地球上的具体位置。空间数据通常伴随着属性数据,属性数据可以是有关每个空间特征的任何附加信息。

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

3.2 Shapefiles

处理地理空间数据有多种格式,栅格和矢量是两种主要数据类型。

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

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

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

4. 创建特征

GeoTools文档规定,特征是可以在地图上绘制的任何事物,例如城市或地标。并且,正如我们所提到的,一旦创建,特征就可以保存到名为shapefile的文件中。

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 定义特征类型

现在,我们有了一个城市Map。为了能够使用这些数据创建特征,我们需要先定义它们的类型。GeoTools提供了两种定义特征类型的方式。

一种方法是使用DataUtilities类的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。

你可能注意到,类型变量TYPE和CITY的命名全部采用大写字母,就像常量一样。类型变量应被视为最终变量,创建后不应更改,因此这种命名方式可用于表明这一点。

4.3 特征创建和特征收集

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

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

SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(CITY);

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

DefaultFeatureCollection collection = new DefaultFeatureCollection();

由于我们在特征类型中声明要保存位置的Point,因此我们需要根据城市的坐标为其创建点。我们可以使用GeoTools的JTSGeometryFactoryFinder来执行此操作:

GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);

请注意,我们还可以使用其他几何类,如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);

该集合现在包含基于保存地理空间数据的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接口。

这个接口让我们能够轻松地将更改提交到文件,如果在写入文件时出现问题,它还提供了一种回滚未成功更改的方法:

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。

7. 总结

在本文中,我们了解了如何利用GeoTools库来完成一些非常有趣的地理空间工作。

虽然这个例子很简单,但它可以扩展并用于创建用于各种目的的丰富的shapefiles。

我们应该记住,GeoTools是一个充满活力的库,本文只是对该库的基本介绍。此外,GeoTools不仅限于创建矢量数据类型-它还可用于创建或处理栅格数据类型。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章