다각형 안에있는 점 생성


30

다각형 기능이 있으며 그 안에 점을 생성 할 수 있기를 원합니다. 하나의 분류 작업에 필요합니다.

임의의 점이 다각형 내부에 올 때까지 임의의 점을 생성하면 시간이 실제로 예측할 수 없기 때문에 작동하지 않습니다.


3
반대로, 시간은 예측 가능합니다. 이는 단일 영역을 생성하고 테스트하는 데 필요한 시간을 다각형 영역으로 나눈 다각형 영역의 비율에 비례합니다. 시간은 조금씩 다르지만 변동은 포인트 수의 제곱근에 비례합니다. 게으른 숫자의 경우 이는 중요하지 않습니다. 이 면적 비율을 낮은 값으로 낮추려면 필요한 경우 구불 구불 한 다각형을 더 작은 조각으로 나눕니다. 프랙탈 다각형 만 있으면 문제가 발생하지만 의심 할 여지가 없습니다!
whuber



@Pablo : 잘 찾았습니다. 그러나 그 질문의 양이 소프트웨어의 특정 및 다각형 내의 점의 정규 배열을 배치 모두 문제가 아닌 임의의 지점입니다
whuber

whuber의 차이점은 다각형 내에서 임의의 점 대 규칙적인 점 생성입니다.
Mapperz

답변:


20

다각형을 삼각형으로 분해 한 다음 그 안에 점생성하십시오 . (균일 한 분포를 위해서는 각 삼각형의 면적을 가중하십시오.)


2
+1 간단하고 효과적입니다. 삼각형과 이등변 직각 삼각형 사이에 (쉽게 계산 된) 영역 보존 매핑이 있기 때문에 삼각형 내에서 균일하게 임의의 점이 전혀 거부되지 않고 생성 될 수 있음을 지적하는 것이 좋습니다. y 좌표가 x 좌표를 초과합니다. 이등변 삼각형에서 임의의 점을 얻기 위해 두 개의 임의의 좌표를 생성하고 정렬 한 다음 원래 삼각형에 다시 매핑합니다.
whuber

+1 나는 당신이 인용 한 기사에서 언급 한 삼선 좌표에 대한 토론을 정말 좋아합니다 . 나는 이것이 표면이 삼각형의 테셀레이션으로 표현되는 구체에 적합하다고 생각합니다. 투영 된 비행기에서, 그것은 실제로 무작위 분포가 아닐까요?
Kirk Kuykendall

@whuber-다시 +1 다른 방법 (링크에서 수동으로 손을 흔들면)은 공유 된 가장자리를 가로 질러 균일하게 샘플링 된 사변형에서 거부 된 점을 삼각형으로 다시 반영하는 것입니다.
Dan S.

@Kirk-인용 링크는 "올바른"방식 이전에 삼선 형 좌표를 포함하여 잘못된 (비 균일) 샘플링 방법을 나열한다는 점에서 도움이되지 않는 터치입니다. 삼선 좌표로 균일 한 샘플링을 얻는 직접적인 방법이없는 것 같습니다. 3d의 임의의 단위 벡터를 위도 / 경도의 등가로 변환하여 전체 구에 걸쳐 균일 한 샘플링에 접근하지만 그저 나뿐입니다. (구형 삼각형 / 폴리곤으로 제한되는 샘플링에 대해서는 확실하지 않습니다.) (예를 들어 wgs84에서 실제로 균일 한 샘플링에 대해서도 확실하지 않습니다. 피킹 각도는 극쪽으로 약간 편향 될 것입니다.)
Dan S.

1
@Dan 구를 균일하게 샘플링하려면 원통형 등 면적 투영법을 사용하십시오 (좌표는 경도와 위도의 코사인입니다). 프로젝션을 사용하지 않고 샘플링하려면 세 가지 독립적 인 표준 정규 변량 (x, y, z)을 생성하고 점 (R x / n, R y / n, R * z / n )으로 투영합니다. ) 여기서 n ^ 2 = x ^ 2 + y ^ 2 + z ^ 2이고 R은 지구 반경입니다. 필요한 경우 (구상, 위도)로 변환하십시오 (스페 로이드 작업시 정위도 사용). 이 삼 변량 정규 분포가 구형 대칭이기 때문에 작동합니다. 삼각형을 샘플링하려면 투영을 고수하십시오.
whuber

