前言:最近在(被迫)使用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. linux安装maven环境

    linux安装maven环境 一. 下载压缩包: 官网地址: http://maven.apache.org/download.cgi 或者百度网盘链接:https://pan.baidu.com/s ...

  2. 如何在Linux Centos上部署配置FastDFS

    一.准备工作: 1.准备下面包文件 -- FastDFS_v5.08.tar.gz -- libevent-2.0.22-stable.tar.gz -- libfastcommon-master.z ...

  3. MyBatis Plus 2.3 个人笔记-04-配置文件与插件使用

    接入 springboot application.yml配置 1.mapper 扫描 mybatis-plus: # 如果是放在src/main/java目录下 classpath:/com/you ...

  4. 树莓派安装ros

    之前电脑安装过ros感觉还好,没成想这次在树莓派上安装费老劲了,出现了很多错误,装了卸,卸了装废了半天劲下面将一些安装的错误和问题做个总结方便以后的安装也希望给别人一个参考 ros安装(对照自己的版本 ...

  5. css写作建议和性能优化小结

    1.前言 还有几天就到国庆中秋了,快要放假了,先祝大家节日快乐!之前写过js的写作建议和技巧,那么今天就来聊聊css吧!说到css,每一个网页都离不开css,但是对于css,很多开发者的想法就是,cs ...

  6. 小程序完整对接 pingpp支付

    小程序完整对接 pingpp支付 有几个先要条件: 小程序需要企业认证且开通支付功能,个人认证是无法使用支付功能的(小程序微信支付官网) pingpp 本身接入的企业服务器(即商户服务器)并不强制要求 ...

  7. ES6-11学习笔记--Iterator

    迭代器 Iterator 是一种接口机制,为各种不同的数据结构提供统一访问的机制 主要供for...of消费 一句话:不支持遍历的数据结构"可遍历"   具备Symbol.iter ...

  8. SQLite实现数据库的储存2+SQLite数据库可视化工具SQLite Stadio

    今日所学 SQLite实现数据库的储存 查看数据库的两种方法 Android 中 SQLite 数据库的查看 - woider - 博客园 SQLite Studio安装教程 [SQLite]可视化工 ...

  9. Jar 包下载以及 maven jar 包配置

    学习内容: jar包下载是我们必须掌握的一个内容,不管是使用Maven项目还是其他项目,一般都需要引入外部的 jar 包 jar包下载 下载地址(打不开网址的直接百度搜索 maven reposito ...

  10. 微信小程序和公众号和H5之间相互跳转

    参考链接:https://www.imooc.com/article/22900 一.小程序和公众号 答案是:可以相互关联. 在微信公众号里可以添加小程序. 可关联已有的小程序或快速创建小程序.已关联 ...