Hasilkan titik yang terletak di dalam poligon

30

Saya memiliki fitur poligon dan ingin dapat menghasilkan poin di dalamnya. Saya memerlukan ini untuk satu tugas klasifikasi.

Menghasilkan titik acak sampai ada di dalam poligon tidak akan berhasil karena itu benar-benar tidak dapat diprediksi waktu yang diperlukan.

pengguna2024
sumber
3
Sebaliknya, waktunya dapat diprediksi. Ini sebanding dengan rasio luas poligon dibagi dengan luas poligon, dikali waktu yang dibutuhkan untuk menghasilkan dan menguji satu titik. Waktunya sedikit bervariasi, tetapi variasinya sebanding dengan akar kuadrat dari jumlah poin. Untuk angka besar, ini menjadi tidak penting. Pecah polong berliku menjadi potongan-potongan yang lebih kompak jika perlu untuk mendapatkan rasio area ini ke nilai yang rendah. Hanya poligon fraktal yang akan membuat Anda kesulitan, tetapi saya ragu Anda memilikinya!
whuber
@Pablo: penemuan bagus. Namun, kedua pertanyaan itu adalah perangkat lunak khusus dan keduanya menyangkut penempatan array titik reguler dalam poligon, bukan titik acak
whuber
setuju dengan perbedaan whuber adalah poin acak vs generasi poin reguler dalam poligon.
Mapperz

Jawaban:

20

Mulailah dengan mendekomposisi poligon menjadi segitiga, lalu hasilkan poin di dalamnya . (Untuk distribusi yang seragam, timbang setiap segitiga berdasarkan wilayahnya.)

Dan S.
sumber
2
+1 Sederhana dan efektif. Patut ditunjukkan bahwa titik acak yang seragam dapat dihasilkan dalam sebuah segitiga tanpa penolakan sama sekali, karena ada pemetaan area pelestarian antara segitiga mana pun dan segitiga sama kaki yang sama sekali tidak sama, yang setengah persegi, katakan setengah di mana koordinat y melebihi koordinat x. Hasilkan dua koordinat acak dan urutkan mereka untuk mendapatkan titik acak dalam segitiga sama kaki, lalu petakan kembali ke segitiga asli.
whuber
+1 Saya sangat suka diskusi tentang koordinat trilinear yang dirujuk oleh artikel yang Anda kutip. Saya kira ini akan setuju untuk bola yang permukaannya diwakili sebagai tesselation segitiga. Pada pesawat yang diproyeksikan, itu tidak akan menjadi distribusi yang benar-benar acak, bukan?
Kirk Kuykendall
@whuber - +1 membalas Anda. Cara lain (dalam tautan, tetapi mereka melambai-lambaikan tangan di atasnya) adalah untuk mencerminkan poin yang ditolak dari segiempat yang disampel secara seragam melintasi tepi bersama dan kembali ke segitiga.
Dan S.
@Kirk - tautan kutipan adalah sentuhan anti-membantu karena mencantumkan sekelompok metode pengambilan sampel yang salah (tidak seragam), termasuk koordinat trilinear, sebelum cara yang "benar". Sepertinya tidak ada cara langsung untuk mendapatkan sampel yang seragam dengan koordinat trilinear. Saya akan mendekati pengambilan sampel yang seragam di seluruh bola dengan mengkonversi vektor satuan acak dalam 3d ke lat / lon yang setara, tapi itu hanya saya. (Tidak yakin tentang pengambilan sampel terbatas pada segitiga bulat / poligon.) (Juga tidak yakin tentang pengambilan sampel yang benar-benar seragam misalnya wgs84: saya hanya memilih sudut yang akan sedikit condong ke arah kutub, saya kira.)
Dan S.
1
@Dan Untuk mengambil sampel bola secara seragam, gunakan proyeksi bidang-sama silindris (koordinat bujur dan kosinus lintang). Jika Anda ingin mengambil sampel tanpa menggunakan proyeksi, ada trik yang bagus: buat tiga varian normal standar independen (x, y, z) dan proyeksikan ke titik (R x / n, R y / n, R * z / n ) dengan n ^ 2 = x ^ 2 + y ^ 2 + z ^ 2 dan R adalah jari-jari bumi. Konversikan ke (lat, lon) jika perlu (menggunakan garis lintang authalic saat mengerjakan spheroid). Ini berfungsi karena distribusi normal trivariat ini simetris berbentuk bola. Untuk mengambil sampel segitiga, pertahankan proyeksi.
whuber
14

