shp系列(五)——利用C++進行shp檔案的寫(建立)
之前介紹了shp檔案、dbf檔案和shx檔案的的讀取,接下來將分別介紹它們的建立過程。一般來說,讀和寫的一一對應的,寫出的檔案就是為了儲存資料供以後讀取的。寫的檔案要符合shapefile的標準。之前讀取的時候使用的函式是fread,寫的函式對應為fwrite,檔案為二進位制流檔案。
建議本部落格和之前shp讀取的部落格一起看!
建議本部落格和之前shp讀取的部落格一起看!
建議本部落格和之前shp讀取的部落格一起看!
1.位序little轉為big
shp檔案中部分引數是big型別,讀取的時候讀取的big要轉化為little,寫的時候little轉化為big,程式碼如下:
// 將十進位制轉換成十六進位制,並轉成big int 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<i + 1; j++) { ss[j] = '0'; } int t, temp; 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; for (i = 0; i < 8; i++) { if (ss[i] != '0') { temp = i; break; } } int k; int num = 0; for (i = temp; i < 8; i++) { if (ss[i] >= '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<CPoint*> 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的建立。