Jak utworzyć regularną siatkę punktów wewnątrz wielokąta w Postgis?

31

Jak stworzyć wewnątrz wielokąta regularną siatkę punktów x, y rozmieszczonych w punktach postgis? Podobnie jak w przykładzie:

alternatywny tekst

Pablo
źródło
Próbowałem zrobić wycinające się wielokąty łączące ten kod z kodem kostki „postGIS w akcji”, ale tworzony jest tylko jeden wielokąt… Czy coś zapomniałem? TWORZENIE LUB WYMIANA FUNKCJI makegrid (geometria, liczba całkowita, liczba całkowita) ZWRACA geometrię AS 'SELECT st_intersection (g1.geom1, g2.geom2) AS geom FROM (SELECT $ 1 AS geom1) AS g1 INNER JOIN (Wybierz st_setsrid (CAST (ST_MakeBox2d (ST_MakeBox2d ST_Point (x, y), 3 USD), st_setsrid (ST_Point (x + 2 USD, y + 2 USD), 3 USD)) jako geometria), 3 USD) jako geom2 FROM generator_series (floor (st_xmin (1 USD)) :: int, sufit ( st_xmax (1 $)) :: int, 2 $) jako x, generate_series (floor (st_ymin (1 $)) :: int, ceiling (st_ymax (
aurel_nc
spójrz na moją szczegółową odpowiedź.
Muhammad Imran Siddique

Odpowiedzi:

30

Robisz to za pomocą generowania_serwisów.

Jeśli nie chcesz ręcznie pisać w miejscu, w którym siatka ma się uruchamiać i zatrzymywać, najłatwiej jest utworzyć funkcję.

Nie przetestowałem poprawnie poniższych elementów, ale myślę, że powinno to działać:

CREATE OR REPLACE FUNCTION makegrid(geometry, integer)
RETURNS geometry AS
'SELECT ST_Collect(ST_POINT(x,y)) FROM 
generate_series(floor(st_xmin($1))::int, ceiling(st_xmax($1)-st_xmin($1))::int, $2) as x
,generate_series(floor(st_ymin($1))::int, ceiling(st_ymax($1)-st_ymin($1))::int,$2) as y 
where st_intersects($1,ST_POINT(x,y))'
LANGUAGE sql

Aby go użyć, możesz:

SELECT makegrid(the_geom, 1000) from mytable;

gdzie pierwszym argumentem jest wielokąt, w którym chcesz siatkę, a drugim argumentem jest odległość między punktami na siatce.

Jeśli chcesz jednego punktu na wiersz, po prostu użyj ST_Dump, takiego jak:

SELECT (ST_Dump(makegrid(the_geom, 1000))).geom as the_geom from mytable;

HTH

Nicklas

Nicklas Avén
źródło
2
Może być konieczne dodanie st_setSRID () do funkcji st_point, w przeciwnym razie st_intersects nie działa.
JaakL
Dodałem moją przetestowaną wersję jako osobną odpowiedź.
JaakL
12

Wziąłem kod funkcji makegrid Nicklasa Avéna i uczyniłem go nieco bardziej ogólnym, czytając i używając okienka z geometrii wielokąta. W przeciwnym razie użycie wielokąta ze zdefiniowanym oknem spowoduje błąd.

Funkcja:

CREATE OR REPLACE FUNCTION makegrid(geometry, integer)
RETURNS geometry AS
'SELECT ST_Collect(ST_SetSRID(ST_POINT(x,y),ST_SRID($1))) FROM 
generate_series(floor(st_xmin($1))::int, ceiling(st_xmax($1)-st_xmin($1))::int, $2) as x
,generate_series(floor(st_ymin($1))::int, ceiling(st_ymax($1)-st_ymin($1))::int,$2) as y 
where st_intersects($1,ST_SetSRID(ST_POINT(x,y),ST_SRID($1)))'
LANGUAGE sql

Aby użyć tej funkcji, należy wykonać dokładnie tak, jak napisał Nicklas Avén :

SELECT makegrid(the_geom, 1000) from mytable;

lub jeśli chcesz jeden punkt na rząd:

SELECT (ST_Dump(makegrid(the_geom, 1000))).geom as the_geom from mytable;

Mam nadzieję, że będzie to przydatne dla kogoś.

Alex

