Znajdowanie najbliższej geometrii w PostGIS

16

Przeglądałem „API” funkcji PostGIS i zauważam, że większość z nich wymaga porównania dwóch elementów. Na przykład funkcja ST_Distance potrzebuje dwóch elementów geometrii / geografii, aby znaleźć odległość.

Nie ma funkcji, aby wykonać coś takiego: „Biorąc pod uwagę geometrię G, podaj mi najbliższą geometrię GClosest w tabeli T, gdzie G.id <> GClosest.id”

Zdaję sobie sprawę, że mógłbym napisać funkcję PL / PgSQL do iteracji po stole i wywoływać ST_Distance na każdym elemencie, ale mam nadzieję, że istnieje lepsze, bardziej wydajne rozwiązanie.

Jmoney38
źródło
1
Jeśli interesuje Cię odległość do najbliższej geometrii, sprawdź gis.stackexchange.com/questions/11979/...
podmroku
daj mi znać, jeśli dobrze rozumiem ... chcesz następnej funkcji, która ma taką samą odległość niż najbliższa?
falcacibar,

Odpowiedzi:

7

Na twoje pytanie można również odpowiedzieć jednym (choć złożonym) zapytaniem, takim jak poniższe, które zwraca cały rekord i odległość do geometrii odniesienia. Należy pamiętać, że jeśli więcej niż jeden rekord odpowiada minimalnej odległości, wszystkie są zwracane.

SELECT 
  i.*,
  md.min_distance
FROM
  address AS i, 
  (SELECT 
     ga.address_geom,
     min( ST_Distance(
            ga.address_geom,
            gb.address_geom)
        ) AS min_distance
   FROM
     address AS ga,
     address AS gb 
   WHERE 
     ga.id <> gb.id 
   AND 
     ga.id = 3
   GROUP BY 
     ga.address_geom
  ) AS md 
WHERE 
  ST_Distance( i.address_geom, md.address_geom) = md.min_distance;

Przetestowałem to zapytanie na tabeli adresów i działa. W powyższym zapytaniu szukam najbliższego punktu z id = 3.

unicoletti
źródło
To dobra informacja - dziękuję ... Rozumiem funkcję agregacyjną min (..) z definicji, ale nie rozumiem, jak jest używana w twoim przykładzie. st_distance (X, Y) przyjmuje dwa typy geometrii i zwraca odległość między nimi, która jest pojedynczą wartością. Dlaczego wywołujesz funkcję agregującą dla tego wyniku pojedynczej wartości? Może źle interpretuję wewnętrzną instrukcję select ...
Jmoney38
Grupa według jest w geometrii ga, która jest stała dla całego zestawu wyników (pamiętaj, że ga jest wybierana przez id = 3), więc w zasadzie nic nie robi. To tylko sztuczka, aby geometria ga była dostępna w st_distance zapytania zewnętrznego bez ponownego dołączania do tabeli. Dzisiaj myślałem, że może uda mi się całkowicie uniknąć wewnętrznego zapytania, używając klauzuli partycji . To powinno również poprawić wydajność. Spróbuję i dam ci znać.
unicoletti
Niestety funkcje okien zostały wprowadzone w 8.4, a teraz nie mam dostępu do serwera, który ma zarówno postgis, jak i tę wersję, więc nie mogę przetestować zapytania przepisanego z klauzulą ​​części.
unicoletti
7

George MacKerron napisał prostą funkcję Neighbor Neighbor, którą uważam za całkiem przydatną. Ta funkcja zwraca identyfikator najbliższego sąsiada do danej funkcji:

create or replace function 
  nn(nearTo                   geometry
   , initialDistance          real
   , distanceMultiplier       real 
   , maxPower                 integer
   , nearThings               text
   , nearThingsIdField        text
   , nearThingsGeometryField  text)
 returns integer as $$
declare 
  sql     text;
  result  integer;
begin
  sql := ' select ' || quote_ident(nearThingsIdField) 
      || ' from '   || quote_ident(nearThings)
      || ' where st_dwithin($1, ' 
      ||   quote_ident(nearThingsGeometryField) || ', $2 * ($3 ^ $4))'
      || ' order by st_distance($1, ' || quote_ident(nearThingsGeometryField) || ')'
      || ' limit 1';
  for i in 0..maxPower loop
     execute sql into result using nearTo              -- $1
                                , initialDistance     -- $2
                                , distanceMultiplier  -- $3
                                , i;                  -- $4
    if result is not null then return result; end if;
  end loop;
  return null;
end
$$ language 'plpgsql' stable;

Przykład użycia:

SELECT id, nn(pt_geom,0.00001,2,100,'nw_node','node_id','node_geom') FROM my_point_table;

... wybiera najbliższy węzeł w tabeli nw_node dla każdego wpisu w mojej tabeli_punktowej.

Istnieje również bardziej ogólna funkcja na stronie GIS w Bostonie .

podmrok
źródło
Bardziej interesuje mnie sposób tworzenia zapytań 1: N w bardziej ogólnym sensie. Na przykład zamiast znajdować element najbliższy geometrii G, może chciałbym znaleźć pierwszy element, który zachodzi na G. Mimo to dziękuję za informację. Link do Boston GIS był bardzo przydatny! Wydrukowałem już niektóre ściągawki :-)
Jmoney38
Być może mógłbyś przeformułować swoje pytanie, aby było trochę jaśniejsze, @ Jmoney48. Więc nie interesuje Cię konkretnie problem najbliższego sąsiada, ale raczej jak porównać jedną geometrię ze wszystkimi geometriami w tabeli?
podmrok
ZAWSZE korzystaj z ogólnej funkcji witryny Boston GIS, prosta jest niewiarygodnie powolna w przypadku dużych stołów, a wysiłek jej zastosowania nie jest większy.
Vladtn,