Oracle sdo_geomery 자료를 PostgreSQL + PostGIS 기반으로 옮겨가기

이글은 이전 홈페이지에서 2012년 쓰다만 글입니다.
그래도 남겨 놓아야겠기에.
이젠 많이 바뀌어서 아래 글이 바르지 않을 수도 있습니다.


오라클 sdo_geometry 자료형에 대해서는
http://docs.oracle.com/cd/B13789_01/appdev.101/b10826/sdo_meth.htm
페이지를
sdo_geometry 자료형을 PostgreSQL 자료형으로 변환 하는 원문은
http://www.tolon.co.uk/2012/09/geometry-objects-across-dblink/
페이지를
PostGIS 관련 문서는
http://postgis.refractions.net/
페이지들을 참조로 작성될 것입니다.

Oracle sdo_geometry 자료형 구조


PostgreSQL용 오라클 호환 sdo_geometry

먼저 위에서 언급한 대로 일단 똑같은 mdsys.sdo_geometry 자료형을 하나 만든다.

CREATE DOMAIN mdsys.sdo_elem_info_array AS numeric[];
CREATE DOMAIN mdsys.sdo_ordinate_array AS numeric[];
CREATE TYPE mdsys.sdo_point_type AS (x numeric, y numeric, z numeric);
CREATE TYPE mdsys.sdo_geometry AS (
 sdo_gtype numeric,
 sdo_srid numeric,
 sdo_point mdsys.sdo_point_type,
 sdo_elem_info mdsys.sdo_elem_info_array,
 sdo_ordinates mdsys.sdo_ordinate_array);
이 자료형은 PostgreSQL 표준 복합 자료형이 됨으로, 입출력 또한 그대로 하면 된다.

dblink를 이용한 자료 옮기기

먼저 오라클에서 sdo_geometry 자료를 PostgreSQL 입력 포멧으로 바꾸기 위해서, 위에서 언급한 사이트에서 제공한 패키지를 약간 수정해서 만든다.
CREATE OR REPLACE PACKAGE pg_user_geom_util AS
-- copyright (c) 2012, Nicander Ltd
-- url: http://www.tolon.co.uk/2012/09/geometry-objects-across-dblink/
-- modified by ioseph

    -- Translates a geometry object into a string representation
    -- for storage and transport. See also StringToGeom.
    FUNCTION GeomToString(pGeom IN mdsys.sdo_geometry ) RETURN VARCHAR2 DETERMINISTIC;

END pg_user_geom_util;

CREATE OR REPLACE PACKAGE BODY ADMCHOI.pg_user_geom_util AS
-- copyright (c) 2012, Nicander Ltd
-- url: http://www.tolon.co.uk/2012/09/geometry-objects-across-dblink/
-- modified by ioseph
 
-- Gets the next token in a separated list. See StringToGeom for an example.
PROCEDURE get_token( iStart   IN NUMBER,
           sPattern in VARCHAR2,
           sBuffer  in VARCHAR2,
           sResult  OUT NOCOPY VARCHAR2,
           iNextPos OUT NOCOPY NUMBER ) IS
  nPos1 number;
  nPos2 number;
BEGIN
  nPos1 := Instr(sBuffer, sPattern, iStart);
  IF nPos1 = 0 then
  sResult := rtrim(ltrim(substr(sBuffer, iStart, LENGTH(sBuffer) - iStart)));
  ELSE
  sResult  := Rtrim(Ltrim(Substr(sBuffer, iStart, nPos1 - iStart)));
  iNextPos := nPos1 + 1;
  END IF;
END;
 
--------------------------------------------------------------------------------
   
FUNCTION NumberToString(pNumber IN NUMBER) RETURN VARCHAR2 IS
BEGIN
  IF (pNumber IS NULL) THEN
    RETURN '';
  ELSE
    RETURN TO_CHAR(pNumber);
  END IF;
EXCEPTION
  WHEN OTHERS THEN
    RETURN '';
END;




function emptytonull(pString in varchar2) return varchar2 is
begin
  return case when pString = '' then 'null' else pString end;
end;
   
--------------------------------------------------------------------------------
   
FUNCTION StringToNumber(pString IN VARCHAR2) RETURN NUMBER IS
BEGIN
    RETURN TO_NUMBER(pString);
EXCEPTION
  WHEN OTHERS THEN
    RETURN NULL;
END;
   
--------------------------------------------------------------------------------
   
  -- String format is as follows:
  --
  -- ,,,,
  --
  -- Where:
  --  := 
  --  := 
  --  := NULL | SDO_POINT_TYPE, OPTIONAL_NUMBER, OPTIONAL_NUMBER, OPTIONAL_NUMBER
  --  := NULL | SDO_ELEM_INFO_ARRAY, NUMBER, {OPTIONAL_NUMBER, ...}
  --  := NULL | SDO_ORDINATE_ARRAY, NUMBER, {OPTIONAL_NUMBER, ...}
  --
  --  := NULL | NUMBER
  -- entries in curly braces are repeated from n times
FUNCTION GeomToString(pGeom IN mdsys.sdo_geometry ) RETURN VARCHAR2 DETERMINISTIC IS
  vString VARCHAR(32767);
  vCount NUMBER;
BEGIN
  -- GTYPE
  vString := NumberToString(pGeom.SDO_GTYPE);
   
  -- SRID
  vString := vString || ',' || NumberToString(pGeom.SDO_SRID);
   
  -- POINT
  IF (pGeom.SDO_POINT IS NULL) THEN
    vString := vString || ',';
  ELSE
    vString := vString || ',"(' || NumberToString(pGeom.SDO_POINT.X) || ',' || NumberToString(pGeom.SDO_POINT.Y) || ',' || NumberToString(pGeom.SDO_POINT.Z) || ')"';
  END IF;
   
  -- ELEM_INFO
  IF (pGeom.SDO_ELEM_INFO IS NULL) THEN
    vString := vString || ',';
  ELSE
    vCount := pGeom.SDO_ELEM_INFO.COUNT;
    vString := vString || ',"{';
    FOR i IN 1..vCount LOOP
      if i > 1 then
        vString := vString || ',';
      end if;    
      vString := vString || emptytonull(NumberToString(pGeom.SDO_ELEM_INFO(i)));
    END LOOP;
    vString := vString || '}"';
  END IF;
   
  -- ORDINATES
  IF (pGeom.SDO_ORDINATES IS NULL) THEN
    vString := vString || ',';
  ELSE
    vCount := pGeom.SDO_ORDINATES.COUNT;
    vString := vString || ',"{';
    FOR i IN 1..vCount LOOP
      if i > 1 then
        vString := vString || ',';
      end if;
      vString := vString || emptytonull(NumberToString(pGeom.SDO_ORDINATES(i)));
    END LOOP;
    vString := vString || '}"';
  END IF;
   
  RETURN '(' || vString || ')';
END;
END pg_user_geom_util;
이것을 가지고, 옮겨감.
문제는 어떻게 옮겨갈 것인가인데, PosgreSQL 쪽에서는 oracle_fdw 뿐인듯하다.

PostGIS 기반 자료형들


PostgreSQL에서 sdo_geomery 자료를 PostGIS  자료로 바꾸기


PostGIS 자료형 입출력


PostGIS 인덱스 사용하기