Çokgenin içinde yatan noktalar oluşturun


30

Çokgen özelliğine sahibim ve içinde noktalar oluşturabilmek istiyorum. Bir sınıflandırma görevi için buna ihtiyacım var.

Biri çokgenin içine girinceye kadar rastgele puan üretmek işe yaramaz çünkü bu zamanın gelmesi gerçekten tahmin edilemez.


3
Aksine, zaman tahmin edilebilir. Çokgenin alanıyla çokgenin bölünmüş alanı arasındaki oranla orantılı, tek bir nokta oluşturmak ve test etmek için gereken zamanla orantılıdır. Zaman biraz değişir, ancak değişiklik nokta sayısının kareköküyle orantılıdır. Büyük sayılar için bu önemsiz hale gelir. Bu alan oranını düşük bir değere düşürmek için gerekiyorsa, kıvrımlı çokgenleri daha küçük parçalara bölün. Sadece fraktal çokgenler size sorun çıkartacaktır, ancak onlara sahip olduğunuzdan şüpheliyim!
whuber



@Pablo: iyi bulur. Bununla birlikte, bu soruların her ikisi de yazılıma özgüdür ve her ikisi de rastgele noktalara değil, çokgenlerin içine düzenli nokta dizileri yerleştirmekle ilgilidir
whuber

Çokgenli fark, rastgele puan vs bir poligon içinde normal nokta üretmedir.
Mapperz

Yanıtlar:


20

Çokgenin üçgenlere ayrıştırılması ile başlayın, sonra bunların içinde noktalar oluşturun . (Düzgün bir dağılım için, her üçgeni alanına göre ağırlıklandırın.)


2
+1 Basit ve etkili. Herhangi bir reddedilme olmadan bir üçgen içinde düzgün olarak rastgele noktaların üretilebileceğini belirtmekte fayda var, çünkü herhangi bir üçgen ile yarım köşeli bir ikizkenar dik üçgen arasında (kolayca hesaplanan) alan koruyan eşlemeler var. y koordinatı x koordinatını aşıyor. İki rastgele koordinat oluşturun ve ikizkenar üçgende rastgele bir nokta elde etmek için bunları sıralayın, sonra tekrar orijinal üçgene eşleyin.
whuber

+1 Alıntıladığınız makalenin referans aldığı trilinear koordinatların tartışılmasını gerçekten seviyorum . Bunun, yüzeyini üçgenlerin ayrılması olarak temsil edilen küreye elverişli olacağını düşünüyorum. Öngörülen bir düzlemde, rastgele bir dağılım olmaz, değil mi?
Kirk Kuykendall

@whuber - +1 sana geri döndü. Başka bir yol (bağlantıda, ancak üzerinde el salladılar), reddedilen noktaları, eşit şekilde örneklenmiş dörtgenden paylaşılan kenar boyunca ve tekrar üçgene yansıtmaktır.
Dan S.,

@Kirk - alıntı bağlantısı, "doğru" yoldan önce, trilens koordinatları da dahil olmak üzere bir sürü yanlış (tek tip olmayan) örnekleme yöntemini listelemesi için anti-faydalı bir dokunuştur. Tek tip örnekleme w / trilinear koordinatlarını almanın doğrudan bir yolu yok gibi gözüküyor. Rasgele birim vektörleri 3B olarak lat / lon eşdeğerlerine dönüştürerek tüm alan üzerinde tek tip örneklemeye yaklaşırdım, ama bu sadece benim. (Küresel üçgenler / poligonlarla sınırlı örnekleme konusunda emin değilsiniz.) (Ayrıca, örneğin wgs84'te gerçekten tek tip örnekleme konusunda da emin değilsiniz: sadece toplama açılarının kutuplara doğru biraz eğilimli olacağına inanıyorum.)
Dan S.

1
@Dan Küreyi eşit şekilde örneklemek için silindirik bir eşit alan çıkıntısı kullanın (koordinatlar enlem ve boylamdır). Bir projeksiyon kullanmadan örnek almak istiyorsanız, güzel bir numara var: üç bağımsız standart normal değişken (x, y, z) oluşturun ve bunları noktaya yansıtın (R x / n, R y / n, R * z / n ) burada n ^ 2 = x ^ 2 + y ^ 2 + z ^ 2 ve R toprak yarıçapıdır. Gerekirse dönüştürün (lat, lon) (bir sferoid üzerinde çalışırken otantik enlemleri kullanarak). Çalışır, çünkü bu önemsiz normal dağılım küresel olarak simetriktir. Örnekleme üçgenleri için bir çıkıntıya yapıştırın.
whuber

14

Bu soruya bir QGIS etiketi koyduğunuzda: Random Points aracı bir sınır katmanıyla birlikte kullanılabilir.

görüntü tanımını buraya girin

Kod arıyorsanız, temel eklenti kaynak kodunun yardımı olmalıdır.


1
5 yıl sonra bile, hala gerçekten yardımcı!
Telli Çocuk

10

Çokgenin genişliğini belirleyebilir, daha sonra bu uzantılardaki X ve Y değerleri için rasgele sayı oluşturmayı sınırlayabilirsiniz.

Temel işlem: 1) Maxx, maxy, minx, çokgen köşelerinin minidini belirle, 2) Bu değerleri sınırlar olarak kullanarak rastgele noktalar oluşturun 3) Poligonunuzla kesişmek için her noktayı test edin, 4) Kesişmeyi tatmin eden yeterli noktaya sahip olduğunuzda üretmeyi durdurun Ölçek

