shp系列(五)——利用C++进行shp文件的写(创建)
发布日期:2022-02-10 11:37:02 浏览次数:40 分类:技术文章

本文共 5991 字,大约阅读时间需要 19 分钟。

之前介绍了shp文件、dbf文件和shx文件的的读取,接下来将分别介绍它们的创建过程。一般来说,读和写的一一对应的,写出的文件就是为了保存数据供以后读取的。写的文件要符合shapefile的标准。之前读取的时候使用的函数是fread,写的函数对应为fwrite,文件为二进制流文件。

建议本博客和之前shp读取的博客一起看!

建议本博客和之前shp读取的博客一起看!

建议本博客和之前shp读取的博客一起看!

 

1.位序little转为big

shp文件中部分参数是big类型,读取的时候读取的big要转化为little,写的时候little转化为big,代码如下:

// 将十进制转换成十六进制,并转成bigint OnChangeByteOrderTenToSixteen(int indata){	int yushu;	int i = 7;	char ss[8];	if (indata == 0) return 0;	while (indata > 0) {		yushu = indata % 16;		char k;		if (yushu > 9)			k = 'a' + yushu - 10;		else			k = '0' + yushu;		ss[i] = k;		indata = indata / 16;		i--;	}	//****进行倒序  	for (int j = 0; j= 'a'&&ss[i] <= 'f')			k = 10 + ss[i] - 'a';		else			k = ss[i] - '0';		num = num * 16 + k;	}	return num;}

2.Shp头文件的创建

Shp头文件参数之前已经介绍很清楚了,按照顺序创建对应类型的变量,赋值,FileCode和FileLength需要转化为big类型。其中FileLength由于刚开始写的时候不确定数值,可以暂时写一个数值(如0或100),后面需要回来修改,因为需要统计所有点的个数和环数等才能得出正确的值。

3.Shp主体信息的创建

主体信息分为记录头和记录信息两部分。

3.1记录头的创建

记录头包括两项,记录的序号RecordNumber和记录长度ContentLength,都是big类型。ContentLength是指本条记录的长度,不包括RecordNumber和ContentLength本身字节数,先暂时写一个值,后面要修改,类似于FileLength。

3.2记录信息的创建

还是以Polygon为例,包括ShapeType,Box[4],NumParts,NumPoints,Parts和Points。各项含义参考shp的读取的博客。

4.FileLength和ContentLength的计算

此二者是shp文件写的最大的难点,需要利用fseek函数(使用方法自行百度),将文件指针移回到FileLength和ContentLength的位置,修改它们的值。每条记录的长度不一样,需要累加每条记录的字节数才能得到所有记录的字节数,然后加上头文件的字节数(100),可以获得FileLength。FileLength和ContentLength的值均为字节数的一半

 

  • 内容长度 = (4 + 4 * 8 + 4 + 4 + NumParts * 4 + NumPoints * 2 * 8)/2
  • 当前总文件量 = (前一条记录时的总文件量 + 当前记录的字节数 + 4(RecordNumber的字节) + 4(ContentLength的字节))/2

5.创建代码

