Oracle Spatial Geometry Type 및 Spatial Table 생성

[출처] 이 글은 오라클에서 제공하는 Oracle Spatial User.s Guide를 참고로 하였습니다.

오라클에서 지원하는 지오메트리는 총 9가지로 Point, LineString, Polygon, Arc Line Stringm Arc Polygon, Compound Polygon, Compound Line String, Circle, Rectangle로 다음과 같습니다.

사용자 삽입 이미지

이 중 Point, Line String, Polygon은 GIS의 Simple Feature 정의를 따르는 것은 Point, Line String, Polygon입니다. 오라클에서는 Line String을 구성하는 선분의 교차를 허용합니다.

공간 테이블을 생성하는 SQL 문은 다음과 같습니다. 공간 데이터는 도형과 속성으로 구성되는 피쳐(Feature) 이므로 예를들어 테이블명이 cola_markets라고 할때 도형에 대한 필드명은 shape이며 SDO_GEOMETRY 타입으로 정의되며 속성은 예를 들어 mkt_id와 name이라는 필드명으로써 각각 NUMBER와 VARCHAR2 타입으로 정의한다고 할때 다음과 같습니다.

CREATE TABLE cola_markets 
(
  mkt_id NUMBER PRIMARY KEY,
  name VARCHAR2(32),
  shape SDO_GEOMETRY
);

이렇게 생성된 테이블에 하나의 레코드를 추가하는 SQL 문의 예는 다음과 같습니다.

INSERT INTO cola_markets VALUES
(
  1,
  'cola_a',
  SDO_GEOMETRY(
    2003, -- 2차원의 폴리곤
    NULL,
    NULL,
    SDO_ELEM_INFO_ARRAY(1,1003,3), -- 1개의 사각형(1003 = exterior)
    SDO_ORDINATE_ARRAY(1,1, 5,7) -- 사각형을 정의하기 위해 오직 2개의 포인트 필요
  )
);

다음은 또 다른 레코드를 추가하는 예로, 도형의 타입이 Polygon인 SQL 문의 예입니다.

INSERT INTO cola_markets VALUES
(
  3,
  'cola_c',
  SDO_GEOMETRY(
    2003,
    NULL,
    NULL,
    SDO_ELEM_INFO_ARRAY(1,1003,1), -- 1개의 폴리곤
    SDO_ORDINATE_ARRAY(3,3, 6,3, 6,5, 4,5, 3,3)
  )
);

다음은 또 다른 레코드를 추가하는 예로써, 도형의 타입이 원(Circle)인 SQL 문의 예입니다.

INSERT INTO cola_markets VALUES
(
  4,
  'cola_d',
  SDO_GEOMETRY(
     2003,
     NULL,
     NULL,
     SDO_ELEM_INFO_ARRAY(1,1003,4), -- 1개의 원
     SDO_ORDINATE_ARRAY(8,7, 10,9, 8,11) -- 1개의 원은 최소 3개의 점으로 정의됨
  )
);

공간 테이블은 R-Tree 등과 같은 공간 인덱싱이 적용되어져야 빠른 공간 검색 등이 가능합니다. 아래는 앞서 생성한 공간 테이블에 대해서 공간 인덱싱을 생성해 주는 SQL 문입니다.

INSERT INTO user_sdo_geom_metadata
(
  TABLE_NAME,
  COLUMN_NAME,
  DIMINFO,
  SRID
)
VALUES 
(
  'cola_markets',
  'shape',
  SDO_DIM_ARRAY -- 20 X 20 그리드
  ( 
    SDO_DIM_ELEMENT('X', 0, 20, 0.005),
    SDO_DIM_ELEMENT('Y', 0, 20, 0.005)
  ),
  NULL -- SRID
);

CREATE INDEX cola_spatial_idx
ON cola_markets(shape) INDEXTYPE IS MDSYS.SPATIAL_INDEX;

이제 이렇게 공간 인덱싱이 적용된 공간 테이블에 대해서 다양한 공간 연산을 수행할 수 있으며 아래의 예와 같은 SQL문을 통해 공간 연산을 수행할 수 있습니다.

-- 2개의 도형과 교차하는 도형 얻기
SELECT SDO_GEOM.SDO_INTERSECTION(c_a.shape, c_c.shape, 0.005)
FROM cola_markets c_a, cola_markets c_c
WHERE c_a.name = 'cola_a' AND c_c.name = 'cola_c';