Kesişim testi için bir algoritma (C #):

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

10

Dışarıda sizin için ağır yüklerin çoğunu yapan iyi kütüphaneler var.

Python'da [düzgün] [1] kullanan örnek.

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)

Veya .representative_point()nesne içinde bir noktaya ulaşmak için kullanın (dain tarafından belirtildiği gibi):

Geometrik nesnenin içinde olması garanti edilen ucuz bir hesaplanan noktayı döndürür.

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

  [1]: https://shapely.readthedocs.io

2
Düzgün bir şekilde konumlandırılmamalı mı, coğrafya ithalatı ...?
PyMapr

1
Ayrıca kullanabilirsiniz representative_point: yöntem shapely.readthedocs.io/en/latest/...
dain

6

R bir seçenek ?spsampleise, sppakete bakınız . Poligonlar, rgdal paketine yerleştirilmiş herhangi bir GDAL destekli formattan okunabilir ve daha sonra spsampleçeşitli örnekleme seçenekleriyle doğrudan ithal edilen nesnede çalışır.


+1 - R açık kaynak olduğundan biri kopyalamak istiyorsa, nasıl yapıldığını görmek için her zaman kaynağa gidebilirsiniz. Nokta desenleri için spatstat paketindeki simülasyon araçları ile de ilgilenilebilir.
Andy W,

5

GIS analizi açısından çok az gerektiren bir çözüm sunmak istiyorum. Özellikle, herhangi bir poligonun üçgenleştirilmesini gerektirmez.

