ST_Distance, kNN के साथ निकटतम बिंदु PostGIS


23

मुझे प्रत्येक तत्व को एक तालिका पर किसी अन्य तालिका के निकटतम बिंदु पर प्राप्त करने की आवश्यकता है। पहली तालिका में ट्रैफ़िक संकेत और दूसरे में शहर का प्रवेश हॉल शामिल है। बात यह है कि मैं ST_ClosestPoint फ़ंक्शन का उपयोग नहीं कर सकता और मुझे ST_Distance फ़ंक्शन का उपयोग करना होगा और मिनट (ST_distance) रिकॉर्ड प्राप्त करना होगा, लेकिन मैं क्वेरी का निर्माण कर रहा हूं।

CREATE TABLE traffic_signs
(
  id numeric(8,0) ),
  "GEOMETRY" geometry,
  CONSTRAINT traffic_signs_pkey PRIMARY KEY (id),
  CONSTRAINT traffic_signs_id_key UNIQUE (id)
)
WITH (
  OIDS=TRUE
);

CREATE TABLE entrance_halls
(
  id numeric(8,0) ),
  "GEOMETRY" geometry,
  CONSTRAINT entrance_halls_pkey PRIMARY KEY (id),
  CONSTRAINT entrance_halls_id_key UNIQUE (id)
)
WITH (
  OIDS=TRUE
);

मुझे प्रत्येक ट्रैफ़िक_साइन के निकटतम एंट्रेंस_हॉल की आईडी प्राप्त करने की आवश्यकता है।

मेरा प्रश्न अब तक:

SELECT senal.id,port.id,ST_Distance(port."GEOMETRY",senal."GEOMETRY")  as dist
    FROM traffic_signs As senal, entrance_halls As port   
    ORDER BY senal.id,port.id,ST_Distance(port."GEOMETRY",senal."GEOMETRY")

इसके साथ मुझे हर traffic_sign से हर प्रवेश द्वार_ दूरी की दूरी मिल रही है। लेकिन मैं केवल न्यूनतम दूरी कैसे प्राप्त कर सकता हूं?

सादर,


PostgreSQL का क्या संस्करण?
जैकब कानिया

जवाबों:


41

आप लगभग वहाँ हैं। थोड़ी चाल है जो पोस्टग्रेज के अलग-अलग ऑपरेटर का उपयोग करना है , जो प्रत्येक संयोजन के पहले मैच को वापस कर देगा - जैसा कि आप ST_Distance द्वारा आदेश दे रहे हैं, प्रभावी रूप से यह प्रत्येक बंदरगाह से प्रत्येक सेनल से निकटतम बिंदु वापस आ जाएगा।

SELECT 
   DISTINCT ON (senal.id) senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY")  as dist
FROM traffic_signs As senal, entrance_halls As port   
ORDER BY senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY");

यदि आप जानते हैं कि प्रत्येक मामले में न्यूनतम दूरी कुछ राशि x से अधिक नहीं है, (और आपकी तालिकाओं पर एक स्थानिक सूचकांक है), तो आप इसको गति प्रदान कर सकते हैं WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", distance), जैसे, यदि सभी ऋण दूरी ज्ञात की जाती हैं 10 किमी से अधिक नहीं, फिर:

SELECT 
   DISTINCT ON (senal.id) senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY")  as dist
FROM traffic_signs As senal, entrance_halls As port  
WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", 10000) 
ORDER BY senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY");

जाहिर है, इसे सावधानी के साथ उपयोग करने की आवश्यकता है, जैसे कि न्यूनतम दूरी अधिक होने पर, आपको बस सीनल और पोर्ट के संयोजन के लिए कोई पंक्ति नहीं मिलेगी।

नोट: ऑर्डर द्वारा ऑर्डर ऑर्डर पर अलग से मेल खाना चाहिए, जो समझ में आता है, क्योंकि अलग-अलग कुछ ऑर्डर करने के आधार पर अलग-अलग पहला समूह ले रहा है।

यह माना जाता है कि आपके पास दोनों तालिकाओं पर एक स्थानिक सूचकांक है।

