• PostGIS 爆管分析之找出上游阀门(优化版)


    说明

    前面描述过利用postgis查找上游阀门的原理,以及代码,其实当初写完就发现又很大的优化空间,但一直没有时间去做。
    最近遇到一个情况,处理60w+条管网数据时,效率太慢了,于是腾时间优化了一版。

    解决方案

    主要优化了两个点:

    1. 这次拿到手的数据处理的很不好,好多阀门点没有在管线上,碍于数据处理工作量大,于是用缓冲区的方式做了个容差范围。
      st_intersects(st_buffer('0101000000D34D62709FC66841FA7E6A9C7C4E5241',0.0001),geom)
      但是发现这个做法比原来直接判断点是否在线上,效率慢了50倍左右,所以直接放弃,只能处理数据。
      ST_intersects(a.geom,b.geom)

    2. 以前查找上游阀门的逻辑,是在找到爆管点影响的所有阀门基础上,再用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;
    
    
  • 相关阅读:
    falsk-web 表单
    falsk-web 表单
    falsk-web 表单
    falsk-web 表单
    治理“假货之都”需要大数据打假
    治理“假货之都”需要大数据打假
    治理“假货之都”需要大数据打假
    治理“假货之都”需要大数据打假
    与好友合伙创业,他开店4家,月销售额已超过30万元
    放弃优越的都市生活,他返乡创业带动家乡人民共同致富
  • 原文地址:https://www.cnblogs.com/giser-s/p/12091219.html
Copyright © 2020-2023  润新知