วิธีสร้างกริดจุดปกติภายในรูปหลายเหลี่ยมใน Postgis


30

จะสร้างตารางจุดปกติโดยเว้นระยะ x, y ใน postgis ได้อย่างไร เหมือนตัวอย่าง:

ข้อความแสดงแทน


ฉันพยายามทำให้รูปหลายเหลี่ยมการรวมรหัสนี้กับรหัส dicing "postGIS ในการดำเนินการ" แต่สร้างรูปหลายเหลี่ยมเดียวเท่านั้น ... ฉันลืมบางสิ่งบางอย่างหรือไม่ สร้างหรือแทนที่ฟังก์ชั่น makegrid (เรขาคณิต, จำนวนเต็ม, จำนวนเต็ม) ผลตอบแทนเรขาคณิต AS 'SELECT st_intersection (g1.geom1, g2.geom2) เป็น geom จาก (เลือก $ 1 เป็น geom1) เป็น g1 ภายในเข้าร่วม (เลือก st_setsrid (CAST (ST_MBox2 ST_Point (x, y), $ 3), st_setsrid (ST_Point (x + $ 2, y + $ 2), $ 3)) เป็นรูปทรงเรขาคณิต), $ 3) เป็น geom2 จาก generate_series (พื้น (st_xmin ($ 1)) :: int, Ceiling ( st_xmax ($ 1)) :: int, $ 2) เป็น x, generate_series (พื้น (st_ymin ($ 1))) :: int, Ceiling (st_ymax (
aurel_nc

ดูคำตอบโดยละเอียดของฉัน
Muhammad Imran Siddique

คำตอบ:


29

คุณทำเช่นนั้นกับ generate_series

หากคุณไม่ต้องการที่จะเขียนด้วยตนเองที่ตารางจะเริ่มและหยุดที่ง่ายที่สุดคือการสร้างฟังก์ชั่น

ฉันไม่ได้ทดสอบด้านล่างอย่างถูกต้อง แต่ฉันคิดว่ามันควรจะทำงาน:

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

ในการใช้งานคุณสามารถทำได้:

SELECT makegrid(the_geom, 1000) from mytable;

โดยที่อาร์กิวเมนต์แรกคือรูปหลายเหลี่ยมที่คุณต้องการให้กริดเข้าไปและอาร์กิวเมนต์ที่สองคือระยะห่างระหว่างจุดต่าง ๆ ในกริด

หากคุณต้องการหนึ่งจุดต่อแถวคุณเพียงแค่ใช้ ST_Dump เช่น:

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

HTH

Nicklas


1
คุณอาจต้องเพิ่ม st_setSRID () ในฟังก์ชัน st_point มิฉะนั้น st_intersects จะไม่ทำงาน
JaakL

เพิ่มเวอร์ชันทดสอบของฉันเป็นคำตอบแยกต่างหาก
JaakL

12

ฉันได้รับรหัสฟังก์ชั่นmakegrid ของ Nicklas Avénและทำให้มันดูธรรมดาขึ้นอีกเล็กน้อยโดยการอ่านและการใช้ srid จากเรขาคณิตรูปหลายเหลี่ยม มิฉะนั้นการใช้รูปหลายเหลี่ยมที่มี srid ที่กำหนดไว้จะทำให้เกิดข้อผิดพลาด

ฟังก์ชั่น:

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

ในการใช้งานฟังก์ชั่นจะทำเหมือนกับที่Nicklas Avénเขียนไว้:

SELECT makegrid(the_geom, 1000) from mytable;

หรือถ้าคุณต้องการหนึ่งจุดต่อแถว:

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

หวังว่านี่จะเป็นประโยชน์สำหรับใครบางคน

อเล็กซ์


คำตอบที่ยอมรับใช้ไม่ได้กับข้อมูลของฉันเนื่องจากข้อผิดพลาดของ SRID การดัดแปลงนี้ทำงานได้ดีขึ้น
Vitaly Isaev

คุณอาจต้องการเพิ่มบางสิ่งบางอย่างเมื่อรูปหลายเหลี่ยมข้าม antimeridian ฉันสามารถจินตนาการได้ว่ามันจะทำให้เกิดปัญหากับ xmin / xmax
โทมัส