void WriteShp(CString& filename){	//****打开对话框,输入文件名	CFileDialog fDLG(false);	if (fDLG.DoModal() != IDOK)		return;	filename = fDLG.GetPathName();	filename = filename + ".shp";	FILE * m_ShpFile_fp = fopen(filename, "wb");//wmj	if (m_ShpFile_fp == NULL)		return;	//****写shp文件的文件头	int i;	int FileCode = 9994;	int Unused = 0;	int FileLength = 100;    //FileLength是整个shp的,先暂时写一个值,后面要修改	int Version = 1000;      //Version默认1000	int ShapeType = 5;       //记录保存的图形类型	double Xmin = map->GetMapRect().left;	double Ymin = map->GetMapRect().top;	double Xmax = map->GetMapRect().right;	double Ymax = map->GetMapRect().bottom;	double Zmin = 0;	double Zmax = 0;	double Mmin = 0;	double Mmax = 0;	FileCode = OnChangeByteOrderTenToSixteen(FileCode);     //转化为big形式	fwrite(&FileCode, sizeof(int), 1, m_ShpFile_fp);	for (i = 0; i < 5; i++)		fwrite(&Unused, sizeof(int), 1, m_ShpFile_fp);	FileLength = OnChangeByteOrderTenToSixteen(FileLength); //转化为big形式	fwrite(&FileLength, sizeof(int), 1, m_ShpFile_fp);	FileLength = OnChangeByteOrder(FileLength);             //转回little,便于后面修改	fwrite(&Version, sizeof(int), 1, m_ShpFile_fp);	fwrite(&ShapeType, sizeof(int), 1, m_ShpFile_fp);	fwrite(&Xmin, sizeof(double), 1, m_ShpFile_fp);         //边界,上下左右	fwrite(&Ymin, sizeof(double), 1, m_ShpFile_fp);	fwrite(&Xmax, sizeof(double), 1, m_ShpFile_fp);	fwrite(&Ymax, sizeof(double), 1, m_ShpFile_fp);	fwrite(&Zmin, sizeof(double), 1, m_ShpFile_fp); 	fwrite(&Zmax, sizeof(double), 1, m_ShpFile_fp);	fwrite(&Mmin, sizeof(double), 1, m_ShpFile_fp);	fwrite(&Mmax, sizeof(double), 1, m_ShpFile_fp); 	//****写文件头结束	//****写几何信息,包括记录头和记录信息	int count = map->layer->getObjects.size();  //总记录条数	for (int i = 1; i <= RecordNumber; i++) {		//****写记录头		int RecordNumber = i;     //RecordNumber从1开始		RecordNumber = OnChangeByteOrderTenToSixteen(RecordNumber);		fwrite(&id, sizeof(int), 1, m_ShpFile_fp);		int ContentLength = 0;    //ContentLength是这条记录的,不包括RecordNumber和ContentLength本身字节数,先暂时写一个值,后面要修改		ContentLength = OnChangeByteOrderTenToSixteen(ContentLength);		fwrite(&ContentLength, sizeof(int), 1, m_ShpFile_fp);		//****写记录信息		CGeoPolygon* polygon = (CGeoPolygon*)map->layer->objects[i - 1];		int shapeType = 5;		fwrite(&shapeType, sizeof(int), 1, m_ShpFile_fp);  //类型		CRect objectRect = polygon->getObjectRect();		double Box[4] = { objectRect.left,objectRect.right,objectRect.top,objectRect.bottom };		for (int j = 0; j < 4; j++)                        //多边形的边界			fwrite(Box + j, sizeof(double), 1, m_ShpFile_fp);		int NumParts = polygon->circleNum;                 //子环个数		fwrite(&NumParts, sizeof(int), 1, m_ShpFile_fp);		int NumPoints = polygon->getAllPointNum();         //表示构成当前面状目标所包含的坐标点个数		fwrite(&NumPoints, sizeof(int), 1, m_ShpFile_fp);		int *Parts = new int[NumParts];                    //记录了每个子环的起点在Points数组中的起始位置 		*(Parts) = 0;                                      //第一个环的起点在Points数组的位置为0		int temp = 0;		for (int j1 = 0; j1 < NumParts - 1; j1++) {			temp = temp + polygon->circles[j1]->GetSize(); //下一个环的起点的位置=上一个环的起点位置+上一个环的点数			*(Parts + j1 + 1) = temp;                      //每一个环的起点位置存在数组中		}		for (int j2 = 0; j2 < NumParts; j2++)			fwrite(Parts + j2, sizeof(int), 1, m_ShpFile_fp);		for (int j3 = 0; j3 < NumParts; j3++) {            //记录每个环的每一个点			vector
pts = polygon->circles[j3]->pts; for (int m = 0; m < pts.size; m++) { double x = (double)pts[m]->Getx(); double y = (double)pts[m]->Gety(); fwrite(&x, sizeof(double), 1, m_ShpFile_fp); fwrite(&y, sizeof(double), 1, m_ShpFile_fp); } } int temp_CL = 4 + 4 * 8 + 4 + 4 + NumParts * 4 + NumPoints * 2 * 8; //不包含ID和FileLength ContentLength = temp_CL / 2; //ContentLength为字节数的一般 recordLength.push_back(ContentLength); //recordLength是一个vector变量,记录每条记录的长度,供写shx时使用 FileLength = FileLength + temp_CL + 8; //当前总文件量 = 前一条记录的总文件量 + 当前记录的字节数 + 4(RecordNumber) + 4(ContentLength) long offset = temp_CL + 4; //偏移量 fseek(m_ShpFile_fp, -offset, SEEK_CUR); //将文件指针移到ContentLength的位置,修改ContentLength的数字 ContentLength = OnChangeByteOrderTenToSixteen(ContentLength); fwrite(&ContentLength, sizeof(int), 1, m_ShpFile_fp); fseek(m_ShpFile_fp, 0, SEEK_END); //移到文件最后,以继续写下一个 } //****几何信息写的过程结束 //****返回去写FileLength fseek(m_ShpFile_fp, 24, SEEK_SET); //移到FileLength的位置,初始为后移24个字节 FileLength = FileLength / 2; // FileLength为字节数的一半 FileLength = OnChangeByteOrderTenToSixteen(FileLength); fwrite(&FileLength, sizeof(int), 1, m_ShpFile_fp); fclose(m_ShpFile_fp); }

下一篇博客介绍dbf的创建。

转载地址:https://blog.csdn.net/Fan_z_0802/article/details/85168294 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:shp系列(四)——利用C++进行Shx文件的读(打开)
下一篇:shp系列(六)——利用C++进行Dbf文件的写(创建)

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2024年04月06日 19时40分44秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

Leetcode 1175:质数排列(超详细的解法!!!) 2019-04-26
Leetcode 1176:健身计划评估(超详细的解法!!!) 2019-04-26
Leetcode 1177:构建回文串检测(超详细的解法!!!) 2019-04-26
Leetcode 1178:猜字谜(超详细的解法!!!) 2019-04-26
Leetcode 801:使序列递增的最小交换次数(超详细的解法!!!) 2019-04-26
Leetcode 1180:统计只含单一字母的子串(超详细的解法!!!) 2019-04-26
Leetcode 1189:“气球” 的最大数量(超详细的解法!!!) 2019-04-26
Leetcode 1190:反转每对括号间的子串(超详细的解法!!!) 2019-04-26
Leetcode 1191:K 次串联后最大子数组之和(超详细的解法!!!) 2019-04-26
Leetcode 1215:步进数(超详细的解法!!!) 2019-04-26
Leetcode 1143:最长公共子序列(超详细的解法!!!) 2019-04-26
Leetcode 1216:验证回文字符串 III(超详细的解法!!!) 2019-04-26
Leetcode 1217:玩筹码(超详细的解法!!!) 2019-04-26
Leetcode 1218:最长定差子序列(超详细的解法!!!) 2019-04-26
Leetcode 1219:黄金矿工(超详细的解法!!!) 2019-04-26
Leetcode 1220:统计元音字母序列的数目(超详细的解法!!!) 2019-04-26
最大流问题(超详细!!!) 2019-04-26
二分图(超详细!!!) 2019-04-26
2019 力扣杯全国秋季编程大赛:覆盖(超详细的解法!!!) 2019-04-26
Leetcode 1221:分割平衡字符串(超详细的解法!!!) 2019-04-26