diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index da4f746d6..36d627b6c 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -48,8 +48,8 @@ export default class GeoLocationHandler extends UIElement { * If the user denies the geolocation this time, we unset this flag * @private */ - private readonly _previousLocationGrant : UIEventSource = LocalStorageSource.Get("geolocation-permissions"); - + private readonly _previousLocationGrant: UIEventSource = LocalStorageSource.Get("geolocation-permissions"); + constructor(currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>, leafletMap: UIEventSource) { super(undefined); @@ -61,9 +61,9 @@ export default class GeoLocationHandler extends UIElement { import("../../vendor/Leaflet.AccuratePosition.js").then(() => { self.init(); }) - + const currentPointer = this._isActive.map(isActive => { - if(isActive && !self._hasLocation.data){ + if (isActive && !self._hasLocation.data) { return "cursor-wait" } return "cursor-pointer" @@ -74,6 +74,33 @@ export default class GeoLocationHandler extends UIElement { }) } + InnerRender(): string { + if (this._hasLocation.data) { + return Svg.crosshair_blue_img; + } + if (this._isActive.data) { + return Svg.crosshair_blue_center_img; + } + + return Svg.crosshair_img; + } + + InnerUpdate(htmlElement: HTMLElement) { + super.InnerUpdate(htmlElement); + + const self = this; + htmlElement.onclick = function () { + self.StartGeolocating(19); + } + + htmlElement.oncontextmenu = function (e) { + self.StartGeolocating(15); + e.preventDefault(); + return false; + } + + } + private init() { this.ListenTo(this._hasLocation); this.ListenTo(this._isActive); @@ -102,16 +129,21 @@ export default class GeoLocationHandler extends UIElement { this._currentGPSLocation.addCallback((location) => { self._previousLocationGrant.setData("granted"); - + const timeSinceRequest = (new Date().getTime() - (self._lastUserRequest?.getTime() ?? 0)) / 1000; - if(timeSinceRequest < 30){ + if (timeSinceRequest < 30) { self._lastUserRequest = undefined; this._leafletMap.data.setView( this._currentGPSLocation.data.latlng, this._leafletMap.data.getZoom() ); } - const color = getComputedStyle(document.body).getPropertyValue("--catch-detail-color") + let color = "#1111cc"; + try { + color = getComputedStyle(document.body).getPropertyValue("--catch-detail-color") + } catch (e) { + console.error(e) + } const icon = L.icon( { iconUrl: Img.AsData(Svg.crosshair.replace(/#000000/g, color)), @@ -128,59 +160,59 @@ export default class GeoLocationHandler extends UIElement { self._marker = newMarker; }); - navigator?.permissions?.query({name: 'geolocation'}) - ?.then(function (status) { - console.log("Geolocation is already", status) - if (status.state === "granted") { - self.StartGeolocating(19, false); - } - self._permission.setData(status.state); - status.onchange = function () { - self._permission.setData(status.state); - } - }); + try { - if(this._previousLocationGrant.data === "granted"){ + navigator?.permissions?.query({name: 'geolocation'}) + ?.then(function (status) { + console.log("Geolocation is already", status) + if (status.state === "granted") { + self.StartGeolocating(19, false); + } + self._permission.setData(status.state); + status.onchange = function () { + self._permission.setData(status.state); + } + }); + + } catch (e) { + console.log(e) + self.StartGeolocating() + } + if (this._previousLocationGrant.data === "granted") { this._previousLocationGrant.setData(""); self.StartGeolocating(); } - + this.HideOnEmpty(true); } - InnerRender(): string { - if (this._hasLocation.data) { - return Svg.crosshair_blue_img; - } - if (this._isActive.data) { - return Svg.crosshair_blue_center_img; - } - - return Svg.crosshair_img; - } - - InnerUpdate(htmlElement: HTMLElement) { - super.InnerUpdate(htmlElement); - + private locate() { const self = this; - htmlElement.onclick = function () { - self.StartGeolocating(19); - } + const map: any = this._leafletMap.data; - htmlElement.oncontextmenu = function (e) { - self.StartGeolocating(15); - e.preventDefault(); - return false; + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(function (position) { + self._currentGPSLocation.setData({ + latlng: [position.coords.latitude, position.coords.longitude], + accuracy: position.coords.accuracy + }); + }, function () { + console.warn("Could not get location with navigator.geolocation") + }); + return; + } else { + map.findAccuratePosition({ + maxWait: 10000, // defaults to 10000 + desiredAccuracy: 50 // defaults to 20 + }); } - } - - private StartGeolocating(zoomlevel = 19, zoomToGPS=true) { + private StartGeolocating(zoomlevel = 19, zoomToGPS = true) { const self = this; console.log("Starting geolocation") + this._lastUserRequest = zoomToGPS ? new Date() : new Date(0); - const map: any = this._leafletMap.data; if (self._permission.data === "denied") { self._previousLocationGrant.setData(""); return ""; @@ -193,10 +225,7 @@ export default class GeoLocationHandler extends UIElement { console.log("Searching location using GPS") - map.findAccuratePosition({ - maxWait: 10000, // defaults to 10000 - desiredAccuracy: 50 // defaults to 20 - }); + this.locate(); if (!self._isActive.data) { @@ -207,11 +236,7 @@ export default class GeoLocationHandler extends UIElement { console.log("Not starting gps: document not visible") return; } - - map.findAccuratePosition({ - maxWait: 10000, // defaults to 10000 - desiredAccuracy: 50 // defaults to 20 - }); + this.locate(); }) } } diff --git a/Models/Constants.ts b/Models/Constants.ts index 907b3ac40..ea63c94c9 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import { Utils } from "../Utils"; export default class Constants { - public static vNumber = "0.7.2l"; + public static vNumber = "0.7.2n"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/Utils.ts b/Utils.ts index 209a02071..8cede1574 100644 --- a/Utils.ts +++ b/Utils.ts @@ -1,4 +1,5 @@ import * as colors from "./assets/colors.json" + export class Utils { /** @@ -13,10 +14,10 @@ export class Utils { private static extraKeys = ["nl", "en", "fr", "de", "pt", "es", "name", "phone", "email", "amenity", "leisure", "highway", "building", "yes", "no", "true", "false"] static EncodeXmlValue(str) { - if(typeof str !== "string"){ - str = ""+str + if (typeof str !== "string") { + str = "" + str } - + return str.replace(/&/g, '&') .replace(//g, '>') @@ -261,6 +262,66 @@ export class Utils { return result; } + public static MapRange(tileRange: TileRange, f: (x: number, y: number) => T): T[] { + const result: T[] = [] + for (let x = tileRange.xstart; x <= tileRange.xend; x++) { + for (let y = tileRange.ystart; y <= tileRange.yend; y++) { + const t = f(x, y); + result.push(t) + } + } + return result; + } + + /** + * Triggers a 'download file' popup which will download the contents + * @param contents + * @param fileName + */ + public static downloadTxtFile(contents: string, fileName: string = "download.txt") { + const element = document.createElement("a"); + const file = new Blob([contents], {type: 'text/plain'}); + element.href = URL.createObjectURL(file); + element.download = fileName; + document.body.appendChild(element); // Required for this to work in FireFox + element.click(); + } + + public static ColourNameToHex(color: string): string { + return colors[color.toLowerCase()] ?? color; + } + + public static HexToColourName(hex: string): string { + hex = hex.toLowerCase() + if (!hex.startsWith("#")) { + return hex; + } + const c = Utils.color(hex); + + let smallestDiff = Number.MAX_VALUE; + let bestColor = undefined; + for (const color in colors) { + if (!colors.hasOwnProperty(color)) { + continue; + } + const foundhex = colors[color]; + if (typeof foundhex !== "string") { + continue + } + if (foundhex === hex) { + return color + } + const diff = this.colorDiff(Utils.color(foundhex), c) + if (diff > 50) { + continue; + } + if (diff < smallestDiff) { + smallestDiff = diff; + bestColor = color; + } + } + return bestColor ?? hex; + } private static tile2long(x, z) { return (x / Math.pow(2, z) * 360 - 180); } @@ -278,102 +339,40 @@ export class Utils { return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom))); } - public static MapRange (tileRange: TileRange, f: (x: number, y: number) => T): T[] { - const result : T[] = [] - for (let x = tileRange.xstart; x <= tileRange.xend; x++) { - for (let y = tileRange.ystart; y <= tileRange.yend; y++) { - const t= f(x, y); - result.push(t) - } - } - return result; + private static colorDiff(c0: { r: number, g: number, b: number }, c1: { r: number, g: number, b: number }) { + return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) + Math.abs(c0.b - c1.b); } - /** - * Triggers a 'download file' popup which will download the contents - * @param contents - * @param fileName - */ - public static downloadTxtFile (contents: string, fileName: string = "download.txt") { - const element = document.createElement("a"); - const file = new Blob([contents], {type: 'text/plain'}); - element.href = URL.createObjectURL(file); - element.download = fileName; - document.body.appendChild(element); // Required for this to work in FireFox - element.click(); - } - - - public static ColourNameToHex(color: string): string{ - return colors[color.toLowerCase()] ?? color; - } - - public static HexToColourName(hex : string): string{ - hex = hex.toLowerCase() - if(!hex.startsWith("#")){ - return hex; - } - const c = Utils.color(hex); - - let smallestDiff = Number.MAX_VALUE; - let bestColor = undefined; - for (const color in colors) { - if(!colors.hasOwnProperty(color)){ - continue; - } - const foundhex = colors[color]; - if(typeof foundhex !== "string"){ - continue - } - if(foundhex === hex){ - return color - } - const diff = this.colorDiff(Utils.color(foundhex), c) - if(diff > 50){ - continue; - } - if(diff < smallestDiff){ - smallestDiff = diff; - bestColor = color; - } - } - return bestColor ?? hex; - } - - private static colorDiff(c0 : {r: number, g: number, b: number}, c1: {r: number, g: number, b: number}){ - return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) +Math.abs(c0.b - c1.b) ; - } - - private static color(hex: string) : {r: number, g: number, b: number}{ - if(hex.startsWith == undefined){ + private static color(hex: string): { r: number, g: number, b: number } { + if (hex.startsWith == undefined) { console.trace("WUT?", hex) throw "wut?" } - if(!hex.startsWith("#")){ - return undefined; + if (!hex.startsWith("#")) { + return undefined; } - if(hex.length === 4){ - return { - r : parseInt(hex.substr(1, 1), 16), - g : parseInt(hex.substr(2, 1), 16), - b : parseInt(hex.substr(3, 1), 16), - } + if (hex.length === 4) { + return { + r: parseInt(hex.substr(1, 1), 16), + g: parseInt(hex.substr(2, 1), 16), + b: parseInt(hex.substr(3, 1), 16), + } } return { - r : parseInt(hex.substr(1, 2), 16), - g : parseInt(hex.substr(3, 2), 16), - b : parseInt(hex.substr(5, 2), 16), + r: parseInt(hex.substr(1, 2), 16), + g: parseInt(hex.substr(3, 2), 16), + b: parseInt(hex.substr(5, 2), 16), } } } -export interface TileRange{ +export interface TileRange { xstart: number, ystart: number, xend: number, yend: number, total: number, zoomlevel: number - + } \ No newline at end of file diff --git a/assets/themes/speelplekken/speelplekken_temp.json b/assets/themes/speelplekken/speelplekken_temp.json index 6eb636b7d..186b0bab4 100644 --- a/assets/themes/speelplekken/speelplekken_temp.json +++ b/assets/themes/speelplekken/speelplekken_temp.json @@ -24,17 +24,6 @@ "socialImage": "", "defaultBackgroundId": "CartoDB.Positron", "layers": [ - { - "id": "shadow", - "source": { - "geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete/master/assets/themes/speelplekken/shadow.geojson", - "osmTags": "shadow=yes" - }, - "color": "#444444", - "width": { - "render": "1" - } - }, { "builtin": "play_forest", "override": {