2
มันไม่ได้ผลสำหรับฉัน ใช้ Postgres 9.6 และ PostGIS 2.3.3 ภายในการเรียก generate_series ฉันต้องใส่พารามิเตอร์นี้เป็นพารามิเตอร์ตัวที่สอง "ceiling (st_xmax ($ 1)) :: int" แทน "ceiling (st_xmax ($ 1) -st_xmin ($ 1)) :: int" และ "ceiling ( st_ymax ($ 1)) :: int "แทน" ceiling (st_ymax ($ 1) -st_ymin ($ 1)) :: int "
Vitor Sapucaia

ฉันอนุมัติความคิดเห็นก่อนหน้า; ขอบเขตบนของ generate_series ควรเป็นเพดานของค่าสูงสุดและไม่ใช่ค่าความแตกต่าง (สูงสุด - นาที)
R. Bourgeon

10

ผู้ที่ใช้รูปทรงเรขาคณิต wgs84 อาจมีปัญหากับฟังก์ชั่นนี้ตั้งแต่

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 

คืนค่าเป็นจำนวนเต็มเท่านั้น ยกเว้นรูปทรงเรขาคณิตที่มีขนาดใหญ่มากเช่นประเทศ (ที่วางอยู่บนละติจูดหลายละติจูด, lng องศา) สิ่งนี้จะทำให้เกิดการสะสมเพียง 1 จุดซึ่งส่วนใหญ่แล้วจะไม่ได้ตัดรูปเรขาคณิต ... => ผลที่ว่างเปล่า!

ปัญหาของฉันคือฉันไม่สามารถใช้ generate_series () ที่มีทศนิยมเป็นทศนิยมเช่น WSG84 เหล่านั้น ... นี่คือสาเหตุที่ฉันปรับแต่งฟังก์ชันเพื่อให้ทำงานได้:

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)))

โดยทั่วไปเหมือนกันทุกประการ เพียงแค่คูณและหารด้วย 1000000 เพื่อให้ได้ทศนิยมในเกมเมื่อฉันต้องการ

ย่อมมีทางออกที่ดีกว่าในการบรรลุเป้าหมายดังกล่าว ++


นั่นเป็นวิธีแก้ปัญหาที่ชาญฉลาด คุณตรวจสอบผลลัพธ์แล้วหรือยัง พวกเขาสอดคล้องกันหรือไม่
Pablo

สวัสดี ใช่ปาโบล ฉันมีความสุขกับผลลัพธ์ที่ได้จนถึงตอนนี้ ฉันต้องการมันเพื่อสร้างรูปหลายเหลี่ยมที่มีความสูงสัมพัทธ์เหนือพื้นดิน (ฉันใช้ SRTM เพื่อคำนวณความสูงที่ฉันต้องการสำหรับจุดกริดทุกจุด) ตอนนี้ฉันไม่มีวิธีที่จะรวมจุดที่วางอยู่บนขอบของรูปหลายเหลี่ยมเช่นกัน ขณะนี้รูปร่างที่แสดงผลถูกตัดทอนที่ขอบบ้าง
Julien Garcia

ทำงานได้เมื่อการแก้ปัญหาของผู้อื่นล้มเหลวขอบคุณ!
Jordan Arseno

7

อัลกอริทึมนี้ควรจะดี:

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);
       }
    }
}

โดยที่ 'รูปหลายเหลี่ยม' เป็นรูปหลายเหลี่ยมและ 'ความละเอียด' เป็นความละเอียดกริดที่ต้องการ

ในการนำไปใช้ใน PostGIS อาจจำเป็นต้องใช้ฟังก์ชันต่อไปนี้:

  • ST_XMin , ST_XMax , ST_YMinและST_YMaxเพื่อรับพิกัดต่ำสุดและสูงสุดของรูปหลายเหลี่ยม
  • ST_ ใช้เพื่อทดสอบว่ารูปหลายเหลี่ยมมีจุดหรือไม่
  • และST_Pointเพื่อสร้างจุด

โชคดี!


1
โปรดทราบว่าหากคุณมีพื้นที่รูปหลายเหลี่ยมเชิงซ้อนขนาดใหญ่ (เช่นฉันมีบัฟเฟอร์แนวชายฝั่ง) วิธีการนี้จะไม่เหมาะสมที่สุด
JaakL

จากนั้นคุณเสนออะไรแทน
julien

4

สามอัลกอริทึมที่ใช้วิธีการต่าง ๆ

ลิงค์ Repo Github

  1. วิธีที่ง่ายและดีที่สุดโดยใช้ระยะทางจากพื้นโลกจริงของพิกัดจากทิศทาง x และ y อัลกอริทึมทำงานร่วมกับ SRID ใด ๆ ภายในจะทำงานกับ WGS 1984 (EPSG: 4326) และผลการแปลงกลับไปเป็นอินพุต SRID

