前言
在之前的博客中,我们讲过在GDAL中如何读取空间数据的属性和数据信息,也简单的讲过如何在GeoTools中读取Shapefile文件的属性信息和数据信息。对于空间矢量数据库,就像我们传统的二维数据库的表字段和表数据的关系,在研究表数据的存储和检索时,通常会配套进行空间表表结构的设计。众所周知,在关系型数据库中,关于数据库物理模型的建设至关重要,因此在空间数据存储也是同样的重要。
本文主要研究使用Java开发语言基于GeoTools地理开发组件来进行空间矢量数据如Shapefile的属性信息的读取,文章首先介绍如何使用GeoTools来打开一个shp文件,在QGIS中展示原始数据的相关信息,然后讲解如何通过GeoTools获取shp文件的空间参考,接着讲解根据空间参考获取我们常见的空间参考编码和代号,其次讲解如何获取空间数据的属性字段。通过本文,您可以了解和掌握如何利用GeoTools来进行Shapefile的属性信息的读取。
一、空间数据说明
最近开始研究POI数据,因此从互联网获取了POI数据,想实现POI数据的空间分布分析。具体使用POI数据来进行空间属性信息的读取演示。为了让大家对POI数据有一定的了解,这里对POI数据进行一个介绍。
1、空间参考
这里使用的POI数据是2020年形成长沙市的POI数据,时间过去4年,在时间上有一定的时效性不足,但是不影响我们的学习使用。我们使用Qgis软件打开上面的POI信息之后,我们可以打开数据信息看一下它的空间参考。
通过上面的截图就可以了解这份空间矢量数据的基础信息,如下表所示:
序号 | 参数 | 说明 |
1 | 编码 | UTF-8 |
2 | 几何图形 | Point (Point) |
3 | 坐标参照系(CRS) | EPSG:4490 - China Geodetic Coordinate System 2000 - 地理的 |
4 | 范围 | 111.9064691681707870,27.8943275145980074 : 114.2292192675388947,28.6342571928722691 |
5 | 单位 | 度 |
6 | 要素数量 | 1848 |
以上就是空间参考和编码等基础信息。
2、空间属性字段
在了解了POI数据的基本信息后,我们还需要了解shapefile的的属性信息,这就好比是关系型数据库的字段,需要维护物理模型。因此这里对POI数据涉及的属性字段进行介绍,为下一步的空间物理建模奠定基础。这里依然以QGIS为例,我们在QGIS中可以打开空间数据的字段表,如下所示:
为了更好的给大家展示这些POI数据,特意将数据集列表的数据进行示例数据的展示,在QGIS鼠标点击右键,打开属性表,可以看到以下的数据:
以上是对空数据的属性字段和示例数据进行简单介绍。到此本小节的内容介绍完毕,后面将介绍如何使用GeoTools来进行相关编码、空间参考坐标、属性表格的信息读取。
二、Geotools读取空间数据属性信息
在了解了以上的POI空间信息之后,下面我们调用GeoTools来进行空间数据的属性信息的读取。包括如何加载shp文件、如何获取空间数据的空间参考以及如何获取矢量数据的属性信息列表,如属性名等信息。
1、如何加载空间信息
在介绍如何使用GeoTools来进行空间信息加载时,必须要介绍必须要设置的Pom.xml来加载相关的依赖,关于Geotools的相关引用如下所示,为了节约篇幅,这里仅列出关键部分:
<dependency><groupId>org.geotools</groupId><artifactId>gt-shapefile</artifactId><version>${geotools.version}</version></dependency><dependency><groupId>org.geotools</groupId><artifactId>gt-swing</artifactId><version>${geotools.version}</version></dependency><dependency><groupId>org.geotools</groupId><artifactId>gt-main</artifactId><version>${geotools.version}</version></dependency><!-- https://mvnrepository.com/artifact/org.geotools.xsd/gt-xsd-sld --><dependency> <groupId>org.geotools.xsd</groupId> <artifactId>gt-xsd-sld</artifactId> <version>${geotools.version}</version> <!-- <version>31.3</version> <scope>test</scope>--></dependency><!-- https://mvnrepository.com/artifact/org.geotools.xsd/gt-xsd-core --><dependency> <groupId>org.geotools.xsd</groupId> <artifactId>gt-xsd-core</artifactId> <version>${geotools.version}</version></dependency><!-- https://mvnrepository.com/artifact/org.geotools/gt-xml --><dependency> <groupId>org.geotools</groupId> <artifactId>gt-xml</artifactId> <version>${geotools.version}</version></dependency>
在Java中读取Shapefile的方式有几种,这里分享一种方式,后面我们可以详细的来介绍不同的打开方式,其实大致的原理都是差不多的。
2、关于空间参考
这里想分享一个注意的地方,众所周知,在空间计算中,空间参考是一个很重要的标准。我们很多的空间分析都是建立在空间参考之上的。因此首先来介绍一下空间参考信息,首先我们先不修改上面的maven引用,您也可以直接在代码中执行以下的代码:
/*** gt-epsg-hsql 这里一定要引入epsg的包,这样才能读取相关的信息,否则报错* * @throws NoSuchAuthorityCodeException* @throws FactoryException*/@Testpublic void testEpsg() throws NoSuchAuthorityCodeException, FactoryException {CoordinateReferenceSystem tCrs = CRS.decode("EPSG:4490");System.out.println(tCrs);System.out.println("*************************************************");String code = CRS.lookupIdentifier(tCrs, true);System.out.println("code==>" + code);System.out.println("**************************************************");Integer epsgCode = CRS.lookupEpsgCode(tCrs, true);if (epsgCode != null) {System.out.println("EPSG Code: " + epsgCode);} else {System.out.println("EPSG Code not found.");}}
这里是获取默认的4490的EPSG空间参考,同时可以获取它对应的代码如:EPSG:4490。运行上述的代码之后,我们发现控制台并没有进行正常的展示,会报以下的错误。
详细的错误信息如下:
org.opengis.referencing.NoSuchAuthorityCodeException: No code "EPSG:4490" from authority "EPSG" found for object of type "EngineeringCRS".at org.geotools.referencing.factory.epsg.CartesianAuthorityFactory.noSuchAuthorityException(CartesianAuthorityFactory.java:140)at org.geotools.referencing.factory.epsg.CartesianAuthorityFactory.createEngineeringCRS(CartesianAuthorityFactory.java:132)at org.geotools.referencing.factory.epsg.CartesianAuthorityFactory.createCoordinateReferenceSystem(CartesianAuthorityFactory.java:123)at org.geotools.referencing.factory.AuthorityFactoryAdapter.createCoordinateReferenceSystem(AuthorityFactoryAdapter.java:811)at org.geotools.referencing.factory.ThreadedAuthorityFactory.createCoordinateReferenceSystem(ThreadedAuthorityFactory.java:667)at org.geotools.referencing.DefaultAuthorityFactory.createCoordinateReferenceSystem(DefaultAuthorityFactory.java:178)at org.geotools.referencing.CRS.decode(CRS.java:538)at org.geotools.referencing.CRS.decode(CRS.java:459)at com.yelang.project.geotools.TestReadPoiShpWriter2Db.testEpsg(TestReadPoiShpWriter2Db.java:216)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)at java.lang.reflect.Method.invoke(Unknown Source)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)at org.junit.runners.ParentRunner.run(ParentRunner.java:413)at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
如果你也遇到这个问题,只需要在pom.xml中加入如下依赖:
<!-- 增加epsg支持 --><dependency> <groupId>org.geotools</groupId> <artifactId>gt-epsg-hsql</artifactId> <version>${geotools.version}</version></dependency>
在此运行,可以在控制台看到问题解决了。
GEOGCS["China Geodetic Coordinate System 2000", DATUM["China 2000", SPHEROID["CGCS2000", 6378137.0, 298.257222101, AUTHORITY["EPSG","1024"]], AUTHORITY["EPSG","1043"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4490"]]*************************************************code==>EPSG:4490**************************************************EPSG Code: 4490
3、加载shp文件
这里讲解如何加载shp文件,geotools中加载shp文件的方法很多,这里分享一种方式,关键代码如下所示:
// 指定Shapefile的文件路径String shpFile = "C:/BaiduDownload/长沙市2020年POI数据集/长沙市2020年POI数据集/长沙POI数据(.shp)/风景名胜.shp";FileDataStore dataStore = FileDataStoreFinder.getDataStore(new File(shpFile));ShapefileDataStore shapefileDataStore = new ShapefileDataStore(new File(shpFile).toURI().toURL());System.out.println(shapefileDataStore.getCharset());String[] typeNames = dataStore.getTypeNames();System.out.println(typeNames.length);for (String type : typeNames) {System.out.println(type);}
我们通过FileDataStore来加载shp数据,然后通过ShapefileDataStore来设置文件的字符编码。然后我们通过getTypeNames来获取图层信息。
4、属性字段的读取
在GeoTools中,使用SimpleFeatureType对象类获取矢量数据的属性描述信息,下面讲解如何使用GeoTools来读取矢量信息的属性字段。完整的代码如下:
@Testpublic void readShpAttr() throws IOException, FactoryException {// 指定Shapefile的文件路径String shpFile = "C:/BaiduDownload/长沙市2020年POI数据集/长沙市2020年POI数据集/长沙POI数据(.shp)/风景名胜.shp";FileDataStore dataStore = FileDataStoreFinder.getDataStore(new File(shpFile));ShapefileDataStore shapefileDataStore = new ShapefileDataStore(new File(shpFile).toURI().toURL());System.out.println(shapefileDataStore.getCharset());String[] typeNames = dataStore.getTypeNames();System.out.println(typeNames.length);for (String type : typeNames) {System.out.println(type);}// 获取特征类型SimpleFeatureType featureType = dataStore.getSchema(dataStore.getTypeNames()[0]);CoordinateReferenceSystem crs = featureType.getGeometryDescriptor().getCoordinateReferenceSystem();System.out.println("坐标参考系统:" + crs);// 获取SRIDString code = CRS.lookupIdentifier(crs, true);System.out.println("EPSG CODE :" + code);// 获取属性名List<String> attributeNames = featureType.getAttributeDescriptors().stream().map(attr -> attr.getLocalName()).collect(Collectors.toList());System.out.println("Attributes: " + attributeNames);System.out.println("以下是属性信息的深度解析:--------------------------------------");List<AttributeDescriptor> attrDescList = featureType.getAttributeDescriptors();System.out.println(attrDescList.size());for (AttributeDescriptor attrDesc : attrDescList) {System.out.println(attrDesc);System.out.println("属性详情:");AttributeType attrType = attrDesc.getType();System.out.println(attrType);System.out.println("name=" + attrDesc.getName() + "\tLocalName=" + attrDesc.getLocalName() + "\t"+ attrDesc.getMaxOccurs() + "\t" + attrDesc.getMinOccurs());System.out.println("UserData" + attrDesc.getUserData());}}
可以看到,上面读取的信息与在QGIS中展示的信息是一致的,说明我们已经成功的读取了POI数据的空间参考、属性信息等重要信息。
三、总结
以上就是本文的主要内容,本文主要研究使用Java开发语言基于GeoTools地理开发组件来进行空间矢量数据如Shapefile的属性信息的读取,文章首先介绍如何使用GeoTools来打开一个shp文件,在QGIS中展示原始数据的相关信息,然后讲解如何通过GeoTools获取shp文件的空间参考,接着讲解根据空间参考获取我们常见的空间参考编码和代号,其次讲解如何获取空间数据的属性字段。通过本文,您可以了解和掌握如何利用GeoTools来进行Shapefile的属性信息的读取。行文仓促,定有不足之处,如有不足,还请各位专家博主在评论区留下真知灼见,不胜感激。