2D 포인트가 다각형 내에 있는지 어떻게 알 수 있습니까?


497

적중 테스트 (예 :)에 사용하기 위해 다각형 알고리즘 내에 빠른 2D 포인트 를 만들려고합니다 Polygon.contains(p:Point). 효과적인 기술에 대한 제안을 부탁드립니다.


오른손 잡이나 왼손잡이 문제에 대한 인식을 알려주는 것을 잊었습니다. "내부"와 "외부"로 해석 될 수 있습니다.-RT
Richard T

13
예, 이제 질문에 많은 세부 사항이 지정되어 있지 않다는 것을 알고 있지만이 시점에서 다양한 응답을 보는 데 관심이 있습니다.
Scott Evernden

4
90면 폴리곤을 에니어 콘 타곤 (enneacontagon)이라고하고 10,000면 폴리곤을 미리 아곤이라고합니다.

"가장 우아하다"는 목표를 벗어난 것입니다. "모든 작업"알고리즘을 찾는 데 어려움을 겪었 기 때문입니다. 나는 나 자신을 밖으로 파악해야합니다 stackoverflow.com/questions/14818567/...
davidkonrad

답변:


731

그래픽의 경우 정수를 선호하지 않습니다. 많은 시스템이 UI 페인팅을 위해 정수를 사용하지만 (픽셀은 정수), 예를 들어 macOS는 모든 것을 위해 float를 사용합니다. macOS는 포인트 만 알고 있으며 포인트는 한 픽셀로 변환 될 수 있지만 모니터 해상도에 따라 다른 것으로 변환 될 수 있습니다. 망막 화면에서 반점 (0.5 / 0.5)은 픽셀입니다. 여전히 macOS UI가 다른 UI보다 훨씬 느리다는 것을 알지 못했습니다. 모든 3D API (OpenGL 또는 Direct3D)도 플로트와 함께 작동하며 최신 그래픽 라이브러리는 종종 GPU 가속을 활용합니다.

이제 당신은 속도가 주된 관심사라고 말했습니다. 알았어요. 정교한 알고리즘을 실행하기 전에 먼저 간단한 테스트를 수행하십시오. 다각형 주위에 축 정렬 경계 상자를 만듭니다 . 이것은 매우 쉽고 빠르며 이미 많은 계산을 안전하게 할 수 있습니다. 어떻게 작동합니까? 다각형의 모든 점을 반복하고 X 및 Y의 최소 / 최대 값을 찾습니다.

예를 들어 포인트가 (9/1), (4/3), (2/7), (8/2), (3/6)있습니다. 즉, Xmin은 2, Xmax는 9, Ymin은 1, Ymax는 7입니다. 두 모서리 (2/1)와 (9/7)이있는 사각형 외부의 점은 다각형 내에있을 수 없습니다.

// p is your point, p.x is the x coord, p.y is the y coord
if (p.x < Xmin || p.x > Xmax || p.y < Ymin || p.y > Ymax) {
    // Definitely not within the polygon!
}

이것은 어느 시점에서나 실행되는 첫 번째 테스트입니다. 보시다시피이 테스트는 매우 빠르지 만 매우 거칠습니다. 경계 사각형 내에있는 점을 처리하려면보다 정교한 알고리즘이 필요합니다. 이를 계산하는 방법에는 몇 가지가 있습니다. 어떤 방법이 작동하는지는 다각형에 구멍이 있거나 항상 단색인지에 따라 다릅니다. 다음은 단단한 것들 (하나의 볼록, 하나의 오목)의 예입니다.

구멍이없는 다각형

그리고 여기에 구멍이 하나 있습니다 :

구멍이있는 다각형

녹색은 중간에 구멍이 있습니다!

위의 세 가지 경우를 모두 처리 할 수 ​​있고 여전히 매우 빠른 가장 쉬운 알고리즘은 레이 캐스팅 입니다. 알고리즘의 아이디어는 매우 간단합니다. 다각형 외부에서 점까지 가상 광선을 그리고 다각형 측면에 얼마나 자주 충돌하는지 계산합니다. 적중 수가 짝수이면 다각형 외부에 있고 홀수이면 내부에 있습니다.

광선이 다각형을 절단하는 방법 시연

권수 알고리즘은 매우 가까운 다각형 라인에있는 점에 대한 더 정확하지만 훨씬 낮은 속도도의 대안이 될 것입니다. 부동 소수점 정밀도 및 반올림 문제로 인해 다각형쪽에 너무 가까운 점에 대해서는 광선 주조가 실패 할 수 있지만 실제로는 점이 측면에 가까운 것처럼 보이는 것처럼 거의 문제가되지 않습니다. 뷰어가 이미 내부 또는 외부에 있는지 인식합니다.

여전히 위의 경계 상자가 있습니다. 기억하십니까? 경계 상자 외부의 점을 선택하여 광선의 시작점으로 사용하십시오. 예를 들어 점 (Xmin - e/p.y)은 다각형 외부에 있어야합니다.

그러나 무엇 e입니까? 글쎄, e(실제로 엡실론) 경계 상자에 패딩을 제공합니다 . 내가 말했듯이, 다각형 선에 너무 가까이 시작하면 광선 추적이 실패합니다. 경계 상자가 다각형과 같을 수 있으므로 (다각형이 축 정렬 된 사각형 인 경우 경계 상자가 다각형 자체와 동일합니다!)이를 안전하게하려면 패딩이 필요합니다. 얼마나 크게 선택해야 e합니까? 너무 크지 않습니다. 그리기에 사용하는 좌표계 배율에 따라 다릅니다. 픽셀 단계 너비가 1.0 인 경우 1.0을 선택하십시오 (아직 0.1도 작동 했음).

이제 시작과 끝 좌표가있는 광선이 생겼으니, 문제는 " 다각형 내의 점 "에서 " 선이 다각형면과 얼마나 자주 교차하는지 "로 이동합니다. 따라서 이전과 같이 다각형 점으로 작업 할 수 없으므로 실제 측면이 필요합니다. 한 변은 항상 두 점으로 정의됩니다.

side 1: (X1/Y1)-(X2/Y2)
side 2: (X2/Y2)-(X3/Y3)
side 3: (X3/Y3)-(X4/Y4)
:

모든면에서 광선을 테스트해야합니다. 광선을 벡터로, 모든면을 벡터로 간주하십시오. 광선은 각면을 정확히 한 번만 또는 전혀 치지 않아야합니다. 같은 쪽을 두 번 칠 수 없습니다. 2D 공간의 두 선은 평행하지 않으면 항상 정확히 한 번 교차합니다.이 경우 절대 교차하지 않습니다. 그러나 벡터의 길이는 제한되어 있기 때문에 두 벡터는 평행하지 않으며 서로 만나기에는 너무 짧기 때문에 여전히 교차하지 않을 수 있습니다.

// Test the ray against all sides
int intersections = 0;
for (side = 0; side < numberOfSides; side++) {
    // Test if current side intersects with ray.
    // If yes, intersections++;
}
if ((intersections & 1) == 1) {
    // Inside of polygon
} else {
    // Outside of polygon
}

지금까지는 잘되었지만 두 벡터가 교차하는지 어떻게 테스트합니까? 다음은 트릭을 수행 해야하는 C 코드 (테스트되지 않음)입니다.

#define NO 0
#define YES 1
#define COLLINEAR 2

