Add multi-apply box/feature, use it in etymology-theme to apply tags onto all segments of the same street

This commit is contained in:
pietervdvn 2021-10-12 02:12:45 +02:00
parent d0dfe9f607
commit d3550fefbe
22 changed files with 355 additions and 78 deletions

View file

@ -28,6 +28,15 @@ The latitude and longitude of the point (or centerpoint in the case of a way/are
### _layer
The layer-id to which this feature belongs. Note that this might be return any applicable if `passAllFeatures` is defined.
### _surface, _surface:ha
@ -173,7 +182,7 @@ For example to get all objects which overlap or embed from a layer, use `_contai
Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered. Returns a single geojson feature or undefined if nothing is found (or not yet laoded)
0. list of features
0. list of features or a layer name or '*' to get all features
### closestn
@ -181,7 +190,7 @@ For example to get all objects which overlap or embed from a layer, use `_contai
If a 'unique tag key' is given, the tag with this key will only appear once (e.g. if 'name' is given, all features will have a different name)
0. list of features or layer name
0. list of features or layer name or '*' to get all features
1. amount of features
2. unique tag key (optional)
3. maxDistanceInMeters (optional)

View file

@ -24,7 +24,7 @@ A geographical length in meters (rounded at two points). Will give an extra mini
## wikidata
A wikidata identifier, e.g. Q42
A wikidata identifier, e.g. Q42. Input helper arguments: [ key: the value of this tag will initialize search (default: name), options: { removePrefixes: string[], removePostfixes: string[] } these prefixes and postfixes will be removed from the initial search value]
## int

View file

@ -14,7 +14,7 @@
name | default | description
------ | --------- | -------------
image key/prefix | image | The keys given to the images, e.g. if <span class='literal-code'>image</span> is given, the first picture URL will be added as <span class='literal-code'>image</span>, the second as <span class='literal-code'>image:0</span>, the third as <span class='literal-code'>image:1</span>, etc...
image key/prefix (multiple values allowed if comma-seperated) | image | The keys given to the images, e.g. if <span class='literal-code'>image</span> is given, the first picture URL will be added as <span class='literal-code'>image</span>, the second as <span class='literal-code'>image:0</span>, the third as <span class='literal-code'>image:1</span>, etc...
#### Example usage
@ -26,10 +26,11 @@ image key/prefix | image | The keys given to the images, e.g. if <span class='li
name | default | description
------ | --------- | -------------
image-key | image | Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added)
label | Add image | The text to show on the button
#### Example usage
`{image_upload(image)}`
`{image_upload(image,Add image)}`
### wikipedia
A box showing the corresponding wikipedia article - based on the wikidata tag
@ -154,4 +155,19 @@ minzoom | 18 | How far the contributor must zoom in before being able to import
#### Example usage
`{import_button(,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,18)}` Generated from UI/SpecialVisualisations.ts
`{import_button(,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,18)}`
### multi_apply
A button to apply the tagging of this object onto a list of other features. This is an advanced feature for which you'll need calculatedTags
name | default | description
------ | --------- | -------------
feature_ids | undefined | A JSOn-serialized list of IDs of features to apply the tagging on
keys | undefined | One key (or multiple keys, seperated by ';') of the attribute that should be copied onto the other features.
text | undefined | The text to show on the button
autoapply | undefined | A boolean indicating wether this tagging should be applied automatically if the relevant tags on this object are changed. A visual element indicating the multi_apply is still shown
overwrite | undefined | If set to 'true', the tags on the other objects will always be overwritten. The default behaviour will be to only change the tags on other objects if they are either undefined or had the same value before the change
#### Example usage
{multi_apply(_features_with_the_same_name_within_100m, name:etymology:wikidata;name:etymology, Apply etymology information on all nearby objects with the same name)} Generated from UI/SpecialVisualisations.ts

View file