-- 2개의 도형(지오메트리, Geometry)에 대한 공간관계 연산의 결과 얻기
SELECT SDO_GEOM.RELATE(c_b.shape, 'anyinteract', c_d.shape, 0.005)
FROM cola_markets c_b, cola_markets c_d
WHERE c_b.name = 'cola_b' AND c_d.name = 'cola_d';

-- 해당 공간 테이블의 전체 면적 얻기
SELECT name, SDO_GEOM.SDO_AREA(shape, 0.005) FROM cola_markets;

-- 특정 도형에 대한 면적 얻기
SELECT c.name, SDO_GEOM.SDO_AREA(c.shape, 0.005) FROM cola_markets c
WHERE c.name = 'cola_a';

-- 2개의 도형 간의 거리 얻기
SELECT SDO_GEOM.SDO_DISTANCE(c_b.shape, c_d.shape, 0.005)
FROM cola_markets c_b, cola_markets c_d
WHERE c_b.name = 'cola_b' AND c_d.name = 'cola_d';

-- 특정 지오메트리가 옳바른가?
SELECT c.name, SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(c.shape, 0.005)
FROM cola_markets c WHERE c.name = 'cola_c';

Oracle Spatial SQL 정리

공간서버에 Spatial 오라클을 지원하면서 파악한 SQL문을 정리해 봅니다. 먼저 오라클에 정의된 Spatial Table의 전체 MBR을 얻는 SQL 문입니다.

SELECT 
    SDO_AGGR_MBR(geometry) 
FROM {SCHEMA}.{TABLE};

이 SDO_AGGR_MBR은 레코드 하나 하나에 대한 지오메트리의 MBR을 얻어 합하는 방식 같다는 생각으로 상당히 비효율적인 구문같습니다. 해당 테이블에 대해서 한번에 MBR을 얻는 방법이 있을듯하니 추후에 파악해 보렵니다.

다음은 해당 테이블의 필드명과 필드 타입을 얻는 SQL 문입니다.

SELECT 
    COLUMN_NAME, DATA_TYPE 
FROM 
    ALL_TAB_COLUMNS 
WHERE 
    OWNER='{SCHEMA}' AND TABLE_NAME='{TABLE}';

다음은 지정된 MBR에 대해 중첩된 지오메트리만을 검색해 주는 SQL 문입니다.

SELECT 
    GEOMETRY 
FROM 
    {SCHEMA}.{TABLE}
