之前介紹了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的創建。
