shp系列(二)——利用C++进行shp文件的读(打开)
1.各数据类型及其字节数
BYTE 1; char 1; short 2; int 4; double 8;
2.位序big和little及其转换
对于位序是big的数据我们在读取时要小心。通常,数据的位序都是Little,但在有些情况下可能会是big,二者的区别在于它们位序的顺序相反。一个位序为big的数据,如果我们想得到它的真实数值,需要将它的位序转换成Little即可。转换原理就是交换字节顺序,下面是转换代码(big->little):
int OnChangeByteOrder(int indata)
{
char ss[9];
char ee[8];
unsigned long val = unsigned long(indata);
ultoa(val, ss, 16);// 将十六进制的数(val)转到一个字符串(ss)中,itoa(val,ss,16);
int i;
int length = strlen(ss);
if (length != 8){
for (i = 0; i<8 - length; i++)
ee[i] = '0';
for (i = 0; i<length; i++)
ee[i + 8 - length] = ss[i];
for (i = 0; i<8; i++)
ss[i] = ee[i];
}
//****** 进行倒序
int t;
t = ss[0]; ss[0] = ss[6]; ss[6] = t;
t = ss[1]; ss[1] = ss[7]; ss[7] = t;
t = ss[2]; ss[2] = ss[4]; ss[4] = t;
t = ss[3]; ss[3] = ss[5]; ss[5] = t; //****** 将存有十六进制数 (val) 的字符串 (ss) 中的十六进制数转成十进制数
int value = 0;
for (i = 0; i<8; i++){
int k;
if (ss[i] == 'a' || ss[i] == 'b' || ss[i] == 'c' || ss[i] == 'd' || ss[i] == 'e' || ss[i] == 'f')
k = 10 + ss[i] - 'a';
else
k = ss[i] - '0';
value = value + int(k*(int)pow((DOUBLE)16, 7 - i));
}
return(value);
}
3.shp文件的基本构成
shp文件,由于包含最主要的数据坐标数据,也叫主文件、坐标文件,由文件头和实体信息构成
文件头是关于主文件的基本描述,包括版本号、文件长度等信息;实体信息是每一个图形的具体信息,也包括记录头和坐标内容。
4.shp头文件
头文件的结构如下,下面两表含义相同。
有值得说明的几点:
- 前9项都是int类型,位序是big;后8项都是double类型,位序是little,所以头文件的大小为 9 * 4 + 8 * 8 = 100。
- FileCode固定为9994,Version固定为1000。
- FileLength是指总文件长度,大小为 头文件长度 + (记录头长度 + 记录内容长度) * 记录数
- ShapeType为该shp文件图形的类型(1代表Point,3代表Polyline,5代表Polygon),更多的类型参考说明文件,目前每个 shape 文件被限定为只能包含上述类型的一种。
- Xmin,Ymin,Xmax,Ymax是空间范围的边界。
- Zmin,Zmax,Mmin,Mmxa一般用不到。
5.shp实体信息
shp实体信息分为记录头和记录内容。
5.1记录头
- RecordNumber代表当前记录的编号。每个记录的记录头都储存着记录号(Record Number)和记录内容长度(Content
Length),位序都是big,类型是int,记录头的长度是 8 个字节,记录号从 1 开始。 - ContentLength代表当前记录内容的长度,不包括RecordNumber和ContentLength本身的长度,下面将以面状要素为例计算ContentLength的大小。
5.2记录内容
- ShapeType是指多边形的类型,Polygon是5。
- Box是double类型的数组,包含多边形的4个边界。
- NumParts代表Polygon的子环数,一个Polygon可能由多个环。
- Parts是一个int类型数组,数组元素个数等于NumParts,每个元素记录了每个子环的坐标在Points数组中的起始位置。
- Points记录Polygon的所有点,是Point类型,Point类型包含2个double类型(x,y均为double)。
不同的要素类型的记录内容不一样,本次以面状要素为例(Polygon,ShapeType为5),面状要素的记录内容如下:
所以一条记录(Polygon)的ContentLength(字节数)大小为:
4 + 4 * 8 + 4 + 4 + NumParts * 4 +
NumPoints * 2 * 8 ,不包括RecordNumber和ContentLength本身的长度,ContentLength实际值为字节数的一半,后面FileLength也是字节数的一半。所以ContentLength = ContentLength/2。
6.shp读取的C++函数代码:
void readShp(void)
{
//****打开坐标文件
CFileDialog fDLG(true);
if (fDLG.DoModal() != IDOK)
return;
filename = fDLG.GetPathName();
FILE* m_ShpFile_fp = fopen(filename, "rb");
if (m_ShpFile_fp == NULL) {
MessageBox("Open File Failed");
exit(0);
} CGeoMap* map = new CGeoMap(); //创建地图对象
CGeoLayer* layer = new CGeoLayer();//新建图层 //****定义读取坐标文件头的变量
int i;
int FileCode = -1;
int Unused = -1;
int FileLength = -1;
int Version = -1;
int ShapeType = -1;
double Xmin;
double Ymin;
double Xmax;
double Ymax;
double Zmin;
double Zmax;
double Mmin;
double Mmax; //****读取坐标头文件
fread(&FileCode, sizeof(int), 1, m_ShpFile_fp); //从m_ShpFile_fp里面的值读到Filecode里面去,每次读一个int型字节的长度,读取一次
FileCode = OnChangeByteOrder(FileCode); //将读取的FileCode的值转化为十进制的数
for (i = 0; i<5; i++)
fread(&Unused, sizeof(int), 1, m_ShpFile_fp);
fread(&FileLength, sizeof(int), 1, m_ShpFile_fp);//读取FileLength
FileLength = OnChangeByteOrder(FileLength); //将FileLength转化为十进制的数
fread(&Version, sizeof(int), 1, m_ShpFile_fp); //读取Version的值
fread(&ShapeType, sizeof(int), 1, m_ShpFile_fp);//读取ShapeType的值
fread(&Xmin, sizeof(double), 1, m_ShpFile_fp);//从m_ShpFile_fp里面的值读到Xmin里面去,每次读取一个double型字节长度,读取一次
fread(&Ymin, sizeof(double), 1, m_ShpFile_fp);//从m_ShpFile_fp里面的值读到Ymin里面去,每次读取一个double型字节长度,读取一次
fread(&Xmax, sizeof(double), 1, m_ShpFile_fp);//从m_ShpFile_fp里面的值读到Xmax里面去,每次读取一个double型字节长度,读取一次
fread(&Ymax, sizeof(double), 1, m_ShpFile_fp);//从m_ShpFile_fp里面的值读到Ymax里面去,每次读取一个double型字节长度,读取一次 CRect rect(Xmin, Ymin, Xmax, Ymax);
layer->setRect(rect); //设置图层的边界 fread(&Zmin, sizeof(double), 1, m_ShpFile_fp);//从m_ShpFile_fp里面的值读到Zmin里面去,每次读取一个double型字节长度,读取一次
fread(&Zmax, sizeof(double), 1, m_ShpFile_fp);//从m_ShpFile_fp里面的值读到Zmax里面去,每次读取一个double型字节长度,读取一次
fread(&Mmin, sizeof(double), 1, m_ShpFile_fp);//从m_ShpFile_fp里面的值读到Mmin里面去,每次读取一个double型字节长度,读取一次
fread(&Mmax, sizeof(double), 1, m_ShpFile_fp);//从m_ShpFile_fp里面的值读到Mmax里面去,每次读取一个double型字节长度,读取一次
//****读取坐标文件头的内容结束 //****读取面状目标的实体信息
int RecordNumber;
int ContentLength;
switch (ShapeType) {
case 5: { //polygon
while ((fread(&RecordNumber, sizeof(int), 1, m_ShpFile_fp) != 0)) { //从第一个开始循环读取每一个Polygon
fread(&ContentLength, sizeof(int), 1, m_ShpFile_fp); //读取ContentLength
RecordNumber = OnChangeByteOrder(RecordNumber); //转换为10进制
ContentLength = OnChangeByteOrder(ContentLength);
//****记录头读取结束 //****读取记录内容
int shapeType; //当前Polygon几何类型
double Box[4]; //当前Polygon的上下左右边界
int NumParts; //当前Polygon所包含的子环的个数
int NumPoints; //当前Polygon所包含的点的个数
int *Parts; //当前Polygon所包含的子环的起点在NumPoints的编号
fread(&shapeType, sizeof(int), 1, m_ShpFile_fp);
for (i = 0; i < 4; i++) //读Box
fread(Box + i, sizeof(double), 1, m_ShpFile_fp);
fread(&NumParts, sizeof(int), 1, m_ShpFile_fp); //表示构成当前Polygon的子环的个数
fread(&NumPoints, sizeof(int), 1, m_ShpFile_fp);//表示构成当前Polygon所包含的坐标点个数
Parts = new int[NumParts]; //记录了每个子环的坐标在Points数组中的起始位置
for (i = 0; i < NumParts; i++)
fread(Parts + i, sizeof(int), 1, m_ShpFile_fp); int pointNum;
CGeoPolygon* polygon = new CGeoPolygon();
polygon->circleNum = NumParts; //设定多边形的点数 //****读取面中子环
for (i = 0; i < NumParts; i++) {
if (i != NumParts - 1) pointNum = Parts[i + 1] - Parts[i];//每个子环的长度 ,非最后一个环
else pointNum = NumPoints - Parts[i]; //最后一个环
double* PointsX = new double[pointNum]; //用于存放读取的点坐标x值;
double* PointsY = new double[pointNum]; //用于存放y值
CGeoPolyline* polyline = new CGeoPolyline(); //每个环实际上就是首尾坐标相同的Polyline
polyline->circleID = i; for (int j = 0; j < pointNum; j++) {
fread(PointsX + j, sizeof(double), 1, m_ShpFile_fp);
fread(PointsY + j, sizeof(double), 1, m_ShpFile_fp);
double a = PointsX[j];
double b = PointsY[j];
CPoint* point = new CPoint(a, b);
polyline->AddPoint(point); //把该子环所有的点添加到一条链上
} CPoint pt1 = polyline->pts[0]->GetCPoint();
CPoint pt2 = polyline->pts[polyline->pts.size() - 1]->GetCPoint();
if (pt1 != pt2) { //若首位点不一致
CString str;
str.Format("%d数据首尾点不一致", RecordNumber);
polyline->pts.push_back(p1);
}
polygon->AddCircle(polyline); //将polyline链添加到对应polygon中
delete[] PointsX;
delete[] PointsY;
}
//****一个面的某个子环循环结束,同时该子环已加入到polygon layer->AddObject((CGeoObject*)polygon); //将该polygon加入到图层中
delete[] Parts;
}
map->AddLayer(layer);
}
break;
case 1://point
break;
case 3://polyline
break;
default:
break;
}
}
下一篇博客介绍dbf文件的读取。
shp系列(二)——利用C++进行shp文件的读(打开)的更多相关文章
- shp系列(四)——利用C++进行Shx文件的读(打开)
1.shx文件的基本情况 shx文件又叫索引文件,主要包含坐标文件的索引信息,文件中每个记录包含对应的坐标文件记录距离坐标文件的初始位置的偏移量.通过索引文件可以很方便地在坐标文件中定位到指定目标的坐 ...
- shp系列(三)——利用C++进行DBF文件的读(打开)
1.DBF文件要点 DBF文件又叫属性文件,也叫dBASE文件,文件后缀是.dbf,实际上ArcGIS打开后的属性表就是DBF的信息.DBF文件遵循以下几个条件: 每个要素在表中必须要包含一个与之相对 ...
- BizTalk开发系列(二) "Hello World" 程序搬运文件
我们在<QuickLearn BizTalk系列之"Hello World">里讲到了如何快速的开发第一个BizTalk 应用程序.现在我们来讲一下如何把这个程序改成用 ...
- python常识系列07-->python利用xlwt写入excel文件
前言 读书之法,在循序而渐进,熟读而精思.--朱熹 抽空又来写一篇,毕竟知识在于分享! 一.xlwt模块是什么 python第三方工具包,用于往excel中写入数据:(ps:只能创建新表格,不能修改表 ...
- shp系列(七)——利用C++进行Shx文件的写(创建)
之前介绍了Shp文件和Dbf的写(创建),最后来介绍一下Shx文件的写(创建).Shx文件是三者之中最简单的一个,原因有两个:第一是Shx文件的头文件与Shp文件的头文件几乎一样(除了FileLeng ...
- shp系列(一)——利用C++进行shp文件的读(打开)与写(创建)开言
博客背景和目的 最近在用C++写一个底层的东西,需要读取和创建shp文件.虽然接触shp文件已经几年了,但是对于shp文件内到底包含什么东西一直是一知半解.以前使用shp文件都是利用软件(如ArcGI ...
- python 利用 ogr 写入shp文件,数据格式
python 利用 ogr 写入 shp 文件, 定义shp文件中的属性字段(field)的数据格式为: OFTInteger # 整型 OFTIntegerList # 整型list OFTReal ...
- (数据科学学习手札65)利用Python实现Shp格式向GeoJSON的转换
一.简介 Shp格式是GIS中非常重要的数据格式,主要在Arcgis中使用,但在进行很多基于网页的空间数据可视化时,通常只接受GeoJSON格式的数据,众所周知JSON(JavaScript Obje ...
- arcgis api 3.x for js 入门开发系列批量叠加 zip 压缩 SHP 图层优化篇(附源码下载)
前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...
随机推荐
- mssql server 2005自动备份数据库
(转) (1)启动[sql server Management Studio],在[对象资源管理器]窗口里选择[管理]——[维护计划]选项. 2)右击[维护计划],在弹出的快捷菜单里选择[维护计划向导 ...
- (转)基于Metronic的Bootstrap开发框架经验总结(6)--对话框及提示框的处理和优化
http://www.cnblogs.com/wuhuacong/p/4775282.html 在各种Web开发过程中,对话框和提示框的处理是很常见的一种界面处理技术,用得好,可以给用户很好的页面体验 ...
- 深入了解Spring Boot 核心注解原理
SpringBoot目前是如火如荼,所以今天就跟大家来探讨下SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot为什么不需要XML,达到 ...
- 区分escape、encodeURI和encodeURIComponent
一.escape和它们不是同一类 简单来说,escape是对字符串(string)进行编码(而另外两种是对URL),作用是让它们在所有电脑上可读.编码之后的效果是%XX或者%uXXXX这种形式.其中 ...
- 37.query string、_all metadata
主要知识点 1.query string基础语法 2._all metadata的理解 一.query string基础语法 1.GET /test_index/test_type/_sear ...
- Nginx面试中最常见的18道题 抱佛脚必备
Nginx的并发能力在同类型网页服务器中的表现,相对而言是比较好的,因此受到了很多企业的青睐,我国使用Nginx网站的知名用户包括腾讯.淘宝.百度.京东.新浪.网易等等.Nginx是网页服务器运维人员 ...
- ro的session
1.需要session管理器,根据需要选择: 2.需要一个login和logout的服务.注意:设定TRORemobteDataMedule.sessionmanager,但要设定TRORemobte ...
- 【例题4-2 uva489】Hangman Judge
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 水题. 中间就赢了算赢.(重复说,算错 [代码] #include <bits/stdc++.h> using name ...
- Centos6.4建立本地yum源
https://jingyan.baidu.com/article/e9fb46e1aed8207521f7662c.html
- [bzoj1195][HNOI2006]最短母串_动态规划_状压dp
最短母串 bzoj-1195 HNOI-2006 题目大意:给一个包含n个字符串的字符集,求一个字典序最小的字符串使得字符集中所有的串都是该串的子串. 注释:$1\le n\le 12$,$1\le ...