ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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.postgresql.org/

     

    PostgreSQL: Downloads

    Downloads PostgreSQL Downloads PostgreSQL is available for download as ready-to-use packages or installers for various platforms, as well as a source code archive if you want to build it yourself. Packages and Installers Select your operating system family

    www.postgresql.org

    설치가이드 : www.slideshare.net/ybh0616/postgis-101511460

     

    PostGIS 시작하기

    [1] 윈도우에서 PostgreSQL과 PostGIS 설치하기 [2] PostgreSQL/PostGIS에서 DB 생성하고 공간데이터 추가하기 [3] QGIS에서 PostGIS 레이어 추가하기 [4] PostgreSQL/PostGIS에서 SQL 언어 학습하기 [5] PostgreSQL/…

    www.slideshare.net

     

    *geoServer ( stable )

    geoserver.org/

     

    Download - GeoServer

    Maintenance Long term support, so you have time to upgrade. GeoServer 2.16 releases: Nightly builds for the 2.17.x series can be found here.

    geoserver.org

     

    war파일 Download

    다운로드 파일에서 .war파일만 ./Tomcat/webapps/ 경로에 넣어주고 서버를 재시작합니다.

     

    *Openlayers ( cdn 가능 )

    openlayers.org/

     

    OpenLayers - Welcome

    A high-performance, feature-packed library for all your mapping needs.

    openlayers.org

     

    설치 및 셋팅 이슈사항

    vworld 인증키 발급

    www.vworld.kr/v4po_main.do

     

    공간정보 오픈플랫폼 포털

    커뮤니티 지역에 대한 정보습득과 정보공유를 목적으로 하며, 지도기반의 다양한 정보를 효과적으로 공유 할 수 있습니다.

    www.vworld.kr

     

    CORS문제 해결

    https://cafe.naver.com/gisapplication/988 

     

    [GeoServer] WFS 이용시 CORS 크로스 도메인

    [GeoServer활용] WFS쿼리시 크로스도메인(cross-domain-policy) 해결하기먼저 WFS 및 WMS 서비스는일반적으로 지리 데이터에 액세스하기위한 OGC 웹...

    cafe.naver.com

     

    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

     

    서울시 법정구역 읍면동 공간정보 (좌표계: WGS1984)

    데이터 이용하기-서울시 법정구역 읍면동 공간정보 (좌표계: WGS1984)

    data.seoul.go.kr

    미리보기 > MAP > SHP.zip Download

    SHP파일을 서버에 등록

    1, 2, 3 순서대로 실행
    1. DB서버정보 입력
    2. Shp 파일 등록 

    3. Import 실행

     

    pgadmin4 실행
    tc_spbe17_2015_w SQL 확인
    geometry 컬럼이 wfs geometry정보를 넣게될 컬럼, 괄호안의 값은 Draw할때 도형의 속성


    [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  &nbsp;</label> 
    			<label> <input type="radio" name="interaction" value="draw" id="rdo_draw" > 
    				Draw &nbsp;
    			</label>
    			
    			<label> <input type="radio" name="interaction" value="modify" id="rdo_modify">
    				Modify &nbsp;
    			</label>
    			
    			<label> <input type="radio" name="interaction" value="delete" id="rdo_delete">
    				Delete &nbsp;
    			</label>
    			<button type="button" onClick="deleteItem()">삭제</button>
    		</div>
    		<div class="radio">
    		</div>
    		<div class="form-group">
    			<label>Draw type &nbsp;</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 &nbsp;</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

    댓글

Designed by Tistory.