Która funkcja do tworzenia POINT w PostGIS?

30

Kiedy definiujesz punkt w PostGIS, kiedy zdecydujesz się zastosować który z poniższych elementów?

  • ST_SetSRID(ST_MakePoint(lon,lat),4326)
  • ST_SetSRID(ST_Point(long,lat),4326)
  • ST_SetSRID(ST_GeomFromText('POINT(lon lat)',4326)
  • ST_GeomFromEWKT('SRID=4326;POINT(lon lat)')

Jeśli jest to zasadniczo różnica w wydajności, która będzie najszybsza?

Nyxynyx
źródło
Sprawdź tę odpowiedź: gis.stackexchange.com/a/285017/6052
Evan Carroll

Odpowiedzi:

26

Domyślam się, że ST_MakePointjest to najszybszy, ale łatwo jest go porównać z 100 000 losowymi punktami.

\timing

WITH test AS (
  SELECT <POINT CONSTRUCTOR METHOD>
  FROM generate_series(1,100000)
)
SELECT count(*) FROM test;

A oto niektóre wyniki z PostGIS 2.1 (trunk) na PostgreSQL 9.1, x64 Debian. Zrobiłem je kilka razy, aby uzyskać przybliżoną średnią. Oto <POINT CONSTRUCTOR METHOD>kolejność od najszybszej do najwolniejszej:

  1. ST_SetSRID(ST_MakePoint(random(), random()), 4326)
    • średni 160 ms
    • zdecydowanie najszybszy i zachowuje precyzję podwójnego punktu (bezstratny)
    • najprostszy sposób na wykonanie sparametryzowanego zapytania z numerycznymi danymi współrzędnych
  2. ST_GeomFromText('POINT(' || random()::text || ' ' || random()::text || ')', 4326)
    • średni 760 ms
    • powoli, ponieważ liczba jest rzutowana na tekst, a następnie ciąg jest składany razem, a następnie PostGIS musi go przeanalizować, aby znaleźć liczby
    • straty, z powodu konwersji liczb -> tekstu -> liczb
  3. ST_GeomFromEWKT('SRID=4326;POINT(' || random()::text || ' ' || random()::text || ')')
    • średni 810 ms
    • najwolniej, nie jestem pewien, dlaczego jest wolniejszy niż ST_GeomFromText

Wreszcie, wee przypis na temat różnicy między bezstratnymi / stratnymi konwersjami przy użyciu powyższych metod. ST_MakePointZachowuje tylko binarne dane precyzyjne zmiennoprzecinkowe, a konwersje tekstu obcinają bardzo małą część danych. Chociaż dwa punkty mogą mieć różnice binarne (widoczne w WKB), zawsze powinny być one przestrzennie równe. Różnice odległości są zasadniczo epsilon maszyny dla podwójnej precyzji .

SELECT
  (geom_text = geom_binary) AS spatially_equal,
  (geom_text::text = geom_binary::text) AS binary_equal,
  (ST_AsText(geom_text) = ST_AsText(geom_binary)) AS wkt_equal,
  ST_Distance(geom_text, geom_binary)
FROM (
  SELECT x, y,
    ST_GeomFromText('POINT(' || x::text || ' ' || y::text || ')') AS geom_text,
    ST_MakePoint(x, y) AS geom_binary
  FROM (SELECT random()::float8 as x, random()::float8 as y) AS f1
) AS f2;

 spatially_equal | binary_equal | wkt_equal |     st_distance
-----------------+--------------+-----------+----------------------
 t               | f            | t         | 1.38777878078145e-16
Mike T.
źródło
1
Dziękujemy za świetne wyjaśnienie, jak to obliczyć. Jestem ciekaw o SQLskładni <POINT CONSTRUCTOR METHOD>. Czy to tylko pseudokod, który odnosi się do czterech różnych podejść, czy też wykonujesz jakąś funkcję?
djq
2
@djq tak, to tylko symbol zastępczy dla rzeczywistego kodu SQL w 1, 2 i 3.
Mike T
Szczegółowe informacje na temat limitów precyzji typu danych zmiennoprzecinkowych używanych jako odniesienie ... Maszyna epsilon to ~ 1e-14... Zmień tabelę f1, FROM (SELECT random()::float8 as x, random()::float8 as y UNION SELECT 12.24343484842,34.58384538483434) AS f1aby zobaczyć ją w psql.
Peter Krauss
5

ST_MakePoint i ST_Point są takie same - oba wywołują LWGEOM_makepoint (można to zobaczyć w pliku postgis / postgis.sql.in w kodzie źródłowym). Użyłbym ST_MakePoint. Procedury konwersji tekstu dają ten sam wynik, ale są wolniejsze z powodu wymaganej ilości parsowania.

BradHards
źródło
1

SRID 4326 i geometria

Na marginesie doskonałej, kompleksowej i aktualnej odpowiedzi MikeT . Wydaje się, że wiele osób zadaje to pytanie, ponieważ chce ustawić SRID w kolumnie POINT.

CREATE TABLE foo ( geom geometry(Point,4326) );

Ale kiedy to robią, mają problemy z najlepszą metodą tworzenia punktu, ale niestety wpadają w kłopoty.

INSERT INTO foo (geom) VALUES ( ST_MakePoint(1,2) );
ERROR:  Geometry SRID (0) does not match column SRID (4326);

Stamtąd uważają, że mają dwie opcje

  • Ustaw SRID ręcznie, ST_SetSRID( ST_MakePoint(1,2) )co jest najbardziej prawe, ale kruche, lub
  • Konstruuj na podstawie tekstu ST_GeomFromText, jest to logicznie wolniejsze i nie wymaga testów porównawczych: PostgreSQL musi przeanalizować argumenty konstruktora z tekstu. Sam też jest wyjątkowo brzydki.

Niestety, istnieje inny sposób.

Typ geograficzny

Domyślny SRID dla geographyto 4326. Jeśli jesteś nowy, sugeruję użycie geographyzamiast geometry. W zasadzie, jeśli nie znasz różnicy, której prawdopodobnie chcesz geography. Możesz łatwo przełączać kolumny.

BEGIN;
  ALTER TABLE foo ADD COLUMN geog geography(point,4326);
  UPDATE foo SET geog = geom::geography;
  ALTER TABLE foo DROP COLUMN geom;
COMMIT;

Teraz wstawianie jest łatwiejsze, ponieważ typ jest już domyślnie powiązany z SRID 4326. Teraz możesz jawnie rzutować na geography, lub po prostu pozwolić, aby rzutowanie niejawne działało

ST_MakePoint(x,y)                     -- implicit cast and srid
ST_MakePoint(x,y)::geography          -- explicit cast, implicit srid
ST_SetSRID( ST_MakePoint(3,4), 4326 ) -- explicit cast and srid

Który wygląda tak (wszystkie wstawiają tę samą rzecz)

INSERT INTO foo (geog) VALUES
  -- implicit cast and SRID
  ( ST_MakePoint(1,2) ),

  -- explicit cast, implicit SRID
  ( ST_MakePoint(1,2)::geography ),

   -- explicit cast and SRID
  ( ST_SetSRID( ST_MakePoint(3,4), 4326 )::geography );

Konwersja na tekst, a następnie zmuszanie PostgreSQL do parsowania tekstu za pomocą ST_GeomFromTextlub ST_GeogFromTextjest głupie i powolne.

Evan Carroll
źródło