shp系列(二)——利用C++進行shp文件的讀(打開)


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM