Skip to content
Snippets Groups Projects
MapPOI.vue 15.02 KiB
<!--
    
  Copyright (c) 2022 NIBIO <http://www.nibio.no/>. 
  
  This file is part of VIPSObservationApp.
  VIPSObservationApp is free software: you can redistribute it and/or modify
  it under the terms of the NIBIO Open Source License as published by 
  NIBIO, either version 1 of the License, or (at your option) any
  later version.
  
  VIPSObservationApp is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  NIBIO Open Source License for more details.
  
  You should have received a copy of the NIBIO Open Source License
  along with VIPSObservationApp.  If not, see <http://www.nibio.no/licenses/>.
    
  Author: Bhabesh Bhabani Mukhopadhyay
  Author: Tor-Einar Skog <tor-einar.skog@nibio.no>
  Dated : 19-Aug-2021
    
-->
<template>
	<div>
		<router-link id='btnBack' :to="{name:'PlacesList', params: {}}" class="vips-btn">
			{{ $t("map.link.back.label") }}
		</router-link>
		<div v-show="userCanEditPOI" id='map-mylocation' style="margin: 5px;">
			<a v-on:click="myposition" style="cursor:pointer; ">
				<font-awesome-icon style="font-size: x-large; padding: 5px; color: white; background-color:#3d8052;"
					icon="location-crosshairs" /></a>
		</div>
		<div id='map-poi'></div>

		<div id='divPoiData' class="container">
			<div class="form-group"><input class="form-control" :placeholder="$t('mapPOI.poiName.label')" id='poiName'
					ref='poiName' v-model="poi.name" :disabled="!userCanEditPOI" /></div>

			<div class='form-group'>
				<select class="form-control" v-model="poi.pointOfInterestTypeId" :disabled="!userCanEditPOI">
					<option v-for="poiType in poiTypes" v-bind:key="poiType.point_of_interest_type_id"
						:value='poiType.point_of_interest_type_id' :disabled="!poiType.is_user_selectable">
						{{$t(poiType.default_name)}}</option>
				</select>
			</div>
			<div class="form-group" v-show="userCanEditPOI">
				<div class="float-right">
					<a class="vips-btn" v-on:click="validate">{{$t("save.label")}}</a>
					<a v-show="POIExists" class="vips-btn danger"
						v-on:click="callForRemovePOI">{{$t("delete.label")}}</a>
				</div>
			</div>
		</div>
		<div id="poiMarker" style="display:none">
			<img src="@/assets/map_icon.png">
		</div>
		<sync ref="sync" />
	</div>
</template>