Saat Anda memasang tag QGIS pada pertanyaan ini: Alat Poin Acak dapat digunakan dengan lapisan batas.

masukkan deskripsi gambar di sini

Jika Anda mencari kode, kode sumber plugin yang mendasarinya harus membantu.

underdark
sumber
1
Bahkan 5 tahun kemudian, masih sangat membantu!
Stranded Kid
10

Anda bisa menentukan tingkat poligon, lalu membatasi pembangkitan angka acak untuk nilai X dan Y dalam luasan tersebut.

Proses dasar: 1) Tentukan maxx, maxy, minx, miny dari simpul poligon, 2) Hasilkan titik acak menggunakan nilai-nilai ini sebagai batas 3) Uji setiap titik untuk persimpangan dengan poligon Anda, 4) Berhenti menghasilkan ketika Anda memiliki cukup poin memenuhi persimpangan uji

Berikut ini adalah algoritma (C #) untuk tes persimpangan:

bool PointIsInGeometry(PointCollection points, MapPoint point)
{
int i;
int j = points.Count - 1;
bool output = false;

for (i = 0; i < points.Count; i++)
{
    if (points[i].X < point.X && points[j].X >= point.X || points[j].X < point.X && points[i].X >= point.X)
    {
        if (points[i].Y + (point.X - points[i].X) / (points[j].X - points[i].X) * (points[j].Y - points[i].Y) < point.Y)
        {
            output = !output;
        }
    }
    j = i;
}
return output;
}
Dan Walton
sumber
10

Ada beberapa perpustakaan bagus di luar sana yang melakukan sebagian besar pekerjaan berat untuk Anda.

Contoh menggunakan [rupawan] [1] dalam python.

import random
from shapely.geometry import Polygon, Point

def get_random_point_in_polygon(poly):
     minx, miny, maxx, maxy = poly.bounds
     while True:
         p = Point(random.uniform(minx, maxx), random.uniform(miny, maxy))
         if poly.contains(p):
             return p

p = Polygon([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)])
point_in_poly = get_random_point_in_polygon(mypoly)

Atau gunakan .representative_point()untuk mendapatkan titik di dalam objek (seperti yang disebutkan oleh dain):

Mengembalikan titik komputasi yang murah yang dijamin berada dalam objek geometrik.

poly.representative_point().wkt
'POINT (-1.5000000000000000 0.0000000000000000)'

  [1]: https://shapely.readthedocs.io
monkut
sumber
2
Bukankah seharusnya dari impor impor shapely.geometry ...?
PyMapr
1
Anda juga bisa menggunakan representative_pointmetode ini: shapely.readthedocs.io/en/latest/…
dain
6

Jika R adalah opsi, lihat ?spsampledalam sppaket. Poligon dapat dibaca dari format apa pun yang didukung GDAL yang dibangun ke dalam paket rgdal, dan kemudian spsamplebekerja langsung pada objek yang diimpor dengan berbagai opsi pengambilan sampel.

mdsumner
sumber
+1 - Karena R adalah open source jika seseorang ingin mereplikasi, Anda selalu dapat masuk ke sumber untuk melihat bagaimana mereka dilakukan. Untuk pola titik, orang mungkin juga tertarik dengan alat simulasi dalam paket spatstat.
Andy W
5

Saya ingin menawarkan solusi yang membutuhkan sangat sedikit dalam hal analisis GIS. Secara khusus, tidak memerlukan triangulasi poligon apa pun.

Algoritme berikut, diberikan dalam pseudocode, merujuk pada beberapa operasi sederhana selain kemampuan penanganan daftar dasar (membuat, menemukan panjang, menambahkan, mengurutkan, mengekstrak sublists, dan menyatukan) dan generasi float acak dalam interval [0, 1):