14

이 질문에 QGIS 태그를 넣으면 랜덤 포인트 도구를 경계 레이어와 함께 사용할 수 있습니다.

여기에 이미지 설명을 입력하십시오

코드를 찾고 있다면 기본 플러그인 소스 코드가 도움이 될 것입니다.


1
5 년이 지난 후에도 여전히 도움이됩니다!
좌초 된 아이

10

다각형의 범위를 결정한 다음 해당 범위 내에서 X 및 Y 값의 난수 생성을 제한 할 수 있습니다.

기본 프로세스 : 1) 다각형 정점의 최대 값, 최대 값, 최소값, 최소값을 결정합니다. 2)이 값을 경계로 사용하여 임의의 점을 생성합니다. 3) 다각형과의 교점에 대해 각 점을 테스트합니다. 4) 교점을 만족하는 충분한 점이 있으면 생성을 중지합니다. 테스트

교차 테스트를위한 알고리즘 (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

당신을 위해 대부분의 무거운 작업을 수행하는 좋은 라이브러리가 있습니다.

파이썬에서 [shapely] [1]을 사용하는 예제.

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)

또는 .representative_point()객체 내에서 포인트를 얻는 데 사용 하십시오 (dain이 언급 한 바와 같이).

지오메트리 객체 내에 있도록 저렴하게 계산 된 점을 반환합니다.

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

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

2
shape.geometry import에서 가져 와서는 안됩니까?
PyMapr

1
당신은 또한 사용할 수 있습니다 representative_point: 방법 shapely.readthedocs.io/en/latest/...을
다인

6

R이 옵션 인 ?spsample경우 sp패키지를 참조하십시오 . 다각형은 rgdal 패키지에 내장 된 모든 GDAL 지원 형식에서 읽을 수 있으며 spsample다양한 샘플링 옵션을 통해 가져온 객체에서 직접 작동합니다.


+1-R은 오픈 소스이므로 복제를 원할 경우 언제든지 소스로 이동하여 수행 방법을 확인할 수 있습니다. 포인트 패턴의 경우 spatstat 패키지의 시뮬레이션 도구에 관심이있을 수도 있습니다.
Andy W

5

GIS 분석 측면에서 거의 요구되지 않는 솔루션을 제공하고 싶습니다. 특히 다각형을 삼각 측량 할 필요가 없습니다.

