diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts
index 218d16d43b..da621a1cba 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 29e2e13cf1..28f5fb8f26 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 1b5c2812b8..c6dae7d7ba 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 5671968272..efd32ecef5 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 497b571b82..785fe43acf 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 91d6c49aca..72b7a83de8 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 db30cf7159..e49ccaa61a 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 = " "
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 = " "
- 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 = " "
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 = " "
+ 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 d73ef20338..8406bc240c 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 0d84c83357..2005b9e7cd 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 0000000000..0b5d201c57
--- /dev/null
+++ b/assets/layers/bike_repair_station/bike_pump.svg
@@ -0,0 +1,148 @@
+
+
diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json
index 619ee69b36..5b5315e88c 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 ee3302cf0c..5be61ad0f4 100644
--- a/assets/svg/clock.svg
+++ b/assets/svg/clock.svg
@@ -1,22 +1,36 @@
diff --git a/assets/svg/closed.svg b/assets/svg/closed.svg
deleted file mode 100644
index 3374a7feb3..0000000000
--- a/assets/svg/closed.svg
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
diff --git a/assets/svg/ring.svg b/assets/svg/ring.svg
new file mode 100644
index 0000000000..c53a899af2
--- /dev/null
+++ b/assets/svg/ring.svg
@@ -0,0 +1,34 @@
+
+
diff --git a/assets/tagRenderings/icons.json b/assets/tagRenderings/icons.json
index 350e2fc13b..6261d99944 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 67f688adb6..0b27f3f7a0 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 8955437a87..b82b3c9ade 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 b694446825..412091b63e 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",