From 989db3b787588844a5ea63964e54d17b7dc02ac0 Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Wed, 27 Jun 2018 11:17:38 +0200 Subject: [PATCH] Few UI improvements * Better info message when fetching geolocation * No longer displaying a marker with heading if heading is unknown. Use a circle marker instead. * Display the accuracy area --- src/components/Map.vue | 34 ++++++++++++++++++++++++++++++++-- src/constants.js | 6 ++++++ src/i18n/en.js | 2 +- src/main.js | 2 ++ src/tools/index.js | 6 ++++-- src/views/Map.vue | 38 +++++++++++++++++++------------------- 6 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 src/constants.js diff --git a/src/components/Map.vue b/src/components/Map.vue index a7f2be5..1d51801 100644 --- a/src/components/Map.vue +++ b/src/components/Map.vue @@ -2,7 +2,12 @@
- + + + + + +
@@ -14,6 +19,8 @@ import iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png'; import iconUrl from 'leaflet/dist/images/marker-icon.png'; import shadowUrl from 'leaflet/dist/images/marker-shadow.png'; +import { EARTH_RADIUS } from '@/constants'; + // Fix for a bug in Leaflet default icon // see https://github.com/PaulLeCam/react-leaflet/issues/255#issuecomment-261904061 delete L.Icon.Default.prototype._getIconUrl; @@ -26,24 +33,46 @@ L.Icon.Default.mergeOptions({ export const DEFAULT_ZOOM = 17; export const MIN_ZOOM = 15; export const MAX_ZOOM = 18; -export const TILE_SERVER = process.env.TILE_SERVER || 'https://a.tile.thunderforest.com/cycle/{z}/{x}/{y}.png'; +export const TILE_SERVER = process.env.TILE_SERVER || 'https://a.tile.thunderforest.com/cycle/{z}/{x}/{y}.png'; export default { props: { + accuracy: { + type: Number, + default: null, + }, heading: Number, lat: Number, lng: Number, markers: Array, }, computed: { + radiusFromAccuracy() { + if (this.accuracy) { + return this.accuracy / ( + (EARTH_RADIUS * 2 * Math.PI * Math.cos(this.lat)) / + (2 ** (this.zoom + 8)) + ); + } + return null; + }, + shouldDisplayAccuracy() { + return ( + this.accuracy && + this.accuracy < 100 && + this.radiusFromAccuracy > this.markerRadius + ); + }, latlng() { return [this.lat, this.lng]; }, markerOptions() { return { fillColor: '#00ff00', + color: '#000000', heading: this.heading, + weight: 1, }; }, }, @@ -51,6 +80,7 @@ export default { return { attribution: 'Map data © OpenStreetMap contributors', zoom: DEFAULT_ZOOM, + markerRadius: 10.0, minZoom: MIN_ZOOM, maxZoom: MAX_ZOOM, tileServer: TILE_SERVER, diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..4ae0182 --- /dev/null +++ b/src/constants.js @@ -0,0 +1,6 @@ +export const MOCK_LOCATION = false; +export const MOCK_LOCATION_UPDATE_INTERVAL = 30 * 1000; + +export const UPDATE_REPORTS_DISTANCE_THRESHOLD = 500; + +export const EARTH_RADIUS = 6378137; diff --git a/src/i18n/en.js b/src/i18n/en.js index 74e13ba..641c193 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -1,7 +1,7 @@ // Keys should be sorted alphabetically export default { geolocation: { - enable: 'Please accept the geolocation permission request to use the app.', + fetching: 'Fetching current position…', unavailable: 'Sorry, geolocation is not available in your browser.', }, menu: { diff --git a/src/main.js b/src/main.js index 19237cd..1131e70 100644 --- a/src/main.js +++ b/src/main.js @@ -19,6 +19,8 @@ Vue.use(Vuetify); Vue.component('v-lmap', Vue2Leaflet.LMap); Vue.component('v-ltilelayer', Vue2Leaflet.LTileLayer); Vue.component('v-lmarker', Vue2Leaflet.LMarker); +Vue.component('v-lcirclemarker', Vue2Leaflet.LCircleMarker); +Vue.component('v-lcircle', Vue2Leaflet.LCircle); Vue.component('v-lpolyline', Vue2Leaflet.LPolyline); Vue.component('v-lts', Vue2LeafletTracksymbol); diff --git a/src/tools/index.js b/src/tools/index.js index 999bcb0..2c7bc37 100644 --- a/src/tools/index.js +++ b/src/tools/index.js @@ -1,3 +1,5 @@ +import { EARTH_RADIUS } from '@/constants'; + export function distance(latLng1, latLng2) { const lat1 = (latLng1[0] * Math.PI) / 180; const lng1 = (latLng1[1] * Math.PI) / 180; @@ -10,7 +12,6 @@ export function distance(latLng1, latLng2) { (Math.cos(lat1) * Math.cos(lat2) * (Math.sin((lng2 - lng1) / 2.0) ** 2)) ); const c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - const EARTH_RADIUS = 6371000; return EARTH_RADIUS * c; } @@ -22,9 +23,10 @@ export function mockLocation() { const LNG_MAX = 2.392742; return { coords: { + accuracy: 10, // In meters latitude: (Math.random() * (LAT_MAX - LAT_MIN)) + LAT_MIN, longitude: (Math.random() * (LNG_MAX - LNG_MIN)) + LNG_MIN, - heading: 20 * (Math.PI / 180), + heading: null, // 20 * (Math.PI / 180), }, }; } diff --git a/src/views/Map.vue b/src/views/Map.vue index b76e9da..7daaf12 100644 --- a/src/views/Map.vue +++ b/src/views/Map.vue @@ -2,7 +2,7 @@ - + -

