前言:最近在(被迫)使用ArcGIS Engine10.2(.NET平台)进行二次开发(桌面应用),因为想做一个最短路径查询的功能,而arcgis的网络分析又比较麻烦,于是想到了使用Postgis。但这样就需要将本地shp存到数据库,在程序中连接数据库。

百度了半天发现Arcgis Engine直接连接PostgreSQL数据库需要用到ArcSDE。ArcSDE还需要另外安装,而且我用的ArcGIS Engine10.2只支持PostgreSQL 9.x(我的数据库版本是11),这样似乎就很麻烦了啊(。)

行叭,另辟蹊径,因为Arcgis和Postgis肯定都遵循OGC的SFS规范,只需要在postgis中把最短路径规划函数写好,导出结果的wkb/wkt,然后用C#连接数据库(使用Npgsql)获取wkb/wkt,最后用ArcGIS Engine把wkb/wkt转成IGeometry类对象,显示到地图上就行了。

整体思路就是这样,其实很类似于WebGIS中路径规划的思路,下面详细介绍一下:


一、PostgreSQL的最短路径函数

这一步网上有很多文章,写的也还不错,但是大部分比较古老了,用的还是pgRouting 1.x或者2.0的函数,这给我造成了很多困扰。

这里我使用PostgreSQL11+postgis 3+pgRouting 3.0来实现。

1、准备数据

准备路网数据,需要将所有线段连接的地方全部断开,这样便于以后的分析。

如果数据在交叉处没有断开,可以在ArcgisDesktop中的ArcToolBox 找到数据管理工具(Data management tools)——>要素(Features)——>要素转线(Feature to line)

2、创建空间数据库,开启扩展

在新的空间数据库里添加空间扩展,使用如下SQL语句:

CREATE EXTENSION postgis;

CREATE EXTENSION pgrouting;

CREATE EXTENSION postgis_topology;

CREATE EXTENSION fuzzystrmatch;

CREATE EXTENSION postgis_tiger_geocoder;

CREATE EXTENSION address_standardizer;
3、导入数据

这一步应该都懂,就不过多介绍了。

值得注意的是,有人说导入时应该勾选下图的选项(生成简单要素),否则自动生成为Multi要素(多面、多线、多点)路径规划会失败。但是我自己做的时候没遇到这种问题,以防万一,可以勾上。

4、添加路网拓扑结构

这里我就只考虑无向图的情况,有向图是类似的可以自行百度。

--添加起点id

ALTER TABLE public.whu_road ADD COLUMN source integer;

--添加终点id

ALTER TABLE public.whu_road ADD COLUMN target integer;

--添加道路权重值

ALTER TABLE public.whu_road ADD COLUMN length double precision;

--为sampledata表创建拓扑布局,即为source和target字段赋值

SELECT pgr_createTopology('public.whu_road',0.0001, 'geom', 'gid');

--为source和target字段创建索引

CREATE INDEX source_idx ON whu_road("source");

CREATE INDEX target_idx ON whu_road("target");

--为length赋值

update whu_road set length =st_length(geom);

--为road_xblk表添加reverse_cost字段并用length的值赋值

ALTER TABLE whu_road ADD COLUMN reverse_cost double precision;

UPDATE whu_road SET reverse_cost =length;
5、创建最短路径函数,返回路径线段序列

其中最短路径使用dijkstra算法,调用pgrouting的pgr_dijkstra函数。

网上有很多教程使用pgr_kdijkstraPath,这种就是比较老的版本,pgrouting3.0是不能使用的。