int areIntersecting(
    float v1x1, float v1y1, float v1x2, float v1y2,
    float v2x1, float v2y1, float v2x2, float v2y2
) {
    float d1, d2;
    float a1, a2, b1, b2, c1, c2;

    // Convert vector 1 to a line (line 1) of infinite length.
    // We want the line in linear equation standard form: A*x + B*y + C = 0
    // See: http://en.wikipedia.org/wiki/Linear_equation
    a1 = v1y2 - v1y1;
    b1 = v1x1 - v1x2;
    c1 = (v1x2 * v1y1) - (v1x1 * v1y2);

    // Every point (x,y), that solves the equation above, is on the line,
    // every point that does not solve it, is not. The equation will have a
    // positive result if it is on one side of the line and a negative one 
    // if is on the other side of it. We insert (x1,y1) and (x2,y2) of vector
    // 2 into the equation above.
    d1 = (a1 * v2x1) + (b1 * v2y1) + c1;
    d2 = (a1 * v2x2) + (b1 * v2y2) + c1;

    // If d1 and d2 both have the same sign, they are both on the same side
    // of our line 1 and in that case no intersection is possible. Careful, 
    // 0 is a special case, that's why we don't test ">=" and "<=", 
    // but "<" and ">".
    if (d1 > 0 && d2 > 0) return NO;
    if (d1 < 0 && d2 < 0) return NO;

    // The fact that vector 2 intersected the infinite line 1 above doesn't 
    // mean it also intersects the vector 1. Vector 1 is only a subset of that
    // infinite line 1, so it may have intersected that line before the vector
    // started or after it ended. To know for sure, we have to repeat the
    // the same test the other way round. We start by calculating the 
    // infinite line 2 in linear equation standard form.
    a2 = v2y2 - v2y1;
    b2 = v2x1 - v2x2;
    c2 = (v2x2 * v2y1) - (v2x1 * v2y2);

    // Calculate d1 and d2 again, this time using points of vector 1.
    d1 = (a2 * v1x1) + (b2 * v1y1) + c2;
    d2 = (a2 * v1x2) + (b2 * v1y2) + c2;

    // Again, if both have the same sign (and neither one is 0),
    // no intersection is possible.
    if (d1 > 0 && d2 > 0) return NO;
    if (d1 < 0 && d2 < 0) return NO;

    // If we get here, only two possibilities are left. Either the two
    // vectors intersect in exactly one point or they are collinear, which
    // means they intersect in any number of points from zero to infinite.
    if ((a1 * b2) - (a2 * b1) == 0.0f) return COLLINEAR;

    // If they are not collinear, they must intersect in exactly one point.
    return YES;
}

입력 값은 벡터 1 ( 및 )과 벡터 2 ( 및 ) 의 두 끝점 입니다 . 따라서 2 개의 벡터, 4 개의 점, 8 개의 좌표가 있습니다. 그리고 분명하다. 교차로를 늘리고 아무것도하지 않습니다.v1x1/v1y1v1x2/v1y2v2x1/v2y1v2x2/v2y2YESNOYESNO

COLLINEAR는 어떻습니까? 즉, 두 벡터 모두 위치와 길이에 따라 동일한 무한 선에 놓이거나 전혀 교차하지 않거나 끝없는 포인트 수로 교차합니다. 나는이 사건을 어떻게 처리 해야할지 잘 모르겠습니다. 어쨌든 교차로 계산하지 않을 것입니다. 부동 소수점 반올림 오류로 인해 실제로이 경우는 드물다. 더 나은 코드는 아마도 테스트하지 않을 == 0.0f것이지만 < epsilonepsilon이 다소 작은 곳 과 같은 것을 대신합니다 .

더 많은 수의 포인트를 테스트 해야하는 경우 다각형 측의 선형 방정식 표준 형태를 메모리에 유지하여 모든 것을 약간 가속화 할 수 있으므로 매번 다시 계산할 필요는 없습니다. 이렇게하면 다각형 면당 3 개의 부동 소수점 값을 메모리에 저장하는 대신 모든 테스트에서 2 개의 부동 소수점 곱셈과 3 개의 부동 소수점 빼기를 저장할 수 있습니다. 일반적인 메모리와 계산 시간의 균형을 유지합니다.

마지막으로, 3D 하드웨어를 사용하여 문제를 해결할 수 있다면 흥미로운 대안이 있습니다. GPU가 모든 작업을 수행하도록하십시오. 화면이 아닌 그림 표면을 만듭니다. 검은 색으로 완전히 채우십시오. 이제 OpenGL 또는 Direct3D를 사용하여 다각형 (또는 점이 다각형 내에 있는지 여부 만 테스트하려는 경우 모든 다각형)을 페인트하고 다각형을 다른 색으로 채 웁니다 색상 (예 : 흰색) 점이 다각형 내에 있는지 확인하려면 그리기 표면에서이 점의 색을 가져옵니다. 이것은 단지 O (1) 메모리 반입입니다.

물론이 방법은 도면 표면이 크지 않아도 사용할 수 있습니다. GPU 메모리에 맞지 않으면이 방법은 CPU에서 수행하는 것보다 느립니다. 거대해야하고 GPU가 최신 셰이더를 지원하는 경우 위에 표시된 레이 캐스팅을 GPU 셰이더로 구현하여 GPU를 계속 사용할 수 있습니다. 더 많은 수의 폴리곤 또는 많은 수의 테스트 포인트가 있다면, 이것은 일부 GPU가 64-256 포인트를 병렬로 테스트 할 수 있다는 점을 고려할 것입니다. 그러나 CPU에서 GPU로 데이터를 다시 전송하는 것은 항상 비용이 많이 들기 때문에 점 또는 다각형이 동적이며 자주 변경되는 몇 가지 간단한 다각형에 대해 몇 개의 점을 테스트하기 만하면 GPU 접근 방식이 거의 지불하지 않습니다. 떨어져서.


26
환상적인 답변 +1. 하드웨어를 사용하여 그래픽 카드에서 데이터를 다시 가져와야하기 때문에 다른 곳에서 느리게 읽었습니다. 그러나 나는 CPU에서 많은 부하를받는 원리를 좋아합니다. OpenGL 에서이 작업을 수행하는 방법에 대한 좋은 참고 자료가 있습니까?
Gavin

3
+1이기 때문에 +1입니다! 가장 큰 문제는 다각형과 테스트 포인트가 그리드에 정렬되어있는 경우 (드문 경우는 아님) "다각적 인"교차점을 처리해야합니다 (예 : 다각형 포인트를 통한 직선)! (1 대신 2의 패리티를 생성). 이 이상한 영역으로 가져옵니다 : stackoverflow.com/questions/2255842/...를 . 컴퓨터 그래픽은 이론적으로 단순하고 실제로는 털이 많은 특수한 사례로 가득합니다.
Jared Updike

7
@RMorrisey : 왜 그렇게 생각하십니까? 오목한 다각형에 어떻게 실패하는지 알 수 없습니다. 광선은 다각형이 오목 할 때 다각형을 여러 번 떠났다가 다시 입력 할 수 있지만 결국 점이 안쪽에 있거나 바깥에 있어도 오목 다각형에 대한 적중 카운터는 홀수입니다.
Mecki

6
softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm에 설명 된 '고속 권선 수 알고리즘' 은 매우 빠르게 작동합니다 ...
SP