{{ error }}

-

- Retry -

+ +
@@ -32,12 +37,9 @@ import NoSleep from 'nosleep.js'; import Map from '@/components/Map.vue'; import ReportDialog from '@/components/ReportDialog/index.vue'; +import * as constants from '@/constants'; import { distance, mockLocation } from '@/tools'; -const MOCK_LOCATION = false; -const MOCK_LOCATION_UPDATE_INTERVAL = 30 * 1000; -const UPDATE_REPORTS_DISTANCE_THRESHOLD = 500; - export default { components: { Map, @@ -62,8 +64,9 @@ export default { }, data() { return { + accuracy: null, dialog: false, - error: this.$t('geolocation.enable'), + error: null, heading: null, lat: null, lng: null, @@ -75,11 +78,11 @@ export default { initializePositionWatching() { this.disablePositionWatching(); // Ensure at most one at the same time - if (MOCK_LOCATION) { + if (constants.MOCK_LOCATION) { this.setPosition(mockLocation()); this.watchID = setInterval( () => this.setPosition(mockLocation()), - MOCK_LOCATION_UPDATE_INTERVAL, + constants.MOCK_LOCATION_UPDATE_INTERVAL, ); } else { if (!('geolocation' in navigator)) { @@ -99,7 +102,7 @@ export default { }, disablePositionWatching() { if (this.watchID !== null) { - if (MOCK_LOCATION) { + if (constants.MOCK_LOCATION) { clearInterval(this.watchID); } else { navigator.geolocation.clearWatch(this.watchID); @@ -115,17 +118,14 @@ export default { [this.lat, this.lng], [position.coords.latitude, position.coords.longitude], ); - if (distanceFromPreviousPoint > UPDATE_REPORTS_DISTANCE_THRESHOLD) { + if (distanceFromPreviousPoint > constants.UPDATE_REPORTS_DISTANCE_THRESHOLD) { this.$store.dispatch('fetchReports'); } } this.lat = position.coords.latitude; this.lng = position.coords.longitude; - if (position.coords.heading) { - this.heading = position.coords.heading; - } else { - this.heading = null; - } + this.heading = position.coords.heading ? position.coords.heading : null; + this.accuracy = position.coords.accuracy ? position.coords.accuracy : null; }, setNoSleep() { this.noSleep = new NoSleep();