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文件的讀取。
