문제

나를 만들려고 fast 2D 포인트 내부에 다각형 알고리즘에 사용하기 위해,히트 테스트(예: Polygon.contains(p:Point)).제안에 대한 효과적인 기술 감사하겠습니다.

도움이 되었습니까?

해결책

에 대한 그래픽,오히려하지 않아보세요 정수입니다.많은 시스템을 사용하여 정수에 대한 UI 그림이(픽셀은 정 모든 후)지만,macOS 예를 들어 사용하는 부유물에 대한 모든 것입니다.macOS 는 점점 번역할 수 있는 하나의 픽셀 하지만,모니터 해상도에 따라,그 수 있는 번역하는 다른 뭔가가 있습니다.망막 화면에 반점(0.5/0.5)은 픽셀.여전히,나는 결코 주는 macOS UIs 은 크게 느린 것보다 다른 Ui.후에는 모든 3D Api(OpenGL 또는 Direct3D)도와 수레와 현대적인 그래픽 라이브러리는 아주 종종 활용 GPU 가속도.

지금 말씀하신 속도는 당신의 주요 관심사,이제에 대한 속도이다.를 실행하기 전에 정교한 알고리즘,첫 번째 간단한 시험입니다.Create an 축 정렬 경계 상자 주변의 다각형.이것은 매우 쉽고,빠르고 안전 이미 당신이 계산이 많습니다.도대체 이게 어떻게 된 것일까요?반복의 모든 지점이 다각형을 찾기 위 min/max values X 와 Y

E.g.당신은 포인트 (9/1), (4/3), (2/7), (8/2), (3/6).이 Xmin2,Xmax9,Ymin1 및 표준을 사용하여 인터넷이 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!
}

이것은 첫 번째 시험을 위해 실행하는 어떤 점이다.당신이 볼 수 있듯이,이 시험은 매우 빠르게 하지만 그것은 또한 매우 거친.에 포인트를 처리하는 경계 사각형 내에서,우리가 필요한 더 정교한 알고리즘이 있습니다.거기에 몇 가지 방법이 어떻게 이것을 계산할 수 있습니다.는 방법도 사실에 따라 달라집하는 경우 폴리곤할 수 있는 구멍이 있는 것입니다 항상 고체.의 예는 다음과 같습니다 단단한 사람(하나 볼록,하나 오목):

Polygon without hole

그리고 여기에 중 하나 구멍:

Polygon with hole

녹색 하고 중간에 구멍이!

가장 쉬운 알고리즘을 처리할 수 있는 세 가지 경우 모두를 위고는 여전히 아주 빠른 이름 레이 캐스팅.의 아이디어 알고리즘은 매우 간단하다:그리 가상 레이 어디에서 외부의 다각형을 자신의 강점과 약점을 가 어떻게 계산은 종종 그것은 안타의 측면을 다각형.면 히트 수도,그 밖의 폴리곤 경우,그것은 이상한,그것은 내부입니다.

Demonstrating how the ray cuts through a polygon

감 번호를 알고리즘 대안이 될는 것이 더 정확한 포인트는 가까운 매우 다각형을 줄지만 그것은 또한 매우 느립니다.레이 캐스팅에 실패할 수 있습에 대한 포인트에 너무 가까이 다각형 면 때문에 제한된 부동 소수점 정밀도와 반올림하 문제이지만,현실은 거의 문제가 같을 경우에는 가까운 쪽을,그것은 종종 시각적으로도 가능 뷰어를 인식하면 그것은 이미 안에는 여전히 외부.

당신은 여전히 경계 상자의 상기,기억하십니까?그냥 지점 경계 상자 외부로 사용을 위한 시작점이다.E.g.점 (Xmin - e/p.y) 외부에 다각형에 대한 확실합니다.