--删除已存在的函数
DROP FUNCTION pgr_fromAtoB(tbl varchar,startx float, starty float,endx float,endy float); --DROP FUNCTION pgr_fromAtoB(varchar, double precision, double precision
-- double precision, double precision);
--基于任意两点之间的最短路径分析
CREATE OR REPLACE FUNCTION pgr_fromAtoB(
IN tbl varchar,--数据库表名
IN x1 double precision,--起点x坐标
IN y1 double precision,--起点y坐标
IN x2 double precision,--终点x坐标
IN y2 double precision,--终点y坐标
OUT seq integer,--道路序号
OUT gid integer,
OUT name text,--道路名
OUT heading double precision,
OUT cost double precision,--消耗
OUT geom geometry--道路几何集合
)
RETURNS SETOF record AS
$BODY$
DECLARE
sql text;
rec record;
source integer;
target integer;
point integer; BEGIN
-- 查询距离出发点最近的道路节点
EXECUTE 'SELECT id::integer FROM '|| quote_ident(tbl) ||'_vertices_pgr
ORDER BY the_geom <-> ST_GeometryFromText(''POINT('
|| x1 || ' ' || y1 || ')'',4326) LIMIT 1' INTO rec;
source := rec.id; -- 查询距离目的地最近的道路节点
EXECUTE 'SELECT id::integer FROM '|| quote_ident(tbl) ||'_vertices_pgr
ORDER BY the_geom <-> ST_GeometryFromText(''POINT('
|| x2 || ' ' || y2 || ')'',4326) LIMIT 1' INTO rec;
target := rec.id; -- 最短路径查询
seq := 0;
sql := 'SELECT gid, geom, node as name, cost, source, target,
ST_Reverse(geom) AS flip_geom FROM ' ||
'pgr_dijkstra(''SELECT gid as id,
source::integer,target::integer,'
|| 'length::float AS cost FROM '
|| quote_ident(tbl) || ''', '
|| source || ', ' || target
|| ' ,false) as di, '
|| quote_ident(tbl) || ' WHERE di.edge = gid ORDER BY seq'; -- Remember start point
point := source; FOR rec IN EXECUTE sql
LOOP
-- Flip geometry (if required)
IF ( point != rec.source ) THEN
rec.geom := rec.flip_geom;
point := rec.source;
ELSE
point := rec.target;
END IF; -- Calculate heading (simplified)
EXECUTE 'SELECT degrees( ST_Azimuth(
ST_StartPoint(''' || rec.geom::text || '''),
ST_EndPoint(''' || rec.geom::text || ''') ) )'
INTO heading; -- Return record
seq := seq + 1;
gid := rec.gid;
name := rec.name;
cost := rec.cost;
geom := rec.geom;
RETURN NEXT;
END LOOP;
RETURN;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE STRICT;
6、测试
select * from sqlpgr_fromAtoB('whu_road',114.3560,30.5362,114.3683,30.5484);

随便选两个点测试一下,OK,已经返回了路径的线段序列。

7、 路线合并,转换为wkb或wkt

因为最终我们需要将最短路径传到桌面端显示,为了方便,使用ST_Union将这些线段合并成一个完整的线路。使用方法如下:

select ST_Union(geom) as route from pgr_fromAtoB('whu_road'::text,114.353,30.539,114.367,30.544) ;

合并的路径我们看不出来是什么,不过将他转成wkt就能理解了:

select ST_astext(ST_Union(geom)) as route from pgr_fromAtoB('whu_road'::text,114.353,30.539,114.367,30.544) ;

当然,为了方便C#读取,还是使用ST_asbinary转换为wkb(二进制)比较好,这也是我们后面需要用到的语句:

select ST_asbinary(ST_Union(geom)) as route from pgr_fromAtoB('whu_road'::text,114.353,30.539,114.367,30.544) ;

二、C#连接读取数据库

到这里事情就简单了。

1、安装Npgsql

在VS中打开项目-管理NuGet程序包

搜索安装Npgsql

2、读取数据库

创建一个DAO类,写一个路经查询函数,这里给一个简单的例子:

namespace WHUGIS.Classes
{
class DAO
{ private static string connectionString = "User ID=postgres;Password=admin;Server=localhost;Port=5432;Database=GIS_engine;";
public DAO()
{ }
public static Byte[] executeRouteQuery(string sqlstr)
{
NpgsqlConnection sqlConn = new NpgsqlConnection(connectionString);
try
{
sqlConn.Open();
NpgsqlCommand objCommand = new NpgsqlCommand(sqlstr, sqlConn);
Byte[] routeWKB = (byte[])objCommand.ExecuteScalar();
return routeWKB;
}
catch(Exception ee)
{
MessageBox.Show(ee.Message);
return null;
}
finally
{
sqlConn.Close();
} } }
}

三、使用ArcGIS Engine在AxMapControl中绘制最短路径

让我们随便创建一个RouteForm,设计一个界面用于获取点坐标。

关于C#的界面设计和交互的实现我在这里就不多说了,下面是在AxMapControl绘制最短路径的简单实现(在RouteForm类当中):

private IElement pElement = null;
//路径起止点
private double startX = 0, startY = 0;
private double endX = 0, endY = 0;
//主界面地图控件的引用
private AxMapControl mMapControl;
//确定按钮被点击函数
private void button1_Click(object sender, EventArgs e)
{
//连接数据库,路径规划
string sqlstr = null;
if ((startX * startY * endX * endY) != 0)
sqlstr = "select ST_asbinary(ST_Union(geom)) as route from pgr_fromAtoB('whu_road_feature2line'::text," + startX + "," + startY + "," + endX + "," + endY + ") ;";
else
{
MessageBox.Show("起始点或终止点不能为空");
return;
}
Byte[] routeWKB = DAO.executeRouteQuery(sqlstr);
IGeometry geom;
int countin = routeWKB.GetLength(0);
//地图容器,创建临时元素
IMap pMap = mMapControl.Map; //
IActiveView pActiveView = pMap as IActiveView;
IGraphicsContainer pGraphicsContainer = pMap as IGraphicsContainer;
if (pElement != null)
{
pGraphicsContainer.DeleteElement(pElement);
}
//转换wkb为IGeometry
IGeometryFactory3 factory = new GeometryEnvironment() as IGeometryFactory3;
factory.CreateGeometryFromWkbVariant(routeWKB, out geom, out countin);
IPolyline pLine = (IPolyline)geom; //定义要素symbol
ISimpleLineSymbol pLineSym = new SimpleLineSymbol();
IRgbColor pColor = new RgbColor();
pColor.Red = 11;
pColor.Green = 120;
pColor.Blue = 233;
pLineSym.Color = pColor;
pLineSym.Style = esriSimpleLineStyle.esriSLSSolid;
pLineSym.Width = 2;
//线元素symbol绑定
ILineElement pLineElement = new LineElementClass();
pLineElement.Symbol = pLineSym;
//添加geom
pElement = pLineElement as IElement;
pElement.Geometry = pLine;
//加入地图并刷新
pGraphicsContainer.AddElement(pElement, 0);
pActiveView.Refresh();
}

最后让我们看看效果:

参考资料:

关于在ArcGIS平台使用PostGIS,对Geometry字段的处理

ArcGIS Engine 几何对象和WKB的转换

ArcGIS实现在线与线交叉处打断线(批量)

pgrouting3.0官方文档

基于pgrouting的路径规划之一

GeoServer+PostgreSQL+PostGIS+pgRouting实现最短路径查询

GeoServer+PostgreSQL+PostGIS+pgRouting实现最短路径查询

postgresql+postgis+pgrouting实现最短路径查询(1)---线数据的处理和建立拓扑...

PostgreSQL+PostGIS+pgRouting最短路径规划之

[AE] ArcGIS Engine - 地图整饰 - 添加图形元素(临时的Geometry)

轻松搞定通过c#连接postgis数据库并且实现增删改查功能

C#连接postgresql数据库

C# 操作PostgreSQL 数据库

PostGIS--线路合并方法比较

基于Arcgis Engine 10.2(C#)+PostgreSQL 11(Postgis 3)+pgRouting 3.0实现使用数据库进行路径规划的更多相关文章

  1. vs2012 arcgis engine 10 丢失arcgis模板

    1.Visual Studio 2012环境下安装ArcGIS Engine 10 Visual Studio 2012环境下安装ArcObject SDK for the Microsoft .Ne ...

  2. VS2010下WPF开发ARCGIS ENGINE 10的带Ribbon控件项目

    原文 http://blog.sina.com.cn/s/blog_47522f7f0100nq5t.html 题目好长,但是集目前最新的工具于一身..VS是最新的2010版,不过用的是.net3.5 ...

  3. ArcGIS Engine 10.2 如何发布服务

    http://blog.csdn.net/arcgis_all/article/details/17376397 1 ArcGIS Engine 10.2 如何发布服务 ArcGIS Engine的代 ...

  4. ArcGIS SDE 10.1 for Postgresql 服务连接配置

    去年写了ArcGIS 10.1 如何连接Postgresql 数据库(http://blog.csdn.net/arcgis_all/article/details/8202709)当时采用的也是Ar ...

  5. ArcGIS Engine 10.x许可代码

    相比9.3,10.x许可代码的书写改变了,ArcObjects SDK 10 Microsoft .NET Framework 帮助文档中,提供了以下两种方式: 1. Calling RuntimeM ...

  6. 【149】ArcGIS Desktop 10.0 & Engine 10.0 安装及破解

    写在前面:可能会出现按照此方法无法破解的情况,那请确保您有将 ArcGIS 10.0 已经完全卸载干净,直接通过控制面板进行卸载的时候并不能将其卸载干净,需要进行更深层次的卸载,包括删除注册表,各种文 ...

  7. ArcGIS Engine开发前基础知识(4)

    ArcGIS不同开发方式的比较 关于GIS应用软件的开发,通常有三种方式:C/S架构.网络GIS和移动GIS.ArcGIS平台提供了对三种开发方式的支持,对于采用从C/S架构的大多数开发者来讲,首先想 ...

  8. ArcGIS Engine开发前基础知识(3)

    对象模型图 一.对象模型图中的类与接口 ArcGIS Engine 提供大量的对象,这些对象之间存在各种各样的关系,如继承.组合.关联等.对象模型图(Object model diagram,ODM) ...

  9. ArcGIS Engine中正确释放打开资源

    转自原文 ArcGIS Engine中正确释放打开资源 AE中对MDB,SDE等数据库操作时,打开后却往往不能及时释放资源,导致别人操作提示对象被锁定. 很多帖子说了很多原理,看的也烦且不实用,比如一 ...

随机推荐

  1. CF914G Sum the Fibonacci(FWT,FST)

    CF914G Sum the Fibonacci(FWT,FST) Luogu 题解时间 一堆FWT和FST缝合而来的丑陋产物. 对 $ cnt[s_{a}] $ 和 $ cnt[s_{b}] $ 求 ...

  2. loj536「LibreOJ Round #6」花札(二分图博弈)

    loj536「LibreOJ Round #6」花札(二分图博弈) loj 题解时间 很明显是二分图博弈. 以某个点为起点,先手必胜的充要条件是起点一定在最大匹配中. 判断方法是看起点到该点的边有流量 ...

  3. VT 入门番外篇——初识 VT

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  4. Redis Cluster 集群搭建与扩容、缩容

    说明:仍然是伪集群,所有的Redis节点,都在一个服务器上,采用不同配置文件,不同端口的形式实现 前提:已经安装好了Redis,本文的redis的版本是redis-6.2.3 Redis的下载.安装参 ...

  5. 安卓开发学习-app结构学习

    开发软件:Android Studio 全局分析 gradle与idea是AS自动生成的文件 buid是编译时生成的文件 gradle里面包含gradle wrapper配置文件 gitignore是 ...

  6. DLink 815路由器栈溢出漏洞分析与复现

    DLink 815路由器栈溢出漏洞分析与复现 qemu模拟环境搭建 固件下载地址 File DIR-815_FIRMWARE_1.01.ZIP - Firmware for D-link DIR-81 ...

  7. 使用 JWT 来保护你的 SpringBoot 应用

    关键词 写在前面 Spring Boot 创建Spring Boot应用 创建一个Web 应用 使用JWT保护你的Spring Boot应用 添加Spring Security 本文代码 关键词 Sp ...

  8. synchronized和 synchronized 了解偏向锁、轻量级锁、重量级锁的概念以及升级机制、以及和ReentrantLock的区别。

    并发 synchronized 了解偏向锁.轻量级锁.重量级锁的概念以及升级机制.以及和ReentrantLock的区别.       https://www.cnblogs.com/deltadeb ...

  9. js Object扩展自定义方法,jQuery抛出 Uncaught TypeError: matchExpr[type].exec is not a function

    使用Jquery的时候,想在Object原型上添加自己扩展的方法的时候,启动项目之后,打开网页就会报如上错误信息,经过测试,可以在Object下的具体类型上进行扩展自定义方法,如String,Arra ...

  10. 什么是 UML?

    UML 是统一建模语言(Unified Modeling Language)的缩写,它发表于 1997 年,综合了当时已经存在的面向对象的建模语言.方法和过程,是一个支持模型 化和软件系统开发的图形化 ...