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

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

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

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

1.位序little转为big

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

  1. // 将十进制转换成十六进制,并转成big
  2. int OnChangeByteOrderTenToSixteen(int indata)
  3. {
  4. int yushu;
  5. int i = 7;
  6. char ss[8];
  7. if (indata == 0) return 0;
  8. while (indata > 0) {
  9. yushu = indata % 16;
  10. char k;
  11. if (yushu > 9)
  12. k = 'a' + yushu - 10;
  13. else
  14. k = '0' + yushu;
  15. ss[i] = k;
  16. indata = indata / 16;
  17. i--;
  18. }
  19.  
  20. //****进行倒序
  21. for (int j = 0; j<i + 1; j++) {
  22. ss[j] = '0';
  23. }
  24. int t, temp;
  25. t = ss[0]; ss[0] = ss[6]; ss[6] = t;
  26. t = ss[1]; ss[1] = ss[7]; ss[7] = t;
  27. t = ss[2]; ss[2] = ss[4]; ss[4] = t;
  28. t = ss[3]; ss[3] = ss[5]; ss[5] = t;
  29. for (i = 0; i < 8; i++) {
  30. if (ss[i] != '0') {
  31. temp = i;
  32. break;
  33. }
  34. }
  35. int k;
  36. int num = 0;
  37. for (i = temp; i < 8; i++) {
  38. if (ss[i] >= 'a'&&ss[i] <= 'f')
  39. k = 10 + ss[i] - 'a';
  40. else
  41. k = ss[i] - '0';
  42. num = num * 16 + k;
  43. }
  44. return num;
  45. }

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.创建代码

  1. void WriteShp(CString& filename)
  2. {
  3. //****打开对话框,输入文件名
  4. CFileDialog fDLG(false);
  5. if (fDLG.DoModal() != IDOK)
  6. return;
  7. filename = fDLG.GetPathName();
  8. filename = filename + ".shp";
  9. FILE * m_ShpFile_fp = fopen(filename, "wb");//wmj
  10. if (m_ShpFile_fp == NULL)
  11. return;
  12.  
  13. //****写shp文件的文件头
  14. int i;
  15. int FileCode = 9994;
  16. int Unused = 0;
  17. int FileLength = 100; //FileLength是整个shp的,先暂时写一个值,后面要修改
  18. int Version = 1000; //Version默认1000
  19. int ShapeType = 5; //记录保存的图形类型
  20. double Xmin = map->GetMapRect().left;
  21. double Ymin = map->GetMapRect().top;
  22. double Xmax = map->GetMapRect().right;
  23. double Ymax = map->GetMapRect().bottom;
  24. double Zmin = 0;
  25. double Zmax = 0;
  26. double Mmin = 0;
  27. double Mmax = 0;
  28.  
  29. FileCode = OnChangeByteOrderTenToSixteen(FileCode); //转化为big形式
  30. fwrite(&FileCode, sizeof(int), 1, m_ShpFile_fp);
  31. for (i = 0; i < 5; i++)
  32. fwrite(&Unused, sizeof(int), 1, m_ShpFile_fp);
  33. FileLength = OnChangeByteOrderTenToSixteen(FileLength); //转化为big形式
  34. fwrite(&FileLength, sizeof(int), 1, m_ShpFile_fp);
  35. FileLength = OnChangeByteOrder(FileLength); //转回little,便于后面修改
  36. fwrite(&Version, sizeof(int), 1, m_ShpFile_fp);
  37. fwrite(&ShapeType, sizeof(int), 1, m_ShpFile_fp);
  38. fwrite(&Xmin, sizeof(double), 1, m_ShpFile_fp); //边界,上下左右
  39. fwrite(&Ymin, sizeof(double), 1, m_ShpFile_fp);
  40. fwrite(&Xmax, sizeof(double), 1, m_ShpFile_fp);
  41. fwrite(&Ymax, sizeof(double), 1, m_ShpFile_fp);
  42. fwrite(&Zmin, sizeof(double), 1, m_ShpFile_fp);
  43. fwrite(&Zmax, sizeof(double), 1, m_ShpFile_fp);
  44. fwrite(&Mmin, sizeof(double), 1, m_ShpFile_fp);
  45. fwrite(&Mmax, sizeof(double), 1, m_ShpFile_fp);
  46. //****写文件头结束
  47.  
  48. //****写几何信息,包括记录头和记录信息
  49. int count = map->layer->getObjects.size(); //总记录条数
  50. for (int i = 1; i <= RecordNumber; i++) {
  51. //****写记录头
  52. int RecordNumber = i; //RecordNumber从1开始
  53. RecordNumber = OnChangeByteOrderTenToSixteen(RecordNumber);
  54. fwrite(&id, sizeof(int), 1, m_ShpFile_fp);
  55. int ContentLength = 0; //ContentLength是这条记录的,不包括RecordNumber和ContentLength本身字节数,先暂时写一个值,后面要修改
  56. ContentLength = OnChangeByteOrderTenToSixteen(ContentLength);
  57. fwrite(&ContentLength, sizeof(int), 1, m_ShpFile_fp);
  58.  
  59. //****写记录信息
  60. CGeoPolygon* polygon = (CGeoPolygon*)map->layer->objects[i - 1];
  61. int shapeType = 5;
  62. fwrite(&shapeType, sizeof(int), 1, m_ShpFile_fp); //类型
  63. CRect objectRect = polygon->getObjectRect();
  64. double Box[4] = { objectRect.left,objectRect.right,objectRect.top,objectRect.bottom };
  65. for (int j = 0; j < 4; j++) //多边形的边界
  66. fwrite(Box + j, sizeof(double), 1, m_ShpFile_fp);
  67. int NumParts = polygon->circleNum; //子环个数
  68. fwrite(&NumParts, sizeof(int), 1, m_ShpFile_fp);
  69. int NumPoints = polygon->getAllPointNum(); //表示构成当前面状目标所包含的坐标点个数
  70. fwrite(&NumPoints, sizeof(int), 1, m_ShpFile_fp);
  71. int *Parts = new int[NumParts]; //记录了每个子环的起点在Points数组中的起始位置
  72. *(Parts) = 0; //第一个环的起点在Points数组的位置为0
  73. int temp = 0;
  74. for (int j1 = 0; j1 < NumParts - 1; j1++) {
  75. temp = temp + polygon->circles[j1]->GetSize(); //下一个环的起点的位置=上一个环的起点位置+上一个环的点数
  76. *(Parts + j1 + 1) = temp; //每一个环的起点位置存在数组中
  77. }
  78. for (int j2 = 0; j2 < NumParts; j2++)
  79. fwrite(Parts + j2, sizeof(int), 1, m_ShpFile_fp);
  80.  
  81. for (int j3 = 0; j3 < NumParts; j3++) { //记录每个环的每一个点
  82. vector<CPoint*> pts = polygon->circles[j3]->pts;
  83. for (int m = 0; m < pts.size; m++) {
  84. double x = (double)pts[m]->Getx();
  85. double y = (double)pts[m]->Gety();
  86. fwrite(&x, sizeof(double), 1, m_ShpFile_fp);
  87. fwrite(&y, sizeof(double), 1, m_ShpFile_fp);
  88. }
  89. }
  90.  
  91. int temp_CL = 4 + 4 * 8 + 4 + 4 + NumParts * 4 + NumPoints * 2 * 8; //不包含ID和FileLength
  92. ContentLength = temp_CL / 2; //ContentLength为字节数的一般
  93. recordLength.push_back(ContentLength); //recordLength是一个vector变量,记录每条记录的长度,供写shx时使用
  94. FileLength = FileLength + temp_CL + 8; //当前总文件量 = 前一条记录的总文件量 + 当前记录的字节数 + 4(RecordNumber) + 4(ContentLength)
  95. long offset = temp_CL + 4; //偏移量
  96. fseek(m_ShpFile_fp, -offset, SEEK_CUR); //将文件指针移到ContentLength的位置,修改ContentLength的数字
  97. ContentLength = OnChangeByteOrderTenToSixteen(ContentLength);
  98. fwrite(&ContentLength, sizeof(int), 1, m_ShpFile_fp);
  99. fseek(m_ShpFile_fp, 0, SEEK_END); //移到文件最后,以继续写下一个
  100. }
  101. //****几何信息写的过程结束
  102.  
  103. //****返回去写FileLength
  104. fseek(m_ShpFile_fp, 24, SEEK_SET); //移到FileLength的位置,初始为后移24个字节
  105. FileLength = FileLength / 2; // FileLength为字节数的一半
  106. FileLength = OnChangeByteOrderTenToSixteen(FileLength);
  107. fwrite(&FileLength, sizeof(int), 1, m_ShpFile_fp);
  108. fclose(m_ShpFile_fp);
  109. }

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