하지만 무엇인 e?론, e (실제로는 엡실론)게 경계 상자 일 패딩.내가 말했듯이,ray tracing 실패하는 경우 우리가 시작에 너무 가까이 다각형 라인입니다.이 경계 상자 것과 같을 수 있습각형을(있을 경우 폴리곤은 축 맞춰 사각형,경계 상자가 동일한 다각형 자체를!), 우리는 일부를 필요로 패딩이 안전이다.얼마나 큰 당신이 선택 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)
:

당신을 테스트해야 ray 에 대한 모든 측면입니다.을 고려한 레이 될 수 있는 벡터와 모든 측면을 것입니다.레이 있을 정확히 측면 또는 모두에서 결코.그것은 공격 할 수 없습니다 같은 측면에 두 번.두 줄을 2D 에서 공간은 항상 intersect 정확하게 번지 않는 한 그들이 평행한 경우에는 그들은 결코 그들이 교차한다.그러나 그 이후 벡터 제한된 길이,두 벡터되지 않을 수도 병행하고 여전히 교차되지 않기 때문에 그들은 너무 짧은 만난다.

// 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(v1x1/v1y1v1x2/v1y2 다)및 벡터 2(v2x1/v2y1v2x2/v2y2).그래서 당신은 2 벡터,4 점 8 좌표입니다. YESNO 은 명확하다. YES 증가 교차로, NO 은 아무것도 하지 않습니다.

에 대해 무엇이 공?그것은 모두 의미 벡터들 속에서 동일한 무한 라인에 따라 위치하고 길이,그들은하지 않에서 교차하는 모든이나 그들이 교차하에 끝없는 수의 포인트입니다.나는 절대적으로 확실하지 않을 처리하는 방법,이 경우 나리로 교차하고 있습니다.만,이 경우 오히려 희귀에서 연습기 때문에 어쨌든의 부동 소수점에서 반올림 오류를;더 코드는 아마에 대한 테스트 == 0.0f 그러나 대신에 대한 뭔가 < epsilon, 는 곳,엡실론은 오히려 작은 수입니다.

필요하신 경우에는 시험의 큰 숫자를 지점,당신은 확실히 수의 속도 모든 것은 비트에 의해 유선형 방정식의 표준 형태의 다각형 면에서 메모리,그래서 당신은 없을 계산한다.이 두 가지 부동 소수점 재미와 세 개의 부동 소수점에서 빼 모든 테스트에서 교환 저장을 위한 세 가지 부동 소수점의 값에 따라 다각형 측 메모리에 있습니다.그것은 일반적인 메모리 vs 계산 시간습니다.

마지막으로:는 경우 사용할 수 있습니다 3D 하드웨어,문제를 해결하기 위해 있는 것은 흥미로운 대안입니다.그냥 GPU 할 모든 당신을 위해 작업을 수행합니다.을 만들 그림 표면 화면.그것을 채우기와 완전히 색은 검정입니다.지금 OpenGL 또는 Direct3D 페인트 다각형(또는 귀하의 모든 다각형을 경우에 당신을 테스트하려면 지점에서의 그들을,하지만 당신은 걱정하지 않는 한)고 채우기 위해 다각형(s)다른 색으로,예를 들어,백색이다.을 확인하는 경우 시점에서 다각형,색상을 얻을의 이점에서 그리기 화면.이것은 그냥 O(1)메모리 데이터베이스를 지원합니다.

물론 이 방법은 가능한 경우 그리기 표면에 없을 것이다.할 수 없는 경우에는 맞으로드 메모리,이 방법은 보다 느리게 하고 그것에 CPU.는 경우에 있어야 합니다 거대하고 귀하의 GPU 를 지원하는 현대적인 셰이더할 수 있습니다 GPU 를 사용하여 구현하여 레이 캐스팅 다음과 같은 위로 GPU 셰이더,이 절대적으로 가능합니다.에 대한 더 많은 수의 다각형 또는 많은 수의 포인트를 테스트,이를 갚을 것을 고려,일부 Gpu 을 테스트 할 수 있 64 256 점에서 병행합니다.그러나 전송 데이터를 CPU GPU and 은 비싸다,그래서에 대한 테스트의 몇 가지 포인트에 대한 몇 가지 간단한 다각형을,어디 하나의 포인트 또는 다각형은 역동적이고 자주 변경,GPU 접근이 거의 돈을 지불합니다.