Area:        Return the area of a polygon (0 for an empty polygon).
BoundingBox: Return the bounding box (extent) of a polygon.
Width:       Return the width of a rectangle.
Height:      Return the height of a rectangle.
Left:        Split a rectangle into two halves and return the left half.
Right:       ... returning the right half.
Top:         ... returning the top half.
Bottom:      ... returning the bottom half.
Clip:        Clip a polygon to a rectangle.
RandomPoint: Return a random point in a rectangle.
Search:      Search a sorted list for a target value.  Return the index  
             of the last element less than the target.
In:          Test whether a point is inside a polygon.

Ini semua tersedia di hampir semua SIG atau lingkungan pemrograman grafis (dan mudah dikodekan jika tidak). Cliptidak boleh mengembalikan poligon yang merosot (yaitu, yang tidak memiliki luas).

Prosedur secara SimpleRandomSampleefisien mendapatkan daftar titik yang didistribusikan secara acak dalam poligon. Ini adalah pembungkus untuk SRS, yang memecah poligon menjadi potongan-potongan kecil sampai masing-masing potongan cukup kompak untuk disampel secara efisien. Untuk melakukan ini, ia menggunakan daftar angka acak yang telah dihitung untuk memutuskan berapa banyak poin yang dialokasikan untuk masing-masing bagian.

SRS dapat "disetel" dengan mengubah parameter t. Ini adalah kotak batas maksimum: rasio area poligon yang dapat ditoleransi. Menjadikannya kecil (tetapi lebih besar dari 1) akan menyebabkan sebagian besar poligon terpecah menjadi banyak; membuatnya besar dapat menyebabkan banyak poin percobaan ditolak untuk beberapa poligon (berliku-liku, dengan sliver, atau penuh lubang). Ini menjamin bahwa waktu maksimum untuk mengambil sampel poligon asli dapat diprediksi.

Procedure SimpleRandomSample(P:Polygon, N:Integer) {
    U = Sorted list of N independent uniform values between 0 and 1
    Return SRS(P, BoundingBox(P), U)
}

Prosedur selanjutnya menyebut dirinya secara rekursif jika perlu. Ekspresi misterius secara t*N + 5*Sqrt(t*N)konservatif memperkirakan batas atas berapa banyak poin yang akan dibutuhkan, yang memperhitungkan variabilitas kesempatan. Kemungkinan bahwa ini akan gagal hanya 0,3 per juta panggilan prosedur. Tingkatkan 5 hingga 6 atau bahkan 7 untuk mengurangi kemungkinan ini jika Anda mau.

Procedure SRS(P:Polygon, B:Rectangle, U:List) {
    N = Length(U)
    If (N == 0) {Return empty list}
    aP = Area(P)
    If (aP <= 0) {
        Error("Cannot sample degenerate polygons.")
        Return empty list
    }
    t = 2
    If (aP*t < Area(B)) {
        # Cut P into pieces
        If (Width(B) > Height(B)) {
            B1 = Left(B); B2 = Right(B)
        } Else {
            B1 = Bottom(B); B2 = Top(B)
        }
        P1 = Clip(P, B1); P2 = Clip(P, B2)
        K = Search(U, Area(P1) / aP)
        V = Concatenate( SRS(P1, B1, U[1::K]), SRS(P2, B2, U[K+1::N]) )
    } Else {
        # Sample P
        V = empty list
        maxIter = t*N + 5*Sqrt(t*N)
        While(Length(V) < N and maxIter > 0) {
            Decrement maxIter
            Q = RandomPoint(B)
            If (Q In P) {Append Q to V}
        }
       If (Length(V) < N) {
            Error("Too many iterations.")
       }
    }
    Return V
}
whuber
sumber
2

Jika poligon Anda adalah cembung dan Anda tahu semua simpul, Anda mungkin ingin mempertimbangkan untuk melakukan pembobotan cembung "acak" dari simpul untuk sampel titik baru yang dijamin terletak di dalam cembung cembung (poligon dalam kasus ini).

Misalnya, Anda memiliki poligon cembung sisi N dengan simpul

V_i, i={1,..,N}

Kemudian menghasilkan bobot N cembung secara acak

 w_1,w_2,..,w_N such that  w_i = 1; w_i>=0

Titik sampel acak kemudian diberikan oleh

Y=  w_i*V_i

