[GIS] FingerEyes-Xr for Flex, ShapeMapLayer의 filterFunction 기능

수치지도 데이터를 처리하는 레이어인 XrShapeMapLayer는 filterFunction이라는 콜백함수 기능을 제공합니다. 이 기능은 공간 서버로부터 공간 데이터를 가져오고 화면에 표시하기 전에 가져온 공간 데이터에 대한 선처리(Preprocessing)을 수행하는 용도로 사용됩니다. 아래의 코드는 XrShapeMapLayer에 filterFunction 콜백함수를 지정하는 코드입니다.

var layer:XrShapeMapLayer = null;
layer = map.layers.getLayer("LAYER_NAME") as XrShapeMapLayer;
layer.filterFunction = filterFunction;

여기서 filterFunction은 다음과 같은 인자를 갖습니다. FingerEyes-Xr 버전 2.2를 기준으로 다릅니다.

ㅡ FingerEyes-Xr version 2.2 이상

// FingerEyes-Xr version 2.2 이상
private function filterFunction(fid:uint, 
    shpLyr:XrShapeMapLayer, index:uint, cntRows:uint):void
{
    // ...
}

ㅡ FingerEyes-Xr version 2.2 미만

// FingerEyes-Xr version 2.2 미만
private function filterFunction(fid:uint, 
    shpSet:XrShapeSet, attSet:XrAttributeSet):void
{
    // ...
}

이 기능은 다양한 용도로 사용될 수 있습니다. 예를 들자면 어떤 기준에 부합되지 않으면 공간 데이터를 화면에 그리기 전에 공간 데이터를 제거함으로써 화면에 표시하지 않는 경우입니다.

ㅡ FingerEyes-Xr version 2.2 이상

// FingerEyes-Xr version 2.2 이상
private function filterFunction(fid:uint, 
    shpLyr:XrShapeMapLayer, index:uint, cntRows:uint):void 
{
    var shpSet = shpLyr.shapeSet;
    var attSet = shpLyr.attributeSet;
    var attr:XrAttribute = attSet.rows[fid];
    var bBeDelete:Boolean = true;
    var from:int, to:int, value:int;

    value = attr.getValueAsInt(13);    
    from = this.nsDeathFrom.value;
    to = this.nsDeathTo.value;

    if(value >= from && value <= to) bBeDelete = false;

    f(bBeDelete) {
        shpSet.removeRow(fid);
        attSet.removeRow(fid);
    }
}

ㅡ FingerEyes-Xr version 2.2 미만

// FingerEyes-Xr version 2.2 미만
private function filterFunction(fid:uint, 
    shpSet:XrShapeSet, attSet:XrAttributeSet):void
{
    var attr:XrAttribute = attSet.rows[fid];
    var bBeDelete:Boolean = true;
    var from:int, to:int, value:int;

    value = attr.getValueAsInt(13);    
    from = this.nsDeathFrom.value;
    to = this.nsDeathTo.value;

    if(value >= from && value <= to) bBeDelete = false;

    f(bBeDelete) {
        shpSet.removeRow(fid);
        attSet.removeRow(fid);
    }
}

위의 코드는 공간 데이터에 대한 13번째 속성값이 정해진 범위값에 들어가지 않을 경우 그리기 전에 제거함으로써 화면에 표시하지 않도록 하는 코드예입니다.

[GIS] FingerEyes-Xr에서 공간 데이터 편집에 대한 정리(Bridge Edit)

(주의) 이 글은 현재 핑거아이즈의 Bridge Edit 방식을 설명한 글입니다. 코드 작성을 위한 효율성을 목적으로 이 글의 내용이 실제 API와 달라질 수 있습니다. 이 글은 FingerEyes-Xr 버전 2.7을 기준으로 합니다.

핑거아이즈(FingerEyes-Xr)는 웹에서 공간 데이터를 편집할 수 있는 강력한 기능을 지원합니다. 편집에 대한 Undo/Redo는 물론 Vertex와 Segment에 대한 스냅핑(Snapping) 기능을 지원함으로써 편집에 대한 편리성을 사용자에게 제공합니다.

핑거아이즈는 공간 데이터 편집에 대해서 Direct Edit 방식과 Bridge Edit 방식을 제공합니다. Direct Edit은 공간 데이터를 편집하면 곧바로 DBMS에 반영되는 방식이고 Bridge Edit 방식은 편집된 공간 데이터를 곧바로 DBMS에 저장하지 않고 저장하기 중간 단계에 사용자의 확인을 통해 저장하는 방식입니다. Direct Edit 방식은 Bridge Edit 방식에 비해 개발이 쉽다는 장점이 있으나 편집을 위해 사용자의 계정 정보가 필요하다는 번거로움이 있습니다.

