GeoService-Xr, 무안군 공간정보 항공영상시스템 서비스에 적용

GeoService-Xr을 기반 서버로 하는 공개 소프트웨어인 NexGen은 어떠한 플러그인 없이 순수한 웹 기술만으로 고품질의 지도 표출과 공간 데이터를 활용한 다양한 분석 결과를 제공하는 제품이다.

NexGen의 주요 기능에 대한 소개는 아래의 이미지를 클릭하면 실제 시연 동영상을 통해 그 내용을 쉽게 이해할 수 있다.

NexGen은 GIS의 기본이 되는 기능을 높은 품질로 개발되어 매우 안정적이고 빠른 속도로 활용할 수 있으며, 이 기반 위에 업무에 필요한 기능을 추가하거나, 커스터마이징하여 개발 기간을 단축할 수 있다.

이러한 NexGen에 년도별로 구축된 다수의 항공영상을 시계열로 살펴볼 수 있는 기능을 중심으로 커스터마이징되어, 올해 하반기 무안군에 “공간정보 항공영상 시스템”으로 납품되어 업무에 활용되고 있으며 이에 대한 기사는 아래와 같다.

무안군, 고품질 대민행정 위한 항공영상시스템 본격 가동

또한 2018년 11월 말에는, NexGen 위에 CCTV를 공간정보시스템에서 매우 효과적으로 활용할 수 있는 기능을 개발 완료하여 임실군청에 솔루션으로써 납품되기도 하였다.

(위의 이미지의 CCTV 영상과 주소 등은 실제 상황과는 무관하며, 테스트를 위한 설정임)

위치기반 CCTV 관리 솔루션은 NexGen과 GeoService-Xr의 확장 기능으로써, 더 많은 관공서에서 손쉽고 빠르게 도입할 수 있도록 조달청에 등록할 예정이다. 참고로 GeoService-Xr은 이미 GS인증 1등급 제품이다.

FingerEyes-Xr의 GraphicLayer에 대한 최상위 지정(Top Most)

그래픽 레이어는 DBMS에 공간 데이터 저장을 위한 별도의 테이블을 준비하지 않아도, 자유로운 방식으로 지도 상에 원하는 위치 기반 데이터를 표시하고 이용할 수 있는 편의성과 유연성을 제공합니다. 그러다보니, 그래픽 레이어는 지도 상에 어떠한 요소보다 최상단에 표출되어 사용자에게 그 시인성을 확실하게 확인할 수 있는 것이 좋습니다. 아래의 화면은 CCTV를 지도 상에 표시하고 있는데, CCTV가 최상단에 노출되지 않음으로써 이러한 시인성 확보가 마련되지 않은 경우입니다.

FingerEyes-Xr에서 그래픽 레이어는 GraphicLayer라는 클래스를 통해 생성되는데, 아래처럼 간단히 topMost 매서드를 통해 최상단으로 지정될 수 있습니다.

var gl = new Xr.layers.GraphicLayer("cctv");

gl.topMost(true);
map.layers().add(gl);

결과적으로 아래처럼 해당 그래픽 레이어는 항상 최상단으로 표출됨으로써 그 시인성이 확보됩니다.

사용자를 위한 시인성 확보를 위한 방안으로 위처럼 기능으로써 개선될 수 있고, 디자인적으로는 그래픽 레이어에 표시되는 항목의 아이콘에 외곽선(Stroke)을 장식하여 훨씬 더 세련되고 강력하게 시인성을 확보할 수도 있습니다.

PostgreSQL의 데이터 백업 및 복원

# 백업

아래의 명령은 데이터베이스 명이 gis이며 테이블 중 ctprvn, sig, emd, road, li를 디렉토리 형식(–foramt=d)로 하여 gis_db_dump라는 파일로 백업하는 명령입니다.

pg_dump --dbname=gis --host=localhost --port=5432 --table=ctprvn --table=sig --table=emd --table=road --table=li --username=postgres --password --format=d --file=./gis_db_dump

참고로 –table 옵션을 지정하지 않으면 지정된 데이터베이스 전체 테이블을 백업합니다.

Windows에서 일반 text(sql 형식)으로 백업 받는 경우의 명령으로 관리자 권한으로 cmd를 실행한다. 아래의 명령에 의해 gis 데이터베이스의 모든 테이블의 데이터를 db.sql 파일이 생성되어 백업된다.

pg_dump --dbname=gis --username=postgres --file=d:/__Temp__/db.sql

# 복구

또한 아래는 위에서 백업한 내용을 gis_20181022 데이터 베이스로 복원시키는 명령입니다.

pg_restore --dbname=gis_20181022 --host=localhost --port=5432 --username=postgres --password --format=d ./gis_db_dump

리눅스에서 실행하기 위해서는 su – postgres 를 실행하여 권한을 얻을 필요가 있습니다. pg_dump 실행 시 –format을 지정하지 않으면 텍스트 형식으로 덤프 파일이 만들어지는데 이 덤프 파일은 pg_restore를 통해 복원하지 않고 psql을 통해 복원합니다. 즉, psql를 실행하여 데이터베이스에 연결하고(\c) 덤프 파일을 실행(\i) 합니다.

GeoService-Xr과 FingerEyes-Xr을 이용한 대용량 파일 업로드 및 다운로드 서비스

사용자의 요구사항을 충분히 반영할 수 있는 GIS 시스템을 개발할 때, 대용량 데이터를 서버 측에 업로드해야 할때가 있습니다. 예를 들어, 최신 지적도를 서버측에 올려 최신 지적도 서비스를 업데이트하거나, 사용자가 보유한 빅데이터를 CSV나 Excel 형태로 서버에 올려 서버측에서 분석을 한 뒤에 그 결과를 공간상에 시각화하는 등이 있는데요. 이러한 대용량 파일을 업로드할 수 있는 기능을 웹에서 쉽게 적용할 수 있다면, 웹 환경에서 어떠한 제약없이 자연스럽게 기능을 개발하고 사용자에게 제공할 수 있습니다. 특히 네트워크 속도와 하드웨어로써의 서버의 발전으로 이러한 웹 기반의 환경은 모든 클라이언트 프로그램의 기반, 기본이 될 것입니다.

이러한 대용량 데이터를 서버에 업로드할 수 있는 기능을 GeoService-Xr과 FingerEyes-Xr을 이용해 쉽게 적용할 수 있는데요. 이러한 대용량 파일 업로드에 대한 API에 대한 정리가 이 글의 주제입니다. 대용량은 아니지만 이미지 파일을 서버측에 업로드 하는 것을 목표로 글을 정리합니다. 이미지가 대용량은 아니지만 이 글에서 설명하는 방법은 서버의 OS가 허락하는 파일 시스템의 최대 용량을 지원합니다.

먼저 아래의 JS 코드로 이미지를 업로드하기 위한 UI를 구성합니다.




위의 JS 코드는 아래와 결과 화면을 생성합니다.

위의 UI에 대한 기능은 아래와 같이 사용자의 3가지 행위를 지원합니다.

  1. Select Image 버튼을 클릭해 사용자의 이미지 파일을 선택하면,
  2. 아래에 선택된 이미지가 표시됩니다.
  3. 그리고 Upload Image 버튼을 클릭하면 서버측에 이미지가 업로드된다.

위의 기능을 구현하기 위한 JS 코드는 아래가 전부입니다.

SelectImageFileUI.onclick = function () {
    ImageFileUI.click();
};

ImageFileUI.onchange = function () {
    if (ImageFileUI.files && ImageFileUI.files[0]) {
        var reader = new FileReader();

        reader.onload = function (e) {
            ImageUI.setAttribute("src", e.target.result);
        }

        reader.readAsDataURL(ImageFileUI.files[0]);
    }
};

UploadImageFileUI.onclick = function () {
    var imageFile = ImageFileUI.files[0];

    if (imageFile) {
        var args = {
            id: 0,
            server: "http://localhost:7777",
            imageFile: imageFile,
            savedFileName: Date.now() + "." + imageFile.name.split('.').pop(),
            uploadDir: "CCTV_IMAGES",

            onCompleted: function (id) {
                alert('completed');
            },

            onFailed: function (id) {
                alert('failed');
            },

            onProgress: function(id, percent) {
              //.
            }
        };

        if (false == Xr.OperationHelper.uploadFile(args)) {
            alert("인자가 옳바르지 않습니다.");
        }
    } else {
        alert("업로드할 파일을 선택하세요.");
    }
};