10
x와 y 좌표를 구분하기 위해 /를 사용하는 것은 혼란스럽고 x를 y로 나눈 값으로 읽습니다. x, y (예 : x 쉼표)를 사용하는 것이 훨씬 더 명확합니다.
Ash

583

다음 코드 조각이 가장 좋은 해결책이라고 생각합니다 ( here ).

int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
  int i, j, c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
     (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
  }
  return c;
}

인수

  • nvert : 다각형의 꼭짓점 수입니다. 끝에서 첫 번째 정점을 반복할지 여부는 위에서 언급 한 기사에서 설명했습니다.
  • vertx, verty : 다각형 정점의 x 및 y 좌표를 포함하는 배열입니다.
  • testx, testy : 테스트 포인트의 X 및 Y 좌표.

짧고 효율적이며 볼록 및 오목 다각형 모두에서 작동합니다. 이전에 제안한대로 먼저 경계 사각형을 확인하고 다각형 구멍을 별도로 처리해야합니다.

이것에 대한 아이디어는 매우 간단합니다. 저자는 다음과 같이 설명합니다.

테스트 포인트에서 반 무한 광선을 수평으로 (x, 고정 y 증가) 가로로 교차하고 얼마나 많은 가장자리를 교차하는지 계산합니다. 각 교차점에서 광선은 내부와 외부 사이를 전환합니다. 이것을 요르단 곡선 정리라고합니다.

변수 c는 수평 광선이 모서리를 가로 질러 갈 때마다 0에서 1로, 1에서 0으로 전환됩니다. 따라서 기본적으로 교차 된 가장자리 수가 짝수인지 홀수인지 추적합니다. 0은 짝수를 의미하고 1은 홀수를 의미합니다.


5
질문. 내가 전달한 변수는 정확히 무엇입니까? 그들은 무엇을 상징합니까?
tekknolagi

9
@Mick 확인 verty[i]하고 verty[j]의 양쪽 testy이므로 서로 같지 않습니다.
피터 우드

4
이 코드는 강력하지 않으므로 사용하지 않는 것이 좋습니다. 자세한 분석을 제공하는 링크는 다음과 같습니다. www-ma2.upc.es/geoc/Schirra-pointPolygon.pdf
Mikola

13
이 접근법은 실제로 한계가 있습니다 (가장자리 대) : 다각형 [(10,10), (10,20), (20,20), (20,10)]에서 점 (15,20)을 확인하면 참 대신 거짓. (10,20) 또는 (20,15)와 동일합니다. 다른 모든 경우에는 알고리즘이 완벽하게 작동하며 가장자리 케이스의 음수는 내 응용 프로그램에 적합합니다.
Alexander Pacha

10
@Alexander, 이것은 실제로 의도적으로 설계된 것입니다 : 왼쪽 및 아래쪽 경계를 위쪽 및 오른쪽 경계와 반대의 의미로 처리하여 두 개의 개별 다각형이 모서리를 공유해야하는 경우이 모서리를 따르는 모든 점은 하나의 다각형에 위치합니다. .. 유용한 속성.
wardw

69

여기의 C # 버전입니다 nirg에 의해 주어진 대답 에서 온다, 이 RPI 교수 . 해당 RPI 소스의 코드를 사용하려면 속성이 필요합니다.

바운딩 박스 체크가 상단에 추가되었습니다. 그러나 James Brown이 지적했듯이 주요 코드는 경계 상자 검사 자체만큼 빠르기 때문에 검사하는 대부분의 점이 경계 상자 안에있는 경우 경계 상자 검사는 실제로 전체 작업을 느리게 할 수 있습니다 . 따라서 경계 상자를 체크 아웃 상태로 두거나 다각형의 모양이 너무 자주 변경되지 않는 경우 다각형의 경계 상자를 미리 계산하는 방법이 있습니다.

public bool IsPointInPolygon( Point p, Point[] polygon )
{
    double minX = polygon[ 0 ].X;
    double maxX = polygon[ 0 ].X;
    double minY = polygon[ 0 ].Y;
    double maxY = polygon[ 0 ].Y;
    for ( int i = 1 ; i < polygon.Length ; i++ )
    {
        Point q = polygon[ i ];
        minX = Math.Min( q.X, minX );
        maxX = Math.Max( q.X, maxX );
        minY = Math.Min( q.Y, minY );
        maxY = Math.Max( q.Y, maxY );
    }

    if ( p.X < minX || p.X > maxX || p.Y < minY || p.Y > maxY )
    {
        return false;
    }

    // https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
    bool inside = false;
    for ( int i = 0, j = polygon.Length - 1 ; i < polygon.Length ; j = i++ )
    {
        if ( ( polygon[ i ].Y > p.Y ) != ( polygon[ j ].Y > p.Y ) &&
             p.X < ( polygon[ j ].X - polygon[ i ].X ) * ( p.Y - polygon[ i ].Y ) / ( polygon[ j ].Y - polygon[ i ].Y ) + polygon[ i ].X )
        {
            inside = !inside;
        }
    }

    return inside;
}

5
감사합니다. JavaScript로 변환했습니다. stackoverflow.com/questions/217578/…
Philipp Lenssen

2
GraphicsPath를 사용하는 것보다 1000 배 이상 빠릅니다. 경계 상자 검사는 기능을 약 70 % 느리게 만듭니다.
James Brown

GraphicsPath.IsVisible ()은 속도가 느릴뿐만 아니라 0.01f 범위의 변이있는 매우 작은 다각형에서는 잘 작동하지 않습니다.
NDepend 팀의 Patrick

50

다음은 Nirg의 접근 방식을 기반으로 한 M. Katz의 답변에 대한 JavaScript 변형입니다.

function pointIsInPoly(p, polygon) {
    var isInside = false;
    var minX = polygon[0].x, maxX = polygon[0].x;
    var minY = polygon[0].y, maxY = polygon[0].y;
    for (var n = 1; n < polygon.length; n++) {
        var q = polygon[n];
        minX = Math.min(q.x, minX);
        maxX = Math.max(q.x, maxX);
        minY = Math.min(q.y, minY);
        maxY = Math.max(q.y, maxY);
    }

    if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
        return false;
    }

    var i = 0, j = polygon.length - 1;
    for (i, j; i < polygon.length; j = i++) {
        if ( (polygon[i].y > p.y) != (polygon[j].y > p.y) &&
                p.x < (polygon[j].x - polygon[i].x) * (p.y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x ) {
            isInside = !isInside;
        }
    }

    return isInside;
}

32

점 p와 각 다각형 정점 사이의 방향 각을 계산합니다. 총 방향 각도가 360도이면 점이 내부에 있습니다. 총계가 0이면 점이 외부에있는 것입니다.

나는이 방법이 더 강력하고 수치 정밀도에 덜 의존하기 때문에이 방법을 더 좋아합니다.

교차점 수를 계산하는 동안 정점에 도달 할 수 있기 때문에 교차점 수의 균일 성을 계산하는 방법이 제한됩니다.

편집 : 그건 그렇고,이 방법은 오목하고 볼록한 다각형으로 작동합니다.

편집 : 최근 에 주제에 대한 전체 Wikipedia 기사 를 찾았습니다 .


1
아니요, 사실이 아닙니다. 이것은 다각형의 볼록성과 관계없이 작동합니다.
David Segonds

