ファイル情報の取得
まずは、Shapeファイルを開いて種別を取得する必要があります。
そこまでのコードは大体以下のようにします。
#include "shapefil.h" // ←"shapefile.h"ではないので注意! int main( int argc, char *argv[] ) { SHPHandle hSHP; int nShapeType, nEntities; double adfMinBound[4], adfMaxBound[4]; hSHP = SHPOpen( argv[1], "r" ); if ( hSHP == NULL ) { exit( 1 ); } SHPGetInfo( hSHP, &nEntities, &nShapeType, adfMinBound, adfMaxBound );
SHPGetInfo()
関数でこのファイルの要素数と要素種別を取得します。
第2引数の要素種別は以下の値をとります。
SHPT_NULL
SHPT_POINT
SHPT_ARC
SHPT_POLYGON
SHPT_MULTIPOINT
SHPT_POINTZ
SHPT_ARCZ
SHPT_POLYGONZ
SHPT_MULTIPOINTZ
SHPT_POINTM
SHPT_ARCM
SHPT_POLYGONM
SHPT_MULTIPOINTM
SHPT_MULTIPATCH
例えば、ポイントShapeだけを処理対象にしたい場合は以下のようにします。
if ( nShapeType != SHPT_POINT &&
nShapeType != SHPT_POINTZ &&
nShapeType != SHPT_POINTM )
{
SHPClose( hSHP );
exit( 1 );
}
図形データの読み込み
ファイルごとに要素種別は決まっていますが、
shapelibでは要素の情報を格納する構造体は種別にかかわらずSHPObject
型です。
なので、以下のようにすれば統一的に読み込むことができます。
SHPObject *psElem; double *padfX, *padfY, *padfZ; // ファイルからデータを読み込んで頂点配列に追加 for ( i = 0; i < nEntities; i++ ) { psElem = SHPReadObject( hSHP, i ); padfX = new double[psElem->nVertices]; padfY = new double[psElem->nVertices]; padfZ = new double[psElem->nVertices]; for ( j = 0; j < psElem->nVertices; j++ ) { padfX[j] = psElem->padfX[j]; padfY[j] = psElem->padfY[j]; padfZ[j] = psElem->padfZ[j]; } // 使い終わったら delete[] padfX; delete[] padfY; delete[] padfZ; SHPDestroyObject( psElem ); }
属性の読み込み
属性データは.dbfに格納されています。
shapelibではDBFは別個に取り扱います。
なので、shpファイルと同時に開いておく必要があります。
以下のような感じでしょうか。
char szDBF[_MAX_PATH+1]; strcpy_s( szDBF, argv[1] ); szDBF[strlen(szDBF)-3] = ''; strcat_s( szDBF, "dbf" ); hDBF = DBFOpen( szDBF, "rb" ); if ( !hDBF ) { SHPClose( hSHP ); exit( 1 ); }
DBFを開いたら、まずはフィールド数を調べたいところです。
レコード数は要素数と同じになっているはずです。
フィールド数を調べたら、各フィールドの種別を調べます。
Shapelibではフィールドの種別はenum
で定義されています。
typedef enum {
FTString,
FTInteger,
FTDouble,
FTLogical,
FTInvalid
} DBFFieldType;
フィールド名はNULL文字を入れて11byteまでとなっています。
フィールド情報を調べるコードは、以下のようになるでしょうか。
int nField; DBFFieldType eType; char szTitle[11]; int nWidth, nDecimals; int i; char *pszTypeName; nField = DBFGetFieldCount( hDBF ); for ( i = 0; i < nField; i++ ) { eType = DBFGetFieldInfo( hDBF, i, szTitle, &nWidth, &nDecimals ); switch ( eType ) { case FTString: pszTypeName = "String"; break; case FTInteger: pszTypeName = "Integer"; break; case FTDouble: pszTypeName = "Double"; break; case FTLogical: pszTypeName = "Logical"; break; default: pszTypeName = "Invalid"; break; } printf( "Field %d: Type = %s, Title = %s, Width = %d, Decimals = %dn", i, pszTypeName, szTitle, nWidth, nDecimals ); }
続いて、レコードを読み込みます。
レコードの順番は図形要素の順番と同じであることとなっています。
なので、図形要素の読み込みと同時にレコードを読み込む必要があります。
また、フィールドの型によってレコードの読み込みで使用する関数が異なります。
上記の図形データの読み込みと併せると、大体以下のようになるでしょうか。
for ( i = 0; i < nEntities; i++ ) { psElem = SHPReadObject( hSHP, i ); padfX = new double[psElem->nVertices]; padfY = new double[psElem->nVertices]; padfZ = new double[psElem->nVertices]; for ( j = 0; j < psElem->nVertices; j++ ) { padfX[j] = psElem->padfX[j]; padfY[j] = psElem->padfY[j]; padfZ[j] = psElem->padfZ[j]; } for ( j = 0; j < nField; j++ ) { eType = DBFGetFieldInfo( hDBF, j, szTitle, &nWidth, &nDecimals ); switch ( eType ) { case FTString: printf( "%s : %s ", szTitle, DBFReadStringAttribute( hDBF, i, j ) ); break; case FTInteger: printf( "%s : %d ", szTitle, DBFReadIntegerAttribute( hDBF, i, j ) ); break; case FTDouble: printf( "%s : %f ", szTitle, DBFReadDoubleAttribute( hDBF, i, j ) ); break; default: break; } } // 使い終わったら delete[] padfX; delete[] padfY; delete[] padfZ; SHPDestroyObject( psElem ); printf( "n" ); }
最後に、shpファイルとdbfファイルを閉じます。
SHPClose( hSHP ); DBFClose( hDBF );