편집 대상이 되는 공간 데이터는 DBMS에 저장되어 있으며 실제 공간 데이터를 사용자에게 보여주기 위해 레이어(Layer)라는 개념을 통해 표현됩니다. DBMS에 연계된 공간 데이터에 대한 레이어가 XrShapeMapLayer입니다. Direct Edit 방식은 이 XrShapeMapLayer에 대해 바로 편집하며 DBMS에 반영시키는 것입니다. 이 글에서 설명할 Bridge Edit 방식은 XrShapeMapLayer를 바로 편집하는 것이 아니라 XrMashupLayer를 브리지(Bridge) 개념으로 하여 원하는 형태의 정보로 편집이 끝나면 DBMS에 반영하는 방식입니다.

이 포스트는 Bridge Edit 방식을 통해 공간 데이터를 편집하기 위한 내용을 정리한 글입니다.

먼저 편집할 공간 데이터에 대한 XrShapeMapLayer와 브리지(Bridge) 역활을 XrMashupLayer가 필요합니다. XrShapeMapLayer에 해당하는 객체를 _shapeLyr이라고 하고 XrMashupLayer에 해당하는 객체를 _mashupLyr이라고 하면 편집을 위한 초기화 코드는 다음과 같습니다.

_map.edit.targetLayer = _mashupLyr;
_map.addEventListener(XrMapMouseEvent.XRMAP_MOUSE_CLICK_EVENT, onMapClick);
_map.addEventListener(
    XrEditFeatureAddedNewEvent.XR_EDIT_FEATURE_ADDED_NEW_EVENT, onAddNewEvent);
_map.edit.editMode = false;

이러한 초기화 이후에 편집 기능을 구현할 수 있으며, 편집은 다음과 같은 기능으로 분류할 수 있습니다.

  1. 새로운 도형 추가
  2. 기존 도형의 편집
  3. 기존 도형의 삭제

1. 새로운 도형 추가

새롭게 편집할 도형이 포인트라고 할때, 새로운 도형포인트을 추가하기 위한 시작 코드는 다음과 같습니다.

_map.edit.editMode = true;
_mashupLyr.removeAllMashups();
_map.edit.addPoint(0);

위의 코드를 통해 사용자는 지도 위에 도형을 새롭게 그릴 수 있고 도형 그리기가 완료되면 편집을 위한 초기화 코드에서 추가한 이벤트인 XR_EDIT_FEATURE_ADDED_NEW_EVENT에 대한 실행 함수인 onAddNewEvent가 실행됩니다. 기본적인 편집에서는 이 이벤트에 코드를 추가할 필요가 없으나 만약 필요하다면 코드를 추가할 수 있으며 이 글에서는 사용하지 않습니다. 이렇게 새롭게 추가된 도형을 DBMS에 저장하는 코드는 다음과 같습니다.

var svc:XrService;
var params:Object;
svc = new XrQueryTableService(
    Global.GIS_SERVER, "mssqlserver", onFIDQueryCompleted, onFIDQueryError);
if(!svc.run({cache: false, sql: "SELECT (MAX(FID)+1) FID FROM SpatialDBTBL"}))
{
    mx.controls.Alert.show("FID Query invoke error");
}

위의 코드는 새롭게 추가된 공간데이터를 저장하기 전에 Feature ID를 얻기 이한 코드입니다. 새로운 Feature ID를 계산하는 방법은 다양하지만 여기서는 기존 ID 값중 최대값에 대해 +1 인 값을 사용하는 방법을 활용했습니다. Feature ID 값을 제대로 얻어오게 되면 onFIDQueryCompleted 함수가 호출하며 다음과 같습니다.

private function onFIDQueryCompleted(result:String):void
{
    XML.ignoreWhitespace = true;
    var xml:XML = new XML(result);
    var rows:XMLListCollection = new XMLListCollection(xml.row);
    var cntRows:int = rows.length;
    var fid:int;
 
    if(cntRows == 1) 
    {
        fid = parseInt(rows[0].FID);
    }
    else 
    {
        mx.controls.Alert.show("FID is Nothing, Error"); 
        return;
    }

    var svc:XrService = new XrUpdateShapeMapFromMashupService(
        _map.edit, _shapeLyr, _mashupLyr, onNewUpdateCompleted, onUpdateFail);
    var params:Object = { targetId:fid, sourceId:0 /*, attributes:attrs*/ };
   
    if(!svc.run(params)) 
    {
        mx.controls.Alert.show(
            "XrUpdateShapeMapFromMashupService params are invalid.");
    }     
}
   