<script>
	import CommonUtil from '@/components/CommonUtil';
	import Sync from '@/components/Sync'

	import 'ol/ol.css';
	import Map from 'ol/Map';
	import OSM from 'ol/source/OSM';
	import TileLayer from 'ol/layer/Tile';
	import View from 'ol/View';
	import WMTS, {optionsFromCapabilities} from 'ol/source/WMTS';
	import WMTSCapabilities from 'ol/format/WMTSCapabilities';

	import {Vector as VectorSource} from 'ol/source';
	import {Vector as VectorLayer} from 'ol/layer';
	import Collection from 'ol/Collection';
	import {fromLonLat} from 'ol/proj';

	import Circle from 'ol/geom/Circle';
	import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style';
	import Feature from 'ol/Feature';
	import GeoJSON from 'ol/format/GeoJSON';

	import {toStringXY} from 'ol/coordinate';
	import {transform} from 'ol/proj';
	import Point from 'ol/geom/Point';
	import {Modify} from 'ol/interaction';
	import Draw from 'ol/interaction/Draw';
	import Overlay from 'ol/Overlay';
	import Geolocation from 'ol/Geolocation';

	import i18n from '@/App'


	export default {
		name: 'MapPOI',
		components: {Sync},
		props: ['pointOfInterestId'],

		data() {
			return {
				poi: {},
				mapZoom: 0,
				poiTypes: [],
				msgErr: '',
				map: '',


			}
		},
		computed: {
			userCanEditPOI: function () {
				return this.isPOIOwnedByUser();
			},
			POIExists: function () {
				return this.poi.pointOfInterestId;
			}
		},
		methods: {

			isPOIOwnedByUser() {
				return !this.poi.pointOfInterestId || (this.poi.userId && this.poi.userId == this.$root.sharedState.userId);
			},
			callForRemovePOI() {
				if (confirm(this.$i18n.t("mapPOI.modal.label.deleteprompt") + " " + this.poi.name + "?")) {
					this.deletePOI();
				}
			},
			deletePOI() {
				if (this.poi.pointOfInterestId) {
					if (this.poi.pointOfInterestId < 0) {
						/** Just remove it locally */
						this.removeLocalPOI(this.poi.pointOfInterestId);
						this.$router.replace({path: '/places'});
					}
					else {
						/** Mark the record - for sending to server */
						this.poi.deleted = true;
						this.saveToStore();
					}

				}
			},
			removeLocalPOI(id) {
				let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST));
				let indexPosition = null;
				$.each(lstPOI, function (index, poi) {
					if (poi.pointOfInterestId === id) {
						indexPosition = index;
						return false;
					}
				});

				if (indexPosition) {

					lstPOI.splice(indexPosition, 1);
					localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI));

				}

			},
			validate() {
				if ((!this.poi.name) || (this.trimString(this.poi.name) === '')) {
					alert(this.$i18n.t("mapPOI.modal.alert.missingPOIName"));
					return;
				}
				if (!this.poi.geoJSON) {
					alert(this.$i18n.t("mapPOI.modal.alert.missingGeoInfo"));
					return;
				}

				// TODO Simplify this logic 
				if (!this.poi.pointOfInterestTypeId) {
					if (this.poi.pointOfInterestTypeId === 0) {
						// Pass
					}
					else {
						alert(this.$i18n.t("mapPOI.modal.alert.missingPOIType"));
						return false;
					}
				}

				if (confirm(this.$i18n.t("mapPOI.modal.label.saveprompt") + " " + this.poi.name + "?")) {
					this.saveToStore();
				}
			},
			getPointOfInterest(id) {
				let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST));
				let poi = lstPOI.find(({pointOfInterestId}) => pointOfInterestId === id);

				this.poi = poi;

			},
			saveToStore() {
				let This = this;
				let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST));
				if (this.poi.pointOfInterestId) {
					$.each(lstPOI, function (index, poi) {
						if (poi.pointOfInterestId === This.poi.pointOfInterestId) {
							poi.latitude = This.poi.latitude;
							poi.longitude = This.poi.longitude;
							poi.name = This.poi.name;
							poi.pointOfInterestTypeId = This.poi.pointOfInterestTypeId;
							poi.geoJSON = This.poi.geoJSON
							poi.uploaded = false;
							if (This.poi.deleted) {
								poi.deleted = This.poi.deleted;
							}

						}
					})
				}
				else {
					this.poi.pointOfInterestId = this.getNewPoiId(lstPOI);
					this.poi.userId = this.$root.sharedState.userId;
					this.poi.uploaded = false;
					if (lstPOI) {
						lstPOI.push(this.poi);
					}
					else {
						lstPOI = [];
						lstPOI.push(this.poi);
					}

				}
				localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI));
				this.$refs.sync.syncTwoWay();
				this.$router.push('/places');
			},
			/** new POI pointOfInterestId */
			getNewPoiId(lstPOI) {
				let newId = 0;
				let poiIds = [];


				if (lstPOI) {
					$.each(lstPOI, function (index, poi) {
						if (poi.pointOfInterestId < 0) {
							poiIds.push(Math.abs(poi.pointOfInterestId));
						}
					});
					if (poiIds.length === 0) {
						newId = CommonUtil.CONST_POI_COUNT_START_ID;
					}
					else {
						let largestValue = Math.max.apply(null, poiIds);
						newId = -Math.abs(largestValue + 1);
					}
				}
				else {
					newId = CommonUtil.CONST_POI_COUNT_START_ID;
				}

				return newId;
			},




			myImage() {
				var fill = new Fill({
					color: 'white'
				});

				return new CircleStyle({
					radius: 15,
					fill: fill,
					stroke: new Stroke({color: '#3d8052', width: 15}),
				});
			},
			myVectorGeoSource(geoInfo) {
				if (geoInfo) {
					return new VectorSource({
						features: new GeoJSON({
							dataProjection: "EPSG:4326",
							featureProjection: "EPSG:3857"
						}).readFeatures(geoInfo),
					})
				}
			},
			myVectorGeoLayer(vectorSource, image) {
				return new VectorLayer({
					source: vectorSource,
					style: new Style({
						image: image,
					}),
				})
			},

			myView(longitude, latitude, mapZoom) {
				return new View({
					center: fromLonLat([longitude, latitude]),
					zoom: mapZoom
				})
			},
			myOverLayCoord(longitude, latitude) {
				let coordinate = [longitude, latitude];
				return this.myOverLay(coordinate);
			},
			myOverLay(coordinates) {
				return new Overlay({
					position: fromLonLat(coordinates),
					positioning: 'bottom-center',
					element: document.getElementById('poiMarker'),
					stopEvent: false
				});
			},
			myInteractions(mapInteractions) {
				return (mapInteractions) ? [] : '';
			},

			/** My current location */
			myposition() {
				let options = {enableHighAccuracy: true};
				navigator.geolocation.getCurrentPosition(this.geolocationSuccess, this.geolocationError, options);
				//navigator.geolocation.getCurrentPosition(this.geolocationSuccess, this.geolocationError, this.geolocationOptions);


			},

			geolocationOptions(options) {
				console.log('geolocation options : ' + options);
			},
			geolocationError(error) {
				console.log('geolocation error : ' + geolocationError);
			},

			geolocationSuccess(pos) {
				let This = this;
				This.poi.latitude = pos.coords.latitude;
				This.poi.longitude = pos.coords.longitude;
				let coord = [pos.coords.longitude, pos.coords.latitude];

				// This is the marker icon for the location of the POI
				let vectorLayer = new VectorLayer({
					source: new VectorSource({
						features: [
							new Feature({
								geometry: new Point(fromLonLat(coord))
							})
						],
					}),
					style: new Style({
						image: This.myImage()
					})
				});
				let geoJSON = new GeoJSON();
				let resultGeoJSON = geoJSON.writeFeatures(vectorLayer.getSource().getFeatures(), {
					dataProjection: 'EPSG:4326',
					featureProjection: 'EPSG:3857'
				});
				this.poi.geoJSON = resultGeoJSON;

				if (This.map) {
					let mapLayers = This.map.getLayers();
					mapLayers.forEach(function (layer) {
						let source = layer.get('source');
						source.clear();
					})
					This.map.addLayer(vectorLayer);
					This.map.getView().setCenter(fromLonLat(coord));
					This.map.getView().setZoom(CommonUtil.CONST_GPS_OBSERVATION_ZOOM);
				}
			},

			mapInit() {
				let This = this;
				let urlMap = CommonUtil.CONST_GPS_URL_NORWAY_MAP;
				let latitude = this.poi.latitude;
				let longitude = this.poi.longitude;
				let mapZoom = this.mapZoom;

				let geoInfo = '';
				if (this.poi && this.poi.geoJSON) {
					geoInfo = JSON.parse(this.poi.geoJSON);
				}
				else {
					let image = this.myImage();
					latitude = CommonUtil.CONST_GPS_DEFAULT_LATITUDE_02_NORWAY;
					longitude = CommonUtil.CONST_GPS_DEFAULT_LONGITUDE_02_NORWAY;
					mapZoom = CommonUtil.CONST_GPS_DEFAULT_ZOOM;

					let coord = [CommonUtil.CONST_GPS_DEFAULT_LONGITUDE_02_NORWAY, CommonUtil.CONST_GPS_DEFAULT_LATITUDE_02_NORWAY];
					let transFormCord = transform(coord, 'EPSG:3857', 'EPSG:4326');
					let iconFeature = new Feature({
						geometry: new Point(fromLonLat(transFormCord))
					});

					let vectorSource = new VectorSource({});
					vectorSource.addFeature(iconFeature);
					let vectorLayer = new VectorLayer({
						source: vectorSource,
						style: new Style({
							image: image,
						}),
					});

					let geoGSON = new GeoJSON();
					let resultGeoGSON = JSON.parse(geoGSON.writeFeatures(vectorLayer.getSource().getFeatures()));
					//this.poi.geoJSON        =   JSON.stringify(resultGeoGSON);
					geoInfo = resultGeoGSON;

				}

				// Set default POI type to field
				if (!this.poi.pointOfInterestTypeId) {
					this.poi.pointOfInterestTypeId = CommonUtil.CONST_POI_TYPE_DEFAULT;
				}

				fetch(urlMap)
					.then(function (response) {
						return response.text();
					})
					.then(function (text) {
						let parser = new WMTSCapabilities();
						var result = parser.read(text);
						var options = optionsFromCapabilities(result, {
							layer: 'topo4',
							matrixSet: 'EPSG:3857',
						});

						This.map = new Map({
							layers: [
								new TileLayer({
									opacity: 1,
									source: new WMTS(options),
								}),
								new VectorLayer({
									source: new VectorSource({
										features: new GeoJSON({
											dataProjection: "EPSG:4326",
											featureProjection: "EPSG:3857"
										}).readFeatures(geoInfo),
									}),
									style: new Style({
										image: This.myImage(),
									}),
								}),

							],
							controls: [],
							target: 'map-poi',
							view: new View({
								center: fromLonLat([longitude, latitude]),
								zoom: mapZoom,
							}),
						});

						/** Remove map pointer for first time newly poi  */
						if (!This.poi.longitude) {

							let mapLayers = This.map.getLayers();
							mapLayers.forEach(function (layer) {
								let source = layer.get('source');
								source.clear();
							})
						}

						This.map.on(['singleclick'], function (event) {
							if (!This.userCanEditPOI) {
								return;
							}
							let transFormCord = transform(event.coordinate, 'EPSG:3857', 'EPSG:4326');
							This.poi.longitude = transFormCord[0];
							This.poi.latitude = transFormCord[1];

							This.map.getView().setCenter(fromLonLat(transFormCord));

							let mapLayers = This.map.getLayers();
							mapLayers.forEach(function (layer) {
								let source = layer.get('source');
								source.clear();
							})

							let vectorLayer = new VectorLayer({
								source: new VectorSource({
									features: [
										new Feature({
											geometry: new Point(fromLonLat(transFormCord))
										})
									],
								}),
								style: new Style({
									image: This.myImage()
								})
							});

							This.map.addLayer(vectorLayer);

							let geoGSON = new GeoJSON();
							let resultGeoGSON = geoGSON.writeFeatures(vectorLayer.getSource().getFeatures(), {
								dataProjection: 'EPSG:4326',
								featureProjection: 'EPSG:3857'
							})
							This.poi.geoJSON = resultGeoGSON;

						})




					});

			},
			trimString(param) {
				return param.replace(/\s*/g, "")
			}


		},
		filters: {

			trim: function (string) {
				return string.trim()
			}

		},
		mounted() {
			var appDiv = document.getElementById("app");
			var navDiv = document.getElementById("vipsobsappmenu");
			var mapDiv = document.getElementById("map-poi");
			appDiv.style.marginTop = "0";
			appDiv.style.paddingRight = "0";
			appDiv.style.paddingLeft = "0";
			mapDiv.style.height = (screen.height - navDiv.offsetHeight) + "px";

			this.mapZoom = CommonUtil.CONST_GPS_OBSERVATION_ZOOM;
			this.poiTypes = CommonUtil.CONST_POI_TYPES;
			//console.info(this.poiTypes);

			if (this.$route.params.pointOfInterestId) {
				this.getPointOfInterest(this.$route.params.pointOfInterestId);
			}
			this.mapInit();


		},
		beforeDestroy() {
			// This resets the container layout when leaving the router page
			var appDiv = document.getElementById("app");
			appDiv.style.marginTop = "15px";
			appDiv.style.paddingRight = "15px";
			appDiv.style.paddingLeft = "15px";
		},
	}
</script>

<style>
	html,
	body,
	#map-poi {
		margin: 0;
		width: 100%;
	}

	#btnBack {
		position: fixed;
		z-index: 1000;
	}

	#map-mylocation {
		position: fixed;
		right: 0;
		z-index: 2000;
	}

	#divPoiData {
		position: fixed;
		z-index: 1100;
		bottom: 0;
	}
</style>