2
@DarenW : 꼭짓점 당 하나의 acos; 반면에이 알고리즘은 분기가 전혀 없으므로 SIMD에서 구현하기가 가장 쉬워야합니다.
Jasper Bekkers

1
@emilio, 점이 삼각형에서 멀리 떨어져 있으면 점에 의해 형성된 각도와 삼각형의 두 정점이 90 도가되는 방법을 알 수 없습니다.
David Segonds 2009

2
첫 번째로 경계 상자 검사를 사용하여 "포인트가 너무 먼"사례를 해결하십시오. 삼각법의 경우 미리 생성 된 테이블을 사용할 수 있습니다.
JOM

3
이것은 O (n)이므로 최소의 계산이 필요하므로 최적의 솔루션입니다. 모든 다각형에 적용됩니다. 30 년 전 첫 IBM 직장에서이 솔루션을 연구했습니다. 그들은 서명을했고 오늘날에도 GIS 기술에 사용하고 있습니다.
Dominic Cerisano

24

이 질문은 매우 흥미 롭습니다. 이 게시물에 대한 다른 답변과 다른 실행 가능한 아이디어가 있습니다. 아이디어는 목표의 내부 또는 외부를 결정하기 위해 각도의 합을 사용하는 것입니다. 감기 번호 로 더 잘 알려져 있습니다 .

x를 목표 지점으로 둡니다. 배열 [0, 1, .... n]을 영역의 모든 점으로 둡니다. 대상 지점을 모든 경계 지점과 선으로 연결하십시오. 목표 지점이이 영역 안에있는 경우 모든 각도의 합은 360 도입니다. 그렇지 않으면 각도가 360보다 작습니다.

아이디어를 기본적으로 이해하려면이 이미지를 참조하십시오. 여기에 이미지 설명을 입력하십시오

내 알고리즘은 시계 방향이 양의 방향이라고 가정합니다. 잠재적 인 입력은 다음과 같습니다.

[[-122.402015, 48.225216], [-117.032049, 48.999931], [-116.919132, 45.995175], [-124.079107, 46.267259], [-124.717175, 48.377557], [-122.92315, 47.047963], [-122.402015, 48.225216]]

다음은 아이디어를 구현하는 파이썬 코드입니다.

def isInside(self, border, target):
degree = 0
for i in range(len(border) - 1):
    a = border[i]
    b = border[i + 1]

    # calculate distance of vector
    A = getDistance(a[0], a[1], b[0], b[1]);
    B = getDistance(target[0], target[1], a[0], a[1])
    C = getDistance(target[0], target[1], b[0], b[1])

    # calculate direction of vector
    ta_x = a[0] - target[0]
    ta_y = a[1] - target[1]
    tb_x = b[0] - target[0]
    tb_y = b[1] - target[1]

    cross = tb_y * ta_x - tb_x * ta_y
    clockwise = cross < 0

    # calculate sum of angles
    if(clockwise):
        degree = degree + math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))
    else:
        degree = degree - math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))

if(abs(round(degree) - 360) <= 3):
    return True
return False

21

에릭 헤인즈 기사 bobobobo 인용은 정말 우수합니다. 알고리즘의 성능을 비교하는 테이블이 특히 흥미 롭습니다. 각도 합산 방법은 다른 방법에 비해 실제로 나쁩니다. 또한 룩업 그리드를 사용하여 폴리곤을 "in"및 "out"섹터로 세분화하는 것과 같은 최적화는>면이 1000 개 이상인 폴리곤에서도 테스트를 매우 빠르게 수행 할 수 있습니다.

어쨌든, 초창기이지만 투표는 "교차"방법으로갑니다. 이것은 Mecki가 생각하는 것과 거의 같습니다. 그러나 나는 David Bourke가 가장 간결하게 묘사하고 체계화 한 것을 발견했다 . 나는 실제 삼각법이 필요하지 않다는 것을 좋아하고 볼록 및 오목에 적합하며 측면 수가 증가함에 따라 합리적으로 잘 수행됩니다.

그건 그렇고, 임의 다각형에 대한 테스트를 위해 Eric Haines의 기사에서 얻은 성능 표 중 하나입니다.

                       number of edges per polygon
                         3       4      10      100    1000
MacMartin               2.9     3.2     5.9     50.6    485
Crossings               3.1     3.4     6.8     60.0    624
Triangle Fan+edge sort  1.1     1.8     6.5     77.6    787
Triangle Fan            1.2     2.1     7.3     85.4    865
Barycentric             2.1     3.8    13.8    160.7   1665
Angle Summation        56.2    70.4   153.6   1403.8  14693

Grid (100x100)          1.5     1.5     1.6      2.1      9.8
Grid (20x20)            1.7     1.7     1.9      5.7     42.2
Bins (100)              1.8     1.9     2.7     15.1    117
Bins (20)               2.1     2.2     3.7     26.3    278

11

nirg답변 의 신속한 버전 :

extension CGPoint {
    func isInsidePolygon(vertices: [CGPoint]) -> Bool {
        guard !vertices.isEmpty else { return false }
        var j = vertices.last!, c = false
        for i in vertices {
            let a = (i.y > y) != (j.y > y)
            let b = (x < (j.x - i.x) * (y - i.y) / (j.y - i.y) + i.x)
            if a && b { c = !c }
            j = i
        }
        return c
    }
}

이것은 계산에있어서 0으로 나눌 수있는 잠재적 인 문제가있다. b. "a"에 대한 계산에 교차 가능성이있는 경우 "b"와 "c"로 다음 행만 계산하면됩니다. 두 지점이 모두 위에 있거나 두 지점이 모두 아래에있는 경우 가능성은 없습니다.
anorskdev

11

Nirg가 게시하고 bobobobo가 편집 한 솔루션과 정말 같습니다. 방금 자바 스크립트에 친숙하고 사용하기에 조금 더 읽기 쉽습니다.

function insidePoly(poly, pointx, pointy) {
    var i, j;
    var inside = false;
    for (i = 0, j = poly.length - 1; i < poly.length; j = i++) {
        if(((poly[i].y > pointy) != (poly[j].y > pointy)) && (pointx < (poly[j].x-poly[i].x) * (pointy-poly[i].y) / (poly[j].y-poly[i].y) + poly[i].x) ) inside = !inside;
    }
    return inside;
}

8

마이클 스톤 브레이커 (Michael Stonebraker) 의 연구원 인 Ingres , PostgreSQL 등을 연구 한 교수 였을 때이 작업에 대해 연구했습니다 .

우리는 가장 빠른 방법은 초고속이기 때문에 먼저 경계 상자를 만드는 것임을 깨달았습니다. 경계 상자 밖에 있으면 바깥에 있습니다. 그렇지 않으면, 당신은 더 열심히 일합니다 ...

훌륭한 알고리즘을 원한다면 지리 작업을위한 오픈 소스 프로젝트 PostgreSQL 소스 코드를 살펴보십시오.

우리는 오른쪽 대 왼손잡이에 대한 통찰력을 얻지 못했습니다 ( "내부"대 "외부"문제로 표현할 수 있음).


최신 정보

