[OpenLayers] 사용자 정의 Interaction

ol에서 마우스나 키보드 등을 통한 사용자의 행위에 대한 처리를 위한 Interaction를 정의하는 내용에 대한 정리이다. 이 글은 [OpenLayers] 직접 좌표를 지정하여 구성하는 벡터 레이어에서 작성된 코드를 기반으로 작성되었으므로, 실제 실행하기 위해서는 해당 글에 대한 코드를 먼저 작성한 후 이 글의 코드를 반영하기 바란다.

사용자의 조작에 대한 Interaction을 정의하기 위한 예로써 사용자가 지도에 표시된 Feature를 마우스로 드레그하여 이동하는 경우에 대한 코드를 작성한다.

먼저 아래처럼 필요한 모듈을 하나 추가한다.

import {defaults as defaultInteractions, Pointer as PointerInteraction} from 'ol/interaction.js';

PointInteraction 클래스를 상속받도록 하여, Drag라는 클래스로 하여 아래처럼 생성한다.

/**
 * @constructor
 * @extends {module:ol/interaction/Pointer}
 */
var Drag = (function (PointerInteraction) {
    function Drag() {
        PointerInteraction.call(this, {
            handleDownEvent: handleDownEvent,
            handleDragEvent: handleDragEvent,
            handleMoveEvent: handleMoveEvent,
            handleUpEvent: handleUpEvent
        });

        this.coordinate_ = null;
        this.cursor_ = 'pointer';
        this.feature_ = null;
        this.previousCursor_ = undefined;
    }

    if ( PointerInteraction ) Drag.__proto__ = PointerInteraction;
    Drag.prototype = Object.create( PointerInteraction && PointerInteraction.prototype );
    Drag.prototype.constructor = Drag;

    return Drag;
}(PointerInteraction));

14~17번 코드에서 정의된 private 변수는 사용자가 마우스로 Feature를 이동할때 필요한 변수들이다. Interaction은 사용자가 마우스나 키보드 등과 같은 입력 장치을 통한 상호작용으로 6번 코드의 생성자에서 4개의 마우스 이벤트에 대한 호출 함수를 지정하고 있다. 4개의 마우스 이벤트는 각각 버튼 Down 이벤트, Drag 이벤트, Move 이벤트, 버튼 Up 이벤트이다. 이들 호출 함수는 아래와 같으며
14~17번 코드에서 정의된 private 변수들에 대해 처리하고 있는 것을 볼 수 있다.

function handleDownEvent(evt) {
    var map = evt.map;

    var feature = map.forEachFeatureAtPixel(evt.pixel,
        function(feature) { return feature; });

    if (feature) {
        this.coordinate_ = evt.coordinate;
        this.feature_ = feature;
    }

    return !!feature;
}

function handleDragEvent(evt) {
    var deltaX = evt.coordinate[0] - this.coordinate_[0];
    var deltaY = evt.coordinate[1] - this.coordinate_[1];

    var geometry = this.feature_.getGeometry();
    geometry.translate(deltaX, deltaY);

    this.coordinate_[0] = evt.coordinate[0];
    this.coordinate_[1] = evt.coordinate[1];
}

function handleMoveEvent(evt) {
    if (this.cursor_) {
        var map = evt.map;
        var feature = map.forEachFeatureAtPixel(evt.pixel,
        
            function(feature) {
            return feature;
        });

        var element = evt.map.getTargetElement();
        
        if (feature) {
            if (element.style.cursor != this.cursor_) {
                this.previousCursor_ = element.style.cursor;
                element.style.cursor = this.cursor_;
            }
        } else if (this.previousCursor_ !== undefined) {
            element.style.cursor = this.previousCursor_;
            this.previousCursor_ = undefined;
        }
    }
}

function handleUpEvent() {
    this.coordinate_ = null;
    this.feature_ = null;
    return false;
}

지도에 대해 위에서 정의한 Interaction을 반영하기 위해 지도를 생성 코드에서 아래처럼 반영한다.

var map = new Map({
    // 아래의 한줄이 반영된 코드임
    interactions: defaultInteractions().extend([new Drag()]),
    target: 'map',

    ...

});

실행하여 Feature를 마우스로 드레그 하면 이동되는 것을 확인할 수 있다.

[OpenLayers] 직접 좌표를 지정하여 구성하는 벡터 레이어

ol의 레이어 종류 중 벡터 레이어는 좌표를 통해 클라이언트 단에서 직접 레이어를 그려주는 방식이다. 여기서 좌표는 DBMS에서, 또는 GPX, GeoJSON, IGC, KML, TopoJSON 포맷의 데이터소스에서 받을 수 있는데.. 여기서는 개발자가 직접 좌표를 지정하여 구성하는 내용에 대해 정리해 본다. 이러한 방식은 내가 원하는 데이터 포맷으로부터 벡터 레이어를 구성하기 위한 기반이 되는 내용이기도 하다.

먼저 HTML 구성은 아래와 같다.



    
        
        OpenLayers
        
    
    
        

index.js에 필요한 코드가 들어가는데.. 먼저 필요한 모듈을 아래처럼 추가한다.

import Feature from 'ol/Feature.js';
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import {LineString, Point, Polygon} from 'ol/geom.js';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer.js';
import {TileJSON, Vector as VectorSource} from 'ol/source.js';
import {Fill, Icon, Stroke, Style} from 'ol/style.js';

벡터 레이어는 벡터 소스를 통해 구성되는데.. 벡터 소스는 Feature들의 집합이기도 하다. 이 Feature 요소를 3개 생성해보자. 아래처럼..

var pointFeature = new Feature(new Point([0, 0]));

var lineFeature = new Feature(new LineString([[-1e7, 1e6], [-1e6, 3e6]]));

var polygonFeature = new Feature(
    new Polygon([[[-3e6, -1e6], [-3e6, 1e6], [-1e6, 1e6], [-1e6, -1e6], [-3e6, -1e6]]]));

포인트 피쳐, 라인 피쳐, 폴리곤 피쳐를 생성하고 있다. 이 3개의 피쳐로 구성된 데이터소스는 아래처럼 생성된다.

var vectorsource = new VectorSource({
    features: [pointFeature, lineFeature, polygonFeature]
});

데이터소스가 정의되었음으로 이제 시각화를 위한 레이어 객체를 아래처럼 생성한다.

var vectorlayer = new VectorLayer({
    source: vectorsource,
    style: new Style({
        image: new Icon(({
            anchor: [0.5, 46],
            anchorXUnits: 'fraction',
            anchorYUnits: 'pixels',
            opacity: 0.95,
            src: 'data/icon.png'
        })),
        stroke: new Stroke({
            width: 3,
            color: [255, 0, 0, 1]
        }),
        fill: new Fill({
            color: [0, 0, 255, 0.6]
        })
    })
});

레이어는 시각화를 위한 개념이 가장 중요함으로 시각화를 위한 스타일을 상세하기 지정하고 있는 것을 알 수 있다. 이제 앞서 생성한 레이어를 구성하여 지도 객체를 생성하면 끝이다.

var map = new Map({
    target: 'map',
    layers: [
        new TileLayer({
            source: new TileJSON({
                url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure'
            })
        }),
        vectorlayer
    ],
    view: new View({
        center: [0, 0],
        zoom: 2
    })
});

실행 결과의 화면은 아래와 같다.