前言:最近在(被迫)使用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. POI完成Excel文件的读和写

    简介 Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office(Excel.WORD.PowerPo ...

  2. Python的类和继承

    一.类的封装: 封装将类的信息隐藏在类内部,不允许外部直接修改该类的变量,只能通过该类提供的方法来实现对隐藏信息的操作和访问 class Boss(): # 类的公共属性 level=1 # 类的初始 ...

  3. KestrelServer详解[2]: 网络链接的创建

    <注册监听终结点(Endpoint)>已经详细讲述了如何使用KestrelServer,现在我们来简单聊聊这种处理器的总体设计和实现原理.当KestrelServer启动的时候,注册的每个 ...

  4. 如何实现 Spring Boot 应用程序的安全性?

    为了实现 Spring Boot 的安全性,我们使用 spring-boot-starter-security 依赖项,并且必须添加安全配置.它只需要很少的代码.配置类将必须扩展WebSecurity ...

  5. python 模块和包的基础知识

    1.常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀 2.为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理.这时我们不仅 ...

  6. CopyOnWriteArrayList 可以用于什么应用场景?

    CopyOnWriteArrayList(免锁容器)的好处之一是当多个迭代器同时遍历和修改这 个列表时,不会抛出 ConcurrentModificationException.在 CopyOnWri ...

  7. BMZCTF 2020祥云杯到点了

    2020祥云杯到点了 下载附件得到三个word文档,我们打开第一个文档然后将隐藏文字显示出来 得到提示 我们查看属性应该就是日期了我们先把他记录下来 然后打开第二个文档 输入刚刚的密码 在第二个wor ...

  8. js 获取和设置css3 属性值的实现方法

    众多周知 CSS3 增加了很多属性,在读写的时候就没有原先那么方便了. 如:<div style="left:100px"></div> 只考虑行间样式的话 ...

  9. Episode 1:正视微信(试播)

    本期是 WEB VIEW 的第一期播客节目. 「不囿于 WEB,不止于 VIEW」,WEB VIEW 是由 yin 和敬礼主持的一档泛科技播客.节目中我们谨慎考量技术进步所带来的优缺点,提倡用「人治」 ...

  10. Hadoop本地编写的jar包放到集群执行时报错处理

    错误描述: 020-03-24 22:45:23,204 WARN org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor ...