PostGIS 爆管分析之找出上游阀门(优化版)
说明
前面描述过利用postgis查找上游阀门的原理,以及代码,其实当初写完就发现又很大的优化空间,但一直没有时间去做。
最近遇到一个情况,处理60w+条管网数据时,效率太慢了,于是腾时间优化了一版。
解决方案
主要优化了两个点:
这次拿到手的数据处理的很不好,好多阀门点没有在管线上,碍于数据处理工作量大,于是用缓冲区的方式做了个容差范围。
st_intersects(st_buffer('0101000000D34D62709FC66841FA7E6A9C7C4E5241',0.0001),geom)
但是发现这个做法比原来直接判断点是否在线上,效率慢了50倍左右,所以直接放弃,只能处理数据。
ST_intersects(a.geom,b.geom)以前查找上游阀门的逻辑,是在找到爆管点影响的所有阀门基础上,再用pgRouting的pgr_dijkstraCost函数,判断消耗实现的。
SELECT count(*) FROM pgr_dijkstraCost('select gid as id, source, target, length as cost, reverse_cost from zy',m_cost, ARRAY[v_startSource,v_startTarget], true) where agg_cost >= 9999999 into m_cost_value;
实测这个函数的执行效率太低,如果我查到10个周围阀门,判断从这个阀门到爆管点的消耗(以60w+条管网算),每个遍历平均近3秒,整个查询耗时将近30+秒。
考虑再三,决定换一个思路:在做广度遍历查询管段source/target的时候,带上方向消耗(如果接点为source,则消耗大于等于9999999,为逆向,找到管段头;如果接点为target,则消耗小于9999999,为逆向,找到管段头)
case when zy1.source = any(v_up_where) then 1
when zy1.target = any(v_up_where) then 2
else 0 end as isuptap
IF (up_temprow.isuptap = 1 AND up_temprow.length >= 9999999) OR (up_temprow.isuptap = 2 AND up_temprow.length < 9999999)
附上所有代码
CREATE OR REPLACE FUNCTION test_getpoint9(
IN tbl character varying,
IN startx double precision,
IN starty double precision)
RETURNS TABLE(v_gid integer, v_res geometry, v_type integer) AS
$BODY$
declare
v_startLine geometry;--离起点最近的线
v_startTarget integer;--距离起点最近线的终点
v_startSource integer;
v_statpoint geometry;--在v_startLine上距离起点最近的点
v_endpoint geometry;--在v_endLine上距离终点最近的点
v_up_source integer;--游标,记录是否有记录
v_up_idx integer;--记录遍历到多少层级
v_uptap_gid integer;--上游阀门gid
v_uptap_geom geometry;--上游阀门要素
v_all_where integer[];--记录所有查询过的管段
v_up_where integer[];--where条件,将遍历到阀门的管段gid排除
v_down_where integer[];--where条件,将遍历到阀门的管段gid排除
up_temprow record ;
--v_cost record;--记录阀门管段source(用于计算消耗,判断方向)
m_cost integer;
m_cost_value integer;
temprow record;
v_cost integer[];
res_source integer;
res_tap_pipe text[];
m_tap_pipe text;
idx_tap_pipe integer; --遍历结果游标
m_up_cost integer;--上游阀门
v_up_cost integer[];--上游阀门集合
res_main_pipe integer[];--总阀门集合
m_main_pipe integer;--总阀门
v_length_cost double precision;--正消耗
v_startGid integer;
begin
--查询离起点最近的线
--3857坐标系
--找起点15米范围内的最近线
execute 'select geom, gid, source, target, length, ST_StartPoint(geom) as startpoint,ST_EndPoint(geom) as endpoint from ' ||tbl||
' where ST_DWithin(geom,ST_Geometryfromtext(''point('|| startx ||' ' || starty ||')'',3857),15)
order by ST_Distance(geom,ST_GeometryFromText(''point('|| startx ||' '|| starty ||')'',3857)) limit 1'
into v_startLine, v_startGid, v_startSource ,v_startTarget ,v_length_cost, v_statpoint ,v_endpoint;
raise notice '%' , 'v_startSource---'|| cast(v_startGid as text);
IF(v_startLine is not null) THEN
--查找上游阀门
v_up_idx = 0;
v_up_source = 1;
--判断流向,开始往上游找
IF (v_length_cost < 9999999) THEN
SELECT array_append(v_up_where, v_startSource) into v_up_where;
ELSE
SELECT array_append(v_up_where, v_startTarget) into v_up_where;
END IF;
raise notice '%' , 'v_up_where---'|| cast(v_up_where as text);
--如果没有下级节点需要遍历
WHILE array_length(v_up_where, 1) > 0
LOOP
--游标归零
v_up_source = 0;
--记录层级
v_up_idx = v_up_idx + 1;
--获取当前层级节点
FOR up_temprow IN
--select zy1.gid,zy1.source,zy1.target,zy1.length,zy1.reverse_cost from zy zy1 where source = any(v_up_where) or target = any(v_up_where)
select zy1.gid,zy1.source,zy1.target,case
when zy1.source = any(v_up_where) then 1
when zy1.target = any(v_up_where) then 2
else 0 end as isuptap,zy1.length,zy1.reverse_cost from zy zy1 where source = any(v_up_where) or target = any(v_up_where)
--select zy1.gid,zy1.source,zy1.target from zy zy1 where target = an y(v_up_where)--找上游
LOOP
--清空需要查的点
IF(v_up_source = 0) THEN
v_up_where = null;
END IF;
--清空初始执行节点
v_startSource = 0;
--标志执行有数据
v_up_source = 1;
--查询管网上的点
select t.gid,t.geom from fm t where t.gid in (
select a.gid from fm a,(select c.* from zy c where c.gid = up_temprow.gid) b where ST_intersects(a.geom,b.geom)
) into v_uptap_gid, v_uptap_geom;
raise notice '%' , 'up_temprow---'|| cast(up_temprow as text);
--如果没查找到阀门,则继续往上游方向查
IF(v_uptap_gid is null) THEN
--找管段上游方向,如果是source判断length逆向,如果是target判断length正向
IF ((up_temprow.isuptap = 1 AND up_temprow.length >= 9999999) OR (up_temprow.isuptap = 2 AND up_temprow.length < 9999999)) THEN
--source去重,判断如果数组中已有,则不添加
IF (v_up_where @> ARRAY[up_temprow.source::integer] OR v_all_where @> ARRAY[up_temprow.source::integer]) THEN
ELSE
SELECT array_append(v_up_where,up_temprow.source) into v_up_where;
SELECT array_append(v_all_where,up_temprow.source) into v_all_where;
END IF;
--target去重,判断如果数组中已有,则不添加
IF (v_up_where @> ARRAY[up_temprow.target::integer] OR v_all_where @> ARRAY[up_temprow.target::integer]) THEN
ELSE
SELECT array_append(v_up_where,up_temprow.target) into v_up_where;
SELECT array_append(v_all_where,up_temprow.target) into v_all_where;
END IF;
END IF;
raise notice '%' , 'v_up_where'||v_up_idx||'---'|| cast(v_up_where as text);
--如果查找到阀门,则执行返回
--ELSEIF (up_temprow.isuptap) THEN
ELSE
raise notice '%' , '找到阀门了!' || up_temprow;
--执行返回结果
--阀门id,阀门图形要素,阀门类型(上游/下游)
return query
select v_uptap_gid as res_uptap_gid,v_uptap_geom as res_uptap_geom ,up_temprow.source as res_source;
END IF;
END LOOP;
END LOOP;
END IF;
end;
$BODY$
LANGUAGE plpgsql VOLATILE STRICT
COST 100
ROWS 1000;
ALTER FUNCTION test_getpoint9(character varying, double precision, double precision)
OWNER TO postgres;
PostGIS 爆管分析之找出上游阀门(优化版)的更多相关文章
- PostGIS 爆管分析之找出上游阀门
环境: Win10 ArcMap10.4(用于数据处理) postgresql9.4 postgis2.2.3 pgRouting2.3(postgresql插件) 说明: 继上一篇文章做了爆管分析找 ...
- PostGIS 爆管分析之找出总阀门
这个算法算是被摒弃了,但是很多自己思考过后留下的成果,虽然不用了,留着做记录. 算法目的是为了发生爆管后找到总阀门,这里分了几个步骤: 1.找到爆管点所在管段 2.通过遍历找到爆管点所有影响的阀门 3 ...
- PostGIS 爆管分析之根据爆点找出所有影响阀门
环境: Win10 ArcMap10.4(用于数据处理) postgresql9.4 postgis2.2.3 pgRouting2.3(postgresql插件) 说明: 做爆管分析的第一步,需要先 ...
- Python练习六十:网页分析,找出里面的正文与链接
网页分析,找出里面的正文与链接 代码如下: from urllib import request from bs4 import BeautifulSoup request = request.url ...
- 《BI那点儿事》Microsoft 决策树算法——找出三国武将特性分布,献给广大的三国爱好者们
根据游戏<三国志11>武将数据,利用决策树分析,找出三国武将特性分布.其中变量包括统率.武力.智力.政治.魅力.身分.变量说明:统率:武将带兵出征时的部队防御力.统帅越高受到普通攻击与兵法 ...
- 如何找出你性能最差的SQL Server查询
我经常会被反复问到这样的问题:”我有一个性能很差的SQL Server.我如何找出最差性能的查询?“.因此在今天的文章里会给你一些让你很容易找到问题答案的信息向导. 问SQL Server! SQL ...
- 记录一下通过分析Tomcat内部jar包找出request.getReader()所用的字符编码在哪里设置和起效的完整分析流程
前言: 之前写Java服务端处理POST请求时遇到了请求体转换成字符流所用编码来源的疑惑,在doPost方法里通过request.getReader()获取的BufferedReader对象内部的 R ...
- Bugku-CTF分析篇-weblogic(黑客攻击了Weblogic应用,请分析攻击过程,找出Weblogic的主机名。)
weblogic 黑客攻击了Weblogic应用,请分析攻击过程,找出Weblogic的主机名. flag格式:flag{} Tip:主机名为十六进制.
- c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)
c#封装DBHelper类 public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...
随机推荐
- 使用Qt Designer进行布局
在使用Form之前,需要将Form上的对象放置到布局中.这确保在应用程序中预览或使用Form时,对象将正确显示.在布局中放置对象还可以确保在调整窗体大小时它们也能正确调整大小. 应用和打断布局 ...
- Linux上Redis安装和简单操作
Redis redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorte ...
- Java实现QQ微信轰炸机1.2(斗图乞丐版)
之前有小可爱评论可以实现斗图的功能,原理上是行的通的,所以我就稍微改了一下,能够实现单个图片循环轰炸,如果大家感兴趣也可以自己探究实现多张图片循环轰炸,不废话了,直接上源码package QQWcha ...
- C++入门经典-例3.9-使用嵌套表达式判断一个数是否是3和5的整数倍
1:代码如下: // 3.9.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using ...
- 第六周课程总结&实验报告(四)
实验报告(四) 一.实验目的 1.掌握类的继承 2.变量的继承和覆盖,方法的继承,重载和覆盖的实现 二.实验的内容 1.根据下面的要求实现圆类Circle. 圆类Circle的成员变量:radius表 ...
- 字符 kotlin(3)
字符 用 Char 类型表示.它们不能直接当作数字) { // 错误:类型不兼容 // …… }} 字符字面值用单引号括起来: '1' . 特殊字符可以用反斜杠转义. 支持这几个转义序列: \t . ...
- docker top 和 docker exec ps 命令查看的PID区别
区别在于 docker top 查看到的 PID 属于宿主机的 PID.我们可以通过 宿主机执行 ps -ef 查看结果 也可以进去容器执行 top 和 ps查看结果
- D2下午
前言 至于为什么D2要分上下午,唯一的原因就是lyd那个毒瘤用了一上午讲他昨天要讲的鬼畜东西,所以今天下午才开始讲数论了 对了,补一下lyd的数论人 <数论人>(大雾) 数论的光束是歌德巴 ...
- python模块------pyautogui
安装 pip install pyautogui 基本使用 查询 screenWidth, screenHeight = pyautogui.size() # 屏幕尺寸 mouseX, mouseY ...
- 【html】合并单元格,并居中显示文本
现状: 想要实现的效果: 代码实现: <tr> <td colspan=" align="center">用例失败为0,无测试详情</td ...