Alexandre Neto
źródło
Akceptowana odpowiedź nie działa z moimi danymi z powodu błędów SRID. Ta modyfikacja działa lepiej.
Vitaly Isaev
Możesz coś dodać, gdy wielokąt przecina antimeridian? Mogę sobie wyobrazić, że spowodowałoby to problem z xmin / xmax.
Thomas
2
Nie działało to dla mnie. Korzystanie z Postgres 9.6 i PostGIS 2.3.3. Wewnątrz wywołania generate_series musiałem umieścić to jako drugi parametr „pułap (st_xmax (1 $)) :: int” zamiast „pułap (st_xmax (1 $) -st_xmin (1 $)) :: int” i „pułap ( st_ymax (1 $)) :: int "zamiast" sufit (st_ymax (1 $) -st_ymin (1 $)) :: int "
Vitor Sapucaia
Popieram poprzedni komentarz; górna granica generowania_series powinna być pułapem maksimum, a nie pułapem różnicy (max - min).
R. Bourgeon,
10

Od tego czasu osoby używające geometrii wgs84 prawdopodobnie będą miały problemy z tą funkcją

generate_series(floor(st_xmin($1))::int, ceiling(st_xmax($1))::int,$2) as x
,generate_series(floor(st_ymin($1))::int, ceiling(st_ymax($1))::int,$2) as y 

zwracają tylko liczby całkowite. Z wyjątkiem bardzo dużych geometrii, takich jak kraje (które leżą na wielu stopniach lng, lng), spowoduje to zebranie tylko 1 punktu, który przez większość czasu nawet nie przecina samej geometrii ... => pusty wynik!

Mój problem polegał na tym, że nie mogę użyć generowania_serwerów () z dziesiętną odległością od liczb zmiennoprzecinkowych, takich jak te WSG84 ... Właśnie dlatego poprawiłem funkcję, aby i tak działała:

SELECT ST_Collect(st_setsrid(ST_POINT(x/1000000::float,y/1000000::float),st_srid($1))) FROM 
  generate_series(floor(st_xmin($1)*1000000)::int, ceiling(st_xmax($1)*1000000)::int,$2) as x ,
  generate_series(floor(st_ymin($1)*1000000)::int, ceiling(st_ymax($1)*1000000)::int,$2) as y 
WHERE st_intersects($1,ST_SetSRID(ST_POINT(x/1000000::float,y/1000000::float),ST_SRID($1)))

Zasadniczo dokładnie tak samo. Wystarczy pomnożyć i podzielić przez 1000000, aby wprowadzić liczby dziesiętne do gry, gdy jej potrzebuję.

Z pewnością jest na to lepsze rozwiązanie. ++

Julien Garcia
źródło
To sprytne obejście. Czy sprawdziłeś wyniki? Czy są spójne?
Pablo
Cześć. Tak pablo. Jak dotąd jestem zadowolony z wyników. Potrzebowałem go do zbudowania wielokąta o względnej wysokości nad ziemią. (Używam SRTM do obliczenia wysokości, którą chcę dla każdego punktu siatki). Obecnie brakuje mi tylko sposobu na uwzględnienie punktów leżących na obwodzie wielokąta. Obecnie renderowany kształt jest nieco obcięty na krawędzi.
Julien Garcia
działało, gdy wszystkie inne rozwiązania zawiodły, dzięki!
Jordan Arseno,
7

Ten algorytm powinien być w porządku:

createGridInPolygon(polygon, resolution) {
    for(x=polygon.xmin; x<polygon.xmax; x+=resolution) {
       for(y=polygon.ymin; y<polygon.ymax; y+=resolution) {
          if(polygon.contains(x,y)) createPoint(x,y);
       }
    }
}

gdzie „wielokąt” jest wielokątem, a „rozdzielczość” jest wymaganą rozdzielczością siatki.

Aby zaimplementować go w PostGIS, mogą być potrzebne następujące funkcje:

Powodzenia!

Julien
źródło
1
Zauważ, że jeśli masz duże złożone obszary wielokątów (np. Mam bufor wybrzeża), to podejście nie jest całkiem optymalne.
JaakL
Co zatem proponujesz?
Julien
4

Trzy algorytmy wykorzystujące różne metody.

Github Repo Link

  1. Proste i najlepsze podejście, wykorzystujące rzeczywistą odległość współrzędnych od ziemi w kierunku xiy. Algorytm działa z dowolnym SRID, wewnętrznie działa z WGS 1984 (EPSG: 4326) i przekształca wynik z powrotem na wejściowy SRID.

Funkcja ================================================= ==================

CREATE OR REPLACE FUNCTION public.I_Grid_Point_Distance(geom public.geometry, x_side decimal, y_side decimal)
RETURNS public.geometry AS $BODY$
DECLARE
x_min decimal;
x_max decimal;
y_max decimal;
x decimal;
y decimal;
returnGeom public.geometry[];
i integer := -1;
srid integer := 4326;
input_srid integer;
BEGIN
CASE st_srid(geom) WHEN 0 THEN
    geom := ST_SetSRID(geom, srid);
        ----RAISE NOTICE 'No SRID Found.';
    ELSE
        ----RAISE NOTICE 'SRID Found.';
