diff --git a/Logic/GeoLocationHandler.ts b/Logic/GeoLocationHandler.ts new file mode 100644 index 0000000..da5052b --- /dev/null +++ b/Logic/GeoLocationHandler.ts @@ -0,0 +1,107 @@ +import {Basemap} from "./Basemap"; +import {UIEventSource} from "../UI/UIEventSource"; +import {UIElement} from "../UI/UIElement"; +import L from "leaflet"; + +export class GeoLocationHandler extends UIElement { + + currentLocation: UIEventSource<{ + latlng: number, + accuracy: number + }> = new UIEventSource<{ latlng: number, accuracy: number }>(undefined); + + private _isActive: UIEventSource = new UIEventSource(false); + + private _map: Basemap; + private _marker: any; + + constructor(map: Basemap) { + super(undefined); + this._map = map; + this.ListenTo(this.currentLocation); + this.ListenTo(this._isActive); + + const self = this; + + + function onAccuratePositionProgress(e) { + console.log(e.accuracy); + console.log(e.latlng); + } + + function onAccuratePositionFound(e) { + console.log(e.accuracy); + console.log(e.latlng); + self.currentLocation.setData({latlng: e.latlng, accuracy: e.accuracy}); + } + + function onAccuratePositionError(e) { + console.log("onerror", e.message); + + } + + map.map.on('accuratepositionprogress', onAccuratePositionProgress); + map.map.on('accuratepositionfound', onAccuratePositionFound); + map.map.on('accuratepositionerror', onAccuratePositionError); + + + const icon = L.icon( + { + iconUrl: './assets/crosshair-blue.svg', + iconSize: [40, 40], // size of the icon + iconAnchor: [20, 20], // point of the icon which will correspond to marker's location + }) + + this.currentLocation.addCallback((location) => { + const newMarker = L.marker(location.latlng, {icon: icon}); + newMarker.addTo(map.map); + + if (self._marker !== undefined) { + map.map.removeLayer(self._marker); + } + self._marker = newMarker; + }); + + navigator.permissions.query({ name: 'geolocation' }) + .then(function(){self.StartGeolocating()}); + + } + + protected InnerRender(): string { + if (this.currentLocation.data) { + return "locate me"; + } + if (this._isActive.data) { + return "locate me"; + } + + return "locate me"; + } + + + private StartGeolocating(){ + const self = this; + if (self.currentLocation.data !== undefined) { + self._map.map.flyTo(self.currentLocation.data.latlng, 18); + return; + } + + self._isActive.setData(true); + + console.log("Searching location using GPS") + self._map.map.findAccuratePosition({ + maxWait: 15000, // defaults to 10000 + desiredAccuracy: 30 // defaults to 20 + }); + } + + InnerUpdate(htmlElement: HTMLElement) { + super.InnerUpdate(htmlElement); + + const self = this; + htmlElement.onclick = function () { + self.StartGeolocating(); + } + } + +} \ No newline at end of file diff --git a/Logic/LayerUpdater.ts b/Logic/LayerUpdater.ts index 1c3faa9..b23e6b9 100644 --- a/Logic/LayerUpdater.ts +++ b/Logic/LayerUpdater.ts @@ -10,7 +10,7 @@ export class LayerUpdater { private _layers: FilteredLayer[]; public readonly runningQuery: UIEventSource = new UIEventSource(false); - + /** * The previous bounds for which the query has been run */ @@ -95,13 +95,12 @@ export class LayerUpdater { private buildBboxFor(): string { const b = this._map.map.getBounds(); - const latDiff = Math.abs(b.getNorth() - b.getSouth()); - const lonDiff = Math.abs(b.getEast() - b.getWest()); - const extra = 0.5; - const n = b.getNorth() + latDiff * extra; - const e = b.getEast() + lonDiff * extra; - const s = b.getSouth() - latDiff * extra; - const w = b.getWest() - lonDiff * extra; + const diff =0.07; + + const n = b.getNorth() + diff; + const e = b.getEast() + diff; + const s = b.getSouth() - diff; + const w = b.getWest() - diff; this.previousBounds = {north: n, east: e, south: s, west: w}; diff --git a/index.css b/index.css index ed28323..077d9f8 100644 --- a/index.css +++ b/index.css @@ -16,6 +16,26 @@ img { border-radius: 1em; } + +#geolocate-button { + position: absolute; + bottom: 27px; + right: 65px; + z-index: 999; /*Just below leaflets zoom*/ + background-color: white; + border-radius: 5px; + border: solid 2px rgba(0,0,0,0.2); + cursor: pointer; + width: 43px; + height:43px; +} + +#geolocate-button img{ + width: 31px; + height:31px; + margin: 6px; +} + /**************** GENERIC ****************/ .uielement { @@ -145,9 +165,9 @@ img { #welcomeMessage { display: inline-block; max-width: 30em; + padding: 1em; } - #messagesboxmobilewrapper { display: none; /*Only shown on small screens*/ } @@ -190,30 +210,33 @@ img { #messagesboxmobile-scroll { display: block; overflow-y: auto; - height: calc(100vh - 9em); - padding-top: 1em; - padding-bottom: 2em; - margin-bottom: 0; + width: 100vw; + padding: 0; + margin:0; + height: calc(100% - 5em); /*Height of to-the-map is 2em, padding is 2 * 0.5em*/ } #messagesboxmobile { margin: 1em; - margin-bottom: 2em; + padding-bottom: 2em; } } #to-the-map { + + height: 4em; + padding: 0.5em; margin: 0; - padding: 1em; - padding-right: 2em; - color: white; - background-color: #7ebc6f; + position: absolute; bottom: 0; right: 0; - text-align: right; + + padding-right: 2em; width: 100%; - height: 4em; + text-align: right; + color: white; + background-color: #7ebc6f; cursor: pointer; } @@ -228,6 +251,7 @@ img { padding: 0; right: 1em; top: 1em; + border-radius: 0; } #centermessage { @@ -319,8 +343,7 @@ img { content:url(assets/arrow-left-smooth.svg); - border-bottom-left-radius: 1em; - border-top-left-radius: 1em; + border-radius: 1em; } @@ -335,8 +358,7 @@ img { top: 50%; right: 0; transform: translate(0, -50%); - border-bottom-right-radius: 1em; - border-top-right-radius: 1em; + border-radius: 1em; z-index: 5060; @@ -345,11 +367,12 @@ img { } -.slide > span > img { - width: 500px; - max-width: 100%; +.slide > span img { height: auto; - + width: auto; + max-width: 100%; + max-height: 20vh; + border-radius: 1em; } @@ -369,8 +392,8 @@ img { } .wikimedia-link { - width: 1.5em; - height: auto; + /*The actual wikimedia logo*/ + width: 1.5em !important; } .attribution { diff --git a/index.html b/index.html index 150f998..ed10daf 100644 --- a/index.html +++ b/index.html @@ -33,10 +33,11 @@
+
- + @@ -44,7 +45,7 @@ - + diff --git a/index.ts b/index.ts index 5d5ba08..0c4e46a 100644 --- a/index.ts +++ b/index.ts @@ -17,6 +17,7 @@ import {MessageBoxHandler} from "./UI/MessageBoxHandler"; import {Overpass} from "./Logic/Overpass"; import {FixedUiElement} from "./UI/FixedUiElement"; import {FeatureInfoBox} from "./UI/FeatureInfoBox"; +import {GeoLocationHandler} from "./Logic/GeoLocationHandler"; let dryRun = false; @@ -188,6 +189,9 @@ Helpers.LastEffortSave(changes); osmConnection.registerActivateOsmAUthenticationClass(); +new GeoLocationHandler(bm).AttachTo("geolocate-button"); + + // --------------- Send a ping to start various action -------- locationControl.ping(); diff --git a/vendor/Leaflet.AccuratePosition.js b/vendor/Leaflet.AccuratePosition.js new file mode 100644 index 0000000..e09af51 --- /dev/null +++ b/vendor/Leaflet.AccuratePosition.js @@ -0,0 +1,133 @@ +/** + * Leaflet.AccuratePosition aims to provide an accurate device location when simply calling map.locate() doesn’t. + * https://github.com/m165437/Leaflet.AccuratePosition + * + * Greg Wilson's getAccurateCurrentPosition() forked to be a Leaflet plugin + * https://github.com/gwilson/getAccurateCurrentPosition + * + * Copyright (C) 2013 Greg Wilson, 2014 Michael Schmidt-Voigt + */ + +L.Map.include({ + _defaultAccuratePositionOptions: { + maxWait: 10000, + desiredAccuracy: 20 + }, + + findAccuratePosition: function (options) { + + if (!navigator.geolocation) { + this._handleAccuratePositionError({ + code: 0, + message: 'Geolocation not supported.' + }); + return this; + } + + this._accuratePositionEventCount = 0; + this._accuratePositionOptions = L.extend(this._defaultAccuratePositionOptions, options); + this._accuratePositionOptions.enableHighAccuracy = true; + this._accuratePositionOptions.maximumAge = 0; + + if (!this._accuratePositionOptions.timeout) + this._accuratePositionOptions.timeout = this._accuratePositionOptions.maxWait; + + var onResponse = L.bind(this._checkAccuratePosition, this), + onError = L.bind(this._handleAccuratePositionError, this), + onTimeout = L.bind(this._handleAccuratePositionTimeout, this); + + this._accuratePositionWatchId = navigator.geolocation.watchPosition( + onResponse, + onError, + this._accuratePositionOptions); + + this._accuratePositionTimerId = setTimeout( + onTimeout, + this._accuratePositionOptions.maxWait); + }, + + _handleAccuratePositionTimeout: function() { + navigator.geolocation.clearWatch(this._accuratePositionWatchId); + + if (typeof this._lastCheckedAccuratePosition !== 'undefined') { + this._handleAccuratePositionResponse(this._lastCheckedAccuratePosition); + } else { + this._handleAccuratePositionError({ + code: 3, + message: 'Timeout expired' + }); + } + + return this; + }, + + _cleanUpAccuratePositioning: function () { + clearTimeout(this._accuratePositionTimerId); + navigator.geolocation.clearWatch(this._accuratePositionWatchId); + }, + + _checkAccuratePosition: function (pos) { + var accuracyReached = pos.coords.accuracy <= this._accuratePositionOptions.desiredAccuracy; + + this._lastCheckedAccuratePosition = pos; + this._accuratePositionEventCount = this._accuratePositionEventCount + 1; + + if (accuracyReached && (this._accuratePositionEventCount > 1)) { + this._cleanUpAccuratePositioning(); + this._handleAccuratePositionResponse(pos); + } else { + this._handleAccuratePositionProgress(pos); + } + }, + + _prepareAccuratePositionData: function (pos) { + var lat = pos.coords.latitude, + lng = pos.coords.longitude, + latlng = new L.LatLng(lat, lng), + + latAccuracy = 180 * pos.coords.accuracy / 40075017, + lngAccuracy = latAccuracy / Math.cos(Math.PI / 180 * lat), + + bounds = L.latLngBounds( + [lat - latAccuracy, lng - lngAccuracy], + [lat + latAccuracy, lng + lngAccuracy]); + + var data = { + latlng: latlng, + bounds: bounds, + timestamp: pos.timestamp + }; + + for (var i in pos.coords) { + if (typeof pos.coords[i] === 'number') { + data[i] = pos.coords[i]; + } + } + + return data; + }, + + _handleAccuratePositionProgress: function (pos) { + var data = this._prepareAccuratePositionData(pos); + this.fire('accuratepositionprogress', data); + }, + + _handleAccuratePositionResponse: function (pos) { + var data = this._prepareAccuratePositionData(pos); + this.fire('accuratepositionfound', data); + }, + + _handleAccuratePositionError: function (error) { + var c = error.code, + message = error.message || + (c === 1 ? 'permission denied' : + (c === 2 ? 'position unavailable' : 'timeout')); + + this._cleanUpAccuratePositioning(); + + this.fire('accuratepositionerror', { + code: c, + message: 'Geolocation error: ' + message + '.' + }); + } +});