基于Java的GeoTools对Shapefile文件属性信息深度解析

目录

前言

一、Shapefile的属性列表信息

1、属性表格信息

2、属性表格包含的要素 

二、GeoTools对属性表格的解析

1、常规解析方法

2、基于dbf文件的属性信息读取

三、总结


前言

        ESRI Shapefile(shp),或简称shapefile,是美国环境系统研究所公司(ESRI)开发的一种空间数据开放格式。该文件格式已经成为了地理信息软件界的一个开放标准,这表明ESRI公司在全球的地理信息系统市场的重要性。Shapefile也是一种重要的交换格式,它能够在ESRI与其他公司的产品之间进行数据互操作。Shapefile文件用于描述几何体对象:点,折线与多边形。例如,Shapefile文件可以存储井、河流、湖泊等空间对象的几何位置。除了几何位置,shp文件也可以存储这些空间对象的属性,例如一条河流的名字,一个城市的温度等等。

        Shapefile属于一种矢量图形格式,它能够保存几何图形的位置及相关属性。但这种格式没法存储地理数据的拓扑信息。Shapefile在九十年代初的ArcView GIS的第二个版本被首次应用。许多自由的程序或商业的程序都可以读取Shapefile。Shapefile是一种比较原始的矢量数据存储方式,它仅仅能够存储几何体的位置数据,而无法在一个文件之中同时存储这些几何体的属性数据。因此,Shapefile还必须附带一个二维表用于存储Shapefile中每个几何体的属性信息。Shapefile中许多几何体能够代表复杂的地理事物,并为他们提供强大而精确的计算能力。
        Shapefile文件指的是一种文件存储的方法,实际上该种文件格式是由多个文件组成的。其中,要组成一个Shapefile,有三个文件是必不可少的,它们分别是".shp", ".shx"与 ".dbf"文件。表示同一数据的一组文件其文件名前缀应该相同。例如,存储一个关于湖的几何与属性数据,就必须有lake.shp,lake.shx与lake.dbf三个文件。而其中“真正”的Shapefile的后缀为shp,然而仅有这个文件数据是不完整的,必须要把其他两个附带上才能构成一组完整的地理数据。除了这三个必须的文件以外,还有八个可选的文件,使用它们可以增强空间数据的表达能力。所有的文件名都必须遵循MS DOS的8.3文件名标准(文件前缀名8个字符,后缀名3个字符,如shapefil.shp),以方便与一些老的应用程序保持兼容性,尽管现在许多新的程序都能够支持长文件名。此外,所有的文件都必须位于同一个目录之中。

        由于Shapefile有着广泛的适用性,是很多公共地理服务所采用的矢量文件规范之一。因此有必要对Shapefile文件进行深度的解析。如果将shapefile类比为一个数据库,那么它就是一个database,而其中的属性表则是一张一张的物理表。他们两存在天然的对应关系。虽然在前面的博客中,我们已经对shapefile的解析有了基本的认识。但是对属性表格的解析并没有介绍得很详细。本文则重点讲解在Java当中,如何使用GeoTools来进行属性表格的解析,首先在Qgis中重点介绍属性表格的字段信息,然后介绍一种常见的属性字段信息解析方式,其次介绍基于dbf的属性信息解析方法,直接解析dbf文件的方法在很多网络知识中很少见,但是却非常有用,比如在获取double类型的数值时,想获取精度值,就可以通过这种方式来获取。学习本文,不仅更加熟悉GeoTools对属性表格的解析,同时对于如何快速的将Shapefile的属性模型映射成空间库模型有更进一步的认识。希望对您有一定的帮助。

一、Shapefile的属性列表信息

        本文主要是讲解如何来深度解析shapefile的属性表,因此就有必要对属性列表信息进行一个详细的讲解。

1、属性表格信息

        shapefile文件其实是文件数据库的一种,它将数据的信息写入到shapefile文件中,然后在其它的电脑终端中可以直接共享这些数据,解析这些属性信息。为了介绍方便,我们将shapefile与常见的关系型数据库进行对比,可以这么进行理解。一个shapefile就是一个database,而一张表就是一个属性表。属性表格中的字段、数据类型、长度、精度等就是与关系型数据库中的数据列是一个意思,而数据行基本就是数据库中一行数据的意思。下面是以某年度某城市POI的矢量数据为例进行讲解,在Qgis中打开字段表如下:

2、属性表格包含的要素 

        既然与关系型数据库的数据定义类似,那么就有必要对属性表格的每一列的信息进行精准的定义。包括数据类型、数据长度、精度等,下面结合示例数据来进行一个深入讲解。

