IDL/Programming

포인트들의 다각형 내부/외부 판별 (Points inside Polygon)

이상우_idl 2019. 10. 11. 16:48
728x90
반응형

오늘은 2차원 평면상에 특정한 형태의 다각형(Polygon)이 존재하는 상태에서 특정한 좌표를 갖는 포인트(Point)가 이 다각형의 내부에 있는가 또는 외부에 있는가를 판별하는 방법에 관하여 알아보도록 하겠습니다. 이와 같이 어떤 포인트의 위치가 특정한 다각형 영역의 내부 또는 외부에 있는가를 판별하는 것은 데이터 처리에서 꽤 중요한 작업일 수도 있습니다. 예를 들어, 어떤 영역(행정구역, 국경선 등)의 경계선에 해당되는 데이터가 있을 때 이를 이용하여 영역 다각형을 정의하고, 그 상태에서 특정한 지점들에 대한 좌표 데이터가 있으면 각 지점의 위치가 영역의 내부 또는 외부인가를 판별하는 것이 가능하기 때문입니다.


IDL에서는 이러한 작업을 할 수 있는 방법이 두가지 정도가 있습니다. 첫번째 방법은 IDL에 기본적으로 탑재되어있지 않은 외부 라이브러리의 프로그램을 이용하는 방법인데, 바로 Coyote 라이브러리에 있는 INSIDE 함수를 사용하는 것입니다. 그리고 두번째 방법은 IDL에 기본적으로 탑재되어 있는 IDLanROI 클래스의 객체를 활용하는 것입니다. 오늘은 두가지 방법을 모두 소개하도록 하겠습니다. 먼저 INSIDE 함수를 활용하는 방법부터 보겠습니다. 만약 Coyote 라이브러리를 이미 사용하고 있을 경우에는 INSIDE 함수를 IDL에서 바로 사용할 수 있습니다. 그리고 Coyote 라이브러리를 사용하지 않은 경우에는 INSIDE 함수의 소스 프로그램을 따로 받아야 합니다. 만약 Coyote 라이브러리 전체를 받지 않고 그냥 INSIDE 코드 파일만 받고자 할 경우에는 아래 링크를 통해서 받을 수 있습니다.


링크 누르기


이 링크를 통하여 insde.pro 파일을 받아서 IDL의 경로(Path)로 지정된 폴더에 저장해두고 사용하면 됩니다. 그러면 본격적으로 사용 방법을 알아보겠습니다. 이를 위하여 X축 방향으로 0~10, Y축 방향으로 0~10의 범위를 갖는 2차원 공간상에 반경이 4인 원이 존재한다고 가정하였습니다. 원의 중심점 좌표는 (4.3, 5.2)입니다. 이러한 원을 정의하고 그림으로 표출하는 과정은 다음과 같습니다.


theta = [0:360:10.]

xpts = COS(theta*!dtor)*4+4.5

ypts = SIN(theta*!dtor)*4+5.2

win = WINDOW(DIMENSIONS=[600, 600], /NO_TOOLBAR)

pl = PLOT(xpts, ypts, XRANGE=[0, 10], YRANGE=[0, 10], /NODATA, $

  XTITLE='X', YTITLE='Y', FONT_SIZE=12, /CURRENT)

plg = POLYGON(xpts, ypts, COLOR='blue', THICK=2, /DATA)


여기서 구현된 원형 다각형은 중심점에서의 각도값들이 0, 10, 20, ...., 350, 360까지인 총 37개의 점(Vertex)들로 구성되어 있습니다. 그래서 이 점들의 X좌표값들로 구성된 배열 xpts와 Y좌표값들로 구성된 배열 ypts를 정의하고, 이 정보를 POLYGON 함수에 투입하여 원형 다각형을 표출한 것입니다. 이 내용을 실행하면 결과는 다음 그림과 같습니다.



이와 같이 원형의 다각형이 존재하는 상태에서 위치 좌표가 (6.7, 2.9)인 점을 하나 가정해 보겠습니다. 다음과 같이 점의 X 및 Y 좌표값을 변수 x, y에 담은 후 SYMBOL 함수를 사용하여 이 점을 표시합니다.


x = 6.7

y = 2.9

sym = SYMBOL(x, y, 'circle', /SYM_FILLED, /DATA)


그러면 그 결과는 다음 그림과 같습니다.



그러면 앞서 소개했던 INSIDE 함수를 사용하여 이 점이 원형 다각형 내부에 존재하는지 여부를 판단해 봅시다. 어차피 이 점이 원형 다각형 내부에 존재하고 있다는 것은 눈으로도 빤히 보이긴 합니다. 어쨌든 이 과정은 다음과 같습니다.


result = INSIDE(x, y, xpts, ypts)

HELP, result

PRINT, 'inside :', result


여기서 INSIDE 함수에 들어가는 인수들을 보면, 처음 두 개는 점의 XY 좌표값들입니다. 그리고 이어서 등장하는 두 개의 인수들은 원형 다각형을 구성하는 점(Vertex)들의 XY 좌표값 배열들인 xpts와 ypts입니다. 판별 결과는 result라는 변수로 전달되었는데, HELP 및 PRINT 명령으로 출력된 결과를 보면 다음과 같습니다.


RESULT          LONG      =            1

inside :           1


즉 result 변수의 값은 1입니다. 1이면 내부이고 0이면 외부라는 의미이기 때문에, 좌표가 (6.7, 2.9)인 점은 이 원형 다각형의 내부에 존재하는 것으로 판별되었음을 알 수 있습니다. 만약 좌표가 (8.2, 7.3)일 경우에는 어떻게 될까요? 위의 내용에서 변수 x, y의 값만 이와 같이 바꿔서 실행해보면 결과는 다음과 같습니다.


x = 8.2

y = 7.3



RESULT          LONG      =            0

inside :           0


표출된 그림을 봐도 이 점이 외부에 있다는 것을 충분히 알 수 있고, 출력된 내용을 봐도 그렇게 판별되었음을 확인할 수 있습니다. 이러한 판별 작업은 이와 같이 점 하나하나에 대하여 적용할 수 있지만, 다수의 점들에 대하여 한꺼번에 적용할 수도 있습니다. 이를 위해서는 다음과 같이 점들의 X좌표값들 및 Y좌표값들로 구성된 배열을 인수로 부여하면 됩니다.


x = [6.7, 8.2, 3.4]

y = [2.9, 7.3, 5.8]


이렇게 하면 좌표가 각각 (6.7, 2.9), (8.2, 7.3), (3.4, 5.8)인 세 개의 점들에 대한 판별 결과를 한꺼번에 얻을 수 있습니다. 그 결과는 다음 그림과 같습니다.



그리고 출력된 결과를 보면 다음과 같습니다.


RESULT          LONG      = Array[3]

inside :           1           0           1


이와 같이 result가 이번에는 3개의 값들로 구성된 배열이 됩니다. 배열 값들을 출력해보면 1, 0, 1로 나오는데, 첫번째 및 세번째 점은 내부에 위치하고 두번째 점은 외부에 위치한다는 의미입니다. 이 결과를 바탕으로 표출도 약간 다르게 해볼 수 있습니다. 예를 들어, 내부에 위치한 점들은 녹색으로 표시하고 외부에 위치한 점들은 빨간색으로 표시하는 것입니다. 이를 위하여 다음과 같은 내용을 추가해 봅시다.


ww = WHERE(result EQ 1, COMPLEMENT=wn)

sym_in = SYMBOL(x[ww], y[ww], 'circle', SYM_COLOR='green', /SYM_FILLED, /DATA)

sym_out = SYMBOL(x[wn], y[wn], 'circle', SYM_COLOR='red', /SYM_FILLED, /DATA)


여기서는 result에 대하여 WHERE 함수를 사용하여 1인 경우의 인덱스들과 0인 경우의 인덱스들을 구분하여 얻은 후, SYMBOL 함수를 두 경우에 대하여 독립적으로 사용한 것입니다. 그리고 이 때 심볼의 색상이 서로 다르게 표출되도록 처리하였습니다. 이 결과는 다음 그림과 같습니다.



따라서 판별하고자 하는 점들이 더 많을 경우에도 이 점들의 좌표값들을 배열로 구성하여 투입해주면 됩니다. 만약 지금 정의되어 있는 것처럼 X축 범위 0~10 및 Y축 범위 0~10 내에서 30개의 랜덤한 점들을 생성하여 각각에 대하여 내부 및 외부 여부를 판별해본다면, 다음과 같이 30개의 점들의 좌표값들을 RANDOMU 함수를 사용하여 생성하여 투입해주면 됩니다.


x = RANDOMU(seed, 30)*10

y = RANDOMU(seed, 30)*10


이 결과는 다음 그림과 같습니다. 어차피 이 점들의 좌표값들은 랜덤으로 생성되기 때문에, 여러분들이 해보시면 그 결과가 이 그림과 똑같지는 않을 것입니다. 어차피 프로그램을 실행할 때마다 모습은 다릅니다. 그러나 점들에 대한 내부 및 외부 판별은 제대로 되었음을 확인할 수 있습니다.



이와 같이 Coyote 라이브러리에서 제공되는 INSIDE 함수를 사용하여 이러한 작업을 쉽게 수행할 수 있음을 확인하였습니다. 그런데 이러한 작업을 외부 라이브러리에 의존하지 않고 IDL에서 자체적으로 제공되는 기능을 사용해서 수행할 수는 없을까요? 다행히 방법이 있습니다. 바로 IDLanROI 클래스의 객체를 활용하는 방법입니다. "객체"라는 용어가 나와서 뭔가 좀 무시무시한 놈은 아닐까하는 우려가 있을 수도 있지만 사실 사용 방법은 꽤 간단합니다. 앞선 예제 코드에서 INSIDE 함수를 사용하고 그 결과를 출력하는 다음과 같은 내용이 있었습니다.


result = INSIDE(x, y, xpts, ypts)

HELP, result

PRINT, 'inside :', result


이 내용을 다음과 같은 내용으로 대체하면 됩니다.


oROI = OBJ_NEW('IDLanROI', xpts, ypts)

result = oROI -> ContainsPoints(x, y)

HELP, result

PRINT, 'IDLanROI :', result


여기서는 IDLanROI 클래스의 객체를 하나 생성하고, 이 객체에 대하여 ContainsPoints 메서드를 적용하여 판별 결과를 얻게 됩니다. 문법상으로 보면 객체를 생성할 때 OBJ_NEW 함수에서 xpts, ypts가 사용되었고, 내/외부 판별에 있어서 ContainsPoints 메서드를 사용할 때 점의 좌표값(들)이 투입되는 방식입니다.


어차피 INSIDE 함수를 사용하든 IDLanROI 객체를 사용하든 결과는 똑같습니다. 여러분들도 직접 확인해보시기 바랍니다. 따라서 두 방법 중 어느 것을 사용하느냐는 그냥 단순한 선택의 문제일 뿐이라고 보면 됩니다. 굳이 별도의 외부 라이브러리를 설치하는 것이 귀찮다면 그냥 IDLanROI 클래스의 객체를 사용하는 후자의 방법을 선택하면 됩니다. 다만 오늘 소개한 IDLanROI 객체는 내부적으로 다양한 기능들을 포함하고 있기 때문에 알아두면 꽤 도움이 됩니다. 다음 기회에는 오늘 했던 작업의 개념을 좀 더 확장시켜 볼 예정인데 그 때 이 IDLanROI 객체가 맹활약을 펼치게 될 것 같습니다. 이 내용은 나중에 준비가 되는대로 올리도록 하겠습니다.

반응형