END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_min := ST_XMin(geom);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    y := ST_YMin(geom);
    x := x_min;
    i := i + 1;
    returnGeom[i] := st_setsrid(ST_MakePoint(x, y), srid);
<<yloop>>
LOOP
IF (y > y_max) THEN
    EXIT;
END IF;

CASE i WHEN 0 THEN 
    y := ST_Y(returnGeom[0]);
ELSE 
    y := ST_Y(ST_Project(st_setsrid(ST_MakePoint(x, y), srid), y_side, radians(0))::geometry);
END CASE;

x := x_min;
<<xloop>>
LOOP
  IF (x > x_max) THEN
      EXIT;
  END IF;
    i := i + 1;
    returnGeom[i] := st_setsrid(ST_MakePoint(x, y), srid);
    x := ST_X(ST_Project(st_setsrid(ST_MakePoint(x, y), srid), x_side, radians(90))::geometry);
END LOOP xloop;
END LOOP yloop;
RETURN
ST_CollectionExtract(st_transform(ST_Intersection(st_collect(returnGeom), geom), input_srid), 1);
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE;

Użyj funkcji z prostym zapytaniem, geometria musi być poprawna, a typ wielokąta, wielokąta lub obwiedni

SELECT I_Grid_Point_Distance(geom, 50, 61) from polygons limit 1;

Wynik ================================================= =====================

wprowadź opis zdjęcia tutaj

  1. Druga funkcja oparta na algorytmie Nicklas Avén . Ulepszyłem go, aby obsługiwał dowolny SRID.

    zaktualizuj następujące zmiany w algorytmie.

    1. Oddzielna zmienna dla kierunku xiy dla rozmiaru piksela,
    2. Nowa zmienna do obliczania odległości w sferoidie lub elipsoidzie.
    3. Wprowadź dowolny SRID, przekształć funkcję Geom w środowisko pracy sferoidy lub elipsoidy Datum, następnie zastosuj odległość z każdej strony, uzyskaj wynik i przekształć na wejściowy SRID.

Funkcja ================================================= ==================

CREATE OR REPLACE FUNCTION I_Grid_Point(geom geometry, x_side decimal, y_side decimal, spheroid boolean default false)
RETURNS SETOF geometry AS $BODY$ 
DECLARE
x_max decimal; 
y_max decimal;
x_min decimal;
y_min decimal;
srid integer := 4326;
input_srid integer; 
BEGIN
CASE st_srid(geom) WHEN 0 THEN
  geom := ST_SetSRID(geom, srid);
  RAISE NOTICE 'SRID Not Found.';
    ELSE
        RAISE NOTICE 'SRID Found.';
    END CASE;

    CASE spheroid WHEN false THEN
        RAISE NOTICE 'Spheroid False';
        srid := 4326;
        x_side := x_side / 100000;
        y_side := y_side / 100000;
    else
        srid := 900913;
        RAISE NOTICE 'Spheroid True';
    END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    x_min := ST_XMin(geom);
    y_min := ST_YMin(geom);
RETURN QUERY
WITH res as (SELECT ST_SetSRID(ST_MakePoint(x, y), srid) point FROM
generate_series(x_min, x_max, x_side) as x,
generate_series(y_min, y_max, y_side) as y
WHERE st_intersects(geom, ST_SetSRID(ST_MakePoint(x, y), srid))
) select ST_TRANSFORM(ST_COLLECT(point), input_srid) from res;
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE STRICT;

Użyj go za pomocą prostego zapytania.

SELECT I_Grid_Point(geom, 22, 15, false) from polygons;

Wynik ================================================= ==================wprowadź opis zdjęcia tutaj

  1. Funkcja oparta na generatorze szeregowym.

Funkcja ================================================= =================

CREATE OR REPLACE FUNCTION I_Grid_Point_Series(geom geometry, x_side decimal, y_side decimal, spheroid boolean default false)
RETURNS SETOF geometry AS $BODY$
DECLARE
x_max decimal;
y_max decimal;
x_min decimal;
y_min decimal;
srid integer := 4326;
input_srid integer;
x_series DECIMAL;
y_series DECIMAL;
BEGIN
CASE st_srid(geom) WHEN 0 THEN
  geom := ST_SetSRID(geom, srid);
  RAISE NOTICE 'SRID Not Found.';
    ELSE
        RAISE NOTICE 'SRID Found.';
    END CASE;

    CASE spheroid WHEN false THEN
        RAISE NOTICE 'Spheroid False';
    else
        srid := 900913;
        RAISE NOTICE 'Spheroid True';
    END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    x_min := ST_XMin(geom);
    y_min := ST_YMin(geom);

    x_series := CEIL ( @( x_max - x_min ) / x_side);
    y_series := CEIL ( @( y_max - y_min ) / y_side );
