GNMAPI教程

本文档旨在描述使用GNM C++类来处理网络。建议阅读 地理网络数据模型 了解GNM类的目的和结构。

管理网络

在第一个示例中,我们将基于一组空间数据(两个形状文件:位于GDAL源树上的管道和井:autotest\gnm\data)创建一个小型水网络。通用网络格式的使用- GNMGdalNetwork 类-将允许我们为网络选择GDAL支持的向量格式之一-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中有两种方法:手动或自动。在大多数情况下,自动生成更为方便,而手动生成则适用于小的编辑。自动生成需要一些参数:我们必须指定哪些“类层”将参与拓扑生成(我们选择我们的两个层)、捕捉公差、直接和反向成本、方向,在我们的情况下等于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");
}

现在我们已经有了一个包含拓扑和空间数据的现成网络,现在可以用于不同的目的(分析、转换成不同的格式等)。但有时需要修改一些网络数据。例如,我们需要添加其他特性并将它们附加到我们构建的拓扑(修改拓扑)。我们在网络中创建了一个新的“类层”,并添加了一个特性。

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

成功创建后,该功能将在网络中注册,我们可以将其与其他功能连接起来。有两种可能的方法可以做到这一点。在第一种情况下,我们需要一个真正的特性,它将是连接中的一个边,而在第二种情况下,我们不需要这样的特性,并将-1传递到 GNMGenericNetwork::ConnectFeatures() 方法意味着将为此连接创建特殊的系统边缘并自动添加到图中。在我们的例子中,我们只添加了一个点特征,而没有将线1作为边,因此我们将使用“虚拟”连接。我们将点的GFID作为源传递,将某个现有功能的GFID作为目标传递,-1作为连接器传递。请注意,我们还手动设置成本(直接和反向)和边的方向,这些值将写入图形。当我们使用自动连接(也在内部使用ConnectFeatures())时,这些值是根据我们之前设置的规则自动设置的。

if (poDS->ConnectFeatures(gfid ,63, -1, 5.0, 5.0, GNMDirection_SrcToTgt) != GNMError_None)
{
    printf("Can not connect features\n");
}

毕竟,我们正确地关闭了释放分配资源的网络。

GDALClose(poDS);

一个街区:

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

分析网络

在第二个例子中,我们将分析我们在第一个例子中建立的网络。我们将通过Dijkstra算法计算两点之间的最短路径,执行特征块并将生成的路径保存到文件中。

最初,我们打开网络,将路径传递到其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);
}

最后用Dijkstra最短路径法进行计算。这个路径将被发现通过阻塞的特性并保存到内部内存OGRLayer中,我们将其复制到真实的数据集。现在它可以用地理信息系统可视化了。

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

一个街区:

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