다른 팁

다음 코드가 가장 좋은 솔루션이라고 생각합니다 ( 여기):

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은 홀수를 의미합니다.

다음은 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;
    }

    // http://www.ecse.rpi.edu/Homepages/wrf/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;
}

다음은 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;
}

지점 P와 각 다각형 정점 사이의 각도의 정상을 계산합니다. 총 방향 각도가 360 도인 경우 포인트가 안에 있습니다. 총계가 0이면 포인트는 외부입니다.

이 방법은 수치 정밀도에 더 강력하고 의존적이지 않기 때문에이 방법을 더 좋아합니다.

교차로 수를 계산하는 동안 정점을 '적중'할 수 있기 때문에 교차로 수의 균등성을 계산하는 방법은 제한됩니다.

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

편집 : 최근에 전체를 찾았습니다 위키 백과 기사 주제에.

그만큼 에릭 하인즈 기사 Bobobobo가 인용하는 것은 정말 훌륭합니다. 특히 흥미로운 테이블은 알고리즘의 성능을 비교하는 것입니다. 각도 요약 방법은 다른 것들에 비해 정말 나쁘다. 또한 조회 그리드를 사용하여 다각형을 "in"및 "out"섹터로 세분화하는 것과 같은 최적화는> 1000면의 다각형에서도 테스트를 엄청나게 빠르게 만들 수 있다는 것입니다.

어쨌든, 그것은 초기이지만 내 투표는 "Crossings"방법으로갑니다. 이것은 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

이 질문은 너무 흥미 롭습니다. 이 게시물의 다른 답변과 다른 실행 가능한 아이디어가 있습니다.

아이디어는 각도의 합을 사용하여 대상이 내부 또는 외부인지 여부를 결정하는 것입니다. 대상이 영역 내부에 있으면 대상의 각도 형태의 합은 360이됩니다. 대상이 외부에 있으면 합은 360이되지 않습니다. 각도에는 방향이 있습니다. 각도가 뒤로 이동하면 각도는 음수입니다. 이것은 계산과 같습니다 와인딩 번호.

아이디어에 대한 기본적인 이해를 얻으려면이 이미지를 참조하십시오.enter image description here

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

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

신속한 버전 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
    }
}

내가 연구원이었을 때 나는 이것에 대해 약간의 일을했다. 마이클 스톤 브레이커 - 알다시피, 잉그레스, PostgreSQL, 등.

우리는 가장 빠른 방법이 먼저 빠르기 때문에 먼저 경계 상자를하는 것이라는 것을 깨달았습니다. 경계 상자 밖에 있다면 외부에 있습니다. 그렇지 않으면 더 어려운 일을합니다 ...

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

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


업데이트

BKB의 링크는 많은 합리적인 알고리즘을 제공했습니다. 나는 지구 과학 문제에 대해 노력하고 있었기 때문에 위도/경도에서 작동하는 해결책이 필요했고, 그것은 손의 독특한 문제가 있습니다 - 더 작은 지역 또는 더 큰 지역의 영역입니까? 대답은 Verticies의 "방향"이 중요하다는 것입니다. 그것은 왼손잡이 또는 오른손이며, 이런 식으로 당신은 어느 부분을 주어진 다각형으로 "내부"로 표시 할 수 있습니다. 따라서 내 작업은 솔루션을 사용하여 해당 페이지에 열거했습니다.

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

... 누군가가 물었 기 때문에 : 우리는 Verticies의 수가 약간 넘어 질 때 경계 박스 테스트가 가장 좋았다는 것을 알아 냈습니다. 필요한 경우 더 긴 테스트를 수행하기 전에 매우 빠른 테스트를 수행합니다. 가장 큰 X, 가장 작은 x, 가장 큰 y 및 가장 작은 y를 모아 4 점의 상자를 만들기 위해 모여 ...