BKB의 링크는 적절한 수의 합리적인 알고리즘을 제공했습니다. 나는 지구 과학 문제를 연구하고 있었기 때문에 위도 / 경도로 작동하는 솔루션이 필요했고, 그것은 손의 특이한 문제가 있습니다-작은 영역이나 넓은 영역의 내부 영역입니까? 답은 정점의 "방향"이 중요하다는 것입니다. 왼손잡이 또는 오른 손잡이이며이 방법으로 어느 한 영역을 주어진 다각형의 "내부"로 표시 할 수 있습니다. 따라서 필자의 작업은 해당 페이지에 열거 된 솔루션 3을 사용했습니다.

또한 제 작업은 "온라인"테스트를 위해 별도의 기능을 사용했습니다.

... 누군가가 물었을 때 : 정점 수가 몇 개를 넘어 서면 경계 상자 테스트가 가장 좋다는 것을 알았습니다. 필요한 경우 더 긴 테스트를 수행하기 전에 매우 빠른 테스트를 수행하십시오 ... 경계 상자는 단순히 가장 큰 x, 가장 작은 x, 가장 큰 y 및 가장 작은 y를 모아서 상자의 네 점을 만들기 위해 ...

다음과 같은 사람들을위한 또 다른 팁 : 우리는 그리드 공간에서 더 정교하고 "조도 희미하게"계산 한 모든 것을 평면의 양의 지점에서 모두 수행 한 다음 "실제"경도 / 위도로 다시 투영하여 가능한 오류를 피했습니다. 경도의 선 180을 넘었을 때와 극지방을 다룰 때 잘 했어!


경계 상자가없는 경우 어떻게합니까? :)
Scott Evernden

8
경계 상자를 쉽게 만들 수 있습니다. 가장 큰 x와 가장 작은 x를 사용하는 4 개의 지점에 불과합니다. 더 빨리
Richard T

"... 한 줄의 경도 180을 넘을 때와 극지방을 다룰 때 줄 바꿈 오류를 피할 수 있습니다." 좀 더 자세히 설명해 주시겠습니까? ... 나는 내 다각형 교차 0 경도를 방지하기 위해 '최대'모두를 이동하는 방법을 알아낼 수 있다고 생각하지만, 나는 극 중 하나가 포함 폴리곤을 처리하는 방법에 대한 취소 아니에요
tiritea

6

David Segond의 대답은 표준 일반 답변과 거의 비슷하며 Richard T 's가 가장 일반적인 최적화이지만 다른 것들도 있습니다. 다른 강력한 최적화는 덜 일반적인 솔루션을 기반으로합니다. 예를 들어, 점이 많은 동일한 다각형을 검사하려는 경우 매우 빠른 TIN 검색 알고리즘이 많으므로 다각형을 삼각 측량하면 속도가 크게 향상 될 수 있습니다. 다른 하나는 다각형과 점이 저해상도의 제한된 평면에있는 경우입니다 (예 : 화면 표시). 다각형을 지정된 색상의 메모리 매핑 된 표시 버퍼에 칠하고 주어진 픽셀의 색상을 검사하여 거짓말이 있는지 확인할 수 있습니다 다각형에서.

많은 최적화와 마찬가지로 이들은 일반적인 경우가 아닌 특정 사례를 기반으로하며 단일 사용이 아닌 상각 시간을 기반으로 수익을 창출합니다.

이 분야에서 일하면서 Joeseph O'Rourkes 'Computation Geometry in C'ISBN 0-521-44034-3이 큰 도움이된다는 것을 알았습니다.


4

사소한 해결책은 다각형을 삼각형으로 나누고 여기에 설명 된대로 삼각형을 치는 것입니다.

다각형이 CONVEX 인 경우 더 나은 접근 방법이있을 수 있습니다. 다각형을 무한 선 모음으로 봅니다. 공간을 두 개로 나누는 각 줄. 모든 점에서 선의 한쪽 또는 다른쪽에 있는지 쉽게 말할 수 있습니다. 점이 모든 선의 같은쪽에 있으면 다각형 안에 있습니다.


매우 빠르며 더 일반적인 모양에 적용 할 수 있습니다. 1990 년경에, 우리는 일부면이 원호 인 "curvigons"를 가졌습니다. 이러한 측면을 원형 웨지와 웨지를 원점에 연결하는 한 쌍의 삼각형 (다각형 중심)으로 분석하면 적중 테스트가 빠르고 쉽습니다.
DarenW

1
이 curvigons에 대한 참조가 있습니까?
shoosh

나는 또한 curvigons에 대한 심판을 좋아할 것입니다.
Joel in Gö November

볼록 다각형의 경우 중요한 해결책을 놓쳤습니다. 점을 대각선과 비교하면 정점 수를 절반으로 줄일 수 있습니다. 그리고이 과정을 반복, 당신은 작업보다는 N. 로그 (N)에서 하나의 삼각형 감소
이브 다어 스트

4

나는 이것이 오래되었다는 것을 알고 있지만 여기에 누군가 관심이있는 경우 Cocoa로 구현 된 레이 캐스팅 알고리즘이 있습니다. 그것이 가장 효과적인 방법인지 확실하지 않지만 누군가를 도울 수 있습니다.

- (BOOL)shape:(NSBezierPath *)path containsPoint:(NSPoint)point
{
    NSBezierPath *currentPath = [path bezierPathByFlatteningPath];
    BOOL result;
    float aggregateX = 0; //I use these to calculate the centroid of the shape
    float aggregateY = 0;
    NSPoint firstPoint[1];
    [currentPath elementAtIndex:0 associatedPoints:firstPoint];
    float olderX = firstPoint[0].x;
    float olderY = firstPoint[0].y;
    NSPoint interPoint;
    int noOfIntersections = 0;

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];
        [currentPath elementAtIndex:n associatedPoints:points];
        aggregateX += points[0].x;
        aggregateY += points[0].y;
    }

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];

        [currentPath elementAtIndex:n associatedPoints:points];
        //line equations in Ax + By = C form
        float _A_FOO = (aggregateY/[currentPath elementCount]) - point.y;  
        float _B_FOO = point.x - (aggregateX/[currentPath elementCount]);
        float _C_FOO = (_A_FOO * point.x) + (_B_FOO * point.y);

        float _A_BAR = olderY - points[0].y;
        float _B_BAR = points[0].x - olderX;
        float _C_BAR = (_A_BAR * olderX) + (_B_BAR * olderY);

        float det = (_A_FOO * _B_BAR) - (_A_BAR * _B_FOO);
        if (det != 0) {
            //intersection points with the edges
            float xIntersectionPoint = ((_B_BAR * _C_FOO) - (_B_FOO * _C_BAR)) / det;
            float yIntersectionPoint = ((_A_FOO * _C_BAR) - (_A_BAR * _C_FOO)) / det;
            interPoint = NSMakePoint(xIntersectionPoint, yIntersectionPoint);
            if (olderX <= points[0].x) {
                //doesn't matter in which direction the ray goes, so I send it right-ward.
                if ((interPoint.x >= olderX && interPoint.x <= points[0].x) && (interPoint.x > point.x)) {  
                    noOfIntersections++;
                }
            } else {
                if ((interPoint.x >= points[0].x && interPoint.x <= olderX) && (interPoint.x > point.x)) {
                     noOfIntersections++;
                } 
            }
        }
        olderX = points[0].x;
        olderY = points[0].y;
    }
    if (noOfIntersections % 2 == 0) {
        result = FALSE;
    } else {
        result = TRUE;
    }
    return result;
}