private function onFIDQueryError():void
{
    trace("ERROR : FID Query");
}

실제 DBMS에 반영하기 위해서 XrUpdateShapeMapFromMashupServer를 사용합니다. 성공적으로 DBMS에 반영이 되면 onUpateCompleted가 실행되며 실패하면 onUpdateFail이 실행됩니다. 이 두 함수는 다음과 같습니다.

protected function onNewUpdateCompleted():void
{
    _map.edit.selectNone();
    _map.edit.editMode = false;
    _map.edit.editHistoryReset();
    _mashupLyr.removeAllMashups();

    _map.update();
}
   
protected function onUpdateFail(msg:String):void
{
    mx.controls.Alert.show("EDIT ERROR: " + msg);
}

2. 기존 도형의 편집

다음은 기존 도형을 편집하는 기능에 대한 내용을 살펴보겠습니다. 먼저 기존 도형을 편집하겠다는 시작 코드(CODE-A)는 다음과 같습니다.

_mashupLyr.removeAllMashups();
_map.edit.editMode = false;
_map.edit.editHistoryReset();

위의 코드는 편집하고자 하는 기존 도형을 마우스로 선택하기 위해 편집 모드를 false로 지정한 것입니다. 앞서 편집을 위한 초기화 코드 중에서 XRMAP_MOUSE_CLICK_EVENT 이벤트에 대한 호출 함수인 onMapClick를 등록하였습니다. 이 onMapClick 함수에서 마우스로 클릭한 지점에 존재하는 도형의 Feature ID를 얻습니다.

protected function onMapClick(event:XrMapMouseEvent):void
{
    var clickX:Number = event.mouseEvent.localX;
    var clickY:Number = event.mouseEvent.localY;
    var fid:int = _shapeLyr.getFIDByMousePoint(x, y);
    
    if(fid != -1)
    {
        var shp:IXrShape = _shapeLyr.shapeSet.rows[fid];
        var point:XrPointShape = shp as XrPointShape;
        if(point != null)
        {
            _map.edit.selectNone();
            _map.edit.editHistoryReset();

            if(_mashupLyr.getMashup(0) != null) _mashupLyr.removeAllMashups();

            var coord:XrCoordinate = 
                new XrCoordinate(point.points[0].x, point.points[0].y);
            var mashup:IXrMashup = new XrMarkerMashup(0, coord, false);
    
            mashup.lineSymbol = new XrLineSymbol();
            mashup.fillSymbol = new XrFillSymbol();
            mashup.markerSymbol = new XrRectangleMarkerSymbol();

            _mashupLyr.addMashup(mashup);
            _mashupLyr.updateItem(0, false);

            var attr:XrAttribute = _shapeLyr.attributeSet.rows[fid];
      
            _map.edit.setSketch(0);
        }
    }
}

위의 코드를 통해 마우스로 편집하고자 하는 도형을 선택할 수 있으며 편집할 수 있습니다. 단, 다른 도형을 선택하기 위해서는 다시 기존 도형을 편집하겠다는 시작 코드(CODE-A)를 다시 실행해 줘야 합니다. 이 부분이 Birdge Edit 방식의 단점 중 하나입니다. 이렇게 편집된 내용을 실제 DBMS에 저장하기 위해 다음 코드의 실행이 필요합니다.

svc = new XrUpdateShapeMapFromMashupService(
    _map.edit, _shapeLyr, _mashupLyr, onModifyUpdateCompleted, onUpdateFail);

params = { targetId:_targetFid, sourceId:0/*, attributes:_attrs*/ };

if(!svc.run(params)) 
{
    mx.controls.Alert.show(
        "XrUpdateShapeMapFromMashupService's params are invalid.");
}

성공적으로 편집한 내용이 DBMS에 반영되 onModifyUpdateCompleted가 호출됩니다.

protected function onModifyUpdateCompleted():void
{
    _shapeLyr.shapeSet.removeRow(_targetFid);
    _shapeLyr.attributeSet.removeRow(_targetFid);
 
    _map.edit.selectNone();
    _map.edit.editMode = false;
    _map.edit.editHistoryReset();
    _mashupLyr.removeAllMashups(); 
    _map.update();
}

3. 기존 도형의 삭제

이제 끝으로, 선택된 도형을 삭제하는 방법에 대해 살펴보겠습니다.

