[GIS] 오픈소스, 자바스크립트 좌표계 변환 라이브러리, proj4js

C언어 기반의 좌표계 변환을 자바스크립트(Javascript) 언어로 그대로 포팅한 proj4js에 대한 사용에 있어 간단한 예를 통해 정리해 보려고 합니다. 아직 다양한 좌표계 간의 상호 변환에 적용해 보지는 않았으나 OpenLayers와 같은 오픈소스에서 사용하는 좌표계 변환 API이므로 이미 검증은 되었다고 판단할 수 있습니다.

먼저 proj4js는 http://trac.osgeo.org/proj4js/ 에서 다운로드 받을 수 있고.. 저는 여기서 WGS84 경위도를 Bessel 타원체 경위도로 변환하는 것과.. WGS84 타원체 경위도를 카텍(Katec) TM 직각 좌표계로 변환하는 것에 대한 2가지 예를 정리해 봅니다. 먼저 WGS84 경위도를 Bessel 경위도로 변환하는 코드입니다.




코드를 설명하면.. 1번 코드는 proj4s 라이브러리를 사용할 수 있도록 가져오는 것입니다. 그리고 4번 코드는 proj4js를 사용함에 있어서 오류가 있다면 오류에 대한 메세지를 표시합니다. 여기서는 alert 함수를 사용하여 메세지 창으로 표시하도록 하였습니다. 그리고 5번과 6번이 proj4의 좌표를 정의하는 문자열입니다. 그리고 9번과 10번은 변환을 위한 Proj 객체를 생성합니다. 12번 코드는 변환할 좌표입니다.

13번이 실제로 좌표계를 변환하는 transfrom 함수입니다. 이 함수의 첫번째 인자는 원본 좌표계이고 두번째는 변환되어질 좌표계입니다. 세번째는 변환할 좌표인데.. 변환이 성공하면 다시 이 인자에 결과가 저장됩니다. 주의할 점은 서로 다른 타원체 간의 변환이므로 5번과 같이 towgs84 파라메터를 반드시 지정해야 합니다. 또한 동일한 타원체 간의 변환에서는 towgs84를 지정해서는 않됩니다. 여기서는 3개의 파라메터를 사용하였으나 보다 정확한 변환을 위해 다른 파라메터를 사용하셔도 됩니다. 다음으로 WGS84 경위도를 카텍으로 변환하는 코드입니다.




중요한 부분은 6번에서 카텍에 대한 좌표계 정보에 대한 문자열 값입니다. 또한 towgs84 값을 반드시 지정하였는데.. 이유는 서로 다른 타원체 간의 변환이기 때문입니다. 즉, WGS84 타원체에서 카텍이 사용하는 타원체인 Bessel의 변환이기 때문입니다. 만약 서로 동일한 타원체 간의 변환이라면 towgs84 파라메터를 지정해서는 않됩니다.

Apache Commons IO 라이브러리

아파치 소프트웨어 재단에서 제공하는 자바 기반의 오픈소스 중  Commons IO 라이브러리가 있습니다. 얼마전 고객과의 미팅을 통해 요구사항을 파악하던 중.. 예상치 못한 기능을 요청받았고 이 기능을 기존 시스템에 추가하려면 상당한 코딩을 해야 할 상황에서 요긴하게 사용한 라이브러리 중에 하나입니다.

사용자 삽입 이미지
이  Commons IO 라이브러리는 기존의 JDK에서 제공하는 클래스들에 대해서 자주 사용하는(Commons 한) 기능들을 클래스화 해 놓은 것으로 이해하면 크게 틀리지 않습니다. 물론 기존의 JDK에서 제공되는 클래스들에 대해 보다 개선된 클래스를 제공하고 있기는 하지만 말입니다.

이 글은 Commons IO 라이브러리의 공식 홈페이지에서 매우 간단한 예제를 통해 해당 라이브러리를 소개하고 있는 글을 한글로 정리한 것입니다. 사실.. 제가 이 Commons IO를 직접 사용한것이 아니고 대용량 파일을 서버로 업로드하는 기능을 제공하는 아파치 소프트웨어 재단에서 제공하는 또 다른 Commons Fileupload 라이브러리에서 이 Commons IO 라이브러리를 사용하는 것을 보고 관심을 가져 보게 되었고 실제 기본적인 내용을 살펴보게 되었습니다.

먼저 해당 URL에 대한 데이터 전체를 문자열로 가져오는 예입니다.

InputStream in = null;
try {
    in = new URL("href='http://www.google.co.kr").openStream();
    System.out.println(IOUtils.toString(in));
} catch (MalformedURLException e) {
     e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    IOUtils.closeQuietly(in);
}

JDK에서 제공하는 기본적인 클래스들을 이용해 위의 코드를 작성했다면 더 긴 코드가 필요했을테고.. 코드가 길다는 것은 그만큼 문제가 발생할 가능성이 높다는 것입니다. 아파치의 Commons IO는 수많은 프로그램에서 사용된 검증된 라이브러리입니다.

다음은 텍스트 파일을 라인별로 읽어 List 컨테이너로 파싱하는 예입니다.

File file = new File("c:/text.txt");
try {
    List lines = FileUtils.readLines(file, "utf-8");
    Iterator it = lines.iterator();
    while(it.hasNext()) {
        System.out.println(it.next());
    }
} catch (IOException e) {
    e.printStackTrace();
}

매우 직관적이고 심플합니다.. 하지만 대용량의 텍스트 파일의 경우 모든 데이터를 읽어 하나의 리스트 컨테이너에 집어 넣으니 메모리 문제가 발생할 수 있다는 점을 염두해 둬야 합니다.

다음은 텍스트 파일을 구성하는 라인을 하나 하나 필요할때마다 읽어 처리할 수 있는 예입니다.

