-
Tor-Einar Skog authoredTor-Einar Skog authored
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>