다음 사람들을위한 또 다른 팁 : 우리는 평면의 긍정적 인 지점으로 그리드 공간에서 더 정교하고 "조명을 내기"컴퓨팅을 모두 수행 한 다음 "실제"경도/위도로 다시 진영을 수행하여 가능한 오류를 피했습니다. 경도 180 줄과 극지 지역을 처리 할 때 마무리. 잘 작동했습니다!

NIRG가 게시하고 Bobobobo가 편집 한 솔루션을 정말 좋아합니다. 방금 JavaScript 친화적이고 사용하기에 조금 더 읽기 쉬웠습니다.

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

David Segond의 답변은 표준 일반적인 답변이며 Richard T 's는 가장 일반적인 최적화이지만 Therre는 다른 사람들입니다. 다른 강력한 최적화는 덜 일반적인 솔루션을 기반으로합니다. 예를 들어, 많은 포인트가있는 동일한 다각형을 확인하려면 다각형을 삼각화하면 매우 빠른 주석 검색 알고리즘이 있기 때문에 다각형의 속도를 높일 수 있습니다. 다른 하나는 다각형과 점이 저해상도로 제한된 평면에있는 경우, 화면 디스플레이를 말하면, 다각형을 메모리 매핑 디스플레이 버퍼에 페인트하여 주어진 픽셀의 색상을 확인하여 거짓말이 있는지 확인할 수 있습니다. 다각형에서.

많은 최적화와 마찬가지로, 이들은 일반적인 경우보다는 구체적으로 기반으로하며 단일 사용이 아닌 상각 시간에 기초한 수익률을 얻습니다.

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

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

당신의 다각형이라면 볼록한 그래도 더 나은 접근 방식이있을 수 있습니다. 다각형을 무한 선의 모음으로보십시오. 각 선은 공간을 2로 나눕니다. 모든 지점에서 라인의 한쪽 또는 다른쪽에 있는지 쉽게 말하기 쉽습니다. 포인트가 모든 라인의 동일한쪽에 있으면 다각형 안에 있습니다.

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

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

테스트 포인트를위한 샘플 방법을 사용한 NIRG의 답변의 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");
    }
}

sample polygon

C# Nirg의 답변의 대답은 여기에 있습니다. 코드를 공유하겠습니다. 누군가가 시간을 절약 할 수 있습니다.

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

문제에 대한 귀납적 정의보다 더 아름다운 것은 없습니다. 완전성을 위해 여기에 당신은 Prolog에 버전이 있습니다. 레이 캐스팅:

단순성 알고리즘의 시뮬레이션을 기반으로합니다 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보다 높을 수 있습니다. 이것은 더 강력한 제한이기 때문에 가장 먼저 확인해야하므로 먼저이 요건을 충족 한 라인 만 가져간 다음 해당 가능성을 확인합니다. 요르단 곡선 정리에 의해 다각형에 투사 된 모든 광선은 짝수의 줄에 교차해야합니다. 그래서 우리는 완료됩니다. 우리는 광선을 오른쪽으로 던지고 매번 줄을 교차하고 상태를 전환 할 것입니다. 그러나 우리의 구현에서 우리는 주어진 제한을 충족시키는 솔루션 백의 렌트를 확인하고 이에 대한 여분을 결정해야합니다. 다각형의 각 선에 대해이를 수행해야합니다.

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

자바 버전 :

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

}

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

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

나는 Python 구현을 만들었습니다 Nirg 's C ++ 암호:

입력

  • 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

다시, 아이디어는 가져 왔습니다 여기

그녀는 Ray-Casting을 사용하지 않는 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, 그러나이 스레드를 읽는 개발자에게는 관심이있을 수 있습니다 (내 테스트에서는 라인 교차 방법을 사용하여 ~ 23 배 느린 속도가 느려집니다).

다각형의 히트를 감지하려면 두 가지를 테스트해야합니다.

  1. 포인트가 다각형 영역 내부에있는 경우. (Ray-Casting 알고리즘으로 달성 할 수 있습니다)
  2. 포인트가 다각형 테두리에있는 경우 (폴리 라인 (line)에서 포인트 감지에 사용되는 동일한 알고리즘에 의해 달성 될 수 있음).

다음과 같은 특별 사례를 처리합니다 레이 캐스팅 알고리즘:

  1. 광선은 다각형의 쪽 중 하나를 겹칩니다.
  2. 요점은 다각형 안에 있고 광선은 다각형의 정점을 통과합니다.
  3. 요점은 다각형 외부에 있으며 광선은 다각형의 각도 중 하나에 만집니다.

확인하다 포인트가 복잡한 다각형 내부에 있는지 여부를 결정합니다. 이 기사는 쉽게 해결할 수있는 방법을 제공하므로 위의 경우에는 특별한 대우가 필요하지 않습니다.

원하는 지점을 다각형의 정점에 연결하여 형성된 영역이 다각형 자체의 영역과 일치하는지 확인하여이를 수행 할 수 있습니다.

또는 지점에서 두 개의 연속 다각형 정점의 각 쌍의 체크 포인트 합계에 이르기까지 내부 각도의 합이 360으로 확인 될 수 있지만, 첫 번째 옵션이 부서 나 계산이 포함되지 않기 때문에 더 빠른 느낌이 있습니다. 삼각 함수의 역전.

당신의 다각형이 그 안에 구멍이 있다면 어떻게되는지 모르겠지만 주요 아이디어 가이 상황에 적응할 수있는 것 같습니다.

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

Java-Script 라이브러리를 찾고 있다면 다각형 클래스에 대한 JavaScript Google Maps v3 확장자가 있습니다.

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

Google Extention Github

사용할 때 (QT 4.3+), QPolygon의 기능을 사용할 수 있습니다. 함유 점

답은 단순하거나 복잡한 다각형이 있는지에 따라 다릅니다. 간단한 다각형에는 라인 세그먼트 교차점이 없어야합니다. 그래서 그들은 구멍을 가질 수 있지만 선은 서로 교차 할 수 없습니다. 복잡한 영역은 라인 교차로를 가질 수 있으므로 겹치는 영역이나 단일 지점만으로 서로 닿는 영역을 가질 수 있습니다.

간단한 다각형의 경우 최고의 알고리즘은 Ray Casting (Crossing Number) 알고리즘입니다. 복잡한 다각형의 경우이 알고리즘은 겹치는 영역 내부에있는 점을 감지하지 않습니다. 따라서 복잡한 다각형의 경우 와인딩 번호 알고리즘을 사용해야합니다.

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

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

NIRG의 Scala 버전 솔루션 (경계 사각형 사전 확인이 별도로 수행된다고 가정) : :

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

다음은 @nirg 답변의 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
}

아무도 이것을 일찍 제기하지 않았지만, 데이터베이스를 요구하는 실용 주의자들에게는 다음과 같습니다. MongoDB는이 쿼리를 포함하여 지오 쿼리를 탁월한 지원을합니다.

당신이 찾고있는 것은 다음과 같습니다.

db.neighborhoods.findone ({Geometry : {$ geointersects : {$ geometry : {type : "point", 좌표 : [ "longitude", "latitude"}}}})).

Neighborhoods 하나 이상의 다각형을 표준 Geojson 형식으로 저장하는 컬렉션입니다. 쿼리가 NULL을 반환하면 교차하지 않으면 그렇지 않습니다.

여기에 매우 잘 문서화되어 있습니다.https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/

330 불규칙한 다각형 그리드로 분류 된 6,000 점 이상의 성능은 1 분 미만이었고 최적화가 전혀없고 각 다각형으로 문서를 업데이트 할 시간을 포함하여 1 분 미만이었습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top