@ -497,6 +497,15 @@
"key": "description:0",
"description": "Layer 'Natuurgebied' shows and asks freeform values for key 'description:0' (in the MapComplete.osm.be theme 'De Natuur in')"
},
{
"key": "wikidata",
"description": "Layer 'Natuurgebied' shows and asks freeform values for key 'wikidata' (in the MapComplete.osm.be theme 'De Natuur in')"
},
{
"key": "wikidata",
"description": "Layer 'Natuurgebied' shows wikidata= with a fixed text, namely 'No Wikipedia page has been linked yet' (in the MapComplete.osm.be theme 'De Natuur in') Picking this answer will delete the key wikidata.",
"value": ""
},
{
"key": "service:bicycle:cleaning:charge",
"description": "Layer 'Natuurgebied' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'De Natuur in')"

View file

@ -105,6 +105,15 @@
"key": "wheelchair",
"description": "Layer 'Observation towers' shows wheelchair=no with a fixed text, namely 'This place is not reachable with a wheelchair' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')",
"value": "no"
},
{
"key": "wikidata",
"description": "Layer 'Observation towers' shows and asks freeform values for key 'wikidata' (in the MapComplete.osm.be theme 'Observation towers')"
},
{
"key": "wikidata",
"description": "Layer 'Observation towers' shows wikidata= with a fixed text, namely 'No Wikipedia page has been linked yet' (in the MapComplete.osm.be theme 'Observation towers') Picking this answer will delete the key wikidata.",
"value": ""
}
]
}

View file

@ -119,7 +119,7 @@ export class ExtraFunction {
{
name: "closest",
doc: "Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered. Returns a single geojson feature or undefined if nothing is found (or not yet laoded)",
args: ["list of features"]
args: ["list of features or a layer name or '*' to get all features"]
},
(params, feature) => {
return (features) => ExtraFunction.GetClosestNFeatures(params, feature, features)?.[0]?.feat
@ -132,7 +132,7 @@ export class ExtraFunction {
doc: "Given either a list of geojson features or a single layer name, gives the n closest objects which are nearest to the feature (excluding the feature itself). In the case of ways/polygons, only the centerpoint is considered. " +
"Returns a list of `{feat: geojson, distance:number}` the empty list if nothing is found (or not yet loaded)\n\n" +
"If a 'unique tag key' is given, the tag with this key will only appear once (e.g. if 'name' is given, all features will have a different name)",
args: ["list of features or layer name", "amount of features", "unique tag key (optional)", "maxDistanceInMeters (optional)"]
args: ["list of features or layer name or '*' to get all features", "amount of features", "unique tag key (optional)", "maxDistanceInMeters (optional)"]
},
(params, feature) => {

View file

@ -402,6 +402,9 @@ export default class FeaturePipeline {
}
public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] {
if(layerId === "*"){
return this.GetAllFeaturesWithin(bbox)
}
const requestedHierarchy = this.perLayerHierarchy.get(layerId)
if (requestedHierarchy === undefined) {
console.warn("Layer ", layerId, "is not defined. Try one of ", Array.from(this.perLayerHierarchy.keys()))

View file

@ -55,7 +55,6 @@ export default abstract class ImageProvider {
}
seenValues.add(value)
this.ExtractUrls(key, value).then(promises => {
console.log("Got ", promises.length, "promises for", value,"by",self.constructor.name)
for (const promise of promises ?? []) {
if (promise === undefined) {
continue

View file

@ -27,7 +27,6 @@ export class WikidataImageProvider extends ImageProvider {
if(entity === undefined){
return []
}
console.log("Entity:", entity)
const allImages : Promise<ProvidedImage>[] = []
// P18 is the claim 'depicted in this image'

View file

@ -64,12 +64,12 @@ export default class MetaTagging {
if(metatag.isLazy){
somethingChanged = true;
metatag.applyMetaTagsOnFeature(feature, freshness)
metatag.applyMetaTagsOnFeature(feature, freshness, layer)
}else{
const newValueAdded = metatag.applyMetaTagsOnFeature(feature, freshness)
const newValueAdded = metatag.applyMetaTagsOnFeature(feature, freshness, layer)
/* Note that the expression:
* `somethingChanged = newValueAdded || metatag.applyMetaTagsOnFeature(feature, freshness)`
* Is WRONG

View file

@ -6,6 +6,7 @@ import Combine from "../UI/Base/Combine";
import BaseUIElement from "../UI/BaseUIElement";
import Title from "../UI/Base/Title";
import {FixedUiElement} from "../UI/Base/FixedUiElement";
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
const cardinalDirections = {
@ -62,6 +63,20 @@ export default class SimpleMetaTagger {
return true;
})
);
private static layerInfo = new SimpleMetaTagger(
{
doc: "The layer-id to which this feature belongs. Note that this might be return any applicable if `passAllFeatures` is defined.",
keys:["_layer"],
includesDates: false,
},
(feature, freshness, layer) => {
if(feature.properties._layer === layer.id){
return false;
}
feature.properties._layer = layer.id
return true;
}
)
private static surfaceArea = new SimpleMetaTagger(
{
keys: ["_surface", "_surface:ha"],
@ -329,6 +344,7 @@ export default class SimpleMetaTagger {
)
public static metatags = [
SimpleMetaTagger.latlon,
SimpleMetaTagger.layerInfo,
SimpleMetaTagger.surfaceArea,
SimpleMetaTagger.lngth,
SimpleMetaTagger.canonicalize,
@ -346,7 +362,7 @@ export default class SimpleMetaTagger {
public readonly doc: string;
public readonly isLazy: boolean;
public readonly includesDates: boolean
public readonly applyMetaTagsOnFeature: (feature: any, freshness: Date) => boolean;
public readonly applyMetaTagsOnFeature: (feature: any, freshness: Date, layer: LayerConfig) => boolean;
/***
* A function that adds some extra data to a feature
@ -354,7 +370,7 @@ export default class SimpleMetaTagger {
* @param f: apply the changes. Returns true if something changed
*/
constructor(docs: { keys: string[], doc: string, includesDates?: boolean, isLazy?: boolean },
f: ((feature: any, freshness: Date) => boolean)) {
f: ((feature: any, freshness: Date, layer: LayerConfig) => boolean)) {
this.keys = docs.keys;
this.doc = docs.doc;
this.isLazy = docs.isLazy

158
UI/Popup/MultiApply.ts Normal file
View file

@ -0,0 +1,158 @@
import {UIEventSource} from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement";
import Combine from "../Base/Combine";
import {SubtleButton} from "../Base/SubtleButton";
import {Changes} from "../../Logic/Osm/Changes";
import {FixedUiElement} from "../Base/FixedUiElement";
import Translations from "../i18n/Translations";
import {VariableUiElement} from "../Base/VariableUIElement";
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction";
import {Tag} from "../../Logic/Tags/Tag";
import {ElementStorage} from "../../Logic/ElementStorage";
import {And} from "../../Logic/Tags/And";
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
import Toggle from "../Input/Toggle";
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
export interface MultiApplyParams {
featureIds: UIEventSource<string[]>,
keysToApply: string[],
text: string,
autoapply: boolean,
overwrite: boolean,
tagsSource: UIEventSource<any>,
state: {
changes: Changes,
allElements: ElementStorage,
layoutToUse: LayoutConfig,
osmConnection: OsmConnection
}
}
class MultiApplyExecutor {
private readonly originalValues = new Map<string, string>()
private readonly params: MultiApplyParams;
private constructor(params: MultiApplyParams) {
this.params = params;
const p = params
for (const key of p.keysToApply) {
this.originalValues.set(key, p.tagsSource.data[key])
}
if (p.autoapply) {
const self = this;
const relevantValues = p.tagsSource.map(tags => {
const currentValues = p.keysToApply.map(key => tags[key])
const v = JSON.stringify(currentValues) // By stringifying, we have a very clear ping when they changec
console.log("Values are", v)
return v;
})
relevantValues.addCallbackD(_ => {
self.applyTaggingOnOtherFeatures()
})
}
}
public applyTaggingOnOtherFeatures() {
console.log("Multi-applying changes...")
const featuresToChange = this.params.featureIds.data
const changes = this.params.state.changes
const allElements = this.params.state.allElements
const keysToChange = this.params.keysToApply
const overwrite = this.params.overwrite
const selfTags = this.params.tagsSource.data;
const theme = this.params.state.layoutToUse.id
for (const id of featuresToChange) {
const tagsToApply: Tag[] = []
const otherFeatureTags = allElements.getEventSourceById(id).data
for (const key of keysToChange) {
const newValue = selfTags[key]
if (newValue === undefined) {
continue
}
const otherValue = otherFeatureTags[key]
if (newValue === otherValue) {
continue;// No changes to be made
}
if (overwrite) {
tagsToApply.push(new Tag(key, newValue))
continue;
}
if (otherValue === undefined || otherValue === "" || otherValue === this.originalValues.get(key)) {
tagsToApply.push(new Tag(key, newValue))
}
}
if (tagsToApply.length == 0) {
continue;
}
changes.applyAction(
new ChangeTagAction(id, new And(tagsToApply), otherFeatureTags, {
theme,
changeType: "answer"
}))
}
}
private static executorCache = new Map<string, MultiApplyExecutor>()
public static GetApplicator(id: string, params: MultiApplyParams): MultiApplyExecutor {
if (MultiApplyExecutor.executorCache.has(id)) {
return MultiApplyExecutor.executorCache.get(id)
}
const applicator = new MultiApplyExecutor(params)
MultiApplyExecutor.executorCache.set(id, applicator)
return applicator
}
}
export default class MultiApply extends Toggle {
constructor(params: MultiApplyParams) {
const p = params
const t = Translations.t.multi_apply
const featureId = p.tagsSource.data.id
if (featureId === undefined) {
throw "MultiApply needs a feature id"
}
const applicator = MultiApplyExecutor.GetApplicator(featureId, params)
const elems: (string | BaseUIElement)[] = []
if (p.autoapply) {
elems.push(new Combine([new FixedUiElement(p.text).SetClass("block") ]).SetClass("flex"))
elems.push(new VariableUiElement(p.featureIds.map(featureIds =>
t.autoApply.Subs({
attr_names: p.keysToApply.join(", "),
count: "" + featureIds.length
}))).SetClass("block subtle text-sm"))
} else {
elems.push(
new SubtleButton(undefined, p.text).onClick(() => applicator.applyTaggingOnOtherFeatures())
)
}
const isShown: UIEventSource<boolean> = p.state.osmConnection.isLoggedIn.map(loggedIn => {
return loggedIn && p.featureIds.data.length > 0
}, [p.featureIds])
super(new Combine(elems), undefined, isShown);
}
}

View file

@ -28,6 +28,7 @@ import Minimap from "./Base/Minimap";
import AllImageProviders from "../Logic/ImageProviders/AllImageProviders";
import WikipediaBox from "./Wikipedia/WikipediaBox";
import SimpleMetaTagger from "../Logic/SimpleMetaTagger";
import MultiApply from "./Popup/MultiApply";
export interface SpecialVisualization {
funcName: string,
@ -484,8 +485,38 @@ There are also some technicalities in your theme to keep in mind:
args[2], args[1], tagSource, rewrittenTags, lat, lon, Number(args[3]), state
)
}
},
{funcName: "multi_apply",
docs: "A button to apply the tagging of this object onto a list of other features. This is an advanced feature for which you'll need calculatedTags",
args:[
{name: "feature_ids", doc: "A JSOn-serialized list of IDs of features to apply the tagging on"},
{name: "keys", doc: "One key (or multiple keys, seperated by ';') of the attribute that should be copied onto the other features." },
{name: "text", doc: "The text to show on the button"},
{name:"autoapply",doc:"A boolean indicating wether this tagging should be applied automatically if the relevant tags on this object are changed. A visual element indicating the multi_apply is still shown"},
{name:"overwrite",doc:"If set to 'true', the tags on the other objects will always be overwritten. The default behaviour will be to only change the tags on other objects if they are either undefined or had the same value before the change"}
],
example: "{multi_apply(_features_with_the_same_name_within_100m, name:etymology:wikidata;name:etymology, Apply etymology information on all nearby objects with the same name)}",
constr: (state, tagsSource, args) => {
const featureIdsKey = args[0]
const keysToApply = args[1].split(";")
const text = args[2]
const autoapply = args[3]?.toLowerCase() === "true"
const overwrite = args[4]?.toLowerCase() === "true"
const featureIds : UIEventSource<string[]> = tagsSource.map(tags => JSON.parse(tags[featureIdsKey]))
return new MultiApply(
{
featureIds,
keysToApply,
text,
autoapply,
overwrite,
tagsSource,
state
}
);
}
}
]
static HelpMessage: BaseUIElement = SpecialVisualizations.GenHelpMessage();

View file

@ -53,7 +53,6 @@ export default class WikidataPreviewBox extends VariableUiElement {
]).SetClass("flex"),
Wikidata.IdToArticle(wikidata.id) ,true).SetClass("must-link")
console.log(wikidata)
let info = new Combine( [
new Combine([Translation.fromMap(wikidata.labels).SetClass("font-bold"),
link]).SetClass("flex justify-between"),

View file

@ -217,7 +217,7 @@ export class Translation extends BaseUIElement {
static fromMap(transl: Map<string, string>) {
const translations = {}
transl.forEach((value, key) => {
transl?.forEach((value, key) => {
translations[key] = value
})
return new Translation(translations);

View file

@ -23,6 +23,10 @@
"en": "All objects which have an etymology known",
"nl": "Alle lagen met een gelinkt etymology"
},
"calculatedTags": [
"_same_name_ids=feat.closestn('*', 250, undefined, 2500)?.filter(f => f.feat.properties.name === feat.properties.name)?.map(f => f.feat.properties.id)??[]",
"_total_segments=JSON.parse(feat.properties._same_name_ids).length + 1 // Plus one for the feature itself"
],
"tagRenderings": [
{
"id": "etymology-images-from-wikipedia",
@ -115,6 +119,18 @@
"nl": "{image_carousel(image:streetsign)}<br/>{image_upload(image:streetsign, Voeg afbeelding van straatnaambordje toe)}"
}
},
{
"id": "minimap",
"render": {
"*": "{minimap(18, id, _same_name_ids):height:10rem}"
}
},
{
"id": "etymology_multi_apply",
"render": {
"en": "{multi_apply(_same_name_ids, name:etymology:wikidata;name:etymology, Auto-applying data on all segments with the same name, true)}"
}
},
"wikipedia"
],
"icon": {

View file

@ -198,28 +198,32 @@
"if": "dog=yes",
"then": {
"en": "Dogs are allowed",
"nl": "honden zijn toegelaten"
"nl": "honden zijn toegelaten",
"pt": "Os cães são permitidos"
}
},
{
"if": "dog=no",
"then": {
"en": "Dogs are <b>not</b> allowed",
"nl": "honden zijn <b>niet</b> toegelaten"
"nl": "honden zijn <b>niet</b> toegelaten",
"pt": "Os cães <b>não</b> são permitidos"
}
},
{
"if": "dog=leashed",
"then": {
"en": "Dogs are allowed, but they have to be leashed",
"nl": "honden zijn <b>enkel aan de leiband</b> welkom"
"nl": "honden zijn <b>enkel aan de leiband</b> welkom",
"pt": "Os cães são permitidos, mas têm de ser presos pela trela"
}
},
{
"if": "dog=unleashed",
"then": {
"en": "Dogs are allowed and can run around freely",
"nl": "honden zijn welkom en mogen vrij rondlopen"
"nl": "honden zijn welkom en mogen vrij rondlopen",
"pt": "Os cães são permitidos e podem correr livremente"
}
}
]
@ -294,7 +298,8 @@
"en": "Cash is accepted here",
"nl": "Cash geld wordt hier aanvaard",
"pt": "Aceitam pagamento com dinheiro aqui",
"pt_BR": "Dinheiro é aceito aqui"
"pt_BR": "Dinheiro é aceito aqui",
"id": "Disini menerima pembayaran tunai"
}
},
{
@ -304,7 +309,8 @@
"en": "Payment cards are accepted here",
"nl": "Betalen met bankkaarten kan hier",
"pt": "Aceitam pagamento com cartões bancários aqui",
"pt_BR": "Cartões de pagamento são aceitos aqui"
"pt_BR": "Cartões de pagamento são aceitos aqui",
"id": "Disini menerima pembayaran dengan kartu"
}
}
]
@ -406,7 +412,8 @@
"fr": "Premier étage",
"pl": "Znajduje się na pierwszym piętrze",
"sv": "Ligger på första våningen",
"pt": "Está no primeiro andar"
"pt": "Está no primeiro andar",
"id": "Berlokasi di lantai pertama"
}
}
]

View file

@ -14,7 +14,7 @@
"nl": "Een kaart om toeristisch relevante info op aan te duiden"
},
"description": {
"nl": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:<br/><ul><li>Eetgelegenheden</li><li>Cafés en bars</li><li>(Fiets)oplaadpunten</li><li>Fietspompen, fietserverhuur en fietswinkels</li><li>Uitkijktorens</li><li>...</li></ul> Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken."
"nl": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:<br/><ul><li>Eetgelegenheden</li><li>Cafés en bars</li><li>(Fiets)oplaadpunten</li><li>Fietspompen, fietserverhuur en fietswinkels</li><li>Uitkijktorens</li><li>...</li></ul> Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken.<br/><br/>Met de steun van Toerisme Vlaanderen<img src='./assets/themes/toerisme_vlaanderen/logo.png' />"
},
"descriptionTail": {
"nl": "Met de steun van Toerisme Vlaanderen<img style='height:5rem; width: auto;' src='./assets/themes/toerisme_vlaanderen/logo.png' />"

View file

@ -36,6 +36,9 @@
"splitTitle": "Choose on the map where to split this road",
"hasBeenSplit": "This way has been split"
},
"multi_apply": {
"autoApply": "When changing the attributes {attr_names}, these attributes will automatically be changed on {count} other objects too"
},
"delete": {
"delete": "Delete",
"cancel": "Cancel",

View file

@ -2504,6 +2504,9 @@
"description": "All objects which have an etymology known",
"name": "Has etymolgy",
"tagRenderings": {
"etymology_multi_apply": {
"render": "{multi_apply(_same_name_ids, name:etymology:wikidata;name:etymology, Auto-applying data on all segments with the same name, true)}"
},
"simple etymology": {
"mappings": {
"0": {

View file

@ -1236,6 +1236,57 @@
"shortDescription": "A map with playgrounds",
"title": "Playgrounds"
},
"postboxes": {
"description": "On this map you can find and add data of post offices and post boxes. You can use this map to find where you can mail your next postcard! :)<br/>Spotted an error or is a post box missing? You can edit this map with a free OpenStreetMap account. ",
"layers": {
"0": {
"description": "The layer showing postboxes.",
"name": "Postboxes",
"presets": {
"0": {
"title": "postbox"
}
},
"title": {
"render": "Postbox"
}
},
"1": {
"description": "A layer showing post offices.",
"filter": {
"0": {
"options": {
"0": {
"question": "Currently open"
}
}
}
},
"name": "Post offices",
"presets": {
"0": {
"title": "Post Office"
}
},
"tagRenderings": {
"OH": {
"mappings": {
"0": {
"then": "24/7 opened (including holidays)"
}
},
"question": "What are the opening hours for this post office?",
"render": "Opening Hours: {opening_hours_table()}"
}
},
"title": {
"render": "Post Office"
}
}
},
"shortDescription": "A map showing postboxes and post offices",
"title": "Postbox and Post Office Map"
},
"shops": {
"description": "On this map, one can mark basic information about shops, add opening hours and phone numbers",
"layers": {
@ -1367,56 +1418,5 @@
"description": "On this map, you'll find waste baskets near you. If a waste basket is missing on this map, you can add it yourself",
"shortDescription": "A map with waste baskets",
"title": "Waste Basket"
},
"postboxes": {
"description": "On this map you can find and add data of post offices and post boxes. You can use this map to find where you can mail your next postcard! :)<br/>Spotted an error or is a post box missing? You can edit this map with a free OpenStreetMap account. ",
"layers": {
"0": {
"description": "The layer showing postboxes.",
"name": "Postboxes",
"presets": {
"0": {
"title": "postbox"
}
},
"title": {
"render": "Postbox"
}
},
"1": {
"description": "A layer showing post offices.",
"filter": {
"0": {
"options": {
"0": {
"question": "Currently open"
}
}
}
},
"name": "Post offices",
"presets": {
"0": {
"title": "Post Office"
}
},
"tagRenderings": {
"OH": {
"mappings": {
"0": {
"then": "24/7 opened (including holidays)"
}
},
"question": "What are the opening hours for this post office?",
"render": "Opening Hours: {opening_hours_table()}"
}
},
"title": {
"render": "Post Office"
}
}
},
"shortDescription": "A map showing postboxes and post offices",
"title": "Postbox and Post Office Map"
}
}

View file

@ -1021,9 +1021,9 @@
},
"toerisme_vlaanderen": {
"description": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:<br/><ul><li>Eetgelegenheden</li><li>Cafés en bars</li><li>(Fiets)oplaadpunten</li><li>Fietspompen, fietserverhuur en fietswinkels</li><li>Uitkijktorens</li><li>...</li></ul> Zie je fouten op de kaart? Dan kan je zelf makkelijk aanpasingen maken, die zichtbaar zijn voor iedereen. Hiervoor dien je een gratis OpenStreetMap account voor te maken.<br/><br/>Met de steun van Toerisme Vlaanderen<img src='./assets/themes/toerisme_vlaanderen/logo.png' />",
"descriptionTail": "Met de steun van Toerisme Vlaanderen<img style='height:5rem; width: auto;' src='./assets/themes/toerisme_vlaanderen/logo.png' />",
"shortDescription": "Een kaart om toeristisch relevante info op aan te duiden",
"title": "Toeristisch relevante info",
"descriptionTail": "Met de steun van Toerisme Vlaanderen<img style='height:5rem; width: auto;' src='./assets/themes/toerisme_vlaanderen/logo.png' />"
"title": "Toeristisch relevante info"
},
"toilets": {
"description": "Een kaart met openbare toiletten",