1 介绍
在Shapefile文件格式介绍一文中我们介绍了shapefile文件的结构组成,本文主要介绍如何读取shapefile文件头部分,使用的语言是c++。
2 文件头结构
Shapefile文件的文件头是固定长度的,总长度为100个字节,其中4-23字节为未使用部分,其他字段分别描述着当前shapefile文件的各类信息,在编码过程中,我们需要注意文件头中各字段的字节顺序以及类型,特别是File Length字段,这个是16位字表示的,在我们读取完成后需要转换成正常表示。
Bytes | Field | Value | Type | Byte Order |
---|---|---|---|---|
0-3 | File Code | 9994 | Integer | Big Endian |
4-23 | Unused | 0 | Integer | Big Endian |
24-27 | File Length | File Length | Integer | Big Endian |
28-31 | Version | 1000 | Integer | Little Endian |
32-35 | Shape Type | Shape Type | Integer | Little Endian |
36-67 | Minimum Bounding Rectangle | Xmin, Ymin, Xmax and Ymax | double | Little Endian |
68-83 | Bounding Box | Zmin, Zmax | double | Little Endian |
84-99 | Bounding Box | Mmin, Mmax | double | Little Endian |
3 数据结构封装
因为文件头是固定长度的,所以我们可以封装一个文件头数据结构,然后直接从文件中读取100个字节即可,另外文件头中还包含其他如Bounding box和shape type等结构,我们一次封装即可。
3.1 Bounding Box
Bounding box主要用来表示几何形状边界信息,在文件头中主要是36-99字节使用,由若干double组成,为了后续使用方便,我们封装两种不同类型的Bounding Box结构,具体如下:
class bounding_box4
{
public:
double x1;
double y1;
double x2;
double y2;
};
class bounding_box2
{
public:
double x;
double y;
};
3.2 Shape type
shape type字段主要是用来表示当前文件所描述的几何类型,在文件头中用一个int表示,下面是所有的shape type不同值所表示的几何类型。
Value | Shape Type |
---|---|
0 | Null Shape |
1 | Point |
3 | Polyline |
5 | Polygon |
8 | MultiPoint |
11 | PointZ |
13 | PolyLineZ |
15 | PolygonZ |
18 | MultiPointZ |
21 | PointM |
23 | PolyLineM |
25 | PolygonM |
28 | MultiPointM |
31 | MultiPatch |
我们可以用enum class来封装所有的shape type值。
enum class ShapeType
{
ST_NULL = 0,
ST_POINT = 1,
ST_POLYLINE = 3,
ST_POLYGON = 5,
ST_MULTI_POINT = 8,
ST_POINT_Z = 11,
ST_POLYLINE_Z = 13,
ST_POLYGON_Z = 15,
ST_MULTI_POINT_Z = 18,
ST_POINT_M = 21,
ST_POLYLINE_M = 23,
ST_POLYGON_M = 25,
ST_MULTI_POINT_M = 28,
ST_MULTI_PATCH = 31,
};
3.3 ShapeFileHeader
我们用ShapeFileHeader类来表示整个文件头部分,这里需要注意文件头中有int和double数据类型,要注意字节对齐引入的整个文件头大小变化问题,这里我们使用pack指令强制4字节对齐,具体如下:
#pragma pack(4)
class ShapeFileHeader
{
public:
int file_code;
char reversed[20];
int file_length;
int version;
ShapeType shape_type;
bounding_box4 xy_bounding;
bounding_box2 z_boudning;
bounding_box2 m_boundnig;
};
#pragma pack()
3.4 大小端转换
因为文件头中的有些字段是用大端模式表示的,所以这里我们添加一个转换方法:
void inline swap_bytes32(unsigned int &val)
{
val = (val >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0xff00) << 8) | (val << 24);
}
测试代码
#include <iostream>
#include <fstream>
using namespace std;
class bounding_box4
{
public:
double x1;
double y1;
double x2;
double y2;
};
class bounding_box2
{
public:
double x;
double y;
};
enum class ShapeType
{
ST_NULL = 0,
ST_POINT = 1,
ST_POLYLINE = 3,
ST_POLYGON = 5,
ST_MULTI_POINT = 8,
ST_POINT_Z = 11,
ST_POLYLINE_Z = 13,
ST_POLYGON_Z = 15,
ST_MULTI_POINT_Z = 18,
ST_POINT_M = 21,
ST_POLYLINE_M = 23,
ST_POLYGON_M = 25,
ST_MULTI_POINT_M = 28,
ST_MULTI_PATCH = 31,
};
#pragma pack(4)
class ShapeFileHeader
{
public:
unsigned int file_code;
char reversed[20];
unsigned int file_length;
unsigned int version;
ShapeType shape_type;
bounding_box4 xy_bounding;
bounding_box2 z_boudning;
bounding_box2 m_boundnig;
};
#pragma pack()
void inline swap_bytes32(unsigned int& val)
{
val = (val >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0xff00) << 8) | (val << 24);
}
int main(int argc, char* argv[])
{
string file_path = "C:\\Users\\Administrator\\Downloads\\AM_RunwayElement.shp";
ShapeFileHeader header;
ifstream in(file_path, ifstream::binary);
in.read((char*)&header, sizeof(header));
swap_bytes32(header.file_code);
swap_bytes32(header.file_length);
// get actual file length
header.file_length *= 2;
cout << header.file_code << endl;
cout << header.version << endl;
in.close();
}