From 1aec00958ac2e7077ccb6a84c8bb7b8d892ecf90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poussard?= <tpoussard@neogeo.fr> Date: Tue, 22 Aug 2023 18:39:10 +0200 Subject: [PATCH 1/4] add geolocation tracking to feature edit --- src/services/map-service.js | 73 ++++++++++++++++++++++++++++++- src/views/Feature/FeatureEdit.vue | 50 +++++++++++++++++++-- 2 files changed, 118 insertions(+), 5 deletions(-) diff --git a/src/services/map-service.js b/src/services/map-service.js index 5aefb5ea..e936cc9f 100644 --- a/src/services/map-service.js +++ b/src/services/map-service.js @@ -2,7 +2,7 @@ import TileWMS from 'ol/source/TileWMS'; import { View, Map } from 'ol'; import { ScaleLine, Zoom, Attribution, FullScreen } from 'ol/control'; import TileLayer from 'ol/layer/Tile'; -import { transform, transformExtent } from 'ol/proj.js'; +import { transform, transformExtent, fromLonLat } from 'ol/proj.js'; import { defaults } from 'ol/interaction'; import XYZ from 'ol/source/XYZ'; import VectorTileLayer from 'ol/layer/VectorTile'; @@ -16,8 +16,10 @@ import { import { asArray } from 'ol/color'; import VectorSource from 'ol/source/Vector'; import VectorLayer from 'ol/layer/Vector'; -import { fromLonLat } from 'ol/proj.js'; import OverlayPositioning from 'ol/OverlayPositioning'; +import Geolocation from 'ol/Geolocation.js'; +import Feature from 'ol/Feature.js'; +import Point from 'ol/geom/Point.js'; import axios from '@/axios-client.js'; import router from '@/router'; @@ -39,6 +41,8 @@ const mapService = { queryParams: {}, + geolocation: undefined, + getMap() { return this.map; @@ -58,6 +62,7 @@ const mapService = { zoom, zoomControl = true, fullScreenControl = false, + geolocationControl = false, interactions = { doubleClickZoom: false, mouseWheelZoom: false, dragPan: true }, controls = [ new Attribution({ collapsible: false }), @@ -92,6 +97,10 @@ const mapService = { if (zoomControl) { this.map.addControl(new Zoom({ zoomInTipLabel: 'Zoomer', zoomOutTipLabel: 'Dézoomer' })); } + if (geolocationControl) { + this.initGeolocation(); + } + this.map.once('rendercomplete', () => { this.map.updateSize(); }); @@ -120,6 +129,66 @@ const mapService = { return this.map; }, + initGeolocation() { + this.geolocation = new Geolocation({ + // enableHighAccuracy must be set to true to have the heading value. + trackingOptions: { + enableHighAccuracy: true, + }, + projection: this.map.getView().getProjection(), + }); + + // handle this.geolocation error. + this.geolocation.on('error', (error) => { + console.error(error.message); + }); + + /* const accuracyFeature = new Feature(); + this.geolocation.on('change:accuracyGeometry', () => { + accuracyFeature.setGeometry(this.geolocation.getAccuracyGeometry()); + }); */ + + const positionFeature = new Feature(); + positionFeature.setStyle( + new Style({ + image: new Circle({ + radius: 6, + fill: new Fill({ + color: '#3399CC', + }), + stroke: new Stroke({ + color: '#fff', + width: 2, + }), + }), + }) + ); + + this.geolocation.on('change:position', () => { + const coordinates = this.geolocation.getPosition(); + positionFeature.setGeometry(coordinates ? new Point(coordinates) : null); + if (coordinates) { + this.map.getView().animate( + { zoom: 16 }, + { center: coordinates }, + { duration: 1000 } + ); + } + }); + + new VectorLayer({ + map: this.map, + source: new VectorSource({ + features: [positionFeature], + //features: [accuracyFeature, positionFeature], + }), + }); + console.log('initGeolocation'); + }, + + toggleGeolocation(value) { + this.geolocation.setTracking(value); + }, addRouterToPopup({ featureId, featureTypeSlug, index }) { diff --git a/src/views/Feature/FeatureEdit.vue b/src/views/Feature/FeatureEdit.vue index b156f07c..9e3f4533 100644 --- a/src/views/Feature/FeatureEdit.vue +++ b/src/views/Feature/FeatureEdit.vue @@ -15,8 +15,6 @@ <form id="form-feature-edit" - action="" - method="post" enctype="multipart/form-data" class="ui form" > @@ -252,6 +250,16 @@ ref="map" > <SidebarLayers v-if="basemaps && map" /> + + <div class="geolocation-container"> + <button + class="button-geolocation" + @click.prevent="toggleTracking" + > + <i class="crosshairs icon" /> + </button> + </div> + <Geocoder /> <EditingToolbar v-if="isEditable" @@ -445,6 +453,7 @@ export default { value: null, }, }, + tracking: false, }; }, @@ -667,6 +676,11 @@ export default { this.showGeoRef = !this.showGeoRef; }, + toggleTracking() { + this.tracking = !this.tracking; + mapService.toggleGeolocation(this.tracking); + }, + handleFileUpload() { this.erreurUploadMessage = ''; this.file = this.$refs.file.files[0]; @@ -970,7 +984,8 @@ export default { mapDefaultViewZoom, maxZoom: this.project.map_max_zoom_level, interactions : { doubleClickZoom :false, mouseWheelZoom:true, dragPan:true }, - fullScreenControl: true + fullScreenControl: true, + geolocationControl: true, }); const currentFeatureId = this.$route.params.slug_signal; const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}v2/features/?feature_type__slug=${this.$route.params.slug_type_signal}&project__slug=${this.$route.params.slug}&output=geojson`; @@ -1035,6 +1050,35 @@ export default { width: 100%; border: 1px solid grey; } + + +div.geolocation-container { + position: absolute; + right: 0.5em; + /* each button have .5em space between, zoom buttons are 60px high and full screen button is 34px high */ + top: calc(1.5em + 60px + 34px + 100px); + z-index: 1000; + border: 2px solid rgba(0,0,0,.2); + background-clip: padding-box; + padding: 0; + border-radius: 2px; + display: flex; +} +button.button-geolocation { + border: none; + padding: 0; + margin: 0; + text-align: center; + background-color: #fff; + color: rgb(39, 39, 39); + width: 28px; + height: 28px; + font: 700 18px Lucida Console,Monaco,monospace; + border-radius: 2px; + line-height: 1.15; + cursor: pointer; +} + #get-geom-from-image-file { margin-bottom: 5px; } -- GitLab From c5f84f22dc316be39a6f777eb5809e56f7475f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poussard?= <tpoussard@neogeo.fr> Date: Wed, 23 Aug 2023 12:03:44 +0200 Subject: [PATCH 2/4] toggel geolocation display, create component and style --- src/components/Map/Geolocation.vue | 68 ++++++++++++++++++++++++++++++ src/services/map-service.js | 58 +++++++++++++++---------- src/views/Feature/FeatureEdit.vue | 43 ++----------------- 3 files changed, 108 insertions(+), 61 deletions(-) create mode 100644 src/components/Map/Geolocation.vue diff --git a/src/components/Map/Geolocation.vue b/src/components/Map/Geolocation.vue new file mode 100644 index 00000000..51cced95 --- /dev/null +++ b/src/components/Map/Geolocation.vue @@ -0,0 +1,68 @@ +<template> + <div class="geolocation-container"> + <button + :class="['button-geolocation', { tracking }]" + @click.prevent="toggleTracking" + > + <i class="crosshairs icon" /> + </button> + </div> +</template> + +<script> + +import mapService from '@/services/map-service'; + +export default { + name: 'Geolocation', + + data() { + return { + tracking: false, + }; + }, + + methods: { + toggleTracking() { + this.tracking = !this.tracking; + mapService.toggleGeolocation(this.tracking); + }, + } +}; +</script> + +<style> +div.geolocation-container { + position: absolute; + right: 0.5em; + /* each button have .5em space between, zoom buttons are 60px high and full screen button is 34px high */ + z-index: 1000; + border: 2px solid rgba(0,0,0,.2); + background-clip: padding-box; + padding: 0; + border-radius: 2px; + display: flex; +} +button.button-geolocation { + border: none; + padding: 0; + margin: 0; + text-align: center; + background-color: #fff; + color: rgb(39, 39, 39); + width: 28px; + height: 28px; + font: 700 18px Lucida Console,Monaco,monospace; + border-radius: 2px; + line-height: 1.15; + cursor: pointer; +} +button.button-geolocation.tracking { + background-color: rgba(255, 145, 0, 0.904); + color: #fff; +} +button.button-geolocation i { + margin: 0; + vertical-align: top; /* strangely top is the only value that center at middle */ +} +</style> diff --git a/src/services/map-service.js b/src/services/map-service.js index e936cc9f..7d2330de 100644 --- a/src/services/map-service.js +++ b/src/services/map-service.js @@ -28,6 +28,19 @@ import { statusChoices } from '@/utils'; let dictLayersToMap = {}; +const geolocationStyle = new Style({ + image: new Circle({ + radius: 6, + fill: new Fill({ + color: '#3399CC', + }), + stroke: new Stroke({ + color: '#fff', + width: 2, + }), + }), +}) + const mapService = { layers: [], @@ -43,6 +56,7 @@ const mapService = { geolocation: undefined, + geolocationSource: null, getMap() { return this.map; @@ -149,20 +163,7 @@ const mapService = { }); */ const positionFeature = new Feature(); - positionFeature.setStyle( - new Style({ - image: new Circle({ - radius: 6, - fill: new Fill({ - color: '#3399CC', - }), - stroke: new Stroke({ - color: '#fff', - width: 2, - }), - }), - }) - ); + positionFeature.setStyle( geolocationStyle ); this.geolocation.on('change:position', () => { const coordinates = this.geolocation.getPosition(); @@ -175,19 +176,32 @@ const mapService = { ); } }); - + + this.geolocationSource = new VectorSource({ + features: [positionFeature], + //features: [accuracyFeature, positionFeature], + }); new VectorLayer({ map: this.map, - source: new VectorSource({ - features: [positionFeature], - //features: [accuracyFeature, positionFeature], - }), + source: this.geolocationSource, }); - console.log('initGeolocation'); }, - toggleGeolocation(value) { - this.geolocation.setTracking(value); + displayGeolocationPoint(isVisible) { + let features = this.geolocationSource.getFeatures(); + if (!features) return; + const hiddenStyle = new Style(); // hide the feature + for (let i = 0; i < features.length; i++) { + features[i].setStyle(isVisible ? geolocationStyle : hiddenStyle); + } + }, + + + toggleGeolocation(isTracking) { + this.geolocation.setTracking(isTracking); + if (this.geolocationSource) { + this.displayGeolocationPoint(isTracking); + } }, addRouterToPopup({ featureId, featureTypeSlug, index }) { diff --git a/src/views/Feature/FeatureEdit.vue b/src/views/Feature/FeatureEdit.vue index 9e3f4533..f61a17c8 100644 --- a/src/views/Feature/FeatureEdit.vue +++ b/src/views/Feature/FeatureEdit.vue @@ -250,16 +250,7 @@ ref="map" > <SidebarLayers v-if="basemaps && map" /> - - <div class="geolocation-container"> - <button - class="button-geolocation" - @click.prevent="toggleTracking" - > - <i class="crosshairs icon" /> - </button> - </div> - + <Geolocation /> <Geocoder /> <EditingToolbar v-if="isEditable" @@ -383,6 +374,7 @@ import Dropdown from '@/components/Dropdown.vue'; import SidebarLayers from '@/components/Map/SidebarLayers'; import EditingToolbar from '@/components/Map/EditingToolbar'; import Geocoder from '@/components/Map/Geocoder'; +import Geolocation from '@/components/Map/Geolocation'; import featureAPI from '@/services/feature-api'; import mapService from '@/services/map-service'; @@ -402,6 +394,7 @@ export default { Dropdown, SidebarLayers, Geocoder, + Geolocation, EditingToolbar, FeatureExtraForm, }, @@ -453,7 +446,6 @@ export default { value: null, }, }, - tracking: false, }; }, @@ -676,11 +668,6 @@ export default { this.showGeoRef = !this.showGeoRef; }, - toggleTracking() { - this.tracking = !this.tracking; - mapService.toggleGeolocation(this.tracking); - }, - handleFileUpload() { this.erreurUploadMessage = ''; this.file = this.$refs.file.files[0]; @@ -1053,32 +1040,10 @@ export default { div.geolocation-container { - position: absolute; - right: 0.5em; /* each button have .5em space between, zoom buttons are 60px high and full screen button is 34px high */ top: calc(1.5em + 60px + 34px + 100px); - z-index: 1000; - border: 2px solid rgba(0,0,0,.2); - background-clip: padding-box; - padding: 0; - border-radius: 2px; - display: flex; -} -button.button-geolocation { - border: none; - padding: 0; - margin: 0; - text-align: center; - background-color: #fff; - color: rgb(39, 39, 39); - width: 28px; - height: 28px; - font: 700 18px Lucida Console,Monaco,monospace; - border-radius: 2px; - line-height: 1.15; - cursor: pointer; } - + #get-geom-from-image-file { margin-bottom: 5px; } -- GitLab From 331f5bbcddd5c9917d8b0b8af9b9a0b904cea131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poussard?= <tpoussard@neogeo.fr> Date: Wed, 23 Aug 2023 13:01:24 +0200 Subject: [PATCH 3/4] adapt geolocation to other map pages --- src/components/Map/EditingToolbar.vue | 5 +++-- src/components/Map/Geocoder.vue | 5 +++-- src/components/Map/Geolocation.vue | 12 +++++------- src/services/map-service.js | 10 ++++++---- src/store/modules/map.store.js | 3 ++- src/views/Feature/FeatureDetail.vue | 12 +++++++++++- src/views/Feature/FeatureEdit.vue | 3 +-- src/views/Project/FeaturesListAndMap.vue | 13 +++++++++++-- src/views/Project/ProjectDetail.vue | 8 ++++++++ 9 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/components/Map/EditingToolbar.vue b/src/components/Map/EditingToolbar.vue index 97c86db1..33c86323 100644 --- a/src/components/Map/EditingToolbar.vue +++ b/src/components/Map/EditingToolbar.vue @@ -117,8 +117,9 @@ export default { .editionToolbar{ position: absolute; - // each button have .5em space between, zoom buttons are 60px high and full screen button is 34px high - top: calc(2em + 60px + 34px + 28px); + // each button have (more or less depends on borders) .5em space between + // zoom buttons are 60px high, geolocation and full screen button is 34px high with borders + top: calc(2em + 60px + 34px + 64px); right: 6px; border: 2px solid rgba(0,0,0,.2); border-radius: 4px; diff --git a/src/components/Map/Geocoder.vue b/src/components/Map/Geocoder.vue index d4644cb9..028a6a75 100644 --- a/src/components/Map/Geocoder.vue +++ b/src/components/Map/Geocoder.vue @@ -143,8 +143,9 @@ export default { .geocoder-container { position: absolute; right: 0.5em; - // each button have .5em space between, zoom buttons are 60px high and full screen button is 34px high - top: calc(1.5em + 60px + 34px); + // each button have (more or less depends on borders) .5em space between + // zoom buttons are 60px high, geolocation and full screen button is 34px high with borders + top: calc(1.6em + 60px + 34px + 34px); pointer-events: auto; z-index: 1000; border: 2px solid rgba(0,0,0,.2); diff --git a/src/components/Map/Geolocation.vue b/src/components/Map/Geolocation.vue index 51cced95..cc83f8c9 100644 --- a/src/components/Map/Geolocation.vue +++ b/src/components/Map/Geolocation.vue @@ -34,14 +34,12 @@ export default { <style> div.geolocation-container { position: absolute; - right: 0.5em; - /* each button have .5em space between, zoom buttons are 60px high and full screen button is 34px high */ + right: 6px; z-index: 1000; border: 2px solid rgba(0,0,0,.2); background-clip: padding-box; padding: 0; - border-radius: 2px; - display: flex; + border-radius: 4px; } button.button-geolocation { border: none; @@ -50,10 +48,10 @@ button.button-geolocation { text-align: center; background-color: #fff; color: rgb(39, 39, 39); - width: 28px; - height: 28px; + width: 30px; + height: 30px; font: 700 18px Lucida Console,Monaco,monospace; - border-radius: 2px; + border-radius: 4px; line-height: 1.15; cursor: pointer; } diff --git a/src/services/map-service.js b/src/services/map-service.js index 7d2330de..53c3acff 100644 --- a/src/services/map-service.js +++ b/src/services/map-service.js @@ -198,10 +198,12 @@ const mapService = { toggleGeolocation(isTracking) { - this.geolocation.setTracking(isTracking); - if (this.geolocationSource) { - this.displayGeolocationPoint(isTracking); - } + if (this.geolocation) { + this.geolocation.setTracking(isTracking); + if (this.geolocationSource) { + this.displayGeolocationPoint(isTracking); + } + } }, addRouterToPopup({ featureId, featureTypeSlug, index }) { diff --git a/src/store/modules/map.store.js b/src/store/modules/map.store.js index f20bf643..8421ef36 100644 --- a/src/store/modules/map.store.js +++ b/src/store/modules/map.store.js @@ -111,7 +111,8 @@ const map = { maxZoom: options.maxZoom || rootState.projects.project.map_max_zoom_level, controls: options.controls, zoomControl: options.zoomControl, - interactions : { doubleClickZoom :false, mouseWheelZoom:true, dragPan:true } + interactions : { doubleClickZoom :false, mouseWheelZoom:true, dragPan:true }, + geolocationControl: true, }); const map = { ...mapService.getMap() }; commit('SET_MAP', map); diff --git a/src/views/Feature/FeatureDetail.vue b/src/views/Feature/FeatureDetail.vue index a233e1b1..991bc5e1 100644 --- a/src/views/Feature/FeatureDetail.vue +++ b/src/views/Feature/FeatureDetail.vue @@ -42,6 +42,7 @@ v-if="basemaps && map" ref="sidebar" /> + <Geolocation /> </div> <div id="popup" @@ -182,6 +183,7 @@ import FeatureTable from '@/components/Feature/Detail/FeatureTable'; import FeatureAttachements from '@/components/Feature/Detail/FeatureAttachements'; import FeatureComments from '@/components/Feature/Detail/FeatureComments'; import SidebarLayers from '@/components/Map/SidebarLayers'; +import Geolocation from '@/components/Map/Geolocation'; import { buffer } from 'ol/extent'; @@ -194,6 +196,7 @@ export default { FeatureAttachements, FeatureComments, SidebarLayers, + Geolocation, }, beforeRouteUpdate (to, from, next) { @@ -482,7 +485,8 @@ export default { mouseWheelZoom: true, dragPan: true }, - fullScreenControl: true + fullScreenControl: true, + geolocationControl: true, }); // Update link to feature list with map zoom and center @@ -600,6 +604,12 @@ export default { min-height: 250px; border: 1px solid grey; } +div.geolocation-container { + /* each button have (more or less depends on borders) .5em space between */ + /* zoom buttons are 60px high, geolocation and full screen button is 34px high with borders */ + top: calc(1.3em + 60px + 34px); +} + .prewrap { white-space: pre-wrap; } diff --git a/src/views/Feature/FeatureEdit.vue b/src/views/Feature/FeatureEdit.vue index f61a17c8..92b6004a 100644 --- a/src/views/Feature/FeatureEdit.vue +++ b/src/views/Feature/FeatureEdit.vue @@ -384,7 +384,6 @@ import axios from '@/axios-client.js'; import { GeoJSON } from 'ol/format'; - export default { name: 'FeatureEdit', @@ -1041,7 +1040,7 @@ export default { div.geolocation-container { /* each button have .5em space between, zoom buttons are 60px high and full screen button is 34px high */ - top: calc(1.5em + 60px + 34px + 100px); + top: calc(1.3em + 60px + 34px); } #get-geom-from-image-file { diff --git a/src/views/Project/FeaturesListAndMap.vue b/src/views/Project/FeaturesListAndMap.vue index 7d57794e..8bf9e85e 100644 --- a/src/views/Project/FeaturesListAndMap.vue +++ b/src/views/Project/FeaturesListAndMap.vue @@ -26,6 +26,7 @@ v-if="basemaps && map" ref="sidebar" /> + <Geolocation /> <Geocoder /> </div> <div @@ -106,8 +107,9 @@ import Geocoder from '@/components/Map/Geocoder'; import featureAPI from '@/services/feature-api'; import FeaturesListAndMapFilters from '@/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters'; -import SidebarLayers from '@/components/Map/SidebarLayers'; import FeatureListTable from '@/components/Project/FeaturesListAndMap/FeatureListTable'; +import SidebarLayers from '@/components/Map/SidebarLayers'; +import Geolocation from '@/components/Map/Geolocation'; const initialPagination = { currentPage: 1, @@ -123,6 +125,7 @@ export default { FeaturesListAndMapFilters, SidebarLayers, Geocoder, + Geolocation, FeatureListTable, }, @@ -332,7 +335,8 @@ export default { mapDefaultViewZoom, maxZoom: this.project.map_max_zoom_level, interactions : { doubleClickZoom :false, mouseWheelZoom:true, dragPan:true }, - fullScreenControl: true + fullScreenControl: true, + geolocationControl: true, }); document.addEventListener('change-layers-order', (event) => { @@ -545,6 +549,11 @@ export default { z-index: 1; } } +div.geolocation-container { + // each button have (more or less depends on borders) .5em space between + // zoom buttons are 60px high, geolocation and full screen button is 34px high with borders + top: calc(1.3em + 60px + 34px); +} @media screen and (max-width: 767px) { #project-features { diff --git a/src/views/Project/ProjectDetail.vue b/src/views/Project/ProjectDetail.vue index 91aed089..ccbb8773 100644 --- a/src/views/Project/ProjectDetail.vue +++ b/src/views/Project/ProjectDetail.vue @@ -39,6 +39,7 @@ v-if="basemaps && map && !projectInfoLoading" ref="sidebar" /> + <Geolocation /> <div id="popup" class="ol-popup" @@ -127,6 +128,7 @@ import ProjectLastComments from '@/components/Project/Detail/ProjectLastComments import ProjectParameters from '@/components/Project/Detail/ProjectParameters'; import ProjectModal from '@/components/Project/Detail/ProjectModal'; import SidebarLayers from '@/components/Map/SidebarLayers'; +import Geolocation from '@/components/Map/Geolocation'; export default { name: 'ProjectDetail', @@ -139,6 +141,7 @@ export default { ProjectParameters, ProjectModal, SidebarLayers, + Geolocation, }, filters: { @@ -483,4 +486,9 @@ export default { margin-top: 0.5em; } } + +div.geolocation-container { + /* each button have .5em space between, zoom buttons are 60px high and full screen button is 34px high */ + top: calc(1.1em + 60px); +} </style> -- GitLab From 640f8493930856b9b6068633cb186435d7eff8b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poussard?= <tpoussard@neogeo.fr> Date: Wed, 23 Aug 2023 14:18:23 +0200 Subject: [PATCH 4/4] adapt geolocation to other map pages (fix eslint) --- src/services/map-service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/map-service.js b/src/services/map-service.js index 53c3acff..01d6cd8b 100644 --- a/src/services/map-service.js +++ b/src/services/map-service.js @@ -39,7 +39,7 @@ const geolocationStyle = new Style({ width: 2, }), }), -}) +}); const mapService = { layers: [], -- GitLab