WHERE 
    SDO_ANYINTERACT(
        GEOMETRY, 
        SDO_GEOMETRY(2003, NULL, NULL, SDO_ELEM_INFO_ARRAY(1,1003,3), 
        SDO_ORDINATE_ARRAY({MinX},{MinY},{MaxX},{MaxY))
    ) = 'TRUE';

이 SQL 문은 실운영되는 오라클 서버에서 간단히 테스트는 했는데 좀더 다양한 환경에서 테스트해 볼 필요가 있을듯합니다.

오라클은 공간DB에 대해서 일반적인 GIS의 Simple Feature 정의를 따르지 않습니다. 하나의 테이블에 폴리곤, 포인트, 커브이 섞여 있을 수 있습니다. 여기서 커브(Curve)는 GIS에서 흔히 말하는 폴리라인이나 라인스트링을 포함하는 것으로 호(Arc)와 같은 표현이 가능합니다.

또한 하나의 Geometry에 대해서도 커브와 포인트 그리고 폴리곤이 섞여 있을 수도 있습니다. 그리고 도형의 회전을 위한 매트릭스 정보가 좌표값을 담는 구조체에 존재합니다.

오라클의 공간DB 구조… 명확하고 심플한 GIS 분야에서는 만만치 않는 놈이네요. 아직 정리되지 않는 글인데 머리가 아파 조금이라도 정리를 해보고자 글을 남겨봅니다.

DBF를 SQLite DB 파일로 만들기

DBF 파일을 엑셀로 불러온 후 첫행에서 FID 라는 이름의 컬럼을 추가합니다. 그리고 이 FID 컬럼의 값을 0부터 시작하여 1씩 증가하도록 지정합니다. 아래가 이와 같은 작업의 예시입니다. 이 FID 컬럼이 필요한 이유는 SHP 파일의 도형와의 1:1 관계키로 사용하기 위함입니다.

그리고 이 파일을 텍스트(탭으로 분리)로 저장합니다. 이 파일을 인코딩은 ANSI로 되어 있는 경우가 있는데 이를 UTF-8로 변환합니다. 저 같은 경우 메모장에서 불러와 UTF-8로 인코딩을 지정하여 저장하였습니다.

SQLite Expert Professional 버전을 실행합니다. [File]-[New Database]를 실행하여 다음과 같이 지정하고 OK 버튼을 클릭합니다.

일반적으로 Database File 안에는 다수의 테이블이 존재하지만 단 하나의 테이블만 존재하며 이 테이블의 이름과 Database File의 이름은 같다라고 약속합니다. 이 부분은 모바일 GIS 엔진인 블랙포인트에서 필요한 약속이므로 블랙포인트를 위한 변환이 아니라면 관계없습니다.

그리고 [Import/Export]-[Import Text File]을 실행하고 다음처럼 앞서 만들어 놓은 텍스트 파일을 지정한 후 Start 버튼을 클릭하여 실행합니다.

테이블이 만들어 졌고 만들어진 필드를 보다 정확하게 조정하기 위해 Design 탭 안의 Fields 탭을 선택하여 Type, Size를 지정합니다. 그리고 필요하다면 Not Null을 체크하도록 하여 Null 값을 경우 기본값을 가지도록 합니다. 이는 Null 일 경우에 발생할 수 있는 예외를 대비할 수 있습니다. 블랙포인트에서는 이런 Not Null 처리가 반드시 필요하며 Type은 Size가 0인 INTEGER, Size가 0이 아닌 CHAR, Size가 0인 FLOAT만이 가능합니다.

그리고 인덱스를 걸기 위해서 Desing 탭 안의 Indexes 탭을 선택하여 인덱스를 추가합니다. 최소한 FID는 인덱스를 걸어주는 것이 좋으며 검색으로 사용하는 필드가 있다면 인덱스를 걸어줍니다.

속성값으로 SHP 파일 데이터 분류 가공

SHP 파일의 속성값에 따라 불필요한 데이터는 제거할 필요가 있어 ExpressionsSHP 툴을 사용한 예를 정리해 봅니다. ExpressionsSHP 툴은 다음 URL을 통해 다운로드 받을 수 있습니다.

http://www.gisdeveloper.co.kr/entry/정규표현식을-이용한-SHP-추출-ExpressionsSHP

제가 하고자 하는 것은 해당 SHP 파일 중 특정 필드의 값이 빈문자값을 가지는 것은 제거였습니다.

“빈문자열로 시작하지 않는다”는 조건에 대한 정규표현식은 다음과 같습니다.

^\S

예를 들어 제가 처리하고자 하는 SHP 파일의 속성값을 살펴보면 아래와 같았습니다. NOBJNM_MB_ 필드에 대해서 빈문자열이 보입니다.

사용자 삽입 이미지
위의 데이터를  ExpressionsSHP 툴에서 SHP 파일로 입력하고 공백으로 시작하지 않음을 의미하는 정규표현식 조건 지정한 화면은 아래와 같습니다.

사용자 삽입 이미지
위와 같이 입력값을 지정하고 실행하면 결과 SHP 파일에 대한 분류가 되는 것을 알 수 있습니다.

사용자 삽입 이미지

VWorld의 WMS 지도 서비스

VWorld에는 TMS 방식의 다양한 배경지도(단순 배경지도와 항공영상 지도 등) 뿐만이 아니라 유용한 WMS 방식의 지도 서비스도 제공하고 있습니다. 이를 VWorld에서 OpenAPI 키를 발급받아 TMS나 WMS를 지원하는 OpenLayers나 FingerEyes와 같은 클라이언트 맵엔진에서 사용할 수 있습니다.

이 글은 VWorld에서 제공하는 WMS 지도 서비스를 이용하는데 필요한  WMS 파라메터에 대해 정리한 글입니다. 저는 제게 익숙한 FingerEyes를 이용해 VWorld의 WMS 지도 서비스를 살펴보았습니다. OpenLayers에 익숙하다면 쉽게 적용이 가능할 것으로 판단됩니다. 참고로 VWorld는 자체적인 2D Map API를 지원하고 있으므로 이를 이용할 수도 있습니다.

먼저 자연환경 보전 지역에 대한 WMS 지도 서비스 요청입니다. 참고로 _KEY_KEY_라고 되어 있는 부분은 VWorld에서 직접 OpenAPI Key를 발급받아 지정하시면 됩니다.

var wmsLayer:XrWMSLayer = new XrWMSLayer("wms", 
    "http://2d.vworld.kr:8895/2DCache/gis/map/WMS2?APIKEY=_YOUR_KEY_&", 
    "LT_C_UQ114", 
    "EPSG:900913");

map.layers.addLayer(wmsLayer);

그 결과에 대한 지도는 아래와 같습니다. 참고로 배경맵은 VWorld의 TMS 지도 서비스를 사용하였습니다.

사용자 삽입 이미지
다음은 개발제한구역에 대한 WMS 지도 서비스 요청입니다.

var wmsLayer:XrWMSLayer = new XrWMSLayer("wms", 
    "http://2d.vworld.kr:8895/2DCache/gis/map/WMS2?APIKEY=_YOUR_KEY_&", 
    "LT_C_UD801", 
    "EPSG:900913");

map.layers.addLayer(wmsLayer);

결과는 아래와 같습니다.

사용자 삽입 이미지
그리고 다음은 지적도에 대한 WMS 지도 서비스 요청입니다.

var wmsLayer:XrWMSLayer = new XrWMSLayer("wms", 
    "http://2d.vworld.kr:8895/2DCache/gis/map/WMS2?APIKEY=_YOUR_KEY_&", 
    "LP_PA_CBND_BUBUN%2CLP_PA_CBND_BONBUN", 
    "EPSG:900913");

    map.layers.addLayer(wmsLayer);

결과는 아래와 같습니다.

사용자 삽입 이미지
그리고 다음은 새주소도로에 대한 WMS 지도 서비스 요청입니다.

var wmsLayer:XrWMSLayer = new XrWMSLayer("wms", 
    "http://2d.vworld.kr:8895/2DCache/gis/map/WMS2?APIKEY=_YOUR_KEY_&", 
    "LT_L_SPRD",
    "EPSG:900913");

map.layers.addLayer(wmsLayer);

결과는 아래와 같습니다. 참고로 새주소도로는 도로 위의 도로명에 대한 표시입니다.

사용자 삽입 이미지   
그리고 다음은 토지이용계획도에 대한 WMS 지도 서비스 요청입니다.

var wmsLayer:XrWMSLayer = new XrWMSLayer("wms", 
    "http://2d.vworld.kr:8895/2DCache/gis/map/WMS2?APIKEY=_YOUR_KEY_&", 
    "LT_C_LHBLPN", 
    "EPSG:900913");

map.layers.addLayer(wmsLayer);

결과는 다음과 같습니다.

사용자 삽입 이미지
그리고 다음은 국토계획구역에 대한 WMS 지도 서비스 요청입니다.

var wmsLayer:XrWMSLayer = new XrWMSLayer("wms", 
    "http://2d.vworld.kr:8895/2DCache/gis/map/WMS2?APIKEY=_YOUR_KEY_&", 
    "LT_C_UQ141",   
    "EPSG:900913");

map.layers.addLayer(wmsLayer);

결과는 다음과 같습니다.

사용자 삽입 이미지
그리고 다음은 사업지구경계도에 대한 WMS 지도 서비스 요청입니다.

var wmsLayer:XrWMSLayer = new XrWMSLayer("wms", 
    "http://2d.vworld.kr:8895/2DCache/gis/map/WMS2?APIKEY=_YOUR_KEY_&", 
    "LT_C_LHZONE", 
    "EPSG:900913");

map.layers.addLayer(wmsLayer);

다음은 그 결과입니다.

사용자 삽입 이미지
그리고 다음은 도시지역도에 대한 WMS 지도 서비스 요청입니다.

var wmsLayer:XrWMSLayer = new XrWMSLayer("wms", 
    "http://2d.vworld.kr:8895/2DCache/gis/map/WMS2?APIKEY=_YOUR_KEY_&", 
    "LT_C_UQ111", 
    "EPSG:900913");

map.layers.addLayer(wmsLayer);

결과는 다음과 같습니다.

사용자 삽입 이미지
이외에도 향후 더 다양한 WMS 지도 서비스를 제공할 것으로 생각됩니다. 이처럼 국가에서 제공하는 양질의 유용한 지도 데이터 서비스를 활용하여 민간업체에서 더 나은 서비스를 연구 및 기획하여 실제 개발할 수 있을 것입니다.