New roaming rendering system which allows layers to push questions, badges and title-icons to all the other layers, improve bike-clean-services

This commit is contained in:
pietervdvn 2021-01-08 03:57:18 +01:00
parent 77ffdc093a
commit 2a31badd3d
16 changed files with 427 additions and 394 deletions

View file

@ -19,22 +19,18 @@ import {UIElement} from "../../UI/UIElement";
export default class LayerConfig {
static WAYHANDLING_DEFAULT = 0;
static WAYHANDLING_CENTER_ONLY = 1;
static WAYHANDLING_CENTER_AND_WAY = 2;
id: string;
name: Translation
description: Translation;
overpassTags: TagsFilter;
doNotDownload: boolean;
passAllFeatures: boolean;
minzoom: number;
title?: TagRenderingConfig;
titleIcons: TagRenderingConfig[];
icon: TagRenderingConfig;
iconOverlays: { if: TagsFilter, then: TagRenderingConfig, badge: boolean }[]
iconSize: TagRenderingConfig;
@ -42,14 +38,7 @@ export default class LayerConfig {
color: TagRenderingConfig;
width: TagRenderingConfig;
dashArray: TagRenderingConfig;
wayHandling: number;
static WAYHANDLING_DEFAULT = 0;
static WAYHANDLING_CENTER_ONLY = 1;
static WAYHANDLING_CENTER_AND_WAY = 2;
hideUnderlayingFeaturesMinPercentage?: number;
presets: {
@ -60,10 +49,10 @@ export default class LayerConfig {
tagRenderings: TagRenderingConfig [];
constructor(json: LayerConfigJson, roamingRenderings: TagRenderingConfig[],
constructor(json: LayerConfigJson,
context?: string) {
context = context + "." + json.id;
const self = this;
this.id = json.id;
this.name = Translations.T(json.name);
this.description = Translations.T(json.description);
@ -81,6 +70,28 @@ export default class LayerConfig {
}))
/** Given a key, gets the corresponding property from the json (or the default if not found
*
* The found value is interpreted as a tagrendering and fetched/parsed
* */
function tr(key: string, deflt) {
const v = json[key];
if (v === undefined || v === null) {
if (deflt === undefined) {
return undefined;
}
return new TagRenderingConfig(deflt, self.overpassTags, `${context}.${key}.default value`);
}
if (typeof v === "string") {
const shared = SharedTagRenderings.SharedTagRendering[v];
if (shared) {
console.log("Got shared TR:", v, "-->", shared)
return shared;
}
}
return new TagRenderingConfig(v, self.overpassTags, `${context}.${key}`);
}
/**
* Converts a list of tagRenderingCOnfigJSON in to TagRenderingConfig
* A string is interpreted as a name to call
@ -90,26 +101,27 @@ export default class LayerConfig {
if (tagRenderings === undefined) {
return [];
}
return tagRenderings.map(
(renderingJson, i) => {
if (typeof renderingJson === "string") {
if(renderingJson === "questions"){
return new TagRenderingConfig("questions")
if (renderingJson === "questions") {
return new TagRenderingConfig("questions", undefined)
}
const shared = SharedTagRenderings.SharedTagRendering[renderingJson];
if (shared !== undefined) {
return shared;
}
throw `Predefined tagRendering ${renderingJson} not found in ${context}`;
}
return new TagRenderingConfig(renderingJson, `${context}.tagrendering[${i}]`);
return new TagRenderingConfig(renderingJson, self.overpassTags, `${context}.tagrendering[${i}]`);
});
}
this.tagRenderings = trs(json.tagRenderings).concat(roamingRenderings);
this.tagRenderings = trs(json.tagRenderings);
const titleIcons = [];
@ -125,29 +137,10 @@ export default class LayerConfig {
this.titleIcons = trs(titleIcons);
function tr(key, deflt) {
const v = json[key];
if (v === undefined || v === null) {
if (deflt === undefined) {
return undefined;
}
return new TagRenderingConfig(deflt);
}
if (typeof v === "string") {
const shared = SharedTagRenderings.SharedTagRendering[v];
if (shared) {
console.log("Got shared TR:", v, "-->", shared)
return shared;
}
}
return new TagRenderingConfig(v, context + "." + key);
}
this.title = tr("title", undefined);
this.icon = tr("icon", Img.AsData(Svg.pin));
this.iconOverlays = (json.iconOverlays ?? []).map(overlay => {
let tr = new TagRenderingConfig(overlay.then);
this.iconOverlays = (json.iconOverlays ?? []).map((overlay, i) => {
let tr = new TagRenderingConfig(overlay.then, self.overpassTags, `iconoverlays.${i}`);
if (typeof overlay.then === "string" && SharedTagRenderings.SharedIcons[overlay.then] !== undefined) {
tr = SharedTagRenderings.SharedIcons[overlay.then];
}
@ -175,18 +168,54 @@ export default class LayerConfig {
}
public AddRoamingRenderings(addAll: {
tagRenderings: TagRenderingConfig[],
titleIcons: TagRenderingConfig[],
iconOverlays: { "if": TagsFilter, then: TagRenderingConfig, badge: boolean }[]
}): LayerConfig {
this.tagRenderings.push(...addAll.tagRenderings);
this.iconOverlays.push(...addAll.iconOverlays);
for (const icon of addAll.titleIcons) {
console.log("Adding ",icon, "to", this.id)
this.titleIcons.splice(0,0, icon);
}
return this;
}
public GetRoamingRenderings(): {
tagRenderings: TagRenderingConfig[],
titleIcons: TagRenderingConfig[],
iconOverlays: { "if": TagsFilter, then: TagRenderingConfig, badge: boolean }[]
} {
const tagRenderings = this.tagRenderings.filter(tr => tr.roaming);
const titleIcons = this.titleIcons.filter(tr => tr.roaming);
const iconOverlays = this.iconOverlays.filter(io => io.then.roaming)
return {
tagRenderings: tagRenderings,
titleIcons: titleIcons,
iconOverlays: iconOverlays
}
}
public GenerateLeafletStyle(tags: UIEventSource<any>, clickable: boolean):
{
color: string;
icon: {
iconUrl: string,
popupAnchor: [number, number];
iconAnchor: [number, number];
iconSize: [number, number];
html: UIElement;
className?: string;
};
weight: number; dashArray: number[]
icon:
{
html: UIElement,
iconSize: [number, number],
iconAnchor: [number, number],
popupAnchor: [number, number],
iconUrl: string,
className: string
},
color: string,
weight: number,
dashArray: number[]
} {
function num(str, deflt = 40) {
@ -259,7 +288,7 @@ export default class LayerConfig {
if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) {
html = new Combine([
(Svg.All[match[1] + ".svg"] as string)
.replace(/#000000/g, match[2])
.replace(/#000000/g, match[2])
]).SetStyle(style);
}
return html;

View file

@ -75,7 +75,7 @@ export default class LayoutConfig {
return SharedTagRenderings.SharedTagRendering[tr];
}
}
return new TagRenderingConfig(tr, `${this.id}.roaming_renderings[${i}]`);
return new TagRenderingConfig(tr, undefined,`${this.id}.roaming_renderings[${i}]`);
}
);
this.defaultBackgroundId = json.defaultBackgroundId;
@ -102,9 +102,23 @@ export default class LayoutConfig {
}
// @ts-ignore
return new LayerConfig(layer, this.roamingRenderings, `${this.id}.layers[${i}]`);
return new LayerConfig(layer, `${this.id}.layers[${i}]`)
});
// ALl the layers are constructed, let them share tags in piece now!
const roaming : {r, source: LayerConfig}[] = []
for (const layer of this.layers) {
roaming.push({r: layer.GetRoamingRenderings(), source:layer});
}
for (const layer of this.layers) {
for (const r of roaming) {
if(r.source == layer){
continue;
}
layer.AddRoamingRenderings(r.r);
}
}
this.clustering = {
maxZoom: 16,

View file

@ -1,4 +1,4 @@
import {TagsFilter} from "../../Logic/Tags";
import {And, TagsFilter} from "../../Logic/Tags";
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
import Translations from "../../UI/i18n/Translations";
import {FromJSON} from "./FromJSON";
@ -11,25 +11,26 @@ import {Translation} from "../../UI/i18n/Translation";
*/
export default class TagRenderingConfig {
render?: Translation;
question?: Translation;
condition?: TagsFilter;
readonly render?: Translation;
readonly question?: Translation;
readonly condition?: TagsFilter;
freeform?: {
key: string,
type: string,
addExtraTags: TagsFilter[];
readonly freeform?: {
readonly key: string,
readonly type: string,
readonly addExtraTags: TagsFilter[];
};
readonly multiAnswer: boolean;
mappings?: {
if: TagsFilter,
then: Translation
hideInAnswer: boolean | TagsFilter
readonly mappings?: {
readonly if: TagsFilter,
readonly then: Translation
readonly hideInAnswer: boolean | TagsFilter
}[]
readonly roaming: boolean;
constructor(json: string | TagRenderingConfigJson, context?: string) {
constructor(json: string | TagRenderingConfigJson, conditionIfRoaming: TagsFilter, context?: string) {
if (json === "questions") {
// Very special value
@ -46,10 +47,16 @@ export default class TagRenderingConfig {
this.multiAnswer = false;
return;
}
this.render = Translations.T(json.render);
this.question = Translations.T(json.question);
this.condition = FromJSON.Tag(json.condition ?? {"and": []}, `${context}.condition`);
this.roaming = json.roaming ?? false;
const condition = FromJSON.Tag(json.condition ?? {"and": []}, `${context}.condition`);
if (this.roaming && conditionIfRoaming !== undefined) {
this.condition = new And([condition, conditionIfRoaming]);
} else {
this.condition = condition;
}
if (json.freeform) {
this.freeform = {
key: json.freeform.key,

View file

@ -53,4 +53,10 @@ export interface TagRenderingConfigJson {
then: string | any
hideInAnswer?: boolean
}[]
/**
* If set to true, this tagRendering will escape the current layer and attach itself to all the other layers too.
* However, it will _only_ be shown if it matches the overpass-tags of the layer it was originally defined in.
*/
roaming?: boolean
}

View file

@ -56,7 +56,7 @@ export default class SharedLayers {
private static getSharedLayers(): Map<string, LayerConfig> {
const sharedLayers = new Map<string, LayerConfig>();
for (const layer of SharedLayers.sharedLayersListRaw) {
const parsed = new LayerConfig(layer, [], "shared_layers")
const parsed = new LayerConfig(layer, "shared_layers")
sharedLayers.set(layer.id, parsed);
sharedLayers[layer.id] = parsed;
}

View file

@ -68,7 +68,7 @@ export default class AllLayersPanel extends UIElement {
const layer = config.layers[i];
if (typeof layer !== "string") {
try {
const iconTagRendering = new TagRenderingConfig(layer.icon, "icon")
const iconTagRendering = new TagRenderingConfig(layer.icon, undefined, "icon")
const icon = iconTagRendering.GetRenderValue({"id": "node/-1"}).txt;
return `<img src='${icon}'>`
} catch (e) {

View file

@ -111,7 +111,7 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
this.validText = new VariableUiElement(value.map((json: TagRenderingConfigJson) => {
try{
new TagRenderingConfig(json, options?.title ?? "");
new TagRenderingConfig(json,undefined, options?.title ?? "");
return "";
}catch(e){
return "<span class='alert'>"+e+"</span>"

View file

@ -40,7 +40,7 @@ export default class TagRenderingPreview extends UIElement {
rendering =
new VariableUiElement(es.map(tagRenderingConfig => {
try {
const tr = new EditableTagRendering(self.previewTagValue, new TagRenderingConfig(tagRenderingConfig, "preview"));
const tr = new EditableTagRendering(self.previewTagValue, new TagRenderingConfig(tagRenderingConfig, undefined,"preview"));
return tr.Render();
} catch (e) {
return new Combine(["Could not show this tagrendering:", e.message]).Render();

View file

@ -25,7 +25,7 @@ export default class FeatureInfoBox extends UIElement {
}
const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI"))
const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI", undefined))
.SetClass("featureinfobox-title");
const titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)))
@ -53,7 +53,6 @@ export default class FeatureInfoBox extends UIElement {
const content = new Combine([
...renderings,
questionBox,
tail.SetClass("featureinfobox-tail")
]
)

View file

@ -67,7 +67,7 @@ export default class TagRenderingQuestion extends UIElement {
}
this._saveButton = new SaveButton(this._inputElement.GetValue(), State.state.osmConnection)
this._saveButton = new SaveButton(this._inputElement.GetValue(), State.state?.osmConnection)
.onClick(save)

View file

@ -4,12 +4,6 @@ import {Utils} from "../Utils";
export abstract class UIElement extends UIEventSource<string> {
/**
* In the 'deploy'-step, some code needs to be run by ts-node.
* However, ts-node crashes when it sees 'document'. When running from console, we flag this and disable all code where document is needed.
* This is a workaround and yet another hack
*/
public static runningFromConsole = false;
private static nextId: number = 0;
public readonly id: string;
public readonly _source: UIEventSource<any>;

View file

@ -26,6 +26,7 @@
"overpassTags": {
"or": [
"service:bicycle:cleaning=yes",
"service:bicycle:cleaning=diy",
"amenity=bicycle_wash"
]
},
@ -38,13 +39,87 @@
"nl": "Fietsschoonmaakpunt"
},
"tags": [
"amenity=bicycle_wash",
"service:bicycle:cleaning=yes"
"amenity=bicycle_wash"
]
}
],
"color": "#6bc4f7",
"iconOverlays": [
{
"if": {
"and": [
"service:bicycle:cleaning~*",
"amenity!=bike_wash"
]
},
"then": {
"render": "./assets/layers/bike_cleaning/bike_cleaning_icon.svg",
"roaming": true
},
"badge": true
}
],
"titleIcons": [
{
"render": "<img src='./assets/layers/bike_cleaning/bike_cleaning_icon.svg'/>",
"roaming": true
}
],
"tagRenderings": [
"images"
"images",
{
"question": "How much does it cost to use the cleaning service?",
"render": "Using the cleaning service costs {charge}",
"condition": "amenity!=bike_wash",
"freeform": {
"key": "service:bicycle:cleaning:charge",
"addExtraTags": [
"service:bicycle:cleaning:fee=yes"
]
},
"mappings": [
{
"if": "service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge=",
"then": "The cleaning service is free to use"
},
{
"if": "service:bicycle:cleaning:fee=no&",
"then": "Free to use",
"hideInAnswer": true
},
{
"if": "service:bicycle:cleaning:fee=yes",
"then": "The cleaning service has a fee"
}
],
"roaming": true
},
{
"question": "How much does it cost to use the cleaning service?",
"render": "Using the cleaning service costs {charge}",
"condition": "amenity=bike_wash",
"freeform": {
"key": "charge",
"addExtraTags": [
"fee=yes"
]
},
"mappings": [
{
"if": "fee=no&charge=",
"then": "Free to use cleaning service"
},
{
"if": "fee=no&",
"then": "Free to use",
"hideInAnswer": true
},
{
"if": "fee=yes",
"then": "The cleaning service has a fee"
}
],
"roaming": false
}
]
}

View file

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="98"
height="98"
viewBox="0 0 98 98"
version="1.1"
id="svg27"
sodipodi:docname="bike_cleaning_icon.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
style="fill:none">
<metadata
id="metadata31">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="999"
id="namedview29"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="1.9032258"
inkscape:cx="3.3258144"
inkscape:cy="153.42093"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer4">
<sodipodi:guide
position="-18.915254,142.92373"
orientation="0,1"
id="guide1101"
inkscape:locked="false" />
</sodipodi:namedview>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="bg"
style="display:inline">
<circle
style="fill:#6bc4f7"
cx="49"
cy="49"
r="49"
id="circle4" />
</g>
<defs
id="defs25">
<filter
id="filter0_d"
x="16"
y="23"
width="67"
height="63"
filterUnits="userSpaceOnUse"
style="color-interpolation-filters:sRGB">
<feFlood
flood-opacity="0"
result="BackgroundImageFix"
id="feFlood10" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
id="feColorMatrix12" />
<feOffset
dy="4"
id="feOffset14" />
<feGaussianBlur
stdDeviation="2"
id="feGaussianBlur16" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
id="feColorMatrix18" />
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow"
id="feBlend20" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow"
result="shape"
id="feBlend22" />
</filter>
</defs>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="fg"
style="display:inline">
<path
style="fill:#fffffc;fill-opacity:1;stroke-width:0.88272834;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
d="m 29.290947,36.983983 c 0.177522,0.916926 1.141318,1.734153 2.152651,1.827133 v 0 c 1.009061,0.09339 1.683548,-0.575238 1.505341,-1.491556 l -0.778933,-3.979234 c -0.177995,-0.914898 -1.141321,-1.734153 -2.15265,-1.827137 v 0 c -1.008849,-0.09196 -1.681967,0.575447 -1.503969,1.490347 z"
id="path1030" />
<g
id="g853"
transform="rotate(-14.887196,22.514768,42.323076)">
<rect
id="rect1000"
height="4.3486524"
width="31.293177"
transform="matrix(-0.90869492,0.41746083,0.41746083,0.90869492,0,0)"
y="37.392448"
x="-38.035305"
style="fill:#fffffc;fill-opacity:1;stroke-width:0.47552237" />
<g
transform="matrix(-0.44591765,-0.16517664,-0.16517664,0.44591765,67.8395,17.448122)"
id="g1056"
style="fill:#fffffc;fill-opacity:1">
<path
id="path1054"
d="m 100,18.328 v -8 c -19.636,0 -22.542,2.165 -31.688,11.338 l -0.925,0.926 2.828,2.829 -5.577,-5.577 c -5.663,-5.663 -14.843,-5.663 -20.506,0 l 31.112,31.113 c 5.662,-5.663 5.662,-14.844 0,-20.506 l -2.201,-2.201 0.934,-0.935 c 7.766,-7.79 8.961,-8.987 26.023,-8.987 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
</g>
</g>
<path
style="display:inline;fill:#fffffc;fill-opacity:1;stroke-width:0.88272834;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
d="m 30.604299,45.933279 c 0.177522,0.916926 1.141318,1.734153 2.152651,1.827133 v 0 c 1.009061,0.09339 1.683548,-0.575238 1.505341,-1.491556 l -0.778933,-3.979234 c -0.177995,-0.914898 -1.141321,-1.734153 -2.15265,-1.827137 v 0 c -1.008849,-0.09196 -1.681967,0.575447 -1.503969,1.490347 z"
id="path1030-1" />
<path
style="display:inline;fill:#fffffc;fill-opacity:1;stroke-width:0.88272834;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
d="m 36.337339,32.524819 c 0.595194,0.719731 1.832735,0.97518 2.764949,0.57217 v 0 c 0.930416,-0.401561 1.202022,-1.31163 0.606518,-2.030499 l -2.590483,-3.119364 c -0.594638,-0.717724 -1.832738,-0.975179 -2.76495,-0.572174 v 0 c -0.929545,0.402715 -1.200535,1.311056 -0.605894,2.02878 z"
id="path1030-2" />
<path
style="display:inline;fill:#fffffc;fill-opacity:1;stroke-width:0.84610248;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
d="m 41.803572,39.170925 c 0.535181,0.717421 1.708166,1.021795 2.620646,0.681415 v 0 c 0.910688,-0.339081 1.215009,-1.196589 0.679489,-1.913201 L 42.773781,34.829254 C 42.239036,34.11378 41.065612,33.807461 40.153135,34.147836 v 0 c -0.90991,0.340226 -1.213557,1.196111 -0.678808,1.911586 z"
id="path1030-7" />
<path
style="display:inline;fill:#fffffc;fill-opacity:1;stroke-width:0.88272834;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
d="m 43.859519,26.949791 c 0.870604,0.338107 2.075596,-0.04236 2.692927,-0.8488 v 0 c 0.616469,-0.804296 0.409796,-1.731271 -0.460658,-2.068474 l -3.782751,-1.460079 c -0.86914,-0.336626 -2.075598,0.04237 -2.692931,0.848797 v 0 c -0.615145,0.804879 -0.408777,1.730045 0.460366,2.066669 z"
id="path1030-0" />
<path
style="display:inline;fill:#fffffc;fill-opacity:1;stroke-width:0.88272834;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
d="m 52.723307,29.001623 c 0.889143,0.285817 2.069419,-0.165527 2.637779,-1.007196 v 0 c 0.567626,-0.83948 0.306279,-1.752549 -0.582661,-2.037473 l -3.862771,-1.232902 c -0.887593,-0.284427 -2.06942,0.16553 -2.637782,1.007193 v 0 c -0.56627,0.839983 -0.305334,1.751264 0.582263,2.035689 z"
id="path1030-9" />
</g>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="cycle"
sodipodi:insensitive="true">
<g
style="display:inline"
id="g896">
<path
inkscape:connector-curvature="0"
d="M 50.491776,73.912443 H 33.444983 l 9.298301,-20.633687 h 9.29819 6.71547 7.231926 m 0,0 -2.582831,-5.81969 h 3.615963 3.615963 m -4.649095,5.81969 1.033132,2.645404 2.066264,4.232492 5.682228,13.755791 m -8.781624,-20.633687 -4.907378,7.935991 -2.944516,4.761594 m -1.962973,3.174508 1.962973,-3.174508 m -11.777729,-6.877784 5.165661,11.63949 -10.331322,-23.278981 3.099397,6.348793 m 10.331322,17.988283 3.512671,-5.819801"
id="path10"
style="stroke:#ffffff;stroke-width:2.21892667" />
<path
inkscape:connector-curvature="0"
d="m 56.056733,73.012225 c 0,1.75162 -1.383057,3.148767 -3.061342,3.148767 -1.678174,0 -3.061231,-1.397147 -3.061231,-3.148767 0,-1.75151 1.383057,-3.148657 3.061231,-3.148657 1.678285,0 3.061342,1.397147 3.061342,3.148657 z"
id="path12"
style="stroke:#ffffff;stroke-width:1.10946333" />
<path
inkscape:connector-curvature="0"
d="m 45.170568,71.95424 c 0,6.717246 -5.310002,12.117115 -11.804689,12.117115 -6.494798,0 -11.804801,-5.399869 -11.804801,-12.117115 0,-6.717356 5.310003,-12.117225 11.804801,-12.117225 6.494687,0 11.804689,5.399869 11.804689,12.117225 z"
id="path14"
style="stroke:#ffffff;stroke-width:2.21892667" />
<g
id="g20"
transform="matrix(1.1094633,0,0,1.1094633,0.48127584,3.080534)">
<circle
style="stroke:#ffffff;stroke-width:2"
id="circle18"
r="11"
cy="63"
cx="66" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -1,292 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="98"
height="98"
viewBox="0 0 98 98"
version="1.1"
id="svg27"
sodipodi:docname="bike_cleaning.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
style="fill:none">
<metadata
id="metadata31">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1001"
id="namedview29"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="3.8064516"
inkscape:cx="-11.021894"
inkscape:cy="44.52432"
inkscape:window-x="0"
inkscape:window-y="1080"
inkscape:window-maximized="1"
inkscape:current-layer="layer4">
<sodipodi:guide
position="-18.915254,142.92373"
orientation="0,1"
id="guide1101"
inkscape:locked="false" />
</sodipodi:namedview>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="bg"
style="display:inline">
<circle
style="fill:#6bc4f7"
cx="49"
cy="49"
r="49"
id="circle4" />
</g>
<defs
id="defs25">
<filter
id="filter0_d"
x="16"
y="23"
width="67"
height="63"
filterUnits="userSpaceOnUse"
style="color-interpolation-filters:sRGB">
<feFlood
flood-opacity="0"
result="BackgroundImageFix"
id="feFlood10" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
id="feColorMatrix12" />
<feOffset
dy="4"
id="feOffset14" />
<feGaussianBlur
stdDeviation="2"
id="feGaussianBlur16" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
id="feColorMatrix18" />
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow"
id="feBlend20" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow"
result="shape"
id="feBlend22" />
</filter>
</defs>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="fg"
style="display:inline">
<path
style="stroke:#ffffff;stroke-width:2"
id="path10"
d="M 53.833,72.506562 H 38.4681 l 8.3809,-18.5979 h 8.3808 6.0529 6.5184 m 0,0 -2.328,-5.2455 h 3.2592 3.2592 m -4.1904,5.2455 0.9312,2.3844 1.8624,3.8149 5.1216,12.3986 m -7.9152,-18.5979 -4.4232,7.153 -2.654,4.2918 m -1.7693,2.8613 1.7693,-2.8613 m -10.6157,-6.1992 4.656,10.4911 -9.312,-20.9822 2.7936,5.7224 m 9.312,16.2135 3.1661,-5.2456"
inkscape:connector-curvature="0" />
<path
style="stroke:#ffffff"
id="path12"
d="m 58.8489,71.695162 c 0,1.5788 -1.2466,2.8381 -2.7593,2.8381 -1.5126,0 -2.7592,-1.2593 -2.7592,-2.8381 0,-1.5787 1.2466,-2.838 2.7592,-2.838 1.5127,0 2.7593,1.2593 2.7593,2.838 z"
inkscape:connector-curvature="0" />
<path
style="stroke:#ffffff;stroke-width:2"
id="path14"
d="m 49.0368,70.741562 c 0,6.0545 -4.7861,10.9216 -10.64,10.9216 -5.854,0 -10.6401,-4.8671 -10.6401,-10.9216 0,-6.0546 4.7861,-10.9217 10.6401,-10.9217 5.8539,0 10.64,4.8671 10.64,10.9217 z"
inkscape:connector-curvature="0" />
<g
transform="translate(8.7566998,8.6631615)"
id="g20">
<circle
cx="66"
cy="63"
r="11"
id="circle18"
style="stroke:#ffffff;stroke-width:2" />
</g>
<g
id="g1094"
transform="matrix(-0.44591765,-0.16517664,-0.16517664,0.44591765,67.8395,17.448122)"
style="display:inline;fill:#fffffc;fill-opacity:1">
<rect
id="rect1000"
height="9.1450005"
width="65.807999"
transform="matrix(0.7071,0.7071,-0.7071,0.7071,44.7059,-26.5459)"
y="36.118999"
x="21.492001"
style="fill:#fffffc;fill-opacity:1" />
<g
id="g1008"
style="fill:#fffffc;fill-opacity:1">
<path
id="path1002"
d="m 27.304,32.959 c -0.825,0.409 -1.825,0.072 -2.233,-0.753 v 0 C 24.662,31.381 25,30.381 25.825,29.972 l 3.584,-1.776 c 0.825,-0.409 1.824,-0.071 2.233,0.754 v 0 c 0.409,0.825 0.071,1.825 -0.753,2.233 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1004"
d="m 16.922,39.164 c -0.789,0.47 -1.809,0.211 -2.278,-0.578 v 0 c -0.47,-0.789 -0.211,-1.809 0.579,-2.278 l 3.427,-2.04 c 0.788,-0.47 1.808,-0.211 2.277,0.578 v 0 c 0.47,0.789 0.211,1.809 -0.578,2.277 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1006"
d="M 6.539,45.369 C 5.786,45.9 4.747,45.719 4.216,44.967 v 0 c -0.53,-0.753 -0.35,-1.792 0.403,-2.323 l 3.27,-2.304 c 0.752,-0.53 1.792,-0.35 2.322,0.403 v 0 c 0.53,0.752 0.35,1.792 -0.403,2.322 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
</g>
<g
id="g1016"
style="fill:#fffffc;fill-opacity:1">
<path
id="path1010"
d="m 65.089,70.024 c -0.526,0.757 -1.564,0.941 -2.32,0.415 v 0 c -0.755,-0.526 -0.94,-1.565 -0.414,-2.321 l 2.286,-3.28 c 0.525,-0.755 1.564,-0.941 2.32,-0.415 v 0 c 0.755,0.526 0.94,1.564 0.415,2.319 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1012"
d="m 58.977,80.461 c -0.465,0.793 -1.484,1.057 -2.274,0.592 v 0 c -0.791,-0.467 -1.056,-1.484 -0.59,-2.275 l 2.021,-3.438 c 0.465,-0.791 1.483,-1.056 2.274,-0.59 v 0 c 0.791,0.465 1.055,1.482 0.59,2.274 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1014"
d="m 52.865,90.898 c -0.404,0.829 -1.404,1.171 -2.23,0.767 v 0 c -0.827,-0.405 -1.17,-1.402 -0.766,-2.229 l 1.757,-3.594 c 0.404,-0.827 1.402,-1.17 2.229,-0.765 v 0 c 0.827,0.403 1.17,1.401 0.766,2.229 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
</g>
<path
id="path1018"
d="m 59.759,64.613 c -0.59,0.708 -1.641,0.802 -2.348,0.211 v 0 c -0.706,-0.59 -0.8,-1.642 -0.21,-2.349 l 2.564,-3.067 c 0.589,-0.706 1.641,-0.802 2.348,-0.211 v 0 c 0.706,0.59 0.8,1.641 0.211,2.347 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1020"
d="m 52.76,74.477 c -0.533,0.75 -1.57,0.924 -2.318,0.391 v 0 C 49.694,74.335 49.52,73.298 50.053,72.55 l 2.314,-3.247 c 0.531,-0.747 1.569,-0.923 2.316,-0.39 v 0 c 0.748,0.533 0.922,1.57 0.39,2.318 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1022"
d="m 45.759,84.341 c -0.475,0.791 -1.5,1.044 -2.288,0.569 v 0 c -0.789,-0.477 -1.043,-1.499 -0.567,-2.288 l 2.064,-3.427 c 0.475,-0.789 1.5,-1.043 2.287,-0.567 v 0 c 0.789,0.475 1.042,1.498 0.568,2.288 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1024"
d="m 54.202,59.292 c -0.629,0.661 -1.675,0.686 -2.335,0.056 v 0 c -0.66,-0.629 -0.685,-1.676 -0.056,-2.337 l 2.736,-2.867 c 0.628,-0.66 1.675,-0.687 2.336,-0.057 v 0 c 0.66,0.63 0.684,1.675 0.056,2.335 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1026"
d="m 46.638,68.593 c -0.576,0.707 -1.613,0.812 -2.318,0.236 v 0 C 43.615,68.253 43.51,67.216 44.086,66.51 l 2.5,-3.061 c 0.574,-0.705 1.612,-0.812 2.316,-0.235 v 0 c 0.706,0.576 0.81,1.613 0.235,2.318 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1028"
d="m 39.073,77.896 c -0.521,0.751 -1.553,0.936 -2.3,0.414 v 0 c -0.749,-0.522 -0.935,-1.551 -0.413,-2.3 l 2.264,-3.256 c 0.521,-0.749 1.551,-0.934 2.299,-0.412 v 0 c 0.749,0.521 0.934,1.549 0.413,2.3 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1030"
d="m 48.646,53.97 c -0.669,0.616 -1.709,0.571 -2.324,-0.099 v 0 c -0.614,-0.668 -0.569,-1.71 0.1,-2.325 l 2.908,-2.667 c 0.668,-0.614 1.709,-0.571 2.324,0.099 v 0 c 0.613,0.669 0.568,1.709 -0.1,2.323 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1032"
d="m 40.517,62.709 c -0.619,0.665 -1.656,0.699 -2.318,0.081 v 0 C 37.537,62.172 37.501,61.135 38.12,60.472 l 2.686,-2.876 c 0.617,-0.662 1.655,-0.698 2.317,-0.08 v 0 c 0.663,0.619 0.698,1.655 0.08,2.318 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1034"
d="m 32.387,71.449 c -0.567,0.713 -1.604,0.827 -2.312,0.26 v 0 c -0.709,-0.568 -0.826,-1.602 -0.258,-2.312 l 2.465,-3.083 c 0.567,-0.71 1.603,-0.825 2.312,-0.259 v 0 c 0.71,0.568 0.825,1.602 0.258,2.312 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1036"
d="m 43.09,48.648 c -0.708,0.57 -1.743,0.456 -2.312,-0.253 v 0 c -0.568,-0.708 -0.454,-1.744 0.255,-2.313 l 3.079,-2.467 c 0.707,-0.569 1.743,-0.457 2.312,0.253 v 0 c 0.568,0.708 0.454,1.743 -0.253,2.312 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1038"
d="m 34.396,56.826 c -0.661,0.621 -1.699,0.587 -2.318,-0.074 v 0 c -0.619,-0.662 -0.585,-1.699 0.076,-2.319 l 2.872,-2.689 c 0.66,-0.619 1.698,-0.587 2.317,0.075 v 0 c 0.619,0.66 0.585,1.697 -0.075,2.318 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1040"
d="m 25.701,65.004 c -0.614,0.673 -1.656,0.719 -2.325,0.105 v 0 c -0.67,-0.615 -0.716,-1.654 -0.103,-2.324 l 2.665,-2.913 c 0.613,-0.671 1.654,-0.716 2.323,-0.104 v 0 c 0.671,0.613 0.717,1.652 0.104,2.325 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1042"
d="m 37.534,43.326 c -0.748,0.524 -1.777,0.341 -2.3,-0.408 v 0 c -0.522,-0.748 -0.338,-1.778 0.41,-2.301 l 3.251,-2.267 c 0.746,-0.523 1.777,-0.341 2.3,0.408 v 0 c 0.522,0.748 0.339,1.777 -0.408,2.3 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1044"
d="m 28.275,50.942 c -0.705,0.578 -1.743,0.476 -2.319,-0.229 v 0 c -0.576,-0.705 -0.474,-1.743 0.23,-2.319 l 3.058,-2.504 c 0.703,-0.577 1.742,-0.475 2.317,0.229 v 0 c 0.577,0.705 0.474,1.741 -0.23,2.319 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1046"
d="m 19.015,58.559 c -0.66,0.633 -1.707,0.609 -2.337,-0.05 v 0 c -0.63,-0.661 -0.608,-1.706 0.052,-2.337 l 2.865,-2.74 c 0.66,-0.632 1.706,-0.608 2.335,0.051 v 0 c 0.631,0.66 0.608,1.703 -0.051,2.337 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1048"
d="m 31.978,38.004 c -0.788,0.478 -1.812,0.226 -2.289,-0.563 v 0 c -0.476,-0.787 -0.223,-1.812 0.565,-2.29 l 3.422,-2.067 c 0.786,-0.477 1.812,-0.226 2.289,0.563 v 0 c 0.476,0.787 0.223,1.812 -0.563,2.288 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1050"
d="m 22.153,45.059 c -0.747,0.536 -1.785,0.363 -2.319,-0.383 v 0 c -0.533,-0.747 -0.361,-1.785 0.386,-2.319 l 3.244,-2.318 c 0.746,-0.535 1.785,-0.362 2.318,0.384 v 0 c 0.534,0.747 0.362,1.783 -0.385,2.319 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<path
id="path1052"
d="M 12.329,52.113 C 11.622,52.707 10.57,52.614 9.98,51.908 v 0 C 9.389,51.201 9.481,50.151 10.187,49.56 l 3.065,-2.57 c 0.706,-0.592 1.757,-0.499 2.348,0.206 v 0 c 0.592,0.706 0.5,1.754 -0.207,2.349 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
<g
id="g1056"
style="fill:#fffffc;fill-opacity:1">
<path
id="path1054"
d="m 100,18.328 v -8 c -19.636,0 -22.542,2.165 -31.688,11.338 l -0.925,0.926 2.828,2.829 -5.577,-5.577 c -5.663,-5.663 -14.843,-5.663 -20.506,0 l 31.112,31.113 c 5.662,-5.663 5.662,-14.844 0,-20.506 l -2.201,-2.201 0.934,-0.935 c 7.766,-7.79 8.961,-8.987 26.023,-8.987 z"
inkscape:connector-curvature="0"
style="fill:#fffffc;fill-opacity:1" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -186,15 +186,6 @@
"condition": "service:bicycle:diy=yes",
"render": "<img src='./assets/layers/bike_shop/tools.svg'/>"
},
{
"condition": {
"or": [
"service:bicycle:cleaning=yes",
"service:bicycle:cleaning=diy"
]
},
"render": "<img src='./assets/layers/bike_shop/bike_cleaning.svg'/>"
},
"defaults"
],
"description": {

View file

@ -1,5 +1,5 @@
import {UIElement} from "../UI/UIElement";
UIElement.runningFromConsole = true;
import {Utils} from "../Utils";
Utils.runningFromConsole = true;
import {equal} from "assert";
import T from "./TestHelper";
import {FromJSON} from "../Customizations/JSON/FromJSON";
@ -10,7 +10,6 @@ import {UIEventSource} from "../Logic/UIEventSource";
import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig";
import EditableTagRendering from "../UI/Popup/EditableTagRendering";
import {SubstitutedTranslation} from "../UI/SpecialVisualizations";
import {Utils} from "../Utils";
import {Translation} from "../UI/i18n/Translation";
import {OH, OpeningHour} from "../UI/OpeningHours/OpeningHours";
import PublicHolidayInput from "../UI/OpeningHours/PublicHolidayInput";
@ -40,6 +39,13 @@ new T([
equal(notReg.matches([{k:"x",v:""}]), true)
equal(notReg.matches([]), true)
const noMatch = FromJSON.Tag("key!=value") as Tag;
equal(noMatch.matches([{k:"key",v:"value"}]), false)
equal(noMatch.matches([{k:"key",v:"otherValue"}]), true)
equal(noMatch.matches([{k:"key",v:""}]), true)
equal(noMatch.matches([{k:"otherKey",v:""}]), true)
})],
@ -92,7 +98,7 @@ new T([
}
],
condition: "x="
}, "");
}, undefined,"");
equal(undefined, tr.GetRenderValue({"foo": "bar"}));
equal("Has no name", tr.GetRenderValue({"noname": "yes"})?.txt);
@ -145,7 +151,7 @@ new T([
]
};
const constr = new TagRenderingConfig(def, "test");
const constr = new TagRenderingConfig(def, undefined,"test");
const uiEl = new EditableTagRendering(new UIEventSource<any>(
{leisure: "park", "access": "no"}), constr
);