RETURN QUERY
SELECT st_collect(st_setsrid(ST_MakePoint(x * x_side + x_min, y*y_side + y_min), srid)) FROM
generate_series(0, x_series) as x,
generate_series(0, y_series) as y
WHERE st_intersects(st_setsrid(ST_MakePoint(x*x_side + x_min, y*y_side + y_min), srid), geom);
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE STRICT;

Użyj go za pomocą prostego zapytania.

SELECT I_Grid_Point_Series(geom, 22, 15, false) from polygons; Wynik ================================================= =========================

wprowadź opis zdjęcia tutaj

Muhammad Imran Siddique
źródło
3

Więc moja stała wersja:

CREATE OR REPLACE FUNCTION makegrid(geometry, integer, integer)
RETURNS geometry AS
'SELECT ST_Collect(st_setsrid(ST_POINT(x,y),$3)) FROM 
  generate_series(floor(st_xmin($1))::int, ceiling(st_xmax($1))::int,$2) as x
  ,generate_series(floor(st_ymin($1))::int, ceiling(st_ymax($1))::int,$2) as y 
where st_intersects($1,st_setsrid(ST_POINT(x,y),$3))'
LANGUAGE sql

Stosowanie:

SELECT (ST_Dump(makegrid(the_geom, 1000, 3857))).geom as the_geom from my_polygon_table
JaakL
źródło
1
Cześć, otrzymuję pusty wynik z funkcją makegrid. Plik shapefile został zaimportowany do PostGIS przy użyciu shp2pgsql. Nie mam pojęcia, co może powodować problemy, srs jest ustawiony na wgs84.
Michal Zimmermann
3

Oto inne podejście, które z pewnością jest szybsze i łatwiejsze do zrozumienia.

Na przykład dla siatki o wymiarach 1000 na 1000 m:

SELECT (ST_PixelAsCentroids(ST_AsRaster(the_geom,1000.0,1000.0))).geom 
FROM the_polygon

Zachowany jest również oryginalny SRID.

Ten fragment przekształca geometrię wielokąta w pusty raster, a następnie przekształca każdy piksel w punkt. Zaleta: nie musimy ponownie sprawdzać, czy oryginalny wielokąt przecina punkty.

Opcjonalny:

Możesz także dodać wyrównanie siatki za pomocą parametru gridx i gridy. Ale ponieważ używamy środka ciężkości każdego piksela (a nie rogu), musimy użyć modulo, aby obliczyć właściwą wartość:

SELECT (ST_PixelAsCentroids(ST_AsRaster(the_geom,1000.0,1000.0,mod(1000/2,100),mod(1000/2,100)))).geom 
FROM the_polygon

Z mod(grid_size::integer/2,grid_precision)

Oto funkcja postgres:

CREATE OR REPLACE FUNCTION st_makegrid(geometry, float, integer)
RETURNS SETOF geometry AS
'SELECT (ST_PixelAsCentroids(ST_AsRaster($1,$2::float,$2::float,mod($2::int/2,$3),mod($2::int/2,$3)))).geom'
LANGUAGE sql;

Można stosować z:

SELECT makegrid(the_geom,1000.0,100) as geom from the_polygon  
-- makegrid(the_geom,grid_size,alignement)
obchardon
źródło
1

Mała potencjalna aktualizacja poprzednich odpowiedzi - trzeci argument jako skala dla wgs84 (lub użyj 1 dla normalnych), a także zaokrąglenie wewnątrz kodu, aby wyskalowane punkty na wielu kształtach były wyrównane.

Mam nadzieję, że to pomaga, Martin

CREATE OR REPLACE FUNCTION makegrid(geometry, integer, integer)
RETURNS geometry AS



/*geometry column , integer: distance between points, integer: scale factor for distance (useful for wgs84, e.g. use there 50000 as distance and 1000000 as scale factor*/

'
SELECT ST_Collect(st_setsrid(ST_POINT(x/$3::float,y/$3::float),st_srid($1))) FROM 
  generate_series(
                (round(floor(st_xmin($1)*$3)::int/$2)*$2)::int, 
                (round(ceiling(st_xmax($1)*$3)::int/$2)*$2)::int,
                $2) as x ,
  generate_series(
                (round(floor(st_ymin($1)*$3)::int/$2)*$2)::int, 
                (round(ceiling(st_ymax($1)*$3)::int/$2)*$2)::int,
                $2) as y 
WHERE st_intersects($1,ST_SetSRID(ST_POINT(x/$3::float,y/$3::float),ST_SRID($1)))
'

LANGUAGE sql
Jaskółka oknówka
źródło
Czy przekształcenie geometrii w określony SRID (jak EPSG: 3857) nie byłoby lepsze niż pomnożenie przez współczynnik skali?
Nikolaus Krismer,