Merge develop

This commit is contained in:
pietervdvn 2022-09-29 16:02:21 +02:00
commit f7370d0ae9
25 changed files with 452 additions and 64 deletions

View file

@ -1 +1,2 @@
["file-overview.json","missing_editor.json","stats.2020-10.json","stats.2020-11.json","stats.2020-12.json","stats.2020-5.json","stats.2020-6.json","stats.2020-7.json","stats.2020-8.json","stats.2020-9.json","stats.2021-1.json","stats.2021-10.json","stats.2021-11.json","stats.2021-12.json","stats.2021-2.json","stats.2021-3.json","stats.2021-4.json","stats.2021-5.json","stats.2021-6.json","stats.2021-7.json","stats.2021-8.json","stats.2021-9.json","stats.2022-1.json","stats.2022-2.json","stats.2022-3.json","stats.2022-4.json","stats.2022-5.json","stats.2022-6.json","stats.2022-7.json","stats.2022-8.json","stats.2022-9-01.day.json","stats.2022-9-02.day.json","stats.2022-9-03.day.json","stats.2022-9-04.day.json","stats.2022-9-05.day.json","stats.2022-9-06.day.json","stats.2022-9-07.day.json","stats.2022-9-08.day.json","stats.2022-9-09.day.json","stats.2022-9-10.day.json","stats.2022-9-11.day.json","stats.2022-9-12.day.json","stats.2022-9-13.day.json","stats.2022-9-14.day.json","stats.2022-9-15.day.json","stats.2022-9-16.day.json","stats.2022-9-17.day.json","stats.2022-9-18.day.json","stats.2022-9-19.day.json","stats.2022-9-20.day.json","stats.2022-9-21.day.json","stats.2022-9-22.day.json","stats.2022-9-23.day.json"] ["file-overview.json","missing_editor.json","stats.2020-10.json","stats.2020-11.json","stats.2020-12.json","stats.2020-5.json","stats.2020-6.json","stats.2020-7.json","stats.2020-8.json","stats.2020-9.json","stats.2021-1.json","stats.2021-10.json","stats.2021-11.json","stats.2021-12.json","stats.2021-2.json","stats.2021-3.json","stats.2021-4.json","stats.2021-5.json","stats.2021-6.json","stats.2021-7.json","stats.2021-8.json","stats.2021-9.json","stats.2022-1.json","stats.2022-2.json","stats.2022-3.json","stats.2022-4.json","stats.2022-5.json","stats.2022-6.json","stats.2022-7.json","stats.2022-8.json","stats.2022-9-01.day.json","stats.2022-9-02.day.json","stats.2022-9-03.day.json","stats.2022-9-04.day.json","stats.2022-9-05.day.json","stats.2022-9-06.day.json","stats.2022-9-07.day.json","stats.2022-9-08.day.json","stats.2022-9-09.day.json","stats.2022-9-10.day.json","stats.2022-9-11.day.json","stats.2022-9-12.day.json","stats.2022-9-13.day.json","stats.2022-9-14.day.json","stats.2022-9-15.day.json","stats.2022-9-16.day.json","stats.2022-9-17.day.json","stats.2022-9-18.day.json","stats.2022-9-19.day.json","stats.2022-9-20.day.json","stats.2022-9-21.day.json","stats.2022-9-22.day.json","stats.2022-9-23.day.json","stats.2022-9-24.day.json","stats.2022-9-25.day.json"]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -137,10 +137,13 @@ export default class AvailableBaseLayersImplementation implements AvailableBaseL
l("Stamen.TonerBackground", "Toner Background - no labels (by Stamen)"), l("Stamen.TonerBackground", "Toner Background - no labels (by Stamen)"),
l("Stamen.Watercolor", "Watercolor (by Stamen)"), l("Stamen.Watercolor", "Watercolor (by Stamen)"),
l("Stadia.OSMBright", "Osm Bright (by Stadia)"), l("Stadia.OSMBright", "Osm Bright (by Stadia)"),
l("Stadia.AlidadeSmoothDark", "Alidade Smooth Dark (by Stadia)"),
l("CartoDB.Positron", "Positron (by CartoDB)"), l("CartoDB.Positron", "Positron (by CartoDB)"),
l("CartoDB.PositronNoLabels", "Positron - no labels (by CartoDB)"), l("CartoDB.PositronNoLabels", "Positron - no labels (by CartoDB)"),
l("CartoDB.Voyager", "Voyager (by CartoDB)"), l("CartoDB.Voyager", "Voyager (by CartoDB)"),
l("CartoDB.VoyagerNoLabels", "Voyager - no labels (by CartoDB)"), l("CartoDB.VoyagerNoLabels", "Voyager - no labels (by CartoDB)"),
l("CartoDB.DarkMatter", "Dark Matter (by CartoDB)"),
l("CartoDB.DarkMatterNoLabels", "Dark Matter - no labels (by CartoDB)"),
] ]
return Utils.NoNull(layers) return Utils.NoNull(layers)
} }

