[OpenLayers] 마우스로 Vector 생성하기(그리기)

ol에서 마우스를 이용해 지도에 도형을 그리기 내용에 대한 정리이다. UI에 대한 DOM 구성은 아래와 같다.

위의 DOM에 대한 CSS는 아래와 같다.


이제 JavaScript 코드이다. 먼저 필요한 모듈을 import 한다.

import 'ol/ol.css';

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

배경지도로 OpenStreetMap을 사용하기 위해 아래의 TileLayer 타입의 raster 객체를 생성한다.

var raster = new TileLayer({
    source: new OSM()
});

마우스를 이용해 도형을 그리면 해당 도형이 저장될 벡터 저장소가 필요하다. 이에 대한 저장소와 표현을 위한 레이어 객체를 생성한다.

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

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

위의 2개의 레이어를 구성하여 지도 객체를 생성한다.

var map = new Map({
    layers: [raster, vector],
    target: 'map',
    view: new View({
        center: [-11000000, 4600000],
        zoom: 4
    })
});

id가 type인 DOM 요소를 얻어오고, 이 DOM 요소를 통해 사용자가 자신이 그릴 도형의 타입을 선택할때 발생하는 이벤트를 아래처럼 지정한다.

var typeSelect = document.getElementById('type');

typeSelect.onchange = function() {
    map.removeInteraction(draw);
    addInteraction();
};

addInteraction();

addInteraction 함수는 아래와 같은데, 그릴 도형을 선택하여 선택된 값으로 Draw 타입의 클래스의 객체를 생성해 지도에 Interaction으로써 추가해 준다.

var draw; // 전역 객체

function addInteraction() {
    var value = typeSelect.value;

    if (value !== 'None') {
        draw = new Draw({
            source: source,
            type: typeSelect.value,
            //freehand: false
        });

        map.addInteraction(draw);
    }
}

실행하면 마우스를 통해 내가 원하는 도형을 선택해 그릴 수 있다.

[OpenLayers] 타일 방식의 DEM 데이터 가시화

Tile 방식은 지도를 인터넷에서 효율적으로 받아 빠르게 표시하기 위한 방식으로 주로 png나 jpg와 같은 이미지 형식이다. 그런데 이 Tile 방식의 이미지에 표고값을 저장해 서비스하기도 한다. 대표적인 경우가 MapBox에서 제공하는 타일방식의 DEM 서비스이다.

이 글은 ol을 이용해 타일방식으로 서비스 되는 DEM 데이터를 이미지 개념으로 그대로 화면에 표시하는 내용이다. 먼저 html에서 id가 map인 지도를 표시할 DOM을 작성하고 js 코드를 아래처럼 작성한다.

import 'ol/ol.css';

import Map from 'ol/Map.js';
import View from 'ol/View.js';
import {XYZ} from 'ol/source.js';
import {Tile} from 'ol/layer.js';

var elevation = new XYZ({
    url: 'https://{a-d}.tiles.mapbox.com/v3/aj.sf-dem/{z}/{x}/{y}.png',
    crossOrigin: 'anonymous',
});

var map = new Map({
    target: 'map',
    layers: [
       new Tile({
           source: elevation
       })
    ],

    view: new View({
        extent: [-13675026, 4439648, -13580856, 4580292],
        center: [-13615645, 4497969],
        minZoom: 10,
        maxZoom: 16,
        zoom: 13
    })
});

실행해 보면 다음과 같은 결과를 볼 수 있다.

MapBox에서 제공하는 이 DEM 데이터의 각 화소에 해당하는 실제 높이값은 다음과 같다.

height = blue_channel_value + green_channel_value * 2 + red_channel_value * 3

그럼, 이 DEM 데이터는 어떻게 활용될 수 있을까? 지형에 대한 표고값을 담을 수 있는 DEM은 지형에 대한 3차원 가시화, 입체적인 지형의 음영의 기복을 표시하는 음영기복도, 평균경사도 등과 같은 지형 분석등에 사용되는 매우 중요한 데이터이다. 추후 이 글의 내용을 토대로 DEM 데이터를 이용한 음영기복도를 ol에서 동적으로 생성해 보는 내용을 정리해 보겠다.

[OpenLayers] 마우스 위치에 대한 Feature의 속성 접근하기

이 글은 클릭된 Feature의 속성 얻기와 매우 비슷한 내용을 담고 있으니, 해당 글도 참고하기 바랍니다.

Vector 류의 Layer는 Feature의 그룹을 데이터셋으로 갖는다. Feature는 공간 데이터의 좌표값을 갖는 Geometry와 속성값을 갖는 Object 타입의 Properties로 이루어져 있다. 아래의 코드는 지도를 구성하는 레이어 중 Vector 류 중 마우스 포인터 위치의 Feature의 속성에 접근하는 것이다.

