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.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 定义特征类型
现在,我们有了一个城市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不仅限于创建矢量数据类型-它还可用于创建或处理栅格数据类型。
Post Directory
