Add geolocation button which uses device GPS
This commit is contained in:
parent
57c9fcc5aa
commit
a566ab6725
6 changed files with 299 additions and 32 deletions
107
Logic/GeoLocationHandler.ts
Normal file
107
Logic/GeoLocationHandler.ts
Normal file
|
@ -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<boolean> = new UIEventSource<boolean>(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 "<img src='./assets/crosshair-blue.svg' alt='locate me'>";
|
||||
}
|
||||
if (this._isActive.data) {
|
||||
return "<img src='./assets/crosshair-blue-center.svg' alt='locate me'>";
|
||||
}
|
||||
|
||||
return "<img src='./assets/crosshair.svg' alt='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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -10,7 +10,7 @@ export class LayerUpdater {
|
|||
private _layers: FilteredLayer[];
|
||||
|
||||
public readonly runningQuery: UIEventSource<boolean> = new UIEventSource<boolean>(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};
|
||||
|
||||
|
|
67
index.css
67
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 {
|
||||
|
|
|
@ -33,10 +33,11 @@
|
|||
<div id="centermessage"></div>
|
||||
<div id="bottomRight" style="display: none">ADD</div>
|
||||
|
||||
<div id="geolocate-button"></div>
|
||||
<div id="leafletDiv"></div>
|
||||
|
||||
<script src="./index.ts"></script>
|
||||
|
||||
<script src="vendor/Leaflet.AccuratePosition.js"></script>
|
||||
|
||||
<!-- 3 dagen eerste protoype -->
|
||||
<!-- 19 juni: eerste feedbackronde, foto's -->
|
||||
|
@ -44,7 +45,7 @@
|
|||
<!-- 24 juni: foto's via imgur -->
|
||||
|
||||
<!-- 26 restylen infobox -->
|
||||
|
||||
<!-- 27 restylen infobox, flow UI verbeteren, mobile, locate-me -->
|
||||
<script data-goatcounter="https://pietervdvn.goatcounter.com/count"
|
||||
async src="//gc.zgo.at/count.js"></script>
|
||||
|
||||
|
|
4
index.ts
4
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();
|
||||
|
|
133
vendor/Leaflet.AccuratePosition.js
vendored
Normal file
133
vendor/Leaflet.AccuratePosition.js
vendored
Normal file
|
@ -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 + '.'
|
||||
});
|
||||
}
|
||||
});
|
Loading…
Reference in a new issue