GNM API チュートリアル
このドキュメントは,ネットワークを扱うためのGNM C++クラスの使用方法を説明することを目的としています.GNMクラスの目的と構造を理解するために, 地理ネットワークデータモデル を読むことをお勧めします.
ネットワークを管理する
最初の例では,空間データのセット(パイプと井戸の2つのシェープファイル)を基に小さな水ネットワークを作成します.これらのファイルは, GDALソースツリーのautotest\gnm\dataに配置されています.一般的なネットワークフォーマットである GNMGdalNetwork
クラスを使用することで,ネットワークにGDALでサポートされているベクトルフォーマットの1つであるESRI Shapefileを選択できます.作成後,トポロジを構築し,追加データ(ポンプレイヤー)を追加してネットワークトポロジを手動で編集します.
最初にGDALドライバを登録し,ネットワーク作成時のパラメータとして渡されるいくつかのオプション(文字列ペア)を作成します.ここでは,ネットワーク名を作成します.
#include "gnm.h"
#include <vector>
int main ()
{
GDALAllRegister();
char **papszDSCO = NULL;
papszDSCO = CSLAddNameValue(papszDSCO, GNM_MD_NAME, "my_pipes_network");
papszDSCO = CSLAddNameValue(papszDSCO, GNM_MD_SRS, "EPSG:4326");
papszDSCO = CSLAddNameValue(papszDSCO, GNM_MD_DESCR, "My pipes network");
papszDSCO = CSLAddNameValue(papszDSCO, GNM_MD_FORMAT, "ESRI Shapefile");
いくつかのオプションは必須です.ネットワーク作成時には,次のパラメータを指定する必要があります:パス/名前;ネットワークストレージの形式;空間参照システム(EPSG, WKTなど)."ネットワーク部分"を持つ対応するデータセットが作成され,結果のネットワークが返されます.
GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("GNMFile");
GNMGenericNetwork* poDS = (GNMGenericNetwork*) poDriver->Create( "..\\network_data", 0, 0, 0, GDT_Unknown,
papszDSCO );
CSLDestroy(papszDSCO);
現時点では, "システムレイヤ"のみで構成された空のネットワークがあります. "クラスレイヤ"を持つフィーチャでネットワークを埋める必要があるため,特定の外部データセットを開き,そのデータセットからネットワークにレイヤをコピーします.注意: "クラスレイヤ"との作業にはGDALDataset::メソッドを使用しています,なぜなら GNMNetwork
はGDALDatasetから継承しているからです.
GDALDataset *poSrcDS = (GDALDataset*) GDALOpenEx("..\\in_data",
GDAL_OF_VECTOR | GDAL_OF_READONLY, NULL, NULL, NULL );
OGRLayer *poSrcLayer1 = poSrcDS->GetLayerByName("pipes");
OGRLayer *poSrcLayer2 = poSrcDS->GetLayerByName("wells");
poDS->CopyLayer(poSrcLayer1, "pipes");
poDS->CopyLayer(poSrcLayer2, "wells");
GDALClose(poSrcDS);
コピーが成功した後,地物がいっぱいのネットワークがありますが,トポロジはありません.地物はネットワークに追加され,登録されましたが,まだ互いに接続されていません.ネットワークトポロジを構築する時期です. GNMでは,トポロジを手動で構築するか自動で構築するかの2つの方法があります.ほとんどの場合,自動構築がより便利ですが,手動構築は小さな編集に便利です.自動構築にはいくつかのパラメータが必要です: トポロジ構築に参加する"クラスレイヤ"を指定する必要があります(2つのレイヤを選択します),スナップ許容度,直接コストと逆コスト,方向,この場合は0.00005です.構築が成功すると,ネットワークのグラフが対応する接続で埋められます.
printf("\nBuilding network topology ...\n");
char **papszLayers = NULL;
for(int i = 0; i < poDS->GetLayerCount(); ++i)
{
OGRLayer* poLayer = poDS->GetLayer(i);
papszLayers = CSLAddString(papszLayers, poLayer->GetName() );
}
if(poGenericNetwork->ConnectPointsByLines(papszLayers, dfTolerance,
dfDirCost, dfInvCost, eDir) != CE_None )
{
printf("Building topology failed\n");
}
else
{
printf("Topology has been built successfully\n");
}
この時点で,異なる目的(解析,異なる形式に変換など)に使用できるトポロジと空間データを持つ準備ができたネットワークがあります.しかし,ネットワークのデータを変更する必要がある場合があります.たとえば,追加の地物を追加し,それらを構築されたトポロジに接続する必要があります(トポロジを変更).ネットワーク内で新しい"クラスレイヤ"を作成し,1つの地物を追加します.
OGRLayer *poNewLayer = poDS->CreateLayer("pumps", , NULL, wkbPoint, NULL );
if( poNewLayer == NULL )
{
printf( "Layer creation failed.\n" );
exit( 1 );
}
OGRFieldDefn fieldDefn ("pressure",OFTReal);
if( poNewLayer->CreateField( &fieldDefn ) != OGRERR_NONE )
{
printf( "Creating Name field failed.\n" );
exit( 1 );
}
OGRFeature *poFeature = OGRFeature::CreateFeature(poNewLayer->GetLayerDefn());
OGRPoint pt;
pt.setX(37.291466);
pt.setY(55.828351);
poFeature->SetGeometry(&pt);
if( poNewLayer->CreateFeature( poFeature ) != OGRERR_NONE )
{
printf( "Failed to create feature.\n" );
exit( 1 );
}
GNMGFID gfid = poFeature->GetFID();
OGRFeature::DestroyFeature( poFeature );
作成が成功した後,地物はネットワークに登録され,他の地物と接続できます.これを行うためには2つの可能な方法があります.最初の場合は,接続のエッジとなる実際の地物が必要です.2番目の場合は,そのような地物が必要ないため, GNMGenericNetwork::ConnectFeatures()
メソッドに-1を渡すと,この接続のために特別なシステムエッジが作成され,自動的にグラフに追加されます.この場合,1つのポイント地物のみを追加し,ライン地物をエッジにする必要がないため,"仮想"接続を使用します.ソースとしてポイントのGFID,ターゲットとして既存の地物のGFIDの1つ,コネクタとして-1を渡します.コスト(直接と逆)とエッジの方向も手動で設定し,これらの値がグラフに書き込まれます.自動接続(内部的にもConnectFeatures() を使用)を使用した場合,これらの値は以前に設定したルールに従って自動的に設定されます.
if (poDS->ConnectFeatures(gfid ,63, -1, 5.0, 5.0, GNMDirection_SrcToTgt) != GNMError_None)
{
printf("Can not connect features\n");
}
最後に,割り当てられたリソースを解放するためにネットワークを正しく閉じます.
GDALClose(poDS);
すべてを1つのブロックに:
#include "gnm.h"
#include "gnm_priv.h"
int main ()
{
GDALAllRegister();
char **papszDSCO = NULL;
papszDSCO = CSLAddNameValue(papszDSCO, GNM_MD_NAME, "my_pipes_network");
papszDSCO = CSLAddNameValue(papszDSCO, GNM_MD_SRS, "EPSG:4326");
papszDSCO = CSLAddNameValue(papszDSCO, GNM_MD_DESCR, "My pipes network");
papszDSCO = CSLAddNameValue(papszDSCO, GNM_MD_FORMAT, "ESRI Shapefile");
GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("GNMFile");
GNMGenericNetwork* poDS = (GNMGenericNetwork*) poDriver->Create( "..\\network_data", 0, 0, 0, GDT_Unknown,
papszDSCO );
CSLDestroy(papszDSCO);
if (poDS == NULL)
{
printf("Failed to create network\n");
exit(1);
}
GDALDataset *poSrcDS = (GDALDataset*) GDALOpenEx("..\\in_data",GDAL_OF_VECTOR | GDAL_OF_READONLY, NULL, NULL, NULL );
if(poSrcDS == NULL)
{
printf("Can not open source dataset at\n");
exit(1);
}
OGRLayer *poSrcLayer1 = poSrcDS->GetLayerByName("pipes");
OGRLayer *poSrcLayer2 = poSrcDS->GetLayerByName("wells");
if (poSrcLayer1 == NULL || poSrcLayer2 == NULL)
{
printf("Can not process layers of source dataset\n");
exit(1);
}
poDS->CopyLayer(poSrcLayer1, "pipes");
poDS->CopyLayer(poSrcLayer2, "wells");
GDALClose(poSrcDS);
printf("\nBuilding network topology ...\n");
char **papszLayers = NULL;
for(int i = 0; i < poDS->GetLayerCount(); ++i)
{
OGRLayer* poLayer = poDS->GetLayer(i);
papszLayers = CSLAddString(papszLayers, poLayer->GetName() );
}
if(poGenericNetwork->ConnectPointsByLines(papszLayers, dfTolerance,
dfDirCost, dfInvCost, eDir) != CE_None )
{
printf("Building topology failed\n");
exit(1);
}
else
{
printf("Topology has been built successfully\n");
}
OGRLayer *poNewLayer = poDS->CreateLayer("pumps", , NULL, wkbPoint, NULL );
if( poNewLayer == NULL )
{
printf( "Layer creation failed.\n" );
exit( 1 );
}
OGRFieldDefn fieldDefn ("pressure",OFTReal);
if( poNewLayer->CreateField( &fieldDefn ) != OGRERR_NONE )
{
printf( "Creating Name field failed.\n" );
exit( 1 );
}
OGRFeature *poFeature = OGRFeature::CreateFeature(poNewLayer->GetLayerDefn());
OGRPoint pt;
pt.setX(37.291466);
pt.setY(55.828351);
poFeature->SetGeometry(&pt);
if( poNewLayer->CreateFeature( poFeature ) != OGRERR_NONE )
{
printf( "Failed to create feature.\n" );
exit( 1 );
}
GNMGFID gfid = poFeature->GetFID();
OGRFeature::DestroyFeature( poFeature );
if (poDS->ConnectFeatures(gfid ,63, -1, 5.0, 5.0, GNMDirection_SrcToTgt) != GNMError_None)
{
printf("Can not connect features\n");
}
GDALClose(poDS);
}
ネットワークの解析
2番目の例では,最初の例で構築したネットワークを解析します. Dijkstraアルゴリズムを使用して2点間の最短経路を計算し,地物のブロックを実行し,結果の経路をファイルに保存します.
最初に,Shapefileデータセットへのパスを渡してネットワークを開きます.
#include "gnm.h"
#include "gnm_priv.h"
int main ()
{
GDALAllRegister();
GNMGenericNetwork *poNet = (GNMGenericNetwork*) GDALOpenEx("..\\network_data",GDAL_OF_GNM | GDAL_OF_UPDATE, NULL, NULL, NULL );
if(poSrcDS == NULL)
{
printf("Can not open source dataset at\n");
exit(1);
}
計算を行う前に,結果の経路を保持するレイヤを持つデータセットを開きます.
GDALDataset *poResDS;
poResDS = (GDALDataset*) GDALOpenEx("..\\out_data",
GDAL_OF_VECTOR | GDAL_OF_UPDATE,
NULL, NULL, NULL);
if (poResDS == NULL)
{
printf("Failed to open resulting dataset\n");
exit(1);
}
最後に,最短経路メソッドを使用して計算します.この経路は,ブロックされた地物を通過して見つけられ,内部メモリOGRLayerに保存され,実際のデータセットにコピーされます.これでGISで可視化できます.
OGRLayer *poResLayer = poNet->GetPath(64, 41, GATDijkstraShortestPath, NULL);
if (poResLayer == NULL)
{
printf("Failed to save or calculate path\n");
}
else if (poResDS->CopyLayer(poResLayer, "shp_tutorial.shp") == NULL)
{
printf("Failed to save path to the layer\n");
}
else
{
printf("Path saved successfully\n");
}
GDALClose(poResDS);
poNet->ReleaseResultSet(poRout);
GDALClose(poNet);
}
すべてを1つのブロックに:
#include "gnm.h"
#include "gnmstdanalysis.h"
int main ()
{
GDALAllRegister();
GNMGenericNetwork *poNet = (GNMGenericNetwork*) GDALOpenEx("..\\network_data",
GDAL_OF_GNM | GDAL_OF_UPDATE,
NULL, NULL, NULL );
if(poSrcDS == NULL)
{
printf("Can not open source dataset at\n");
exit(1);
}
GDALDataset *poResDS;
poResDS = (GDALDataset*) GDALOpenEx("..\\out_data",
GDAL_OF_VECTOR | GDAL_OF_UPDATE,
NULL, NULL, NULL);
if (poResDS == NULL)
{
printf("Failed to open resulting dataset\n");
exit(1);
}
poNet->ChangeBlockState(36, true);
OGRLayer *poResLayer = poNet->GetPath(64, 41, GATDijkstraShortestPath, NULL);
if (poResLayer == NULL)
{
printf("Failed to save or calculate path\n");
}
else if (poResDS->CopyLayer(poResLayer, "shp_tutorial.shp") == NULL)
{
printf("Failed to save path to the layer\n");
}
else
{
printf("Path saved successfully\n");
}
GDALClose(poResDS);
poNet->ReleaseResultSet(poRout);
GDALClose(poNet);
}