map.on('pointermove', showInfo);
    
function showInfo(event) {
    var features = map.getFeaturesAtPixel(event.pixel);
    if (!features) {
        // 마우스 위치에 어떠한 Feature도 없음
        return;
    }

    // 마우스 포인터 위치에 존재하는 Feature 중 첫번째(features[0])
    var properties = features[0].getProperties();  
    // properties 객체가 object 타입의 Feature에 대한 속성값임
}

[OpenLayers] Stroke를 여러 개의 색상으로 지정해 그리기

ol에서 폴리라인이나 폴리곤의 외곽선은 Stroke로 지정된 스타일로 그려집니다. 이 글은 하나의 도형에 대해 여러개의 Stroke로 그려주는 방법에 대한 내용을 정리합니다. 하나의 폴리라인에 대해서 3개의 색상으로 표현하는 것을 예로 설명합니다. 먼저 폴리라인의 좌표를 인코딩된 문자열로 다음처럼 표현할 수 있습니다.

var polyline = [
    'hldhx@lnau`BCG_EaC??cFjAwDjF??uBlKMd@}@z@??aC^yk@z_@se@b[wFdE??wFfE}N',
    'fIoGxB_I\\gG}@eHoCyTmPqGaBaHOoD\\??yVrGotA|N??o[N_STiwAtEmHGeHcAkiA}^',
    'aMyBiHOkFNoI`CcVvM??gG^gF_@iJwC??eCcA]OoL}DwFyCaCgCcCwDcGwHsSoX??wI_E',
    'kUFmq@hBiOqBgTwS??iYse@gYq\\cp@ce@{vA}s@csJqaE}{@iRaqE{lBeRoIwd@_T{]_',
    'Ngn@{PmhEwaA{SeF_u@kQuyAw]wQeEgtAsZ}LiCarAkVwI}D??_}RcjEinPspDwSqCgs@',
    'sPua@_OkXaMeT_Nwk@ob@gV}TiYs[uTwXoNmT{Uyb@wNg]{Nqa@oDgNeJu_@_G}YsFw]k',
    'DuZyDmm@i_@uyIJe~@jCg|@nGiv@zUi_BfNqaAvIow@dEed@dCcf@r@qz@Egs@{Acu@mC',
    'um@yIey@gGig@cK_m@aSku@qRil@we@{mAeTej@}Tkz@cLgr@aHko@qOmcEaJw~C{w@ka',
    'i@qBchBq@kmBS{kDnBscBnFu_Dbc@_~QHeU`IuyDrC_}@bByp@fCyoA?qMbD}{AIkeAgB',
    'k_A_A{UsDke@gFej@qH{o@qGgb@qH{`@mMgm@uQus@kL{_@yOmd@ymBgwE}x@ouBwtA__',
    'DuhEgaKuWct@gp@cnBii@mlBa_@}|Asj@qrCg^eaC}L{dAaJ_aAiOyjByH{nAuYu`GsAw',
    'Xyn@ywMyOyqD{_@cfIcDe}@y@aeBJmwA`CkiAbFkhBlTgdDdPyiB`W}xDnSa}DbJyhCrX',
    'itAhT}x@bE}Z_@qW_Kwv@qKaaAiBgXvIm}A~JovAxCqW~WanB`XewBbK{_A`K}fBvAmi@',
    'xBycBeCauBoF}}@qJioAww@gjHaPopA_NurAyJku@uGmi@cDs[eRaiBkQstAsQkcByNma',
    'CsK_uBcJgbEw@gkB_@ypEqDoqSm@eZcDwjBoGw`BoMegBaU_`Ce_@_uBqb@ytBwkFqiT_',
    'fAqfEwe@mfCka@_eC_UmlB}MmaBeWkkDeHwqAoX}~DcBsZmLcxBqOwqE_DkyAuJmrJ\\o',
    '~CfIewG|YibQxBssB?es@qGciA}RorAoVajA_nAodD{[y`AgPqp@mKwr@ms@umEaW{dAm',
    'b@umAw|@ojBwzDaaJsmBwbEgdCsrFqhAihDquAi`Fux@}_Dui@_eB_u@guCuyAuiHukA_',
    'lKszAu|OmaA{wKm}@clHs_A_rEahCssKo\\sgBsSglAqk@yvDcS_wAyTwpBmPc|BwZknF',
    'oFscB_GsaDiZmyMyLgtHgQonHqT{hKaPg}Dqq@m~Hym@c`EuiBudIabB{hF{pWifx@snA',
    'w`GkFyVqf@y~BkoAi}Lel@wtc@}`@oaXi_C}pZsi@eqGsSuqJ|Lqeb@e]kgPcaAu}SkDw',
    'zGhn@gjYh\\qlNZovJieBqja@ed@siO{[ol\\kCmjMe\\isHorCmec@uLebB}EqiBaCg}',
    '@m@qwHrT_vFps@kkI`uAszIrpHuzYxx@e{Crw@kpDhN{wBtQarDy@knFgP_yCu\\wyCwy',
    'A{kHo~@omEoYmoDaEcPiuAosDagD}rO{{AsyEihCayFilLaiUqm@_bAumFo}DgqA_uByi',
    '@swC~AkzDlhA}xEvcBa}Cxk@ql@`rAo|@~bBq{@``Bye@djDww@z_C_cAtn@ye@nfC_eC',
    '|gGahH~s@w}@``Fi~FpnAooC|u@wlEaEedRlYkrPvKerBfYs}Arg@m}AtrCkzElw@gjBb',
    'h@woBhR{gCwGkgCc[wtCuOapAcFoh@uBy[yBgr@c@iq@o@wvEv@sp@`FajBfCaq@fIipA',
    'dy@ewJlUc`ExGuaBdEmbBpBssArAuqBBg}@s@g{AkB{bBif@_bYmC}r@kDgm@sPq_BuJ_',
    's@{X_{AsK_d@eM{d@wVgx@oWcu@??aDmOkNia@wFoSmDyMyCkPiBePwAob@XcQ|@oNdCo',
    'SfFwXhEmOnLi\\lbAulB`X_d@|k@au@bc@oc@bqC}{BhwDgcD`l@ed@??bL{G|a@eTje@',
    'oS~]cLr~Bgh@|b@}Jv}EieAlv@sPluD{z@nzA_]`|KchCtd@sPvb@wSb{@ko@f`RooQ~e',
    '[upZbuIolI|gFafFzu@iq@nMmJ|OeJn^{Qjh@yQhc@uJ~j@iGdd@kAp~BkBxO{@|QsAfY',
    'gEtYiGd]}Jpd@wRhVoNzNeK`j@ce@vgK}cJnSoSzQkVvUm^rSgc@`Uql@xIq\\vIgg@~k',
    'Dyq[nIir@jNoq@xNwc@fYik@tk@su@neB}uBhqEesFjoGeyHtCoD|D}Ed|@ctAbIuOzqB',
    '_}D~NgY`\\um@v[gm@v{Cw`G`w@o{AdjAwzBh{C}`Gpp@ypAxn@}mAfz@{bBbNia@??jI',
    'ab@`CuOlC}YnAcV`@_^m@aeB}@yk@YuTuBg^uCkZiGk\\yGeY}Lu_@oOsZiTe[uWi[sl@',
    'mo@soAauAsrBgzBqgAglAyd@ig@asAcyAklA}qAwHkGi{@s~@goAmsAyDeEirB_{B}IsJ',
    'uEeFymAssAkdAmhAyTcVkFeEoKiH}l@kp@wg@sj@ku@ey@uh@kj@}EsFmG}Jk^_r@_f@m',
    '~@ym@yjA??a@cFd@kBrCgDbAUnAcBhAyAdk@et@??kF}D??OL'
].join('');

