GDAL GNM API 教程
这是一个关于使用GNM(Geographic Networks Model)C++类处理网络数据的技术教程。在此教程中,我们将创建一个小型水网络,并进行网络管理和分析。
GDAL GNM API 教程
本文旨在描述如何使用 GNM C++ 类来处理网络数据。建议在阅读本文之前先了解地理网络数据模型(Geographic Networks Data Model)以理解 GNM 类的目的和结构。
管理网络
在第一个示例中,我们将基于一组空间数据(两个 shapefile:pipes 和 wells,这些文件位于 GDAL 源树的 autotestgnmdata 目录中)创建一个小型水网络。使用通用网络格式 – 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 failedn");
}
else
{
printf("Topology has been built successfullyn");
}
此时,我们有一个准备好的网络,具有拓扑和空间数据,现在可以用于不同的目的(分析、转换成不同的格式等)。但有时需要修改网络数据。例如,我们需要添加额外的要素并将其附加到我们构建的拓扑上(修改拓扑)。我们在网络中创建一个新的”类层”,并向其添加一个要素。
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() 方法表示将为此连接创建一个特殊的系统边并将其自动添加到图中。在我们的情况下,我们只添加了一个点要素,没有将线要素作为边,因此我们将使用”虚拟”连接。我们将我们的点的 GFID 作为源,现有要素的 GFID 之一作为目标,并将 -1 作为连接器。请注意,我们还手动设置了边的成本(直接和反向)和方向,这些值将被写入图中。当我们使用自动连接(它也在内部使用 ConnectFeatures())时,这些值将根据我们之前设置的规则自动设置。
if (poDS->ConnectFeatures(gfid ,63, -1, 5.0, 5.0, GNMDirection_SrcToTgt) != GNMError_None)
{
printf("Can not connect featuresn");
}
最后,我们正确关闭网络,释放分配的资源。
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 atn");
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 datasetn");
exit(1);
}
最后,我们使用 Dijkstra 最短路径方法进行计算。这条路径将经过阻塞要素并保存在内部内存 OGRLayer 中,然后我们将其复制到实际数据集中。现在可以在 GIS 中进行可视化。
OGRLayer *poResLayer = poNet->GetPath(64, 41, GATDijkstraShortestPath, NULL);
if (poResLayer == NULL)
{
printf("Failed to save or calculate pathn");
}
else if (poResDS->CopyLayer(poResLayer, "shp_tutorial.shp") == NULL)
{
printf("Failed to save path to the layern");
}
else
{
printf("Path saved successfullyn");
}
GDALClose(poResDS);
poNet->ReleaseResultSet(poRout);
GDALClose(poNet);
}