shp系列(五)——利用C++进行shp文件的写(创建)的更多相关文章

  1. shp系列(七)——利用C++进行Shx文件的写(创建)

    之前介绍了Shp文件和Dbf的写(创建),最后来介绍一下Shx文件的写(创建).Shx文件是三者之中最简单的一个,原因有两个:第一是Shx文件的头文件与Shp文件的头文件几乎一样(除了FileLeng ...

  2. shp系列(六)——利用C++进行Dbf文件的写(创建)

    上一篇介绍了shp文件的创建,接下来介绍dbf的创建. 推荐结合读取dbf的博客一起看! 推荐结合读取dbf的博客一起看! 推荐结合读取dbf的博客一起看! 1.Dbf头文件的创建 Dbf头文件的结构 ...

  3. python常识系列07-->python利用xlwt写入excel文件

    前言 读书之法,在循序而渐进,熟读而精思.--朱熹 抽空又来写一篇,毕竟知识在于分享! 一.xlwt模块是什么 python第三方工具包,用于往excel中写入数据:(ps:只能创建新表格,不能修改表 ...

  4. 快学Scala 第十五课 (二进制读取文件,写文件,访问目录,序列化)

    二进制读取文件: val file = new File("F:\\scalaWorkspace\\ScalaLearning\\files\\test.txt") val in ...

  5. python 利用 ogr 写入shp文件,数据格式

    python 利用 ogr 写入 shp 文件, 定义shp文件中的属性字段(field)的数据格式为: OFTInteger # 整型 OFTIntegerList # 整型list OFTReal ...

  6. arcgis api 3.x for js 入门开发系列批量叠加 zip 压缩 SHP 图层优化篇(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  7. (数据科学学习手札65)利用Python实现Shp格式向GeoJSON的转换

    一.简介 Shp格式是GIS中非常重要的数据格式,主要在Arcgis中使用,但在进行很多基于网页的空间数据可视化时,通常只接受GeoJSON格式的数据,众所周知JSON(JavaScript Obje ...

  8. 读CSV文件并写arcgis shp文件

    一.在这里我用到的csv文件是包含x,y坐标及高程.降雨量数据的文件.如下图所示. 二.SF简介 简单要素模型(Simple Feature,SF),是 OGC 国际组织定义的面向对象的矢量数据模型. ...

  9. JVM系列五:JVM监测&工具

    JVM系列五:JVM监测&工具[整理中]  http://www.cnblogs.com/redcreen/archive/2011/05/09/2040977.html 前几篇篇文章介绍了介 ...

  10. Hexo系列(五) 撰写文章

    在利用 Hexo 框架搭建一个属于我们自己的博客网站后,下面我们就来谈谈怎样在网站上书写我们的第一篇博客吧 一.创建文章 在站点文件夹中打开 git bash,输入如下命令创建文章,其中 title ...

随机推荐

  1. C#:winform项目在win7,xp32位和64位都能运行

    vs中项目配置管理器活动解决方案平台选择X86平台.

  2. SLAM: Ubuntu14.04_Kylin安装ROS-Indigo

    参考连接:ROS-Indigo版在Ubuntu上的安装第一步: 软件源配置 1. 增加下载源(增加ubuntu版的ros数据仓库,即下载源)(通用指令适合任何版本的ros) sudo sh -c 'e ...

  3. centos6.5 安装Python3.6.0

      首先安装python3.6可能使用的依赖 # yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel ...

  4. jmeter3.1 压测

    压测目标:error 为0,线程起到250,服务器配置达到最大 一.Jmeter3.1 压测 JMeter3.1提供一个用于生成HTML页面格式图形化报告的扩展模块.该模块支持通过两种方式生成多维度图 ...

  5. eas之树

    如何设置树的深度(即树总共有几级) // 设置树的深度为3,即树包括0.1.2三级结点 table.getTreeColumn().setDepth(3); 如何设置树的方向 树的方向包括两种:自上向 ...

  6. 【剑指Offer】52、正则表达式匹配

      题目描述:   请实现一个函数用来匹配包括'.'和'*'的正则表达式.模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次). 在本题中,匹配是指字符串的所有字符匹 ...

  7. [tyvj1935 Poetize3]导弹防御塔 (二分图多重匹配)

    传送门 Description Freda控制着N座可以发射导弹的防御塔.每座塔都有足够数量的导弹,但是每座塔每次只能发射一枚.在发射导弹时,导弹需要T1秒才能从防御塔中射出,而在发射导弹后,发射这枚 ...

  8. [bzoj1860 ZJOI2006] 超级麻将 (线性dp)

    传送门 Description Input 第一行一个整数N(N<=100),表示玩了N次超级麻将. 接下来N行,每行100个数a1..a100,描述每次玩牌手中各种牌的数量.ai表示数字为i的 ...

  9. mkdir:创建目录

    mkdir:命令 1.命令详解 [功能说明] mkdir命令是“make  directories”中每个单词的粗体字母组合而成,其功能是创建目录,默认情况下,如果要创建的目录已经存在,则会提示此文件 ...

  10. open-ldap schema (2)

    schema介绍及用途 schema 是OpenLDAP 软件的重要组成部分,主要用于控制目录树中各种条目所拥有的对象类以及各种属性的定义,并通过自身内部规范机制限定目录树条目所遵循的逻辑结构以及定义 ...