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