ฟังก์ชั่น ================================================= ==================

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;

ใช้ฟังก์ชั่นที่มีแบบสอบถามง่ายเรขาคณิตจะต้องถูกต้องและรูปหลายเหลี่ยมหลายรูปหลายเหลี่ยมหรือประเภทซองจดหมาย

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

ส่งผลให้เกิด ================================================= =====================

ป้อนคำอธิบายรูปภาพที่นี่

  1. ฟังก์ชั่นที่สองขึ้นอยู่กับอัลกอริทึมของNicklas Avén ฉันปรับปรุงมันเพื่อจัดการ SRID ใด ๆ

    ได้อัพเกรดการเปลี่ยนแปลงต่อไปนี้ในอัลกอริทึม

    1. แยกตัวแปรสำหรับทิศทาง x และ y สำหรับขนาดพิกเซล
    2. ตัวแปรใหม่ในการคำนวณระยะทางในรูปทรงกลมหรือทรงรี
    3. ใส่ SRID ใด ๆ , ฟังก์ชั่นแปลง Geom เป็นสภาพแวดล้อมการทำงานของ Spheroid หรือ Ellipsoid Datum จากนั้นใช้ระยะทางกับแต่ละด้านรับผลลัพธ์และแปลงเป็นอินพุต SRID

ฟังก์ชั่น ================================================= ==================

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;

ใช้มันกับแบบสอบถามง่ายๆ

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

ส่งผลให้เกิด ================================================= ==================ป้อนคำอธิบายรูปภาพที่นี่

  1. ฟังก์ชั่นขึ้นอยู่กับชุดเครื่องกำเนิดไฟฟ้า

ฟังก์ชั่น ================================================= =================

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;

ใช้มันกับแบบสอบถามง่ายๆ

SELECT I_Grid_Point_Series(geom, 22, 15, false) from polygons; ส่งผลให้เกิด ================================================= =========================

ป้อนคำอธิบายรูปภาพที่นี่


3

นี่เป็นอีกวิธีหนึ่งที่เร็วกว่าและเข้าใจง่ายกว่า

ตัวอย่างเช่นกริด 1,000 ม. คูณ 1,000 ม:

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

SRID ดั้งเดิมยังคงอยู่

ตัวอย่างนี้แปลงเรขาคณิตของรูปหลายเหลี่ยมเป็นแรสเตอร์ว่างแล้วแปลงแต่ละพิกเซลให้เป็นจุด ข้อได้เปรียบ: เราไม่จำเป็นต้องตรวจสอบอีกครั้งว่ารูปหลายเหลี่ยมเดิมตัดกับจุดหรือไม่

ไม่จำเป็น:

คุณยังสามารถเพิ่มการจัดตำแหน่งกริดด้วยพารามิเตอร์ gridx และ gridy แต่เนื่องจากเราใช้เซนทรอยด์ของแต่ละพิกเซล (และไม่ใช่มุม) เราจำเป็นต้องใช้โมดูโล่ในการคำนวณค่าที่ถูกต้อง:

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

กับ mod(grid_size::integer/2,grid_precision)

นี่คือฟังก์ชั่น 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;

สามารถใช้กับ:

SELECT makegrid(the_geom,1000.0,100) as geom from the_polygon  
-- makegrid(the_geom,grid_size,alignement)

2

ดังนั้นรุ่นที่แน่นอนของฉัน:

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

การใช้งาน:

SELECT (ST_Dump(makegrid(the_geom, 1000, 3857))).geom as the_geom from my_polygon_table

1
สวัสดีฉันได้รับผลว่างด้วยฟังก์ชั่น makegrid Shapefile ถูกนำเข้าสู่ PostGIS โดยใช้ shp2pgsql ไม่มีความคิดว่าจะทำให้เกิดปัญหาอะไรบ้าง srs ถูกตั้งค่าเป็น wgs84
Michal Zimmermann

1

การปรับปรุงที่เป็นไปได้เล็กน้อยสำหรับคำตอบก่อนหน้านี้ - อาร์กิวเมนต์ที่สามเป็นสเกลสำหรับ wgs84 (หรือใช้ 1 สำหรับค่าปกติ) และการปัดเศษในโค้ดเพื่อให้คะแนนที่ปรับขนาดของรูปร่างหลาย ๆ

หวังว่านี่จะช่วยได้มาร์ติน

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

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