วิธีการหาจุดที่ใกล้ที่สุดใน dateline อย่างมีประสิทธิภาพ?


10

ฉันมีตาราง PostgreSQL 9.1 พร้อมจุด PostGIS นับแสนรายการ สำหรับแต่ละสิ่งเหล่านี้ฉันต้องการค้นหาจุดที่ใกล้ที่สุดในตาราง POINTs อื่น คะแนนในตารางที่สองแสดงตารางทั่วโลกดังนั้นฉันรู้ว่าจะมีการแข่งขันภายใน 1 องศาเสมอ นี่คือคำถามที่ฉันใช้จนถึงตอนนี้ซึ่งใช้ดัชนี GIST ดังนั้นมันจึงค่อนข้างเร็ว (รวมประมาณ 30 วินาที)

SELECT DISTINCT ON (p.id)
    p.id, ST_AsText(p.pos)
    , ST_AsText(first_value(g.location) OVER (PARTITION BY p.id ORDER BY ST_Distance(p.pos, g.location::geography)))
FROM point p
JOIN grid g ON ST_DWithin(p.pos::geometry, g.location, 1)

ปัญหาเดียวคือวันที่ จุดกริดนั้นมีละติจูด 180 เท่านั้นไม่ใช่ -180 เมื่อใช้รุ่นเรขาคณิตของ ST_Distance สิ่งนี้จะไม่ส่งคืนจุดที่อีกด้านหนึ่งของวันที่ เช่น. ถ้า p.pos เป็นPOINT(-179.88056 -16.68833)จุดกริดที่ใกล้ที่สุดอาจเป็นไปได้POINT(180 -16.25)แต่แบบสอบถามข้างต้นจะไม่ส่งคืน วิธีที่ดีที่สุดในการแก้ไขปัญหานี้คืออะไร

ฉันไม่ต้องการมีสองพิกัดสำหรับจุดกริดเดียว (-180 และ +180) ฉันลองเพิ่มฟังก์ชันของตัวเองซึ่งตรวจสอบกรณีเฉพาะนี้ แต่จากนั้นแบบสอบถามจะไม่ส่งคืนภายใน 5 นาทีอาจเป็นเพราะไม่สามารถใช้ดัชนีได้อีก ฉันยังลองใช้รุ่นภูมิศาสตร์ของ ST_D ภายในนั้นและแบบสอบถามนั้นก็ไม่ได้กลับมาหลังจาก 5 นาที


เป็นคำถามที่ดี (และแฮ็คที่ฉลาดในการตอบของคุณ!) สิ่งหนึ่งที่ต้องสงสัยก็คือ: หากซอฟต์แวร์ไม่สามารถรับรู้ว่า -180 = 180 สำหรับลองจิจูดแล้วมันอาจจะแกล้งทำเป็นพิกัดที่ฉายและใช้อัลกอริทึมแบบยุคลิดเพื่อหาจุดที่ใกล้เคียงที่สุดซึ่งจะทำให้เกิดข้อผิดพลาด เส้นศูนย์สูตรขนาดใหญ่ใกล้กับเสาและ + -180 เส้นเมอริเดียน) ฉันไม่ทราบว่าสิ่งที่นำไปสู่ปัญหาที่สำคัญในใบสมัครของคุณ แต่ในหลาย ๆ กรณีมันจะเป็นเช่นนั้นและการแก้ไขปัญหานั้นจะไม่สามารถแก้ไขข้อผิดพลาดได้
whuber

จุดดี แต่ในกรณีนี้แอปพลิเคชันไคลเอนต์จะไม่ทำการคำนวณอื่น ๆ "ใกล้เคียงที่สุด" เพียงแค่จะได้รับข้อมูลบางอย่างที่เกี่ยวข้องกับจุดกริดที่ส่งคืนจากแบบสอบถามของฉัน
EM0

คำตอบ:


6

ตกลงในที่สุดฉันก็หาวิธีที่จะแฮ็คมันซึ่งไม่เพียง แต่แก้ไขปัญหา dateline แต่ยังเร็วกว่า

CREATE OR REPLACE FUNCTION nearest_grid_point(point geography(Point))
RETURNS integer
AS $BODY$
    SELECT pointid
    FROM
    (
            -- The normal case
        SELECT pointid, location
        FROM grid
        WHERE ST_DWithin($1::geometry, location, 1)

        UNION ALL

            -- The dateline hack
        SELECT pointid, location
        FROM grid
        WHERE (ST_X($1::geometry) < -178.75 AND longitude = 180)
    ) sub
    ORDER BY ST_Distance($1, location::geography)
    LIMIT 1;
$BODY$ LANGUAGE SQL STABLE;

SELECT p.id, ST_AsText(p.pos), g.pointid, ST_AsText(g.location)
FROM point p
JOIN grid g ON nearest_grid_point(p.pos) = g.pointid

ฉันประหลาดใจมากที่เห็นว่าฟังก์ชั่นนี้ซึ่งถูกเรียกใช้สำหรับทุกแถวเร็วกว่าฟังก์ชั่นหน้าต่างต้นฉบับ แต่เร็วกว่าถึง 10 เท่า ประสิทธิภาพของ PostgreSQL นั้นเป็นศิลปะสีดำจริงๆ!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.