[OpenLayers3] WFS를 이용한 벡터 레이어

WFS는 Web Feature Service로 Feature는 도형과 속성을 가진 데이터 단위입니다. WFS를 지원하는 공간 서버를 통해 도형의 구성 좌표와 속성 데이터를 가져오고 이런 데이터를 클라이언트에서 받아 화면에 그릴 수 있는데요. 이에 대해 ol3에서 어떻게 처리하는지 살펴보도록 하겠습니다. 먼저 우리가 만들 최종 결과 지도 웹 페이지는 아래와 같습니다.

오픈 스트리트 맵을 배경지도 레이어로 사용 되고 있고, 벡터 레이어를 통해 WFS로 받은 도형이 노란색 외곽선과 반투명한 빨간색의 채움 스타일로 그려지고 있습니다. 위의 웹 페이지에 대한 구현 코드를 살펴보겠는데요. 먼저 필요한 외부 CSS와 자바스크립트 라이브러리를 페이지에 포함하는 코드입니다.

    
    
    

다음은 UI에 대한 태그입니다.

위의 div는 다음과 같은 크기에 대한 스타일이 지정되어 있습니다.


이제 필요한 라이브러리와 스타일, 그리고 UI는 준비가 되었는데요. 이 UI가 작동할 수 있도록 스크립트 코드를 작성하겠습니다. 먼저 jQuery의 ready 이벤트 함수를 아래처럼 미리 추가해 둡니다.


이제 위의 ….에 해당하는 부분에 스크립트 코드를 추가해 보도록 하겠습니다. 먼저 WFS를 이용해 데이터를 받을 벡터 레이어의 소스 객체를 아래처럼 생성합니다.

var vectorSource = new ol.source.Vector({
    format: new ol.format.GeoJSON(),
    url: function (extent) {
        var strUrl = 'https://ahocevar.com/geoserver/wfs?service=WFS&' +
            'version=1.1.0&request=GetFeature&typename=osm:water_areas&' +
            'outputFormat=application/json&srsname=EPSG:3857&' +
            'bbox=' + extent.join(',') + ',EPSG:3857';

        return strUrl;
    },
    strategy: ol.loadingstrategy.bbox
});

WFS 방식을 통해 공간서버에 Feature 데이터를 요청할 수 있는데요. WFS는 요청받은 데이터를 XML이나 JSON과 같은 형식으로 결과를 클라이언트에게 전달합니다. 또한 WFS는 URL 호출 방식으로 Feature 데이터를 요청합니다. 바로 3번의 url 속성이 WFS를 URL로 호출할때 사용하는 url 문자열입니다. https://ahocevar.com/geoserver/wfs 까지가 WFS 서비스를 제공하는 서버의 주소이고, 이후 ? 다음이 WFS 서비스 호출시 전달하는 인자값들입니다. 각 인자값은 다음과 같습니다.

  1. service=WFS
  2. version=1.1.0
  3. request=GetFeature
  4. typename=osm:water_areas
  5. outputFormat=application/json
  6. srsname=EPSG:3857
  7. bbox=100,100,200,200,EPSG:3857

1번은 해당 URL 요청이 WFS 서비스라는 의미이고, 2번은 WFS의 버전을 의미하며, 3번은 WFS 기능 중 Feature 정보를 얻기 위한 GetFeature 기능에 대한 호출의 의미이고, 4번은 WFS에 의해 서비스 되는 공간 데이터 이름이고, 5번은 결과의 형식을 JSON 포맷으로 전달해줄 것에 대한 내용이며, 6번은 결과에 대한 좌표 체계를 무엇으로 할 것인지를 지정하는 것입니다. 끝으로 7번은 Feature를 조회할 좌표 범위인데요. 100,100,200,200,EPSG:3857은 최소 X 좌표값, 최소 Y 좌표값, 최대 X 좌표값, 최대 Y 좌표값, 좌표값의 좌표체계을 의미하며 해당 좌표체계로 지정된 좌표계 범위(Bounding Box)에 포함되는 Feature를 조회하라는 의미입니다.

레이어를 위한 데이터 소스가 준비되었으므로 레이어 객체를 생성할 수 있는데요. 해당 코드는 아래와 같습니다.

var vector = new ol.layer.Vector({
    source: vectorSource,
    style: new ol.style.Style({
        stroke: new ol.style.Stroke({
            color: 'rgba(255, 255, 0, 1.0)',
            width: 4
        }),
        fill: new ol.style.Fill({
            color: 'rgba(255,0,0,0.4)'
        })
    })
});

WFS 서비스를 위한 레이어는 ol.layer.Vector 타입에 대한 객체인데요. 이 객체의 생성을 위해 낲서 생성한 데이터 소스 객체인 vectorSource 변수를 생성자의 source 속성에 지정하고 style 속성에는 도형을 그릴때 사용할 수 있는 외곽선 스타일과 채움 스타일을 지정합니다.

