SG দূরত্ব, কেএনএন সহ নিকটতম পয়েন্টগুলি পোস্টজিআইএস করুন


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")

এটির সাথে আমি প্রতিটি ট্র্যাফিক_সাইন থেকে প্রতিটি প্রবেশদ্বার_হলে দূরত্ব পাচ্ছি। তবে আমি কীভাবে কেবল মিনিমুন দূরত্ব পেতে পারি?

শুভেচ্ছা সহ,


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)যদি সমস্ত ন্যূনতম দূরত্ব বলে জানা যায় তবে আপনি এটি বাড়িয়ে দিতে পারেন eg 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");

স্পষ্টতই, এটি সতর্কতার সাথে ব্যবহার করা দরকার, যেন ন্যূনতম দূরত্ব বেশি হয় তবে আপনি কেবল সেনাল এবং বন্দরের সংমিশ্রনের জন্য কোনও সারি পাবেন না।

দ্রষ্টব্য: আদেশ অনুসারে ক্রমটি অবশ্যই অর্ডের সাথে স্বতন্ত্রের সাথে মিলবে, যা বোঝা যায়, কারণ কিছু আদেশের ভিত্তিতে স্বতন্ত্র প্রথম স্বতন্ত্র গ্রুপটি গ্রহণ করে।

ধারণা করা হয় যে উভয় টেবিলে আপনার স্থানিক সূচক রয়েছে।

সম্পাদনা 1 । আরেকটি বিকল্প রয়েছে, যা পোস্টগ্রিসের <-> এবং <#> অপারেটরগুলি (যথাক্রমে সেন্টার পয়েন্ট এবং বাউন্ডিং বক্সের দূরত্ব গণনা) ব্যবহার করা হয় যা স্থানিক সূচকের আরও দক্ষ ব্যবহার করে এবং এন এড়ানোর জন্য এসT_ডুইথিন হ্যাকের প্রয়োজন হয় না । 2 তুলনা। তারা কীভাবে কাজ করে তা বোঝানোর জন্য একটি ভাল ব্লগ নিবন্ধ রয়েছে । সাধারণ বিষয় লক্ষণীয় যে এই দুটি অপারেটর অর্ডার বাই ক্লজে কাজ করে।

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

সম্পাদনা 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 টি বন্দরগুলি সন্ধান করতে চান তবে আপনাকে কেবল পাশের সাব-কোয়েরিতে LIMIT টি ধারা পরিবর্তন করতে হবে। ল্যাটারাল জয়েনগুলি ছাড়া এটি করা খুব শক্ত এবং এআরএআর টাইপের যুক্তি ব্যবহারের সাথে জড়িত। এই পদ্ধতিরটি ভালভাবে কাজ করার সময়, এটি যদি আপনি জানেন তবে আপনাকে কেবল একটি নির্দিষ্ট দূরত্বে সন্ধান করতে হবে তবে তা প্রচুর পরিমাণে বাড়ানো যেতে পারে। এই উদাহরণস্বরূপ, আপনি উপকোয়ায় ST_DWithin (চিহ্ন.জম, পোর্টস.জম, 1000) ব্যবহার করতে পারেন , যেহেতু <-> অপারেটরের সাথে ইনডেক্সিং কাজ করে - কারণ জ্যামিতির একটি স্থির হওয়া উচিত কলাম রেফারেন্স - অনেক দ্রুত হতে পারে। সুতরাং, উদাহরণস্বরূপ, 3 নিকটতম বন্দর পেতে, 10 কিলোমিটারের মধ্যে, আপনি নীচের মতো কিছু লিখতে পারেন।

 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;

সর্বদা হিসাবে, ব্যবহার আপনার ডেটা বিতরণ এবং ক্যোয়ারির উপর নির্ভর করে পরিবর্তিত হবে, সুতরাং আপনার সেরা বন্ধু হিসাবে ব্যাখ্যা করুন

