[OpenLayers] 특정 지점에 HotSpot 애니메이션 효과 주기

지도에 특정한 지점을 효과적으로 표현하기 애니메이션 효과를 사용하는 내용을 정리합니다. 먼저 그 결과는 아래와 같습니다.

화면은 지도에 대한 div 요소 하나입니다. 필요한 스타일과 함께 언급하면 아래와 같습니다.



이제 실제 코드인데요. 먼저 필요한 css와 모듈을 추가합니다.

import 'ol/ol.css';

import Feature from 'ol/Feature.js';
import Map from 'ol/Map.js';
import {unByKey} from 'ol/Observable.js';
import View from 'ol/View.js';
import {easeOut} from 'ol/easing.js';
import Point from 'ol/geom/Point.js';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer.js';
import {fromLonLat} from 'ol/proj.js';
import {OSM, Vector as VectorSource} from 'ol/source.js';
import {Circle as CircleStyle, Style, Fill} from 'ol/style.js';

이제 레이어와 지도를 구성합니다.

var map = new Map({
    layers: [
        new TileLayer({
            source: new OSM({
                wrapX: false
            })
        })
    ],
    target: 'map',
    view: new View({
        center: [0, 0],
        zoom: 2
    })
});

var source = new VectorSource({
    wrapX: false
});

var vector = new VectorLayer({
    source: source
});

map.addLayer(vector);

HotSpot 지점은 위의 20번 코드의 vector 객체의 데이터소스인 source 객체에 Feature로 추가됩니다. 위치는 난수로 지정되는데 아래의 함수 호출을 통해 추가됩니다.

function addRandomFeature() {
    var x = Math.random() * 360 - 180;
    var y = Math.random() * 180 - 90;
    var coord = fromLonLat([x, y]);
    var geom = new Point(coord);
    var feature = new Feature(geom);

    source.addFeature(feature);
}

위치를 난수로 정하고 Geometry를 구성한 뒤 Feature를 추가하는 형태입니다. Feature가 추가될 때 이벤트가 발생하게 할 수 있는데 아래와 같습니다.

source.on('addfeature', function(e) {
    flash(e.feature);
});

이벤트의 호출 함수에 전달되는 인자를 통해 어떤 Feature가 추가되었는지를 알 수 있고, 이 Feature을 인자로 flash 함수를 호출합니다. flash 함수는 다음과 같습니다.

var duration = 2000;
function flash(feature) {
    var start = new Date().getTime();
    var listenerKey = map.on('postcompose', animate);

    function animate(event) {
        var vectorContext = event.vectorContext;
        var frameState = event.frameState;
        var flashGeom = feature.getGeometry().clone();
        var elapsed = frameState.time - start;
        var elapsedRatio = elapsed / duration;

        var radius = easeOut(elapsedRatio) * 30;
        var opacity = easeOut(1 - elapsedRatio);

        var style = new Style({
            image: new CircleStyle({
                radius: radius,
                fill: new Fill({
                    color: 'rgba(255, 0, 0, ' + 0.5 * opacity + ')',        
                })
            })
        });

        vectorContext.setStyle(style);
        vectorContext.drawGeometry(flashGeom);

        if (elapsed > duration) {
            unByKey(listenerKey);
        } else {
            map.render();
        }
    }
}

짧다고 하기엔 조금 긴데요. 내부적으로 복잡한 처리가 실행됩니다. 이 flash 함수가 처음 호출되면 결국 31번의 map.render()가 호출됩니다. 이 map.render()가 호출되면 지도가 (데이터 요청없이) 다시 그려지고4번 코드에서 등록된 postcompose 이벤트 함수인 animate가 호출됩니다. animate 함수는 함수 내의 함수인데 6번 라인에 정의되어 있습니다. 해당 Feature의 위치를 기준으로 시간을 기반 투명도와 반지름의 크기를 변경하면서 빨간색 채움색으로 원을 그려주는 함수입니다. 이 flash 함수는 계속 자신의 호출하는 재귀함수로써 1번의 duration으로 지정된 시간이 지나면 28번의 조건이 True가 되어 앞서 등록한 postcompose 이벤트 등록을 제거합니다.

이제 마지막으로 1초마다 무작위로 Feature를 추가하도록 하는 코드는 아래와 같습니다.

window.setInterval(addRandomFeature, 1000);

[OpenLayers] 지도를 이미지로 저장하기

ol의 지도로 표현된 내용을 이미지 파일로 저장하는 내용에 대한 정리입니다. 먼저 지도를 위한 div와 이미지 저장을 위한 버튼에 대한 tag를 아래처럼 index.html에 작성합니다.

지도를 구성하는 코드는 아래와 같고..

import 'ol/ol.css';

import Map from 'ol/Map.js';
import View from 'ol/View.js';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer.js';
import {OSM, Vector as VectorSource} from 'ol/source.js';

var map = new Map({
    layers: [
        new TileLayer({
        source: new OSM()
        })
    ],
    target: 'map',
    view: new View({
        center: [0, 0],
        zoom: 2
    })
});

이미지 저장을 위한 버튼을 클릭할 경우와 관련된 코드는 아래와 같습니다.

