PostGIS'te belirli mesafedeki noktalar arasında çizgi çizme?


9

Sokaklar boyunca noktalara dair verilerim var, bu noktaları basit renkli çizgilere dönüştürmek istiyorum. Bu sorunun adı verilebilecek herhangi bir işaretçi veya bunu çözmeme yardımcı olabilecek herhangi bir algoritma? Cadde üzerindeki noktalar çizgilere dönüşmek istiyorum.

Bunu yapmak için PostGISişlevleri kullanmayı umuyordum ama önerilere açığım, bu bir .shpdosyadan bir veri .

Edit1: Bu sorunun ideal çözümünü göstermek için resmi güncelledi.

Çizgiyi çizmek tamamen bu noktalar arasındaki mesafeye dayanır, onları gruplamak için kullanabileceğim başka bir şey yoktur. İdeal olarak, bu, yansıtılan çizgi boyunca belirtilen maksimum mesafedeki noktalar mıdır? Ve yansıtılan çizgi ile 1. noktayı bulduktan sonra ona en yakın olanı bir çizgi yansıtın ve bu çizgi üzerinde zaten çizgi üzerinde olanlardan maksimum mesafede herhangi bir nokta olup olmadığını kontrol edin.


1
Hangi yazılımı kullanmayı planlıyorsunuz?
ArMoraer

bunları kaldırımlara dönüştürmeye mi çalışıyorsun?
DPSSpatial

Bunu yapmak için PostGIS işlevlerini kullanmayı umuyordum ama önerilere açığım, bu bir .shp dosyasından bir veri.
Mahakala

1
Çiziminizde veya başka bir çizimde tam olarak hangi noktaları bağlamak istediğinizi gösterebilir misiniz? Bir seferde sadece iki puan mı? Yoksa üç mü? Bağlanması gereken noktalar arasındaki mesafe her zaman aynı mıdır yoksa belirli bir eşiğin altında mıdır?
Peter Horsbøll Møller

1
Hem @ dbaston hem de MarHoff'a büyük teşekkürler, Nisan ayı sonuna kadar fikirlerinizi test etmek için zamanım olmayacak, diliyorum ikramiyeyi ikiye ayırabileceğim, ancak bunu 1'e ödüllendirmem gerekecek ve dbaston bana bazı sorular da verdi onun cevabını kabul edeceğim. Cevap vermek için zaman ayıran herkese teşekkürler! Harika bir topluluk :-) parçası olmak
Mahakala

Yanıtlar:


8

Oluşturmak istediğiniz satırların algılanan her bir ucundan başlayarak her noktanın en yakın komşusunu keşfetmek için özyinelemeli bir sorgu kullanabilirsiniz .

Önkoşullar : Yollarınızı içeren tek bir Multi-linestring nesnesiyle noktalarınızla bir postgis katmanı hazırlayın. İki katmanın aynı CRS üzerinde olması gerekir. İşte oluşturduğum test veri kümesinin kodu, lütfen gerektiği gibi değiştirin. (Postgres 9.2 ve postgis 2.1 üzerinde test edilmiştir)

WITH RECURSIVE
points as (SELECT id, st_transform((st_dump(wkb_geometry)).geom,2154) as geom, my_comment as com FROM mypoints),
roads as (SELECT st_transform(ST_union(wkb_geometry),2154) as geom from highway),

resim açıklamasını buraya girin