사용자의 3개의 행위에 대한 3개의 이벤트에 대한 코드로 구성되는데요. 먼저 Select Image 버튼을 클릭하면 실행되는 코든느 1-3번코드입니다. hidden으로 설정된 File Input 컨트를의 click 이벤트를 트리거(trigger, 개발자가 코드를 통해 이벤트를 발생해 주는 것)해주는게 전부입니다. 사용자가 이미지 파일을 선택하면 id가 ImageFileUI인 File Input 컨트롤에 onchange 이벤트가 발생하는데, 이 이벤트의 코드는 5-15번입니다. FileReader 객체를 이용해 파일의 내용을 읽고, 내용을 다 읽으면 10번 코드에서 이미지를 화면에 표시합니다. 그리고 Upload Image 버튼을 클릭하면 실행되는 코드는 17-47번입니다. 41번의 Xr.OperationHelper.uploadFile 함수 호출을 위한 인자(Argument)들을 준비하고 실행하고 있습니다. 이 uploadFile 함수를 호출하기 위해 전달되는 인자에는 업로드 Task에 대한 id, 서버의 주소(server), 사용자가 선택한 이미지 파일의 객체(imageFile), 서버측에 저장될 파일명(savedFileName), 파일이 저장될 서버측 디렉토리(uploadDir) 등 이고, 서버측에 업로드가 성공적으로 완료되면 호출되는 이벤트와 업로드가 실행하면 호출되는 이벤트인 onCompleted와 onFailed, 그리고 업로드 진행율에 대한 onProgress 콜백 함수이며, 이 이벤트는 필수가 아닌 옵션입니다.

덧붙여 GeoService-Xr은 서버 측에 파일을 올리기도 하는 기능 뿐만 아니라, 서버 측에서 파일을 내려 받기 위한 서비스도 제공 하는데요. 아래는 url 호출의 예는 서버측에 존재하는 2323123.pdf 파일을 myFile.pdf라는 파일로 다운로드 받습니다.

다운로드

또한 서버 측에 존재하는 여러개의 파일을 압축하여 하나의 zip 파일로 내려 받기 위한 url 호출 서비스의 예는 아래와 같습니다.

즉, 서버측에 존재하는 a.hwp, b.hwp, c.hwp에 대한 3개의 파일을 single.zip으로 압축하여 내려 받습니다.

FingerEyes-Xr에서 GraphicLayer의 Row 순회 및 MBR 얻기

GraphicLayer의 구성요소들을 순회하는 방법은 for 문을 통해 쉽게 해결할 수 있습니다. 즉, 아래의 예와 같습니다.

var targetGl = map.layers("sketch_grp");

for (var id in targetRows) {
    var row = targetRows[id];

    ...
}

그래픽 레이어를 구성하는 그래픽 요소를 순회하면서 해당 그래픽 요소의 MBR을 얻어야 하는 경우가 많이 생깁니다. 위의 코드에서 row 변수의 MBR 함수를 통해 쉽게 MBR을 얻을 수 있는데, FingerEyes-Xr에서는 그래픽 요소의 정확한 MBR을 얻기 위해서 약간의 특이점이 발생합니다. 이 특이점으로 인해 혼란스러움이 있어 이 글을 통해 정리합니다. 즉, Text 형태의 그래픽 요소 등과 같은 정확한 MBR을 얻기 위해서는 CoordMapper와 그래픽 요소(SVG)를 담고 있는 DOM 컨테이너를 인자로 넣어줘야 합니다. 즉, 아래의 예제 코드와 같습니다.

var targetGl = map.layers("sketch_grp");
var cm = map.coordMapper();
var svgContainer = targetGl.container();

for (var id in targetRows) {
    var row = targetRows[id];
    var mbrTarget = row.MBR(cm, svgContainer);

    ...
}

FingerEyes-Xr의 소스코드는 GitHub에서 다운로드 받으실 수 있습니다.