PostGIS में लिंकेस्टेड समूह बनाना?


12

मेरे पास गलियों की एक तालिका है जिसे मैंने विशेषताओं के एक सेट के आधार पर चुना है (चलो कहते हैं speed_limit < 25)। सड़कों के समूह हैं जो स्थानीय रूप से सन्निहित हैं; मैं ज्योमेट्रीकोलियेशन में कनेक्टेड लिनेस्टरों के इन सेटों को समूह में रखना चाहूंगा। नीचे दी गई छवि में, दो ज्यामितिएँ होंगी: एक लाल रेखाओं वाली और दूसरी नीली रेखाओं वाली।

यहाँ छवि विवरण दर्ज करें

मैंने "भंग, बहरेपन" प्रश्नों के एक जोड़े को चलाने की कोशिश की:

SELECT (ST_Dump(st_union)).geom
FROM 
    (SELECT ST_Union(geom) FROM roads) sq

सब कुछ के साथ मैं कोशिश की है, मैं या तो एक एकल सुविधा (अंत ST_Union) या अपने मूल ज्यामिति ( ST_Dumpकी ST_Union)।

शायद किसी तरह के WITH RECURSIVEजादू से ऐसा करना संभव है ?


कुछ ठीक से नहीं लगती है "। (ST_Dump (st_union)) geom"
मार्टिन एफ

चूंकि वह उर्फ ​​ST_Union (जियोम) नहीं बना था, इसलिए नए जियोम का नाम st_union बनने के लिए फ़ंक्शन का नाम विरासत में मिला। इसलिए यह थोड़ा अजीब लगता है
LR1234567

जवाबों:


19

इसलिए, उदाहरण के लिए। यहां किनारों से जुड़े दो समूहों के साथ एक सरल तालिका है:

drop table lines;
create table lines ( id integer primary key, geom geometry(linestring) );
insert into lines (id, geom) values ( 1, 'LINESTRING(0 0, 0 1)');
insert into lines (id, geom) values ( 2, 'LINESTRING(0 1, 1 1)');
insert into lines (id, geom) values ( 3, 'LINESTRING(1 1, 1 2)');
insert into lines (id, geom) values ( 4, 'LINESTRING(1 2, 2 2)');
insert into lines (id, geom) values ( 11, 'LINESTRING(10 10, 10 11)');
insert into lines (id, geom) values ( 12, 'LINESTRING(10 11, 11 11)');
insert into lines (id, geom) values ( 13, 'LINESTRING(11 11, 11 12)');
insert into lines (id, geom) values ( 14, 'LINESTRING(11 12, 12 12)');
create index lines_gix on lines using gist(geom);

अब, यहाँ एक पुनरावर्ती फ़ंक्शन है, जो एक किनारे की आईडी को देखते हुए, स्पर्श करने वाले सभी किनारों को जमा करता है:

CREATE OR REPLACE FUNCTION find_connected(integer) returns integer[] AS
$$
WITH RECURSIVE lines_r AS (
  SELECT ARRAY[id] AS idlist, geom, id
  FROM lines 
  WHERE id = $1
  UNION ALL
  SELECT array_append(lines_r.idlist, lines.id) AS idlist, 
         lines.geom AS geom, 
         lines.id AS id
  FROM lines, lines_r
  WHERE ST_Touches(lines.geom, lines_r.geom)
  AND NOT lines_r.idlist @> ARRAY[lines.id]
)
SELECT 
  array_agg(id) AS idlist
  FROM lines_r
$$ 
LANGUAGE 'sql';

वह बस हमें खोजने की जरूरत छोड़ देता है, प्रत्येक समूह के जमा होने के बाद, एक किनारे की आईडी जो पहले से ही एक समूह का हिस्सा नहीं है। जो, दुख की बात है, एक दूसरी पुनरावर्ती क्वेरी की आवश्यकता है।

WITH RECURSIVE groups_r AS (
  (SELECT find_connected(id) AS idlist, 
          find_connected(id) AS grouplist, 
          id FROM lines WHERE id = 1)
  UNION ALL
  (SELECT array_cat(groups_r.idlist,find_connected(lines.id)) AS idlist,
         find_connected(lines.id) AS grouplist,
         lines.id
  FROM lines, groups_r
  WHERE NOT idlist @> ARRAY[lines.id]
  LIMIT 1)
)
SELECT id, grouplist
FROM groups_r;   

जो एक साथ लिया एक अच्छा सेट बीज आईडी और इसे जमा प्रत्येक समूह के साथ वापसी। मैं इसे मैपिंग के लिए ज्यामिति बनाने के लिए id के सरणियों को क्वेरी में वापस करने के लिए एक अभ्यास के रूप में छोड़ता हूं।

 id |   grouplist   
----+---------------
  1 | {1,2,3,4}
 11 | {11,12,13,14}
(2 rows)

