FingerEyes-Xr에서 코드를 통한 지도 이동시 깜빡거리는 현상 제거

FingerEyes-Xr은 웹 기반에서 공간 데이터를 편집할 수 있도록 도형 데이터를 클라이언트에서 직접 렌더링하여 표시합니다. 즉 배경지도는 Image로 표시하고 그외 수치지도 레이어는 SVG와 같은 그래픽 요소로 표시됩니다.

이러한 구조로 인해 마우스 등을 통한 지도 이동 시에는 문제가 없으나, 코드를 통한 지도 이동시에 수치지도 레이어가 깜빡 거리는 현상이 발생할 수 있습니다. 그러나 움직이는 물체를 지도에서 추적하거나 GPS 등을 이용해 내 위치를 중심으로 지도를 이동시키는 등의 기능을 개발할때 이러한 깜빡거리는 현상은 사용자에게 좋지 않은 경험을 제공합니다.

그러나 FingerEyes-Xr에서도 움직이는 물체나 GPS 등을 통한 지도 이동 등과 같은 기능에서 자연스럽게 지도를 깜빡임없이 이동시키는 기능의 구현이 가능합니다. 이럴때 사용할 수 있는 코드는 아래와 같습니다.

function smoothMapMoveByPixel(map, px, py) {
    var cm = map.coordMapper();

    cm.moveMapByViewOffset(px, py);
    map.update(Xr.MouseActionEnum.MOUSE_DRAG_END, px, py);
}
                        
function smoothMapMove(map, newX, newY) {
    var cm = map.coordMapper();
    var prePt = cm.currentCenter();

    cm.moveTo(newX, newY);

    var deltaX = cm.viewLength(newX - prePt.x);
    var deltaY = cm.viewLength(newY - prePt.y);

    if (newX > prePt.x) deltaX = -deltaX;
    if (newY < prePt.y) deltaY = -deltaY;

    map.update(Xr.MouseActionEnum.MOUSE_DRAG_END, deltaX, deltaY);
}

함수가 2개인데요. 먼저 smoothMapMoveByPixel는 픽셀 단위값 만큼 지도를 자연스럽게 이동시키는 함수이고, smoothMapMove는 이동하고자 하는 위치를 지도의 절대좌표값으로 해서 자연스럽게 이동하는 함수입니다. 이중 smoothMapMove 함수의 사용예는 아래와 같습니다.

setInterval(function () {
    var map = ...;
    var cm = map.coordMapper();
    var prePt = cm.currentCenter();
    var newX = prePt.x + 2; 
    var newY = prePt.y - 2;

    smoothMapMove(map, newX, newY);
}, 100);

위의 코드는 0.1초마다 지도를 우측하단으로 x와 y축 모두에 대해 2미터만큼 이동시키는 것으로, 실제 시스템에 적용한 결과 화면은 아래와 같습니다.

보시는 것처럼 깜빡거림없이 자연스럽게 지도가 이동하는 것을 볼 수 있는데요. 만약, 구성 레이어가 많거나 해서 깜빡거림이 여전이 발생한다면 지도 이동 반복 주기에 대한 시간을 늘려 테스트 해보기 바랍니다.

[FingerEyes-Xr] 이미지를 레이어로 추가하기

간단히 하나의 이미지를 지도를 구성하는 레이어로 추가하기 위한 코드에 대한 설명입니다. 예를 들어 아래처럼 TMS 등과 같은 방식으로 추가된 배경지도 위에 하나의 이미지를 중첩하고자 할때 사용할 수 있는 기능입니다.

코드의 예는 아래와 같습니다. url을 통해 중첩하고자 하는 이미지의 웹 url을 지정하고, 이미지에 대한 MBR을 minX, minY, maxX, maxY로 지정해 레이어를 생성해 추가해 주면 됩니다.

var lyr = new Xr.layers.ImageLayer(
    "imageLyr",
    {
        url: "http://www.gisdeveloper.co.kr:8080/download/map.jpg",
        minX: 150613,
        minY: 246114,
        maxX: 151129,
        maxY: 246633,
    }
);

map.layers().add(lyr);
map.update();

결과는 아래와 같습니다. 지정된 위치에 지도가 표시됩니다.

이 기능은 드론등과 같은 장치를 이용해 특정 지역에 대한 지도를 변경할때 간단히 사용할 수 있는 기능입니다.

FingerEyes-Xr for HTML5의 레이어 추가시 연결 완료 이벤트

FingerEyes-Xr에서 레이어를 추가하기 위한 매서드는 LayerManager의 add 입니다. 이 add 매서드는 2개의 인자를 받는데요. 첫번째는 추가하고자 하는 Layer 객체이고 두번째는 선택 사항으로 추가하려는 레이어가 네트워크를 통해 성공적으로 연결되었을 때 호출할 callback 함수입니다. 이 callback 함수는 레이어 추가시 개별적으로 세밀한 흐름 제어를 위해 사용됩니다. 이와 관련한 예는 아래와 같습니다.

var baseLyr = new Xr.layers.TileMapLayer(layerName,
    {
        url: "http://localhost/....",
        ext: "jpg"
    }
);

var lm = map.layers();

lm.add(baseLyr, 
    function (lyr) {
        lm.moveToFirst(layerName);
        map.updateLayer(layerName);
    }
);

위의 코드는 배경지도를 새롭게 추가할 때 추가된 레이어를 가장 첫번째 순위로 이동하고 추가된 레이어만을 새롭게 화면상에 업데이트하라는 코드입니다. 이러한 코드는 해당 레이어가 네트워크를 통해 완전히 연결된 이후에 실행되어야 하는 코드이므로 callback 함수의 사용에 적합합니다.

