Fix performance, add 'isClosed' badge to shops

This commit is contained in:
Pieter Vander Vennet 2020-12-06 00:20:27 +01:00
parent efd7631837
commit a9dfce72a6
18 changed files with 334 additions and 124 deletions

View file

@ -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 = `<img src="${sourcePart}" style="${style}" />`;
let html: UIElement = new FixedUiElement(`<img src="${sourcePart}" style="${style}" />`);
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],

View file

@ -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', ...

View file

@ -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;
}

View file

@ -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;

View file

@ -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";
}
})
})

View file

@ -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 = {

14
Svg.ts

File diff suppressed because one or more lines are too long

View file

@ -19,9 +19,9 @@ export class LayerSelection extends UIElement {
const leafletStyle = layer.layerDef.GenerateLeafletStyle(new UIEventSource<any>({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;");

View file

@ -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<any>(tags), false).icon.html).SetClass("simple-add-ui-icon");
let icon: UIElement = new FixedUiElement(layer.layerDef.GenerateLeafletStyle(new UIEventSource<any>(tags), false).icon.html.Render()).SetClass("simple-add-ui-icon");
const csCount = State.state.osmConnection.userDetails.data.csCount;
let tagInfo = "";

View file

@ -0,0 +1,148 @@
<?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="svg42"
sodipodi:docname="logo.svg"
style="fill:none"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata46">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</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="namedview44"
showgrid="false"
inkscape:zoom="4"
inkscape:cx="-2.2900136"
inkscape:cy="62.988337"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer4" />
<defs
id="defs40">
<filter
id="filter0_d"
x="-3"
y="2"
width="102.479"
height="102.479"
filterUnits="userSpaceOnUse"
style="color-interpolation-filters:sRGB">
<feFlood
flood-opacity="0"
result="BackgroundImageFix"
id="feFlood22" />
<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="feColorMatrix24" />
<feOffset
dy="4"
id="feOffset26" />
<feGaussianBlur
stdDeviation="2"
id="feGaussianBlur28" />
<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="feColorMatrix30" />
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow"
id="feBlend32" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow"
result="shape"
id="feBlend34" />
</filter>
<clipPath
id="clip0">
<rect
width="66.806396"
height="66.806396"
transform="rotate(-45,59.937093,23.412543)"
id="rect37"
x="0"
y="0"
style="fill:#ffffff" />
</clipPath>
</defs>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="re"
style="display:inline"
transform="translate(-12.466103,-2.0847473)">
<rect
style="fill:#fffcff;fill-opacity:1;stroke:#ffffff;stroke-width:3.50900006;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect834"
width="23.963444"
height="3.9908068"
x="47.66375"
y="21.641689" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect839"
width="6.5160999"
height="8.9842997"
x="56.447304"
y="27.271845" />
<path
style="fill:none;stroke:#ffffff;stroke-width:7.93900013;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 47.842047,77.142657 23.821625,0.209511"
id="path842"
inkscape:connector-curvature="0" />
<rect
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2.87199998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect863"
width="7.4682202"
height="36.044868"
x="55.90678"
y="37.580132" />
<path
style="display:inline;fill:none;stroke:#ffffff;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 64.040577,70.321209 c 0,0 6.250256,1.104191 6.250256,-4.19911 0,-5.303301 0,-31.201087 0,-31.201087 0,0 0.176777,-3.623922 4.154253,-3.623922 3.977475,0 4.065864,3.447145 4.065864,3.447145 l 0.08839,29.875262"
id="path868"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscscc" />
<path
style="display:inline;fill:#fffeff;fill-opacity:1;stroke:#fffeff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.97014926"
d="m 76.543734,62.21875 4.018766,-0.03125 0.125,4.640625 3,-0.0625 0.104018,4.221891 -3.783471,-0.02589 c -2.730516,-0.27045 -3.676466,-1.207877 -3.48639,-3.569907 z"
id="path871"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -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"
},

View file

@ -1,22 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="100"
height="100"
viewBox="0 0 100 100"
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"
id="svg5"
style="fill:none"
version="1.1"
style="fill:none">
<circle
cx="50"
cy="50"
r="45.0"
id="circle4"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
viewBox="0 0 100 100"
height="100"
width="100">
<metadata
id="metadata11">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs9" />
<path
id="path823"
style="fill:none;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 46,55.932203 H 66.550847"
style="fill:none;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path823" />
<path
id="path825"
style="fill:none;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 46,55.533898 v -27.805085 0"
style="fill:none;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="path825" />
</svg>

Before

Width:  |  Height:  |  Size: 805 B

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,42 +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"
id="svg3"
width="100"
height="100"
viewBox="0 0 100 100"
version="1.1"
style="fill:none">
<metadata
id="metadata9">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs7" />
<circle
cx="50"
cy="50"
r="45.408772"
id="circle4"
style="fill:none;fill-opacity:1;stroke:#180000;stroke-width:7.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path823"
d="M 46,55.932203 H 66.550847"
style="fill:none;stroke:#180000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path825"
d="m 46,55.533898 v -27.805085 0"
style="fill:none;stroke:#180000;stroke-width:7.38227367;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

34
assets/svg/ring.svg Normal file
View file

@ -0,0 +1,34 @@
<?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"
id="svg5"
style="fill:none"
version="1.1"
viewBox="0 0 100 100"
height="100"
width="100">
<metadata
id="metadata11">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs9" />
<circle
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="circle4"
r="45.0"
cy="50"
cx="50" />
</svg>

After

Width:  |  Height:  |  Size: 924 B

View file

@ -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": "<a href='tel:{phone}'><img src='./assets/svg/phone.svg'/></a>",
"condition": "phone~*"

View file

@ -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
}
],

6
package-lock.json generated
View file

@ -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",

View file

@ -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",