Mungkin ada cara berbeda untuk sampel bobot N cembung

  • Pilih nomor N-1 secara acak secara acak dalam suatu rentang (tanpa penggantian), urutkan dan normalkan interval N di antara mereka untuk mendapatkan bobot.
  • Anda juga dapat mengambil sampel dari distribusi Dirichlet yang sering digunakan sebagai konjugasi sebelum untuk distribusi multinomial, yang mirip dengan bobot cembung dalam kasing Anda.

Ketika poligon Anda tidak terlalu non-cembung, Anda dapat mempertimbangkan untuk mengubahnya menjadi lambung cembung. Setidaknya ini harus membatasi jumlah titik yang berada di luar poligon Anda untuk sebagian besar.

algoseer
sumber
2

Tugas ini sangat mudah diselesaikan dalam GRASS GIS (satu perintah) menggunakan v.random .

Di bawah ini adalah contoh tentang cara menambahkan 3 titik acak ke dalam poligon yang dipilih (di sini area kode ZIP kota Raleigh, NC) dari halaman manual. Dengan memodifikasi pernyataan SQL "where", poligon dapat dipilih.

Menghasilkan titik acak dalam poligon yang dipilih

markN
sumber
1
Pengingat wajib bahwa kode pos adalah garis, bukan poligon.
Richard
Bisakah Anda menguraikan? Bagi saya juga di sini mengacu pada area: en.wikipedia.org/wiki/ZIP_Code#Primary_state_prefixes
markusN
Tentu: kode pos merujuk kantor pos tertentu dan rute pengiriman suratnya. Akibatnya, kode pos adalah garis, bukan poligon. Mereka dapat tumpang tindih satu sama lain, mengandung lubang, dan tidak harus mencakup seluruh AS atau negara bagian mana pun. Menggunakannya untuk membagi area berbahaya karena alasan ini. Unit sensus (seperti kelompok blok) adalah pilihan yang lebih baik. Lihat juga: ini dan ini .
Richard
1
Terima kasih! Ini mungkin juga tergantung pada negara, lihat misalnya en.wikipedia.org/wiki/Postal_codes_in_Germany - namun, kode ZIP bukan topik inti saya, hanya ingin menggambarkan dan menjawab pertanyaan asli "Hasilkan poin yang ada di dalam poligon" daripada bahas definisi kode ZIP yang merupakan PL di sini :-)
markusN
1
Tercatat dalam kedua hal. Saya mungkin harus membuat sedikit entri blog sehingga saya bisa mengatakan ini dengan lebih ringkas di lain waktu :-)
Richard
1

Tautan jawaban

https://gis.stackexchange.com/a/307204/103524

Tiga algoritma menggunakan pendekatan yang berbeda.

Git Repo Link

  1. Berikut ini adalah pendekatan sederhana dan terbaik, menggunakan jarak aktual koordinat dari arah x dan y. Algoritma internal menggunakan WGS 1984 (4326) dan hasil mentransformasikan ke SRID yang dimasukkan.

Fungsi ================================================= ==================

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;

Gunakan fungsi dengan kueri sederhana, geometri harus valid dan poligon, multi-poligon, atau amplop

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

Hasil ================================================= ===================== masukkan deskripsi gambar di sini

  1. Fungsi kedua berdasarkan algoritma Nicklas Avén . Sudah mencoba menangani SRID apa pun.

    Saya telah menerapkan perubahan berikut dalam algoritma.

    1. Variabel terpisah untuk arah x dan y untuk ukuran piksel,
    2. Variabel baru untuk menghitung jarak dalam spheroid atau ellipsoid.
    3. Masukkan SRID, fungsi ubah Geom ke lingkungan kerja Spheroid atau Ellipsoid Datum, lalu terapkan jarak ke setiap sisi, dapatkan hasilnya dan ubah ke input SRID.

Fungsi ================================================= ==================

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;

Gunakan dengan kueri sederhana.

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

Hasil ================================================= ==================masukkan deskripsi gambar di sini

  1. Fungsi berdasarkan generator seri.

Fungsi ================================================= =================

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;

Gunakan dengan kueri sederhana.

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

masukkan deskripsi gambar di sini

Muhammad Imran Siddique
sumber