diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts
index 1bb0557..c2bbd3f 100644
--- a/Customizations/JSON/LayerConfig.ts
+++ b/Customizations/JSON/LayerConfig.ts
@@ -12,7 +12,6 @@ import {SubstitutedTranslation} from "../../UI/SpecialVisualizations";
import {Utils} from "../../Utils";
import Combine from "../../UI/Base/Combine";
import {VariableUiElement} from "../../UI/Base/VariableUIElement";
-import {UIElement} from "../../UI/UIElement";
import {UIEventSource} from "../../Logic/UIEventSource";
export default class LayerConfig {
@@ -35,6 +34,7 @@ export default class LayerConfig {
titleIcons: TagRenderingConfig[];
icon: TagRenderingConfig;
+ iconOverlays: { if: TagsFilter, then: string, badge: boolean }[]
iconSize: TagRenderingConfig;
rotation: TagRenderingConfig;
color: TagRenderingConfig;
@@ -138,6 +138,14 @@ export default class LayerConfig {
this.title = tr("title", undefined);
this.icon = tr("icon", Img.AsData(Svg.bug));
+ this.iconOverlays = (json.iconOverlays ?? []).map(overlay => {
+ return {
+ if: FromJSON.Tag(overlay.if),
+ then: overlay.then,
+ badge: overlay.badge ?? false
+ }
+ });
+
const iconPath = this.icon.GetRenderValue({id: "node/-1"}).txt;
if (iconPath.startsWith(Utils.assets_path)) {
const iconKey = iconPath.substr(Utils.assets_path.length);
@@ -222,23 +230,65 @@ export default class LayerConfig {
}
const iconUrlStatic = render(this.icon);
-
- var mappedHtml = tags.map(_ => {
+ const self = this;
+ var mappedHtml = tags.map(tags => {
// What do you mean, 'tags' is never read?
// It is read implicitly in the 'render' method
- const iconUrl = render(this.icon);
- const rotation = render(this.rotation, "0deg");
- let html = `
`;
+ const iconUrl = render(self.icon);
+ const rotation = render(self.rotation, "0deg");
- if (iconUrl.startsWith(Utils.assets_path)) {
- const key = iconUrl.substr(Utils.assets_path.length);
- html = new Combine([
- (Svg.All[key] as string).replace(/stop-color:#000000/g, 'stop-color:' + color)
- ]).SetStyle(`width:100%;height:100%;rotate:${rotation};display:block;`)
+ let htmlParts = [];
+ let sourceParts = iconUrl.split(";");
- .Render();
+ function genHtmlFromString(sourcePart: string, style?: string): string {
+ style = style ?? `width:100%;height:100%;rotate:${rotation};display:block;position: absolute; top: 0, left: 0`;
+ let html = `
`;
+ 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();
+ }
+
+ 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();
+ }
+ return html;
}
- return html;
+
+
+ for (const sourcePart of sourceParts) {
+ htmlParts.push(genHtmlFromString(sourcePart))
+ }
+
+
+ let badges = [];
+ for (const iconOverlay of self.iconOverlays) {
+ if (!iconOverlay.if.matchesProperties(tags)) {
+ continue;
+ }
+ if (iconOverlay.badge) {
+ badges.push(genHtmlFromString(iconOverlay.then, "display: block;height:100%"))
+ } else {
+ htmlParts.push(genHtmlFromString(iconOverlay.then));
+ }
+ }
+
+ if (badges.length > 0) {
+ const badgesComponent = new Combine(badges)
+
+ .SetStyle("display:flex;height:50%;width:100%;position:absolute;top:50%;left:50%;")
+ .Render()
+
+ htmlParts.push(badgesComponent)
+ }
+ return htmlParts.join("");
})
diff --git a/Customizations/JSON/LayerConfigJson.ts b/Customizations/JSON/LayerConfigJson.ts
index ac1b338..3017882 100644
--- a/Customizations/JSON/LayerConfigJson.ts
+++ b/Customizations/JSON/LayerConfigJson.ts
@@ -60,6 +60,14 @@ export interface LayerConfigJson {
*/
icon?: string | TagRenderingConfigJson;
+ /**
+ * IconsOverlays are a list of extra icons/badges to overlay over the icon.
+ * The 'badge'-toggle changes their behaviour.
+ * If badge is set, it will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.
+ * If badges is false, it'll be a simple overlay
+ */
+ iconOverlays?: {if: AndOrTagConfigJson, then: string, badge?: boolean}[]
+
/**
* A string containing "width,height" or "width,height,anchorpoint" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...
* Default is '40,40,center'
diff --git a/Svg.ts b/Svg.ts
index 144fdb3..5010076 100644
--- a/Svg.ts
+++ b/Svg.ts
@@ -44,6 +44,11 @@ export default class Svg {
public static checkmark_svg() { return new FixedUiElement(Svg.checkmark);}
public static checkmark_ui() { return new FixedUiElement(Svg.checkmark_img);}
+ public static circle = " "
+ public static circle_img = Img.AsImageElement(Svg.circle)
+ public static circle_svg() { return new FixedUiElement(Svg.circle);}
+ public static circle_ui() { return new FixedUiElement(Svg.circle_img);}
+
public static close = " "
public static close_img = Img.AsImageElement(Svg.close)
public static close_svg() { return new FixedUiElement(Svg.close);}
@@ -54,6 +59,11 @@ export default class Svg {
public static compass_svg() { return new FixedUiElement(Svg.compass);}
public static compass_ui() { return new FixedUiElement(Svg.compass_img);}
+ public static cross_bottom_right = " "
+ public static cross_bottom_right_img = Img.AsImageElement(Svg.cross_bottom_right)
+ public static cross_bottom_right_svg() { return new FixedUiElement(Svg.cross_bottom_right);}
+ public static cross_bottom_right_ui() { return new FixedUiElement(Svg.cross_bottom_right_img);}
+
public static crosshair_blue_center = " "
public static crosshair_blue_center_img = Img.AsImageElement(Svg.crosshair_blue_center)
public static crosshair_blue_center_svg() { return new FixedUiElement(Svg.crosshair_blue_center);}
@@ -179,6 +189,11 @@ export default class Svg {
public static phone_svg() { return new FixedUiElement(Svg.phone);}
public static phone_ui() { return new FixedUiElement(Svg.phone_img);}
+ public static pin = " "
+ public static pin_img = Img.AsImageElement(Svg.pin)
+ public static pin_svg() { return new FixedUiElement(Svg.pin);}
+ public static pin_ui() { return new FixedUiElement(Svg.pin_img);}
+
public static pop_out = " "
public static pop_out_img = Img.AsImageElement(Svg.pop_out)
public static pop_out_svg() { return new FixedUiElement(Svg.pop_out);}
@@ -229,4 +244,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,"close.svg": Svg.close,"compass.svg": Svg.compass,"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,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"star.svg": Svg.star,"statistics.svg": Svg.statistics,"up.svg": Svg.up,"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,"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,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"star.svg": Svg.star,"statistics.svg": Svg.statistics,"up.svg": Svg.up,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};}
diff --git a/UI/LayerSelection.ts b/UI/LayerSelection.ts
index 3391e14..d73ef20 100644
--- a/UI/LayerSelection.ts
+++ b/UI/LayerSelection.ts
@@ -5,6 +5,7 @@ import State from "../State";
import Translations from "./i18n/Translations";
import {FixedUiElement} from "./Base/FixedUiElement";
import {VariableUiElement} from "./Base/VariableUIElement";
+import {UIEventSource} from "../Logic/UIEventSource";
export class LayerSelection extends UIElement {
@@ -15,15 +16,15 @@ export class LayerSelection extends UIElement {
this._checkboxes = [];
for (const layer of State.state.filteredLayers.data) {
- let iconUrl = "./asets/checkbox.svg";
- if (layer.layerDef.icon ) {
- iconUrl = layer.layerDef.icon.GetRenderValue({id:"node/-1"}).txt;
- }
- const icon = new FixedUiElement(`
`);
+ const leafletStyle = layer.layerDef.GenerateLeafletStyle(new UIEventSource({id: "node/-1"}), true)
+ const leafletHtml = leafletStyle.icon.html;
+ const icon =
+ new FixedUiElement(leafletHtml)
+ .SetClass("single-layer-selection-toggle")
+ let iconUnselected: UIElement = new FixedUiElement(leafletHtml)
+ .SetClass("single-layer-selection-toggle")
+ .SetStyle("opacity:0.2;");
- let iconUnselected: UIElement;
- iconUnselected = new FixedUiElement(`
`);
-
const name = Translations.WT(layer.layerDef.name).Clone()
.SetStyle("font-size:large;margin-left: 0.5em;");
diff --git a/assets/layers/bike_repair_station/bike_repair_station.json b/assets/layers/bike_repair_station/bike_repair_station.json
index 2506d6c..a0e5432 100644
--- a/assets/layers/bike_repair_station/bike_repair_station.json
+++ b/assets/layers/bike_repair_station/bike_repair_station.json
@@ -490,6 +490,13 @@
}
]
},
+ "iconOverlays": [
+ {
+ "if": "operator=De Fietsambassade Gent",
+ "then": "./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg",
+ "badge": true
+ }
+ ],
"iconSize": {
"render": {
"en": "50,50,bottom"
diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json
index 7f0ce2e..b85ff4f 100644
--- a/assets/layers/drinking_water/drinking_water.json
+++ b/assets/layers/drinking_water/drinking_water.json
@@ -17,15 +17,21 @@
}
},
"icon": {
- "render": "./assets/layers/drinking_water/drinking_water.svg",
- "mappings": [
- {
- "if": {"or": ["operational_status=broken", "operational_status=closed"]},
-
- "then": "./assets/layers/drinking_water/drinking_water_broken.svg"
- }
- ]
+ "render": "pin:#6BC4F7;./assets/layers/drinking_water/drips.svg"
},
+ "iconOverlays": [
+ {
+ "if": {
+ "or": [
+ "operational_status=broken",
+ "operational_status=closed"
+ ]
+ },
+ "then": "close:#c33",
+ "badge": true
+ }
+ ],
+
"iconSize": "40,40,bottom",
"overpassTags": {
"and": [
diff --git a/assets/layers/drinking_water/drinking_water.svg b/assets/layers/drinking_water/drinking_water.svg
deleted file mode 100644
index 4962afe..0000000
--- a/assets/layers/drinking_water/drinking_water.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-
diff --git a/assets/layers/drinking_water/drinking_water_broken.svg b/assets/layers/drinking_water/drinking_water_broken.svg
deleted file mode 100644
index 9d0ba40..0000000
--- a/assets/layers/drinking_water/drinking_water_broken.svg
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
diff --git a/assets/layers/drinking_water/drips.svg b/assets/layers/drinking_water/drips.svg
new file mode 100644
index 0000000..b4e242e
--- /dev/null
+++ b/assets/layers/drinking_water/drips.svg
@@ -0,0 +1,101 @@
+
+
diff --git a/assets/svg/circle.svg b/assets/svg/circle.svg
new file mode 100644
index 0000000..c4463d6
--- /dev/null
+++ b/assets/svg/circle.svg
@@ -0,0 +1,98 @@
+
+
diff --git a/assets/svg/cross_bottom_right.svg b/assets/svg/cross_bottom_right.svg
new file mode 100644
index 0000000..5e0b2c3
--- /dev/null
+++ b/assets/svg/cross_bottom_right.svg
@@ -0,0 +1,85 @@
+
+
+
+
diff --git a/assets/svg/pin.svg b/assets/svg/pin.svg
new file mode 100644
index 0000000..97b1577
--- /dev/null
+++ b/assets/svg/pin.svg
@@ -0,0 +1,102 @@
+
+
diff --git a/index.css b/index.css
index f013127..6259763 100644
--- a/index.css
+++ b/index.css
@@ -96,6 +96,21 @@ a {
box-shadow: 0 0 10px var(--shadow-color);
}
+.single-layer-selection-toggle{
+ position: relative;
+ width: 2.5em;
+ height: 2.5em;
+}
+.single-layer-selection-toggle img{
+ max-height: 2.5em !important;
+ max-width: 2.5em !important;
+}
+
+.single-layer-selection-toggle svg{
+ max-height:2.5em !important;
+ max-width: 2.5em !important;
+}
+
.layer-selection-toggle {
border-radius: 1em;
display: flex;