【老文新说】C++读取.shp文件

1. .shp文件简介

shp文件是ESRI(美国环境系统研究所公司)开发的一种用于存储和描述空间数据的文件格式(shapefile),目前已经成为GIS中的开放标准。shp常用来描述基本几何对象:点、线、多边形。(shapefile其实也可以存储对象的各种人为定义的属性,但那些属性数据是存放在dBase File(dbf)中的,不属于本文的内容。)

ArcMap中这样描述:

Shapefile 是一种用于存储地理要素的几何位置和属性信息的非拓扑简单格式。 shapefile 中的地理要素可表示为点、线或面(区域)。 包含 shapefile 的工作空间还可以包含 dBASE 表,它们用于存储可连接到 shapefile 的要素的附加属性。

笔者这样理解shape文件:

一种可扩展的、用于存储基本几何对象的、可描述基本地理信息的二进制文件格式。

shp文件的读写,源代码(openfile仓库的shp文件夹中)在github上,有兴趣的同学可以自取,如对代码有任何疑问,欢迎提交issue,或者添加笔者QQ:3495421705。同时也欢迎各位同学加入到该仓库的建设和维护中。

注意,在本文中,只讨论shp文件这一个文件格式相关内容,关于shxdbf这两个文件格式,将会在后续文章中介绍。

2. shp文件格式

shp文件格式比较简单,分为文件头记录列表两块。

2.1 文件头

shp文件头是一个定长100字节的数据块,其中的内容由以下部分组成:

字段名数据长度(字节)字节序备注
文件码(file code)sizeof(int32) = 4big
未使用5 * sizeof(int32) = 20big
文件长度(File Length)sizeof(int32) = 4big
文件版本(version)sizeof(int32) = 4little
图元类型(shape type)sizeof(int32)=4little
xy数据范围(包围盒)4 * sizeof(double) = 32little分别为xminyminxmaxymax
z和m数据范围4 * sizeof(double)=32little分别为:zminzmaxmminmmax

上表中关于字节序的知识,大家可以上网搜索,其原理就是不同系统要求的数据在内存中的字节顺序不一样,其中:

BigEndian机器上,0x0102在内存中的字节顺序为0x010x02,但是在LittleEndian的机器上,他的字节顺序就变成了0x020x01
字节转换的工具函数,在shp读写的源码仓库中已有实现。

2.2 文件记录

shp的文件记录,是变长的,根据其内部存储的数据量而定。shp文件并没有记录一说,从概念上来看,其每一个shape实际上都可以看成列表中的一条记录,因此被称为记录

shp中的每一条记录,都可以被分为两个部分:记录描述字段shape数据

  1. 记录描述字段
    用来描述当前记录的基本信息:记录编号、记录长度(字节)。
  2. shape数据
    图元的顶点及分段。

shp图元类型(ShapeType) 总共有13种:点(Z、M)多点(Z、M)折线(Z、M)多边形(Z、M)片元(Patch)。其中,Z代表含有z坐标值的图元,M表示含有量测(Measurement)值的图元。图元类型 在shape规范中对应的值如下:

enum ShapeType
{
    kNullShape = 0,
    kPoint = 1,
    kPolyLine = 3,
    kPolygon = 5,
    kMultiPoint = 8,
    kPointZ = 11,
    kPolyLineZ = 13,
    kPolygonZ = 15,
    kMultiPointZ = 18,
    kPointM = 21,
    kPolyLineM = 23,
    kPolygonM = 25,
    kMultiPointM = 28,
    kMultiPatch = 31
};

在shape数据记录中,记录描述可以做如下定义:

    struct RecordField
    {
        int32_t recordNumber;   // big
        int32_t contentLength;  // big
        int32_t shapeType;      // little
    };

其中,RecordNumber是记录编号,ContentLength是当前图元的数据长度(字节),ShapeType是当前记录的图元类型。在shapefile的规范文件中,ShapeType是与记录描述分开的,但笔者认为,可以将ShapeType放到记录描述字段结构体中

ShapeType之后,就是图元的真实数据,根据下表,可以查询每种图元的数据内容:

图元类型数据内容备注
Pointdouble[X,Y]
PointMdouble[X,Y,M]
PointZdouble[X,Y,Z,M]
MultiPointdouble[4] Box
int32 NumPoints
Points[NumPoints] Points
包围盒
点数
顶点数组
MultiPointMdouble[4] Box
int32 NumPoints
Points[NumPoints] Points
double[2] MRange
double[NumPoints] M Array
包围盒
点数
顶点数组
M数据范围
M值数组
MultiPointZdouble[4] Box
int32 NumPoints
Points[NumPoints] Points
double[2] MRange
double[NumPoints] M Array
double[2] Z Range
double[NumPoints] Z Array
包围盒
点数
顶点数组
M数据范围
M值数组
Z值范围
Z值数组
Polylinedouble[4] Box
int32 NumParts
int32 NumPoints
int32[NumParts] Parts
Points[NumPoints] Points
包围盒
段数
点数
段索引数组
顶点数组
PolylineMdouble[4] Box
int32 NumParts
int32 NumPoints
int32[NumParts] Parts
Points[NumPoints] Points
double[2] MRange
double[NumPoints] M Array
包围盒
段数
点数
段索引数组
顶点数组
M数据范围
M值数组
PolylineZdouble[4] Box
int32 NumParts
int32 NumPoints
int32[NumParts] Parts
Points[NumPoints] Points
double[2] MRange
double[NumPoints] M Array
double[2] Z Range
double[NumPoints] Z Array
包围盒
段数
点数
段索引数组
顶点数组
M数据范围
M值数组
Z值范围
Z值数组

PolygonPolyline的数据内容一样,同理ZM类型也一样。不一样的是,多边形和折线对**段(Parts)**的解释不一样。

2.3 多边形的段(Part)

Polygon在shapefile中有一些需要 注意 的点:

  1. 多边形的环(Ring)是闭合的:第一个顶点和最后一个顶点必须一样;
  2. 环在多边形顶点数组的顺序并不重要。
  3. 多边形必须是干净的,干净的含义是:
    a. 无自相交;
    b. 多边形的顶点顺序是顺时针方向的,顺时针的右边为多边形内部。这也是第2点的原因;

多边形的段,每一段的最后一个顶点,和该段的第一个顶点坐标是一样的,这符合上面的第1点,也是多边形和折线的最大不同之处。有了以上基本信息之后,shp文件的读写就很简单了。

3. shp文件读写——C++

shp文件读写的C++实现已经在前文提到的github仓库中了,这里再次给出github链接:openfile.shp

3.1 仓库简介

openfile是一个自包含无外部依赖的文件读写工具集,这些文件包含大部分常见的文件格式,包括但不限于:

栅格数据:tiff、geotiff、img等;

矢量数据:shp(shx和dbf)、dxf等;

点云数据:las(laz)、xyz等;

文本数据:json、xml等;

该工具集的目标如下:

  1. 自包含:一些常用工具已经自己实现;
  2. 无外部依赖:不依赖任何其他三方库,如png文件的读写,不依赖libpng;
  3. 模块化构建和使用:每种文件格式都与其他格式独立,需要什么格式,直接编译对应库即可;
  4. 跨平台:支持Windows、Linux、MacOS等;
  5. 一建构建编译:需要json,开启json模块编译选项,直接输出对应库;

openfile在本文编写时,尚处于起步阶段,本文所介绍的shp文件也是openfile实现的第一个文件读写库。希望读者朋友们能够参与本仓库的开发和维护,一起为开源社区贡献力量。

3.1 代码结构简介

shp读写库的公开接口只有以下5个:

  1. enum ShapeType:图元类型枚举;
  2. class shp:shp文件的操作类;
  3. class Shape:图元操作接口类;
  4. struct ShpHeader:shp文件头的结构体;
  5. 字节序转换函数;

source/shp中,则包含了以下内容:

  1. class ShpInternal:shp类的内部接口类,被读写器类内部依赖;
  2. class Reader:shp文件的读工具类;
  3. class Writer:shp文件的写工具类;
  4. class ShpRecord:shp文件记录类,负责管理和操作记录描述结构体图元
  5. impl/xxx.hpp:图元的具体实现类

4 结语

openfile的QQ交流群已经建好,有兴趣的同学可以加入一起交流:157588707,也可以扫下面二维码加入:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

geocat

球球大佬们赏赐点吃喝!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值