FingerEyes-Xr의 DeferableShapeDrawTheme 추상 클래스

FingerEyes-Xr for HTML5는 DeferableShapeDrawTheme 추상 클래스를 제공합니다. Deferable은 “미룰 수 있는”이라는 의미인데요. 수치지도 레이어를 그리기 위한 심벌의 지정을 미룬다는 의미입니다. 이 추상 클래스는 상속 받아 구현해야 하는 매서드는 /* void */ requestCondition: function (/* int */fid)입니다. 이 클래스의 목적에 대한 설명은 잠시 미루고 이 클래스를 상속받아 만든 예로써 MyDeferableShapeDrawTheme 클래스는 아래와 같습니다.

MyDeferableShapeDrawTheme = Xr.Class({
    name: "MyDeferableShapeDrawTheme",
    extend: Xr.theme.DeferableShapeDrawTheme,

    construct: function() {
        this.superclass();

        this._ON_sds = new Xr.symbol.ShapeDrawSymbol();
        this._ON_sds.markerSymbol(new Xr.symbol.ImageMarkerSymbol(
            { width: 16, height: 16, url: "images/gis/facility/OH_SW_ON.png" }));

        this._OFF_sds = new Xr.symbol.ShapeDrawSymbol();
        this._OFF_sds.markerSymbol(new Xr.symbol.ImageMarkerSymbol(
            { width: 16, height: 16, url: "images/gis/facility/OH_SW_OFF.png" }));
    },

    methods: {
        /* void */ requestCondition: function (fid) {
            var that = this;
            var sql = "SELECT swstatcd FROM ecl_sw_p WHERE fid = " + fid;
            var url = mg_MapLayers.GIS_HTTP_SERVER + "/Xr?sql|" + encodeURIComponent(sql) + "|mg_db|1"

            $.ajax({
                url: url,
                dataType: "text",
                type: "GET",
                statusCode: {
                    200: function (response) {
                        // response 문자열 끝에 \0 문자를 제거
                        response = response.substr(0, response.length - 1); 

                        var result = JSON.parse(response);
                        if (result.length == 1) {
                            if (result[0]["swstatcd"] === "ON") {
                                that.setSymbol(fid, that._ON_sds);
                            } else {
                                that.setSymbol(fid, that._OFF_sds);
                            }
                        }                        
                    }
                }
            });            
        }
    }
});

위의 코드는 순수한 Javascript 코드이며, FingerEyes-Xr 방식의 클래스 정의입니다. 위의 코드에서 보이는 것처럼 3번 코드에서 Xr.theme.DeferableShapeDrawTheme 클래스는 확장한다고 명시되어 있고, 18번 코드에서 requestCondition 매서드를 구현하고 있습니다. 5번 코드가 생성자 함수인데요. 이 생성자에서는 2개의 이미지 심벌을 정의하고 있습니다. 이 2개의 이미지 심벌은 설비 따위에 대한 상태값으로 ON 상태의 이미지와 OFF 상태의 이미지를 나타냅니다. 이렇게 정의된 심벌은 18번 코드의 매서드인 requestCondition에서 사용되는데요. 이 requestCondition은 이름 그대로 “조건을 요청한다”라는 매서드로 그 구현 코드를 보면 어떤 SQL문을 서버측에 요청하고 있습니다. 그 요청 결과는 JSON으로 받게 되는데요. 34~38번 코드에서처럼 JSON 결과 중 swstatcd 필드의 값이 ON일때와 그 외의 값일때에 대해 setSymbol 매서드를 호출해 표현할 심벌을 지정해 줍니다.

이렇게 정의된 MyDeferableShapeDrawTheme 클래스는 수치지도 레이어를 생성하고 추가할때 사용되는데요. 아래의 예와 같습니다.

var lyr = new Xr.layers.ShapeMapLayer(layerId, ...);
lyr.deferableTheme(new MyDeferableShapeDrawTheme());

이해를 돕고자 위의 코드가 적용된 실행 결과에 대한 지도 화면은 다음과 같습니다.

위의 지도 화면에서 파랑색의 ON 아이콘과 빨간색의 OFF 아이콘 심벌이 위의 코드를 통해 반영된 심벌 결과입니다.

그렇다면 이 클래스의 제공하는 이유는 무엇일까요? 일반적으로 GIS의 DB 구조를 살펴보면 공간 데이터와 속성 데이터가 동일한 DBMS의 동일한 Database 내에 존재하게 됩니다. 그러나 전체 시스템이 커지거나, 타 시스템와의 연계가 필요할 경우 도형 데이터와 속성 데이터가 전혀 다른 DBMS로 분리 구분되어 저장됩니다. 바로 이러한 환경에서도 공간 데이터와 다른 DBMS의 속성 데이터가 접목되어 유연하게 조합되어 활용될 수 있도록 하기 위해 제공되는 클래스가 바로 DeferableShapeDrawTheme입니다.

끝으로, ShapeDrawSymbol 클래스를 통한 심벌 정의 시에 Brush와 Pen에 대한 심벌의 정의 예는 아래와 같습니다.

this._ON_sds = new Xr.symbol.ShapeDrawSymbol();
this._sds.brushSymbol(new Xr.symbol.BrushSymbol({ color: 'red' }));
this._sds.penSymbol(new Xr.symbol.PenSymbol({ color: 'yellow', width: 2 }));
this._ON_sds.markerSymbol(new Xr.symbol.ImageMarkerSymbol(
    { width: 16, height: 16, url: "images/gis/facility/OH_SW_ON.png" }));