의사 코드로 제공되는 다음 알고리즘은 기본 목록 처리 기능 (생성, 길이 찾기, 추가, 정렬, 하위 목록 추출 및 연결) 외에 간격 [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.

이들은 거의 모든 GIS 또는 그래픽 프로그래밍 환경에서 사용할 수 있습니다 (그렇지 않으면 코딩하기 쉽습니다). Clip축퇴 다각형 (즉, 면적이 0 인 다각형)을 반환해서는 안됩니다.

프로시 저는 SimpleRandomSample다각형 내에 무작위로 분포 된 점 목록을 효율적으로 얻습니다. 에 대한 래퍼로 SRS, 각 조각이 효율적으로 샘플링되기에 충분히 컴팩트해질 때까지 다각형을 작은 조각으로 나눕니다. 이를 위해 사전 계산 된 난수 목록을 사용하여 각 조각에 할당 할 포인트 수를 결정합니다.

매개 변수를 변경하여 SRS를 "조정"할 수 있습니다 t. 이것은 허용 가능한 최대 경계 상자 : 폴리곤 면적 비율입니다. 크기를 작게 (그러나 1보다 크게) 만들면 대부분의 다각형이 여러 조각으로 분할됩니다. 크기를 크게하면 일부 다각형 (심각한, 은색 또는 구멍이 가득 찬)에 대해 많은 시험 점이 거부 될 수 있습니다. 이를 통해 원래 다각형을 샘플링하는 최대 시간을 예측할 수 있습니다.

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

다음 절차는 필요한 경우 재귀 적으로 호출합니다. 신비한 표현 t*N + 5*Sqrt(t*N)은 기회 변동성을 고려하여 필요한 포인트 수의 상한을 보수적으로 추정합니다. 이것이 실패 할 가능성은 백만 건의 프로 시저 호출 당 0.3입니다. 원하는 경우이 가능성을 줄이려면 5에서 6 또는 7을 늘리십시오.

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

다각형이 볼록하고 모든 꼭짓점을 알고 있다면 꼭짓점에 대한 "무작위"볼록한 가중치를 적용하여 볼록 껍질 (이 경우 다각형) 안에 놓일 수있는 새 점을 샘플링하는 것이 좋습니다.

예를 들어 정점이있는 N면 볼록 다각형이 있다고 가정합니다.

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

그런 다음 무작위로 N 볼록한 가중치를 생성하십시오.

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

그런 다음 무작위로 샘플링 된 점은

Y=  w_i*V_i

N 볼록한 무게를 샘플링하는 다른 방법이있을 수 있습니다

  • 범위 내에서 균일하게 무작위로 N-1 숫자를 골라 내고 (교체하지 않고) 정렬하고 가중치를 얻기 위해 N 간격을 정규화하십시오.
  • 또한 다항식 분포 이전에 켤레로 자주 사용되는 Dirichlet 분포에서 표본을 추출 할 수 있으며, 이는 케이스의 볼록한 가중치와 유사합니다.

다각형이 볼록하지 않은 심각하지 않은 경우 먼저 다각형을 볼록 껍질로 변환하는 것이 좋습니다. 이것은 최소한 다각형 바깥에있는 점의 수를 크게 제한해야합니다.


2

이 작업은 v.random을 사용하여 GRASS GIS (하나의 명령)에서 매우 쉽게 해결할 수 있습니다.

다음은 매뉴얼 페이지에서 선택한 다각형 (여기서는 노스 캐롤라이나 롤리시의 우편 번호 영역)에 3 개의 임의의 점을 추가하는 방법에 대한 예입니다. SQL "where"문을 수정하여 다각형을 선택할 수 있습니다.

선택한 다각형에서 임의의 점 생성


1
우편 번호는 다각형이 아니라 선이라는 것을 상기시켜야합니다.
Richard

정교하게 할 수 있습니까? 나에게도 여기에 영역이있다 : en.wikipedia.org/wiki/ZIP_Code#Primary_state_prefixes
markusN

물론 : 우편 번호는 특정 우체국과 우편 배달 경로를 참조합니다. 결과적으로 우편 번호는 다각형이 아닌 선입니다. 그것들은 서로 겹칠 수 있고 구멍을 포함 할 수 있으며 반드시 미국 전체 나 특정 주를 덮을 필요는 없습니다. 이러한 이유로 영역을 나누는 것이 위험합니다. 인구 조사 단위 (예 : 블록 그룹)가 더 나은 선택입니다. 참조 : this and this .
Richard

1
감사! 아마도 국가에 따라 다를 수 있습니다 (예 : en.wikipedia.org/wiki/Postal_codes_in_Germany 참조). 그러나 우편 번호는 핵심 주제가 아니며 "다각형 내부에있는 점 생성"이라는 원래 질문을 설명하고 대답하고 싶었습니다. 여기에 OT 인 우편 번호 정의에 대해 토론하십시오 :-)
markusN

1
두 가지 모두에 주목하십시오. 아마 다음 번에 더 간결하게 말할 수 있도록 약간의 블로그 항목을 만들어야 할 것입니다 :-)
Richard

1

답변 링크

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

다른 접근법을 사용하는 세 가지 알고리즘.

힘내 레포 링크

  1. 여기에 x와 y 방향에서 실제 좌표 거리를 사용하는 간단하고 가장 좋은 방법이 있습니다. 내부 알고리즘은 WGS 1984 (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; 결과 ================================================== ==========================

여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.