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);
}