File file = new File("c:/text.txt");
LineIterator it = null;
try {
    it = FileUtils.lineIterator(file);
    while(it.hasNext()) {
        System.out.println(it.next());
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if(it != null) {
        LineIterator.closeQuietly(it);
    }
}

다음은 파일 경로를 정규화해주는 코드입니다.

 String filename = "c:/commons/io/../../lang/../project.txt";
  String normailzed = FilenameUtils.normalize(filename);
  System.out.println(normailzed);

위 코드의 결과는 c:/project.txt가 됩니다.

그리고 해당 경로에 대한 저장 장치의 가용 용량을 얻는 코드입니다.

try {
    long freeSpace = FileSystemUtils.freeSpaceKb("c:/");
    System.out.println(freeSpace + "kb");
} catch (IOException e) {
    e.printStackTrace();
}

이 외에도 파일의 복사, Endian 처리, 다양한 응용 스트림 클래스 등을 제공합니다. 이 처럼 일반적으로 많이 사용되는 IO 루틴에 대해서 더 짧은 코드를 통해서, 그리고 검증된 방법을 통해 구현할 수 있는 라이브러리를 사용한다면 원하는 기능을 더욱 빠르고 안정적으로 추가 개발할 수 있습니다. 이러한 라이브러리를 미리 알고 있는 것으로 개발자의 역량은 기술면에서 더 높아질 것입니다.

[GIS] 가벼운 좌표 변환 Java 오픈소스

오픈소스를 검색하고 사용할만하다.. 라는 항목 중에 하나가 “가볍다”입니다. 필요한 기능은 “딱” 하나인데.. 이 하나의 기능을 사용하기 위해 다른 라이브러리가 덕지 덕지 붙어야만 하는 라이브러리는 아무리 기능이 좋아도 사용할 수 없는 상황(모바일 GIS 개발)입니다..

GPS로부터 받은 WGS84 경위도좌표를 우리나라에서 흔히 사용하는 좌표계로 변환하기 위한 자바 기반의 오픈소스를 검색하던 중.. 가벼운 녀석을 찾았고.. 다시 몇일 동안의 검증을 통해 사용하기로 결정된 좌표변환 오픈소스가 바로 Java Map Projection Library입니다.

이 오픈소스가 참조하고 있는 또 다른 오픈소스 라이브러리는 행렬계산을 위한 Jama라는 오픈소스 라이브러리입니다. Jama는 이미 행렬계산을 위해 제가 예전부터 사용하고 있던터라.. 문제가 없었습니다. 아래는 간단히 경위도 좌표를 카텍 좌표계로 투영하는 코드의 예입니다.

import java.awt.geom.Point2D;
import com.jhlabs.map.proj.Projection;
import com.jhlabs.map.proj.ProjectionFactory;

public class EntryMain { 
    public static void main(String[] args) {
        String[] params = {
            "+proj=tmerc",
            "+lat_0=38N",
            "+lon_0=128E",
            "+ellps=bessel",
            "+x_0=400000",
            "+y_0=600000",
            "+k=0.9999",
            "+unit=m"
        };
  
        Projection katec = ProjectionFactory.fromPROJ4Specification(params);
        Point2D.Double pKatec = new Point2D.Double();
        katec.transform(128, 38, pKatec);
        System.out.println("result(" + pKatec.getX() +", " + pKatec.getY() +")");
    }
}

경도 128, 위도 38이 (400000, 600000)으로 투영되는 카텍좌표 투영에 대한 정보에 대한 인자로써 7~16라인의 코드에 params라는 문자 배열로 정의하고 있습니다. 이러한 인자값은 C언어 기반의 PROJ.4의 인자.. 바로 그것입니다. PROJ.4를 유용하고 사용하고 있는 저에게는 매우 반가운 방법입니다. 실제로 투영을 위한 Projection이라는 클래스의 객체를 생성하는 코드는 18번으로 앞서 정의한 인자값을 인자로 받습니다. 그리고 경도 128, 위도 38에 대한 좌표 변환은 20번 코드이고.. 21번 코드를 통해 화면에 출력해 확인하고 있습니다.

사용자 삽입 이미지

네, 위의 화면이 실행결과입니다. 그런데 석연치 않은 부분이 있습니다. 바로 20번 코드의 transform의 입력 인자로 받은 경도 128, 위도 38에 해당하는 타원체가 무엇인지에 대한 것입니다. WGS84 타원체일까… 아니면 Bessel 타원체 일까… 답은 Bessel인데요. 이유는 Projection 클래스의 객체를 생성하기 위해 정의했던 인자들 중 11번째 코드의 +ellips=bessel에 따릅니다. 그렇다면 앞서 말씀드렸던 목표.. GPS에서 수신받은 좌표체계인 WGS84 경위도가 아니라는 것입니다.

그렇다면 WGS84 경위도좌표계를 Bessel 경위도좌표계로 변환하고 이렇게 변환된 경위좌표를 카텍으로 변환해 주면 되겠군요. 하지만 여기서 문제는 이 Java Map Porjection 오픈소스가 타원체 간의 경위도 좌표 변환을 지원하지 않는다는 점입니다. 여기에 대한 해결점은 또 다른 포스팅(타원체간의 경위도 좌표계 변환 오픈소스 라이브러리)을 통해 공유해 드리겠습니다.

[GIS] JTS Geometry Class Diagram

JTS는 자바로 잘짜여진 지오메트리 공간연산 기능을 제공하는 오픈소스입니다. 세상에 나온지 수년이 지났고 워낙 잘짜여진터라 C언어로도 포팅되졌습니다. 위의 UML을 그려본 이유는 최근에 자바언어로 ESRI의 SHP 파일과 DBF 파일을 읽고 쓸 수 있는 라이브러리를 오픈소스 차원에서 개발할때 JTS에서 제공하는 Geometry 타입을 사용하기 위함입니다. 자바언어로 SHP와 DBF 파일을 읽는 오픈소스를 찾아보았으나 너무 다른 라이브러리에 깊이 관계를 맺고 있어 최적화시켜 사용하기가 제겐 부담이 되어 이번 기회에 새롭게 만들어 오픈소스 형태로 공유해볼 생각입니다.