İşte adımlar :

  1. Her bir nokta için, bu üç kriteri karşılayan her komşu ve onların mesafe listesini oluşturun.

    • Mesafe, kullanıcı tanımlı bir eşiği aşmamalıdır (bu, izole noktaya bağlantı yapılmasını önler) resim açıklamasını buraya girin
      graph_full as (
      SELECT a.id, b.id as link_id, a.com, st_makeline(a.geom,b.geom) as geom, st_distance(a.geom,b.geom) as distance
      FROM points a
      LEFT JOIN points b ON a.id<>b.id
      WHERE st_distance(a.geom,b.geom) <= 15
      ),
    • Doğrudan yol bir yoldan geçmemelidir resim açıklamasını buraya girin
      graph as (
      SELECt graph_full.*
      FROM graph_full RIGHT JOIN
      roads ON st_intersects(graph_full.geom,roads.geom) = false
      ),
    • Mesafe, en yakın komşudan uzaklığın kullanıcı tanımlı bir oranını aşmamalıdır (bu, sabit mesafeden daha düzensiz dijitalleşmeye daha iyi uyum sağlamalıdır) Bu parçanın uygulanması çok zordu, sabit arama yarıçapına yapıştı

    Bu tabloya "grafik" diyelim

  2. Grafiğe katılarak ve grafikte yalnızca bir girişi olan noktayı tutarak çizgi sonu noktasını seçin. resim açıklamasını buraya girin

    eol as (
    SELECT points.* FROM
    points  JOIN
    (SELECT id, count(*) FROM graph 
    GROUP BY id
    HAVING count(*)= 1) sel
    ON points.id = sel.id),

    Bu tabloya "eol" (satır sonu) diyelim
    kolay mı? harika bir grafik yapmanın ama bir şeyleri tutmanın ödülünün bir sonraki adımda çıldırması

  3. Her bir eol'den başlayarak komşulardan komşulara geçiş yapacak bir özyinelemeli sorgu ayarlama resim açıklamasını buraya girin

    • Eol tablosunu kullanarak ve derinlik için bir sayaç, yol için bir toplayıcı ve çizgileri oluşturmak için bir geometri yapıcısı kullanarak özyinelemeli sorguyu başlatın
    • Grafiği kullanarak en yakın komşuya geçip yolu kullanarak asla geriye gitmediğinizi kontrol ederek bir sonraki yinelemeye geçin
    • Yineleme bittikten sonra, her başlangıç ​​noktası için yalnızca en uzun yolu saklayın (veri kümeniz, parçanın daha fazla koşula ihtiyaç duyacağı beklenen çizgiler arasında potansiyel kavşak içeriyorsa)
    recurse_eol (id, link_id, depth, path, start_id, geom) AS (--initialisation
    SELECT id, link_id, depth, path, start_id, geom FROM (
        SELECT eol.id, graph.link_id,1 as depth,
        ARRAY[eol.id, graph.link_id] as path,
        eol.id as start_id,
        graph.geom as geom,
        (row_number() OVER (PARTITION BY eol.id ORDER BY distance asc))=1 as test
        FROM eol JOIn graph ON eol.id = graph.id 
        ) foo
    WHERE test = true
    
    UNION ALL ---here start the recursive part
    
    SELECT id, link_id, depth, path, start_id, geom  FROM (
        SELECT graph.id, graph.link_id, r.depth+1 as depth,
        path || graph.link_id as path,
        r.start_id,
        ST_union(r.geom,graph.geom) as geom,
        (row_number() OVER (PARTITION BY r.id ORDER BY distance asc))=1 as test
        FROM recurse_eol r JOIN graph ON r.link_id = graph.id AND NOT graph.link_id = ANY(path)) foo
    WHERE test = true AND depth < 1000), --this last line is a safe guard to stop recurring after 1000 run adapt it as needed

    Bu tabloya "recurse_eol" diyelim

  4. Her başlangıç ​​noktası için yalnızca en uzun satırı tutun ve her yinelenen her yolu kaldırın Örnek: yollar 1,2,3,5 VE 5,3,2,1, iki farklı "satır sonu" tarafından keşfedilen aynı satırdır

    result as (SELECT start_id, path, depth, geom FROM
    (SELECT *,
    row_number() OVER (PARTITION BY array(SELECT * FROM unnest(path) ORDER BY 1))=1 as test_duplicate,
    (max(depth) OVER (PARTITION BY start_id))=depth as test_depth
    FROM recurse_eol) foo
    WHERE  test_depth = true AND test_duplicate = true)
    
    SELECT * FROM result
  5. Kalan hataları manuel olarak kontrol eder (yalıtılmış noktalar, çakışan çizgiler, garip şekilli sokak)


Söz verildiği gibi güncellenmiş, yine de bazen yinelemeli sorgunun aynı satırın ters eolünden başlarken neden tam olarak aynı sonucu vermediğini anlayamıyorum, bu nedenle bazı yinelemeler şu anda sonuç katmanında kalabilir.

Tamamen bu kodun daha fazla yoruma ihtiyaç duyduğunu sormaktan çekinmeyin. İşte tam sorgu:

WITH RECURSIVE
points as (SELECT id, st_transform((st_dump(wkb_geometry)).geom,2154) as geom, my_comment as com FROM mypoints),
roads as (SELECT st_transform(ST_union(wkb_geometry),2154) as geom from highway),

graph_full as (
    SELECT a.id, b.id as link_id, a.com, st_makeline(a.geom,b.geom) as geom, st_distance(a.geom,b.geom) as distance
    FROM points a
    LEFT JOIN points b ON a.id<>b.id
    WHERE st_distance(a.geom,b.geom) <= 15
    ),

graph as (
    SELECt graph_full.*
    FROM graph_full RIGHT JOIN
    roads ON st_intersects(graph_full.geom,roads.geom) = false
    ),

eol as (
    SELECT points.* FROM
    points  JOIN
        (SELECT id, count(*) FROM graph 
        GROUP BY id
        HAVING count(*)= 1) sel
    ON points.id = sel.id),