Sözde kodda verilen aşağıdaki algoritma, temel liste işleme yeteneklerine (aralık oluşturma, uzunluk bulma, toplama, sıralama, alt listeleri çıkarma ve birleştirme) ve aralıktaki rasgele kayan noktaların üretilmesine ek olarak bazı basit işlemlere atıfta bulunur: [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.

Bunların hepsi neredeyse tüm GIS veya grafik programlama ortamlarında mevcuttur (ve değilse kodlaması kolaydır). Clipyozlaşmış poligonları (yani, sıfır alana sahip olanlar) döndürmemelidir.

Prosedür SimpleRandomSampleverimli bir şekilde bir çokgen içinde rastgele dağıtılan noktaların bir listesini alır. Bunun için bir sarıcı SRSher parça etkili bir numune için yeterince kompakt kadar küçük parçalar halinde çokgen kırılır. Bunu yapmak için, her bir parçaya kaç nokta tahsis edileceğine karar vermek için önceden hesaplanmış bir rasgele sayı listesi kullanır.

SRS, parametre değiştirilerek "ayarlanabilir" t. Bu maksimum sınırlayıcı kutudur: tolere edilebilen çokgen alan oranı. Küçültmek (ancak 1'den büyük) çoğu poligonun birçok parçaya bölünmesine neden olur; onu büyütmek, bazı poligonlar için birçok deneme noktasının reddedilmesine neden olabilir (kıvrımlı, şeritlerle veya deliklerle dolu). Bu, orijinal poligonu örneklemek için maksimum sürenin tahmin edilebilir olduğunu garanti eder.

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

Bir sonraki prosedür gerektiğinde kendisini tekrarlı olarak çağırır. Gizemli ifade t*N + 5*Sqrt(t*N)muhafazakar bir şekilde kaç noktaya ihtiyaç duyulacağına dair bir üst sınır olduğunu tahmin eder, bu da şans değişkenliğini açıklar. Bunun başarısız olacağı olasılığı milyonlarca işlem başına sadece 0.3 puandır. İsterseniz bu olasılığını azaltmak için 5 ila 6, hatta 7'yi artırın.

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
}

2

Poligonunuz dışbükey ise ve tüm köşeleri biliyorsanız, dışbükey kabuğun içinde uzanması garantili yeni bir noktayı örneklemek için köşelerin "rasgele" dışbükey ağırlığını düşünmeyi düşünebilirsiniz (bu durumda çokgen).

Örneğin, köşeli bir N dışbükey poligonunuz var

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

Ardından rasgele N dışbükey ağırlıkları oluşturun

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

Rastgele örneklenmiş nokta daha sonra

Y=  w_i*V_i

N dışbükey ağırlıklarını ölçmenin farklı bir yolu olabilir

  • N-1 sayılarını eşit aralıklarla rastgele bir aralıkta (değiştirmeden) seçin, sıralayın ve ağırlıkları almak için aralarındaki N aralıklarını normalleştirin.
  • Ayrıca, durumunuzdaki dışbükey ağırlıklara benzer olan multinom dağılımından önce konjugat olarak kullanılan Dirichlet dağılımından da örnekleyebilirsiniz.

Poligonunuz çok fazla dışbükey olmadığında, önce onu dışbükey bir gövdeye dönüştürmeyi düşünebilirsiniz. Bu, en azından çokgeninizin dışında kalan noktaların sayısını büyük ölçüde sınırlamalıdır.


2

Görevi v.random kullanarak GRASS GIS'de (bir komut) çözmek çok kolaydır .

Seçilen sayfadaki 3 rastgele noktanın nasıl ekleneceğine dair bir örnek aşağıda (Raleigh, NC şehrinin posta kodu alanları) kılavuz sayfasından bulabilirsiniz. SQL "where" deyimini değiştirerek, çokgen (ler) seçilebilir.

Seçili poligonlarda rastgele noktalar oluşturma


1
Zorunlu hatırlatma posta kodlarının çokgen değil, satır olduğunu hatırlatır.
Richard,

Ayrıntılı olabilir misiniz? Bana da burada burada ifade eder: en.wikipedia.org/wiki/ZIP_Code#Primary_state_prefixes
markusN

Elbette: posta kodları, belirli postanelere ve posta teslim yollarına gönderme yapar. Sonuç olarak, posta kodları çokgen değil, satırlardır. Birbirleriyle üst üste gelebilirler, delikler içerebilirler ve tüm ABD'yi veya verilen herhangi bir devleti kapsamazlar. Bunları bölgeleri bölmek için kullanmak bu nedenle tehlikelidir. Sayım birimleri (blok grupları gibi) daha iyi bir seçimdir. Ayrıca bakınız: bu ve bu .
Richard

1
Teşekkürler! Muhtemelen ülkeye de bağlıdır, örneğin bkz. En.wikipedia.org/wiki/Postal_codes_in_Germany - ancak, ZIP kodları benim asıl konum değil, sadece "Çokgen içinde yatan noktaları oluştur" sorusunu açıklamak ve cevaplamak istedim. burada OT olan ZIP kodu tanımlarını tartışmak :-)
markusN

1
Her iki sayımda da kaydedildi. Muhtemelen küçük bir blog girişi yapmalıyım, bu yüzden bir dahaki sefere tüm bunları daha kısa sürede söyleyebilirim :-)
Richard

1

Cevap linki

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

Farklı yaklaşımlar kullanarak üç algoritma.

Git Repo Bağlantısı

  1. İşte koordinatların gerçek mesafesini x ve y yönünden kullanarak basit ve en iyi yaklaşım. Dahili algoritma WGS 1984'ü (4326) kullanır ve sonuçta eklenen SRID'ye dönüşür.

İşlev =========================================================================== ==================

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;

Fonksiyonu basit bir sorgu ile kullanın, geometri geçerli olmalı ve çokgen, çokgenler veya zarf

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

Sonuç ================================================= ===================== görüntü tanımını buraya girin

  1. Nicklas Avén algoritmasına dayalı ikinci fonksiyon . Herhangi bir SRID ile işlem yapmaya çalıştınız.

    Algoritmada aşağıdaki değişiklikleri uyguladım.

    1. Piksel boyutu için x ve y yönü için ayrı değişken,
    2. Yeni değişken, sferoid veya elipsoid içindeki mesafeyi hesaplar.
    3. Herhangi bir SRID girişi yapın, fonksiyon Geom'i Spheroid veya Ellipsoid Datum'un çalışma ortamına dönüştürün, ardından her bir tarafa mesafeyi uygulayın, sonucu alın ve SRID girişine dönüştürün.

İşlev =========================================================================== ==================

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;

Basit bir sorgu ile kullanın.

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

Sonuç ================================================= ==================görüntü tanımını buraya girin

  1. Seri jeneratöre dayalı fonksiyon.

Fonksiyon ================================================= =================

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;

Basit bir sorgu ile kullanın.

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

görüntü tanımını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.