다음은 WFS에 의한 벡터 레이어 밑에 배경맵으로 사용할 오픈 스트리트 맵에 대한 레이어 객체의 생성입니다.

var raster = new ol.layer.Tile({
    source: new ol.source.OSM()
});

이제 끝으로 앞서 생성한 2개의 레이어 객체, 즉 WFS에 의한 벡터 레이어와 오픈 스트리트 맵에 대한 레이어로 구성된 지도 객체를 생성합니다.

var map = new ol.Map({
    layers: [raster, vector],
    target: 'map',
    view: new ol.View({
        center: [-8910887.277395891, 5382318.072437216],
        maxZoom: 19,
        zoom: 15
    })
});

아래는 위에서 설명한 전체 코드에 대한 다운로드입니다.

[OpenLayers3] 지도 상에 팝업 정보창 표시

ol3에서 지도 상의 특정한 좌표 지점에 팝업창을 표시하여 해당 지점과 관련된 정보를 표시하는 기능에 대한 API를 살펴 보겠습니다. 먼저 아래의 웹 페이지에 표시된 지도를 클릭하면 클릭된 지점에 대한 경위도 좌표계를 도분초로 표시하는데요. 한번 확인해 보시기 바랍니다.

위의 지도 상의 팝업에 대한 기능을 구현하는 코드를 하나 하나 살펴 보겠습니다. 먼저 필요한 외부 CSS와 스크립트를 아래처럼 추가합니다.




다음은 필요한 UI에 대한 태그인데요. ol3에서는 팝업창에 대한 구체적인 UI에 대해 ol3 엔진단에서 제공하는 것이 아니고 개발자가 직접 제공해야 합니다. 팝업창을 구성하는 닫기 버튼이라든지, 실제 내용이 표시되는 div 영역이라든지 말입니다. 아래는 UI에 대한 태그입니다.

id가 map인 div에 지도가 표시됩니다. 그리고 id가 popup인 div가 실제 팝업창인데요. 이 div 내부에는 다시 id가 popup-closer이라는 a 태그가 있고, id가 popup-content인 div가 있습니다. popup-closer인 a 태그는 팝업을 닫는 버튼으로 사용되고, popup-content인 div는 팝업의 내용을 채울 컨테이너로 사용됩니다. 이제 각 UI에 대한 지정된 스타일에 대한 코드를 살펴 봅시다.





자, 이제 위의 지도 페이지에 대한 코드를 살펴보겠습니다. 먼저 필요한 CSS와 js 라이브러리를 포함하는 코드입니다.




다음으로 UI에 대한 코드를 살펴 보겠습니다. 지도가 표시되는 div가 있고, 이 div에 대한 크기를 css로 지정하고 있습니다.




    

이제 다음은 스크립트 코드에 대해 살펴볼텐데요. 먼저 웹 페이지의 모든 css 및 외부 라이브러리 로딩과 UI가 준비되었을 때 호출되는 jQuery의 ready 이벤트를 아래처럼 만들어 둡니다.


위의 코드 중 … 부분에 앞으로 더 많은 코드를 추가할 것인데요. Feature를 추가할 수 있는 레이어를 위한 데이터 소스는 ol.source.Vector입니다. 또한 이 소스를 통해 생성할 수 있는 레이어는 ol.layer.Vector이구요. 아래의 코드처럼 필요한 벡터 소스와 레이어에 대한 객체를 생성합니다.

var vectorSource = new ol.source.Vector();
var vectorLayer = new ol.layer.Vector({ source: vectorSource });

위에서 생성한 레이어는 지도에 추가되어야 합니다. 아래 코드의 6번째 줄을 보면 앞서 생성해 둔 레이어 객체 변수가 보입니다.

var map = new ol.Map({
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM()
        }),
        vectorLayer
    ],
    
    target: 'map',
    
    controls: ol.control.defaults({
        attributionOptions: ({
            collapsible: false
        })
    }),

    view: new ol.View({
        center: [14827315, 4785815],
        zoom: 5
    })
});

위의 코드를 좀더 살펴보면, 지도 객체의 생성을 위해 ol.Map의 생성자를 호출하고 있는데요. 이 생성자에서 받는 옵션 객체를 보면 layers 키 값으로 Open Street Map 레이어와 앞서 만들어 둔 벡터 레이어를 지정해 레이어를 구성하고 있습니다. 그리고 9번의 target은 지도 표시될 div의 id이구요. 17번은 초기에 지도가 표시될 좌표와 줌 레벨값을 지정하고 있습니다.

다음으로 4개의 Feature를 추가하는 사용자 정의 함수를 어래처럼 호출합니다.

addImagePoint(vectorSource);
addTextPoint(vectorSource);
addPolyline(vectorSource);
addPolygon(vectorSource);