EDIT 1 । एक और विकल्प है, जो Postgres के <-> और <#> ऑपरेटरों का उपयोग करना है, (केंद्र बिंदु और क्रमशः बॉक्स दूरी की गणना,) जो स्थानिक सूचकांक का अधिक कुशल उपयोग करते हैं और n से बचने के लिए ST_DWithin हैक की आवश्यकता नहीं होती है ^ 2 तुलना। एक अच्छा ब्लॉग लेख है जो बताता है कि वे कैसे काम करते हैं। सामान्य बात यह है कि ये दोनों ऑपरेटर ORDER BY क्लॉज में काम करते हैं।

SELECT senal.id, 
  (SELECT port.id 
   FROM entrance_halls as port 
   ORDER BY senal.geom <#> port.geom LIMIT 1)
FROM  traffic_signs as senal;

EDIT 2 । जैसा कि इस सवाल पर बहुत अधिक ध्यान दिया गया है और के-निकटतम पड़ोसियों (केएनएन) जीआईएस में आम तौर पर एक कठिन समस्या है (एल्गोरिदमिक रन-टाइम के संदर्भ में), इस प्रश्न के मूल दायरे पर कुछ हद तक विस्तार करना सार्थक लगता है।

एक वस्तु के x निकटतम पड़ोसियों को खोजने का मानक तरीका एक लोट्टल जॉयइन (प्रत्येक लूप के लिए वैचारिक रूप से समान) का उपयोग करना है। डबस्टन के जवाब से बेशर्मी से बोलना , आप कुछ ऐसा करेंगे:

SELECT
  signs.id,
  closest_port.id,
  closest_port.dist
 FROM traffic_signs
CROSS JOIN LATERAL 
  (SELECT
      id, 
      ST_Distance(ports.geom, signs.geom) as dist
      FROM ports
      ORDER BY signs.geom <-> ports.geom
     LIMIT 1
   ) AS closest_port

इसलिए, यदि आप दूरी द्वारा आदेश दिए गए निकटतम 10 बंदरगाहों को खोजना चाहते हैं, तो आपको बस पार्श्व उप-क्वेरी में लिमिट क्लॉज को बदलना होगा। LATERAL JOINS के बिना ऐसा करना बहुत कठिन है और इसमें ARRAY प्रकार के तर्क का उपयोग करना शामिल है। जबकि यह दृष्टिकोण अच्छी तरह से काम करता है, इसे काफी हद तक फैलाया जा सकता है यदि आप जानते हैं कि आपको केवल एक निश्चित दूरी पर खोज करना है। इस उदाहरण में, आप ST_DWithin (signs.geom, ports.geom, 1000) का उपयोग उपकुंजी में कर सकते हैं , क्योंकि जिस तरह से इंडेक्सिंग <-> ऑपरेटर के साथ काम करता है - एक ज्यामितीय का एक स्थिर होना चाहिए, बजाय स्तंभ संदर्भ - बहुत तेज हो सकता है। इसलिए, उदाहरण के लिए, 10 किमी के भीतर 3 निकटतम बंदरगाहों को प्राप्त करने के लिए, आप निम्नलिखित की तरह कुछ लिख सकते हैं।

 SELECT
  signs.id,
  closest_port.id,
  closest_port.dist
 FROM traffic_signs
CROSS JOIN LATERAL 
  (SELECT
      id, 
      ST_Distance(ports.geom, signs.geom) as dist
      FROM ports
      WHERE ST_DWithin(ports.geom, signs.geom, 10000)
      ORDER BY ST_Distance(ports.geom, signs.geom)
     LIMIT 3
   ) AS closest_port;

हमेशा की तरह, उपयोग आपके डेटा वितरण और प्रश्नों के आधार पर अलग-अलग होगा, इसलिए EXPLAIN आपका सबसे अच्छा दोस्त है।

अंत में, एक छोटी सी पकड़ लिया है, का उपयोग कर यदि वाम के बजाय क्रॉस पार्श्व में शामिल हों आप जोड़ने के लिए है कि सच पार्श्व प्रश्नों उर्फ के बाद, जैसे,

SELECT
  signs.id,
  closest_port.id,
  closest_port.dist
 FROM traffic_signs
LEFT JOIN LATERAL 
  (SELECT
      id, 
      ST_Distance(ports.geom, signs.geom) as dist
      FROM ports          
      ORDER BY signs.geom <-> ports.geom
      LIMIT 1
   ) AS closest_port
   ON TRUE;

यह ध्यान दिया जाना चाहिए कि यह डेटा की बड़ी मात्रा के साथ अच्छा प्रदर्शन नहीं करेगा।
जैकब कानिया

@JakubKania। यह इस बात पर निर्भर करता है कि आप ST_DWithin का उपयोग कर सकते हैं या नहीं। लेकिन, हाँ, बिंदु लिया गया। दुर्भाग्य से, ऑर्डर द्वारा <-> / <#> ऑपरेटर को एक ज्यामितीय की आवश्यकता होती है एक स्थिर, नहीं?
जॉन पॉवेल

@ JohnPowellakaBarça आपको कोई भी मौका पता है कि आजकल ब्लॉग पोस्ट कहाँ रहता है? - या, <-> और <#> ऑपरेटरों के समान विवरण? धन्यवाद!!
DPSSpatial

@DPSSpatial, यह कष्टप्रद है। मैं नहीं, लेकिन यह है और यह जो इस दृष्टिकोण के बारे में थोड़ा बात करते हैं। 2 लेटर, लेटरल का उपयोग करके भी जुड़ता है, जो एक और दिलचस्प वृद्धि है।
जॉन पॉवेल

@DPSSpatial। यह सब थोड़ा फिसलन है इस <->, <#> और पार्श्व सामान में शामिल हो। मैंने बहुत बड़े डेटासेट के साथ ऐसा किया है और ST_DWithin का उपयोग किए बिना प्रदर्शन भयानक रहा है, जिससे सभी को बचना चाहिए। अंततः, knn एक जटिल समस्या है, इसलिए उपयोग भिन्न हो सकते हैं। सौभाग्य :-)
जॉन पॉवेल

13

यह LATERAL JOINPostgreSQL 9.3+ में एक के साथ किया जा सकता है :

SELECT
  signs.id,
  closest_port.id,
  closest_port.dist
FROM traffic_signs
CROSS JOIN LATERAL 
  (SELECT
     id, 
     ST_Distance(ports.geom, signs.geom) as dist
     FROM ports
     ORDER BY signs.geom <-> ports.geom
   LIMIT 1) AS closest_port

10

क्रॉस-ज्वाइन के साथ aproach इंडेक्स का उपयोग नहीं करता है और बहुत अधिक मेमोरी की आवश्यकता होती है। इसलिए आपके पास मूल रूप से दो विकल्प हैं। 9.3 आप एक सहसंबद्ध उपसमुच्चय का उपयोग करेंगे। 9.3+ आप एक का उपयोग कर सकते हैं LATERAL JOIN

केएनएन जिस्ट एक लेटरल ट्विस्ट के साथ जल्द ही आपके पास एक डेटाबेस में आ रहा है

(सटीक प्रश्नों का जल्द ही पालन करें)


1
एक पार्श्व जुड़ने का ठंडा उपयोग। इस संदर्भ में पहले नहीं देखा था।
जॉन पॉवेल

1
@ JohnBarça यह मेरे द्वारा देखे गए सबसे अच्छे संदर्भों में से एक है। मुझे यह भी संदेह है कि यह तब उपयोगी होगा जब आपको वास्तव में ST_DISTANCE()निकटतम बहुभुज को खोजने के लिए उपयोग करने की आवश्यकता होती है और क्रॉस ज्वाइन होने के कारण सर्वर स्मृति से बाहर चला जाता है। निकटतम बहुभुज क्वेरी अभी भी AFAIK अनसुलझी है।
जकूब कानिया

2

@ जॉन बरका

आदेश द्वारा गलत है!

ORDER BY senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY");

सही

senal.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY"),port.id;

अन्यथा यह निकटतम नहीं लौटेगा, केवल जिसकी छोटी पोर्ट आईडी है


1
सही एक इस तरह दिखता है (मैं अंक और रेखाओं का उपयोग करता हूं):SELECT DISTINCT ON (points.id) points.id, lines.id, ST_Distance(lines.geom, points.geom) as dist FROM development.passed_entries As points, development."de_muc_rawSections_cleaned" As lines ORDER BY points.id, ST_Distance(lines.geom, points.geom),lines.id;
ब्लैकजिस

1
ठीक है, मैं तुम्हें अभी ले आता हूं। यह वास्तव में LATERAL JOIN दृष्टिकोण का उपयोग करने के लिए बेहतर है, जैसा कि @ dbaston के उत्तर में है, जो यह स्पष्ट करता है कि निकटता के संदर्भ में किसी अन्य चीज की तुलना में किस चीज की तुलना की जा रही है। मैं किसी भी अधिक ऊपर दृष्टिकोण का उपयोग नहीं करते।
जॉन पॉवेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.