지오코딩 서비스(GEOCODER-XR) 기능 개선

OpenAPI 형태의 서비스 단위로 제공되는 GEOCODER-XR이 고객의 요구사항에 맞춰 개선되었습니다. 다음과 같이 3가지 내용이 변경되었습니다.

  1. 지오코딩 결과에 대한 정좌표, 인근좌표, 대표좌표에 대한 구분(기존에는 인근좌표와 대표좌표의 구분이 없었음)
  2. 결과가 여러 개인 경우 정확도 순서에 따라 정렬해서 모든 결과를 다 제공하도록 변경
  3. 결과 형식을 JSON으로 변경(기존은 XML 형식였음)

예를들어 주소 중 “중앙동”으로 검색을 하기 위한 REST 방식의 OpenAPI는 다음과 같습니다.

http://localhost:8080/Gp?command=gc;addr=중앙동

위의 호출에 대한 결과는 다음과 같습니다.

[
   {
      "user-input":"중앙동",
      "full-address":"경기도 성남시 중원구 중앙동",
      "coordinate":[
         37.43959765091859,
         127.1505276103632
      ],
      "vicinity":true,
      "type":"대표좌표"
   },
   {
      "user-input":"중앙동",
      "full-address":"경상남도 통영시 중앙동",
      "coordinate":[
         34.84482143430977,
         128.42308057122085
      ],
      "vicinity":true,
      "type":"대표좌표"
   },
   {
      "user-input":"중앙동",
      "full-address":"경상남도 창원시 진해구 중앙동",
      "coordinate":[
         35.14902784720422,
         128.66126916577852
      ],
      "vicinity":true,
      "type":"대표좌표"
   },

   ...
]

JSON 형식이며 중앙동이라는 이름의 행정명칭은 우리나라에 여러 개이므로 복수의 결과를 모두 전달하고 vicinity가 true이므로 인근좌표이면서 type을 통해 “대표좌표”라는 것을 파악할 수 있습니다.

이번에는 “중앙로10″으로 호출하기 위해 다음 API를 사용합니다.

http://localhost:8080/Gp?command=gc;addr=중앙로10

결과는 다음과 같습니다.

[
   {
      "user-input":"중앙로10",
      "full-address":"경기도 가평군 중앙로 10 (가평읍)",
      "coordinate":[
         37.82638794989058,
         127.5132600931772
      ],
      "vicinity":false,
      "type":"도로명주소"
   },
   {
      "user-input":"중앙로10",
      "full-address":"강원도 영월군 중앙로 10 (영월읍)",
      "coordinate":[
         37.18337775533778,
         128.46505641724204
      ],
      "vicinity":false,
      "type":"도로명주소"
   },
   {
      "user-input":"중앙로10",
      "full-address":"강원도 원주시 중앙로 10 (인동)",
      "coordinate":[
         37.34571078558531,
         127.95473309264851
      ],
      "vicinity":false,
      "type":"도로명주소"
   },

   ...
]

vicinity 값이 false이므로 “정좌표”를 의미합니다.

마지막으로 정확히 1건으로 매칭되는 주소에 대한 경우입니다.

http://localhost:8080/Gp?command=gc;addr=아차산로7나길18

결과는 다음과 같습니다.

[
   {
      "user-input":"아차산로7나길18",
      "full-address":"서울특별시 성동구 아차산로7나길 18 (성수동2가)",
      "coordinate":[
         37.54770170889392,
         127.05746040023593
      ],
      "vicinity":false,
      "type":"도로명주소"
   }
]

경위도로 지정한 위치 사이의 흐름선을 3D로 표현하기

구에 지구에 대한 텍스쳐를 맵핑하고 경위도로 지정된 2개의 위치 사이에 흐름선을 표현하는 시각화에 대한 구현체입니다. 최종 실행 결과는 아래와 같습니다.

WebGL을 기반으로 하는 3차원 라이브러리 three.js를 사용했으며, class를 통한 모듈방식으로 구현하였는데, 전체 소스 코드는 다음 URL로 다운로드 받으시기 바랍니다.