삭제할 도형을 선택하는 방법은 편집하고자 하는 도형을 선택하는 것과 동일하며, 일단 선택된 상태에서 다음 코드를 실행하면 삭제됩니다.

var svc:XrUpdateShapeMapFromMashupService = new XrUpdateShapeMapFromMashupService(
    _map.edit, _shapeLyr, _mashupLyr, onModifyUpdateCompleted, onUpdateFail);
     
var params:Object = {targetId:_targetFid, removal:true };
     
if(!svc.run(params)) 
{
    mx.controls.Alert.show("params are invalid.");
}

Direct Edit는 DBMS에 편집한 내용을 반영하기 전까지 여러개의 공간도형을 편집할 수 있습니다. 그러나 Bridge Edit는 Direct Edit와 다르게 오직 하나의 공간도형에 대해서 편집을 할 수 있고 다른 공간 도형을 편집하기 전에 DBMS에 반영을 시켜야 합니다. 편집 이력에 대해서는 DBMS에 반영되기 전까지만 Undo/Redo가 가능하므로 Direct Edit이 Bridge Edit에 비해 더 많은 공간 데이터에 대해서 반영이 가능합니다.

[GIS] FingerEyes-Xr, TMS(OSGeo Spec) 지원

핑거아이즈가 OSGeo 스펙인 TMS를 지원합니다. TMS는 타일맵 형태의 맵 서비스로 오픈스트리트, 다음, 네이버, 지오서비스의 지도 서비스에서 따르는 맵 서비스 형태입니다. 핑거아이즈에서  TMS를 지원함으로써 사용자가 원하는 다양한 배경지도를 쉽게 활용할 수 있습니다. 다음은 네이버에서 제공하는 배경지도를 핑거아이즈에서 레이어로 추가한 화면입니다.

사용자 삽입 이미지
TMS를 활용하기 위해서는 최소한 다음과 같은 메타데이터(Metadata)를 알고 있어야 합니다.

  • 1픽셀에 대한 지도단위 길이
  • 타일맵 이미지 요청 URL
  • MBR(Boundray)
  • 타일맵 이미지 크기(256×256 등)
  • 타일맵 이미지 형식(PNG/JPEG/GIF 등)

다음은 핑거아이즈에서 네이버의 지도를 TMS를 통해 레이어로 추가하는 코드입니다.

var lyr:IXrLayer = new XrTMSLayer(
  "basemap", 
  [
    "http://onetile1.map.naver.net/get/18/0/0/${z}/${x}/${y}/bl_vc_bg/ol_vc_an",
    "http://onetile2.map.naver.net/get/18/0/0/${z}/${x}/${y}/bl_vc_bg/ol_vc_an",
    "http://onetile3.map.naver.net/get/18/0/0/${z}/${x}/${y}/bl_vc_bg/ol_vc_an",
    "http://onetile4.map.naver.net/get/18/0/0/${z}/${x}/${y}/bl_vc_bg/ol_vc_an"
  ],     
  [2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0.5, 0.25],
  new XrExtent(90112, 1192896, 1990673, 2761664)
);
    
map.layers.addLayer(lyr);

참고로 TMS를 지원 사이트에서 지도 데이터를 업데이트 하면서 metadata가 자주 변경될 수 있습니다.

[GIS] FingerEyes-Xr, 핑거아이즈로 생성한 밀집도(밀도도)

아래의 이미지는 웹 GIS 엔진인 핑거아이즈(FingerEyes-Xr)로 생성한 밀집도입니다. 흔히 밀집도는 Kernel Density 알고리즘을 사용해 계산됩니다. 알고리즘 명칭에 Density가 사용되어 밀집도라는 용어 대신 밀도도라는 용어가 사용될 수도 있습니다.


위의 밀집도의 대상 데이터는 교통사고 발생 지점입니다. 해외나 국내에서 웹기반의 지도 엔진에서 제공되는 밀집도 분석 결과는 HeatMap이라는 기능에서 사용된 오픈소스를 그대로 적용하거거나 개선해 구현함으로써 그 품질이 떨어집니다.

핑거아이즈는 오픈소스이므로 위의 밀집도 결과 이미지를 생성해 내는 알고리즘에 대한 코드가 제공됩니다. 혹.. 관심이 있으신 분은 해당 소스를 참고하시기 바랍니다. 핑거아이즈의 소스코드는 http://www.gisdeveloper.co.kr/?page_id=3916의 자료실을 통해 누구나 다운로드 받으실 수 있습니다.