-
[Javascript] OpenLayers + GeoServer + PostGIS + VWORLD + WFS,WMS기초/자바스크립트 2020. 11. 11. 16:23
[목표]
제공받은 SHP파일을 DB에 저장하고 웹페이지에서 데이터를 불러와 수정할 수 있는 사이트를 개발
[PPT 정리]
drive.google.com/file/d/1rcVbrXGgZy5zdDSkk-uvDB16624rB0cw/view?usp=sharing
[환경설정]
*Windows 10 64bit
*Tomcat 9.0
*Java OpenJDK 1.8.0
[설치프로그램]
*postgreSQL
설치가이드 : www.slideshare.net/ybh0616/postgis-101511460
*geoServer ( stable )
다운로드 파일에서 .war파일만 ./Tomcat/webapps/ 경로에 넣어주고 서버를 재시작합니다.
*Openlayers ( cdn 가능 )
설치 및 셋팅 이슈사항
vworld 인증키 발급
CORS문제 해결
https://cafe.naver.com/gisapplication/988
libsqlite3-0 not found 시
libsql3-0.dll Download로드 후 C:\Program Files\PostgreSQL\13\bin\postgisgui에 넣기
http://internetaccessmonitor.ru/dll/libsqlite3-0.dll
[Shp 파일 PostGIS에 등록]
예제 SHP 파일
data.seoul.go.kr/dataList/OA-13221/S/1/datasetView.do
SHP파일을 서버에 등록
3. Import 실행
[GeoServer - PostGIS 연결]
[geoserver 주소] : 톰캣 주소/geoserver , 도메인/geoserver/web/
처음 계정 : admin/geoserver
기본 작업공간, 저장소, 레이어 전부삭제
[GeoServer 설정]
*서비스의 모든탭 사용
레이어 미리보기로 확인
[Openlayers 연동]
1. WMS
- 이미지로 띄움
- 수정불가
- 속도 빠름, DB정보 표출가능
[wms.html]
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <title>환경주제도 맵 예제(wms)</title> <link rel="stylesheet" type="text/css" href="/resources/ol.css"> <link rel="stylesheet" type="text/css" href="/resources/wms.css"> <script src="/resources/ol.js"></script> </head> <body> <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer --> <div id="map" class="map"></div> <div id="popup" class="ol-popup"> <a href="#" id="popup-closer" class="ol-popup-closer"></a> <div id="popup-content"></div> </div> <div id = "info"> </div> <script src="/resources/wms.js"></script> </body> </html>
[wms.css]
@charset "UTF-8"; <style> html, body, #map { margin: 0; padding: 0; width: 100%; height: 100%; } .map { width: 100%; height:400px; } .ol-popup { position: absolute; background-color: white; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); padding: 15px; border-radius: 10px; border: 1px solid #cccccc; bottom: 12px; left: -50px; min-width: 280px; } .ol-popup:after, .ol-popup:before { top: 100%; border: solid transparent; content: " "; height: 0; width: 0; position: absolute; pointer-events: none; } .ol-popup:after { border-top-color: white; border-width: 10px; left: 48px; margin-left: -10px; } .ol-popup:before { border-top-color: #cccccc; border-width: 11px; left: 48px; margin-left: -11px; } .ol-popup-closer { text-decoration: none; position: absolute; top: 2px; right: 8px; } .ol-popup-closer:after { content: "✖"; } </style>
[wms.js]
/** * Elements that make up the popup. */ var container1 = document.getElementById('popup'); var content1 = document.getElementById('popup-content'); var closer1 = document.getElementById('popup-closer'); /** * Create an overlay to anchor the popup to the map. */ var overlay = new ol.Overlay({ element : container1, autoPan : true, autoPanAnimation : { duration : 250, }, }); /** * Add a click handler to hide the popup. * @return {boolean} Don't follow the href. */ closer1.onclick = function () { overlay.setPosition(undefined); closer1.blur(); return false; }; var wmsSource = new ol.source.TileWMS({ url: '/geoserver/seoul/wms', params: { 'layers':'seoul:tc_spbe17_2015_w', 'TILED':true}, serverType: 'geoserver', // Countries have transparency, so do not fade tiles: transition: 0, projection: 'EPSG:4326', }); var layers = [ new ol.layer.Tile({ source : new ol.source.XYZ({ // Vworld Tile 변경 url : 'http://api.vworld.kr/req/wmts/1.0.0/[인증키]/Satellite/{z}/{y}/{x}.jpeg' }) }), new ol.layer.Tile({ source : wmsSource, }) ]; var view = new ol.View({ center: [14128579.82, 4512570.74], zoom: 11 }); var map = new ol.Map({ layers: layers, overlays: [overlay], target: 'map', view: view, }); map.on('singleclick', function (evt) { var viewResolution = /** @type {number} */(view.getResolution()); var url = wmsSource.getGetFeatureInfoUrl(evt.coordinate, viewResolution, 'EPSG:3857', { 'INFO_FORMAT' : 'text/html', 'QUERY_LAYERS' : 'tc_spbe17_2015_w' }); if (url) { fetch(url).then(function(response) { return response.text(); }).then(function(html) { document.getElementById('info').innerHTML = html; content1.innerHTML = html; overlay.setPosition(evt.coordinate); }); } });
[Openlayers 연동2]
2. WFS
- Vector로 띄움
- 수정가능
- 속도 느림
[wfs.html]
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <title>공간지도 서비스(wfs)</title> <style type="text/css"> html, body, #map { margin: 0; padding: 0; width: 100%; height: 100%; } </style> <link rel="stylesheet" type="text/css" href="/resources/ol.css"> <link rel="stylesheet" type="text/css" href="/resources/wfs.css"> <script src="/resources/ol.js"></script> </head> <body> <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer --> <div id="map" class="map"></div> <form id="options-form" autocomplete="off"> <div class="radio"> <label>Mode </label> <label> <input type="radio" name="interaction" value="draw" id="rdo_draw" > Draw </label> <label> <input type="radio" name="interaction" value="modify" id="rdo_modify"> Modify </label> <label> <input type="radio" name="interaction" value="delete" id="rdo_delete"> Delete </label> <button type="button" onClick="deleteItem()">삭제</button> </div> <div class="radio"> </div> <div class="form-group"> <label>Draw type </label> <select name="draw-type" id="draw-type"> <option value="Point">Point</option> <option value="LineString">LineString</option> <option value="Polygon">Polygon</option> <option value="Circle">Circle</option> </select> </div> <div class="form-group"> <label>Map type </label> <select name="map-type" id="map-type"> <option value="tc_spbe17_2015_w">tc_spbe17_2015_w</option> <!-- map 추가 --> </select> </div> </form> <script src="/resources/wfs.js"></script> </body> </html>
[wfs.css]
@charset "UTF-8"; <style> .map { width: 100%; height:400px; } </style>
[wfs.js]
/* ServerSide Value */ var formatWFS = new ol.format.WFS(); var xs = new XMLSerializer(); //맵생성 시 추가 var gmlJson = {'tc_spbe17_2015_w':{}}; var mapJson = {}; var target_map='tc_spbe17_2015_w'; /* GML 데이터 */ //맵생성시 추가 gmlJson['tc_spbe17_2015_w']['insert'] = new ol.format.GML({ featureNS : 'seoul', featureType : 'tc_spbe17_2015_w', srsName: 'EPSG:3857' }); gmlJson['tc_spbe17_2015_w']['upddel'] = new ol.format.GML({ featureNS : 'http://localhost/geoserver/seoul/ows', featureType : 'tc_spbe17_2015_w', srsName: 'EPSG:3857' }); function transactWFS(mode, f) { var node; switch (mode) { case 'insert': node = formatWFS.writeTransaction([ f ], null, null, gmlJson[target_map]['insert']); break; case 'update': node = formatWFS.writeTransaction(null, [ f ], null, gmlJson[target_map]['upddel']); break; case 'delete': node = formatWFS.writeTransaction(null, null, [ f ], gmlJson[target_map]['upddel']); break; } var payload = xs.serializeToString(node); switch (mode) { case 'update': var payload=payload.replace("feature:"+target_map,"seoul:"+target_map); break; } $.ajax('http://localhost/geoserver/seoul/ows', { type : 'POST', dataType : 'xml', processData : false, contentType : 'text/xml', data : payload }).done(function(data1,data2,data3) { console.log(data1); console.log(data2); //mapJson[target_map].clear(); }).always(function(){ switch (mode) { case 'insert': var propNodes = node.getElementsByTagName("Property"); for (var i = 0; i < propNodes.length; i++) { var propNode = propNodes[i]; var propNameNode = propNode.firstElementChild; var propNameNodeValue = propNameNode.firstChild; if (propNameNodeValue.nodeValue === "geometry") { propNode.parentNode.removeChild(propNode); break; } } break; } }); }; /* 맵데이터 */ temp_tc_spbe17_2015_w = new ol.source.Vector({ loader: function (extent) { $.ajax('http://localhost/geoserver/seoul/ows', { type: 'GET', data: { service: 'WFS', version: '1.1.0', request: 'GetFeature', typename: 'seoul:tc_spbe17_2015_w', srsname: 'EPSG:3857', bbox: extent.join(',') + ',EPSG:3857' } }).done(function (response) { temp_tc_spbe17_2015_w.addFeatures(formatWFS.readFeatures(response)); }); }, strategy: ol.loadingstrategy.bbox, projection: 'EPSG:3857' }); mapJson['tc_spbe17_2015_w'] = new ol.layer.Vector({ source : temp_tc_spbe17_2015_w, style : new ol.style.Style({ fill : new ol.style.Fill({ color : 'rgba(255, 0, 0, 0.2)', }), stroke : new ol.style.Stroke({ color : '#ff0000', width : 2, }), image : new ol.style.Circle({ radius : 7, fill : new ol.style.Fill({ color : '#ff0000', }), }), }), }); var map = new ol.Map({ layers : [ new ol.layer.Tile({ source : new ol.source.XYZ({ url : 'http://api.vworld.kr/req/wmts/1.0.0/[인증키]/Satellite/{z}/{y}/{x}.jpeg' }) }), mapJson['tc_spbe17_2015_w'] ], target : document.getElementById('map'), view : new ol.View({ center : [ 14128579.82, 4512570.74 ], maxZoom : 19, zoom : 11 }) }); /* Delete */ var ExampleDelete = { init : function() { this.select = new ol.interaction.Select(); map.addInteraction(this.select); this.setEvents(); this.setActive(false); this.selectItem = null; }, setEvents : function() { var selectedFeatures = this.select.getFeatures(); selectedFeatures.on('add', function(event){ this.selectItem = event.element; }, this); selectedFeatures.on('remove', function(event){ this.selectItem = null; }, this); }, setActive : function(active) { this.select.setActive(active); }, }; ExampleDelete.init(); function deleteItem(){ if( rdo_delete.checked == true ){ if( ExampleDelete.selectItem != null ){ if (confirm("정말 삭제하시겠습니까?")) { transactWFS('delete', ExampleDelete.selectItem); mapJson[target_map].getSource().removeFeature(ExampleDelete.selectItem); ExampleDelete.select.getFeatures().clear(); } } } } /* ModiFy */ var ExampleModify = { init : function() { this.select = new ol.interaction.Select(); map.addInteraction(this.select); this.modify = new ol.interaction.Modify({ features : this.select.getFeatures(), }); map.addInteraction(this.modify); this.setEvents(); this.setActive(false); }, setEvents : function() { var selectedFeatures = this.select.getFeatures(); this.select.on('change:active', function() { selectedFeatures.forEach(function(each) { selectedFeatures.remove(each); }); }); this.dirty = {}; this.select.getFeatures().on('add', function (e) { e.element.on('change', function (e) { ExampleModify.dirty[e.target.getId()] = true; }); }); this.select.getFeatures().on('remove', function (e) { var f = e.element; if (ExampleModify.dirty[f.getId()]) { delete ExampleModify.dirty[f.getId()]; var featureProperties = f.getProperties(); delete featureProperties.boundedBy; var clone = new ol.Feature(featureProperties); clone.setId(f.getId()); transactWFS('update', clone); } }); }, setActive : function(active) { this.select.setActive(active); this.modify.setActive(active); }, }; ExampleModify.init(); /* Draw */ var optionsForm = document.getElementById('options-form'); var drawObj = null; var ExampleDraw = { init : function() { this.setDrawType(); map.addInteraction(this.Point); map.addInteraction(this.LineString); map.addInteraction(this.Polygon); map.addInteraction(this.Circle); this.Point.on('drawend',function(e){ transactWFS('insert', e.feature); }) this.LineString.on('drawend',function(e){ transactWFS('insert', e.feature); }) this.Polygon.on('drawend',function(e){ var f = e.feature; f.set('emd_cd', '11111111'); f.set('emd_nm', '테스트'); f.set('emd_eng_nm', 'test'); f.setGeometryName('geom'); drawObj = f; transactWFS('insert', e.feature); }) this.Circle.on('drawend',function(e){ transactWFS('insert', e.feature); }) this.Point.setActive(false); this.LineString.setActive(false); this.Polygon.setActive(false); this.Circle.setActive(false); // The snap interaction must be added after the Modify and Draw interactions // in order for its map browser event handlers to be fired first. Its handlers // are responsible of doing the snapping. map.addInteraction(this.snap); }, setDrawType:function(){ var source = mapJson[target_map].getSource(); this.Point = new ol.interaction.Draw({ source : source, type : 'Point', geometryName : 'geom', }); this.LineString = new ol.interaction.Draw({ source : source, type : 'MultiLineString', geometryName : 'geom', }); this.Polygon = new ol.interaction.Draw({ source : source, type : 'MultiPolygon', geometryName : 'geom', }); this.Circle = new ol.interaction.Draw({ source : source, type : 'Circle', geometryName : 'geom', }); this.snap = new ol.interaction.Snap({ source: source, }); }, getActive : function() { return this.activeType ? this[this.activeType].getActive() : false; }, setActive : function(active) { var type = optionsForm.elements['draw-type'].value; if (active) { this.activeType && this[this.activeType].setActive(false); this[type].setActive(true); this.activeType = type; } else { this.activeType && this[this.activeType].setActive(false); this.activeType = null; } }, removeInteraction: function(){ map.removeInteraction(this.Point); map.removeInteraction(this.LineString); map.removeInteraction(this.Polygon); map.removeInteraction(this.Circle); map.removeInteraction(this.snap); } }; ExampleDraw.init(); /** * Let user change the geometry type. * @param {Event} e Change event. */ optionsForm.onchange = function (e) { var type = e.target.getAttribute('name'); var value = e.target.value; if (type == 'draw-type') { ExampleDraw.getActive() && ExampleDraw.setActive(true); }else if(type == 'map-type'){ if(rdo_draw.checked){ rdo_draw.checked = false; }else if(rdo_modify.checked){ ExampleModify.setActive(false); ExampleModify.setActive(true); }else if(rdo_delete.checked){ ExampleDelete.setActive(false); ExampleDelete.setActive(true); } map.removeLayer(mapJson[target_map]); map.addLayer(mapJson[value]); ExampleDraw.removeInteraction(); target_map = value; ExampleDraw.init(); } else if (type == 'interaction') { if (value == 'modify') { ExampleDraw.setActive(false); ExampleModify.setActive(true); ExampleDelete.setActive(false); } else if (value == 'draw') { ExampleDraw.setActive(true); ExampleModify.setActive(false); ExampleDelete.setActive(false); } else if (value == 'delete') { ExampleDraw.setActive(false); ExampleModify.setActive(false); ExampleDelete.setActive(true); } } };
wms화면
wfs 화면
Draw : Drawend 이벤트를 통하여 자동 반영 ( 이때 위에 postgis의 DB 테이블에서 geometery 컬럼을 확인하여 컬럼이름과 Geometry 도형중 어떤도형이 들어가는지 확인하여 그 도형만 그릴것, 예제는 MultiPolygon )
Update : remove 이벤트를 통하여 자동 반영
Delete : Prompt 호출 후 응답여부에 따라 저장
'기초 > 자바스크립트' 카테고리의 다른 글
[JS] 일급 함수와 고차함수 (0) 2022.12.14 GridSatck (Test.html) (0) 2022.03.16 [JS] 모자이크 처리 (2) 2022.03.04 [JS] 기초 훈련(내장함수) (0) 2017.04.26 [JS] 시작 (0) 2017.04.26