অবশেষে, একটি ছোটখাটো গ্যাচা রয়েছে, যদি পাশের ক্রস লেটারেলের পরিবর্তে বাম ব্যবহার করা হয় তবে আপনাকে পাশ্ববর্তী ক্যোয়ারী নামগুলির পরে সত্য যুক্ত করতে হবে , উদাহরণস্বরূপ,

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 ব্যবহার করতে পারবেন কিনা তা নির্ভর করে। তবে, হ্যাঁ, পয়েন্ট নেওয়া হয়েছে। দুর্ভাগ্যক্রমে, <-> / <#> অপারেটর দ্বারা অর্ডার জ্যামিতির একটি ধ্রুবক হতে হবে, না?
জন পাওয়েল

@ জনপাউল্লাকা বারিয়াকে আপনি কি জানবেন যে আজকাল সেই ব্লগ পোস্টটি কোথায় থাকে? - বা, <-> এবং <#> অপারেটরগুলির একটি অনুরূপ ব্যাখ্যা? ধন্যবাদ !!
ডিপিএসএসপিশিয়াল

@DPSSpatial, এটি বিরক্তিকর। আমি না, কিন্তু এই এবং এই এটি যা এই পদ্ধতির সম্পর্কে কিছুটা কথা বলে। দ্বিতীয়টি, পাশ্বর্ ব্যবহার করেও যোগ দেয়, যা অন্য একটি আকর্ষণীয় বর্ধন।
জন পাওয়েল 16

@DPSSpatial। এটি এই <->, <#> এবং পার্শ্বীয় যোগদানের স্টাফগুলি কিছুটা পিচ্ছিল। আমি খুব বড় ডেটাসেটের সাহায্যে এটি করেছি এবং পারফরম্যান্সটি ভয়াবহ হয়ে উঠেছে, এস এসডিডিবিথিন ব্যবহার না করে, যা এগুলি এড়াতে হবে বলে মনে করা হচ্ছে। শেষ পর্যন্ত, জীড়ন একটি সমৃদ্ধ সমস্যা, তাই ব্যবহারের ভিন্নতা থাকতে পারে। শুভকামনা :-)
জন পাওয়েল

13

LATERAL JOINপোস্টগ্র্রেএসকিউএল 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

ক্রস-জয়েন সহ অ্যাপ্রোচ সূচকগুলি ব্যবহার করে না এবং প্রচুর মেমরির প্রয়োজন। সুতরাং আপনার মূলত দুটি পছন্দ আছে। প্রাক 9.3 পূর্ব আপনি একটি সহযোগিত সাবকোয়ারি ব্যবহার করবেন। 9.3+ আপনি এ ব্যবহার করতে পারেন LATERAL JOIN

পার্শ্ববর্তী মোচড়ের সাথে KNN জিআইএসটি শীঘ্রই আপনার কাছাকাছি একটি ডাটাবেসে আসবে

(শিগগিরই অনুসরণ করার সঠিক প্রশ্নগুলি)


1
পার্শ্বযুক্ত যোগদানের শীতল ব্যবহার। এই প্রসঙ্গে আগে দেখা হয়নি।
জন পাওয়েল

1
@ জনবারিয়া এটি আমার দেখা অন্যতম সেরা প্রসঙ্গ। আমার সন্দেহ হয় যে এটি কার্যকর হবে যখন আপনার সত্যিকারের ST_DISTANCE()নিকটতম বহুভুজ এবং ক্রস জয়েন সন্ধানের জন্য সার্ভারের স্মৃতিশক্তি শেষ হয়ে যাওয়ার কারণ প্রয়োজন find নিকটতম বহুভুজের ক্যোয়ারী এখনও অচল নয় আফাইক।
জাকুব কানিয়া

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;
blackgis

1
ঠিক আছে, আমি আপনাকে এখনই পেতে। @ ডাবাস্টনের জবাব অনুসারে ল্যাটারাল জয়েন্ট পদ্ধতির ব্যবহার করা আসলে সম্ভবত আরও ভাল, এটি স্পষ্ট করে দেয় যে ঘনিষ্ঠতার দিক থেকে অন্য জিনিসটির সাথে কোন জিনিসটির তুলনা করা হচ্ছে। আমি আরও উপরে পদ্ধতির ব্যবহার না।
জন পাওয়েল
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.