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;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM