IDL/New Graphics

NG 체계에서도 POLAR_CONTOUR를?

이상우_idl 2018. 9. 3. 11:18
728x90
반응형

POLAR_CONTOUR는 (R, theta, Z) 기반의 극좌표 데이터를 등위선(Contour)의 형태로 표출하는 역할을 하는 IDL의 프로시저입니다. 주로 레이더(Radar)로 관측된 자료가 이런 극좌표 기반의 데이터 구조를 갖는 경우가 많습니다. 그래서 IDL의 POLAR_CONTOUR 프로시저는 이런 특성의 데이터를 표출하는데 있어서 IDL 유저들 사이에서도 꽤 자주 활용되어온 기능이기도 합니다. 그런데 그래픽 체계의 관점에서 보면 이 기능은 사실 DG(Direct Graphics) 체계에서만 사용 가능한 기능이기도 합니다. 그런데 IDL 8.0부터 도입된 NG 체계에서는 이와 유사한 그래픽 기능이 딱히 없다는 것이 문제입니다. 물론 CONTOUR 함수는 당연히 지원되지만, 앞서 언급한 기존의 POLAR_CONTOUR처럼 극좌표기반 데이터를 단번에 표출하는 전용 그래픽 함수가 NG 체계에서는 없는 것이 사실입니다. 그래서 저도 간혹 "POLAR_CONTOUR와 같은 기능을 NG 체계에서 사용하려면 어떻게 해야 하느냐?"라는 질문을 받기도 합니다.


사실 현 시점에서도 "POLAR_CONTOUR의 NG 체계 버전이 따로 있다"라고 속시원하게 말씀드리고 싶은 마음은 간절하지만, 실제로 존재하지도 않으니 그렇게 말씀드리지는 못합니다. 다만 NG 체계에서도 유사한 방식의 표출을 하는 것은 가능은 합니다. 물론 이를 위해서는 약간의 코딩이 더 필요하며, 표출되는 속도가 기존의 POLAR_CONTOUR만큼 빠르지는 못하다는 단점들은 있습니다. 그럼에도 불구하고 제 생각에는 나름 쓸만한 방법일 것 같아서 오늘 한번 소개를 해보고자 합니다. 이 방법에서는 제가 예전에 한번 다룬 적이 있었던 POLAR_SURFACE라는 함수를 사용하게 됩니다. POLAR_SURFACE는 (R, theta, Z) 기반의 극좌표 데이터를 (X, Y, Z) 기반의 직교 좌표 데이터로 변환해주는 역할을 하는 IDL의 내장함수입니다. 오늘은 제가 이 POLAR_SURFACE 함수에 관한 내용을 실었던 예전 게시물에서 사용했던 예제 데이터를 그대로 사용해보겠습니다. 먼저 다음과 같은 방식으로 (R, theta) 격자 형태를 갖는 가상 데이터를 만들어봅시다.


r = FINDGEN(201) / 200.0

theta = FINDGEN(72)*5*!dtor

z = r^2 # COS(theta*3)

HELP, z

PRINT, MIN(z), MAX(z)


생성된 데이터 배열 z에 대하여 HELP 명령을 사용하여 출력된 내용을 보면, z는 201x72의 격자 구조를 갖는 실수형 배열임을 알 수 있습니다.


Z               FLOAT     = Array[201, 72]


즉 반경을 따라가는 방사상(Radial) 방향(R 방향)으로 201개의 격자점들이 분포해있고, 각도를 따라 휩쓰는(sweep) 방향(theta 방향)으로는 72개의 격자점들이 분포해 있다는 얘기입니다. 실제로 데이터가 생성된 과정을 보면, R 방향으로는 0.0부터 1.0까지 0.005의 간격으로 총 201개의 격자점들이 분포해 있고, theta 방향으로는 0도부터 355도까지 총 72개의 격자점들이 분포해 있음을 알 수 있습니다. 그리고 이렇게 생성된 배열 z의 최소값 및 최대값은 각각 -1과 1입니다. 즉 격자값들의 범위는 -1에서 +1 사이에 존재합니다. 그러면 이 데이터를 먼저 DG 체계에서 POLAR_CONTOUR 프로시저를 사용하여 먼저 표출해봅시다. 전체적인 과정은 다음과 같습니다.


DEVICE, DECOMPOSED=0

WINDOW, XSIZE=600, YSIZE=600, RETAIN=2

cval = [-1:1:0.1]

clabel = MAKE_ARRAY(N_ELEMENTS(cval), VALUE=1)

LOADCT, 0

POLAR_CONTOUR, TRANSPOSE(z), theta, r, /NODATA, BACKGROUND=255, COLOR=0

LOADCT, 74

POLAR_CONTOUR, TRANSPOSE(z), theta, r, LEVELS=cval, /FILL, /OVERPLOT

LOADCT, 0

POLAR_CONTOUR, TRANSPOSE(z), theta, r, LEVELS=cval, COLOR=0, $

  C_LABELS=clabel, /OVERPLOT


여기서는 무슨 이유에서인지 POLAR_CONTOUR 명령이 세번이나 사용되었습니다. 사실 각각 역할이 있습니다. 첫번째는 /NODATA 키워드와 함께 사용되었는데, 이는 흑백 색상으로 기본 틀만 표출하는 역할입니다. 두번째는 등위선을 채워진(Filled) 형태로 표출하는 역할입니다. 즉 선과 선 사이의 빈 공간을 색상으로 채우도록 하였으며, 이 때 74번 컬러테이블의 색상들이 사용되었습니다. 그런데 이렇게 색상이 채워진 형태로 표출할 경우에는 선이나 라벨 문자들은 덮어지게 됩니다. 따라서 이 상태에서 세번째 POLAR_CONTOUR가 검은 선 및 라벨 문자들만 중첩 표출하는 역할을 하게 됩니다. 등위선들의 간격은 원래 데이터인 z의 값 범위를 고려하여 -1부터 1까지 0.1의 간격이 되도록 하였습니다. 만약 세 개의 POLAR_CONTOUR들의 각각의 역할이 궁금하다면 하나씩 따로 실행되도록 해보면 좀 더 명확하게 구분이 가능할 것입니다. 어쨌든 그 결과는 다음 그림과 같습니다.



이것이 POLAR_CONTOUR 프로시저에 의하여 DG 체계에서 표출된 모습입니다. 이와 유사한 형태의 표출을 NG 체계에서는 어떻게 해야 할까요? 앞서 언급했듯이 POLAR_CONTOUR 프로시저와 유사한 기능을 직접 수행하는 NG 체계의 그래픽 함수는 아직 없습니다. 하지만 DG 체계의 POLAR_CONTOUR 프로시저와 유사한 작업을 NG 체계에서 구현하는 것은 어느 정도 가능합니다. 이를 위해서는 앞서 제가 언급한 POLAR_SURFACE 함수를 활용하여 (R, theta) 극좌표 기반의 데이터를 (X, Y) 직교 좌표 기반의 데이터로 변환한 다음, 이 변환된 데이터를 CONTOUR 함수에 투입하면 됩니다. 먼저 POLAR_SURFACE 함수를 이용한 데이터의 변환 과정은 다음과 같습니다.


ps = POLAR_SURFACE(z, r, theta, /GRID, SPACING=[0.004, 0.004], $

  MISSING=!values.f_nan)

HELP, ps


이 과정에 관한 자세한 내용은 앞서 링크를 알려드린 예전 게시물의 내용을 참조하시기 바랍니다. HELP에 의하여 출력된 정보를 보면, ps라는 배열은 501x501의 배열 구조를 갖습니다. 그 이유는 원본 데이터인 z가 반경의 방향으로 -1부터 +1의 범위에 있는데(위의 그림 참조), 이 범위의 총 크기인 2를 위의 내용에서 SPACING 키워드에 부여된 0.004로 나눈 값이 500이기 때문입니다. 위의 SPACING 키워드는 극좌표 기반의 데이터를 직교좌표 기반의 데이터로 변환할 때 X 및 Y 방향 격자의 실제 크기에 해당됩니다. 따라서 이 값이 더 작아지면 결과로 얻어지는 ps의 격자 크기는 더 커질 것입니다. 그리고 추가적으로 다음과 같이 X 및 Y 방향 격자 위치값들로 구성된 psx, psy도 함께 만들어두는 것이 좋습니다. 나중에 CONTOUR 함수를 사용한 표출 과정에서는 ps, psx, psy가 모두 필요하기 때문입니다.