5
Cocoa에서 실제로 수행하는 경우 [NSBezierPath containsPoint :] 메소드를 사용할 수 있습니다.
ThomasW

4

포인트 테스트를위한 샘플 방법이 포함 된 nirg 's answer의 Obj-C 버전. Nirg의 대답은 저에게 효과적이었습니다.

- (BOOL)isPointInPolygon:(NSArray *)vertices point:(CGPoint)test {
    NSUInteger nvert = [vertices count];
    NSInteger i, j, c = 0;
    CGPoint verti, vertj;

    for (i = 0, j = nvert-1; i < nvert; j = i++) {
        verti = [(NSValue *)[vertices objectAtIndex:i] CGPointValue];
        vertj = [(NSValue *)[vertices objectAtIndex:j] CGPointValue];
        if (( (verti.y > test.y) != (vertj.y > test.y) ) &&
        ( test.x < ( vertj.x - verti.x ) * ( test.y - verti.y ) / ( vertj.y - verti.y ) + verti.x) )
            c = !c;
    }

    return (c ? YES : NO);
}

- (void)testPoint {

    NSArray *polygonVertices = [NSArray arrayWithObjects:
        [NSValue valueWithCGPoint:CGPointMake(13.5, 41.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 56.5)],
        [NSValue valueWithCGPoint:CGPointMake(39.5, 69.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 84.5)],
        [NSValue valueWithCGPoint:CGPointMake(13.5, 100.0)],
        [NSValue valueWithCGPoint:CGPointMake(6.0, 70.5)],
        nil
    ];

    CGPoint tappedPoint = CGPointMake(23.0, 70.0);

    if ([self isPointInPolygon:polygonVertices point:tappedPoint]) {
        NSLog(@"YES");
    } else {
        NSLog(@"NO");
    }
}

샘플 다각형


2
물론 Objective-C에서는 CGPathContainsPoint()친구입니다.
Zev Eisenberg

@ ZevEisenberg하지만 너무 쉽습니다! 메모 주셔서 감사합니다. 이 프로젝트를 특정 시점에서 파헤쳐 서 왜 커스텀 솔루션을 사용했는지 살펴 보겠습니다. 나는 아마 몰랐다CGPathContainsPoint()
Jon

4

문제의 귀납적 정의보다 더 아름다운 것은 없습니다. 완전성을 위해 여기에 광선 주조 뒤에있는 생각을 분명히 할 수있는 프롤로그 버전이 있습니다. .

http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html 의 단순 알고리즘 시뮬레이션을 기반으로 함

일부 도우미 술어 :

exor(A,B):- \+A,B;A,\+B.
in_range(Coordinate,CA,CB) :- exor((CA>Coordinate),(CB>Coordinate)).

inside(false).
inside(_,[_|[]]).
inside(X:Y, [X1:Y1,X2:Y2|R]) :- in_range(Y,Y1,Y2), X > ( ((X2-X1)*(Y-Y1))/(Y2-Y1) +      X1),toggle_ray, inside(X:Y, [X2:Y2|R]); inside(X:Y, [X2:Y2|R]).

get_line(_,_,[]).
get_line([XA:YA,XB:YB],[X1:Y1,X2:Y2|R]):- [XA:YA,XB:YB]=[X1:Y1,X2:Y2]; get_line([XA:YA,XB:YB],[X2:Y2|R]).

2 점 A와 B (선 (A, B))가 주어진 선의 방정식은 다음과 같습니다.

                    (YB-YA)
           Y - YA = ------- * (X - XA) 
                    (XB-YB) 

선의 회전 방향은 경계의 경우 시계 방향으로, 구멍의 경우 시계 반대 방향으로 설정해야합니다. 우리는 점 (X, Y), 즉 테스트 된 점이 우리 선의 왼쪽 반 평면에 있는지 여부를 확인하려고합니다 (맛의 문제, 오른쪽 일 수도 있고 경계의 방향 일 수도 있습니다) 이 경우 선을 변경해야합니다), 이것은 점에서 오른쪽 (또는 왼쪽)으로 광선을 투사하고 선과의 교차점을 확인하는 것입니다. 우리는 광선을 수평 방향으로 투사하기로 선택했습니다 (다시 맛의 문제이며 비슷한 제한으로 수직으로 할 수도 있습니다).

               (XB-XA)
           X < ------- * (Y - YA) + XA
               (YB-YA) 

이제 점이 전체 평면이 아닌 선 세그먼트의 왼쪽 (또는 오른쪽)에 있는지 알아야하므로 검색을이 세그먼트로만 제한해야하지만 세그먼트 내부에 있기 때문에 쉬워집니다. 수직 축에서 선의 한 점만 Y보다 높을 수 있습니다. 이것이 더 강력한 제한이므로 먼저 확인해야하므로 먼저이 요구 사항을 충족하는 행만 취한 다음 위치를 확인하십시오. Jordan Curve 정리에 의해 다각형으로 투영 된 광선은 짝수 개의 선에서 교차해야합니다. 그래서 우리는 광선을 오른쪽으로 던질 때마다 선을 교차 할 때마다 상태를 토글합니다. 그러나 우리의 구현에서 우리는 주어진 제한을 충족시키는 솔루션 백의 길이를 확인하고 그에 대한 내성을 결정해야합니다. 다각형의 각 선에 대해이 작업을 수행해야합니다.

is_left_half_plane(_,[],[],_).
is_left_half_plane(X:Y,[XA:YA,XB:YB], [[X1:Y1,X2:Y2]|R], Test) :- [XA:YA, XB:YB] =  [X1:Y1, X2:Y2], call(Test, X , (((XB - XA) * (Y - YA)) / (YB - YA) + XA)); 
                                                        is_left_half_plane(X:Y, [XA:YA, XB:YB], R, Test).

in_y_range_at_poly(Y,[XA:YA,XB:YB],Polygon) :- get_line([XA:YA,XB:YB],Polygon),  in_range(Y,YA,YB).
all_in_range(Coordinate,Polygon,Lines) :- aggregate(bag(Line),    in_y_range_at_poly(Coordinate,Line,Polygon), Lines).

traverses_ray(X:Y, Lines, Count) :- aggregate(bag(Line), is_left_half_plane(X:Y, Line, Lines, <), IntersectingLines), length(IntersectingLines, Count).

% This is the entry point predicate
inside_poly(X:Y,Polygon,Answer) :- all_in_range(Y,Polygon,Lines), traverses_ray(X:Y, Lines, Count), (1 is mod(Count,2)->Answer=inside;Answer=outside).

3

nirg의 대답의 C # 버전은 다음과 같습니다. 코드를 공유하겠습니다. 누군가 시간을 절약 할 수 있습니다.

public static bool IsPointInPolygon(IList<Point> polygon, Point testPoint) {
            bool result = false;
            int j = polygon.Count() - 1;
            for (int i = 0; i < polygon.Count(); i++) {
                if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y) {
                    if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X) {
                        result = !result;
                    }
                }
                j = i;
            }
            return result;
        }

이것은 대부분의 경우에 작동하지만 잘못되어 항상 제대로 작동하지는 않습니다! M Katz의 솔루션을 사용하십시오
Lukas Hanacek

3

자바 버전 :

public class Geocode {
    private float latitude;
    private float longitude;

    public Geocode() {
    }

