From a9dfce72a61a5343d447406935a3227fe91e0e71 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 6 Dec 2020 00:20:27 +0100 Subject: [PATCH] Fix performance, add 'isClosed' badge to shops --- Customizations/JSON/LayerConfig.ts | 43 ++--- Customizations/JSON/LayerConfigJson.ts | 2 +- Customizations/SharedTagRenderings.ts | 20 +-- Logic/FilteredLayer.ts | 10 +- Logic/MetaTagging.ts | 20 ++- State.ts | 2 +- Svg.ts | 14 +- UI/LayerSelection.ts | 4 +- UI/SimpleAddUI.ts | 2 +- .../layers/bike_repair_station/bike_pump.svg | 148 ++++++++++++++++++ assets/layers/bike_shop/bike_shop.json | 12 ++ assets/svg/clock.svg | 42 +++-- assets/svg/closed.svg | 42 ----- assets/svg/ring.svg | 34 ++++ assets/tagRenderings/icons.json | 33 ++++ assets/themes/shops/shops.json | 22 +-- package-lock.json | 6 +- package.json | 2 +- 18 files changed, 334 insertions(+), 124 deletions(-) create mode 100644 assets/layers/bike_repair_station/bike_pump.svg delete mode 100644 assets/svg/closed.svg create mode 100644 assets/svg/ring.svg diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts index 218d16d..da621a1 100644 --- a/Customizations/JSON/LayerConfig.ts +++ b/Customizations/JSON/LayerConfig.ts @@ -13,6 +13,7 @@ import {Utils} from "../../Utils"; import Combine from "../../UI/Base/Combine"; import {VariableUiElement} from "../../UI/Base/VariableUIElement"; import {UIEventSource} from "../../Logic/UIEventSource"; +import {FixedUiElement} from "../../UI/Base/FixedUiElement"; import {UIElement} from "../../UI/UIElement"; export default class LayerConfig { @@ -35,7 +36,7 @@ export default class LayerConfig { titleIcons: TagRenderingConfig[]; icon: TagRenderingConfig; - iconOverlays: { if: TagsFilter, then: string, badge: boolean }[] + iconOverlays: { if: TagsFilter, then: TagRenderingConfig, badge: boolean }[] iconSize: TagRenderingConfig; rotation: TagRenderingConfig; color: TagRenderingConfig; @@ -140,9 +141,13 @@ export default class LayerConfig { this.title = tr("title", undefined); this.icon = tr("icon", Img.AsData(Svg.bug)); this.iconOverlays = (json.iconOverlays ?? []).map(overlay => { + let tr = new TagRenderingConfig(overlay.then); + if (typeof overlay.then === "string" && SharedTagRenderings.SharedIcons[overlay.then] !== undefined) { + tr = SharedTagRenderings.SharedIcons[overlay.then]; + } return { if: FromJSON.Tag(overlay.if), - then: overlay.then, + then: tr, badge: overlay.badge ?? false } }); @@ -172,7 +177,7 @@ export default class LayerConfig { popupAnchor: [number, number]; iconAnchor: [number, number]; iconSize: [number, number]; - html: string; + html: UIElement; className?: string; }; weight: number; dashArray: number[] @@ -232,32 +237,31 @@ export default class LayerConfig { const iconUrlStatic = render(this.icon); const self = this; - var mappedHtml = tags.map(tags => { - // What do you mean, 'tags' is never read? + var mappedHtml = tags.map(tgs => { + // What do you mean, 'tgs' is never read? // It is read implicitly in the 'render' method const iconUrl = render(self.icon); const rotation = render(self.rotation, "0deg"); - let htmlParts = []; + let htmlParts: UIElement[] = []; let sourceParts = iconUrl.split(";"); - function genHtmlFromString(sourcePart: string): string { + function genHtmlFromString(sourcePart: string): UIElement { const style = `width:100%;height:100%;rotate:${rotation};display:block;position: absolute; top: 0, left: 0`; - let html = ``; + let html: UIElement = new FixedUiElement(``); const match = sourcePart.match(/([a-zA-Z0-9_]*):#([0-9a-fA-F]{3,6})/) if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) { html = new Combine([ (Svg.All[match[1] + ".svg"] as string) .replace(/#000000/g, "#" + match[2]) - ]).SetStyle(style).Render(); + ]).SetStyle(style); } if (sourcePart.startsWith(Utils.assets_path)) { const key = sourcePart.substr(Utils.assets_path.length); html = new Combine([ (Svg.All[key] as string).replace(/stop-color:#000000/g, 'stop-color:' + color) - ]).SetStyle(style) - .Render(); + ]).SetStyle(style); } return html; } @@ -270,12 +274,12 @@ export default class LayerConfig { let badges = []; for (const iconOverlay of self.iconOverlays) { - if (!iconOverlay.if.matchesProperties(tags)) { + if (!iconOverlay.if.matchesProperties(tgs)) { continue; } if (iconOverlay.badge) { - const badgeParts: string[] = []; - const partDefs = iconOverlay.then.split(";"); + const badgeParts: UIElement[] = []; + const partDefs = iconOverlay.then.GetRenderValue(tgs).txt.split(";"); for (const badgePartStr of partDefs) { badgeParts.push(genHtmlFromString(badgePartStr)) @@ -287,25 +291,24 @@ export default class LayerConfig { badges.push(badgeCompound) } else { - htmlParts.push(genHtmlFromString(iconOverlay.then)); + htmlParts.push(genHtmlFromString( + iconOverlay.then.GetRenderValue(tgs).txt)); } } if (badges.length > 0) { const badgesComponent = new Combine(badges) - .SetStyle("display:flex;height:50%;width:100%;position:absolute;top:50%;left:50%;") - .Render() - + .SetStyle("display:flex;height:50%;width:100%;position:absolute;top:50%;left:50%;"); htmlParts.push(badgesComponent) } - return htmlParts.join(""); + return new Combine(htmlParts).Render(); }) return { icon: { - html: new VariableUiElement(mappedHtml).Render(), + html: new VariableUiElement(mappedHtml), iconSize: [iconW, iconH], iconAnchor: [anchorW, anchorH], popupAnchor: [0, 3 - anchorH], diff --git a/Customizations/JSON/LayerConfigJson.ts b/Customizations/JSON/LayerConfigJson.ts index 29e2e13..28f5fb8 100644 --- a/Customizations/JSON/LayerConfigJson.ts +++ b/Customizations/JSON/LayerConfigJson.ts @@ -74,7 +74,7 @@ export interface LayerConfigJson { * * Note: strings are interpreted as icons, so layering and substituting is supported */ - iconOverlays?: {if: string | AndOrTagConfigJson, then: string, badge?: boolean}[] + iconOverlays?: {if: string | AndOrTagConfigJson, then: string | TagRenderingConfigJson, badge?: boolean}[] /** * A string containing "width,height" or "width,height,anchorpoint" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ... diff --git a/Customizations/SharedTagRenderings.ts b/Customizations/SharedTagRenderings.ts index 1b5c281..c6dae7d 100644 --- a/Customizations/SharedTagRenderings.ts +++ b/Customizations/SharedTagRenderings.ts @@ -5,27 +5,29 @@ import * as icons from "../assets/tagRenderings/icons.json"; export default class SharedTagRenderings { public static SharedTagRendering = SharedTagRenderings.generatedSharedFields(); + public static SharedIcons = SharedTagRenderings.generatedSharedFields(true); - private static generatedSharedFields() { + private static generatedSharedFields(iconsOnly = false) { const dict = {} - - - function add(key, store){ + + + function add(key, store) { try { dict[key] = new TagRenderingConfig(store[key]) } catch (e) { console.error("BUG: could not parse", key, " from questions.json or icons.json", e) } } - - - for (const key in questions) { - add(key, questions); + + if (!iconsOnly) { + for (const key in questions) { + add(key, questions); + } } for (const key in icons) { add(key, icons); } - + return dict; } diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts index 5671968..efd32ec 100644 --- a/Logic/FilteredLayer.ts +++ b/Logic/FilteredLayer.ts @@ -152,8 +152,16 @@ export class FilteredLayer { color: style.color }); } else { + style.icon.html.ListenTo(self.isDisplayed) marker = L.marker(latLng, { - icon: L.divIcon(style.icon) + icon: L.divIcon({ + html: style.icon.html.Render(), + className: style.icon.className, + iconAnchor: style.icon.iconAnchor, + iconUrl: style.icon.iconUrl, + popupAnchor: style.icon.popupAnchor, + iconSize: style.icon.iconSize + }) }); } return marker; diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index 497b571..785fe43 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -67,11 +67,15 @@ export default class MetaTagging { let centerPoint: any = GeoOperations.centerpoint(feature); const lat = centerPoint.geometry.coordinates[1]; - const lon = centerPoint.geometry.coordinates[0] + const lon = centerPoint.geometry.coordinates[0]; coder.GetCountryCodeFor(lon, lat, (countries) => { - feature.properties["_country"] = countries[0].trim().toLowerCase(); - const tagsSource = State.state.allElements.getEventSourceFor(feature); - tagsSource.ping(); + try { + feature.properties["_country"] = countries[0].trim().toLowerCase(); + const tagsSource = State.state.allElements.getEventSourceFor(feature); + tagsSource.ping(); + } catch (e) { + console.error(e) + } }); } ) @@ -85,6 +89,8 @@ export default class MetaTagging { if (tags.opening_hours === undefined || tags._country === undefined) { return; } + try{ + const oh = new opening_hours(tags["opening_hours"], { lat: tags._lat, lon: tags._lon, @@ -105,7 +111,7 @@ export default class MetaTagging { tagsSource.ping(); } - const nextChange = oh.getNextChange() as Date; + const nextChange = oh.getNextChange(); if (nextChange !== undefined) { window.setTimeout( updateTags, @@ -114,6 +120,10 @@ export default class MetaTagging { } } updateTags(); + }catch(e){ + console.error(e); + tags["_isOpen"] = "parse_error"; + } }) }) diff --git a/State.ts b/State.ts index 91d6c49..72b7a83 100644 --- a/State.ts +++ b/State.ts @@ -23,7 +23,7 @@ export default class State { // The singleton of the global state public static state: State; - public static vNumber = "0.2.3d"; + public static vNumber = "0.2.4"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/Svg.ts b/Svg.ts index db30cf7..e49ccaa 100644 --- a/Svg.ts +++ b/Svg.ts @@ -49,7 +49,7 @@ export default class Svg { public static circle_svg() { return new FixedUiElement(Svg.circle);} public static circle_ui() { return new FixedUiElement(Svg.circle_img);} - public static clock = " " + public static clock = " image/svg+xml " public static clock_img = Img.AsImageElement(Svg.clock) public static clock_svg() { return new FixedUiElement(Svg.clock);} public static clock_ui() { return new FixedUiElement(Svg.clock_img);} @@ -59,11 +59,6 @@ export default class Svg { public static close_svg() { return new FixedUiElement(Svg.close);} public static close_ui() { return new FixedUiElement(Svg.close_img);} - public static closed = " image/svg+xml " - public static closed_img = Img.AsImageElement(Svg.closed) - public static closed_svg() { return new FixedUiElement(Svg.closed);} - public static closed_ui() { return new FixedUiElement(Svg.closed_img);} - public static compass = " image/svg+xml N S E W NW SW NE SE " public static compass_img = Img.AsImageElement(Svg.compass) public static compass_svg() { return new FixedUiElement(Svg.compass);} @@ -214,6 +209,11 @@ export default class Svg { public static reload_svg() { return new FixedUiElement(Svg.reload);} public static reload_ui() { return new FixedUiElement(Svg.reload_img);} + public static ring = " image/svg+xml " + public static ring_img = Img.AsImageElement(Svg.ring) + public static ring_svg() { return new FixedUiElement(Svg.ring);} + public static ring_ui() { return new FixedUiElement(Svg.ring_img);} + public static search = " " public static search_img = Img.AsImageElement(Svg.search) public static search_svg() { return new FixedUiElement(Svg.search);} @@ -264,4 +264,4 @@ export default class Svg { public static wikipedia_svg() { return new FixedUiElement(Svg.wikipedia);} public static wikipedia_ui() { return new FixedUiElement(Svg.wikipedia_img);} -public static All = {"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"closed.svg": Svg.closed,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapillary.svg": Svg.mapillary,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"statistics.svg": Svg.statistics,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};} +public static All = {"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapillary.svg": Svg.mapillary,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"ring.svg": Svg.ring,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"statistics.svg": Svg.statistics,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};} diff --git a/UI/LayerSelection.ts b/UI/LayerSelection.ts index d73ef20..8406bc2 100644 --- a/UI/LayerSelection.ts +++ b/UI/LayerSelection.ts @@ -19,9 +19,9 @@ export class LayerSelection extends UIElement { const leafletStyle = layer.layerDef.GenerateLeafletStyle(new UIEventSource({id: "node/-1"}), true) const leafletHtml = leafletStyle.icon.html; const icon = - new FixedUiElement(leafletHtml) + new FixedUiElement(leafletHtml.Render()) .SetClass("single-layer-selection-toggle") - let iconUnselected: UIElement = new FixedUiElement(leafletHtml) + let iconUnselected: UIElement = new FixedUiElement(leafletHtml.Render()) .SetClass("single-layer-selection-toggle") .SetStyle("opacity:0.2;"); diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts index 0d84c83..2005b9e 100644 --- a/UI/SimpleAddUI.ts +++ b/UI/SimpleAddUI.ts @@ -54,7 +54,7 @@ export class SimpleAddUI extends UIElement { const presets = layer.layerDef.presets; for (const preset of presets) { const tags = TagUtils.KVtoProperties(preset.tags ?? []); - let icon: UIElement = new FixedUiElement(layer.layerDef.GenerateLeafletStyle(new UIEventSource(tags), false).icon.html).SetClass("simple-add-ui-icon"); + let icon: UIElement = new FixedUiElement(layer.layerDef.GenerateLeafletStyle(new UIEventSource(tags), false).icon.html.Render()).SetClass("simple-add-ui-icon"); const csCount = State.state.osmConnection.userDetails.data.csCount; let tagInfo = ""; diff --git a/assets/layers/bike_repair_station/bike_pump.svg b/assets/layers/bike_repair_station/bike_pump.svg new file mode 100644 index 0000000..0b5d201 --- /dev/null +++ b/assets/layers/bike_repair_station/bike_pump.svg @@ -0,0 +1,148 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index 619ee69..5b5315e 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -577,6 +577,18 @@ } ] }, + "iconOverlays": [ + { + "if": "opening_hours~*", + "then": "isOpen", + "badge": true + }, + { + "if": "service:bicycle:pump=yes", + "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg", + "badge": true + } + ], "width": { "render": "1" }, diff --git a/assets/svg/clock.svg b/assets/svg/clock.svg index ee3302c..5be61ad 100644 --- a/assets/svg/clock.svg +++ b/assets/svg/clock.svg @@ -1,22 +1,36 @@ - + viewBox="0 0 100 100" + height="100" + width="100"> + + + + image/svg+xml + + + + + + + id="path823" /> + id="path825" /> diff --git a/assets/svg/closed.svg b/assets/svg/closed.svg deleted file mode 100644 index 3374a7f..0000000 --- a/assets/svg/closed.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - diff --git a/assets/svg/ring.svg b/assets/svg/ring.svg new file mode 100644 index 0000000..c53a899 --- /dev/null +++ b/assets/svg/ring.svg @@ -0,0 +1,34 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/assets/tagRenderings/icons.json b/assets/tagRenderings/icons.json index 350e2fc..6261d99 100644 --- a/assets/tagRenderings/icons.json +++ b/assets/tagRenderings/icons.json @@ -14,6 +14,39 @@ } ] }, + "isOpen": { + "#": "Shows a coloured clock if opening hours are parsed. Uses the metatagging, suitable to use as a (badged) overlay", + "mappings": [ + { + "if": "_isOpen=yes", + "then": "clock:#0f0;ring:#0f0" + }, + { + "if": "_isOpen=no", + "then": "circle:#f00;clock:#fff" + }, + { + "#": "Still loading the country", + "if": { + "and": [ + "_isOpen=", + "opening_hours~*" + ] + }, + "then": "clock:#ff0;ring:#ff0" + }, + { + "#": "Still loading the country", + "if": { + "and": [ + "_isOpen=parse_error", + "opening_hours~*" + ] + }, + "then": "circle:#f0f;clock:#fff" + } + ] + }, "phonelink": { "render": "", "condition": "phone~*" diff --git a/assets/themes/shops/shops.json b/assets/themes/shops/shops.json index 67f688a..0b27f3f 100644 --- a/assets/themes/shops/shops.json +++ b/assets/themes/shops/shops.json @@ -67,6 +67,9 @@ } ] }, + "titleIcons": [ + "isOpen" + ], "description": { "en": "A shop", "fr": "Un magasin" @@ -238,23 +241,8 @@ }, "iconOverlays": [ { - "if": "_isOpen=yes", - "then": "clock:#0f0", - "badge": true - }, - { - "if": "_isOpen=no", - "then": "circle:#f00;clock:#fff", - "badge": true - }, - { - "if": { - "and": [ - "_isOpen=", - "opening_hours~*" - ] - }, - "then": "clock:#ff0", + "if": "opening_hours~*", + "then": "isOpen", "badge": true } ], diff --git a/package-lock.json b/package-lock.json index 8955437..b82b3c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6917,9 +6917,9 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "latlon2country": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/latlon2country/-/latlon2country-1.0.7.tgz", - "integrity": "sha512-47ZiSbCR7O7is1gNtd4T+1RyRHKoiunBvvZWET+oi+Y4+PgS2x/RsKqcBwhsyTXIDdNmMfrZB1sjFNMYEXqg4w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/latlon2country/-/latlon2country-1.0.8.tgz", + "integrity": "sha512-V0Zz9Zq/fAtuAoZWc+eaqpsNpCeGw2O0gu2XIA1KuWA5ePp3sK4o8ndz2dP9k68KtMx3Y//IoOgOb+NzFGgebQ==", "requires": { "@turf/boolean-point-in-polygon": "^6.0.1", "@turf/turf": "^5.1.6", diff --git a/package.json b/package.json index b694446..412091b 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "escape-html": "^1.0.3", "i18next-client": "^1.11.4", "jquery": "latest", - "latlon2country": "^1.0.7", + "latlon2country": "^1.0.8", "leaflet": "^1.7.1", "leaflet-providers": "^1.10.2", "libphonenumber": "0.0.10",