序号参数参数
1名称具体的字段名称,如name
2别名类比于关系数据库的字段别名
3类型数据类型,比如QString
4类型名数据类型名,比如String,Real
5长度字段的长度,比如:255
6精度double等类型的精度,如11
7注解其它辅助信息

        有了上面的信息之后,我们也可以根据属性表格来进行空间表的创建,这样管理起来也比较方便。在数据库中定义好每一个列的数据信息之后,就可以将具体的空间信息填充到每一行中,这时候可能就需要外业和内业的工作人员通力配合。

        在了解上面的基本信息后,在下面的章节中,我们就要进行属性表格的深度解析操作。 

二、GeoTools对属性表格的解析

        本节重点使用两种方法来进行属性表格解析的实战,第一种方法是常规的通过featureType的方式来获取属性表格的信息。第二种是基于GeoTools来读取dbf文件来获取元数据信息。这里不仅说明两种方式的优缺点,也说明两者的具体开发代码,帮助大家来深度掌握GeoTools的属性表解析操作。

1、常规解析方法

        首先讲解普通的解析方法,同时这种解析的方式在互联网或者很多博客当中是最常见的。也就是直接解析shp文件。具体的解析步骤流程如下所示:

        这里给出详细的遍历代码,如下所示:

 

public void testAttrLength() throws Exception {// 指定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);// 获取属性名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("userData=========>" + attrType.getUserData());System.out.println("数据库数据类型:" + convertShpFieldType2H2GISOrPG(attrType.getBinding()));System.out.println(attrType);System.out.println("name=" + attrDesc.getName() + "\tLocalName=" + attrDesc.getLocalName() + "\t"+ attrDesc.getMaxOccurs() + "\t" + attrDesc.getMinOccurs());List<Filter> restrictions = attrType.getRestrictions();System.out.println("过滤器为 : " + restrictions);for (Filter filter : restrictions) {if (filter instanceof IsLessThenOrEqualToImpl) {IsLessThenOrEqualToImpl impl = (IsLessThenOrEqualToImpl) filter;Expression exp1 = impl.getExpression1();Expression exp2 = impl.getExpression2();System.out.println(exp1.getClass());System.out.println(exp2.getClass());if (exp1 instanceof LengthFunction && exp2 instanceof LiteralExpressionImpl) {LengthFunction length = (LengthFunction) exp1;LiteralExpressionImpl literal = (LiteralExpressionImpl) exp2;System.out.println( length.getName() + ":\t" + literal.getValue());}}}System.out.println("**************************************************");}}

        可以看到,这里关于数据的属性列表信息,尤其是获取字段的长度和精度时,是通过在过滤器中的具体子类来实现的。在Filter列表中,一定会用到两个表达式对象,其中的一个是:IsLessThenOrEqualToImpl,另一个则是LiteralExpressionImpl,通过这两个类来实现长度的控制。因此如果需要在代码中获取具体的长度,可以通过这两个表达式对象来获取。同时,在进行数据的解析时,虽然我们在前面对两者进行了简单的类比,但毕竟两者不是一个东西,因此在进行数据的存储时,具体的设置还是有一定的区分的,比如对应的具体数据类型,这里我们来提供一个类来进行转换。

/**
* convert shpFileType to db field type
** 备注:目前arcgis的字段类型有:短整型 长整型 浮点型 双精度 文本 日期
* @param value
* @return
*/
public static String convertShpFieldType2H2GISOrPG(Class<?> value) throws Exception {if (value == String.class) {//文本return "varchar";}if (value == Integer.class) {//短整型return "int";}if (value == Long.class) {//长整型return "bigint";}if (value == Double.class || value == Float.class) {//浮点型 双精度 保留精度,比如在金币上运算更安全return "numeric";}if (value == Date.class) {return "TIMESTAMP WITH TIME ZONE ";//日期, 使用包含时区的来处理}if (Geometry.class.isAssignableFrom(value)) {return "geometry";}//除了上述,真不知道还会有啥了。除非arcgis的shp又增加了新类型?!那无能为力了,抛出异常吧throw new Exception("不支持的类型!" + value.getName());////if (value.getSuperclass().getName().equals(String.class))// return null;
}

        在GeoTools中,关于Filter的相关类,非常的庞大,这里不全部展开,以防止抓不住重点。先给一个Filter的类图给朋友们,感兴趣的可以先了解一下。

        通过以上的步骤和示例代码,我们可以在控制台中输出以下信息:

        为了方便展示字段的解析,这里特意将复制一段处理的输出信息:

AttributeDescriptorImpl WGS84_纬 <Double:Double> nillable 0:1
属性详情:
userData=========>{}
数据库数据类型:numeric
AttributeTypeImpl Double<Double>
restrictions=[ length([.]) <= 19 ]
name=WGS84_纬	LocalName=WGS84_纬	1	0
过滤器为 : [[ length([.]) <= 19 ]]
class org.geotools.filter.LengthFunction
class org.geotools.filter.LiteralExpressionImpl
length:	19

        可以看到,通过上述的代码就可以获取属性表格的字段名、字段类型、字段的长度等信息,有了这些信息,我们其实可以做的事情很多了。不知道大家发现一个问题没有,在上述的代码中,并没有返回精度,即11位小数的长度。上述代码是无法实现的。那么有没有什么办法来实现精度值的获取呢。

2、基于dbf文件的属性信息读取

        众所周知,在shapefile文件夹中,除了shp文件之外,另外一个prj和dbf文件也是缺一不可的。既然我们无法通过读取shp来实现精度的精确读取,那么是否可以从这两个文件当中来获取呢?shp文件有我们定义好的空间信息,而大量的属性表格,我们是存放在dbf中,因此我们来测试读取dbf文件看能否找到另外的一条大路。

        幸运的是,在Geotools当中,官方提供了DbaseFileReader这个类,通过这个类实现对dbf文件的读取,之前也查找了一些相关的资料,但是很少有博主写这种实现模式。这里提供基于DbaseFileReader的实现方式,代码实现更简单,性能更好。其原理非常简单,通过DbaseFileReader来获取DbaseFileHeader,然后就可以遍历DbaseFileHeader中的信息,就可以实现所有信息的识别。代码如下:

 /*** - 读取dbf文件* @throws IOException*/@Testpublic void testReadDbf() throws IOException {File dbfFile = new File("C:/BaiduDownload/长沙市2020年POI数据集/长沙市2020年POI数据集/长沙POI数据(.shp)/风景名胜.shp");// 打开 DBF 文件ShpFiles shpFile = new ShpFiles(dbfFile);System.out.println(Charset.defaultCharset().toString());DbaseFileReader dbfReader = new DbaseFileReader(shpFile, true, Charset.defaultCharset());// 读取 DBF 文件的头信息DbaseFileHeader header = dbfReader.getHeader();System.out.println(header.getNumFields());System.out.println(header.getNumRecords());for (int i = 0; i < header.getNumFields(); i++) {System.out.println(header.getFieldName(i) + "\t" + header.getFieldLength(i) + "\t" + header.getFieldType(i)+ "\t" + header.getFieldDecimalCount(i));}// 关闭 DBF 文件读取器dbfReader.close();
}

        这个类的方法如下:

Modifier and TypeMethodDescription
voidclose()

Clean up all resources associated with this reader.Highly recomended.

protected intfill​(ByteBuffer buffer, ReadableByteChannel channel)
DbaseFileHeadergetHeader()

Get the header from this file.

booleanhasNext()

Query the reader as to whether there is another record.

Stringid()

An id for the reader.

static voidmain​(String[] args)
voidread()

Reads the next record into memory.

Object[]readEntry()

Get the next record (entry).

Object[]readEntry​(Object[] entry)

Copy the next entry into the array.

Object[]readEntry​(Object[] entry, int offset)

Copy the next record into the array starting at offset.

ObjectreadField​(int fieldNum)

Reads a single field from the current record and returns it.

DbaseFileReader.RowreadRow()
voidskip()

Skip the next record.

voidtransferTo​(DbaseFileWriter writer)

Transfer, by bytes, the next record to the writer.

        想要获取属性就是通过获取Head对象即可实现属性列表的深度解析。返回DbaseFileHead,它的方法如下表所示:

Modifier and TypeMethodDescription
voidaddColumn​(String inFieldName, char inFieldType, int inFieldLength, int inDecimalCount)

Add a column to this DbaseFileHeader.

Class<?>getFieldClass​(int i)

Determine the most appropriate Java Class for representing the data in the field.

intgetFieldDecimalCount​(int inIndex)

Get the decimal count of this field.

intgetFieldLength​(int inIndex)

Returns the field length in bytes.

StringgetFieldName​(int inIndex)

Get the field name.

chargetFieldType​(int inIndex)

Get the character class of the field.

intgetHeaderLength()

Get the length of the header

intgetLargestFieldSize()

Get the largest field size of this table.

DategetLastUpdateDate()

Get the date this file was last updated.

longgetLengthForRecords​(int records)

Returns the expected file size for the given number of records in the file

intgetNumFields()

Return the number of fields in the records.

intgetNumRecords()

Return the number of records in the file

intgetRecordLength()

Get the length of the records in bytes.

voidreadHeader​(ByteBuffer in)

Read the header data from the DBF file.

voidreadHeader​(ReadableByteChannel channel)

Read the header data from the DBF file.

intremoveColumn​(String inFieldName)

Remove a column from this DbaseFileHeader.

voidsetNumRecords​(int inNumRecords)

Set the number of records in the file

StringtoString()

Get a simple representation of this header.

voidwriteHeader​(WritableByteChannel out)

Write the header data to the DBF file.

        在Heade对象中,要想获取字段的长度和精度,主要是使用下列两个方法,分别是:getFieldLength(index)和getFieldDecimalCount(index)。在IDE中运行以上程序后,可以在控制台中看到如下输出:

名称	254	C	0
大类	254	C	0
中类	254	C	0
小类	254	C	0
地址	254	C	0
省	254	C	0
市	254	C	0
区	254	C	0
WGS84_经	19	F	11
WGS84_纬	19	F	11

        可以看到,这里不仅输出了数据的长度,同时还输出了数据的精度。到此,使用第二种方式来进行dbf文件的解析已经成功实现。

三、总结

        以上就是本文的主要内容,本文则重点讲解在Java当中,如何使用GeoTools来进行属性表格的解析,首先在Qgis中重点介绍属性表格的字段信息,然后介绍一种常见的属性字段信息解析方式,其次介绍基于dbf的属性信息解析方法,直接解析dbf文件的方法在很多网络知识中很少见,但是却非常有用,比如在获取double类型的数值时,想获取精度值,就可以通过这种方式来获取。学习本文,不仅更加熟悉GeoTools对属性表格的解析,同时对于如何快速的将Shapefile的属性模型映射成空间库模型有更进一步的认识。行文仓促,难免有许多不足之处,如有不足还请各位专家博主在评论区留言指出,不胜感激。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1555403.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【Spring Boot React】Spring Boot和React教程 完整版

【Spring Boot & React】Spring Boot和React教程 在B站找到一个不错的SpringBoot和React的学习视频&#xff0c;作者是amigoscode 【Spring Boot & React】Spring Boot和React教程 2023年更新版【Spring Boot React】价值79.9美元&#xff0c;全栈开发&#xff0c;搭…

如何使用CMD命令启动应用程序(二)

说明&#xff1a;去年1024发布了一篇博客&#xff0c;介绍如何使用CMD命令启动应用程序&#xff0c;但实际情况&#xff0c;有些程序可能无法用配置环境变量的方式来启动&#xff0c;本文针对两种情况下的程序&#xff0c;如何使用CMD命令来启动&#xff0c;算是对上一篇博客的…

Qt操作主/从视图及XML——实例:汽车管理系统

目录 1. 主界面布局2.连接数据库3.主/从视图应用 1. 主界面布局 先创建一个QMainwindow&#xff0c;不带设计界面 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QGroupBox> #include <QTableView> #include <QListWidg…

鸿蒙harmonyos next flutter混合开发之开发plugin(获取操作系统版本号)

创建Plugin为my_plugin flutter create --org com.example --templateplugin --platformsandroid,ios,ohos my_plugin 创建Application为my_application flutter create --org com.example my_application flutter_application引用flutter_plugin&#xff0c;在pubspec.yam…

如何解决 MySQL ERROR 1040 (08004): Too many connections ?

MySQL 是最流行的开源关系数据库管理系统之一&#xff0c;它也是开发人员中非常常用的数据库。即便它高度健壮和可伸缩性极强&#xff0c;像任何软件一样&#xff0c;它也可能出现错误。我们会经常遇到一个错误&#xff0c;特别是在高流量系统中&#xff0c;error 1040 (08004)…

51c视觉~CV~合集3

我自己的原文哦~ https://blog.51cto.com/whaosoft/11668984 一、 CV确定对象的方向 介绍如何使用OpenCV确定对象的方向(即旋转角度&#xff0c;以度为单位)。 先决条件 安装Python3.7或者更高版本。可以参考下文链接&#xff1a; https://automaticaddison.com/how-to-s…

阳台山足球营地的停车位探寻

阳台山足球营地的环境是真好哈。停车场名称&#xff1a;阳台山森林公园配套停车场。应该很多爬山的人车子也停在这里。而且我没想到的&#xff0c;阳台山的山泉水还有不少居民每天提着空桶去山上装。看来环境是真的很好哈 停车场有地面和地下停车场&#xff0c;停车位个数查不…

java 数据存储方式

1. 变量存储 这是最基本的数据存储方式&#xff0c;通过声明变量来存储数据。变量可以是基本数据类型&#xff08;如int、float、char等&#xff09;&#xff0c;也可以是引用数据类型&#xff08;如对象、数组等&#xff09;。变量存储的数据通常存储在内存中&#xff0c;随着…

【记录】Excel|Excel 打印成 PDF 页数太多怎么办

【记录】Excel&#xff5c;解决 Excel 打印成 PDF 页数过多的问题 文章目录 【记录】Excel&#xff5c;解决 Excel 打印成 PDF 页数过多的问题方法一&#xff1a;调整页边距WPS OfficeMicrosoft Excel 方法二&#xff1a;优化页面布局调整列宽和行高使用“页面布局”视图合并单…

python全栈开发是什么?

全栈指掌握多种技能&#xff0c;并能利用多种技能独立完成产品。通俗的说就是与这项技能有关的都会&#xff0c;都能独立完成。 python&#xff0c;因为目前很火&#xff0c;能开发的项目很多。例如&#xff1a;web前端后端&#xff0c;自动化运维&#xff0c;软件、小型游戏开…

八大排序--01冒泡排序

假设有一组数据 arr[]{2&#xff0c;0&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;7} 方法&#xff1a;开辟两个指针&#xff0c;指向如图&#xff0c;前后两两进行比较&#xff0c;大数据向后冒泡传递&#xff0c;小数据换到前面。 一次冒泡后&#xff0c;数组中最大…

【网络篇】计算机网络——应用层详述(笔记)

目录 一、应用层协议原理 1. 进入应用层 2. 网络应用程序体系结构 &#xff08;1&#xff09;客户-服务器体系结构&#xff08;client-server architecture&#xff09; &#xff08;2&#xff09; P2P 体系结构&#xff08;P2P architecture&#xff09; 3. 进程间通讯 …

【GC日志和OOM日志分析】JVM GC日志和OOM Dump文件分析

1 缘起 充电、充电、充电。 增加一些必备的知识&#xff0c;帮助后续使用。 2 配置JVM参数 为分析GC日志以及OOM相关信息&#xff0c;配置JVM参数&#xff0c;分为三个部分&#xff1a; &#xff08;1&#xff09;堆内存&#xff0c;包括年轻代、最大堆内存&#xff1b; &a…

PELT算法

PELT算法的范畴 PELT算法&#xff08;Pruned Exact Linear Time&#xff09;属于时间序列分析和变点检测&#xff08;Change Point Detection&#xff09;范畴的算法。 从更广泛的角度来看&#xff0c;PELT算法还可以归类为以下几类算法的子集&#xff1a; 1. 时间序列分析&…

初识Linux · 文件(1)

目录 前言&#xff1a; 回顾语言层面的文件 理解文件的预备知识 文件和磁盘 使用和认识系统调用函数 前言&#xff1a; 本文以及下篇文章&#xff0c;揭露的都是Linux中文件的奥秘&#xff0c;对于文件来说&#xff0c;初学Linux第一节课接触的就是文件&#xff0c;对于C…

COMP 9517 Computer Vision week3

目录 特征表示图像特征概念(image feature)图像特征应该具备的属性 图像特征种类颜色特征颜色直方图(Color Histogram)颜色矩(Colour moments) 纹理特征(texture features)Haralick特征局部二值模式(Local Binary Patterns, LBP)尺度不变特征变换SIFT(Scale-invariant feature …

Error while loading conda entry point: conda-libmamba-solver

问题 解决方法 conda install --solverclassic conda-forge::conda-libmamba-solver conda-forge::libmamba conda-forge::libmambapy conda-forge::libarchive

如何实现事件流操作

文章目录 1 概念介绍2 使用方法3 示例代码我们在上一章回中介绍了通道相关的内容,本章回中将介绍StreamProvider组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 在Flutter中Stream是经常使用的组件,对该组件的监听可void main() {///让状态栏和程序的appBar融为一体…

zotero WebDAV同步忘记密码

https://www.jianguoyun.com/#/safety 找到应用密码

thinkphp 学习记录

1、PHP配置 &#xff08;点开链接后&#xff0c;往下拉&#xff0c;找到PHP8.2.2版本&#xff0c;下载的是ZIP格式&#xff0c;解压即用&#xff09; PHP For Windows: Binaries and sources Releases &#xff08;这里是下载地址&#xff09; 我解压的地址是&#xff1a;D:\…