View file

@ -13,7 +13,7 @@ import {
Polygon, Polygon,
Properties, Properties,
} from "@turf/turf" } from "@turf/turf"
import {GeoJSON, LineString} from "geojson"; import {GeoJSON, LineString, Point} from "geojson";
export class GeoOperations { export class GeoOperations {
private static readonly _earthRadius = 6378137 private static readonly _earthRadius = 6378137
@ -27,8 +27,8 @@ export class GeoOperations {
* Converts a GeoJson feature to a point GeoJson feature * Converts a GeoJson feature to a point GeoJson feature
* @param feature * @param feature
*/ */
static centerpoint(feature: any) { static centerpoint(feature: any): Feature<Point> {
const newFeature = turf.center(feature) const newFeature : Feature<Point> = turf.center(feature)
newFeature.properties = feature.properties newFeature.properties = feature.properties
newFeature.id = feature.id newFeature.id = feature.id
return newFeature return newFeature

View file

@ -9,6 +9,7 @@ import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import { CountryCoder } from "latlon2country" import { CountryCoder } from "latlon2country"
import Constants from "../Models/Constants" import Constants from "../Models/Constants"
import { TagUtils } from "./Tags/TagUtils" import { TagUtils } from "./Tags/TagUtils"
import {Feature, LineString} from "geojson";
export class SimpleMetaTagger { export class SimpleMetaTagger {
public readonly keys: string[] public readonly keys: string[]
@ -420,6 +421,38 @@ export default class SimpleMetaTaggers {
return true return true
} }
) )
private static directionCenterpoint = new SimpleMetaTagger(
{
keys:["_direction:centerpoint"],
isLazy: true,
doc: "_direction:centerpoint is the direction of the linestring (in degrees) if one were standing at the projected centerpoint."
},
(feature: Feature) => {
if(feature.geometry.type !== "LineString"){
return false
}
const ls = <Feature<LineString>> feature;
Object.defineProperty(feature.properties, "_direction:centerpoint", {
enumerable: false,
configurable: true,
get: () => {
const centroid = GeoOperations.centerpoint(feature)
const projected = GeoOperations.nearestPoint(ls, <[number,number]> centroid.geometry.coordinates)
const nextPoint = ls.geometry.coordinates[projected.properties.index + 1]
const bearing = GeoOperations.bearing(projected.geometry.coordinates, nextPoint)
delete feature.properties["_direction:centerpoint"]
feature.properties["_direction:centerpoint"] = bearing
return bearing
},
})
return true
}
)
private static currentTime = new SimpleMetaTagger( private static currentTime = new SimpleMetaTagger(
{ {
keys: ["_now:date", "_now:datetime", "_loaded:date", "_loaded:_datetime"], keys: ["_now:date", "_now:datetime", "_loaded:date", "_loaded:_datetime"],
@ -457,6 +490,7 @@ export default class SimpleMetaTaggers {
SimpleMetaTaggers.country, SimpleMetaTaggers.country,
SimpleMetaTaggers.isOpen, SimpleMetaTaggers.isOpen,
SimpleMetaTaggers.directionSimplified, SimpleMetaTaggers.directionSimplified,
SimpleMetaTaggers.directionCenterpoint,
SimpleMetaTaggers.currentTime, SimpleMetaTaggers.currentTime,
SimpleMetaTaggers.objectMetaInfo, SimpleMetaTaggers.objectMetaInfo,
SimpleMetaTaggers.noBothButLeftRight, SimpleMetaTaggers.noBothButLeftRight,

View file

@ -371,7 +371,7 @@ export default class LayerConfig extends WithContextLoader {
throw "Error in " + context + ": use 'filter' instead of 'filters'" throw "Error in " + context + ": use 'filter' instead of 'filters'"
} }
this.titleIcons = this.ParseTagRenderings(<TagRenderingConfigJson[]>json.titleIcons, { this.titleIcons = this.ParseTagRenderings(<TagRenderingConfigJson[]>json.titleIcons ?? [], {
readOnlyMode: true, readOnlyMode: true,
}) })

View file

@ -148,7 +148,7 @@ export default abstract class BaseUIElement {
} catch (e) { } catch (e) {
const domExc = e as DOMException const domExc = e as DOMException
if (domExc) { if (domExc) {
console.log("An exception occured", domExc.code, domExc.message, domExc.name) console.error("An exception occured", domExc.code, domExc.message, domExc.name, domExc)
} }
console.error(e) console.error(e)
} }

View file

@ -8,7 +8,7 @@ import Translations from "../i18n/Translations";
export class CheckBox extends InputElementMap<number[], boolean> { export class CheckBox extends InputElementMap<number[], boolean> {
constructor(el: (BaseUIElement | string), defaultValue?: boolean) { constructor(el: (BaseUIElement | string), defaultValue?: boolean) {
super( super(
new CheckBoxes([Translations.T(el)]), new CheckBoxes([Translations.W(el)]),
(x0, x1) => x0 === x1, (x0, x1) => x0 === x1,
(t) => t.length > 0, (t) => t.length > 0,
(x) => (x ? [0] : []) (x) => (x ? [0] : [])

View file

@ -10,16 +10,15 @@ import Toggle from "./Input/Toggle"
export default class LanguagePicker extends Toggle { export default class LanguagePicker extends Toggle {
constructor(languages: string[], label: string | BaseUIElement = "") { constructor(languages: string[], label: string | BaseUIElement = "") {
console.log("Constructing a language pîcker for languages", languages)
if (languages === undefined || languages.length <= 1) { if (languages === undefined || languages.length <= 1) {
super(undefined, undefined, undefined) super(undefined, undefined, undefined)
return undefined }else {
}
const allLanguages: string[] = used_languages.languages
const normalPicker = LanguagePicker.dropdownFor(languages, label) const normalPicker = LanguagePicker.dropdownFor(languages, label)
const fullPicker = new Lazy(() => LanguagePicker.dropdownFor(allLanguages, label)) const fullPicker = new Lazy(() => LanguagePicker.dropdownFor(allLanguages, label))
super(fullPicker, normalPicker, Locale.showLinkToWeblate) super(fullPicker, normalPicker, Locale.showLinkToWeblate)
const allLanguages: string[] = used_languages.languages
}
} }
private static dropdownFor(languages: string[], label: string | BaseUIElement): BaseUIElement { private static dropdownFor(languages: string[], label: string | BaseUIElement): BaseUIElement {

View file

@ -248,9 +248,8 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
) )
editElements.push( editElements.push(
new VariableUiElement( Toggle.If(state.featureSwitchIsDebugging,
state.featureSwitchIsDebugging.map((isDebugging) => { () => {
if (isDebugging) {
const config_all_tags: TagRenderingConfig = new TagRenderingConfig( const config_all_tags: TagRenderingConfig = new TagRenderingConfig(
{ render: "{all_tags()}" }, { render: "{all_tags()}" },
"" ""
@ -271,7 +270,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
"This is layer " + layerConfig.id, "This is layer " + layerConfig.id,
]) ])
} }
})
) )
) )

View file

@ -43,6 +43,38 @@ export default class TagApplyButton implements AutoAction {
public readonly example = public readonly example =
"`{tag_apply(survey_date=$_now:date, Surveyed today!)}`, `{tag_apply(addr:street=$addr:street, Apply the address, apply_icon.svg, _closest_osm_id)" "`{tag_apply(survey_date=$_now:date, Surveyed today!)}`, `{tag_apply(addr:street=$addr:street, Apply the address, apply_icon.svg, _closest_osm_id)"
/**
* Parses a tag specification
*
* TagApplyButton.parseTagSpec("key=value;key0=value0") // => [["key","value"],["key0","value0"]]
*
* // Should handle escaped ";"
* TagApplyButton.parseTagSpec("key=value;key0=value0\\;value1") // => [["key","value"],["key0","value0;value1"]]
*/
private static parseTagSpec(spec: string): [string, string][]{
const tgsSpec : [string, string][] = []
while(spec.length > 0){
const [part] = spec.match(/((\\;)|[^;])*/)
spec = spec.substring(part.length + 1) // +1 to remove the pending ';' as well
const kv = part.split("=").map((s) => s.trim().replace("\\;",";"))
if (kv.length == 2) {
tgsSpec.push(<[string, string]> kv)
}else if (kv.length < 2) {
throw "Invalid key spec: no '=' found in " + spec
}else{
throw "Invalid key spec: multiple '=' found in " + spec
}
}
for (const spec of tgsSpec) {
if (spec[0].endsWith(":")) {
throw "The key for a tag specification for import or apply ends with ':'. The theme author probably wrote key:=otherkey instead of key=$otherkey"
}
}
return tgsSpec
}
public static generateTagsToApply(spec: string, tagSource: Store<any>): Store<Tag[]> { public static generateTagsToApply(spec: string, tagSource: Store<any>): Store<Tag[]> {
// Check whether we need to look up a single value // Check whether we need to look up a single value
@ -51,19 +83,7 @@ export default class TagApplyButton implements AutoAction {
spec = tagSource.data[spec.replace("$", "")] spec = tagSource.data[spec.replace("$", "")]
} }
const tgsSpec = spec.split(";").map((spec) => { const tgsSpec = TagApplyButton.parseTagSpec(spec)
const kv = spec.split("=").map((s) => s.trim())
if (kv.length != 2) {
throw "Invalid key spec: multiple '=' found in " + spec
}
return kv
})
for (const spec of tgsSpec) {
if (spec[0].endsWith(":")) {
throw "A tag specification for import or apply ends with ':'. The theme author probably wrote key:=otherkey instead of key=$otherkey"
}
}
return tagSource.map((tags) => { return tagSource.map((tags) => {
const newTags: Tag[] = [] const newTags: Tag[] = []

View file

@ -41,7 +41,7 @@ export default class Translations {
* translation.textFor("nl") // => "Nederlands" * translation.textFor("nl") // => "Nederlands"
* *
*/ */
static T(t: string | any, context = undefined): TypedTranslation<object> { static T(t: string | undefined | null | Translation | TypedTranslation<object>, context = undefined): TypedTranslation<object> {
if (t === undefined || t === null) { if (t === undefined || t === null) {
return undefined return undefined
} }
@ -51,7 +51,7 @@ export default class Translations {
if (typeof t === "string") { if (typeof t === "string") {
return new TypedTranslation<object>({ "*": t }, context) return new TypedTranslation<object>({ "*": t }, context)
} }
if (t.render !== undefined) { if (t["render"] !== undefined) {
const msg = const msg =
"Creating a translation, but this object contains a 'render'-field. Use the translation directly" "Creating a translation, but this object contains a 'render'-field. Use the translation directly"
console.error(msg, t) console.error(msg, t)

View file

@ -1,7 +1,7 @@
{ {
"id": "filters", "id": "filters",
"description": "This layer acts as library for common filters", "description": "This layer acts as library for common filters",
"mapRendering": [], "mapRendering": null,
"source": { "source": {
"osmTags": "id~*" "osmTags": "id~*"
}, },

View file

@ -1,7 +1,7 @@
[ [
{ {
"path": "hotel.svg", "path": "hotel.svg",
"license": "", "license": "CC0",
"authors": [ "authors": [
"Andy Allan", "Andy Allan",
"Michael Glanznig", "Michael Glanznig",

View file

@ -58,6 +58,7 @@
} }
], ],
"tagRenderings": [ "tagRenderings": [
"images",
{ {
"id": "kerb-type", "id": "kerb-type",
"question": { "question": {

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="500"
height="500"
version="1.1"
id="svg20"
sodipodi:docname="icon.svg"
inkscape:version="1.2.1 (0f2f062aeb, 2022-09-21, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs24" />
<sodipodi:namedview
id="namedview22"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="false"
showguides="true"
inkscape:zoom="0.6971062"
inkscape:cx="755.98238"
inkscape:cy="65.269826"
inkscape:window-width="1920"
inkscape:window-height="995"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg20">
<sodipodi:guide
position="4.4436256,249.99444"
orientation="0,-1"
id="guide132"
inkscape:locked="false" />
</sodipodi:namedview>
<path
d="M 34.421203,251.09539 H 466.71474 m -356.64217,-86.4587 -86.458705,86.4587 86.458705,86.45871 m 280.9908,-172.91741 86.45871,86.4587 -86.45871,86.45871"
stroke="#000000"
stroke-width="43.2294"
stroke-linejoin="round"
stroke-linecap="round"
fill="none"
id="path18" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,8 @@
[
{
"path": "icon.svg",
"license": "CC0; trivial",
"authors": [],
"sources": []
}
]

View file

@ -0,0 +1,271 @@
{
"id": "width",
"description": {
"nl": "<h3>De straat is opgebruikt</h3> <p>Er is steeds meer druk op de openbare ruimte. Voetgangers, fietsers, steps, auto's, bussen, bestelwagens, buggies, cargobikes, ... willen allemaal hun deel van de openbare ruimte en de straat.</p> <p>In deze studie nemen we Brugge onder de loep en kijken we hoe breed elke straat is én hoe breed elke straat zou moeten zijn voor een veilig én vlot verkeer.</p> <h3>Legende</h3> <span style='background: red'>&NonBreakingSpace;&NonBreakingSpace;&NonBreakingSpace;</span> Straat te smal voor veilig verkeer<br/> <span style='background: #0f0'>&NonBreakingSpace;&NonBreakingSpace;&NonBreakingSpace;</span> Straat is breed genoeg veilig verkeer<br/> <span style='background: orange'>&NonBreakingSpace;&NonBreakingSpace;&NonBreakingSpace;</span> Straat zonder voetpad, te smal als ook voetgangers plaats krijgen<br/> <span style='background: lightgrey'>&NonBreakingSpace;&NonBreakingSpace;&NonBreakingSpace;</span> Autoluw, autoloos of enkel plaatselijk verkeer<br/> <br/> <br/> Een gestippelde lijn is een straat waar ook voor fietsers éénrichtingsverkeer geldt.<br/> Klik op een straat om meer informatie te zien."
},
"title": {
"nl": "Straatbreedtes"
},
"mustHaveLanguage": [
"nl"
],
"hideFromOverview": true,
"enableUserBadge": false,
"enableShareScreen": false,
"enableLayers": false,
"enableMoreQuests": false,
"enableSearch": false,
"enableBackgroundLayerSelection": false,
"icon": "./assets/themes/width/icon.svg",
"startLat": 51.20875,
"startLon": 3.22435,
"startZoom": 14,
"widenFactor": 0.05,
"clustering": false,
"lockLocation": [
[
3.2006263732910156,
51.22699040520305
],
[
3.2529830932617188,
51.190748429411705
]
],
"defaultBackgroundId": "Stadia.AlidadeSmoothDark",
"layers": [
{
"id": "street_with_width",
"description": "A layer showing street with corresponding widths + an analysis of what this width is used for",
"name": {
"nl": "Straten met een breedte"
},
"calculatedTags": [
"_car_width:=2 /* The width that a single car needs */",
"_cyclistWidth:=1.5 /* The width a single cyclist needs to be safely overtaken */",
"_pedestrianWidth:=0.75 /* The width a pedestrian needs if sidewalks are missing */",
"_has_left_parking=(feat.properties['parking:lane:left'] ?? feat.properties['parking:lane:both']) === 'parallel'",
"_has_right_parking=(feat.properties['parking:lane:right'] ?? feat.properties['parking:lane:both']) === 'parallel'",
"_has_other_parking= ['parking:lane:left','parking:lane:right','parking:lane:both'].some(key => ['perpendicular','diagonal'].indexOf(feat.properties[key]) >= 0)",
"_parallel_parking_count=feat.get('_has_right_parking') + feat.get('_has_left_parking') /* in javascript logic: true + true == 2*/",
"_width:needed:parking=feat.get('_parallel_parking_count') * feat.get('_car_width')",
"_has_sidewalk_left=['left','both'].indexOf(feat.properties['sidewalk']) >= 0",
"_has_sidewalk_right=['right','both'].indexOf(feat.properties['sidewalk']) >= 0",
"_pedestrian_flows_in_carriageway= 2 - feat.get('_has_sidewalk_left') - feat.get('_has_sidewalk_right')",
"_width:needed:pedestrians=feat.get('_pedestrianWidth') * feat.get('_pedestrian_flows_in_carriageway')",
"_oneway_car=(feat.properties['oneway:motor_vehicle'] ?? feat.properties['oneway']) == 'yes'",
"_width:needed:cars=feat.get('_car_width') * (2 - feat.get('_oneway_car'))",
"_cycling_allowed=feat.properties.bicycle != 'use_sidepath' && feat.properties.bicycle!='no'",
"_oneway_bicycle=((feat.properties['oneway:bicycle'] ?? feat.properties['oneway']) == 'yes') && feat.properties['cycleway'] != 'opposite'",
"_width:needed:cyclists=feat.get('_cycling_allowed') ? (feat.get('_cyclistWidth') * (2 - feat.get('_oneway_bicycle'))) : 0",
"_width:needed:total:=feat.get('_width:needed:cars') + feat.get('_width:needed:parking') + feat.get('_width:needed:cyclists') + feat.get('_width:needed:pedestrians')",
"_width:difference:=feat.get('_width:needed:total') - feat.get('width:carriageway')",
"_width:difference:no_pedestrians:=feat.get('_width:difference') - feat.get('_width:needed:pedestrians')"
],
"minzoom": 12,
"source": {
"osmTags": "width:carriageway~*"
},
"title": {
"render": {
"nl": "{name}"
},
"mappings": [
{
"if": "name=",
"then": {
"nl": "Naamloos segment"
}
}
]
},
"tagRenderings": [
{
"id": "carriageway_width",
"render": "Deze straat is <b>{width:carriageway}m</b> breed",
"question": "Hoe breed is deze straat?",
"freeform": {
"key": "width:carriageway",
"type": "distance",
"helperArgs": [
21,
"map"
]
}
},
{
"id": "too_little_width",
"render": "Deze straat heeft <span class='alert'>{_width:difference}m</span> te weinig. De ruimte die nodig zou zijn is:",
"mappings": [
{
"if": {
"or": [
"_width:difference~-.*",
"_width:difference=0"
]
},
"then": "Deze straat is breed genoeg:"
}
]
},
{
"id": "needed_for_cars",
"render": "<b>{_width:needed:cars}m</b> voor het autoverkeer",
"mappings": [
{
"if": "oneway=yes",
"then": "<b>{_width:needed:cars}m</b> voor het éénrichtings-autoverkeer"
},
{
"if": "oneway=no",
"then": "<b>{_width:needed:cars}m</b> voor het tweerichtings-autoverkeer"
}
]
},
{
"id": "needed_for_parking",
"render": "<b>{_width:needed:parking}m</b> voor het geparkeerde wagens",
"condition": "_width:needed:parking!=0"
},
{
"id": "needed_for_cyclists",
"render": "<b>{_width:needed:cyclists}m</b> voor fietsers",
"mappings": [
{
"if": "bicycle=use_sidepath",
"then": "Fietsers hebben hier een vrijliggend fietspad en worden dus niet meegerekend"
},
{
"if": "oneway:bicycle=yes",
"then": "<b>{_width:needed:cyclists}m</b> voor fietsers die met de rijrichting mee moeten"
}
]
},
{
"id": "needed_for_pedestrians",
"render": "<b>{_width:needed:pedestrians}m</b> voor voetgangers",
"condition": "_width:needed:pedestrians!=0",
"mappings": [
{
"if": {
"or": [
"sidewalk=none",
"sidewalk=no"
]
},
"then": "<b>{_width:needed:pedestrians}m</b> voor voetgangers: er zijn hier geen voetpaden"
},
{
"if": {
"or": [
"sidewalk=left",
"sidewalk=right"
]
},
"then": "<b>{_width:needed:pedestrians}m</b> voor voetgangers: er is slechts aan één kant een voetpad"
}
]
},
{
"id": "total_width_needed",
"render": "<span style='border: 1px solid black; border-radius: 0.5em; padding: 0.25em;'><b>{_width:needed:total}m</b> nodig in het totaal</span>"
},
{
"id": "has_sidewalks",
"condition": "id=disabled",
"question": {
"nl": "Heeft deze straat voetpaden?"
},
"mappings": [
{
"if": "sidewalk=both",
"then": {
"nl": "Voetpad aan beide zijden"
}
},{
"if": "sidewalk=none",
"then": {
"nl": "Heeft géén voetpaden"
}
},{
"if": "sidewalk=left",
"then": {
"nl": "Voetpad aan de linkerkant"
}
},
{
"if": "sidewalk=right",
"then": {
"nl": "Voetpad aan de rechterzijde"
}
}
]
}
],
"mapRendering": [
{
"location": [
"point"
],
"icon": "./assets/themes/width/icon.svg",
"iconSize": "40,40,center"
},
{
"width": "4",
"color": {
"render": "#00f",
"mappings": [
{
"if": {
"or": [
"access=destination",
"highway=pedestrian",
"motor_vehicle=no",
"motor_vehicle=destination"
]
},
"then": "lightgrey"
},
{
"if": {
"and": [
"_width:difference!~-.*",
"_width:difference:no_pedestrians~-.*"
]
},
"then": "orange"
},
{
"if": "_width:difference~-.*",
"then": "#0f0"
},
{
"if": "_width:difference!~-.*",
"then": "#f00"
}
]
},
"dashArray": {
"render": "",
"mappings": [
{
"if": {
"and": [
"oneway=yes",
{
"or": [
"oneway:bicycle=yes",
"oneway:bicycle="
]
}
]
},
"then": "5 6"
}
]
}
}
]
}
]
}

View file

@ -261,7 +261,9 @@ function main(args: string[]) {
mkdirSync("./assets/generated") mkdirSync("./assets/generated")
} }
let contents = ScriptUtils.readDirRecSync("./assets").filter( let contents = ScriptUtils.readDirRecSync("./assets")
.filter(p => !p.startsWith("./assets/templates/"))
.filter(
(entry) => entry.indexOf("./assets/generated") != 0 (entry) => entry.indexOf("./assets/generated") != 0
) )
let licensePaths = contents.filter((entry) => entry.indexOf("license_info.json") >= 0) let licensePaths = contents.filter((entry) => entry.indexOf("license_info.json") >= 0)