मुझे लगता है कि यह कोड सरल हो सकता है, अगर PostgreSQL में ज्यामिति प्रकार का समर्थन करने वाले हैशिंग (जब आप एक सरल RCTE लिखते हैं जिसमें आईडी के जमा संचय शामिल नहीं होते हैं, तो आपको एक त्रुटि मिलती है "सभी स्तंभ डेटाटैब्स को धोने योग्य होना चाहिए), इसलिए एक है मेरे लिए थोड़ा वृद्धि का अनुरोध।
पॉल रेम्सी

यह वास्तव में भयानक दृष्टिकोण है। जैसा कि मैंने इसे बड़े परीक्षण सेट पर लागू किया है, मैं कुछ अजीब परिणाम नहीं देख रहा हूं; मैं देखूंगा कि क्या मैं इस मुद्दे को एक साधारण उदाहरण में कम कर सकता हूं। 100 लाइनें: 85 क्लस्टर, सबसे बड़ा क्लस्टर = 3, 0.03 s //// 200 लाइनें: 144 क्लस्टर, सबसे बड़ा क्लस्टर = 9, 0.08 s //// 300 लाइनें: 180 क्लस्टर, सबसे बड़ा क्लस्टर = 51, 0.16 s /// / 400 लाइनें: 188 क्लस्टर, सबसे बड़ा क्लस्टर = 41, 0.27 s //// 500 लाइनें: 176 क्लस्टर, सबसे बड़ा क्लस्टर = 112, 0.56 s //// 600 लाइनें: 143 क्लस्टर, सबसे बड़ा क्लस्टर = 449, 1.0 s //। // 650 लाइनें: 133 क्लस्टर, सबसे बड़ा क्लस्टर = 7601, 6.8 एस
dbaston

इसे परीक्षण डेटा में जोड़ने से grouplistसरणी में डुप्लिकेट आईडी का कारण होगा insert into lines (id, geom) values ( 15, 'LINESTRING(0 0, 10 10)');:। समस्या को हल करने के लिए array_agg(id)फ़ंक्शन रिटर्न में परिवर्तन array_agg(DISTINCT id)प्रतीत होता है।
dbaston

यह एक अच्छा समाधान है, इसलिए अब हम ज्यामितीय को एक तालिका में कैसे संग्रहीत कर सकते हैं ताकि हम जुड़े हुए लाइनों को देख सकें?
ज़कारिया मोइकिट

6

यहाँ एक दृष्टिकोण है जो एक साथ अस्थायी रूप से समुच्चय समूहों के लिए एक अस्थायी तालिका का उपयोग करता है। मैं वास्तव में अस्थायी तालिका दृष्टिकोण की परवाह नहीं करता हूं, लेकिन यह बहुत अच्छा प्रदर्शन करता है क्योंकि लाइनों की संख्या बढ़ जाती है (मेरे इनपुट में 1.2 एम लाइनें हैं)।

DO
$$
DECLARE
this_id bigint;
this_geom geometry;
cluster_id_match integer;

id_a bigint;
id_b bigint;

BEGIN
DROP TABLE IF EXISTS clusters;
CREATE TABLE clusters (cluster_id serial, ids bigint[], geom geometry);
CREATE INDEX ON clusters USING GIST(geom);

-- Iterate through linestrings, assigning each to a cluster (if there is an intersection)
-- or creating a new cluster (if there is not)
FOR this_id, this_geom IN SELECT id, geom FROM lines LOOP
  -- Look for an intersecting cluster.  (There may be more than one.)
  SELECT cluster_id FROM clusters WHERE ST_Intersects(this_geom, clusters.geom)
     LIMIT 1 INTO cluster_id_match;

  IF cluster_id_match IS NULL THEN
     -- Create a new cluster
     INSERT INTO clusters (ids, geom) VALUES (ARRAY[this_id], this_geom);
  ELSE
     -- Append line to existing cluster
     UPDATE clusters SET geom = ST_Union(this_geom, geom),
                          ids = array_prepend(this_id, ids)
      WHERE clusters.cluster_id = cluster_id_match;
  END IF;
END LOOP;

-- Iterate through the clusters, combining clusters that intersect each other
LOOP
    SELECT a.cluster_id, b.cluster_id FROM clusters a, clusters b 
     WHERE ST_Intersects(a.geom, b.geom)
       AND a.cluster_id < b.cluster_id
      INTO id_a, id_b;

    EXIT WHEN id_a IS NULL;
    -- Merge cluster A into cluster B
    UPDATE clusters a SET geom = ST_Union(a.geom, b.geom), ids = array_cat(a.ids, b.ids)
      FROM clusters b
     WHERE a.cluster_id = id_a AND b.cluster_id = id_b;

    -- Remove cluster B
    DELETE FROM clusters WHERE cluster_id = id_b;
END LOOP;
END;
$$ language plpgsql;

पूरी तरह से काम करता है
ज़कारिया माउकिट

@zakariamouqcit खुशी है कि यह आपके लिए काम कर रहा है! ST_ClusterIntersectingपोस्टजीआईएस में फ़ंक्शन लिखने से पहले मैंने यह उत्तर लिखा था । यदि आपका डेटा मेमोरी में फिट होने के लिए पर्याप्त छोटा है, तो मैं सुझाव दूंगा कि अधिक निष्पादन समाधान के लिए।
dbaston

इस सवाल की खोज मुझे यहाँ ले आई। पुनरावृति और st_clusterintersecting की कोशिश की, लेकिन सबसे उपयुक्त होने के लिए st_clusterDBScan पाया। मामले में किसी और को भी यहां लाया जाता है। postgis.net/docs/manual-dev/ST_ClusterDBSCAN.html
D_C

सहमत, ST_ClusterDBSCAN लगभग हमेशा सबसे अच्छा तरीका है PostGIS 2.3+ के लिए जाने के लिए
dbaston
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.