recurse_eol (id, link_id, depth, path, start_id, geom) AS (
    SELECT id, link_id, depth, path, start_id, geom FROM (
        SELECT eol.id, graph.link_id,1 as depth,
        ARRAY[eol.id, graph.link_id] as path,
        eol.id as start_id,
        graph.geom as geom,
        (row_number() OVER (PARTITION BY eol.id ORDER BY distance asc))=1 as test
        FROM eol JOIn graph ON eol.id = graph.id 
        ) foo
    WHERE test = true

UNION ALL
    SELECT id, link_id, depth, path, start_id, geom  FROM (
        SELECT graph.id, graph.link_id, r.depth+1 as depth,
        path || graph.link_id as path,
        r.start_id,
        ST_union(r.geom,graph.geom) as geom,
        (row_number() OVER (PARTITION BY r.id ORDER BY distance asc))=1 as test
        FROM recurse_eol r JOIN graph ON r.link_id = graph.id AND NOT graph.link_id = ANY(path)) foo
    WHERE test = true AND depth < 1000),

result as (SELECT start_id, path, depth, geom FROM
    (SELECT *,
    row_number() OVER (PARTITION BY array(SELECT * FROM unnest(path) ORDER BY 1))=1 as test_duplicate,
    (max(depth) OVER (PARTITION BY start_id))=depth as test_depth
    FROM recurse_eol) foo
WHERE  test_depth = true AND test_duplicate = true)

SELECT * FROM result

Merhaba @MarHoff, Cevabınız için teşekkürler Sonra gidecek bir şeyim var. Tam bir çözüm beklemiyordum, sadece cevapları arayabileceğimiz bir işaretçi. Bunu daha fazla anlamak istiyorum ve kazmaya devam edeceğim ve muhtemelen daha sonra sorularım olacak. Algoritmanızı anlamam gerekiyor ve bu yine de biraz zamanımı alacak :)
Mahakala

Çalışan bir script var, önizleme burada qgiscloud.com/MarHoff/test_qgiscloud_bis çoğaltma için küçük bir uyarı kalır ... Artık ödül yok, artık baskı yok sanırım, bu yüzden serbest bıraktığım zaman serbest bırakacağım. Bu bulmaca eğlenceliydi
MarHoff

teşekkür ederim @ MarHoff, eğer yapabilseydim bu ödülü bölebilirdim, sana nasıl puan verebileceğimi göremiyorum ama buna ve kanıtına baktığın için çok teşekkürler. Orijinal görünüyor :)
Mahakala

Bitti. Bulmaca için teşekkürler, ve ranting için üzgünüm. Diğer cevap sizin için yaptıysa, o zaman tamamen basit bir şey en iyisidir ... Cevabım belki biraz düşünmekteydi. CTE + özyinelemeli sorgu + Windows işlevi + postgis tek bir sorguda kullanarak güzel bir örnek olsa da;)
MarHoff

8

@FelixIP'in işaret ettiği gibi, ilk adım her satırı oluşturacak noktaları bulmaktır. Bunu, maksimum ayırma mesafenizle ST_ClusterWithin'i arayarak yapabilirsiniz :

SELECT
  row_number() OVER () AS cid, 
  (ST_Dump(geom)).geom 
FROM (
  SELECT unnest(st_clusterwithin(geom, 0.05)) AS geom 
  FROM inputs) sq

Ardından, her kümedeki tüm noktalar boyunca bir çizgi oluşturmak için bazı sezgisel yöntemler kullanmanız gerekir. Örneğin, istediğiniz satırların Y-monoton olduğunu varsayabilirseniz, her kümedeki noktaları sıralayabilir ve ST_MakeLine'a besleyebilirsiniz . Hepsini bir araya getirmek şöyle görünecektir:

SELECT 
  ST_MakeLine(geom ORDER BY ST_Y(geom)) AS geom
FROM (
  SELECT row_number() OVER () AS cid, 
  (ST_Dump(geom)).geom FROM (
    SELECT unnest(st_clusterwithin(geom, 0.05)) AS geom 
    FROM inputs) sq) ssq 
GROUP BY cid

Veri seti kavisli yol içeriyorsa, Y-monoton (hatta X / Y-monoton arasında geçiş) yaklaşımı iyi çalışmaz. Durum böyle mi? Sipariş algoritması bu sorunun en zor kısmı IMHO.
MarHoff

@MarHoff: evet kavisli yollar bir sorun olacak, ancak verilerin çoğunun otomatik olarak dönüştürülmesini sağlamaya çalışıyorum ve geri kalanı manuel olarak yapılmalıdır. Ya da çözümü bulmak için konuyu daha fazla araştırmaya devam edeceğim, ancak birisinin kalan verileri düzeltmesini sağlamak daha uzun sürebilir. Karar verebilmek için sonuçları değerlendirmem gerekecek. Bunu işaret ettiğiniz için teşekkürler!
Mahakala

Statut ayarlı Sadece kontrol etmek için bir hile düşündüm ...
MarHoff

Bunu yapmanın tüm olası sıralama düzenlerini denemeyi ve hangisinin en kısa toplam uzunluğu verdiğini bulmayı içermeyen sağlam bir yolu var mı?
dbaston

Bu nokta kümeleri her zaman yolları takip ederse, noktanın konumunu yol segmentine (ST_Line_Locate_Point) yansıtırsınız ve ardından noktaları sonuca göre sıralarsınız.
travis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.