위의 좌표는 Geometry 중 Polyline으로 객체화되어야 하는데 아래와 같습니다.

var route = (new Polyline({
    factor: 1e6
}).readGeometry(polyline, {
    dataProjection: 'EPSG:4326',
    featureProjection: 'EPSG:3857'
}));

이제 Polyline 객체는 Feature로 객체화되어야 벡터 레이어의 구성요소가 될 수 있는데, Feature 객체화를 위한 코드는 아래와 같습니다.

var routeFeature = new Feature({
    geometry: route
});

벡터 레이어의 구성 요소인 Feature 객체를 그릴때 사용하는 스타일로 3개의 서로 다른 굵기와 색상에 대한 객체는 아래와 같습니다.

var style = [
    new Style({
        stroke: new Stroke({
            color: 'black',
            width: 6
        })
    }),
    new Style({
        stroke: new Stroke({
        color: 'white',
        width: 4
        })
    }),
    new Style({
        stroke: new Stroke({
        color: 'red',
        width: 2
        })
    })
];

이제 아래처럼 벡터 레이어 객체를 생성할 수 있습니다.

var vectorLayer = new VectorLayer({
    source: new VectorSource({
        features: [routeFeature]
    }),
    style: style
});

최종적으로 위의 레이어로 구성되는 지도 객체를 생성합니다.

var map = new Map({
    target: document.getElementById('map'),
    view: new View({
        center: [-5639523.95, -3501274.52],
        zoom: 10,
        minZoom: 2,
        maxZoom: 19
    }),
    layers: [
        new TileLayer({ source: new OSM() }),
        vectorLayer
    ]
});

실행해 보면 아래와 같은 결과를 볼 수 있습니다.