1.シェープファイル形式
1.1最初にシェープファイルを見る
ESRIシェープファイルの技術的説明によると、完全なシェープファイルにとって最も重要な3つのファイルは、***。shp、**。shx、および.dbfです。3つの意味は、MainFile(メインファイル)、IndexFile(インデックスファイル)、dBaseFile(プロパティファイル)です。MainFile(.shp)は、ポイント、ライン、およびサーフェスの3つの基本的な幾何学的タイプを格納するために使用されます。IndexFile(.shx)インデックスファイルは、対応するメインファイル内の各レコードの開始点の位置を格納します。dBaseFile(.dbf)属性ファイルには、各ポイントの属性が格納されます。
すべてのファイルはバイナリモードで保存されるため、C ++を使用してファイルを読み取る場合、ifstreamオブジェクトの初期化時にios :: binaryを追加する必要があります。これは、ファイルがバイナリモードで開かれていることを示します。開封方法は以下の通りです。
ifstream inFile("C:\\Users\\Administrator\\Desktop\\test.shp", ios::binary | ios::in )
1.2.shpファイル
各.shpファイルは2つの部分に分かれており、1つの部分は100バイトファイルのヘッダーとその他の部分は、使用する必要のあるデータコンテンツであり、データレコードと呼ばれます。
1.2.1.shpファイルヘッダー
ファイルの最初のバイトから100バイトまでは、ファイルヘッダーの内容です。データを読み取るときは、0〜99の合計100バイトです。配列のインデックスと同様に、0から始まり、以下で説明するファイルポインタはすべて配列インデックスに基づいています。
100バイトからファイルの終わりまでがデータレコードの内容です。
ファイルヘッダーの説明については、次の図を参照してください。
上の図で、右端の列はコンピューターのバイトオーダーモードです。情報を検索した後、著者は、異なるコンピューターシステムが異なるエンディアンを使用する可能性があることを学びました。Bigはビッグエンディアンで、Littleはリトルエンディアンです。ビッグエンディアンとリトルエンディアンについては、ここではあまり説明しません。直接使用できるバイトオーダー変換のコードを投稿するだけです。
template<class T>
T ByteTrans(T m)
{
//联合体内所有的数据公用一个内存
union n_
{
T n;
char mem[sizeof(T)];
};
n_ big, small;
big.n = m;
for (int i = 0; i < sizeof(T); i++)
{
small.mem[i] = big.mem[sizeof(T) - i - 1];
}
return small.n;
}
ファイルヘッダーの最も重要なデータは、ShapeTypeとBoxです。ここでのShapeTypeは、ポイント、ライン、サーフェスの3つの幾何学的要素に対応するマップタイプです。ボックスは、Xの最大値と最小値、Yの最大値と最小値(ZとMがある場合、最大値と最小値を含む)を含む、現在の.shpファイルに記録されているすべてのポイントの範囲です。 ZとMの、しかしこれまでのところ、著者はMを含む.shpファイルを見たことがありません)。
ファイルヘッダーのShapeTypeについては、次の図を参照してください。
次に、ファイルのデータレコードがあります。
1.2.2.shpファイルのデータレコード
.shpファイルの101バイト目、つまりファイルポインタが100バイトから始まり、ファイルの終わりまで続くのは、.shpファイルのデータレコードです。
各データレコードには、2つの部分も含まれています。最初の部分は、レコードヘッダーと呼ばれる2つのビッグエンディアン整数を含むコンテンツです。これは固定長で、わずか8バイトであり、レコードヘッダーはビッグエンディアンの順序で格納されます。他の部分は可変長、つまり可変長のレコードコンテンツです。すべてのデータはリトルエンディアンの順序で格納されます。
レコードヘッダーの最初のビッグエンディアン整数は、1から始まる現在のレコードの番号です。2番目のビッグエンディアン整数はレコード長(RecordLength)です。固定長の記録ヘッドは一般的に役に立たない。
可変長レコードの内容は、最初にマップタイプ(ShapeType)であり、次に実際のポイントレコードです。次の表に示すように、ポイント、ライン、サーフェスの3つの幾何学的要素について、.shpファイルでのそれらの保存方法は異なります。
ShapeType | ストレージ形式 |
---|---|
1時ちょうど) | X(ダブル)、Y(ダブル) |
2(行) | double Box [4]、int NumParts、int NumPoints、int Parts [NumParts]、double [NumPoints [X、Y]] |
3(顔) | double Box [4]、int NumParts、int NumPoints、int Parts [NumParts]、double [NumPoints [X、Y]] |
ポイントの場合、レコードコンテンツにはShapeType(値= 1)と2つのdouble値(X座標とY座標)のみがあります。
ラインとサーフェスの場合、ShapeTypeに加えて、最初のダブルBox [4]は現在のレコード内のすべてのポイントの範囲を参照し、ストレージモードはXmin、Ymin、Xmax、Ymaxです。
NumPartsは、現在のレコード内のラインのセグメントの数(またはサーフェスのリングの数)です。
NumPointsは、現在のレコードのポイント数です。
int Parts [NumParts]は、ライン(またはリング)の各セグメントの開始点を格納するファイルポインタのインデックスです。
double [NumPoints [X、Y]]は、現在のレコードの終わりまで、X1、Y1、X2、Y2、X3、Y3 ...のように順番に格納されたポイントのX座標とY座標です。
1.2.3まとめ
.shpファイルの紹介は以上です。
1.3.shxファイル
.shxファイルは、100バイトのファイルヘッダーと固定長のレコードを含むインデックスファイルです。
ファイルヘッダーは.shpファイルと同じです。固定長レコードは8バイト、2つのint型データです。以下に示すように:
1.4.dbfファイル
とりあえずフォーマットがわからないので、後で使うときに更新します。
2. C ++クラスの設計
2.1アイデア
シェープファイルはマップタイプのファイルです。マップには複数のレイヤーが含まれ、各レイヤーにはポイント、ライン、ポリゴンが含まれます。点、線、平面から始めて、点が線を構成し、線が表面を構成することがわかります。同時に、ポイントはレイヤーで構成でき、線や領域も使用できます。図に示すように、
ポイント、ライン、エリアはすべてオブジェクトであり、レイヤーもオブジェクトであり、複数のレイヤーで構成されるマップもオブジェクトです。
したがって、クラス間の関係が出てきます。
2.2コードの実装
3種類のポイント、ライン、サーフェスには、ポイントの座標を格納してファイルに配置し、NumParts、NumPoints、Boxなどの共通の属性を持つという共通点が1つあるためです。したがって、ポイント、ライン、およびサーフェスクラスを設計する前に、Shapeクラスを抽象化する必要があります。
* [ HTML ]:この記事のすべてのコードは、QTで正常に実行されたコードです。自分のプロジェクトから直接コピーして貼り付けているため、コードは問題なく実行されます。
2.2.1形状クラス
コードは次のように表示されます。
#include <vector>
struct strXY
{
double dX;
double dY;
};
class Shape
{
public:
int _vParts[1000];
int _iNumParts;
int _iShapeType;
double _dBox[4];
std::vector<strXY> _vPoints;
virtual void toShape(double, double) = 0;
};
2.2.2ポイントクラス
コードは次のように表示されます。
class Point :public Shape
{
public:
~Point();
void toShape(double, double);
}
2.2.3LinePolygonクラス
ポリラインとポリゴンには特定の共通属性があるため、これらの共通属性は各レコードの数と範囲です。
コードは次のように表示されます。
#include "shape.h"
class LinePolygon :public Shape
{
public:
int _iNumPnts;
double _dBox[4];
virtual void toShape(double, double) = 0;
};
2.2.4ポリラインクラス
コードは次のように表示されます。
#include "linepolygon.h"
class Polyline :public LinePolygon
{
public:
void toShape(double, double);
};
2.2.5ポリゴンクラス
コードは次のように表示されます。
#include "linepolygon.h"
class Polygon :public LinePolygon
{
public:
void toShape(double, double);
};
2.2.6レイヤークラス
各ファイルはレイヤーであり、各レイヤーには特定の幾何学的要素が含まれています。複数のレイヤーを組み合わせると、次のような抽象的なプロパティがいくつかあります。
int _iShpTyp; // 地图类型
double _dBox[4]; // 边界
int _iRecnt; // 记录数
string _sfilename; // 文件名
vector<Shape*> _vShape; // 记录的集合
また、レイヤーはファイルを処理する必要があるため、ファイルを読み取るアクション、つまりファイルを読み取るための一連の関数も必要です。次のように:
bool loadFile(string);
void readFilehead(ifstream&);
void toPoint(ifstream& inFile,int iIndex);
void toPolyline(ifstream& inFile,int iIndex);
void toPolygon(ifstream& inFile,int iIndex);
void toPointZ(ifstream& inFile,int iIndex);
void readRecContent(string);
int getReCnt(string);
したがって、レイヤーヘッダーファイル全体は次のようになります。
#include <fstream>
#include <vector>
#include "shape.h"
#include "point.h"
#include "polyline.h"
#include "polygon.h"
using namespace std;
class Layer
{
protected:
void readFilehead(ifstream&);
void toPoint(ifstream& inFile, int iIndex);
void toPolyline(ifstream& inFile, int iIndex);
void toPolygon(ifstream& inFile, int iIndex);
void toPointZ(ifstream& inFile, int iIndex);
void readRecContent(string);
int getReCnt(string);
public:
Layer();
~Layer();
bool loadFile(string);
Point* _pPoint;
Polyline* _pLine;
Polygon* _pPolygon;
int _iShpTyp; // 地图类型
double _dBox[4]; // 边界
int _iRecnt; // 记录数
string _sfilename; // 文件名
vector<Shape*> _vShape; // 记录的集合
double _dRecordBox[50000][4];
};
2.2.7マップクラス
Mapオブジェクトには多くのLayersが含まれているため、Layerに多くのShapesが含まれているように、Mapクラスのデザインは次のように比較できます。
#include <vector>
#include "layer.h"
#include "box.h"
#include <QPoint>
#include <QPolygon>
class Map
{
public:
Map();
Map(string);
// 自定义函数部分
void addFile(string); // 向地图里添加图层
void addFile(string*, int); // 重载addFile函数
// 属性
double _dBox[4]; // 整个地图里面的Box,是所有的Box求并之后的结果
std::vector<Layer*> _vMap; // 用来存储所有的图层
protected:
Box _box;
void setBox();
};
2.2.8まとめ
クラスの構文は非常に単純ですが、抽象的な概念を実際のオブジェクトに導入し、それらの共通点を抽象的に記述する方法は非常に困難です。
先生が言ったように、クラスを設計するとき、1つのポイントだけが把握されます。それは単純で直接的なものです。
道のりは長く、C ++はプログラマーの頭脳を台無しにします。
3.全体的なコード
ここで言うことはあまりありませんが、コードは直接非常に長くなるので、ソースコードを見たい場合は、ジャンプしてダウンロードできます。
ソースコードを提供する必要がある理由としては、一人では食べられない良いものがいくつかありますが、オープンソースは良くありません!