psx = [-1:1:0.004D]

psy = [-1:1:0.004D]

HELP, psx, psy


이렇게 얻어진 psx, psy는 각각 501개의 값들로 구성됩니다. 정리해보면 ps는 501x501의 형태를 갖는 2차원 배열, psx는 501개의 X방향 격자위치 값들로 구성된 1차원 배열, psy는 501개의 Y방향 격자위치 값들로 구성된 1차원 배열입니다. 이제 CONTOUR 함수를 사용하여 ps를 표출해봅시다. 앞서 POLAR_CONTOUR로 표출했던 그림과 가급적 유사한 형태로 표출되도록 하였습니다.


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

cval = [-1:1:0.1]

cn = CONTOUR(ps, psx, psy, C_VALUE=cval, RGB_TABLE=74, /FILL, $

  AXIS_STYLE=2, MARGIN=0.1, /CURRENT)

cno = CONTOUR(ps, psx, psy, C_VALUE=cval, COLOR='black', $

  C_LABEL_SHOW=1, /OVERPLOT)


여기서는 CONTOUR 함수를 사용할 때 ps뿐 아니라 psx, psy도 함께 사용하였습니다. 이렇게 해야 X 및 Y 방향의 격자값 범위가 제대로 표시됩니다. 표출된 모습은 다음 그림과 같습니다.



아마 이 정도면 POLAR_CONTOUR로 표출하는 경우와 거의 비슷하게 표출된 것이라고 생각해도 될 것 같습니다. 다만 이 작업들을 실제로 해보면 아마 표출되는 속도 측면에서 아무래도 후자의 경우가 좀 느리다는 것이 느껴질 것입니다. 이것은 DG와 NG 체계 사이의 그래픽적 특성 차이 때문에 어쩔 수 없는 부분입니다. 물론 앞서 언급한 POLAR_SURFACE 함수의 SPACING 키워드의 값을 조정하여 ps의 격자 크기를 줄일 경우에는 약간 더 빨라질 수 있지만, 그래도 예전처럼 DG 체계에서 POLAR_CONTOUR에 비하여 약간 느린 것은 어쩔 수가 없습니다. 제 컴퓨터에서 테스트해본 바로는 후자의 방법으로 표출할 경우 약 3~4초 정도의 시간이 걸렸습니다. 물론 컴퓨터의 사양이나 제반 여건에 따라 달라질 가능성은 얼마든지 있습니다.


그리고 참고로 하나만 더 언급한다면, CONTOUR 함수를 사용할 때 위에서 했던 것처럼 ps, psx, psy를 모두 활용하지 않고 그냥 ps만 활용한다면 어떻게 될까요?


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

cval = [-1:1:0.1]

cn = CONTOUR(ps, C_VALUE=cval, RGB_TABLE=74, /FILL, $

  AXIS_STYLE=2, MARGIN=0.1, /CURRENT)

cno = CONTOUR(ps, C_VALUE=cval, COLOR='black', $

  C_LABEL_SHOW=1, /OVERPLOT)


이와 같이 할 경우 표출되는 그림은 다음과 같습니다.



얼핏 보면 앞서 얻은 그림과 비슷해보일 수도 있지만, X축과 Y축의 눈금을 잘 보면 X 및 Y 방향의 정확한 범위가 제대로 반영되지 않고, 그냥 ps의 X 및 Y 방향 격자 갯수(501개)에만 근거하여 축 범위가 0~500으로 매겨진 것을 볼 수 있습니다. 따라서 X 및 Y 방향의 범위가 정확히 명시되게 하려면 ps뿐 아니라 psx, psy까지 모두 필요하다는 점을 염두에 둬야 하겠습니다.

반응형