document.querySelector('#saveAsImage').addEventListener('click', function() {
    map.once('rendercomplete', function(event) {
        var canvas = event.context.canvas;
        if (navigator.msSaveBlob) {
            navigator.msSaveBlob(canvas.msToBlob(), 'map.png');
        } else {
            canvas.toBlob(function(blob) {
                saveAs(blob, 'map.png');
            });
        }
    });
    map.renderSync();
});

saveAs라는 함수가 보이는데, 이 함수를 사용하기 위해서는 다음과 같은 js 라이브러리를 페이지에 포함해야 합니다.


[OpenLayers] Geolocation을 이용한 내 위치 추적하기

HTML5에서도 Geolocation를 제공하지만, 이를 좀더 쉽게 사용할 수 있도록 랩핑 클래스로써 Geolocation를 제공합니다. ol의 Geolocation에 대한 API를 정리합니다. 먼저 html 코드는 아래와 같습니다.



    
        
        OpenLayers
        

    
      
        

Javascript 코드는 Geolocation 관련 코드가 들어가는데, 하나씩 살펴보겠습니다. 먼저 필요한 모듈을 import 합니다.

import 'ol/ol.css';

import Feature from 'ol/Feature.js';
import Geolocation from 'ol/Geolocation.js';
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import Point from 'ol/geom/Point.js';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer.js';
import {OSM, Vector as VectorSource} from 'ol/source.js';
import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style.js';

그리고 View와 Map 객체를 생성하는데 레이어로는 OpenStreetMap을 사용합니다.

var view = new View({
    center: [0, 0],
    zoom: 2
});

var map = new Map({
    layers: [
        new TileLayer({
            source: new OSM()
        })
    ],
    target: 'map',
    view: view
});

이제 이 글의 주인공인 Geolocation 객체를 생성합니다.

var geolocation = new Geolocation({
    trackingOptions: {
        enableHighAccuracy: true
    },
    projection: view.getProjection()
});

Geolocation은 좌표 단위는 WGS84 경위도 좌표인데, 이를 지도의 좌표계와 일치시켜주고 위해 projection 속성을 지도의 View 객체에 대한 projection 객체로 설정하고 있습니다. 그리고 바로 위치를 추적합니다.

geolocation.setTracking(true);

Geolocation은 위치의 변동, 추척 그리고 어떤 문제 발생을 이벤트 처리합니다. 아래와 같이 3개의 이벤트를 추가합니다.

geolocation.on('change', function() {
    console.log('accuracy = ' + geolocation.getAccuracy() + 'm ' +
        'altitude = ' + geolocation.getAltitude() + 'm ' +
        'altitudeAccuracy = ' +  geolocation.getAltitudeAccuracy() + 'm ' +
        'heading = ' + geolocation.getHeading() + 'rad ' +
        'speed = ' + geolocation.getSpeed() + 'm/s');
});

geolocation.on('error', function(error) {
    console.log('geolocation error: ' + error.message);
});

geolocation.on('change:position', function() {
    var coordinates = geolocation.getPosition();

    positionFeature.setGeometry(coordinates ? new Point(coordinates) : null);
});

위치에 대한 정확도, 고도, 고도에 대한 정확도, 방향각, 속도에 대한 변경이 발생할 경우 change 이벤트가 발생하고, 어떤 문제가 발생할 경우 error 이벤트가 발생합니다. 그리고 change:position를 통해 위치 변경시 이벤트가 발생됩니다. change:position 이벤트에서 지도 상에 현재의 위치를 표시하기 위해 positionFeature라는 이름의 객체에 위치를 지정하고 있는데, 이 positionFeature는 아래와 같습니다.

var positionFeature = new Feature();

positionFeature.setStyle(new Style({
    image: new CircleStyle({
        radius: 6,
        fill: new Fill({
            color: '#3399CC'
        }),
        stroke: new Stroke({
            color: '#fff',
            width: 2
        })
    })
}));

네, Feature입니다. Feature이므로 어떠한 모양으로 표시될지를 setStyle로 지정하고 있습니다. 또 Feature이므로 이를 지도와 함께 표시하기 위해 레이어가 필요합니다. 아래처럼요.

new VectorLayer({
    map: map,
    source: new VectorSource({
        features: [positionFeature]
    })
});

이제 실행하면, 위치 확인에 따른 사용자 확인을 묻고 사용자가 허락하면 사용자의 위치를 지도에 표시해 줍니다.

[OpenLayers] 지도를 원하는 위치와 Zoom으로 조정하기

Map 객체는 View 객체를 통해 화면에 지도의 어떤 위치를 표시할지를 결정할 수 있다. 먼저 MBR을 통해 조정하는코드의 예는 아래와 같다.

var mbr = vectorSource.getExtent();
map.getView().fit(mbr);

또한 원하는 지도의 위치로 화면을 이동하기 위해 필요한 코드는 아래와 같다.

var pt = [100000, 200000];
map.getView().setCenter(pt);

원하는 Zoom 레벨값으로 조저하는 코드는 아래와 같다.

map.getView().setZoom(10);

지도를 각도로 회전할 수 있는데 코드는 아래와 같다.

map.getView().setRotation(radianV);

지도를 해상도(Resolution)으로도 Zoom 할수 있는데 아래와 같다.

map.getView().setResolution(123.12344);