    public Geocode(float latitude, float longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
    }

    public float getLatitude() {
        return latitude;
    }

    public void setLatitude(float latitude) {
        this.latitude = latitude;
    }

    public float getLongitude() {
        return longitude;
    }

    public void setLongitude(float longitude) {
        this.longitude = longitude;
    }
}

public class GeoPolygon {
    private ArrayList<Geocode> points;

    public GeoPolygon() {
        this.points = new ArrayList<Geocode>();
    }

    public GeoPolygon(ArrayList<Geocode> points) {
        this.points = points;
    }

    public GeoPolygon add(Geocode geo) {
        points.add(geo);
        return this;
    }

    public boolean inside(Geocode geo) {
        int i, j;
        boolean c = false;
        for (i = 0, j = points.size() - 1; i < points.size(); j = i++) {
            if (((points.get(i).getLongitude() > geo.getLongitude()) != (points.get(j).getLongitude() > geo.getLongitude())) &&
                    (geo.getLatitude() < (points.get(j).getLatitude() - points.get(i).getLatitude()) * (geo.getLongitude() - points.get(i).getLongitude()) / (points.get(j).getLongitude() - points.get(i).getLongitude()) + points.get(i).getLatitude()))
                c = !c;
        }
        return c;
    }

}

2

.Net 포트 :

    static void Main(string[] args)
    {

        Console.Write("Hola");
        List<double> vertx = new List<double>();
        List<double> verty = new List<double>();

        int i, j, c = 0;

        vertx.Add(1);
        vertx.Add(2);
        vertx.Add(1);
        vertx.Add(4);
        vertx.Add(4);
        vertx.Add(1);

        verty.Add(1);
        verty.Add(2);
        verty.Add(4);
        verty.Add(4);
        verty.Add(1);
        verty.Add(1);

        int nvert = 6;  //Vértices del poligono

        double testx = 2;
        double testy = 5;


        for (i = 0, j = nvert - 1; i < nvert; j = i++)
        {
            if (((verty[i] > testy) != (verty[j] > testy)) &&
             (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
                c = 1;
        }
    }

2

VBA 버전 :

참고 : 다각형이지도 내의 영역 인 경우 위도 / 경도가 X / Y (위도 = Y, 경도 = X)와 반대로 Y / X 값인 경우 기억하십시오. 경도는 측정이 아닙니다.

클래스 모듈 : CPoint

Private pXValue As Double
Private pYValue As Double

'''''X Value Property'''''

Public Property Get X() As Double
    X = pXValue
End Property

Public Property Let X(Value As Double)
    pXValue = Value
End Property

'''''Y Value Property'''''

Public Property Get Y() As Double
    Y = pYValue
End Property

Public Property Let Y(Value As Double)
    pYValue = Value
End Property

구성 단위:

Public Function isPointInPolygon(p As CPoint, polygon() As CPoint) As Boolean

    Dim i As Integer
    Dim j As Integer
    Dim q As Object
    Dim minX As Double
    Dim maxX As Double
    Dim minY As Double
    Dim maxY As Double
    minX = polygon(0).X
    maxX = polygon(0).X
    minY = polygon(0).Y
    maxY = polygon(0).Y

    For i = 1 To UBound(polygon)
        Set q = polygon(i)
        minX = vbMin(q.X, minX)
        maxX = vbMax(q.X, maxX)
        minY = vbMin(q.Y, minY)
        maxY = vbMax(q.Y, maxY)
    Next i

    If p.X < minX Or p.X > maxX Or p.Y < minY Or p.Y > maxY Then
        isPointInPolygon = False
        Exit Function
    End If


    ' SOURCE: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

    isPointInPolygon = False
    i = 0
    j = UBound(polygon)

    Do While i < UBound(polygon) + 1
        If (polygon(i).Y > p.Y) Then
            If (polygon(j).Y < p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        ElseIf (polygon(i).Y < p.Y) Then
            If (polygon(j).Y > p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        End If
        j = i
        i = i + 1
    Loop   
End Function

Function vbMax(n1, n2) As Double
    vbMax = IIf(n1 > n2, n1, n2)
End Function

Function vbMin(n1, n2) As Double
    vbMin = IIf(n1 > n2, n2, n1)
End Function


Sub TestPointInPolygon()

    Dim i As Integer
    Dim InPolygon As Boolean

'   MARKER Object
    Dim p As CPoint
    Set p = New CPoint
    p.X = <ENTER X VALUE HERE>
    p.Y = <ENTER Y VALUE HERE>

'   POLYGON OBJECT
    Dim polygon() As CPoint
    ReDim polygon(<ENTER VALUE HERE>) 'Amount of vertices in polygon - 1
    For i = 0 To <ENTER VALUE HERE> 'Same value as above
       Set polygon(i) = New CPoint
       polygon(i).X = <ASSIGN X VALUE HERE> 'Source a list of values that can be looped through
       polgyon(i).Y = <ASSIGN Y VALUE HERE> 'Source a list of values that can be looped through
    Next i

    InPolygon = isPointInPolygon(p, polygon)
    MsgBox InPolygon

End Sub

2

nirg의 C ++ 코드를 Python으로 구현했습니다 .

입력

  • bounding_points : 다각형을 구성하는 노드.
  • bounding_box_positions : 후보 지점을 필터링합니다. (내 구현에서 경계 상자에서 생성되었습니다.

    (입력은 다음 형식의 튜플 목록입니다. [(xcord, ycord), ...])

보고

  • 다각형 안에있는 모든 점.
def polygon_ray_casting(self, bounding_points, bounding_box_positions):
    # Arrays containing the x- and y-coordinates of the polygon's vertices.
    vertx = [point[0] for point in bounding_points]
    verty = [point[1] for point in bounding_points]
    # Number of vertices in the polygon
    nvert = len(bounding_points)
    # Points that are inside
    points_inside = []

    # For every candidate position within the bounding box
    for idx, pos in enumerate(bounding_box_positions):
        testx, testy = (pos[0], pos[1])
        c = 0
        for i in range(0, nvert):
            j = i - 1 if i != 0 else nvert - 1
            if( ((verty[i] > testy ) != (verty[j] > testy))   and
                    (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]) ):
                c += 1
        # If odd, that means that we are inside the polygon
        if c % 2 == 1: 
            points_inside.append(pos)


    return points_inside

다시, 아이디어는 여기 에서 가져옵니다


2

놀랍게도 아무도 이것을 일찍 제기하지는 않았지만 데이터베이스를 필요로하는 실용 주의자들에게는 MongoDB가 이것을 포함하여 지리 쿼리를 훌륭하게 지원합니다.

당신이 찾고있는 것은 :

db.neighborhoods.findOne ({geometry : {$ geoIntersects : {$ geometry : {유형 : "Point", 좌표 : [ "longitude", "latitude"]}}}})

Neighborhoods표준 GeoJson 형식으로 하나 이상의 다각형을 저장하는 모음입니다. 쿼리가 null을 반환하면 교차되지 않습니다.

https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/에 잘 설명되어 있습니다.

330 개의 불규칙 다각형 그리드로 분류 된 6,000 개 이상의 포인트에 대한 성능은 전혀 최적화되지 않았으며 각각의 다각형으로 문서를 업데이트하는 시간을 포함하여 1 분 미만이었습니다.


1

광선 캐스팅을 사용하지 않는 C의 다각형 테스트 포인트가 있습니다. 그리고 그것은 겹치는 영역 (자체 교차점)에 효과가 있습니다 use_holes.

/* math lib (defined below) */
static float dot_v2v2(const float a[2], const float b[2]);
static float angle_signed_v2v2(const float v1[2], const float v2[2]);
static void copy_v2_v2(float r[2], const float a[2]);

/* intersection function */
bool isect_point_poly_v2(const float pt[2], const float verts[][2], const unsigned int nr,
                         const bool use_holes)
{
    /* we do the angle rule, define that all added angles should be about zero or (2 * PI) */
    float angletot = 0.0;
    float fp1[2], fp2[2];
    unsigned int i;
    const float *p1, *p2;

    p1 = verts[nr - 1];

    /* first vector */
    fp1[0] = p1[0] - pt[0];
    fp1[1] = p1[1] - pt[1];

    for (i = 0; i < nr; i++) {
        p2 = verts[i];

        /* second vector */
        fp2[0] = p2[0] - pt[0];
        fp2[1] = p2[1] - pt[1];

        /* dot and angle and cross */
        angletot += angle_signed_v2v2(fp1, fp2);

        /* circulate */
        copy_v2_v2(fp1, fp2);
        p1 = p2;
    }

    angletot = fabsf(angletot);
    if (use_holes) {
        const float nested = floorf((angletot / (float)(M_PI * 2.0)) + 0.00001f);
        angletot -= nested * (float)(M_PI * 2.0);
        return (angletot > 4.0f) != ((int)nested % 2);
    }
    else {
        return (angletot > 4.0f);
    }
}

/* math lib */

static float dot_v2v2(const float a[2], const float b[2])
{
    return a[0] * b[0] + a[1] * b[1];
}

static float angle_signed_v2v2(const float v1[2], const float v2[2])
{
    const float perp_dot = (v1[1] * v2[0]) - (v1[0] * v2[1]);
    return atan2f(perp_dot, dot_v2v2(v1, v2));
}

static void copy_v2_v2(float r[2], const float a[2])
{
    r[0] = a[0];
    r[1] = a[1];
}

참고 : 이것은에 대한 많은 호출을 포함하기 때문에 덜 최적의 방법 중 하나 atan2f이지만,이 스레드를 읽는 개발자에게는 관심이있을 수 있습니다 (제 테스트에서는 ~ 23x 느리고 라인 교차 방법 사용).


0

Polygon에서 적중을 탐지하려면 다음 두 가지를 테스트해야합니다.

  1. 점이 다각형 영역 안에있는 경우 (Ray-Casting Algorithm으로 달성 가능)
  2. 점이 다각형 경계에있는 경우 (폴리 라인 (선)의 점 감지에 사용되는 동일한 알고리즘으로 수행 할 수 있음).

0

레이 캐스팅 알고리즘 에서 다음과 같은 특수한 경우를 처리하려면 다음을 수행하십시오 .

  1. 광선은 다각형면 중 하나와 겹칩니다.
  2. 점은 다각형 내부에 있으며 광선은 다각형의 정점을 통과합니다.
  3. 점은 다각형 외부에 있으며 광선은 다각형 각도 중 하나에 닿습니다.

점이 복잡한 다각형 안에 있는지 확인 합니다. 이 기사는 이러한 문제를 해결하는 쉬운 방법을 제공하므로 위의 경우 특별한 처리가 필요하지 않습니다.


0

원하는 점을 다각형의 꼭짓점에 연결하여 형성된 영역이 다각형 자체의 영역과 일치하는지 확인하면됩니다.

또는 점에서 두 개의 연속 다각형 정점의 각 쌍까지의 내부 각도의 합이 검사 점의 합인지 360으로 확인할 수 있지만 나누기 나 계산이 포함되지 않으므로 첫 번째 옵션이 더 빠릅니다. 삼각 함수의 역수.

다각형에 구멍이 있으면 어떻게되는지 모르지만 주요 아이디어는이 상황에 적응할 수 있습니다.

수학 커뮤니티에 질문을 게시 할 수도 있습니다. 나는 그들이 백만 가지 방법을 가지고 내기


0

자바 스크립트 라이브러리를 찾고 있다면 Polygon 클래스에 대한 자바 스크립트 google maps v3 확장 기능이 포인트가 그 안에 있는지 여부를 감지합니다.

var polygon = new google.maps.Polygon([], "#000000", 1, 1, "#336699", 0.3);
var isWithinPolygon = polygon.containsLatLng(40, -90);

구글 익스텐션 Github



0

대답은 단순하거나 복잡한 다각형이 있는지 여부에 따라 다릅니다. 단순 다각형에는 선분 교차점이 없어야합니다. 따라서 구멍이 생길 수 있지만 선은 서로 교차 할 수 없습니다. 복잡한 영역에는 선 교차점이있을 수 있으므로 겹치는 영역 또는 단일 포인트만으로 서로 닿는 영역이있을 수 있습니다.

단순한 다각형의 경우 가장 좋은 알고리즘은 Ray casting (Crossing number) 알고리즘입니다. 복잡한 다각형의 경우이 알고리즘은 겹치는 영역 안에있는 점을 감지하지 않습니다. 복잡한 다각형의 경우 권선 수 알고리즘을 사용해야합니다.

다음은 두 알고리즘의 C 구현에 대한 훌륭한 기사입니다. 나는 그들을 시도하고 그들은 잘 작동합니다.

http://geomalgorithms.com/a03-_inclusion.html


0

nirg에 의한 솔루션의 스칼라 버전 (경계 사각형 사전 검사가 별도로 수행되었다고 가정) :

def inside(p: Point, polygon: Array[Point], bounds: Bounds): Boolean = {

  val length = polygon.length

  @tailrec
  def oddIntersections(i: Int, j: Int, tracker: Boolean): Boolean = {
    if (i == length)
      tracker
    else {
      val intersects = (polygon(i).y > p.y) != (polygon(j).y > p.y) && p.x < (polygon(j).x - polygon(i).x) * (p.y - polygon(i).y) / (polygon(j).y - polygon(i).y) + polygon(i).x
      oddIntersections(i + 1, i, if (intersects) !tracker else tracker)
    }
  }

  oddIntersections(0, length - 1, tracker = false)
}

0

@nirg answer의 golang 버전은 다음과 같습니다 (@@ m-katz의 C # 코드에서 영감을 얻음)

func isPointInPolygon(polygon []point, testp point) bool {
    minX := polygon[0].X
    maxX := polygon[0].X
    minY := polygon[0].Y
    maxY := polygon[0].Y

    for _, p := range polygon {
        minX = min(p.X, minX)
        maxX = max(p.X, maxX)
        minY = min(p.Y, minY)
        maxY = max(p.Y, maxY)
    }

    if testp.X < minX || testp.X > maxX || testp.Y < minY || testp.Y > maxY {
        return false
    }

    inside := false
    j := len(polygon) - 1
    for i := 0; i < len(polygon); i++ {
        if (polygon[i].Y > testp.Y) != (polygon[j].Y > testp.Y) && testp.X < (polygon[j].X-polygon[i].X)*(testp.Y-polygon[i].Y)/(polygon[j].Y-polygon[i].Y)+polygon[i].X {
            inside = !inside
        }
        j = i
    }

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