hapefile格式說明及讀寫代碼示例
Shape files 數據說明
Shape files 是 ESRI 提供的一種矢量數據格式,它沒有拓撲信息,一個 Shape files 由一組文件組成,其中必要的基本文件包括座標文件( .shp )、索引文件( .shx )和屬性文件( .dbf )三個文件。
座標文件的結構說明
座標文件 (.shp) 用於記錄空間座標信息。它由頭文件和實體信息兩部分構成(如圖 2.1 所示)。
坐標文件的文件頭
座標文件的文件頭是一個長度固定 (100 bytes) 的記錄段,一共有 9 個 int 型和 7 個 double 型數據,主要記錄內容見表 2.2 。
文件頭 |
|
記錄頭 |
記錄內容 |
記錄頭 |
記錄內容 |
記錄頭 |
記錄內容 |
記錄頭 |
記錄內容 |
圖 2.1 座標文件的結構
起始位置 |
名稱 |
數值 |
類型 |
位序 |
0 |
File Code |
9994 |
Integer |
big |
4 |
Unused |
0 |
Integer |
big |
8 |
Unused |
0 |
Integer |
big |
12 |
Unused |
0 |
Integer |
big |
16 |
Unused |
0 |
Integer |
big |
20 |
Unused |
0 |
Integer |
big |
24 |
文件長度 |
文件的實際長度 |
Integer |
big |
28 |
版本號 |
1000 |
Integer |
Little |
32 |
幾何類型 |
表示這個 Shapefile 文件所記錄的空間數據的幾何類型 |
Integer |
Little |
36 |
Xmin |
空間數據所佔空間範圍的 X 方向最小值 |
Double |
Little |
44 |
Ymin |
空間數據所佔空間範圍的 Y 方向最小值 |
Double |
Little |
52 |
Xmax |
空間數據所佔空間範圍的 X 方向最大值 |
Double |
Little |
60 |
Ymax |
空間數據所佔空間範圍的 Y 方向最大值 |
Double |
Little |
68* |
Zmin |
空間數據所佔空間範圍的 Z 方向最小值 |
Double |
Little |
76* |
Zmax |
空間數據所佔空間範圍的 Z 方向最大值 |
Double |
Little |
84* |
Mmin |
最小 Measure 值 |
Double |
Little |
92* |
Mmax |
最大 Measure 值 |
Double |
Little |
表 2.2shapefiles 頭文件表
注:最後 4 個加星號特別標示的四個數據只有當這個 Shapefile 文件包含 Z 方向 座標或者具有 Measure 值時纔有值,否則爲 0.0 。所謂 Measure 值,是用於存儲需要的 附加數據,可以用來記錄各種數據,例如權值、道路長度等信息。
位序
細心的讀者會注意到表 2.2 中的數值的位序有 Little 和 big 的區別,對於位序是 big 的數據我們在讀取時要小心。通常,數據的位序都是 Little ,但在有些情況下可能會是 big ,二者的區別在於它們位序的順序相反。一個位序爲 big 的數據,如果我們想得到它的真實數值,需要將它的位序轉換成 Little 即可。轉換原理非常簡單,就是交換字節順序,下面是作者實現的在兩者間進行轉換的程序,代碼如下:
// 位序轉換程序
unsigned long OnChangeByteOrder (int indata)
{
char ss[8];
char ee[8];
unsigned long val = unsigned long(indata);
_ultoa( val, ss, 16 );// 將十六進制的數 (val) 轉到一個字符串 (ss) 中
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;
CString mass;
mass=ss[i];
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
sscanf(mass,"%d",&k);
value=value+int(k*pow(16,7-i));
}
return (value);
}
Shapefile 文件支持的幾何類型( ShapeType )
Shapefile 文件所支持的幾何類型如表 2.3 所示:
編號 |
幾何類型 |
0 |
Null Shape (表示這個 Shapefile 文件不含座標) |
1 |
Point (表示 Shapefile 文件記錄的是點狀目標,但不是多點) |
3 |
PolyLine (表示 Shapefile 文件記錄的是線狀目標) |
5 |
Polygon (表示 Shapefile 文件記錄的是面狀目標) |
8 |
MultiPoint (表示 Shapefile 文件記錄的是多點,即點集合) |
11 |
PointZ (表示 Shapefile 文件記錄的是三維點狀目標) |
13 |
PolyLineZ (表示 Shapefile 文件記錄的是三維線狀目標) |
15 |
PolygonZ (表示 Shapefile 文件記錄的是三維面狀目標) |
18 |
MultiPointZ (表示 Shapefile 文件記錄的是三維點集合目標) |
21 |
PointM (表示含有 Measure 值的點狀目標) |
23 |
PolyLineM (表示含有 Measure 值的線狀目標) |
25 |
PolygonM (表示含有 Measure 值的面狀目標) |
28 |
MultiPointM (表示含有 Measure 值的多點目標) |
31 |
MultiPatch (表示複合目標) |
表 2.3shapefiles 文件支持的幾何類型
對於一個不是記錄 Null Shape 類型的 Shapefile 文件,它所記錄的空間目標的幾何類型必須一致,不能在一個 Shapefile 文件中同時記錄兩種不同類型的幾何目標。
讀取座標文件( .shp )的文件頭的代碼 如下:
void OnReadShp ( CString ShpFileName )
{
FILE* m_ShpFile_fp; //****Shp 文件指針
// 打開座標文件
if((m_ShpFile_fp=fopen(ShpFileName,"rb"))==NULL)
{
return;
}
// 讀取座標文件頭的內容開始
int FileCode;
int Unused;
int FileLength;
int Version;
int ShapeType;
double Xmin;
double Ymin;
double Xmax;
double Ymax;
double Zmin;
double Zmax;
double Mmin;
double Mmax;
fread(&FileCode, sizeof(int), 1,m_ShpFile_fp);
FileCode = OnChangeByteOrder(FileCode);
for(i=0;i<5;i++)
fread(&Unused,sizeof(int), 1,m_ShpFile_fp);
fread(&FileLength, sizeof(int), 1,m_ShpFile_fp);
FileLength = OnChangeByteOrder(FileLength);
fread(&Version, sizeof(int), 1,m_ShpFile_fp);
fread(&ShapeType, sizeof(int), 1,m_ShpFile_fp);
fread(&Xmin, sizeof(double),1,m_ShpFile_fp);
fread(&Ymin, sizeof(double),1,m_ShpFile_fp);
fread(&Xmax, sizeof(double),1,m_ShpFile_fp);
fread(&Ymax, sizeof(double),1,m_ShpFile_fp);
fread(&Zmin, sizeof(double),1,m_ShpFile_fp);
fread(&Zmax, sizeof(double),1,m_ShpFile_fp);
fread(&Mmin, sizeof(double),1,m_ShpFile_fp);
fread(&Mmax, sizeof(double),1,m_ShpFile_fp);
// 讀取座標文件頭的內容結束
// 根據幾何類型讀取實體信息
}
實體信息的內容
實體信息負責記錄座標信息,它以記錄段爲基本單位,每一個記錄段記錄一個地理實體目標的座標信息,每個記錄段分爲記錄頭和記錄內容兩部分。
記錄頭的內容包括記錄號( Record Number )和座標記錄長度 (Content Length) 兩個記錄項。它們的位序都是 big 。記錄號( Record Number )和座標記錄長度 (Content Length) 兩個記錄項都是 int 型,並且 shapefile 文件中的記錄號都是從 1 開始的。
記錄內容包括目標的幾何類型( ShapeType )和具體的座標記錄 (X 、 Y) ,記錄內容因要素幾何類型的不同其具體的內容及格式都有所不同。下面分別介紹點狀目標( Point )、線狀目標( PolyLine )和麪狀目標( Polygon )三種幾何類型的 .shp 文件的記錄內容:
點狀目標
shapefile 中的點狀目標由一對 X 、 Y 座標構成,座標值爲雙精度型( double )。點狀目標的記錄內容如表 2.4 :
記錄項 |
數值 |
數據類型 |
長度 |
個數 |
位序 |
幾何類型( ShapeType) |
1 (表示點狀目標) |
int 型 |
4 |
1 |
Little |
X 方向座標 |
X 方向座標值 |
double 型 |
8 |
1 |
Little |
Y 方向座標 |
Y 方向座標值 |
double 型 |
8 |
1 |
Little |
下面是 讀取點狀目標的記錄內容的代碼:
OnReadPointShp(CString ShpFileName)
{
// 打開座標文件
……
// 讀取座標文件頭的內容開始
……
// 讀取點狀目標的實體信息
int RecordNumber;
int ContentLength;
int num =0;
while((fread(&RecordNumber, sizeof(int), 1,ShpFile_fp)!=0))
{
num++;
fread(&ContentLength,sizeof(int), 1,ShpFile_fp);
RecordNumber = OnChangeByteOrder(RecordNumber);
ContentLength = OnChangeByteOrder(ContentLength);
int shapeType;
double x;
double y;
fread(&shapeType, sizeof(int), 1,ShpFile_fp);
fread(&x, sizeof(double), 1,ShpFile_fp);
fread(&y, sizeof(double), 1,ShpFile_fp);
}
}
線狀目標
shapefile 中的線狀目標是由一系列點座標串構成,一個線目標可能包括多個子線段,子線段之間可以是相離的,同時子線段之間也可以相交。 Shapefile 允許出現多個座標完全相同的連續點,當讀取文件時一定要注意這種情況,但是不允許出現某個退化的、長度爲 0 的子線段出現。線狀目標的記錄內容如表 2.5 :
記錄項 |
數值 |
數據類型 |
長度 |
個數 |
位序 |
幾何類型( ShapeType ) |
3 (表示線狀目標) |
int 型 |
4 |
1 |
Little |
座標範圍( Box ) |
表示當前線目標的座標範圍 |
double 型 |
32 |
4 |
Little |
子線段個數( NumParts ) |
表示構成當前線目標的子線段的個數 |
int 型 |
4 |
1 |
Little |
座標點數( NumPoints ) |
表示構成當前線目標所包含的座標點個數 |
int 型 |
4 |
1 |
Little |
Parts 數組 |
記錄了每個子線段的座標在 Points 數組中的起始位置 |
int 型 |
4×NumParts |
NumParts |
Little |
Points 數組 |
記錄了所有的座標信息 |
Point 型 |
根據點個數來確定 |
NumPoints |
Little |
表 2.5 線狀目標的記錄內容
具體的數據結構如下:
PolyLine
{
Double[4] Box // 當前線狀目標的座標範圍
Integer NumParts // 當前線目標所包含的子線段的個數
Integer NumPoints // 當前線目標所包含的頂點個數
Integer[NumParts] Parts // 每個子線段的第一個座標點在 Points 的位置
Point[NumPoints] Points // 記錄所有座標點的數組
}
這些記錄項的具體含義如下:
Box 記錄了當前的線目標的座標範圍,它是一個 double 型的數組,按照 Xmin 、 Ymin 、 Xmax 、 Ymax 的順序記錄了座標範圍;
NumParts 記錄了當前線目標所包含的子線段的個數;
NumPoints 記錄了當前線目標的座標點總數;
Parts 記錄了每個子線段的第一個座標點在座標數組 points 中的位置,以便讀取數據;
Points 是用於存放當前線目標的 X 、 Y 座標的數組。
下面是讀取線狀目標的記錄內容的代碼:
OnReadLineShp(CString ShpFileName)
{
// 打開座標文件
……
// 讀取座標文件頭的內容開始
……
// 讀取線狀目標的實體信息
int RecordNumber;
int ContentLength;
int num =0;
while((fread(&RecordNumber, sizeof(int), 1,ShpFile_fp)!=0))
{
fread(&ContentLength,sizeof(int), 1,ShpFile_fp);
RecordNumber = OnChangeByteOrder (RecordNumber);
ContentLength = OnChangeByteOrder (ContentLength);
int shapeType;
double Box[4];
int NumParts;
int NumPoints;
int *Parts;
fread(&shapeType, sizeof(int), 1,ShpFile_fp); // 讀 Box
for(i=0;i<4;i++)
fread(Box+i, sizeof(double),1,ShpFile_fp); // 讀 NumParts 和 NumPoints
fread(&NumParts, sizeof(int), 1,ShpFile_fp);
fread(&NumPoints, sizeof(int), 1,ShpFile_fp); // 讀 Parts 和 Points
Parts = new int[NumParts];
for(i=0;i<NumParts;i++)
fread(Parts+i, sizeof(int), 1,ShpFile_fp);
int pointNum;
for(i=0;i<NumParts;i++)
{
if(i!=NumParts-1)
pointNum =Parts[i+1]-Parts[i];
else
pointNum =NumPoints-Parts[i];
double *PointsX;
double *PointsY;
PointsX =new double[pointNum];
PointsY =new double[pointNum];
for(j=0;j<pointNum;j++)
{
fread(PointsX+j, sizeof(double),1,ShpFile_fp);
fread(PointsY+j, sizeof(double),1,ShpFile_fp);
}
delete[] PointsX;
delete[] PointsY;
}
delete[] Parts;
}
}
面狀目標
shapefile 中的面狀目標是由多個子環構成,每個子環是由至少四個頂點構成的封閉的、無自相交現象的環。對於含有島的多邊形,構成它的環有內外環之分,每個環的頂點的排列順序或者方向說明了這個環到底是內環還是外環。一個內環的頂點是按照逆時針順序排列的;而對於外環,它的頂點排列順序是順時針方向。如果一個多邊形只由一個環構成,那麼它的頂點排列順序肯定是順時針方向。
每條多邊形記錄的數據結構與線目標的數據結構完全相同,
Polygon
{
Double[4] Box // 當前面狀目標的座標範圍
Integer NumParts // 當前面目標所包含的子環的個數
Integer NumPoints // 構成當前面狀目標的所有頂點的個數
Integer[NumParts] Parts // 每個子環的第一個座標點在 Points 的位置
Point[NumPoints] Points // 記錄所有座標點的數組
}
對於一個 shapefile 中的多邊形,它必須滿足下面三個條件:
構成多邊形的每個子環都必須是閉合的,即每個子環的第一個頂點跟最後一個頂點是同一個點;
每個子環在 Points 數組中的排列順序並不重要,但每個子環的頂點必須按照一定的順序連續排列;
存儲在 shapefile 中的多邊形必須是乾淨的。所謂一個乾淨的多邊形,它必須滿足兩點:
沒有自相交現象。這就要求任何一個子環不能跟其它的子環相交,共線的現 象也將被當作相交。但是允許兩個子環的頂點重合;
對於一個不含島的多邊形或者是含島的多邊形的外環,它們的頂點排列順序必須是順時針方向;而對於內環,它的排列順序必須是逆時針方向。所謂的 “ 髒多邊形 ” 就是指頂點排列順序爲順時針的內環。
圖 2.2 中的多邊形是一個典型的例子。這個多邊形包括一個島,所有頂點的個數爲 8 。 NumParts 等於 2 , NumPoints 等於 10 。請注意內環(島)的頂點的排列順序是逆時針的(如圖 2.3 所示)
表 2.6 :面狀目標的記錄內容
記錄項 |
數值 |
數據類型 |
長度 |
個數 |
位序 |
幾何類型( ShapeType ) |
5 (表示面狀目標) |
int 型 |
4 |
1 |
Little |
座標範圍( Box ) |
表示當前面目標的座標範圍 |
double 型 |
32 |
4 |
Little |
子線段個數( NumParts ) |
表示構成當前面狀目標的子環的個數 |
int 型 |
4 |
1 |
Little |
座標點數( NumPoints ) |
表示構成當前面狀目標所包含的座標點個數 |
int 型 |
4 |
1 |
Little |
Parts 數組 |
記錄了每個子環的座標在 Points 數組中的起始位置 |
int 型 |
4×NumParts |
NumParts |
Little |
Points 數組 |
記錄了所有的座標信息 |
Point 型 |
根據點個數來確定 |
NumPoints |
Little |
下面是 讀取面狀目標的記錄內容的代碼:
void OnReadAreaShp(CString ShpFileName)
{
// 打開座標文件
……
// 讀取座標文件頭的內容開始
……
// 讀取面狀目標的實體信息
int RecordNumber;
int ContentLength;
while((fread(&RecordNumber, sizeof(int), 1,m_ShpFile_fp)!=0))
{
fread(&ContentLength,sizeof(int), 1,m_ShpFile_fp);
RecordNumber = OnChangeByteOrder (RecordNumber);
ContentLength = OnChangeByteOrder (ContentLength);
int shapeType;
double Box[4];
int NumParts;
int NumPoints;
int *Parts;
fread(&shapeType, sizeof(int), 1,m_ShpFile_fp);
// 讀 Box
for(i=0;i<4;i++)
fread(Box+i, sizeof(double),1,m_ShpFile_fp);
// 讀 NumParts 和 NumPoints
fread(&NumParts, sizeof(int), 1,m_ShpFile_fp);
fread(&NumPoints, sizeof(int), 1,m_ShpFile_fp);
// 讀 Parts 和 Points
Parts =new int[NumParts];
for(i=0;i<NumParts;i++)
fread(Parts+i, sizeof(int), 1,m_ShpFile_fp);
int pointNum;
int xx;
int yy;
for(i=0;i<NumParts;i++)
{
if(i!=NumParts-1)
pointNum =Parts[i+1]-Parts[i];
else
pointNum =NumPoints-Parts[i];
double *PointsX;
double *PointsY;
PointsX =new double[pointNum];
PointsY =new double[pointNum];
for(j=0;j<pointNum;j++)
{
fread(PointsX+j, sizeof(double),1,m_ShpFile_fp);
fread(PointsY+j, sizeof(double),1,m_ShpFile_fp);
}
delete[] PointsX;
delete[] PointsY;
}
delete[] Parts;
}
}
屬性文件的結構說明
屬性文件 (.dbf) 用於記錄屬性信息。它是一個標準的 DBF 文件,也是由頭文件和實體信息兩部分構成。
文件頭 |
|
記錄 1 |
|
記錄 2 |
|
記錄 3 |
|
記錄 4 |
|
……
記錄 n |
屬性文件的文件頭
其中文件頭部分的長度是不定長的,它主要對 DBF 文件作了一些總體說明(見表 2.7 ),其中最主要的是對這個 DBF 文件的記錄項的信息進行了詳細地描述,比如對每個記錄項的名稱、數據類型、長度等信息都有具體的說明。
在文件中的位置 |
內容 |
說明 |
0 |
1 個字節 |
表示當前的版本信息 |
1 - 3 |
3 個字節 |
表示最近的更新日期,按照 YYMMDD 格式。 |
4 - 7 |
1 個 32 位數 |
文件中的記錄條數。 |
8 - 9 |
1 個 16 位數 |
文件頭中的字節數。 |
10 - 11 |
1 個 16 位數 |
一條記錄中的字節長度。 |
12 - 13 |
2 個字節 |
保留字節,用於以後添加新的說明性信息時使用,這裏用 0 來填寫。 |
14 |
1 個字節 |
表示未完成的操作。 |
15 |
1 個字節 |
dBASE IV 編密碼標記。 |
16 - 27 |
12 個字節 |
保留字節,用於多用戶處理時使用。 |
28 |
1 個字節 |
DBF 文件的 MDX 標識。在創建一個 DBF 表時,如果使用了 MDX 格式的索引文件,那麼 DBF 表的表頭中的這個字節就自動被設置了一個標誌,當你下次試圖重新打開這個 DBF 表的時候,數據引擎會自動識別這個標誌,如果此標誌爲真,則數據引擎將試圖打開相應的 MDX 文件。 |
29 |
1 個字節 |
Language driver ID. |
30 - 31 |
2 個字節 |
保留字節,用於以後添加新的說明性信息時使用,這裏用 0 來填寫。 |
32 - X |
( n*32 )個字節 |
記錄項信息描述數組。 n 表示記錄項的個數。這個數組的結構在表 2.8 中有詳細的解釋。 |
X + 1 |
1 個字節 |
作爲記錄項終止標識。 |
表 2.7 屬性文件( .dbf )的文件頭
位置 |
內容 |
說明 |
0 - 10 |
11 個字節 |
記錄項名稱,是 ASCII 碼值。 |
11 |
1 個字節 |
記錄項的數據類型,是 ASCII 碼值。( B 、 C 、 D 、 G 、 L 、 M 和N ,具體的解釋見表 2.9 )。 |
12 - 15 |
4 個字節 |
保留字節,用於以後添加新的說明性信息時使用,這裏用 0 來填寫。 |
16 |
1 個字節 |
記錄項長度,二進制型。 |
17 |
1 個字節 |
記錄項的精度,二進制型。 |
18 - 19 |
2 個字節 |
保留字節,用於以後添加新的說明性信息時使用,這裏用 0 來填寫。 |
20 |
1 個字節 |
工作區 ID 。 |
21 - 30 |
10 個字節 |
保留字節,用於以後添加新的說明性信息時使用,這裏用 0 來填寫。 |
31 |
1 個字節 |
MDX 標識。如果存在一個 MDX 格式的索引文件,那麼這個記錄項爲真,否則爲空。 |
表 2.8 記錄項信息描述
代碼 |
數據類型 |
允許輸入的數據 |
B |
二進制型 |
各種字符。 |
C |
字符型 |
各種字符。 |
D |
日期型 |
用於區分年、月、日的數字和一個字符,內部存儲按照 YYYYMMDD 格式。 |
G |
(General or OLE) |
各種字符。 |
N |
數值型 (Numeric) |
- . 0 1 2 3 4 5 6 7 8 9 |
L |
邏輯型( Logical) |
? Y y N n T t F f (? 表示沒有初始化 ) 。 |
M |
(Memo) |
各種字符。 |
屬性文件的實體信息
實體信息部分就是一條條屬性記錄,每條記錄都是由若干個記錄項構成,因此只要依次循環讀取每條記錄就可以了。
一個讀取 dbf 文件的例子
假設要讀取一個名爲 soil 的 dbf 文件(存儲了土地利用信息),它含有 8 個記錄項,記錄項信息如表 2.10 所示:
記錄項名稱 |
數據類型 |
長度 |
小數位數 |
Area |
數值型( double ) |
31 |
15 |
Perimeter |
數值型( double ) |
31 |
15 |
soils_ |
數值型( int ) |
11 |
0 |
soils_id |
數值型( int ) |
11 |
0 |
soil_code |
字符型( character ) |
3 |
無 |
Suit |
字符型( character ) |
1 |
無 |
Centroid_x |
數值型( double ) |
31 |
15 |
Centroid_y |
數值型( double ) |
31 |
15 |
表 2.10dbf 文件中的數據類型
下面是讀取這個 dbf 文件的代碼:
void OnReadDbf(CString DbfFileName)
{
FILE* m_DbfFile_fp; //****Dbf 文件指針
// 打開 dbf 文件
if((m_DbfFile_fp=fopen(DbfFileName,"rb"))==NULL)
{
return;
}
int i,j;
//////**** 讀取 dbf 文件的文件頭 開始
BYTE version;
fread(&version, 1, 1,m_DbfFile_fp);
BYTE date[3];
for(i=0;i<3;i++)
{
fread(date+i, 1, 1,m_DbfFile_fp);
}
int RecordNum; //******
fread(&RecordNum, sizeof(int), 1,m_DbfFile_fp);
short HeaderByteNum;
fread(&HeaderByteNum, sizeof(short), 1,m_DbfFile_fp);
short RecordByteNum
fread(&RecordByteNum, sizeof(short), 1,m_DbfFile_fp);
short Reserved1;
fread(&Reserved1, sizeof(short), 1,m_DbfFile_fp);
BYTE Flag4s;
fread(&Flag4s, sizeof(BYTE), 1,m_DbfFile_fp);
BYTE EncrypteFlag;
fread(&EncrypteFlag, sizeof(BYTE), 1,m_DbfFile_fp);
for(i=0;i<3;i++)
{
fread(&Unused, sizeof(int), 1,m_DbfFile_fp);
}
BYTE MDXFlag;
fread(&MDXFlag, sizeof(BYTE), 1,m_DbfFile_fp);
BYTE LDriID;
fread(&LDriID, sizeof(BYTE), 1,m_DbfFile_fp);
short Reserved2;
fread(&Reserved2, sizeof(short), 1,m_DbfFile_fp);
BYTE name[11];
BYTE fieldType;
int Reserved3;
BYTE fieldLength;
BYTE decimalCount;
short Reserved4;
BYTE workID;
short Reserved5[5];
BYTE mDXFlag1;
int fieldscount;
fieldscount = (HeaderByteNum - 32) / 32;
// 讀取記錄項信息-共有 8 個記錄項
for(i=0;i< HeaderByteNum;i++)
{
//FieldName----11 bytes
fread(name, 11, 1,m_DbfFile_fp);
//FieldType----1 bytes
fread(&fieldType, sizeof(BYTE), 1,m_DbfFile_fp);
//Reserved3----4 bytes
Reserved3 =0;
fread(&Reserved3, sizeof(int), 1,m_DbfFile_fp);
//FieldLength--1 bytes
fread(&fieldLength,sizeof(BYTE), 1,m_DbfFile_fp);
//DecimalCount-1 bytes
fread(&decimalCount,sizeof(BYTE), 1,m_DbfFile_fp);
//Reserved4----2 bytes
Reserved4 =0;
fread(&Reserved4, sizeof(short), 1,m_DbfFile_fp);
//WorkID-------1 bytes
fread(&workID, sizeof(BYTE), 1,m_DbfFile_fp);
//Reserved5----10 bytes
for(j=0;j<5;j++)
{
fread(Reserved5+j,sizeof(short), 1,m_DbfFile_fp);
}
//MDXFlag1-----1 bytes
fread(&mDXFlag1, sizeof(BYTE), 1,m_DbfFile_fp);
}
BYTE terminator;
fread(&terminator,
sizeof(BYTE), 1,m_DbfFile_fp); // 讀取 dbf 文件頭結束
double Area,Perimeter,Centroid_y,Centroid_x;
int Soils_,Soils_id;
CString Soil_code,suit;
BYTE deleteFlag;
char media[31];
// 讀取 dbf 文件記錄 開始
for(i=0;i<RecordNum;i++)
{
fread(&deleteFlag, sizeof(BYTE), 1,m_DbfFile_fp); // 讀取 Area double
for(j=0;j<31;j++)
fread(media+j, sizeof(char), 1,m_DbfFile_fp);
Area =atof(media); // 讀取 Perimeter double
for(j=0;j<31;j++)
fread(media+j, sizeof(char), 1,m_DbfFile_fp);
Perimeter =atof(media); // 讀取 soils_ int
for(j=0;j<31;j++)
strcpy(media+j,"");
for(j=0;j<11;j++)
fread(media+j, sizeof(char), 1,m_DbfFile_fp);
Soils_ =atoi(media); // 讀取 Soils_id int
for(j=0;j<31;j++)
strcpy(media+j,"");
for(j=0;j<11;j++)
fread(media+j, sizeof(char), 1,m_DbfFile_fp);
Soils_id =atoi(media);
// 讀取 soil_code string
for(j=0;j<31;j++)
strcpy(media+j,"");
for(j=0;j<3;j++)
fread(media+j, sizeof(char), 1,m_DbfFile_fp);
Soil_code =media; // 讀取 suit string
for(j=0;j<31;j++)
strcpy(media+j,"");
for(j=0;j<1;j++)
fread(media+j, sizeof(char), 1,m_DbfFile_fp);
suit =media; // 讀取 Centroid_y double
for(j=0;j<31;j++)
strcpy(media+j,"");
for(j=0;j<31;j++)
fread(media+j, sizeof(char), 1,m_DbfFile_fp);
Centroid_y =atof(media); // 讀取 Centroid_x double
for(j=0;j<31;j++)
strcpy(media+j,"");
for(j=0;j<31;j++)
fread(media+j, sizeof(char), 1,m_DbfFile_fp);
Centroid_x =atof(media);
}
// 讀取 dbf 文件記錄 結束
}
索引文件的結構說明
索引文件( .shx )主要包含座標文件的索引信息,文件中每個記錄包含對應的座標文件記錄距離座標文件的文件頭的偏移量。通過索引文件可以很方便地在座標文件中定位到指定目標的座標信息。
索引文件也是由頭文件和實體信息兩部分構成(如圖 2.5 ),其中文件頭部分是一個長度固定 (100 bytes) 的記錄段,其內容與座標文件的文件頭基本一致。它的實體信息以記錄爲基本單位,每一條記錄包括偏移量( offset )和記錄段長度( Content Length )兩個記錄項,它們的位序都是 big ,兩個記錄項都是 int 型。
文件頭 |
|
記錄 1 |
|
記錄 2 |
|
記錄 3 |
|
記錄 4 |
|
……
……
記錄 n |
圖 2.5 索引文件的結構
記錄項 |
數值 |
數據類型 |
長度 |
個數 |
位序 |
位移量( Offset ) |
表示座標文件中的對應記錄的起始位置相對於座標文件起始位置的位移量。 |
int 型 |
4 |
1 |
Big |
記錄長度 ( Content Length ) |
表示座標文件中的對應記錄的長度。 |
int 型 |
4 |
1 |
Big |
表 2.11 索引文件的記錄內容
下面是一段讀取索引文件的代碼:
void OnReadShx ( CString ShxFileName )
{
FILE* m_ShxFile_fp; //****Shx 文件指針
// 打開索引文件
if((m_ShxFile_fp=fopen(ShxFileName,"rb"))==NULL)
{
return;
}
// 讀取索引文件頭的內容開始
int FileCode;
int Unused;
int FileLength;
int Version;
int ShapeType;
double Xmin;
double Ymin;
double Xmax;
double Ymax;
double Zmin;
double Zmax;
double Mmin;
double Mmax;
fread(&FileCode, sizeof(int), 1,m_ShxFile_fp);
FileCode = OnChangeByteOrder(FileCode);
for(i=0;i<5;i++)
fread(&Unused,sizeof(int), 1,m_ShxFile_fp);
fread(&FileLength, sizeof(int), 1,m_ShxFile_fp);
FileLength = OnChangeByteOrder(FileLength);
fread(&Version, sizeof(int), 1,m_ShxFile_fp);
fread(&ShapeType, sizeof(int), 1,m_ShxFile_fp);
fread(&Xmin, sizeof(double),1,m_ShxFile_fp);
fread(&Ymin, sizeof(double),1,m_ShxFile_fp);
fread(&Xmax, sizeof(double),1,m_ShxFile_fp);
fread(&Ymax, sizeof(double),1,m_ShxFile_fp);
fread(&Zmin, sizeof(double),1,m_ShxFile_fp);
fread(&Zmax, sizeof(double),1,m_ShxFile_fp);
fread(&Mmin, sizeof(double),1,m_ShxFile_fp);
fread(&Mmax, sizeof(double),1,m_ShxFile_fp);
// 讀取索引文件頭的內容結束
int Offset, ContentLength;
// 讀取實體信息
while((fread(&Offset, sizeof(int), 1, m_ShxFile_fp)!=0))
{
fread(&ContentLength,sizeof(int), 1, m_ShxFile_fp);
Offset = OnChangeByteOrder(Offset);
ContentLength = OnChangeByteOrder(ContentLength);
}
}
小結
本節介紹了 MapObjects 支持的各種數據,並詳細介紹了 shapefiles 的文件結構,同時給出了讀取 shapefiles 的座標文件( .shp )、屬性文件( .dbf )和索引文件( .shx )的程序,給出這些程序的目的在於讓讀者通過這些例子深入掌握 shapefiles 文件的格式,進而具備將特定格式的數據文件轉換成 shapefiles 文件的能力。