GIS 개발자로써 가장 중요한 코드 중 하나를 언급하면 바로 경위도 좌표를 xyz 좌표계로 변환해 주는 함수인데요. 바로 아래의 코드입니다.

_getPosFromLatLonRad(lat, lon, radius) {
    var phi = (90 - lat) * (Math.PI / 180)
    var theta = (lon + 180) * (Math.PI / 180)

    let x = -((radius) * Math.sin(phi)*Math.cos(theta))
    let z = ((radius) * Math.sin(phi)*Math.sin(theta))
    let y = ((radius) * Math.cos(phi))

    return {x,y,z}
}

위도와 경도 그리고 구의 반지름을 받아 해당하는 xyz 축의 좌표를 반환합니다.

[PostGIS] 가장 가까운 위치 찾기

시나리오는 어떤 지점에서 가장 가까운 위치를 찾아야 한다.. 입니다. 그런데 위치에 대한 좌표 타입이 geometry가 아니고 일단 실수형이라는 점입니다. 그리고 기준 좌표와 찾아야 하는 좌표의 좌표 체계가 다릅니다. 하나는 구글 좌표계(EPSG:3857)이고 하나는 경위도 좌표계(EPSG:4326)입니다.

아래의 쿼리가 위의 내용에 대한 솔루션 중 하나입니다.

SELECT 
    station_id,
    address,
    lng,
    lat,
    ST_DISTANCE(
        ST_TRANSFORM(
            ('SRID=4326;POINT(' || lng || ' ' || lat || ')')::geometry,
            3857
        ),
        'SRID=3857;POINT(14128453 4506986)'::geometry
    ) AS distance
FROM 
    weather_station
ORDER BY
    distance

즉, 구글 좌표계로 (14128453, 4506986)인 지점과의 거리를 구하고 있습니다. 거리를 구하는 ST_DISTANCE는 서로 다른 좌표계를 사용할 수 없으므로 ST_TRANSFORM을 통해 경위도 좌표를 X,Y 좌표계로 변경하고 있습니다. 결과는 다음과 같습니다.

보시면 가장 첫번째 Row가 가장 가까운 대상이라는 것을 알 수 있습니다.

크리깅(Kriging) 분석에 대한 색상 시각화

아래의 결과는 크리깅(Kriging) 분석을 통한 보간 결과(공간상의 숫자값은 보간을 위한 입력 값이 아닌 단순 번호임)입니다. 크리깅 보간의 결과값의 범위가 최대값 7.4, 최소값 0.6이 산출된 경우입니다. 최대값은 빨간색으로 최소값은 초록색으로 표현하고 있습니다.

단순히 시각적인 관점으로 보면 나쁘진 않은데, 문제는.. 빨간색은 위험 범위의 값일 경우 표현에 사용하고자 한다는데 있습니다. 위험 수위 값은 10부터라고 정의 했을때.. 위의 경우 최대값인 7.4는 위험 수위값이 아니므로 빨간색으로 표현되면 사용자에게 혼란을 야기합니다.

위의 문제점을 개선한 동일한 크리깅 결과를 기대한 색상으로 시각화한 결과는 아래와 같습니다.

모든 지점이 10보다 작으므로 위험을 나타내는 빨간색으로 표현되지 않습니다. 바로 이게 원하는 결과입니다.

이러한 결과를 얻기 위한 색상을 정의하는 코드는 다음과 같습니다.

const colors = [
    { value: 10, steps: 10, color: [255, 0, 0, 200] },
    { value: 5, steps: 10, color: [255, 255, 0, 200] },
    { value: 0, steps: 10, color: [0, 255, 0, 200] },
    { value: -5, steps: 10, color: [0, 255, 255, 200] },
    { value: -10, color: [0, 0, 255, 200] },
];

const clrTbl = new Xr.RangesColorTable(colors);
if (clrTbl.build()) {
    gridLyr.updateByRangesColorTable(clrTbl, psd);
    map.update();
}

위의 코드는 공간 데이터를 시각화 해주는 FingerEyes-Xr에 대한 코드입니다. FingerEyes-Xr은 자바스크립트(js) 기반의 웹 GIS 라이브러리입니다.