一种Shapefile文件的剖析及读写方法
马文涛;陈宜金;王淼淼;张雪;张凯旋
【摘 要】为加深对 Shapefile文件结构的理解,培养将一定格式的数据文件转换成 Shapefile图形文件的能力.详细探讨了Shapefile文件的底层结构,并以此为出发点,在.Net环境下利用 Microsoft Visual Stu-dio2015 设计出可对 Shapefile图形文件进行交互式读写的 GIS程序.结果表明通过一定的编程手段,可以将一定格式的图形文件按照我们所期望的方式展现出来,且能够创造出我们所需要的图形文件.可见特定格式的数据图形文件与Shapefile图形文件可以通过编程技术进行互相转换. 【期刊名称】《北京测绘》 【年(卷),期】2018(032)012 【总页数】5页(P1517-1521)
【关键词】Shapefile;地理信息系统(GIS)程序;网络;数据结构 【作 者】马文涛;陈宜金;王淼淼;张雪;张凯旋
【作者单位】中国矿业大学(北京)地球科学与测绘工程学院,北京 100083;中国矿业大学(北京)地球科学与测绘工程学院,北京 100083;中国矿业大学(北京)地球科学与测绘工程学院,北京 100083;中国矿业大学(北京)地球科学与测绘工程学院,北京 100083;中国矿业大学(北京)地球科学与测绘工程学院,北京 100083 【正文语种】中 文 【中图分类】P208
0 引言
ArcGIS作为 ESRI(Environmental Systems Research Institute)公司推出的一套具有完善体系的 GIS平台产品,是目前使用最广泛的地理信息系统平台软件,主要用来创建和绘制地图,空间数据编辑与管理,分析,共享和显示地理要素信息,并在一系列应用中使用地图和地理信息[1]。然而这些功能都是在数据的基础上进行
的,Shapefile图形文件格式作为 ArcGIS使用的最主要的几种数据图形格式之一,与其它图形数据文件相对比,该图形文件只占用少量的磁盘存储空间,并且没有拓扑数据结构,具有更快的制图和编辑速度,更易于读写,是 ESRI公司发布的用来描述空间图形数据的几何和属性特征的非拓扑实体矢量数据结构的一种格式,旨在用来培养系统开发人员和软件用户具备将不同数据格式进行互相转换的能力,使得能够在更广泛的应用情况下读取或建立 Shapefile文件,从而将不同的矢量格式转换成 ArcGIS能够接受的数据格式,增强地理空间数据的共享性,普遍性和互操作性[2]。 1 Shapefile图形文件的结构
Shapefile图形文件至少由主文件(*.shp),索引文件(*.shx),dBase表文件 (*.dbf) 三个基本文件组成。其作为一种简单的特征数据格式,用点、线、多边形存储特征性状,但并不存储拓扑关系,投影坐标系信息和地理对象的符号化信息,仅仅存储空间对象的几何特征和属性信息[3]。这种结构最大的好处是结构简单、显示速度快,但不足的是不能强化空间约束。尽管Shapefile文件无法存储投影坐标系等信息,但是可以对它进行定义投影坐标系和建立空间索引等操作,在同一文件夹下生成具有不同扩展名的文件[4]。如,*.prj文件用于存储坐标系的信息;*.xml文件作为元数据文件,用于存储Shapefile的相关信息;*.sbn和*.sbx文件用于存储几何体的空间索引等。
1.1 .shp文件的数据结构
主文件用作来存储地理要素的几何图形,一个.shp文件由文件头和记录实体两方面构成[5]。文件头大小定长为100个字节,文件头中第4-23位字节表示为ESRI公司未利用的字节,方便以后对Shapefile文件增加信息;第24-27位字节为一个整型,其值反映了Shapefile文件的大小;第28-31位字节也为一个整型,其值反映了Shapefile文件的版本;第32-35位字节所对应的整型值代表了shapefile的图形要素的类型,例如:1对应点要素,3对应线要素,5对应面要素; 第36-99位字节都是double类型的字节,其包含了图幅所在的范围。其中需要注意的是Big代表高字节位序(big endian),而Little代表(little endian)小字节位序,这两类字节位序正好相反[6]。在写入Shapefile文件时,对于Big位序的数据来说,只有Little位序的数据转换成Big位序的数据才能正确的进行写入。文件头的布局如表1所示: 表1 主文件头结构PositionFieldValueTypeOrderByte 0File
Code9994IntegerBigByte 4Unused0IntegerBigByte 24File LengthFile LengthIntegerBigByte 28Version1000IntegerLittleByte 32Shape TypeShape TypeIntegerLittleByte 36Bounding BoxXminDoubleLittleByte 92∗Bounding BoxMmaxDoubleLittle
每条记录由记录头和实体内容构成,记录头格式固定[7],具体信息如表2所示:记录头包含记录号(Record Number)和坐标记录项长度(Content Length)两种信息;而实体内容因几何要素的不同,所包含的信息各不相同。
表2 主文件记录头结构Position FieldValueTypeByteOrderByte 0Record NumberRecord NumberIntegerBigByte 4 Content LengthContent LengthIntegerBig 1.2 .shx索引文件结构
shx索引文件是存储图形要素与属性信息索引的文件,主要起到定位的作用,其由文件头和记录两部分组成,文件头的内容和主文件的基本一致[8]。每条记录由8个字
节组成,具体内容见表3。其中,Offset可以理解为这条记录在.shp文件内的偏移量,Content Length可以理解为这条记录的长度[9]。 1.3 .dbf文件结构
.dbf属性表文件,是由头记录及数据记录组成[10]。头记录定义了该表的结构并包含与表相关的其它信息,它主要对属性文件作一些概括性描述,尤其是对属性文件的记录项内容进行了着重性说明,例如对每个记录项的名称、记录项的数据类型、记录项的精度,记录项的长度都作了详细的介绍[11]。每条数据记录都包含了一个要素的基本内容,并且由许多个记录项组成[12]。几何信息和属性表信息必需具有一样的记录数目,且属性表文件中记录项的顺序和主文件中记录项的必需一致[13]。 表3 索引文件记录结构PositionFieldValue Type ByteOrderByte 0 Offset Offset IntegerBigByte 4 Content LengthContent LengthIntegerBig 2 Shapefile读写程序的设计
结构体是值类型的数据结构。它使得一个单一变量可以存储各种数据类型的相关数据,在其中可以定义各种变量值,与类相似,其也可以通过 new关键字进行实例化,但是其值存储在栈中,而且占用空间比较小,访问速度比较快,适合于轻量级对象。类成员一般由成员函数和成员变量两个部分组成,成员函数是为了实现某些功能而封装好的一种代码,是与对象有关的操作,而成员变量则可以看作是与对象操作相适应的特性。笔者以.net为开发环境,自行设计点,线,面结构体,并在类中创建与读写Shapefile文件相关的函数以及变量。结构的设计以及类的成员定义叙述如下: ∥定义点结构 struct PointShape {
public double X;∥存储X坐标 public double Y;∥存储Y坐标
}
∥定义线结构 struct PolylineShape {
public double[] Box; ∥边界盒 public int numParts; ∥部分的数目 public int numPoints; ∥点的总数目
public ArrayList parts; ∥在部分中第一个点的索引 public ArrayList points; ∥所有部分的点 }
∥定义面结构
struct PolygonShape {
public double[] Box; ∥边界盒 public int numParts; ∥部分的数目 public int numPoints; ∥点的总数目
public ArrayList parts; ∥在部分中第一个点的索引的集合 public ArrayList points; ∥所有部分的点的集合 }
ArrayList polygons=new ArrayList();∥用来存储PolygonShape结构的变量 ArrayList polylines=new ArrayList();∥用来存储PolylineShape结构的变量 ArrayList points=new ArrayList();∥用来存储PointShape结构的变量
int tab=0;∥tab=0表示当前记录的图形绘制完毕,开始绘制下条记录的图形;tab=1表示继续当前记录的绘制
private void pictureBox1_Paint(object sender, PaintEventArgs e);∥在pictureBox中绘制shape文件 3 读写操作的代码说明
3.1 将Big位序转换为Little位序
把Big位序转换为Little位序的函数代码如下: int BigLittleEndian(int num) {
int reverse; ∥返回结果 char bit0, bit1, bit2, bit3; bit0=(num & 0x000000ff); bit1=(num & 0x0000ff00) >> 8; bit2=(num & 0x00ff0000) >> 16; bit3=(num & 0xff000000) >> 24;
reverse=(bit0 << 24) | (bit1 << 16) | (bit2 << 8) | (bit3); return reverse; }
3.2 面状Shapefile文件的读取 面状Shapefile文件读取操作代码如下:
函数功能:读取shape文件并在PictureBox中显示 参数:fileName(shape文件的路径) 返回值:无
Private void ReadShp(string fileName) {
BinaryReader br=new BinaryReader (fileName);∥以二进制形式读取shape文
件
br.ReadBytes(32); ∥读取32个字节
int shapeType=br.ReadInt32();∥shapefile文件的类型 br.ReadBytes();∥读取个字节 ∥文件头读取完毕,开始读取记录 switch (shapeType) {case 5:
polygons.Clear();∥清空集合
while (br.PeekChar() !=-1)∥当没有读到最后一个字节时 {
PolygonShape polygon=new PolygonShape();∥实例化面状结构 polygon.Parts=new ArrayList(); polygon.Points=new ArrayList(); br.ReadBytes(44);
polygon.NumParts=br.ReadInt32();∥一条记录中部分的数量 polygon.NumPoints=br.ReadInt32();∥一条记录中点的数量 for (int j=0; j < polygon.NumParts; j++) {int parts=br.ReadInt32();
polygon.Parts.Add(parts); ∥将部分中第一个点的索引添加到集合中 }
∥获取该条记录的坐标值
for (int j=0; j < polygon.NumPoints; j++)
{Point_shape pointtemp=new Point_shape();∥实例化点状结构 pointtemp.X=br.ReadDouble();
pointtemp.Y=-1 * br.ReadDouble();
polygon.Points.Add(pointtemp);∥将点状结构变量添加到集合中 }
polygons.Add(polygon);∥将面状结构类型的变量添加到集合中 }
this.pictureBox1.Refresh();∥调用函数pictureBox1_Paint(object sender, PaintEventArgs e) break; }
br.Close();∥将二进制读写流关闭 }
3.3 面状Shapefile文件的交互式写入操作
面状Shapefile文件的交互式写入操作主要代码如下:
∥当交互的写入Shapefile文件时,单击代表继续绘制这条记录的图形
private void pictureBox1_MouseDown(object sender, MouseEventArgs e) { pointtemp.X=e.X; pointtemp.Y=e.Y; alist.Add(pointtemp);
if (ShapeType == 5 && tab==1)
{ Private void DrawPolygons();∥绘制面状图形 }
else if(tab==0)
{ Private void DrawPoint();∥绘制下一条记录的起始点 tab=1;
} }
∥当交互的写入Shapefile文件时,双击代表当前记录的图形已绘制完毕 private void pictureBox1_DoubleClick(object sender, EventArgs e) {
if (ShapeType == 5)
{ alist.RemoveAt(alist.Count-1);∥当双击鼠标时,相当于单击了两次,所以需要删除一个重复的坐标
foreach (Point_shape item in alist) { pointtemp.X=item.X; pointtemp.Y=item.Y; break; }
alist.Add(pointtemp);∥添加一个起始坐标,形成一个闭合的平面 polygons.Add(alist);∥把当前记录添加到集合中 tab=0;∥表示绘制下一条记录 } }
∥创建面状Shapefile文件
private void Create_PolygonShp()
{ WriteShpfile(polygons); ∥创建.shp,.shx文件 WriteDbf(polygons.Count);∥创建.dbf文件 }
4 实例验证
本文采用某一研究区域土地利用矢量数据现状图,用来验证编写的系统读写Shapefile 图形文件的功能。
(1)读取土地利用现状图.shp,如图1所示。
(2)创建一个面状Shapefile文件,其图形显示如图2所示。 图1 土地利用现状 图2 面要素 5 结束语
本文首先剖析了Shapefile图形文件的基本结构,结合对该图形文件的深入理解,参考 Shapefile文件的开源代码,在.net环境下利用 Microsoft Visual Studio2015平台开发了可以交互式读写 Shapefile数据格式的 GIS系统,同时给出了读写 Shapefile文件程序所涉及的一些关键代码,加深了读者对Shapefile文件结构的理解,使读者具备了将一定数据结构类型的文件转换为Shapefile文件的基础。主要得出以下两点结论。
(1) 通过某种编程手段,可以将一定格式的图形文件按照我们所期望的方式展现出来,且能够创造出我们所需要的图形文件。
(2) 通过对Shapefile图形文件底层结构的剖析,我们可以彻底的对Shapefile文件进行操作,可以通过编程技术将特定格式的数据图形文件与shapefile图形文件进行互相转换。
由于时间和能力上的,该程序不足之处的地方还有很多,例如读写 Shapefile文件的算法并不是最优,读取速度并不是最快,而且没有考虑多点,多面体等要素类型,在今后的学习当中应该进行多方面的研究。 参考文献
【相关文献】
[1] 张彩丽,刘珍,姚晓巍.DAT(TXT)数据向SHP格式批量转换的方法研究[J].北京测绘,2017(1):95-99.
[2] 常春雷,刘信,马天福,等.多级格网框架下多源矢量空间数据动态存取优化方法[J].科学技术与工程,2018,18(8):85-90.
[3] 朱新铭,李少梅,彭湃.基于Illustrator的Shapefile空间数据读取方法研究[J].测绘与空间地理信息,2016,39(2):100-103.
[4] 武世虎.基于C#.NET的DWG到ShapeFile格式转换程序实现[J].山西大同大学学报(自然科学版),2016,32(1):73-74,79.
[5] 孔帅可,乔保军,付征叶.基于GDAL的ESRI Shapefile格式文件处理[J].计算机时代,2014(8):43-45,47.
[6] 柳佳佳,栾晓岩.ShapeFile格式文件写入方法研究[J].测绘通报,2012(9):90-92. [7] 郑翠玲.空间数据的格式转换研究与实现[J].现代计算机(专业版),2010(9):171-174.
[8] 李学渊,李成尊,赵博.基于ArcGIS Engine的数据文件到Shapefile转换方法及其实现[J].国土资源遥感,2011(3):156-160.
[9] 杨冬.Shapefile图形文件的数据存储格式及读写[J].首都师范大学学报(自然科学版),2010,31(2):4-8,14.
[10] 刘锋,张继贤,李海涛.SHP文件格式的研究与应用[J].测绘科学,2006(6):116-117,8. [11] 刘蕊,梁虹,冯涛等.基于AE的Shapefile和Excel之间数据转换的方法[J].计算机工程与设计,2007(14):3515-3517.
[12] ,赵国成,阚映红.ShapeFile格式数据与地理信息交换格式数据转换[J].测绘科学,2010,35(6):82-83.
[13] 孙晓莉,赵俊三.DXF文件到Shape文件无损转换方法的研究[J].科学技术与工程,2011,11(14):3336-3341.