먼저 addImagePoint 함수를 살펴보면 다음과 같습니다.

function addImagePoint(/* ol.source.Vector */ src) {
    var feature = new ol.Feature(
        {
            geometry: new ol.geom.Point([14827315, 4785815])
        }
    );

    var style = new ol.style.Style({
        image: new ol.style.Icon({
            src: 'http://www.gisdeveloper.co.kr/images/kochu.png',
            scale: 0.7,
        })
    });

    feature.setStyle(style);
    feature.set('name', '이미지 포인트 Feature');

    src.addFeature(feature);
}

Feature의 정의는 3가지가 필요합니다. 하나는 도형, 즉 지오메트리(Geometry)의 좌표값이고 이 도형을 그리기 위한 스타일이며, 마지막으로 속성값의 지정입니다. 2-6번 코드가 포인트 지오메트리의 좌표값을 지정하는 코드이며, 8~13번은 해당 포인트를 화면에 표시할때 이미지 스타일 객체를 생성하는 코드입니다. 그리고 15번의 setStyle을 통해 스타일을 지정합니다. 그리고 16번은 속성을 key 값과 value 값을 통해 지정합니다. 이렇게 생성된 피쳐는 벡터 레이어의 소스에 addFeature 함수를 통해 18번 코드처럼 추가됩니다.

나머지 3개에 대한 함수에 대한 코드는 아래와 같습니다.

function addPolygon(/* ol.source.Vector */ src) {
    var feature = new ol.Feature({
        geometry: new ol.geom.Polygon(
            [
                [
                    [13768449, 4871327],
                    [14556056, 5287144],
                    [14445986, 4166883],
                    [13995925, 3861135],
                    [13768449, 4871327],
                ]
            ]
        )
    });

    var style = new ol.style.Style({
        stroke: new ol.style.Stroke({
            color: 'blue',
            width: 3
        }),
        fill: new ol.style.Fill({
            color: 'rgba(0,0,255,0.6)'
        })
    });

    feature.setStyle(style);
    feature.set('name', '폴리곤 Feature');

    src.addFeature(feature);
}

function addPolyline(/* ol.source.Vector */ src) {
    var feature = new ol.Feature({
        geometry: new ol.geom.LineString([
            [16030985, 5565986],
            [15480638, 4318534],
            [14384837, 3780417],                            
        ])
    });

    var style = new ol.style.Style({
        stroke: new ol.style.Stroke({
            color: 'red',
            width: 4
        })
    });

    feature.setStyle(style);
    feature.set('name', '폴리라인 Feature');

    src.addFeature(feature);
}

function addTextPoint(/* ol.source.Vector */ src) {
    var feature = new ol.Feature({
        geometry: new ol.geom.Point([13778283, 4331832])
    });

    var style = new ol.style.Style({
        text: new ol.style.Text({
            text: "ol3",
            scale: 2,
            offsetY: 0,
            stroke: new ol.style.Stroke({
                color: 'black',
                width: 1
            }),
            fill: new ol.style.Fill({
                color: 'yellow'
            })
        })
    });

    feature.setStyle(style);
    feature.set('name', '텍스트 포인트 Feature');

    src.addFeature(feature);
}

각 함수를 보면 도형의 형태에 따른 지오메트리의 타입과 해당 좌표값의 지정에 대한 형태만 다르고 스타일이나 속성값을 지정하는 형태는 유사합니다. 여기서는 지오메트리에 대해 Point과 Polyline에 대한 LineString 그리고 Polygon 만을 언급하였는데요. 이외에도 MultiLineString, MultiPolygon, MultiPoint 등도 있습니다.

이제 끝으로 피쳐를 마우스로 클릭하면 해당 피쳐에 대한 속성값을 표시하는 코드를 살펴 보겠습니다.

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

    if (feature) {
        alert(feature.get('name'));
    }
});

지도에 대한 singleclick 이벤트를 등록했고, 이 이벤트를 통해 클릭한 피쳐와 피쳐의 소속 레이어를 얻을 수 있는 forEachFeatureAtPixel 함수를 이용하고 있습니다. 앞서 피쳐를 생성할때 name이라는 키값으로 속성값을 저장해 두었는데요. 위의 코드에서 사용하고 있는 것을 볼 수 있습니다.

아래는 위에서 설명한 전체 코드에 대한 다운로드입니다.

[OpenLayers3] 마우스로 도형 그리기

OL3에서 도형을 마우스로 그리기는 코드를 정리해 봅니다. OL3에서 마우스를 이용해 도형을 그리기 위한 목적으로 사용되는 레이어가 있는데요. 바로 ol.source.Vector를 소스(source)로 갖는 ol.layer.Vector 레이어입니다. 먼저 이 글에서 만들어 볼 웹 페이지는 아래와 같습니다.

그리고자 하는 도형을 선택하고 지도 위에서 마우스로 도형을 그릴 수 있습니다. 선이나 폴리곤의 경우 Shift 키를 누른 상태에서 마우스를 드래그하면 Free-Drawing 기능처럼 자유롭게 도형을 그려나갈 수 있습니다. 위의 지도 페이지에 대한 코드를 살펴 봅시다.

먼저 필요한 CSS와 라이브러리를 웹 페이지에 포함하기 위해 다음 코드를 입력합니다.






6번의 #map은 지도를 포함하는 div의 너비와 높이에 대한 스타일 설정입니다.

다음으로 UI에 대한 코드를 살펴 보겠습니다. 지도를 위한 div와 그리고자 하는 도형을 선택할 수 있는 select 컨트롤인데요. 다음과 같습니다.

select 컨트롤에 대한 중요한 부분은 select을 구성하는 항목(option)에 대한 value 속성값인데요. 이 속성값의 문자열 값을 그리고자 하는 도형으로 지정할때 그대로 사용됩니다.

다음으로 스크립트 코드를 살펴보겠습니다. jQuery의 ready 이벤트에 필요한 코드를 모두 작성할 것인데요. jQuery의 ready 이벤트를 아래처럼 입력해 놓습니다.


위의 …. 부분에 앞으로 설명한 코드가 입력되는데요. 먼저 Open Street Map을 레이어로 추가하기 위해 raster 변수를 정의합니다.

var raster = new ol.layer.Tile({
    source: new ol.source.OSM()
});

다음으로 지도 위에 마우스로 도형을 생성하기 위한 레이어를 위해 vector라는 변수를 아래처럼 정의합니다.

var source = new ol.source.Vector({ wrapX: false });

var vector = new ol.layer.Vector({
    source: source
});

위의 코드 중 1번은 vector 레이어에서 사용할 소스를 위한 변수 정의인데요. wrapX를 false로 줌으로써 화면 상의 동일한 좌표에 대해 중복으로 도형을 표시하지 않도록 합니다.

다음으로 지도를 위한 map 변수를 정의하는 코드인데요. 앞서 만들어 둔 raster와 vector 변수를 layers 속성 지정을 위해 사용하고 있고, 지도를 표시할 div의 id를 target 속성에 지정하고 있으며, view에 화면에 표시할 초기 지도 좌표와 줌 레벨 값을 지정하고 있습니다.

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

화면에 지도가 표시되고 마우스를 이용해 도형을 그릴 수 있는 레이어까지 추가해 두었습니다. 다음으로 select 컨트롤에서 지도에 그릴 도형을 선택하면 마우스를 이용해 도형을 그릴 수 있는 코드를 살펴보겠습니다.

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

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

function addInteraction() {
    var value = typeSelect.value;
    if (value !== 'None') {
        draw = new ol.interaction.Draw({
            source: source,
            type: (typeSelect.value)
        });
        
        map.addInteraction(draw);
    }
}

addInteraction();

먼저 그릴 도형을 선택할 수 있는 select 컨트롤을 어디서든 접근할 수 있도록 1번 코드에서 typeSelect 변수에 select 컨트롤을 id로 얻어 받아 둡니다. 그리고 2번에 draw 변수가 있는데요. 이 draw 변수는 우리가 선택한 도형을 그리기(Draw) 위한 사용자와의 상호작용(Interaction)에 대한 객체를 생성해 두고 지도 객체에 addInteraction 함수를 통해 추가하면 그리기 상호작용이 작동되며 필요한 때에 다시 removeInteraction 함수를 통해 제거하면 상호작용을 제거 할 수 있습니다. 4번 코드는 select 컨트롤에 대한 선택 항목이 변경될때 호출되는 이벤트인데요. 5~6번의 실행 코드를 보면 먼저 기존의 상호작용 객체를 제거하고 다시 addInteraction이라는 사용자 정의 함수를 호출하여 상호작용 객체를 추가해 줍니다. 9번에 addInteraction 함수의 정의가 있는데요. 선택 항목의 value 속성이 None이 아닌 경우에 12번 코드를 통해 ol.interaction.Draw 클래스 객체를 생성합니다. ol.interaction.Draw 객체를 생성할때 인자는 어떤 레이어의 소스에 사용자가 그릴 도형이 저장될지(source)와 도형의 타입(type)을 지정합니다. 이렇게 생성된 그리기 상호작용 객체를 17번에서 지도에 추가하면 바로 지도 상에 도형을 그릴 수 있게 됩니다. 마지막으로 21번에서 addInteraction 함수를 직접 호출해서 웹 페이지가 처음 뜰때 바로 도형을 그릴 수 있게 합니다.

아래는 위에서 설명한 전체 코드에 대한 다운로드입니다.