From db40a5dfa5e059e284b727c8c9f9c32d31691311 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 17 Sep 2021 16:13:35 +0200 Subject: [PATCH 001/110] Add streetcomplete as option when querying licenses --- scripts/generateLicenseInfo.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/generateLicenseInfo.ts b/scripts/generateLicenseInfo.ts index 0a559a77c..f9e6d485f 100644 --- a/scripts/generateLicenseInfo.ts +++ b/scripts/generateLicenseInfo.ts @@ -72,6 +72,14 @@ knownLicenses.set("me", { sources: [] }) +knownLicenses.set("streetcomplete", { + authors: ["Tobias Zwick (westnordost)"], + path: undefined, + license: "CC0", + sources: ["https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics","https://f-droid.org/packages/de.westnordost.streetcomplete/"] +}) + + knownLicenses.set("t", { authors: [], path: undefined, From 202e3cd677943bdd5e7daaee0889ade38fab8e78 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 17 Sep 2021 16:54:12 +0200 Subject: [PATCH 002/110] Fix canonicalization if no units are given --- Logic/SimpleMetaTagger.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 27f4225b9..7440a067a 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -83,13 +83,20 @@ export default class SimpleMetaTagger { }, (feature => { - const units = Utils.NoNull([].concat(State.state?.layoutToUse?.data?.layers?.map(layer => layer.units ?? []))); + const units = Utils.NoNull([].concat(...State.state?.layoutToUse?.data?.layers?.map(layer => layer.units ?? []))); + if(units.length == 0){ + return; + } let rewritten = false; for (const key in feature.properties) { if (!feature.properties.hasOwnProperty(key)) { continue; } for (const unit of units) { + if(unit.appliesToKeys === undefined){ + console.error("The unit ", unit, "has no appliesToKey defined") + continue + } if (!unit.appliesToKeys.has(key)) { continue; } From d5853050b0c6314517dd12967a44bc5f3429bf52 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:28:17 +0200 Subject: [PATCH 003/110] Fix padding calculation by using the leaflet builtin padding function --- Logic/Actors/OverpassFeatureSource.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index b650d819b..4d4110935 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -155,17 +155,15 @@ export default class OverpassFeatureSource implements FeatureSource { return; } - const bounds = this._leafletMap.data?.getBounds(); + const bounds = this._leafletMap.data?.getBounds()?.pad( this._layoutToUse.data.widenFactor); if (bounds === undefined) { return; } - const diff = this._layoutToUse.data.widenFactor; - - const n = Math.min(90, bounds.getNorth() + diff); - const e = Math.min(180, bounds.getEast() + diff); - const s = Math.max(-90, bounds.getSouth() - diff); - const w = Math.max(-180, bounds.getWest() - diff); + const n = Math.min(90, bounds.getNorth() ); + const e = Math.min(180, bounds.getEast() ); + const s = Math.max(-90, bounds.getSouth()); + const w = Math.max(-180, bounds.getWest()); const queryBounds = {north: n, east: e, south: s, west: w}; const z = Math.floor(this._location.data.zoom ?? 0); From 30ceaa74b1e442cbae03c719975cd89a656a4f65 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:29:05 +0200 Subject: [PATCH 004/110] Add closestN function, update docs --- Logic/ExtraFunction.ts | 185 ++++++++++++++++++++++++++++++----------- 1 file changed, 138 insertions(+), 47 deletions(-) diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index b3b10ac2a..4deb3e01f 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -45,7 +45,11 @@ export class ExtraFunction { private static readonly OverlapFunc = new ExtraFunction( { name: "overlapWith", - doc: "Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point", + doc: "Gives a list of features from the specified layer which this feature (partly) overlaps with. " + + "If the current feature is a point, all features that embed the point are given. " + + "The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point.\n" + + "\n" + + "For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')`", args: ["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"] }, (params, feat) => { @@ -92,59 +96,32 @@ export class ExtraFunction { } } ) - private static readonly ClosestObjectFunc = new 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.", + 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"] }, (params, feature) => { - return (features) => { - if (typeof features === "string") { - const name = features - features = params.featuresPerLayer.get(features) - if (features === undefined) { - var keys = Utils.NoNull(Array.from(params.featuresPerLayer.keys())); - if (keys.length > 0) { - throw `No features defined for ${name}. Defined layers are ${keys.join(", ")}`; - } else { - // This is the first pass over an external dataset - // Other data probably still has to load! - return undefined; - } - - } - } - - let closestFeature = undefined; - let closestDistance = undefined; - for (const otherFeature of features) { - if (otherFeature == feature || otherFeature.id == feature.id) { - continue; // We ignore self - } - let distance = undefined; - if (otherFeature._lon !== undefined && otherFeature._lat !== undefined) { - distance = GeoOperations.distanceBetween([otherFeature._lon, otherFeature._lat], [feature._lon, feature._lat]); - } else { - distance = GeoOperations.distanceBetween( - GeoOperations.centerpointCoordinates(otherFeature), - [feature._lon, feature._lat] - ) - } - if (distance === undefined) { - throw "Undefined distance!" - } - if (closestFeature === undefined || distance < closestDistance) { - closestFeature = otherFeature - closestDistance = distance; - } - } - return closestFeature; - } + return (features) => ExtraFunction.GetClosestNFeatures(params, feature, features)[0].feat } ) + private static readonly ClosestNObjectFunc = new ExtraFunction( + { + name: "closestn", + doc: "Given either a list of geojson features or a single layer name, gives the n closest objects which are nearest to the feature. 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 laoded)\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", "amount of features", "unique tag key (optional)"] + }, + (params, feature) => { + return (features, amount, uniqueTag) => ExtraFunction.GetClosestNFeatures(params, feature, features, { + maxFeatures: Number(amount), + uniqueTag: uniqueTag + }) + } + ) private static readonly Memberships = new ExtraFunction( { @@ -158,7 +135,6 @@ export class ExtraFunction { return () => params.relations ?? []; } ) - private static readonly AspectedRouting = new ExtraFunction( { name: "score", @@ -178,11 +154,11 @@ export class ExtraFunction { } } ) - private static readonly allFuncs: ExtraFunction[] = [ ExtraFunction.DistanceToFunc, ExtraFunction.OverlapFunc, ExtraFunction.ClosestObjectFunc, + ExtraFunction.ClosestNObjectFunc, ExtraFunction.Memberships, ExtraFunction.AspectedRouting ]; @@ -221,6 +197,121 @@ export class ExtraFunction { ]); } + /** + * Gets the closes N features, sorted by ascending distance + */ + private static GetClosestNFeatures(params, feature, features, options?: { maxFeatures?: number, uniqueTag?: string | undefined }): { feat: any, distance: number }[] { + const maxFeatures = options?.maxFeatures ?? 1 + const uniqueTag : string | undefined = options?.uniqueTag + if (typeof features === "string") { + const name = features + features = params.featuresPerLayer.get(features) + if (features === undefined) { + var keys = Utils.NoNull(Array.from(params.featuresPerLayer.keys())); + if (keys.length > 0) { + throw `No features defined for ${name}. Defined layers are ${keys.join(", ")}`; + } else { + // This is the first pass over an external dataset + // Other data probably still has to load! + return undefined; + } + + } + } + + let closestFeatures: { feat: any, distance: number }[] = []; + for (const otherFeature of features) { + if (otherFeature == feature || otherFeature.id == feature.id) { + continue; // We ignore self + } + let distance = undefined; + if (otherFeature._lon !== undefined && otherFeature._lat !== undefined) { + distance = GeoOperations.distanceBetween([otherFeature._lon, otherFeature._lat], [feature._lon, feature._lat]); + } else { + distance = GeoOperations.distanceBetween( + GeoOperations.centerpointCoordinates(otherFeature), + [feature._lon, feature._lat] + ) + } + if (distance === undefined) { + throw "Undefined distance!" + } + + if (closestFeatures.length === 0) { + closestFeatures.push({ + feat: otherFeature, + distance: distance + }) + continue; + } + + if (closestFeatures.length >= maxFeatures && closestFeatures[maxFeatures - 1].distance < distance) { + // The last feature of the list (and thus the furthest away is still closer + // No use for checking, as we already have plenty of features! + continue + } + + let targetIndex = closestFeatures.length + for (let i = 0; i < closestFeatures.length; i++) { + const closestFeature = closestFeatures[i]; + + if (uniqueTag !== undefined) { + const uniqueTagsMatch = otherFeature.properties[uniqueTag] !== undefined && + closestFeature.feat.properties[uniqueTag] === otherFeature.properties[uniqueTag] + if (uniqueTagsMatch) { + targetIndex = -1 + if (closestFeature.distance > distance) { + // This is a very special situation: + // We want to see the tag `uniquetag=some_value` only once in the entire list (e.g. to prevent road segements of identical names to fill up the list of 'names of nearby roads') + // AT this point, we have found a closer segment with the same, identical tag + // so we replace directly + closestFeatures[i] = {feat: otherFeature, distance: distance} + } + break; + } + } + + if (closestFeature.distance > distance) { + targetIndex = i + + if (uniqueTag !== undefined) { + const uniqueValue = otherFeature.properties[uniqueTag] + // We might still have some other values later one with the same uniquetag that have to be cleaned + for (let j = i; j < closestFeatures.length; j++) { + if(closestFeatures[j].feat.properties[uniqueTag] === uniqueValue){ + closestFeatures.splice(j, 1) + } + } + } + break; + } + } + + if (targetIndex == -1) { + continue; // value is already swapped by the unique tag + } + + if (targetIndex < maxFeatures) { + // insert and drop one + closestFeatures.splice(targetIndex, 0, { + feat: otherFeature, + distance: distance + }) + if (closestFeatures.length >= maxFeatures) { + closestFeatures.splice(maxFeatures, 1) + } + } else { + // Overwrite the last element + closestFeatures[targetIndex] = { + feat: otherFeature, + distance: distance + } + + } + } + return closestFeatures; + } + public PatchFeature(featuresPerLayer: Map, relations: { role: string, relation: Relation }[], feature: any) { feature[this._name] = this._f({featuresPerLayer: featuresPerLayer, relations: relations}, feature) } From c0651479270b06b4adc2e8a57ecee6749cc3231b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:29:47 +0200 Subject: [PATCH 005/110] Documentation update --- Docs/CalculatedTags.md | 170 ++++---- Docs/SpecialInputElements.md | 6 +- Docs/SpecialRenderings.md | 145 +++---- .../mapcomplete_charging_stations.json | 381 ++++++++++++++++++ Docs/TagInfo/mapcomplete_toilets.json | 5 + Docs/TagInfo/mapcomplete_waste_basket.json | 30 ++ Docs/URL_Parameters.md | 123 +++--- 7 files changed, 648 insertions(+), 212 deletions(-) diff --git a/Docs/CalculatedTags.md b/Docs/CalculatedTags.md index 13f2d287e..8d8973b7b 100644 --- a/Docs/CalculatedTags.md +++ b/Docs/CalculatedTags.md @@ -1,91 +1,115 @@ -Metatags + + Metatags ========== Metatags are extra tags available, in order to display more data or to give better questions. -The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an -overview of the available metatags. +The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags. -**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a -box in the popup for features which shows all the properties of the object +**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object -Metatags calculated by MapComplete + Metatags calculated by MapComplete ------------------------------------ -The following values are always calculated, by default, by MapComplete and are available automatically on all elements -in every theme +The following values are always calculated, by default, by MapComplete and are available automatically on all elements in every theme + + +### _lat, _lon + -### _lat, _lon The latitude and longitude of the point (or centerpoint in the case of a way/area) -### _surface, _surface:ha + +### _surface, _surface:ha + + The surface area of the feature, in square meters and in hectare. Not set on points and ways -### _length, _length:km -The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the -length of the perimeter +### _length, _length:km -### Theme-defined keys -If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical -form (e.g. `1meter` will be rewritten to `1m`) -### _country +The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter + + +### Theme-defined keys + + + +If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`) + + +### _country + + The country code of the property (with latlon2country) -### _isOpen, _isOpen:description + +### _isOpen, _isOpen:description + + If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no') -### _width:needed, _width:needed:no_pedestrians, _width:difference -Legacy for a specific project calculating the needed width for safe traffic on a road. Only activated if 'width: -carriageway' is present +### _width:needed, _width:needed:no_pedestrians, _width:difference -### _direction:numerical, _direction:leftright -_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only -present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is -left-looking on the map or 'right-looking' on the map -### _now:date, _now:datetime, _loaded:date, _loaded:_datetime +Legacy for a specific project calculating the needed width for safe traffic on a road. Only activated if 'width:carriageway' is present + + +### _direction:numerical, _direction:leftright + + + +_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map + + +### _now:date, _now:datetime, _loaded:date, _loaded:_datetime + + + +Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely + + +### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number -Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh: -mm, aka 'sortable' aka ISO-8601-but-not-entirely -### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number Information about the last edit of this object. -Calculating tags with Javascript + Calculating tags with Javascript ---------------------------------- -In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by -default (e.g. `lat`, `lon`, `_country`), as detailed above. +In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by default (e.g. `lat`, `lon`, `_country`), as detailed above. It is also possible to calculate your own tags - but this requires some javascript knowledge. + + Before proceeding, some warnings: -- DO NOT DO THIS AS BEGINNER -- **Only do this if all other techniques fail** This should _not_ be done to create a rendering effect, only to - calculate a specific value -- **THIS MIGHT BE DISABLED WITHOUT ANY NOTICE ON UNOFFICIAL THEMES** As unofficial themes might be loaded from the - internet, this is the equivalent of injecting arbitrary code into the client. It'll be disabled if abuse occurs. -To enable this feature, add a field `calculatedTags` in the layer object, e.g.: + + - DO NOT DO THIS AS BEGINNER + - **Only do this if all other techniques fail** This should _not_ be done to create a rendering effect, only to calculate a specific value + - **THIS MIGHT BE DISABLED WITHOUT ANY NOTICE ON UNOFFICIAL THEMES** As unofficial themes might be loaded from the internet, this is the equivalent of injecting arbitrary code into the client. It'll be disabled if abuse occurs. + + +To enable this feature, add a field `calculatedTags` in the layer object, e.g.: ```` @@ -101,56 +125,58 @@ To enable this feature, add a field `calculatedTags` in the layer object, e.g.: ```` -The above code will be executed for every feature in the layer. The feature is accessible as `feat` and is an amended -geojson object: -- `area` contains the surface area (in square meters) of the object -- `lat` and `lon` contain the latitude and longitude -Some advanced functions are available on **feat** as well: +The above code will be executed for every feature in the layer. The feature is accessible as `feat` and is an amended geojson object: -- distanceTo -- overlapWith -- closest -- memberships -- score -### distanceTo -Calculates the distance between the feature and a specified point in kilometer. The input should either be a pair of -coordinates, a geojson feature or the ID of an object + - `area` contains the surface area (in square meters) of the object + - `lat` and `lon` contain the latitude and longitude -0. longitude -1. latitude -### overlapWith +Some advanced functions are available on **feat** as well: -Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a -point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` -where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current -feature is a line or `undefined` if the current feature is a point + - distanceTo + - overlapWith + - closest + - memberships + - score + +### distanceTo -0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap) + Calculates the distance between the feature and a specified point in kilometer. The input should either be a pair of coordinates, a geojson feature or the ID of an object -### closest + 0. longitude + 1. latitude + +### overlapWith -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. + Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point. -0. list of features +For example to get all objects which overlap or embed from a layer, use `_contained_climbing_routes_properties=feat.overlapWith('climbing_route')` -### memberships + 0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap) + +### closest -Gives a list of `{role: string, relation: Relation}`-objects, containing all the relations that this feature is part of. + 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. -For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')` + 0. list of features + +### memberships -### score + Gives a list of `{role: string, relation: Relation}`-objects, containing all the relations that this feature is part of. -Given the path of an aspected routing json file, will calculate the score. This score is wrapped in a UIEventSource, so -for further calculations, use `.map(score => ...)` +For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')` -For -example: `_comfort_score=feat.score('https://raw.githubusercontent.com/pietervdvn/AspectedRouting/master/Examples/bicycle/aspects/bicycle.comfort.json')` -0. path Generated from SimpleMetaTagger, ExtraFunction \ No newline at end of file + +### score + + Given the path of an aspected routing json file, will calculate the score. This score is wrapped in a UIEventSource, so for further calculations, use `.map(score => ...)` + +For example: `_comfort_score=feat.score('https://raw.githubusercontent.com/pietervdvn/AspectedRouting/master/Examples/bicycle/aspects/bicycle.comfort.json')` + + 0. path + Generated from SimpleMetaTagger, ExtraFunction \ No newline at end of file diff --git a/Docs/SpecialInputElements.md b/Docs/SpecialInputElements.md index b7628b51b..957a74db4 100644 --- a/Docs/SpecialInputElements.md +++ b/Docs/SpecialInputElements.md @@ -1,7 +1,6 @@ # Available types for text fields -The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to -activate them +The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them ## string @@ -21,8 +20,7 @@ A geographical direction, in degrees. 0° is north, 90° is east, ... Will retur ## length -A geographical length in meters (rounded at two points). Will give an extra minimap with a measurement tool. -Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `["21", "map,photo"] +A geographical length in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `["21", "map,photo"] ## wikidata diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index 056765e58..da4a4bc20 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -1,110 +1,92 @@ -### Special tag renderings -In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and -visualizations to be reused by custom themes or even to query third-party API's. General usage is `{func_name()}` -, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_fcs need to use quotes around -your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args +### Special tag renderings -### all_tags + In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's. General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_fcs need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args +### all_tags -Prints all key-value pairs of the object - used for debugging + Prints all key-value pairs of the object - used for debugging name | default | description ------ | --------- | ------------- -#### Example usage + +#### Example usage -{all_tags()} + `{all_tags()}` +### image_carousel -### image_carousel - -Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: -Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links) + Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links) name | default | description ------ | --------- | ------------- -image key/prefix | image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... +image key/prefix | image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... smart search | true | Also include images given via 'Wikidata', 'wikimedia_commons' and 'mapillary + +#### Example usage -#### Example usage + `{image_carousel(image,true)}` +### image_upload -{image_carousel(image,true)} - -### image_upload - -Creates a button where a user can upload an image to IMGUR + Creates a button where a user can upload an image to IMGUR 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) + +#### Example usage -#### Example usage + `{image_upload(image)}` +### minimap -{image_upload(image)} - -### minimap - -A small map showing the selected feature. Note that no styling is applied, wrap this in a div + A small map showing the selected feature. Note that no styling is applied, wrap this in a div name | default | description ------ | --------- | ------------- zoomlevel | 18 | The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close idKey | id | (Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap. + +#### Example usage -#### Example usage + `{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}` +### reviews -`{minimap()}` -, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}` - -### reviews - -Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed -object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten + Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten name | default | description ------ | --------- | ------------- subjectKey | name | The key to use to determine the subject. If specified, the subject will be tags[subjectKey] fallback | undefined | The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value + +#### Example usage -#### Example usage + `{reviews()}` for a vanilla review, `{reviews(name, play_forest)}` to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used +### opening_hours_table -{reviews()} for a vanilla review, {reviews(name, play_forest)} to review a play forest. If a name is known, -the name will be used as identifier, otherwise 'play_forest' is used - -### opening_hours_table - -Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag ' -opening_hours'. + Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag 'opening_hours'. name | default | description ------ | --------- | ------------- key | opening_hours | The tagkey from which the table is constructed. + +#### Example usage -#### Example usage + `{opening_hours_table(opening_hours)}` +### live -{opening_hours_table(opening_hours)} - -### live - -Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will -download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will -return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, -needed_value)} + Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)} name | default | description ------ | --------- | ------------- Url | undefined | The URL to load Shorthands | undefined | A list of shorthands, of the format 'shorthandname:path.path.path'. separated by ; path | undefined | The path (or shorthand) that should be returned + +#### Example usage -#### Example usage + {live({url},{url:format},hour)} {live(https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CB2105,hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt,hour)} +### histogram -{live({url},{url:format},hour)} -{live(https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CB2105,hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt,hour)} - -### histogram - -Create a histogram for a list of given values, read from the properties. + Create a histogram for a list of given values, read from the properties. name | default | description ------ | --------- | ------------- @@ -112,31 +94,50 @@ key | undefined | The key to be read and to generate a histogram from title | | The text to put above the given values column countHeader | | The text to put above the counts colors* | undefined | (Matches all resting arguments - optional) Matches a regex onto a color value, e.g. `3[a-zA-Z+-]*:#33cc33` + +#### Example usage -#### Example usage + `{histogram('some_key')}` with properties being `{some_key: ['a','b','a','c']} to create a histogram +### share_link -`{histogram('some_key')}` with properties being `{some_key: ['a','b','a','c']} to create a histogram - -### share_link - -Creates a link that (attempts to) open the native 'share'-screen + Creates a link that (attempts to) open the native 'share'-screen name | default | description ------ | --------- | ------------- url | undefined | The url to share (default: current URL) + +#### Example usage -#### Example usage + {share_link()} to share the current page, {share_link()} to share the given url +### canonical -{share_link()} to share the current page, {share_link()} to share the given url - -### canonical - -Converts a short, canonical value into the long, translated text + Converts a short, canonical value into the long, translated text name | default | description ------ | --------- | ------------- key | undefined | The key of the tag to give the canonical text for + +#### Example usage -#### Example usage + {canonical(length)} will give 42 metre (in french) +### import_button -{canonical(length)} will give 42 metre (in french) Generated from UI/SpecialVisualisations.ts \ No newline at end of file + This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. + +If you want to import a dataset, make sure that: + +1. The dataset to import has a suitable license +2. The community has been informed of the import +3. The theme will filter out duplicate nodes4. All other requirements of the [import guidelines](https://wiki.openstreetmap.org/wiki/Import/Guidelines) have been followed + +name | default | description +------ | --------- | ------------- +tags | undefined | Tags to copy-specification. This contains one or more pairs (seperated by a `;`), e.g. `amenity=fast_food; addr:housenumber={number}`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. (Hint: prepare these values, e.g. with calculatedTags) +text | Import this data into OpenStreetMap | The text to show on the button +icon | ./assets/svg/addSmall.svg | A nice icon to show in the button + +#### Example usage + + `{import_button(,Import this data into OpenStreetMap,./assets/svg/addSmall.svg)}` + +Generated from UI/SpecialVisualisations.ts \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_charging_stations.json b/Docs/TagInfo/mapcomplete_charging_stations.json index 948444129..0306d7284 100644 --- a/Docs/TagInfo/mapcomplete_charging_stations.json +++ b/Docs/TagInfo/mapcomplete_charging_stations.json @@ -189,38 +189,381 @@ "key": "socket:schuko", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:schuko:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:schuko:voltage", + "description": "Layer 'Charging stations' shows socket:socket:schuko:voltage=230 V with a fixed text, namely 'Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "230 V" + }, + { + "key": "socket:schuko:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:schuko:current", + "description": "Layer 'Charging stations' shows socket:socket:schuko:current=16 A with a fixed text, namely 'Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "16 A" + }, + { + "key": "socket:schuko:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:schuko:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:schuko:output", + "description": "Layer 'Charging stations' shows socket:socket:schuko:output=3.6 kw with a fixed text, namely 'Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "3.6 kw" + }, { "key": "socket:typee", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:typee:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:typee:voltage", + "description": "Layer 'Charging stations' shows socket:socket:typee:voltage=230 V with a fixed text, namely 'European wall plug with ground pin (CEE7/4 type E) outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "230 V" + }, + { + "key": "socket:typee:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:typee:current", + "description": "Layer 'Charging stations' shows socket:socket:typee:current=16 A with a fixed text, namely 'European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "16 A" + }, + { + "key": "socket:typee:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:typee:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:typee:output", + "description": "Layer 'Charging stations' shows socket:socket:typee:output=3 kw with a fixed text, namely 'European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "3 kw" + }, + { + "key": "socket:socket:typee:output", + "description": "Layer 'Charging stations' shows socket:socket:typee:output=22 kw with a fixed text, namely 'European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "22 kw" + }, { "key": "socket:chademo", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:chademo:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:chademo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:chademo:voltage=500 V with a fixed text, namely 'Chademo outputs 500 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "500 V" + }, + { + "key": "socket:chademo:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:chademo:current", + "description": "Layer 'Charging stations' shows socket:socket:chademo:current=120 A with a fixed text, namely 'Chademo outputs at most 120 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "120 A" + }, + { + "key": "socket:chademo:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:chademo:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:chademo:output", + "description": "Layer 'Charging stations' shows socket:socket:chademo:output=50 kw with a fixed text, namely 'Chademo outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "50 kw" + }, { "key": "socket:type1_cable", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type1_cable:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_cable:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:voltage=200 V with a fixed text, namely 'Type 1 with cable (J1772) outputs 200 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "200 V" + }, + { + "key": "socket:socket:type1_cable:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:voltage=240 V with a fixed text, namely 'Type 1 with cable (J1772) outputs 240 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "240 V" + }, + { + "key": "socket:type1_cable:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_cable:current", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:current=32 A with a fixed text, namely 'Type 1 with cable (J1772) outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "32 A" + }, + { + "key": "socket:type1_cable:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_cable:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_cable:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:output=3.7 kw with a fixed text, namely 'Type 1 with cable (J1772) outputs at most 3.7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "3.7 kw" + }, + { + "key": "socket:socket:type1_cable:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_cable:output=7 kw with a fixed text, namely 'Type 1 with cable (J1772) outputs at most 7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "7 kw" + }, { "key": "socket:type1", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type1:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1:voltage=200 V with a fixed text, namely 'Type 1 without cable (J1772) outputs 200 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "200 V" + }, + { + "key": "socket:socket:type1:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1:voltage=240 V with a fixed text, namely 'Type 1 without cable (J1772) outputs 240 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "240 V" + }, + { + "key": "socket:type1:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1:current", + "description": "Layer 'Charging stations' shows socket:socket:type1:current=32 A with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "32 A" + }, + { + "key": "socket:type1:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1:output", + "description": "Layer 'Charging stations' shows socket:socket:type1:output=3.7 kw with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 3.7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "3.7 kw" + }, + { + "key": "socket:socket:type1:output", + "description": "Layer 'Charging stations' shows socket:socket:type1:output=6.6 kw with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 6.6 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "6.6 kw" + }, + { + "key": "socket:socket:type1:output", + "description": "Layer 'Charging stations' shows socket:socket:type1:output=7 kw with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 7 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "7 kw" + }, + { + "key": "socket:socket:type1:output", + "description": "Layer 'Charging stations' shows socket:socket:type1:output=7.2 kw with a fixed text, namely 'Type 1 without cable (J1772) outputs at most 7.2 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "7.2 kw" + }, { "key": "socket:type1_combo", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type1_combo:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_combo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:voltage=400 V with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs 400 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "400 V" + }, + { + "key": "socket:socket:type1_combo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:voltage=1000 V with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs 1000 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "1000 V" + }, + { + "key": "socket:type1_combo:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_combo:current", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:current=50 A with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 50 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "50 A" + }, + { + "key": "socket:socket:type1_combo:current", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:current=125 A with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "125 A" + }, + { + "key": "socket:type1_combo:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type1_combo:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type1_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=50 kw with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "50 kw" + }, + { + "key": "socket:socket:type1_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=62.5 kw with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "62.5 kw" + }, + { + "key": "socket:socket:type1_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=150 kw with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "150 kw" + }, + { + "key": "socket:socket:type1_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type1_combo:output=350 kw with a fixed text, namely 'Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "350 kw" + }, { "key": "socket:tesla_supercharger", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:tesla_supercharger:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:tesla_supercharger:voltage", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:voltage=480 V with a fixed text, namely 'Tesla Supercharger outputs 480 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "480 V" + }, + { + "key": "socket:tesla_supercharger:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:tesla_supercharger:current", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:current=125 A with a fixed text, namely 'Tesla Supercharger outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "125 A" + }, + { + "key": "socket:socket:tesla_supercharger:current", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:current=350 A with a fixed text, namely 'Tesla Supercharger outputs at most 350 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "350 A" + }, + { + "key": "socket:tesla_supercharger:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:tesla_supercharger:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:tesla_supercharger:output", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:output=120 kw with a fixed text, namely 'Tesla Supercharger outputs at most 120 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "120 kw" + }, + { + "key": "socket:socket:tesla_supercharger:output", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:output=150 kw with a fixed text, namely 'Tesla Supercharger outputs at most 150 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "150 kw" + }, + { + "key": "socket:socket:tesla_supercharger:output", + "description": "Layer 'Charging stations' shows socket:socket:tesla_supercharger:output=250 kw with a fixed text, namely 'Tesla Supercharger outputs at most 250 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "250 kw" + }, { "key": "socket:type2", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type2:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type2:voltage=230 V with a fixed text, namely 'Type 2 (mennekes) outputs 230 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "230 V" + }, + { + "key": "socket:socket:type2:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type2:voltage=400 V with a fixed text, namely 'Type 2 (mennekes) outputs 400 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "400 V" + }, + { + "key": "socket:type2:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2:current", + "description": "Layer 'Charging stations' shows socket:socket:type2:current=16 A with a fixed text, namely 'Type 2 (mennekes) outputs at most 16 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "16 A" + }, + { + "key": "socket:socket:type2:current", + "description": "Layer 'Charging stations' shows socket:socket:type2:current=32 A with a fixed text, namely 'Type 2 (mennekes) outputs at most 32 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "32 A" + }, + { + "key": "socket:type2:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2:output", + "description": "Layer 'Charging stations' shows socket:socket:type2:output=11 kw with a fixed text, namely 'Type 2 (mennekes) outputs at most 11 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "11 kw" + }, + { + "key": "socket:socket:type2:output", + "description": "Layer 'Charging stations' shows socket:socket:type2:output=22 kw with a fixed text, namely 'Type 2 (mennekes) outputs at most 22 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "22 kw" + }, { "key": "socket:type2_combo", "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo' (in the MapComplete.osm.be theme 'Charging stations')" }, + { + "key": "socket:type2_combo:voltage", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo:voltage' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2_combo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:voltage=500 V with a fixed text, namely 'Type 2 CCS (mennekes) outputs 500 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "500 V" + }, + { + "key": "socket:socket:type2_combo:voltage", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:voltage=920 V with a fixed text, namely 'Type 2 CCS (mennekes) outputs 920 volt' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "920 V" + }, + { + "key": "socket:type2_combo:current", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo:current' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2_combo:current", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:current=125 A with a fixed text, namely 'Type 2 CCS (mennekes) outputs at most 125 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "125 A" + }, + { + "key": "socket:socket:type2_combo:current", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:current=350 A with a fixed text, namely 'Type 2 CCS (mennekes) outputs at most 350 A' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "350 A" + }, + { + "key": "socket:type2_combo:output", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'socket:type2_combo:output' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "socket:socket:type2_combo:output", + "description": "Layer 'Charging stations' shows socket:socket:type2_combo:output=50 kw with a fixed text, namely 'Type 2 CCS (mennekes) outputs at most 50 kw' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "50 kw" + }, { "key": "authentication:membership_card", "description": "Layer 'Charging stations' shows authentication:membership_card=yes with a fixed text, namely 'Authentication by a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", @@ -274,6 +617,44 @@ "description": "Layer 'Charging stations' shows opening_hours=24/7 with a fixed text, namely '24/7 opened (including holidays)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "24/7" }, + { + "key": "charge", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'charge' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "fee", + "description": "Layer 'Charging stations' shows fee=no&charge= with a fixed text, namely 'Free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "no" + }, + { + "key": "charge", + "description": "Layer 'Charging stations' shows fee=no&charge= with a fixed text, namely 'Free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key charge.", + "value": "" + }, + { + "key": "payment:cash", + "description": "Layer 'Charging stations' shows payment:cash=yes with a fixed text, namely 'Cash is accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "payment:cards", + "description": "Layer 'Charging stations' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "payment:app", + "description": "Layer 'Charging stations' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "yes" + }, + { + "key": "maxstay", + "description": "Layer 'Charging stations' shows and asks freeform values for key 'maxstay' (in the MapComplete.osm.be theme 'Charging stations')" + }, + { + "key": "maxstay", + "description": "Layer 'Charging stations' shows maxstay=unlimited with a fixed text, namely 'No timelimit on leaving your vehicle here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "value": "unlimited" + }, { "key": "network", "description": "Layer 'Charging stations' shows and asks freeform values for key 'network' (in the MapComplete.osm.be theme 'Charging stations')" diff --git a/Docs/TagInfo/mapcomplete_toilets.json b/Docs/TagInfo/mapcomplete_toilets.json index 161489fb5..713b2b65b 100644 --- a/Docs/TagInfo/mapcomplete_toilets.json +++ b/Docs/TagInfo/mapcomplete_toilets.json @@ -55,6 +55,11 @@ "description": "Layer 'Toilets' shows access=key with a fixed text, namely 'Accessible, but one has to ask a key to enter' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", "value": "key" }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=public with a fixed text, namely 'Public access' (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "public" + }, { "key": "fee", "description": "Layer 'Toilets' shows fee=yes with a fixed text, namely 'These are paid toilets' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", diff --git a/Docs/TagInfo/mapcomplete_waste_basket.json b/Docs/TagInfo/mapcomplete_waste_basket.json index a1a4c9149..eeebd2e5b 100644 --- a/Docs/TagInfo/mapcomplete_waste_basket.json +++ b/Docs/TagInfo/mapcomplete_waste_basket.json @@ -14,6 +14,36 @@ "key": "amenity", "description": "The MapComplete theme Waste Basket has a layer Waste Basket showing features with this tag", "value": "waste_basket" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste= with a fixed text, namely 'A waste basket for general waste' (in the MapComplete.osm.be theme 'Waste Basket') Picking this answer will delete the key waste.", + "value": "" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=trash with a fixed text, namely 'A waste basket for general waste' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "trash" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=dog_excrement with a fixed text, namely 'A waste basket for dog excrements' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "dog_excrement" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=cigarettes with a fixed text, namely 'A waste basket for cigarettes' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "cigarettes" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=drugs with a fixed text, namely 'A waste basket for drugs' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "drugs" + }, + { + "key": "waste", + "description": "Layer 'Waste Basket' shows waste=sharps with a fixed text, namely 'A waste basket for needles and other sharp objects' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Waste Basket')", + "value": "sharps" } ] } \ No newline at end of file diff --git a/Docs/URL_Parameters.md b/Docs/URL_Parameters.md index 6b0afe450..f35380901 100644 --- a/Docs/URL_Parameters.md +++ b/Docs/URL_Parameters.md @@ -1,3 +1,4 @@ + URL-parameters and URL-hash ============================ @@ -8,8 +9,8 @@ What is a URL parameter? URL-parameters are extra parts of the URL used to set the state. -For example, if the url is `https://mapcomplete.osm.be/cyclofix?lat=51.0&lon=4.3&z=5&test=true#node/1234`, the -URL-parameters are stated in the part between the `?` and the `#`. There are multiple, all separated by `&`, namely: +For example, if the url is `https://mapcomplete.osm.be/cyclofix?lat=51.0&lon=4.3&z=5&test=true#node/1234`, +the URL-parameters are stated in the part between the `?` and the `#`. There are multiple, all separated by `&`, namely: - The url-parameter `lat` is `51.0` in this instance - The url-parameter `lon` is `4.3` in this instance @@ -19,175 +20,169 @@ URL-parameters are stated in the part between the `?` and the `#`. There are mul Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case. -download-control-toggle + download-control-toggle ------------------------- -Whether or not the download panel is shown The default value is _false_ + Whether or not the download panel is shown The default value is _false_ -filter-toggle + filter-toggle --------------- -Whether or not the filter view is shown The default value is _false_ + Whether or not the filter view is shown The default value is _false_ -tab + tab ----- -The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = -more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets) The default value is _0_ + The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets) The default value is _0_ -z + z --- -The initial/current zoom level The default value is _0_ + The initial/current zoom level The default value is _0_ -lat + lat ----- -The initial/current latitude The default value is _0_ + The initial/current latitude The default value is _0_ -lon + lon ----- -The initial/current longitude of the app The default value is _0_ + The initial/current longitude of the app The default value is _0_ -fs-userbadge + fs-userbadge -------------- -Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus -disables editing all together, effectively putting MapComplete into read-only mode. The default value is _true_ + Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode. The default value is _true_ -fs-search + fs-search ----------- -Disables/Enables the search bar The default value is _true_ + Disables/Enables the search bar The default value is _true_ -fs-background + fs-background --------------- -Disables/Enables the background layer control The default value is _true_ + Disables/Enables the background layer control The default value is _true_ -fs-filter + fs-filter ----------- -Disables/Enables the filter The default value is _true_ + Disables/Enables the filter The default value is _true_ -fs-add-new + fs-add-new ------------ -Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) The default -value is _true_ + Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) The default value is _true_ -fs-welcome-message + fs-welcome-message -------------------- -Disables/enables the help menu or welcome message The default value is _true_ + Disables/enables the help menu or welcome message The default value is _true_ -fs-iframe + fs-iframe ----------- -Disables/Enables the iframe-popup The default value is _false_ + Disables/Enables the iframe-popup The default value is _false_ -fs-more-quests + fs-more-quests ---------------- -Disables/Enables the 'More Quests'-tab in the welcome message The default value is _true_ + Disables/Enables the 'More Quests'-tab in the welcome message The default value is _true_ -fs-share-screen + fs-share-screen ----------------- -Disables/Enables the 'Share-screen'-tab in the welcome message The default value is _true_ + Disables/Enables the 'Share-screen'-tab in the welcome message The default value is _true_ -fs-geolocation + fs-geolocation ---------------- -Disables/Enables the geolocation button The default value is _true_ + Disables/Enables the geolocation button The default value is _true_ -fs-all-questions + fs-all-questions ------------------ -Always show all questions The default value is _false_ + Always show all questions The default value is _false_ -fs-export + fs-export ----------- -Enable the export as GeoJSON and CSV button The default value is _false_ + Enable the export as GeoJSON and CSV button The default value is _false_ -fs-pdf + fs-pdf -------- -Enable the PDF download button The default value is _false_ + Enable the PDF download button The default value is _false_ -test + test ------ -If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the -console instead of actually uploaded to osm.org The default value is _false_ + If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org The default value is _false_ -debug + debug ------- -If true, shows some extra debugging help such as all the available tags on every object The default value is _false_ + If true, shows some extra debugging help such as all the available tags on every object The default value is _false_ -fake-user + fake-user ----------- -If true, 'dryrun' mode is activated and a fake user account is loaded The default value is _false_ + If true, 'dryrun' mode is activated and a fake user account is loaded The default value is _false_ -backend + backend --------- -The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default -value is _osm_ + The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_ -overpassUrl + overpassUrl ------------- -Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter The default value -is _https://overpass-api.de/api/interpreter_ + Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter The default value is _https://overpass-api.de/api/interpreter_ -overpassTimeout + overpassTimeout ----------------- -Set a different timeout (in seconds) for queries in overpass The default value is _30_ + Set a different timeout (in seconds) for queries in overpass The default value is _30_ -custom-css + custom-css ------------ -If specified, the custom css from the given link will be loaded additionaly The default value is __ + If specified, the custom css from the given link will be loaded additionaly The default value is __ -background + background ------------ -The id of the background layer to start with The default value is _osm_ + The id of the background layer to start with The default value is _osm_ -layer- + layer- ------------------ -Wether or not the layer with id is shown The default value is _true_ Generated from QueryParameters \ No newline at end of file + Wether or not the layer with id is shown The default value is _true_ Generated from QueryParameters \ No newline at end of file From 2676b0e439567eb2669bbc52b5af4569dd916885 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:30:00 +0200 Subject: [PATCH 006/110] Documentation update --- .../mapcomplete_observation_towers.json | 124 ++++++++++++++++++ Docs/TagInfo/mapcomplete_uk_addresses.json | 39 ++++++ 2 files changed, 163 insertions(+) create mode 100644 Docs/TagInfo/mapcomplete_observation_towers.json create mode 100644 Docs/TagInfo/mapcomplete_uk_addresses.json diff --git a/Docs/TagInfo/mapcomplete_observation_towers.json b/Docs/TagInfo/mapcomplete_observation_towers.json new file mode 100644 index 000000000..ecf00cd66 --- /dev/null +++ b/Docs/TagInfo/mapcomplete_observation_towers.json @@ -0,0 +1,124 @@ +{ + "data_format": 1, + "project": { + "name": "MapComplete Observation towers", + "description": "Publicly accessible towers to enjoy the view", + "project_url": "https://mapcomplete.osm.be/observation_towers", + "doc_url": "https://github.com/pietervdvn/MapComplete/tree/master/assets/themes/", + "icon_url": "https://mapcomplete.osm.be/assets/layers/observation_tower/Tower_observation.svg", + "contact_name": "Pieter Vander Vennet, ", + "contact_email": "pietervdvn@posteo.net" + }, + "tags": [ + { + "key": "tower:type", + "description": "The MapComplete theme Observation towers has a layer Observation towers showing features with this tag", + "value": "observation" + }, + { + "key": "image", + "description": "The layer 'Observation towers allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "mapillary", + "description": "The layer 'Observation towers allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikidata", + "description": "The layer 'Observation towers allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "wikipedia", + "description": "The layer 'Observation towers allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary" + }, + { + "key": "name", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'name' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "noname", + "description": "Layer 'Observation towers' shows noname=yes with a fixed text, namely 'This tower doesn't have a specific name' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "height", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'height' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "operator", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'operator' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "website", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'website' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "charge", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'charge' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "fee", + "description": "Layer 'Observation towers' shows fee=no&charge= with a fixed text, namely 'Free to visit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "no" + }, + { + "key": "charge", + "description": "Layer 'Observation towers' shows fee=no&charge= with a fixed text, namely 'Free to visit' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers') Picking this answer will delete the key charge.", + "value": "" + }, + { + "key": "payment:cash", + "description": "Layer 'Observation towers' shows payment:cash=yes with a fixed text, namely 'Cash is accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "payment:cards", + "description": "Layer 'Observation towers' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "payment:app", + "description": "Layer 'Observation towers' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "wheelchair", + "description": "Layer 'Observation towers' shows wheelchair=designated with a fixed text, namely 'This place is specially adapated for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "designated" + }, + { + "key": "wheelchair", + "description": "Layer 'Observation towers' shows wheelchair=yes with a fixed text, namely 'This place is easily reachable with a wheelchair' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + }, + { + "key": "wheelchair", + "description": "Layer 'Observation towers' shows wheelchair=limited with a fixed text, namely 'It is possible to reach this place in a wheelchair, but it is not easy' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "limited" + }, + { + "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": "service:bicycle:cleaning:charge", + "description": "Layer 'Observation towers' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the MapComplete.osm.be theme 'Observation towers')" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Observation towers' shows service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge= with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "no&service:bicycle:cleaning:charge=" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Observation towers' shows service:bicycle:cleaning:fee=no& with a fixed text, namely 'Free to use' (in the MapComplete.osm.be theme 'Observation towers')", + "value": "no&" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Observation towers' shows service:bicycle:cleaning:fee=yes with a fixed text, namely 'The cleaning service has a fee' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')", + "value": "yes" + } + ] +} \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_uk_addresses.json b/Docs/TagInfo/mapcomplete_uk_addresses.json new file mode 100644 index 000000000..92774e2f1 --- /dev/null +++ b/Docs/TagInfo/mapcomplete_uk_addresses.json @@ -0,0 +1,39 @@ +{ + "data_format": 1, + "project": { + "name": "MapComplete UK Addresses", + "description": "Help to build an open dataset of UK addresses", + "project_url": "https://mapcomplete.osm.be/uk_addresses", + "doc_url": "https://github.com/pietervdvn/MapComplete/tree/master/assets/themes/", + "icon_url": "https://mapcomplete.osm.be/assets/themes/uk_addresses/housenumber_unknown.svg", + "contact_name": "Pieter Vander Vennet, Pieter Vander Vennet, Rob Nickerson, Russ Garrett", + "contact_email": "pietervdvn@posteo.net" + }, + "tags": [ + { + "key": "inspireid", + "description": "The MapComplete theme UK Addresses has a layer Addresses to check showing features with this tag" + }, + { + "key": "addr:housenumber", + "description": "The MapComplete theme UK Addresses has a layer Known addresses in OSM showing features with this tag" + }, + { + "key": "addr:street", + "description": "The MapComplete theme UK Addresses has a layer Known addresses in OSM showing features with this tag" + }, + { + "key": "addr:housenumber", + "description": "Layer 'Known addresses in OSM' shows and asks freeform values for key 'addr:housenumber' (in the MapComplete.osm.be theme 'UK Addresses')" + }, + { + "key": "nohousenumber", + "description": "Layer 'Known addresses in OSM' shows nohousenumber=yes with a fixed text, namely 'This building has no house number' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'UK Addresses')", + "value": "yes" + }, + { + "key": "addr:street", + "description": "Layer 'Known addresses in OSM' shows and asks freeform values for key 'addr:street' (in the MapComplete.osm.be theme 'UK Addresses')" + } + ] +} \ No newline at end of file From 706c5e3d53414e20e48eea50cc337fcd1a1b050b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:31:45 +0200 Subject: [PATCH 007/110] Add import button --- UI/BigComponents/ImportButton.ts | 59 ++++++++++++++++++++++++ UI/SpecialVisualizations.ts | 79 +++++++++++++++++++++++++++++--- langs/en.json | 3 +- 3 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 UI/BigComponents/ImportButton.ts diff --git a/UI/BigComponents/ImportButton.ts b/UI/BigComponents/ImportButton.ts new file mode 100644 index 000000000..1d690bae6 --- /dev/null +++ b/UI/BigComponents/ImportButton.ts @@ -0,0 +1,59 @@ +import BaseUIElement from "../BaseUIElement"; +import {SubtleButton} from "../Base/SubtleButton"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Combine from "../Base/Combine"; +import {VariableUiElement} from "../Base/VariableUIElement"; +import Translations from "../i18n/Translations"; +import State from "../../State"; +import Constants from "../../Models/Constants"; +import Toggle from "../Input/Toggle"; +import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"; +import {Tag} from "../../Logic/Tags/Tag"; + +export default class ImportButton extends Toggle { + constructor(imageUrl: string | BaseUIElement, message: string | BaseUIElement, + originalTags: UIEventSource, + newTags: UIEventSource, lat: number, lon: number) { + const t = Translations.t.general.add; + const isImported = originalTags.map(tags => tags._imported === "yes") + const appliedTags = new Toggle( + new VariableUiElement( + newTags.map(tgs => { + const parts = [] + for (const tag of tgs) { + parts.push(tag.key + "=" + tag.value) + } + const txt = parts.join(" & ") + return t.presetInfo.Subs({tags: txt}).SetClass("subtle") + })), undefined, + State.state.osmConnection.userDetails.map(ud => ud.csCount >= Constants.userJourney.tagsVisibleAt) + ) + const button = new SubtleButton(imageUrl, message) + + + button.onClick(() => { + if (isImported.data) { + return + } + originalTags.data["_imported"] = "yes" + originalTags.ping() // will set isImported as per its definition + const newElementAction = new CreateNewNodeAction(newTags.data, lat, lon) + State.state.changes.applyAction(newElementAction) + State.state.selectedElement.setData(State.state.allElements.ContainingFeatures.get( + newElementAction.newElementId + )) + console.log("Did set selected element to", State.state.allElements.ContainingFeatures.get( + newElementAction.newElementId + )) + + + }) + + const withLoadingCheck = new Toggle( + t.stillLoading, + new Combine([button, appliedTags]).SetClass("flex flex-col"), + State.state.layerUpdater.runningQuery + ) + super(t.hasBeenImported, withLoadingCheck, isImported) + } +} \ No newline at end of file diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index ef6ae1698..6af9e5a57 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -24,6 +24,8 @@ import Loc from "../Models/Loc"; import {Utils} from "../Utils"; import BaseLayer from "../Models/BaseLayer"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; +import ImportButton from "./BigComponents/ImportButton"; +import {Tag} from "../Logic/Tags/Tag"; export interface SpecialVisualization { funcName: string, @@ -65,7 +67,6 @@ export default class SpecialVisualizations { })).SetStyle("border: 1px solid black; border-radius: 1em;padding:1em;display:block;") }) }, - { funcName: "image_carousel", docs: "Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links)", @@ -87,7 +88,6 @@ export default class SpecialVisualizations { return new ImageCarousel(searcher, tags); } }, - { funcName: "image_upload", docs: "Creates a button where a user can upload an image to IMGUR", @@ -185,7 +185,7 @@ export default class SpecialVisualizations { { funcName: "reviews", docs: "Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten", - example: "{reviews()} for a vanilla review, {reviews(name, play_forest)} to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used", + example: "`{reviews()}` for a vanilla review, `{reviews(name, play_forest)}` to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used", args: [{ name: "subjectKey", defaultValue: "name", @@ -222,7 +222,6 @@ export default class SpecialVisualizations { return new OpeningHoursVisualization(tagSource, args[0]) } }, - { funcName: "live", docs: "Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)}", @@ -243,7 +242,6 @@ export default class SpecialVisualizations { return new VariableUiElement(source.map(data => data[neededValue] ?? "Loading...")); } }, - { funcName: "histogram", docs: "Create a histogram for a list of given values, read from the properties.", @@ -381,6 +379,75 @@ export default class SpecialVisualizations { [state.layoutToUse]) ) } + }, + { + funcName: "import_button", + args: [ + { + name: "tags", + doc: "Tags to copy-specification. This contains one or more pairs (seperated by a `;`), e.g. `amenity=fast_food; addr:housenumber=$number`. This new point will then have the tags `amenity=fast_food` and `addr:housenumber` with the value that was saved in `number` in the original feature. (Hint: prepare these values, e.g. with calculatedTags)" + }, + { + name: "text", + doc: "The text to show on the button", + defaultValue: "Import this data into OpenStreetMap" + }, + { + name: "icon", + doc: "A nice icon to show in the button", + defaultValue: "./assets/svg/addSmall.svg" + }], + docs: `This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes. + +If you want to import a dataset, make sure that: + +1. The dataset to import has a suitable license +2. The community has been informed of the import +3. All other requirements of the [import guidelines](https://wiki.openstreetmap.org/wiki/Import/Guidelines) have been followed + +There are also some technicalities in your theme to keep in mind: + +1. The new point will be added and will flow through the program as any other new point as if it came from OSM. + This means that there should be a layer which will match the new tags and which will display it. +2. The original point from your geojson layer will gain the tag '_imported=yes'. + This should be used to change the appearance or even to hide it (eg by changing the icon size to zero) +3. There should be a way for the theme to detect previously imported points, even after reloading. + A reference number to the original dataset is an excellen way to do this +`, + constr: (state, tagSource, args) => { + if (!state.layoutToUse.data.official && !state.featureSwitchIsTesting.data) { + return new Combine([new FixedUiElement("The import button is disabled for unofficial themes to prevent accidents.").SetClass("alert"), + new FixedUiElement("To test, add 'test=true' to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")]) + } + const tgsSpec = args[0].split(",").map(spec => { + const kv = spec.split("=").map(s => s.trim()); + if (kv.length != 2) { + throw "Invalid key spec: multiple '=' found in " + spec + } + return kv + }) + const rewrittenTags : UIEventSource = tagSource.map(tags => { + const newTags : Tag [] = [] + for (const [key, value] of tgsSpec) { + if (value.startsWith('$')) { + const origKey = value.substring(1) + newTags.push(new Tag(key, tags[origKey])) + } else { + newTags.push(new Tag(key, value)) + } + } + return newTags + }) + const id = tagSource.data.id; + const feature = State.state.allElements.ContainingFeatures.get(id) + if (feature.geometry.type !== "Point") { + return new FixedUiElement("Error: can only import point objects").SetClass("alert") + } + const [lon, lat] = feature.geometry.coordinates; + return new ImportButton( + args[2], args[1], tagSource, rewrittenTags, lat, lon + ) + } } ] @@ -399,7 +466,7 @@ export default class SpecialVisualizations { ), new Title("Example usage", 4), new FixedUiElement( - viz.example ?? "{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}" + viz.example ?? "`{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}`" ).SetClass("literal-code"), ] diff --git a/langs/en.json b/langs/en.json index e48e5805f..09709dc9f 100644 --- a/langs/en.json +++ b/langs/en.json @@ -100,7 +100,8 @@ "confirmIntro": "

Add a {title} here?

The point you create here will be visible for everyone. Please, only add things on to the map if they truly exist. A lot of applications use this data.", "confirmButton": "Add a {category} here.
Your addition is visible for everyone
", "openLayerControl": "Open the layer control box", - "layerNotEnabled": "The layer {layer} is not enabled. Enable this layer to add a point" + "layerNotEnabled": "The layer {layer} is not enabled. Enable this layer to add a point", + "hasBeenImported": "This point has already been imported" }, "pickLanguage": "Choose a language: ", "about": "Easily edit and add OpenStreetMap for a certain theme", From 47011370ddf3994f67a55eab33a111bfa170fdb1 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:32:40 +0200 Subject: [PATCH 008/110] Add more documentation and checks to the models --- Models/ThemeConfig/Json/LayerConfigJson.ts | 4 +++- Models/ThemeConfig/Json/TagRenderingConfigJson.ts | 4 ++++ Models/ThemeConfig/LayerConfig.ts | 12 ++++++++++++ Models/ThemeConfig/LayoutConfig.ts | 6 +++--- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index f03166d07..3068d25fd 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -84,7 +84,9 @@ export interface LayerConfigJson { /** * This tag rendering should either be 'yes' or 'no'. If 'no' is returned, then the feature will be hidden from view. - * This is useful to hide certain features from view. Important: hiding features does not work dynamically, but is only calculated when the data is first renders. + * This is useful to hide certain features from view. + * + * Important: hiding features does not work dynamically, but is only calculated when the data is first renders. * This implies that it is not possible to hide a feature after a tagging change * * The default value is 'yes' diff --git a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index 9152bf463..415c17cd1 100644 --- a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -79,6 +79,10 @@ export interface TagRenderingConfigJson { /** * If this condition is met, then the text under `then` will be shown. * If no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM. + * + * For example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'} + * + * This can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'} */ if: AndOrTagConfigJson | string, /** diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index ee8c836fe..c3cadc1e0 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -104,6 +104,10 @@ export default class LayerConfig { throw context + "Use 'geoJson' instead of 'geoJsonSource'"; } + if (json.source["geojson"] !== undefined) { + throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)"; + } + this.source = new SourceConfig( { osmTags: osmTags, @@ -133,6 +137,14 @@ export default class LayerConfig { const key = kv.substring(0, index); const code = kv.substring(index + 1); + try{ + + new Function("feat", "return " + code + ";"); + }catch(e){ + throw `Invalid function definition: code ${code} is invalid:${e} (at ${context})` + } + + this.calculatedTags.push([key, code]); } } diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index e34a83cb3..b57b6bab3 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -53,10 +53,10 @@ export default class LayoutConfig { public readonly cacheTimeout?: number; public readonly overpassUrl: string; public readonly overpassTimeout: number; - private readonly _official: boolean; + public readonly official: boolean; constructor(json: LayoutConfigJson, official = true, context?: string) { - this._official = official; + this.official = official; this.id = json.id; context = (context ?? "") + "." + this.id; this.maintainer = json.maintainer; @@ -221,7 +221,7 @@ export default class LayoutConfig { } public CustomCodeSnippets(): string[] { - if (this._official) { + if (this.official) { return []; } const msg = "
This layout uses custom javascript, loaded for the wide internet. The code is printed below, please report suspicious code on the issue tracker of MapComplete:
" From 0fa3a28a480c05055ceaa834f1e05789b7972a21 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:34:13 +0200 Subject: [PATCH 009/110] CreateNewNode action: fix crash if option are undefined --- Logic/Osm/Actions/CreateNewNodeAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Logic/Osm/Actions/CreateNewNodeAction.ts b/Logic/Osm/Actions/CreateNewNodeAction.ts index 4b127a7da..86cacab20 100644 --- a/Logic/Osm/Actions/CreateNewNodeAction.ts +++ b/Logic/Osm/Actions/CreateNewNodeAction.ts @@ -24,7 +24,7 @@ export default class CreateNewNodeAction extends OsmChangeAction { throw "Lat or lon are undefined!" } this._snapOnto = options?.snapOnto; - this._reusePointDistance = options.reusePointWithinMeters ?? 1 + this._reusePointDistance = options?.reusePointWithinMeters ?? 1 } CreateChangeDescriptions(changes: Changes): ChangeDescription[] { From 0848a57462098a3c55ee09ff1d98a77acc0ce748 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:45:44 +0200 Subject: [PATCH 010/110] First working version of the UK-addresses 'import' tool --- .../themes/uk_addresses/housenumber_add.svg | 289 +++ assets/themes/uk_addresses/housenumber_ok.svg | 75 + .../themes/uk_addresses/housenumber_text.svg | 72 + .../uk_addresses/housenumber_unknown.svg | 70 + .../islington_small_piece.geojson | 2163 +++++++++++++++++ assets/themes/uk_addresses/license_info.json | 43 + assets/themes/uk_addresses/uk_addresses.json | 245 ++ 7 files changed, 2957 insertions(+) create mode 100644 assets/themes/uk_addresses/housenumber_add.svg create mode 100644 assets/themes/uk_addresses/housenumber_ok.svg create mode 100644 assets/themes/uk_addresses/housenumber_text.svg create mode 100644 assets/themes/uk_addresses/housenumber_unknown.svg create mode 100644 assets/themes/uk_addresses/islington_small_piece.geojson create mode 100644 assets/themes/uk_addresses/license_info.json create mode 100644 assets/themes/uk_addresses/uk_addresses.json diff --git a/assets/themes/uk_addresses/housenumber_add.svg b/assets/themes/uk_addresses/housenumber_add.svg new file mode 100644 index 000000000..e156438b1 --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_add.svg @@ -0,0 +1,289 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/uk_addresses/housenumber_ok.svg b/assets/themes/uk_addresses/housenumber_ok.svg new file mode 100644 index 000000000..bf5f1b9db --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_ok.svg @@ -0,0 +1,75 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/assets/themes/uk_addresses/housenumber_text.svg b/assets/themes/uk_addresses/housenumber_text.svg new file mode 100644 index 000000000..56d57373e --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_text.svg @@ -0,0 +1,72 @@ + + + + + + image/svg+xml + + + + + + + + + OK diff --git a/assets/themes/uk_addresses/housenumber_unknown.svg b/assets/themes/uk_addresses/housenumber_unknown.svg new file mode 100644 index 000000000..0c6c0e5c4 --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_unknown.svg @@ -0,0 +1,70 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/assets/themes/uk_addresses/islington_small_piece.geojson b/assets/themes/uk_addresses/islington_small_piece.geojson new file mode 100644 index 000000000..ced6a1fb1 --- /dev/null +++ b/assets/themes/uk_addresses/islington_small_piece.geojson @@ -0,0 +1,2163 @@ + +{ + "type": "FeatureCollection", + "generator": "JOSM", + "features": [ + { + "type": "Feature", + "properties": { + "inspireid": "44760782", + "uprn_count": "19" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08528530407, + 51.52103754846 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760166", + "uprn_count": "18" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08518862375, + 51.52302887251 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53875715", + "uprn_count": "176" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08768220681, + 51.52027207654 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "48199892", + "uprn_count": "32" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09051161088, + 51.52328524465 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760298", + "uprn_count": "21" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08519096645, + 51.52229137569 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760648", + "uprn_count": "43" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09039984580, + 51.52168966695 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760158", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08979845152, + 51.52470373164 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760268", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08584518403, + 51.52362781792 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760606", + "uprn_count": "34" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08574285775, + 51.52447487890 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760147", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08478417875, + 51.52230226544 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760181", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08597751642, + 51.52262480980 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "59756691", + "uprn_count": "68" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09000674703, + 51.52412334790 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53839893", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08632490840, + 51.51956380364 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760254", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08445931332, + 51.52362994921 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760985", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08550522898, + 51.52481338112 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53517508", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08531729267, + 51.52055437518 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760172", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08621709906, + 51.52245066605 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53815743", + "uprn_count": "28" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08609559738, + 51.52000280555 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760229", + "uprn_count": "2" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08466859613, + 51.52247735237 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "52054693", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08869160634, + 51.52496110557 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760329", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08610136910, + 51.52318693588 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760753", + "uprn_count": "9" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08538513949, + 51.52424009690 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760164", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09008514341, + 51.52505292621 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760216", + "uprn_count": "190" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08700282926, + 51.52503663329 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760279", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08530920735, + 51.52549852437 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613792", + "uprn_count": "72" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08833908631, + 51.52631952661 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760639", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08580868931, + 51.52429800891 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760153", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08456440427, + 51.52329504288 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760200", + "uprn_count": "95" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08770188351, + 51.52407460026 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "47582675", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08892578863, + 51.52706921088 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760263", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08583263531, + 51.52548367268 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613655", + "uprn_count": "18" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08843733386, + 51.52669877413 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760564", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09046694451, + 51.52125764642 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760143", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08576039998, + 51.52479998964 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "57943078", + "uprn_count": "80" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08990077541, + 51.52590434415 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760178", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08479378350, + 51.52288142387 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760247", + "uprn_count": "16" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08570423028, + 51.52383831047 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "60347715", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08913137569, + 51.52638282816 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760169", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08741982002, + 51.52232472527 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53876178", + "uprn_count": "14" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08848929384, + 51.51968662476 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "58831132", + "uprn_count": "120" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08811742827, + 51.52524678003 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760702", + "uprn_count": "17" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08626679662, + 51.52229285532 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56996893", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08961930563, + 51.52736429296 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760161", + "uprn_count": "30" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08903973308, + 51.52443351442 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "57212602", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09025128250, + 51.52349660479 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760214", + "uprn_count": "30" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08491766007, + 51.52195690215 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760274", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08710416098, + 51.52394294309 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613782", + "uprn_count": "11" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08630939921, + 51.52567781493 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760635", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08591747289, + 51.52406421610 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760150", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08736624492, + 51.52184217908 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760190", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08950436522, + 51.52301269883 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53842347", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08629952517, + 51.51902467199 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760261", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08870422830, + 51.52481415717 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613643", + "uprn_count": "108" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08522752552, + 51.52590169102 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760506", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08680870061, + 51.52270885497 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53529840", + "uprn_count": "14" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08752332940, + 51.52076211974 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "55694053", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08582489157, + 51.52423214013 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760232", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08460415534, + 51.52252827681 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "55841333", + "uprn_count": "23" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09046401124, + 51.52500845422 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760366", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08665901033, + 51.52347731730 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760167", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08523817483, + 51.52199801036 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53875840", + "uprn_count": "21" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08790573136, + 51.52005170198 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760300", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08523971621, + 51.52550166079 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760673", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08740702488, + 51.52227145851 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760159", + "uprn_count": "37" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09015395806, + 51.52469077487 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760210", + "uprn_count": "10" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08735406455, + 51.52190815063 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760269", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08517606458, + 51.52360663718 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613767", + "uprn_count": "38" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08894666543, + 51.52728330710 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760630", + "uprn_count": "9" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08596436686, + 51.52399163027 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760148", + "uprn_count": "90" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08559244253, + 51.52179703997 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53555715", + "uprn_count": "23" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09069974009, + 51.52099643929 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "54843927", + "uprn_count": "36" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08536900467, + 51.52258204957 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760186", + "uprn_count": "60" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08749154622, + 51.52123763708 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "59797478", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08460319625, + 51.52285695300 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53842288", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08651616784, + 51.51911374360 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760258", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08880030229, + 51.52508388296 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760450", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08877219383, + 51.52489267275 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760989", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09008638232, + 51.52338474772 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53523418", + "uprn_count": "36" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08833393118, + 51.52076103473 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760173", + "uprn_count": "17" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08907044601, + 51.52478857712 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56354285", + "uprn_count": "41" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08708248675, + 51.51950432943 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53815989", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08712150084, + 51.52007985063 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760230", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08459866660, + 51.52256307871 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53894914", + "uprn_count": "53" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08932705394, + 51.52020780589 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760340", + "uprn_count": "57" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08700821071, + 51.52470369200 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760757", + "uprn_count": "94" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08629573687, + 51.52185922605 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760165", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08568241478, + 51.52253231799 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760289", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08494465189, + 51.52548899801 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760645", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08589810776, + 51.52411473063 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "57797640", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08496788091, + 51.52179114697 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56970529", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08673035105, + 51.52606173015 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760156", + "uprn_count": "11" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08743964400, + 51.52240633893 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53673626", + "uprn_count": "20" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08457962682, + 51.52315791127 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760202", + "uprn_count": "63" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08514280715, + 51.52153941124 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "47582725", + "uprn_count": "2" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08903179064, + 51.52700439325 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760266", + "uprn_count": "66" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08958919395, + 51.52486571171 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613662", + "uprn_count": "2" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09055456990, + 51.52704347329 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760589", + "uprn_count": "10" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08739339578, + 51.52221604989 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "47863432", + "uprn_count": "20" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08656652832, + 51.52377354927 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760179", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08735058986, + 51.52204111283 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760248", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08545068911, + 51.52547159555 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760404", + "uprn_count": "23" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08569047233, + 51.52316678822 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "60347880", + "uprn_count": "156" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08873887407, + 51.52589244765 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760981", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08508447894, + 51.52250703445 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53517488", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08540249619, + 51.52063943555 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760170", + "uprn_count": "23" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08950340680, + 51.52518881505 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53815719", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08580214812, + 51.51976909788 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760228", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08461864254, + 51.52260180398 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53877676", + "uprn_count": "29" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08850676500, + 51.52023902397 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760314", + "uprn_count": "15" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08872036555, + 51.52471439061 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760751", + "uprn_count": "180" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08677326589, + 51.52308738524 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760162", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08893070202, + 51.52527974813 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760215", + "uprn_count": "13" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08750749576, + 51.52270832689 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760278", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08509855123, + 51.52550722342 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613787", + "uprn_count": "11" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09045829084, + 51.52773739133 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760638", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08587468014, + 51.52417859166 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760151", + "uprn_count": "15" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08738616252, + 51.52175540266 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760195", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08577241806, + 51.52228063806 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760262", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08881139622, + 51.52515946758 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613654", + "uprn_count": "30" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08841210101, + 51.52660740062 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760559", + "uprn_count": "2" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09036979446, + 51.52201621806 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760051", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08651408714, + 51.52230407716 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53530202", + "uprn_count": "21" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08558143776, + 51.52017053246 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760177", + "uprn_count": "115" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09011378562, + 51.52279114581 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760235", + "uprn_count": "41" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08631542933, + 51.52256625361 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760839", + "uprn_count": "19" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.09069863118, + 51.52235527530 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760168", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08591546179, + 51.52346670045 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53876171", + "uprn_count": "112" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08780026547, + 51.51966975438 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760301", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08517005909, + 51.52550434483 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760686", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08683964335, + 51.52238336083 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760160", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08554780465, + 51.52227517161 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "54668028", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08868565970, + 51.52707814958 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760271", + "uprn_count": "9" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08511551871, + 51.52564108792 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613778", + "uprn_count": "4" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08556279304, + 51.52566116202 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "58775318", + "uprn_count": "5" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08476391706, + 51.52322955215 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760634", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08575556474, + 51.52523072818 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760149", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08736477713, + 51.52209660754 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "57099529", + "uprn_count": "14" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08747715586, + 51.52255778464 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760189", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08645676995, + 51.52485658336 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53842336", + "uprn_count": "16" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08644851873, + 51.51932417520 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "55728650", + "uprn_count": "15" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08606772431, + 51.51932747399 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760259", + "uprn_count": "7" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08879089932, + 51.52501718371 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56517732", + "uprn_count": "16" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08537944456, + 51.52045732895 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44613640", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08508351171, + 51.52602201951 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760498", + "uprn_count": "9" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08737904118, + 51.52215533460 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "60457716", + "uprn_count": "12" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08741608314, + 51.52162504468 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "50741600", + "uprn_count": "11" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08601118306, + 51.52237364065 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53528831", + "uprn_count": "18" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08796303430, + 51.52075390699 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760174", + "uprn_count": "3" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08975339083, + 51.52521825973 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "53815990", + "uprn_count": "6" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08698937756, + 51.52000181841 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760231", + "uprn_count": "1" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08485451783, + 51.52255188290 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "51082874", + "uprn_count": "15" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08930634369, + 51.52701309542 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "44760352", + "uprn_count": "14" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08734604831, + 51.52197314390 + ] + } + }, + { + "type": "Feature", + "properties": { + "inspireid": "56669648", + "uprn_count": "8" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.08851621061, + 51.52684922637 + ] + } + } + ] +} \ No newline at end of file diff --git a/assets/themes/uk_addresses/license_info.json b/assets/themes/uk_addresses/license_info.json new file mode 100644 index 000000000..0c6fb0715 --- /dev/null +++ b/assets/themes/uk_addresses/license_info.json @@ -0,0 +1,43 @@ +[ + { + "path": "housenumber_add.svg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + }, + { + "path": "housenumber_ok.svg", + "license": "CC0", + "authors": [ + "Tobias Zwick (westnordost)" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics", + "https://f-droid.org/packages/de.westnordost.streetcomplete/" + ] + }, + { + "path": "housenumber_text.svg", + "license": "CC0", + "authors": [ + "Tobias Zwick (westnordost)" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics", + "https://f-droid.org/packages/de.westnordost.streetcomplete/" + ] + }, + { + "path": "housenumber_unknown.svg", + "license": "CC0", + "authors": [ + "Tobias Zwick (westnordost)" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics", + "https://f-droid.org/packages/de.westnordost.streetcomplete/" + ] + } +] \ No newline at end of file diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json new file mode 100644 index 000000000..d97172c5d --- /dev/null +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -0,0 +1,245 @@ +{ + "id": "uk_addresses", + "title": { + "en": "UK Addresses" + }, + "shortDescription": { + "en": "Help to build an open dataset of UK addresses" + }, + "description": { + "en": "Contribute to OpenStreetMap by filling out address information" + }, + "language": [ + "en" + ], + "maintainer": "Pieter Vander Vennet, Rob Nickerson, Russ Garrett", + "icon": "./assets/themes/uk_addresses/housenumber_unknown.svg", + "version": "2021-09-17", + "startLat": -0.08528530407, + "startLon": 51.52103754846, + "startZoom": 18, + "widenFactor": 0.5, + "socialImage": "", + "layers": [ + { + "id": "to_import", + "source": { + "#geoJson": "http://127.0.0.1:8080/islington_small_piece.geojson", + "geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete/develop/assets/themes/uk_addresses/islington_small_piece.json", + "##geoJson": "https://raw.githubusercontent.com/russss/osm-uk-addresses/main/output/islington.geojson", + "osmTags": "inspireid~*", + "isOsmCache": false + }, + "name": "Addresses to check", + "minzoom": 12, + "wayHandling": 1, + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_unknown.svg" + }, + "iconSize": { + "render": "40,40,center", + "mappings": [ + { + "if": "_embedding_object:id~*", + "then": "15,15,center" + }, + { + "if": "_imported=yes", + "then": "8,8,center" + } + ] + }, + "title": { + "render": "Address to be determined" + }, + "tagRenderings": [ + { + "render": "There probably is an address here" + }, + { + "render": "An outline embedding this point with an address already exists in OpenStreetMap.
This object has address {_embedding_object:addr:street} {_embedding_object:addr:housenumber}", + "condition": "_embedding_object:id~*" + }, + { + "render": "{import_button(ref:inspireid=$inspireid, Add this address, ./assets/themes/uk_addresses/housenumber_add.svg)}" + }, + "all_tags" + ], + "calculatedTags": [ + "_embedding_object=feat.overlapWith('addresses')[0]?.feat?.properties ?? null", + "_embedding_object:addr:housenumber=JSON.parse(feat.properties._embedding_object)?.['addr:housenumber']", + "_embedding_object:addr:street=JSON.parse(feat.properties._embedding_object)?.['addr:street']", + "_embedding_object:id=JSON.parse(feat.properties._embedding_object)?.id" + ] + }, + { + "id": "addresses", + "name": { + "en": "Known addresses in OSM" + }, + "minzoom": 18, + "source": { + "osmTags": { + "or": [ + "addr:housenumber~*", + "addr:street~*", + "ref:inspireid~*" + ] + } + }, + "calculatedTags": [ + "_closest_3_street_names=feat.closestn('named_streets',3, 'name').map(f => ({name: f.feat.properties.name, distance: Math.round(1000*f.distance), id: f.id}))", + "_closest_street:0:name=JSON.parse(feat.properties._closest_3_street_names)[0]?.name", + "_closest_street:1:name=JSON.parse(feat.properties._closest_3_street_names)[1]?.name", + "_closest_street:2:name=JSON.parse(feat.properties._closest_3_street_names)[2]?.name", + "_closest_street:0:distance=JSON.parse(feat.properties._closest_3_street_names)[0]?.distance", + "_closest_street:1:distance=JSON.parse(feat.properties._closest_3_street_names)[1]?.distance", + "_closest_street:2:distance=JSON.parse(feat.properties._closest_3_street_names)[2]?.distance", + "_closest_street:0:id=JSON.parse(feat.properties._closest_3_street_names)[0]?.id", + "_closest_street:1:id=JSON.parse(feat.properties._closest_3_street_names)[1]?.id", + "_closest_street:2:id=JSON.parse(feat.properties._closest_3_street_names)[2]?.id" + ], + "title": { + "render": { + "en": "Known address" + } + }, + "description": { + "en": "Addresses" + }, + "tagRenderings": [ + { + "render": { + "en": "This address is saved in OpenStreetMap" + } + }, + { + "render": { + "en": "The housenumber is {addr:housenumber}" + }, + "question": { + "en": "What is the number of this house?" + }, + "freeform": { + "key": "addr:housenumber" + }, + "mappings": [ + { + "if": { + "and": [ + "nohousenumber=yes" + ] + }, + "then": { + "en": "This object has no house number" + } + } + ] + }, + { + "render": { + "en": "This address is in street {addr:street}" + }, + "question": { + "en": "What street is this address located in?" + }, + "freeform": { + "key": "addr:street" + }, + "mappings": [ + { + "if": "addr:street:={_closest_street:0:name}", + "then": "{_closest_street:0:name} {_closest_street:0:distance}m", + "hideInAnswer": "_closest_street:0:name=" + }, + { + "if": "addr:street:={_closest_street:1:name}", + "then": "{_closest_street:1:name} {_closest_street:1:distance}m", + "hideInAnswer": "_closest_street:1:name=" + }, + { + "if": "addr:street:={_closest_street:2:name}", + "then": "{_closest_street:2:name} {_closest_street:2:distance}m", + "hideInAnswer": "_closest_street:2:name=" + } + ], + "condition": { + "and": [ + "nohousenumber!~yes" + ] + } + } + ], + "icon": { + "render": "./assets/themes/uk_addresses/housenumber_ok.svg", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" + } + ] + }, + "width": { + "render": "8" + }, + "iconSize": { + "render": "40,40,center" + }, + "color": { + "render": "#00f", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "#ff0" + } + ] + }, + "presets": [] + }, + { + "id": "named_streets", + "name": "Named streets", + "minzoom": 18, + "source": { + "osmTags": { + "and": [ + "highway~*", + "name~*" + ] + } + }, + "title": { + "render": { + "en": "{name}" + } + }, + "color": { + "render": "#ccc" + }, + "width": { + "render": "3" + } + } + ], + "roamingRenderings": [] +} \ No newline at end of file From c88e69b04f1ac1818fb5d3accb01694d799d12c7 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:46:16 +0200 Subject: [PATCH 011/110] Fix version format in waste_basket theme --- assets/themes/waste_basket/waste_basket.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/themes/waste_basket/waste_basket.json b/assets/themes/waste_basket/waste_basket.json index 797e8d341..2a1ecef84 100644 --- a/assets/themes/waste_basket/waste_basket.json +++ b/assets/themes/waste_basket/waste_basket.json @@ -18,7 +18,7 @@ ], "maintainer": "", "icon": "./assets/themes/waste_basket/waste_basket.svg", - "version": "7/7/2021", + "version": "2021-07-07", "startLat": 0, "startLon": 0, "startZoom": 1, From e1cc24df2bcf1c98c76e7072422248a2917b3cd4 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:46:39 +0200 Subject: [PATCH 012/110] Add a default width to the first column of a table --- UI/Base/Table.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/Base/Table.ts b/UI/Base/Table.ts index 11e2fd871..a26f4a025 100644 --- a/UI/Base/Table.ts +++ b/UI/Base/Table.ts @@ -12,7 +12,7 @@ export default class Table extends BaseUIElement { contents: (BaseUIElement | string)[][], contentStyle?: string[][]) { super(); - this._contentStyle = contentStyle ?? []; + this._contentStyle = contentStyle ?? [["min-width: 9rem"]]; this._header = header?.map(Translations.W); this._contents = contents.map(row => row.map(Translations.W)); } From 6e9c8e29614e283f1a84e359c43c1ec97e9e6747 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 18 Sep 2021 02:47:22 +0200 Subject: [PATCH 013/110] small refactorings --- Logic/FeatureSource/GeoJsonSource.ts | 17 ++++++++++----- Logic/MetaTagging.ts | 3 +-- langs/themes/en.json | 32 ++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Logic/FeatureSource/GeoJsonSource.ts b/Logic/FeatureSource/GeoJsonSource.ts index 84a89327e..0c28e1488 100644 --- a/Logic/FeatureSource/GeoJsonSource.ts +++ b/Logic/FeatureSource/GeoJsonSource.ts @@ -168,20 +168,27 @@ export default class GeoJsonSource implements FeatureSource { let i = 0; let skipped = 0; for (const feature of json.features) { - if (feature.properties.id === undefined) { - feature.properties.id = url + "/" + i; + const props = feature.presets + for (const key in props) { + if(typeof props[key] !== "string"){ + props[key] = ""+props[key] + } + } + + if (props.id === undefined) { + props.id = url + "/" + i; feature.id = url + "/" + i; i++; } - if (self.seenids.has(feature.properties.id)) { + if (self.seenids.has(props.id)) { skipped++; continue; } - self.seenids.add(feature.properties.id) + self.seenids.add(props.id) let freshness: Date = time; if (feature.properties["_last_edit:timestamp"] !== undefined) { - freshness = new Date(feature.properties["_last_edit:timestamp"]) + freshness = new Date(props["_last_edit:timestamp"]) } newFeatures.push({feature: feature, freshness: freshness}) diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index 5d55518ba..1f614cfec 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -103,11 +103,10 @@ export default class MetaTagging { if (code === undefined) { continue; } + const func = new Function("feat", "return " + code + ";"); try { - - const f = (featuresPerLayer, feature: any) => { try { let result = func(feature); diff --git a/langs/themes/en.json b/langs/themes/en.json index 2a8c449cd..94eaf954b 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1324,6 +1324,38 @@ "shortDescription": "Map all the trees", "title": "Trees" }, + "uk_addresses": { + "description": "Contribute to OpenStreetMap by filling out address information", + "layers": { + "1": { + "description": "Addresses", + "name": "Known addresses in OSM", + "tagRenderings": { + "0": { + "render": "This address is saved in OpenStreetMap" + }, + "1": { + "mappings": { + "0": { + "then": "This building has no house number" + } + }, + "question": "What is the number of this house?", + "render": "The housenumber is {addr:housenumber}" + }, + "2": { + "question": "What street is this address located in?", + "render": "This address is in street {addr:street}" + } + }, + "title": { + "render": "Known address" + } + } + }, + "shortDescription": "Help to build an open dataset of UK addresses", + "title": "UK Addresses" + }, "waste_basket": { "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", From 77932973487138cf8bac164e05b1459d86a0e8ec Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 19 Sep 2021 00:27:03 +0200 Subject: [PATCH 014/110] Fix link --- assets/themes/uk_addresses/uk_addresses.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index d97172c5d..552f9c1eb 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -25,7 +25,7 @@ "id": "to_import", "source": { "#geoJson": "http://127.0.0.1:8080/islington_small_piece.geojson", - "geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete/develop/assets/themes/uk_addresses/islington_small_piece.json", + "geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete/develop/assets/themes/uk_addresses/islington_small_piece.geojson", "##geoJson": "https://raw.githubusercontent.com/russss/osm-uk-addresses/main/output/islington.geojson", "osmTags": "inspireid~*", "isOsmCache": false From 973b5d8bbecfd0a4a8ddcff1d8e81a62d356e5e4 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 20 Sep 2021 17:14:55 +0200 Subject: [PATCH 015/110] Huge refactoring of the feature pipeline, WIP --- ...Resetter.ts => BackgroundLayerResetter.ts} | 0 .../Actors/LocalStorageSaverActor.ts | 35 +++ .../RegisteringAllFromFeatureSourceActor.ts} | 2 +- .../FeatureDuplicatorPerLayer.ts | 64 ------ Logic/FeatureSource/FilteringFeatureSource.ts | 162 -------------- Logic/FeatureSource/GeoJsonSource.ts | 207 ------------------ Logic/FeatureSource/LocalStorageSaver.ts | 41 ---- .../FeatureSource/MetaTaggingFeatureSource.ts | 52 ----- .../PerLayerFeatureSourceSplitter.ts | 87 ++++++++ .../{ => Sources}/FeatureSourceMerger.ts | 47 ++-- .../Sources/FilteringFeatureSource.ts | 101 +++++++++ Logic/FeatureSource/Sources/GeoJsonSource.ts | 95 ++++++++ .../{ => Sources}/LocalStorageSource.ts | 4 +- .../{ => Sources}/OsmApiFeatureSource.ts | 27 ++- .../{ => Sources}/RememberingSource.ts | 18 +- .../Sources/SimpleFeatureSource.ts | 16 ++ .../WayHandlingApplyingFeatureSource.ts | 40 ++-- .../DynamicGeoJsonTileSource.ts | 0 .../TiledFeatureSource/DynamicTileSource.ts | 72 ++++++ .../TiledFeatureSource/README.md | 3 + .../TiledFeatureSource/SingleLayerSource.ts | 0 .../TiledFeatureSource/TileHierarchy.ts | 0 .../TiledFeatureSource/TiledFeatureSource.ts | 0 .../TiledFromLocalStorageSource.ts | 40 ++++ ...xtractRelations.ts => RelationsTracker.ts} | 0 25 files changed, 522 insertions(+), 591 deletions(-) rename Logic/Actors/{LayerResetter.ts => BackgroundLayerResetter.ts} (100%) create mode 100644 Logic/FeatureSource/Actors/LocalStorageSaverActor.ts rename Logic/FeatureSource/{RegisteringFeatureSource.ts => Actors/RegisteringAllFromFeatureSourceActor.ts} (88%) delete mode 100644 Logic/FeatureSource/FeatureDuplicatorPerLayer.ts delete mode 100644 Logic/FeatureSource/FilteringFeatureSource.ts delete mode 100644 Logic/FeatureSource/GeoJsonSource.ts delete mode 100644 Logic/FeatureSource/LocalStorageSaver.ts delete mode 100644 Logic/FeatureSource/MetaTaggingFeatureSource.ts create mode 100644 Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts rename Logic/FeatureSource/{ => Sources}/FeatureSourceMerger.ts (62%) create mode 100644 Logic/FeatureSource/Sources/FilteringFeatureSource.ts create mode 100644 Logic/FeatureSource/Sources/GeoJsonSource.ts rename Logic/FeatureSource/{ => Sources}/LocalStorageSource.ts (91%) rename Logic/FeatureSource/{ => Sources}/OsmApiFeatureSource.ts (85%) rename Logic/FeatureSource/{ => Sources}/RememberingSource.ts (74%) create mode 100644 Logic/FeatureSource/Sources/SimpleFeatureSource.ts rename Logic/FeatureSource/{ => Sources}/WayHandlingApplyingFeatureSource.ts (61%) create mode 100644 Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts create mode 100644 Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts create mode 100644 Logic/FeatureSource/TiledFeatureSource/README.md create mode 100644 Logic/FeatureSource/TiledFeatureSource/SingleLayerSource.ts create mode 100644 Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts create mode 100644 Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts create mode 100644 Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts rename Logic/Osm/{ExtractRelations.ts => RelationsTracker.ts} (100%) diff --git a/Logic/Actors/LayerResetter.ts b/Logic/Actors/BackgroundLayerResetter.ts similarity index 100% rename from Logic/Actors/LayerResetter.ts rename to Logic/Actors/BackgroundLayerResetter.ts diff --git a/Logic/FeatureSource/Actors/LocalStorageSaverActor.ts b/Logic/FeatureSource/Actors/LocalStorageSaverActor.ts new file mode 100644 index 000000000..9d0cb6e3b --- /dev/null +++ b/Logic/FeatureSource/Actors/LocalStorageSaverActor.ts @@ -0,0 +1,35 @@ +import {FeatureSourceForLayer} from "./FeatureSource"; +import {Utils} from "../../Utils"; + +/*** + * Saves all the features that are passed in to localstorage, so they can be retrieved on the next run + * + * Technically, more an Actor then a featuresource, but it fits more neatly this ay + */ +export default class LocalStorageSaverActor { + public static readonly storageKey: string = "cached-features"; + + constructor(source: FeatureSourceForLayer, x: number, y: number, z: number) { + source.features.addCallbackAndRunD(features => { + const index = Utils.tile_index(z, x, y) + const key = `${LocalStorageSaverActor.storageKey}-${source.layer.layerDef.id}-${index}` + const now = new Date().getTime() + + if (features.length == 0) { + return; + } + + try { + localStorage.setItem(key, JSON.stringify(features)); + console.log("Saved ", features.length, "elements to", key) + localStorage.setItem(key + "-time", JSON.stringify(now)) + } catch (e) { + console.warn("Could not save the features to local storage:", e) + } + }) + + + } + + +} \ No newline at end of file diff --git a/Logic/FeatureSource/RegisteringFeatureSource.ts b/Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts similarity index 88% rename from Logic/FeatureSource/RegisteringFeatureSource.ts rename to Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts index e464a60b8..b3b921195 100644 --- a/Logic/FeatureSource/RegisteringFeatureSource.ts +++ b/Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts @@ -2,7 +2,7 @@ import FeatureSource from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; import State from "../../State"; -export default class RegisteringFeatureSource implements FeatureSource { +export default class RegisteringAllFromFeatureSourceActor { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name; diff --git a/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts b/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts deleted file mode 100644 index 87d91bb0c..000000000 --- a/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts +++ /dev/null @@ -1,64 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import FilteredLayer from "../../Models/FilteredLayer"; - - -/** - * In some rare cases, some elements are shown on multiple layers (when 'passthrough' is enabled) - * If this is the case, multiple objects with a different _matching_layer_id are generated. - * In any case, this featureSource marks the objects with _matching_layer_id - */ -export default class FeatureDuplicatorPerLayer implements FeatureSource { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - - public readonly name; - - constructor(layers: UIEventSource, upstream: FeatureSource) { - this.name = "FeatureDuplicator of " + upstream.name; - this.features = upstream.features.map(features => { - const newFeatures: { feature: any, freshness: Date }[] = []; - if (features === undefined) { - return newFeatures; - } - - for (const f of features) { - if (f.feature._matching_layer_id) { - // Already matched previously - // We simply add it - newFeatures.push(f); - continue; - } - - - let foundALayer = false; - for (const layer of layers.data) { - if (layer.layerDef.source.osmTags.matchesProperties(f.feature.properties)) { - foundALayer = true; - if (layer.layerDef.passAllFeatures) { - - // We copy the feature; the "properties" field is kept identical though! - // Keeping "properties" identical is needed, as it might break the 'allElementStorage' otherwise - const newFeature = { - geometry: f.feature.geometry, - id: f.feature.id, - type: f.feature.type, - properties: f.feature.properties, - _matching_layer_id: layer.layerDef.id - } - newFeatures.push({feature: newFeature, freshness: f.freshness}); - } else { - // If not 'passAllFeatures', we are done - f.feature._matching_layer_id = layer.layerDef.id; - newFeatures.push(f); - break; - } - } - } - } - return newFeatures; - - }) - - } - -} \ No newline at end of file diff --git a/Logic/FeatureSource/FilteringFeatureSource.ts b/Logic/FeatureSource/FilteringFeatureSource.ts deleted file mode 100644 index 718f711b1..000000000 --- a/Logic/FeatureSource/FilteringFeatureSource.ts +++ /dev/null @@ -1,162 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import Loc from "../../Models/Loc"; -import Hash from "../Web/Hash"; -import {TagsFilter} from "../Tags/TagsFilter"; -import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; - -export default class FilteringFeatureSource implements FeatureSource { - public features: UIEventSource<{ feature: any; freshness: Date }[]> = - new UIEventSource<{ feature: any; freshness: Date }[]>([]); - public readonly name = "FilteringFeatureSource"; - - constructor( - layers: UIEventSource<{ - isDisplayed: UIEventSource; - layerDef: LayerConfig; - appliedFilters: UIEventSource; - }[]>, - location: UIEventSource, - selectedElement: UIEventSource, - upstream: FeatureSource - ) { - const self = this; - - function update() { - const layerDict = {}; - if (layers.data.length == 0) { - console.warn("No layers defined!"); - return; - } - for (const layer of layers.data) { - const prev = layerDict[layer.layerDef.id] - if (prev !== undefined) { - // We have seen this layer before! - // We prefer the one which has a name - if (layer.layerDef.name === undefined) { - // This one is hidden, so we skip it - console.log("Ignoring layer selection from ", layer) - continue; - } - } - layerDict[layer.layerDef.id] = layer; - } - - const features: { feature: any; freshness: Date }[] = - upstream.features.data; - - const missingLayers = new Set(); - - const newFeatures = features.filter((f) => { - const layerId = f.feature._matching_layer_id; - - if ( - selectedElement.data?.id === f.feature.id || - f.feature.id === Hash.hash.data) { - // This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away - return true; - } - - if (layerId === undefined) { - return false; - } - const layer: { - isDisplayed: UIEventSource; - layerDef: LayerConfig; - appliedFilters: UIEventSource; - } = layerDict[layerId]; - if (layer === undefined) { - missingLayers.add(layerId); - return false; - } - - const isShown = layer.layerDef.isShown; - const tags = f.feature.properties; - if (isShown.IsKnown(tags)) { - const result = layer.layerDef.isShown.GetRenderValue( - f.feature.properties - ).txt; - if (result !== "yes") { - return false; - } - } - - const tagsFilter = layer.appliedFilters.data; - if (tagsFilter) { - if (!tagsFilter.matchesProperties(f.feature.properties)) { - // Hidden by the filter on the layer itself - we want to hide it no matter wat - return false; - } - } - if (!FilteringFeatureSource.showLayer(layer, location)) { - // The layer itself is either disabled or hidden due to zoom constraints - // We should return true, but it might still match some other layer - return false; - } - - return true; - }); - - self.features.setData(newFeatures); - if (missingLayers.size > 0) { - console.error( - "Some layers were not found: ", - Array.from(missingLayers) - ); - } - } - - upstream.features.addCallback(() => { - update(); - }); - location - .map((l) => { - // We want something that is stable for the shown layers - const displayedLayerIndexes = []; - for (let i = 0; i < layers.data.length; i++) { - const layer = layers.data[i]; - if (l.zoom < layer.layerDef.minzoom) { - continue; - } - - if (!layer.isDisplayed.data) { - continue; - } - displayedLayerIndexes.push(i); - } - return displayedLayerIndexes.join(","); - }) - .addCallback(() => { - update(); - }); - - layers.addCallback(update); - - const registered = new Set>(); - layers.addCallbackAndRun((layers) => { - for (const layer of layers) { - if (registered.has(layer.isDisplayed)) { - continue; - } - registered.add(layer.isDisplayed); - layer.isDisplayed.addCallback(() => update()); - layer.appliedFilters.addCallback(() => update()); - } - }); - - update(); - } - - private static showLayer( - layer: { - isDisplayed: UIEventSource; - layerDef: LayerConfig; - }, - location: UIEventSource - ) { - return ( - layer.isDisplayed.data && - layer.layerDef.minzoomVisible <= location.data.zoom - ); - } -} diff --git a/Logic/FeatureSource/GeoJsonSource.ts b/Logic/FeatureSource/GeoJsonSource.ts deleted file mode 100644 index 0c28e1488..000000000 --- a/Logic/FeatureSource/GeoJsonSource.ts +++ /dev/null @@ -1,207 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import Loc from "../../Models/Loc"; -import State from "../../State"; -import {Utils} from "../../Utils"; -import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; - - -/** - * Fetches a geojson file somewhere and passes it along - */ -export default class GeoJsonSource implements FeatureSource { - - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - public readonly name; - public readonly isOsmCache: boolean - private onFail: ((errorMsg: any, url: string) => void) = undefined; - private readonly layerId: string; - private readonly seenids: Set = new Set() - - private constructor(locationControl: UIEventSource, - flayer: { isDisplayed: UIEventSource, layerDef: LayerConfig }, - onFail?: ((errorMsg: any) => void)) { - this.layerId = flayer.layerDef.id; - let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id); - this.name = "GeoJsonSource of " + url; - const zoomLevel = flayer.layerDef.source.geojsonZoomLevel; - - this.isOsmCache = flayer.layerDef.source.isOsmCacheLayer; - - this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) - - if (zoomLevel === undefined) { - // This is a classic, static geojson layer - if (onFail === undefined) { - onFail = _ => { - } - } - this.onFail = onFail; - - this.LoadJSONFrom(url) - } else { - this.ConfigureDynamicLayer(url, zoomLevel, locationControl, flayer) - } - } - - /** - * Merges together the layers which have the same source - * @param flayers - * @param locationControl - * @constructor - */ - public static ConstructMultiSource(flayers: { isDisplayed: UIEventSource, layerDef: LayerConfig }[], locationControl: UIEventSource): GeoJsonSource[] { - - const flayersPerSource = new Map, layerDef: LayerConfig }[]>(); - for (const flayer of flayers) { - const url = flayer.layerDef.source.geojsonSource?.replace(/{layer}/g, flayer.layerDef.id) - if (url === undefined) { - continue; - } - - if (!flayersPerSource.has(url)) { - flayersPerSource.set(url, []) - } - flayersPerSource.get(url).push(flayer) - } - - const sources: GeoJsonSource[] = [] - - flayersPerSource.forEach((flayers, key) => { - if (flayers.length == 1) { - sources.push(new GeoJsonSource(locationControl, flayers[0])); - return; - } - - const zoomlevels = Utils.Dedup(flayers.map(flayer => "" + (flayer.layerDef.source.geojsonZoomLevel ?? ""))) - if (zoomlevels.length > 1) { - throw "Multiple zoomlevels defined for same geojson source " + key - } - - let isShown = new UIEventSource(true, "IsShown for multiple layers: or of multiple values"); - for (const flayer of flayers) { - flayer.isDisplayed.addCallbackAndRun(() => { - let value = false; - for (const flayer of flayers) { - value = flayer.isDisplayed.data || value; - } - isShown.setData(value); - }); - - } - - const source = new GeoJsonSource(locationControl, { - isDisplayed: isShown, - layerDef: flayers[0].layerDef // We only care about the source info here - }) - sources.push(source) - - }) - return sources; - - } - - private ConfigureDynamicLayer(url: string, zoomLevel: number, locationControl: UIEventSource, flayer: { isDisplayed: UIEventSource, layerDef: LayerConfig }) { - // This is a dynamic template with a fixed zoom level - url = url.replace("{z}", "" + zoomLevel) - const loadedTiles = new Set(); - const self = this; - this.onFail = (msg, url) => { - console.warn(`Could not load geojson layer from`, url, "due to", msg) - loadedTiles.add(url); // We add the url to the 'loadedTiles' in order to not reload it in the future - } - - const neededTiles = locationControl.map( - location => { - if (!flayer.isDisplayed.data) { - // No need to download! - the layer is disabled - return undefined; - } - - if (location.zoom < flayer.layerDef.minzoom) { - // No need to download! - the layer is disabled - return undefined; - } - - // Yup, this is cheating to just get the bounds here - const bounds = State.state.leafletMap.data?.getBounds() - if(bounds === undefined){ - // We'll retry later - return undefined - } - const tileRange = Utils.TileRangeBetween(zoomLevel, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) - const needed = Utils.MapRange(tileRange, (x, y) => { - return url.replace("{x}", "" + x).replace("{y}", "" + y); - }) - return new Set(needed); - } - , [flayer.isDisplayed, State.state.leafletMap]); - neededTiles.stabilized(250).addCallback((needed: Set) => { - if (needed === undefined) { - return; - } - - needed.forEach(neededTile => { - if (loadedTiles.has(neededTile)) { - return; - } - - loadedTiles.add(neededTile) - self.LoadJSONFrom(neededTile) - - }) - }) - - } - - private LoadJSONFrom(url: string) { - const eventSource = this.features; - const self = this; - Utils.downloadJson(url) - .then(json => { - if (json.elements === [] && json.remarks.indexOf("runtime error") > 0) { - self.onFail("Runtime error (timeout)", url) - return; - } - const time = new Date(); - const newFeatures: { feature: any, freshness: Date } [] = [] - let i = 0; - let skipped = 0; - for (const feature of json.features) { - const props = feature.presets - for (const key in props) { - if(typeof props[key] !== "string"){ - props[key] = ""+props[key] - } - } - - if (props.id === undefined) { - props.id = url + "/" + i; - feature.id = url + "/" + i; - i++; - } - if (self.seenids.has(props.id)) { - skipped++; - continue; - } - self.seenids.add(props.id) - - let freshness: Date = time; - if (feature.properties["_last_edit:timestamp"] !== undefined) { - freshness = new Date(props["_last_edit:timestamp"]) - } - - newFeatures.push({feature: feature, freshness: freshness}) - } - console.debug("Downloaded " + newFeatures.length + " new features and " + skipped + " already seen features from " + url); - - if (newFeatures.length == 0) { - return; - } - - eventSource.setData(eventSource.data.concat(newFeatures)) - - }).catch(msg => self.onFail(msg, url)) - } - -} diff --git a/Logic/FeatureSource/LocalStorageSaver.ts b/Logic/FeatureSource/LocalStorageSaver.ts deleted file mode 100644 index 354adb75a..000000000 --- a/Logic/FeatureSource/LocalStorageSaver.ts +++ /dev/null @@ -1,41 +0,0 @@ -/*** - * Saves all the features that are passed in to localstorage, so they can be retrieved on the next run - * - * Technically, more an Actor then a featuresource, but it fits more neatly this ay - */ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; - -export default class LocalStorageSaver implements FeatureSource { - public static readonly storageKey: string = "cached-features"; - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - - public readonly name = "LocalStorageSaver"; - - constructor(source: FeatureSource, layout: UIEventSource) { - this.features = source.features; - - this.features.addCallbackAndRunD(features => { - const now = new Date().getTime() - features = features.filter(f => layout.data.cacheTimeout > Math.abs(now - f.freshness.getTime()) / 1000) - - - if (features.length == 0) { - return; - } - - try { - const key = LocalStorageSaver.storageKey + layout.data.id - localStorage.setItem(key, JSON.stringify(features)); - console.log("Saved ", features.length, "elements to", key) - } catch (e) { - console.warn("Could not save the features to local storage:", e) - } - }) - - - } - - -} \ No newline at end of file diff --git a/Logic/FeatureSource/MetaTaggingFeatureSource.ts b/Logic/FeatureSource/MetaTaggingFeatureSource.ts deleted file mode 100644 index 3ae5c9015..000000000 --- a/Logic/FeatureSource/MetaTaggingFeatureSource.ts +++ /dev/null @@ -1,52 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import State from "../../State"; -import Hash from "../Web/Hash"; -import MetaTagging from "../MetaTagging"; - -export default class MetaTaggingFeatureSource implements FeatureSource { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>(undefined); - - public readonly name; - - /*** - * Constructs a new metatagger which'll calculate various tags - * @param allFeaturesSource: A source where all the currently known features can be found - used to calculate overlaps etc - * @param source: the source of features that should get their metatag and which should be exported again - * @param updateTrigger - */ - constructor(allFeaturesSource: UIEventSource<{ feature: any; freshness: Date }[]>, source: FeatureSource, updateTrigger?: UIEventSource) { - const self = this; - this.name = "MetaTagging of " + source.name - - if (allFeaturesSource === undefined) { - throw ("UIEVentSource is undefined") - } - - function update() { - const featuresFreshness = source.features.data - if (featuresFreshness === undefined) { - return; - } - featuresFreshness.forEach(featureFresh => { - const feature = featureFresh.feature; - - if (Hash.hash.data === feature.properties.id) { - State.state.selectedElement.setData(feature); - } - }) - - MetaTagging.addMetatags(featuresFreshness, - allFeaturesSource, - State.state.knownRelations.data, State.state.layoutToUse.data.layers); - self.features.setData(featuresFreshness); - } - - source.features.addCallbackAndRun(_ => update()); - updateTrigger?.addCallback(_ => { - console.debug("Updating because of external call") - update(); - }) - } - -} \ No newline at end of file diff --git a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts new file mode 100644 index 000000000..296f318b7 --- /dev/null +++ b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts @@ -0,0 +1,87 @@ +import FeatureSource from "./FeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import FilteredLayer from "../../Models/FilteredLayer"; +import OverpassFeatureSource from "../Actors/OverpassFeatureSource"; +import SimpleFeatureSource from "./SimpleFeatureSource"; + + +/** + * In some rare cases, some elements are shown on multiple layers (when 'passthrough' is enabled) + * If this is the case, multiple objects with a different _matching_layer_id are generated. + * In any case, this featureSource marks the objects with _matching_layer_id + */ +export default class PerLayerFeatureSourceSplitter { + + constructor(layers: UIEventSource, + handleLayerData: (source: FeatureSource) => void, + upstream: OverpassFeatureSource) { + + const knownLayers = new Map() + + function update() { + const features = upstream.features.data; + if (features === undefined) { + return; + } + if(layers.data === undefined){ + return; + } + + // We try to figure out (for each feature) in which feature store it should be saved. + // Note that this splitter is only run when it is invoked by the overpass feature source, so we can't be sure in which layer it should go + + const featuresPerLayer = new Map(); + + function addTo(layer: FilteredLayer, feature: { feature, freshness }) { + const id = layer.layerDef.id + const list = featuresPerLayer.get(id) + if (list !== undefined) { + list.push(feature) + } else { + featuresPerLayer.set(id, [feature]) + } + } + + for (const f of features) { + for (const layer of layers.data) { + if (layer.layerDef.source.osmTags.matchesProperties(f.feature.properties)) { + // We have found our matching layer! + addTo(layer, f) + if (!layer.layerDef.passAllFeatures) { + // If not 'passAllFeatures', we are done for this feature + break; + } + } + } + } + + // At this point, we have our features per layer as a list + // We assign them to the correct featureSources + for (const layer of layers.data) { + const id = layer.layerDef.id; + const features = featuresPerLayer.get(id) + if (features === undefined) { + // No such features for this layer + continue; + } + + let featureSource = knownLayers.get(id) + if (featureSource === undefined) { + // Not yet initialized - now is a good time + featureSource = new SimpleFeatureSource(layer) + knownLayers.set(id, featureSource) + handleLayerData(featureSource) + } + featureSource.features.setData(features) + } + + + upstream.features.addCallbackAndRunD(_ => update()) + layers.addCallbackAndRunD(_ => update()) + + } + + layers.addCallbackAndRunD(_ => update()) + upstream.features.addCallbackAndRunD(_ => update()) + } +} \ No newline at end of file diff --git a/Logic/FeatureSource/FeatureSourceMerger.ts b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts similarity index 62% rename from Logic/FeatureSource/FeatureSourceMerger.ts rename to Logic/FeatureSource/Sources/FeatureSourceMerger.ts index 3d1b794c6..17d6db690 100644 --- a/Logic/FeatureSource/FeatureSourceMerger.ts +++ b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts @@ -1,27 +1,44 @@ -import FeatureSource from "./FeatureSource"; +import FeatureSource, {FeatureSourceForLayer} from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; +import FilteredLayer from "../../Models/FilteredLayer"; /** - * Merges features from different featureSources + * Merges features from different featureSources for a single layer * Uses the freshest feature available in the case multiple sources offer data with the same identifier */ -export default class FeatureSourceMerger implements FeatureSource { +export default class FeatureSourceMerger implements FeatureSourceForLayer { public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name; - private readonly _sources: FeatureSource[]; + public readonly layer: FilteredLayer + private readonly _sources: UIEventSource; - constructor(sources: FeatureSource[]) { + constructor(layer: FilteredLayer ,sources: UIEventSource) { this._sources = sources; - this.name = "SourceMerger of (" + sources.map(s => s.name).join(", ") + ")" + this.layer = layer; + this.name = "SourceMerger" const self = this; - for (let i = 0; i < sources.length; i++) { - let source = sources[i]; - source.features.addCallback(() => { - self.Update(); - }); - } - this.Update(); + + const handledSources = new Set(); + + sources.addCallbackAndRunD(sources => { + let newSourceRegistered = false; + for (let i = 0; i < sources.length; i++) { + let source = sources[i]; + if (handledSources.has(source)) { + continue + } + handledSources.add(source) + newSourceRegistered = true + source.features.addCallback(() => { + self.Update(); + }); + if (newSourceRegistered) { + self.Update(); + } + } + }) + } private Update() { @@ -34,7 +51,7 @@ export default class FeatureSourceMerger implements FeatureSource { all.set(oldValue.feature.id + oldValue.feature._matching_layer_id, oldValue) } - for (const source of this._sources) { + for (const source of this._sources.data) { if (source?.features?.data === undefined) { continue; } @@ -64,7 +81,7 @@ export default class FeatureSourceMerger implements FeatureSource { } const newList = []; - all.forEach((value, key) => { + all.forEach((value, _) => { newList.push(value) }) this.features.setData(newList); diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts new file mode 100644 index 000000000..67a12af35 --- /dev/null +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -0,0 +1,101 @@ +import {FeatureSourceForLayer} from "./FeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import Hash from "../Web/Hash"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import FilteredLayer from "../../Models/FilteredLayer"; + +export default class FilteringFeatureSource implements FeatureSourceForLayer { + public features: UIEventSource<{ feature: any; freshness: Date }[]> = + new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name = "FilteringFeatureSource"; + public readonly layer: FilteredLayer; + + constructor( + state: { + locationControl: UIEventSource<{ zoom: number }>, + selectedElement: UIEventSource, + }, + upstream: FeatureSourceForLayer + ) { + const self = this; + + this.layer = upstream.layer; + const layer = upstream.layer; + + function update() { + const features: { feature: any; freshness: Date }[] = upstream.features.data; + const newFeatures = features.filter((f) => { + if ( + state.selectedElement.data?.id === f.feature.id || + f.feature.id === Hash.hash.data) { + // This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away + return true; + } + + const isShown = layer.layerDef.isShown; + const tags = f.feature.properties; + if (isShown.IsKnown(tags)) { + const result = layer.layerDef.isShown.GetRenderValue( + f.feature.properties + ).txt; + if (result !== "yes") { + return false; + } + } + + const tagsFilter = layer.appliedFilters.data; + if (tagsFilter) { + if (!tagsFilter.matchesProperties(f.feature.properties)) { + // Hidden by the filter on the layer itself - we want to hide it no matter wat + return false; + } + } + if (!FilteringFeatureSource.showLayer(layer, state.locationControl.data)) { + // The layer itself is either disabled or hidden due to zoom constraints + // We should return true, but it might still match some other layer + return false; + } + return true; + }); + + self.features.setData(newFeatures); + } + + upstream.features.addCallback(() => { + update(); + }); + + let isShown = state.locationControl.map((l) => FilteringFeatureSource.showLayer(layer, l), + [layer.isDisplayed]) + + isShown.addCallback(isShown => { + if (isShown) { + update(); + } else { + self.features.setData([]) + } + }); + + layer.appliedFilters.addCallback(_ => { + if(!isShown.data){ + // Currently not shown. + // Note that a change in 'isSHown' will trigger an update as well, so we don't have to watch it another time + return; + } + update() + }) + + update(); + } + + private static showLayer( + layer: { + isDisplayed: UIEventSource; + layerDef: LayerConfig; + }, + location: { zoom: number }) { + return layer.isDisplayed.data && + layer.layerDef.minzoomVisible <= location.zoom; + + } +} diff --git a/Logic/FeatureSource/Sources/GeoJsonSource.ts b/Logic/FeatureSource/Sources/GeoJsonSource.ts new file mode 100644 index 000000000..165f672cf --- /dev/null +++ b/Logic/FeatureSource/Sources/GeoJsonSource.ts @@ -0,0 +1,95 @@ +import {FeatureSourceForLayer} from "./FeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import {Utils} from "../../Utils"; +import FilteredLayer from "../../Models/FilteredLayer"; +import {control} from "leaflet"; + + +/** + * Fetches a geojson file somewhere and passes it along + */ +export default class GeoJsonSource implements FeatureSourceForLayer { + + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; + public readonly name; + public readonly isOsmCache: boolean + private onFail: ((errorMsg: any, url: string) => void) = undefined; + private readonly seenids: Set = new Set() + public readonly layer: FilteredLayer; + + + public constructor(flayer: FilteredLayer, + zxy?: [number, number, number]) { + + if (flayer.layerDef.source.geojsonZoomLevel !== undefined && zxy === undefined) { + throw "Dynamic layers are not supported. Use 'DynamicGeoJsonTileSource instead" + } + + this.layer = flayer; + let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id); + if (zxy !== undefined) { + url = url + .replace('{z}', "" + zxy[0]) + .replace('{x}', "" + zxy[1]) + .replace('{y}', "" + zxy[2]) + } + + this.name = "GeoJsonSource of " + url; + + this.isOsmCache = flayer.layerDef.source.isOsmCacheLayer; + this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) + this.LoadJSONFrom(url) + } + + + private LoadJSONFrom(url: string) { + const eventSource = this.features; + const self = this; + Utils.downloadJson(url) + .then(json => { + if (json.elements === [] && json.remarks.indexOf("runtime error") > 0) { + self.onFail("Runtime error (timeout)", url) + return; + } + const time = new Date(); + const newFeatures: { feature: any, freshness: Date } [] = [] + let i = 0; + let skipped = 0; + for (const feature of json.features) { + const props = feature.properties + for (const key in props) { + if (typeof props[key] !== "string") { + props[key] = "" + props[key] + } + } + + if (props.id === undefined) { + props.id = url + "/" + i; + feature.id = url + "/" + i; + i++; + } + if (self.seenids.has(props.id)) { + skipped++; + continue; + } + self.seenids.add(props.id) + + let freshness: Date = time; + if (feature.properties["_last_edit:timestamp"] !== undefined) { + freshness = new Date(props["_last_edit:timestamp"]) + } + + newFeatures.push({feature: feature, freshness: freshness}) + } + console.debug("Downloaded " + newFeatures.length + " new features and " + skipped + " already seen features from " + url); + + if (newFeatures.length == 0) { + return; + } + + eventSource.setData(eventSource.data.concat(newFeatures)) + + }).catch(msg => console.error("Could not load geojon layer", url, "due to", msg)) + } + +} diff --git a/Logic/FeatureSource/LocalStorageSource.ts b/Logic/FeatureSource/Sources/LocalStorageSource.ts similarity index 91% rename from Logic/FeatureSource/LocalStorageSource.ts rename to Logic/FeatureSource/Sources/LocalStorageSource.ts index e072b948e..c8c28b5c0 100644 --- a/Logic/FeatureSource/LocalStorageSource.ts +++ b/Logic/FeatureSource/Sources/LocalStorageSource.ts @@ -1,6 +1,6 @@ import FeatureSource from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; -import LocalStorageSaver from "./LocalStorageSaver"; +import LocalStorageSaverActor from "./LocalStorageSaverActor"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; export default class LocalStorageSource implements FeatureSource { @@ -9,7 +9,7 @@ export default class LocalStorageSource implements FeatureSource { constructor(layout: UIEventSource) { this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) - const key = LocalStorageSaver.storageKey + layout.data.id + const key = LocalStorageSaverActor.storageKey + layout.data.id layout.addCallbackAndRun(_ => { try { const fromStorage = localStorage.getItem(key); diff --git a/Logic/FeatureSource/OsmApiFeatureSource.ts b/Logic/FeatureSource/Sources/OsmApiFeatureSource.ts similarity index 85% rename from Logic/FeatureSource/OsmApiFeatureSource.ts rename to Logic/FeatureSource/Sources/OsmApiFeatureSource.ts index d48cc5f4b..de3157da2 100644 --- a/Logic/FeatureSource/OsmApiFeatureSource.ts +++ b/Logic/FeatureSource/Sources/OsmApiFeatureSource.ts @@ -4,7 +4,6 @@ import {OsmObject} from "../Osm/OsmObject"; import {Utils} from "../../Utils"; import Loc from "../../Models/Loc"; import FilteredLayer from "../../Models/FilteredLayer"; -import Constants from "../../Models/Constants"; export default class OsmApiFeatureSource implements FeatureSource { @@ -15,19 +14,23 @@ export default class OsmApiFeatureSource implements FeatureSource { leafletMap: UIEventSource; locationControl: UIEventSource, filteredLayers: UIEventSource}; - constructor(minZoom = undefined, state: {locationControl: UIEventSource, filteredLayers: UIEventSource, leafletMap: UIEventSource}) { + constructor(state: {locationControl: UIEventSource, filteredLayers: UIEventSource, leafletMap: UIEventSource, + overpassMaxZoom: UIEventSource}) { this._state = state; - if(minZoom !== undefined){ + const self = this; + function update(){ + const minZoom = state.overpassMaxZoom.data; + const location = state.locationControl.data + if(minZoom === undefined || location === undefined){ + return; + } if(minZoom < 14){ throw "MinZoom should be at least 14 or higher, OSM-api won't work otherwise" } - const self = this; - state.locationControl.addCallbackAndRunD(location => { - if(location.zoom > minZoom){ - return; - } - self.loadArea() - }) + if(location.zoom > minZoom){ + return; + } + self.loadArea() } } @@ -59,10 +62,6 @@ export default class OsmApiFeatureSource implements FeatureSource { if (disabledLayers.length > 0) { return false; } - const loc = this._state.locationControl.data; - if (loc.zoom < Constants.useOsmApiAt) { - return false; - } if (this._state.leafletMap.data === undefined) { return false; // Not yet inited } diff --git a/Logic/FeatureSource/RememberingSource.ts b/Logic/FeatureSource/Sources/RememberingSource.ts similarity index 74% rename from Logic/FeatureSource/RememberingSource.ts rename to Logic/FeatureSource/Sources/RememberingSource.ts index 458b278d0..42b0b0ba3 100644 --- a/Logic/FeatureSource/RememberingSource.ts +++ b/Logic/FeatureSource/Sources/RememberingSource.ts @@ -1,12 +1,14 @@ -/** - * Every previously added point is remembered, but new points are added - */ -import FeatureSource from "./FeatureSource"; + +import FeatureSource, {FeatureSourceForLayer} from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; - +import FilteredLayer from "../../Models/FilteredLayer"; +/** + * Every previously added point is remembered, but new points are added. + * Data coming from upstream will always overwrite a previous value + */ export default class RememberingSource implements FeatureSource { - public readonly features: UIEventSource<{ feature: any, freshness: Date }[]>; + public readonly features: UIEventSource<{ feature: any, freshness: Date }[]>; public readonly name; constructor(source: FeatureSource) { @@ -20,9 +22,9 @@ export default class RememberingSource implements FeatureSource { } // Then new ids - const ids = new Set(features.map(f => f.feature.properties.id + f.feature.geometry.type + f.feature._matching_layer_id)); + const ids = new Set(features.map(f => f.feature.properties.id + f.feature.geometry.type)); // the old data - const oldData = oldFeatures.filter(old => !ids.has(old.feature.properties.id + old.feature.geometry.type + old.feature._matching_layer_id)) + const oldData = oldFeatures.filter(old => !ids.has(old.feature.properties.id + old.feature.geometry.type)) return [...features, ...oldData]; }) } diff --git a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts new file mode 100644 index 000000000..6237a2ddb --- /dev/null +++ b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts @@ -0,0 +1,16 @@ +import {FeatureSourceForLayer} from "./FeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import FilteredLayer from "../../Models/FilteredLayer"; + +export default class SimpleFeatureSource implements FeatureSourceForLayer { + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name: string = "SimpleFeatureSource"; + public readonly layer: FilteredLayer; + + constructor(layer: FilteredLayer) { + this.name = "SimpleFeatureSource("+layer.layerDef.id+")" + this.layer = layer + } + + +} \ No newline at end of file diff --git a/Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts similarity index 61% rename from Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts rename to Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts index b2e5fba14..54e1137a0 100644 --- a/Logic/FeatureSource/WayHandlingApplyingFeatureSource.ts +++ b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts @@ -1,4 +1,4 @@ -import FeatureSource from "./FeatureSource"; +import {FeatureSourceForLayer} from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; import {GeoOperations} from "../GeoOperations"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; @@ -6,39 +6,31 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; /** * This is the part of the pipeline which introduces extra points at the center of an area (but only if this is demanded by the wayhandling) */ -export default class WayHandlingApplyingFeatureSource implements FeatureSource { +export default class WayHandlingApplyingFeatureSource implements FeatureSourceForLayer { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name; + public readonly layer; - constructor(layers: UIEventSource<{ - layerDef: LayerConfig - }[]>, - upstream: FeatureSource) { + constructor(upstream: FeatureSourceForLayer) { this.name = "Wayhandling of " + upstream.name; + this.layer = upstream.layer + const layer = upstream.layer.layerDef; + + if (layer.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) { + // We don't have to do anything fancy + // lets just wire up the upstream + this.features = upstream.features; + return; + } + this.features = upstream.features.map( features => { if (features === undefined) { return; } - - const layerDict = {}; - let allDefaultWayHandling = true; - for (const layer of layers.data) { - layerDict[layer.layerDef.id] = layer; - if (layer.layerDef.wayHandling !== LayerConfig.WAYHANDLING_DEFAULT) { - allDefaultWayHandling = false; - } - } - const newFeatures: { feature: any, freshness: Date }[] = []; for (const f of features) { const feat = f.feature; - const layerId = feat._matching_layer_id; - const layer: LayerConfig = layerDict[layerId].layerDef; - if (layer === undefined) { - console.error("No layer found with id " + layerId); - continue; - } if (layer.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) { newFeatures.push(f); @@ -47,19 +39,17 @@ export default class WayHandlingApplyingFeatureSource implements FeatureSource { if (feat.geometry.type === "Point") { newFeatures.push(f); - // it is a point, nothing to do here + // feature is a point, nothing to do here continue; } // Create the copy const centerPoint = GeoOperations.centerpoint(feat); - centerPoint["_matching_layer_id"] = feat._matching_layer_id; newFeatures.push({feature: centerPoint, freshness: f.freshness}); if (layer.wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY) { newFeatures.push(f); } - } return newFeatures; } diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts new file mode 100644 index 000000000..e69de29bb diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts new file mode 100644 index 000000000..931b85d3c --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts @@ -0,0 +1,72 @@ +/*** + * A tiled source which dynamically loads the required tiles + */ +import State from "../../../State"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer} from "../FeatureSource"; +import {Utils} from "../../../Utils"; +import {UIEventSource} from "../../UIEventSource"; +import Loc from "../../../Models/Loc"; + +export default class DynamicTileSource { + private readonly _loadedTiles = new Set(); + + public readonly existingTiles: Map> = new Map>() + + constructor( + layer: FilteredLayer, + zoomlevel: number, + constructTile: (xy: [number, number]) => FeatureSourceForLayer, + state: { + locationControl: UIEventSource + leafletMap: any + } + ) { + state = State.state + const self = this; + const neededTiles = state.locationControl.map( + location => { + if (!layer.isDisplayed.data) { + // No need to download! - the layer is disabled + return undefined; + } + + if (location.zoom < layer.layerDef.minzoom) { + // No need to download! - the layer is disabled + return undefined; + } + + // Yup, this is cheating to just get the bounds here + const bounds = state.leafletMap.data?.getBounds() + if (bounds === undefined) { + // We'll retry later + return undefined + } + const tileRange = Utils.TileRangeBetween(zoomlevel, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) + + const needed = Utils.MapRange(tileRange, (x, y) => Utils.tile_index(zoomlevel, x, y)).filter(i => !self._loadedTiles.has(i)) + if(needed.length === 0){ + return undefined + } + return needed + } + , [layer.isDisplayed, state.leafletMap]).stabilized(250); + + neededTiles.addCallbackAndRunD(neededIndexes => { + for (const neededIndex of neededIndexes) { + self._loadedTiles.add(neededIndex) + const xy = Utils.tile_from_index(zoomlevel, neededIndex) + const src = constructTile(xy) + let xmap = self.existingTiles.get(xy[0]) + if(xmap === undefined){ + xmap = new Map() + self.existingTiles.set(xy[0], xmap) + } + xmap.set(xy[1], src) + } + }) + + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/README.md b/Logic/FeatureSource/TiledFeatureSource/README.md new file mode 100644 index 000000000..36b0c1b01 --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/README.md @@ -0,0 +1,3 @@ +Data in MapComplete can come from multiple sources. + +In order to keep thins snappy, they are distributed over a tiled database \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/SingleLayerSource.ts b/Logic/FeatureSource/TiledFeatureSource/SingleLayerSource.ts new file mode 100644 index 000000000..e69de29bb diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts new file mode 100644 index 000000000..e69de29bb diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts new file mode 100644 index 000000000..e69de29bb diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts new file mode 100644 index 000000000..58c1fcaeb --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -0,0 +1,40 @@ +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer} from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import Loc from "../../../Models/Loc"; +import GeoJsonSource from "../GeoJsonSource"; +import DynamicTileSource from "./DynamicTileSource"; + +export default class DynamicGeoJsonTileSource extends DynamicTileSource { + constructor(layer: FilteredLayer, + registerLayer: (layer: FeatureSourceForLayer) => void, + state: { + locationControl: UIEventSource + leafletMap: any + }) { + const source = layer.layerDef.source + if (source.geojsonZoomLevel === undefined) { + throw "Invalid layer: geojsonZoomLevel expected" + } + if (source.geojsonSource === undefined) { + throw "Invalid layer: geojsonSource expected" + } + + super( + layer, + source.geojsonZoomLevel, + (xy) => { + const xyz: [number, number, number] = [xy[0], xy[1], source.geojsonZoomLevel] + const src = new GeoJsonSource( + layer, + xyz + ) + registerLayer(src) + return src + }, + state + ); + + } + +} \ No newline at end of file diff --git a/Logic/Osm/ExtractRelations.ts b/Logic/Osm/RelationsTracker.ts similarity index 100% rename from Logic/Osm/ExtractRelations.ts rename to Logic/Osm/RelationsTracker.ts From d144f70ffb8117ba60435d8150563c8cea3355a5 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 20 Sep 2021 17:32:04 +0200 Subject: [PATCH 016/110] Huge refactoring of the feature pipeline, WIP --- Logic/FeatureSource/TiledFeatureSource/SingleLayerSource.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Logic/FeatureSource/TiledFeatureSource/SingleLayerSource.ts diff --git a/Logic/FeatureSource/TiledFeatureSource/SingleLayerSource.ts b/Logic/FeatureSource/TiledFeatureSource/SingleLayerSource.ts deleted file mode 100644 index e69de29bb..000000000 From c11ff652b8100a9344e2bb0b181830411b8fb61c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 21 Sep 2021 01:47:58 +0200 Subject: [PATCH 017/110] More refactoring of the featuresources, cleanup, small changes --- .../Sources/LocalStorageSource.ts | 35 --------- .../Sources/StaticFeatureSource.ts | 20 +++++ .../TiledFeatureSource/TileHierarchyMerger.ts | 45 +++++++++++ UI/Base/MinimapImplementation.ts | 0 UI/{ => ShowDataLayer}/ShowDataLayer.ts | 78 ++++++++----------- UI/ShowDataLayer/ShowDataLayerOptions.ts | 0 UI/ShowDataLayer/ShowDataMultiLayer.ts | 22 ++++++ 7 files changed, 121 insertions(+), 79 deletions(-) delete mode 100644 Logic/FeatureSource/Sources/LocalStorageSource.ts create mode 100644 Logic/FeatureSource/Sources/StaticFeatureSource.ts create mode 100644 Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts create mode 100644 UI/Base/MinimapImplementation.ts rename UI/{ => ShowDataLayer}/ShowDataLayer.ts (74%) create mode 100644 UI/ShowDataLayer/ShowDataLayerOptions.ts create mode 100644 UI/ShowDataLayer/ShowDataMultiLayer.ts diff --git a/Logic/FeatureSource/Sources/LocalStorageSource.ts b/Logic/FeatureSource/Sources/LocalStorageSource.ts deleted file mode 100644 index c8c28b5c0..000000000 --- a/Logic/FeatureSource/Sources/LocalStorageSource.ts +++ /dev/null @@ -1,35 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import LocalStorageSaverActor from "./LocalStorageSaverActor"; -import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; - -export default class LocalStorageSource implements FeatureSource { - public features: UIEventSource<{ feature: any; freshness: Date }[]>; - public readonly name = "LocalStorageSource"; - - constructor(layout: UIEventSource) { - this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) - const key = LocalStorageSaverActor.storageKey + layout.data.id - layout.addCallbackAndRun(_ => { - try { - const fromStorage = localStorage.getItem(key); - if (fromStorage == null) { - return; - } - const loaded: { feature: any; freshness: Date | string }[] = - JSON.parse(fromStorage); - - const parsed: { feature: any; freshness: Date }[] = loaded.map(ff => ({ - feature: ff.feature, - freshness: typeof ff.freshness == "string" ? new Date(ff.freshness) : ff.freshness - })) - - this.features.setData(parsed); - console.log("Loaded ", loaded.length, " features from localstorage as cache") - } catch (e) { - console.log("Could not load features from localStorage:", e) - localStorage.removeItem(key) - } - }) - } -} \ No newline at end of file diff --git a/Logic/FeatureSource/Sources/StaticFeatureSource.ts b/Logic/FeatureSource/Sources/StaticFeatureSource.ts new file mode 100644 index 000000000..7ffe1b02a --- /dev/null +++ b/Logic/FeatureSource/Sources/StaticFeatureSource.ts @@ -0,0 +1,20 @@ +import FeatureSource from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; + +/** + * A simple dummy implementation for whenever it is needed + */ +export default class StaticFeatureSource implements FeatureSource { + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; + public readonly name: string = "StaticFeatureSource" + + constructor(features: any[]) { + const now = new Date(); + this.features = new UIEventSource(features.map(f => ({ + feature: f, + freshness: now + }))) + } + + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts new file mode 100644 index 000000000..768a935fc --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts @@ -0,0 +1,45 @@ +import TileHierarchy from "./TiledFeatureSource/TileHierarchy"; +import FeatureSource, {FeatureSourceForLayer, Tiled} from "./FeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import FilteredLayer from "../../Models/FilteredLayer"; +import FeatureSourceMerger from "./Sources/FeatureSourceMerger"; +import {BBox} from "../GeoOperations"; +import {Utils} from "../../Utils"; + +export class TileHierarchyMerger implements TileHierarchy { + public readonly loadedTiles: Map = new Map(); + private readonly sources: Map> = new Map>(); + + public readonly layer: FilteredLayer; + private _handleTile: (src: FeatureSourceForLayer, index: number) => void; + + constructor(layer: FilteredLayer, handleTile: (src: FeatureSourceForLayer, index: number) => void) { + this.layer = layer; + this._handleTile = handleTile; + } + + /** + * Add another feature source for the given tile. + * Entries for this tile will be merged + * @param src + * @param index + */ + public registerTile(src: FeatureSource, index: number) { + + if (this.sources.has(index)) { + const sources = this.sources.get(index) + sources.data.push(src) + sources.ping() + return; + } + + // We have to setup + const sources = new UIEventSource([src]) + this.sources.set(index, sources) + const merger = new FeatureSourceMerger(this.layer, index, BBox.fromTile(...Utils.tile_from_index(index)), sources) + this.loadedTiles.set(index, merger) + this._handleTile(merger, index) + } + + +} \ No newline at end of file diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts new file mode 100644 index 000000000..e69de29bb diff --git a/UI/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts similarity index 74% rename from UI/ShowDataLayer.ts rename to UI/ShowDataLayer/ShowDataLayer.ts index ced30b29f..661e51d07 100644 --- a/UI/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -3,48 +3,46 @@ */ import {UIEventSource} from "../Logic/UIEventSource"; import * as L from "leaflet" -import "leaflet.markercluster" import State from "../State"; import FeatureInfoBox from "./Popup/FeatureInfoBox"; -import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; +import FeatureSource from "../Logic/FeatureSource/FeatureSource"; +export interface ShowDataLayerOptions { + features: FeatureSource, + leafletMap: UIEventSource, + enablePopups?: true | boolean, + zoomToFeatures? : false | boolean, +} export default class ShowDataLayer { - private _layerDict; private readonly _leafletMap: UIEventSource; - private _cleanCount = 0; private readonly _enablePopups: boolean; private readonly _features: UIEventSource<{ feature: any }[]> + private readonly _layerToShow: LayerConfig; - constructor(features: UIEventSource<{ feature: any }[]>, - leafletMap: UIEventSource, - layoutToUse: UIEventSource, - enablePopups = true, - zoomToFeatures = false) { - this._leafletMap = leafletMap; - this._enablePopups = enablePopups; + // Used to generate a fresh ID when needed + private _cleanCount = 0; + + constructor(options: ShowDataLayerOptions & { layerToShow: LayerConfig}) { + this._leafletMap = options.leafletMap; + this._enablePopups = options.enablePopups ?? true; + if(options.features === undefined){ + throw "Invalid ShowDataLayer invocation" + } + const features = options.features.features.map(featFreshes => featFreshes.map(ff => ff.feature)); this._features = features; + this._layerToShow = options.layerToShow; const self = this; - self._layerDict = {}; - - layoutToUse.addCallbackAndRun(layoutToUse => { - for (const layer of layoutToUse.layers) { - if (self._layerDict[layer.id] === undefined) { - self._layerDict[layer.id] = layer; - } - } - }); let geoLayer = undefined; - let cluster = undefined; function update() { if (features.data === undefined) { return; } - const mp = leafletMap.data; + const mp =options. leafletMap.data; if (mp === undefined) { return; @@ -55,11 +53,8 @@ export default class ShowDataLayer { if (geoLayer !== undefined) { mp.removeLayer(geoLayer); } - if (cluster !== undefined) { - mp.removeLayer(cluster); - } - const allFeats = features.data.map(ff => ff.feature); + const allFeats = features.data; geoLayer = self.CreateGeojsonLayer(); for (const feat of allFeats) { if (feat === undefined) { @@ -68,17 +63,10 @@ export default class ShowDataLayer { // @ts-ignore geoLayer.addData(feat); } - if (layoutToUse.data.clustering.minNeededElements <= allFeats.length) { - // Activate clustering if it wasn't already activated - const cl = window["L"]; // This is a dirty workaround, the clustering plugin binds to the L of the window, not of the namespace or something - cluster = cl.markerClusterGroup({disableClusteringAtZoom: layoutToUse.data.clustering.maxZoom}); - cluster.addLayer(geoLayer); - mp.addLayer(cluster); - } else { + mp.addLayer(geoLayer) - } - if (zoomToFeatures) { + if (options.zoomToFeatures ?? false) { try { mp.fitBounds(geoLayer.getBounds(), {animate: false}) } catch (e) { @@ -91,7 +79,7 @@ export default class ShowDataLayer { } features.addCallback(() => update()); - leafletMap.addCallback(() => update()); + options.leafletMap.addCallback(() => update()); update(); } @@ -99,8 +87,8 @@ export default class ShowDataLayer { private createStyleFor(feature) { const tagsSource = State.state.allElements.addOrGetElement(feature); // Every object is tied to exactly one layer - const layer = this._layerDict[feature._matching_layer_id]; - return layer?.GenerateLeafletStyle(tagsSource, layer._showOnPopup !== undefined); + const layer = this._layerToShow + return layer?.GenerateLeafletStyle(tagsSource, true); } private pointToLayer(feature, latLng): L.Layer { @@ -108,7 +96,7 @@ export default class ShowDataLayer { // We have to convert them to the appropriate icon // Click handling is done in the next step - const layer: LayerConfig = this._layerDict[feature._matching_layer_id]; + const layer: LayerConfig = this._layerToShow if (layer === undefined) { return; } @@ -131,12 +119,14 @@ export default class ShowDataLayer { }); } + /** + * POst processing - basically adding the popup + * @param feature + * @param leafletLayer + * @private + */ private postProcessFeature(feature, leafletLayer: L.Layer) { - const layer: LayerConfig = this._layerDict[feature._matching_layer_id]; - if (layer === undefined) { - console.warn("No layer found for object (probably a now disabled layer)", feature, this._layerDict) - return; - } + const layer: LayerConfig = this._layerToShow if (layer.title === undefined || !this._enablePopups) { // No popup action defined -> Don't do anything // or probably a map in the popup - no popups needed! diff --git a/UI/ShowDataLayer/ShowDataLayerOptions.ts b/UI/ShowDataLayer/ShowDataLayerOptions.ts new file mode 100644 index 000000000..e69de29bb diff --git a/UI/ShowDataLayer/ShowDataMultiLayer.ts b/UI/ShowDataLayer/ShowDataMultiLayer.ts new file mode 100644 index 000000000..5bff575cb --- /dev/null +++ b/UI/ShowDataLayer/ShowDataMultiLayer.ts @@ -0,0 +1,22 @@ +import {UIEventSource} from "../Logic/UIEventSource"; +import FilteredLayer from "../Models/FilteredLayer"; +import ShowDataLayer, {ShowDataLayerOptions} from "./ShowDataLayer/ShowDataLayer"; +import PerLayerFeatureSourceSplitter from "../Logic/FeatureSource/PerLayerFeatureSourceSplitter"; + +/** + * SHows geojson on the given leaflet map, but attempts to figure out the correct layer first + */ +export default class ShowDataMultiLayer { + constructor(options: ShowDataLayerOptions & { layers: UIEventSource }) { + + new PerLayerFeatureSourceSplitter(options.layers, (perLayer => { + const newOptions = { + layerToShow: perLayer.layer.layerDef, + ...options + } + new ShowDataLayer(newOptions) + }), + options.features) + + } +} \ No newline at end of file From d5c1ba4cd1d423c9c534425f50344a3278b1fa98 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 21 Sep 2021 02:10:42 +0200 Subject: [PATCH 018/110] More refactoring, move minimap behind facade --- InitUiElements.ts | 78 ++---- Logic/Actors/BackgroundLayerResetter.ts | 2 +- Logic/Actors/OverpassFeatureSource.ts | 84 +++--- Logic/Actors/SelectedFeatureHandler.ts | 2 +- Logic/ContributorCount.ts | 44 ++- Logic/ExtraFunction.ts | 248 +++++++++-------- .../Actors/LocalStorageSaverActor.ts | 7 +- .../RegisteringAllFromFeatureSourceActor.ts | 6 +- Logic/FeatureSource/ChangeApplicator.ts | 58 +++- Logic/FeatureSource/FeaturePipeline.ts | 250 +++++++++++------ Logic/FeatureSource/FeatureSource.ts | 58 ++-- .../PerLayerFeatureSourceSplitter.ts | 26 +- .../Sources/FeatureSourceMerger.ts | 20 +- .../Sources/FilteringFeatureSource.ts | 13 +- Logic/FeatureSource/Sources/GeoJsonSource.ts | 30 ++- .../Sources/OsmApiFeatureSource.ts | 12 +- .../Sources/RememberingSource.ts | 7 +- .../Sources/SimpleFeatureSource.ts | 6 +- .../Sources/StaticFeatureSource.ts | 17 +- .../WayHandlingApplyingFeatureSource.ts | 12 +- .../DynamicGeoJsonTileSource.ts | 63 +++++ .../TiledFeatureSource/DynamicTileSource.ts | 42 +-- .../TiledFeatureSource/README.md | 26 +- .../TiledFeatureSource/TileHierarchy.ts | 25 ++ .../TiledFeatureSource/TileHierarchyMerger.ts | 17 +- .../TiledFeatureSource/TiledFeatureSource.ts | 191 +++++++++++++ .../TiledFromLocalStorageSource.ts | 114 ++++++-- Logic/GeoOperations.ts | 62 ++++- Logic/ImageProviders/Mapillary.ts | 1 - Logic/MetaTagging.ts | 63 +---- Logic/Osm/Actions/ChangeDescription.ts | 23 +- Logic/Osm/Geocoding.ts | 2 +- Logic/Osm/Overpass.ts | 15 +- Logic/Osm/RelationsTracker.ts | 36 ++- Logic/SimpleMetaTagger.ts | 19 +- Logic/UIEventSource.ts | 9 +- Models/Constants.ts | 8 +- Models/ThemeConfig/Json/LayerConfigJson.ts | 7 +- Models/ThemeConfig/LayoutConfig.ts | 8 - Models/ThemeConfig/SourceConfig.ts | 12 +- State.ts | 23 +- UI/Base/Img.ts | 3 + UI/Base/Minimap.ts | 222 ++------------- UI/Base/MinimapImplementation.ts | 215 +++++++++++++++ UI/BigComponents/AllDownloads.ts | 2 +- UI/BigComponents/Attribution.ts | 10 +- UI/BigComponents/AttributionPanel.ts | 5 +- UI/BigComponents/DownloadPanel.ts | 83 +++++- UI/BigComponents/FullWelcomePaneWithTabs.ts | 9 +- UI/BigComponents/ImportButton.ts | 2 +- UI/BigComponents/LeftControls.ts | 9 +- UI/BigComponents/SimpleAddUI.ts | 12 +- UI/CenterMessageBox.ts | 2 +- UI/ExportPDF.ts | 66 +++-- UI/Image/AttributedImage.ts | 10 +- UI/Input/DirectionInput.ts | 4 +- UI/Input/LengthInput.ts | 4 +- UI/Input/LocationInput.ts | 60 +++-- UI/Popup/SplitRoadWizard.ts | 47 ++-- UI/ShowDataLayer/ShowDataLayer.ts | 18 +- UI/ShowDataLayer/ShowDataLayerOptions.ts | 9 + UI/ShowDataLayer/ShowDataMultiLayer.ts | 11 +- UI/SpecialVisualizations.ts | 29 +- Utils.ts | 28 +- .../layers/drinking_water/drinking_water.json | 2 +- assets/layers/toilet/toilet.json | 38 +++ assets/themes/bookcases/bookcases.json | 2 +- .../themes/drinking_water/drinking_water.json | 2 +- assets/themes/natuurpunt/natuurpunt.json | 2 + assets/themes/speelplekken/speelplekken.json | 2 +- assets/themes/uk_addresses/uk_addresses.json | 2 +- index.ts | 21 +- langs/en.json | 2 +- langs/layers/en.json | 23 ++ langs/nl.json | 2 +- langs/themes/en.json | 5 + package.json | 4 +- scripts/ScriptUtils.ts | 1 - scripts/generateCache.ts | 252 +++++++----------- 79 files changed, 1848 insertions(+), 1118 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index e3e61ce2e..353192dc3 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -1,7 +1,6 @@ import {FixedUiElement} from "./UI/Base/FixedUiElement"; import Toggle from "./UI/Input/Toggle"; import State from "./State"; -import LoadFromOverpass from "./Logic/Actors/OverpassFeatureSource"; import {UIEventSource} from "./Logic/UIEventSource"; import {QueryParameters} from "./Logic/Web/QueryParameters"; import StrayClickHandler from "./Logic/Actors/StrayClickHandler"; @@ -18,17 +17,15 @@ import * as L from "leaflet"; import Img from "./UI/Base/Img"; import UserDetails from "./Logic/Osm/OsmConnection"; import Attribution from "./UI/BigComponents/Attribution"; -import LayerResetter from "./Logic/Actors/LayerResetter"; +import BackgroundLayerResetter from "./Logic/Actors/BackgroundLayerResetter"; import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs"; -import ShowDataLayer from "./UI/ShowDataLayer"; +import ShowDataLayer from "./UI/ShowDataLayer/ShowDataLayer"; import Hash from "./Logic/Web/Hash"; import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; import ScrollableFullScreen from "./UI/Base/ScrollableFullScreen"; import Translations from "./UI/i18n/Translations"; import MapControlButton from "./UI/MapControlButton"; -import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler"; import LZString from "lz-string"; -import FeatureSource from "./Logic/FeatureSource/FeatureSource"; import AllKnownLayers from "./Customizations/AllKnownLayers"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; import {TagsFilter} from "./Logic/Tags/TagsFilter"; @@ -38,7 +35,6 @@ import {LayoutConfigJson} from "./Models/ThemeConfig/Json/LayoutConfigJson"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; import LayerConfig from "./Models/ThemeConfig/LayerConfig"; import Minimap from "./UI/Base/Minimap"; -import Constants from "./Models/Constants"; export class InitUiElements { static InitAll( @@ -130,10 +126,9 @@ export class InitUiElements { } } if (somethingChanged) { - console.log("layoutToUse.layers:", layoutToUse.layers); State.state.layoutToUse.data.layers = Array.from(neededLayers); State.state.layoutToUse.ping(); - State.state.layerUpdater?.ForceRefresh(); + State.state.featurePipeline?.ForceRefresh(); } } @@ -320,7 +315,7 @@ export class InitUiElements { (layer) => layer.id ); - new LayerResetter( + new BackgroundLayerResetter( State.state.backgroundLayer, State.state.locationControl, State.state.availableBackgroundLayers, @@ -333,13 +328,14 @@ export class InitUiElements { State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, - State.state.leafletMap + State.state.currentBounds ); - new Minimap({ + Minimap.createMiniMap({ background: State.state.backgroundLayer, location: State.state.locationControl, leafletMap: State.state.leafletMap, + bounds: State.state.currentBounds, attribution: attr, lastClickLocation: State.state.LastClickLocation }).SetClass("w-full h-full") @@ -371,7 +367,7 @@ export class InitUiElements { } } - private static InitLayers(): FeatureSource { + private static InitLayers(): void { const state = State.state; state.filteredLayers = state.layoutToUse.map((layoutToUse) => { const flayers = []; @@ -396,51 +392,35 @@ export class InitUiElements { return flayers; }); - const updater = new LoadFromOverpass( - state.locationControl, - state.layoutToUse, - state.leafletMap, - state.overpassUrl, - state.overpassTimeout, - Constants.useOsmApiAt - ); - State.state.layerUpdater = updater; - - const source = new FeaturePipeline( - state.filteredLayers, - State.state.changes, - updater, - state.osmApiFeatureSource, - state.layoutToUse, - state.locationControl, - state.selectedElement + State.state.featurePipeline = new FeaturePipeline( + source => { + new ShowDataLayer( + { + features: source, + leafletMap: State.state.leafletMap, + layerToShow: source.layer.layerDef + } + ); + }, state ); - State.state.featurePipeline = source; - new ShowDataLayer( - source.features, - State.state.leafletMap, - State.state.layoutToUse - ); - - const selectedFeatureHandler = new SelectedFeatureHandler( - Hash.hash, - State.state.selectedElement, - source, - State.state.osmApiFeatureSource - ); - selectedFeatureHandler.zoomToSelectedFeature( - State.state.locationControl - ); - return source; + /* const selectedFeatureHandler = new SelectedFeatureHandler( + Hash.hash, + State.state.selectedElement, + source, + State.state.osmApiFeatureSource + ); + selectedFeatureHandler.zoomToSelectedFeature( + State.state.locationControl + );*/ } private static setupAllLayerElements() { // ------------- Setup the layers ------------------------------- - const source = InitUiElements.InitLayers(); + InitUiElements.InitLayers(); - new LeftControls(source).AttachTo("bottom-left"); + new LeftControls(State.state).AttachTo("bottom-left"); new RightControls().AttachTo("bottom-right"); // ------------------ Setup various other UI elements ------------ diff --git a/Logic/Actors/BackgroundLayerResetter.ts b/Logic/Actors/BackgroundLayerResetter.ts index 1a2175dfc..96c43bda3 100644 --- a/Logic/Actors/BackgroundLayerResetter.ts +++ b/Logic/Actors/BackgroundLayerResetter.ts @@ -6,7 +6,7 @@ import Loc from "../../Models/Loc"; /** * Sets the current background layer to a layer that is actually available */ -export default class LayerResetter { +export default class BackgroundLayerResetter { constructor(currentBackgroundLayer: UIEventSource, location: UIEventSource, diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index 4d4110935..e788c7616 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -3,14 +3,15 @@ import Loc from "../../Models/Loc"; import {Or} from "../Tags/Or"; import {Overpass} from "../Osm/Overpass"; import Bounds from "../../Models/Bounds"; -import FeatureSource from "../FeatureSource/FeatureSource"; +import FeatureSource, {FeatureSourceState} from "../FeatureSource/FeatureSource"; import {Utils} from "../../Utils"; import {TagsFilter} from "../Tags/TagsFilter"; import SimpleMetaTagger from "../SimpleMetaTagger"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import RelationsTracker from "../Osm/RelationsTracker"; -export default class OverpassFeatureSource implements FeatureSource { +export default class OverpassFeatureSource implements FeatureSource, FeatureSourceState { public readonly name = "OverpassFeatureSource" @@ -24,6 +25,9 @@ export default class OverpassFeatureSource implements FeatureSource { public readonly runningQuery: UIEventSource = new UIEventSource(false); public readonly timeout: UIEventSource = new UIEventSource(0); + public readonly relationsTracker: RelationsTracker; + + private readonly retries: UIEventSource = new UIEventSource(0); /** * The previous bounds for which the query has been run at the given zoom level @@ -33,56 +37,61 @@ export default class OverpassFeatureSource implements FeatureSource { * we start checking the bounds at the first zoom level the layer might operate. If in bounds - no reload needed, otherwise we continue walking down */ private readonly _previousBounds: Map = new Map(); - private readonly _location: UIEventSource; - private readonly _layoutToUse: UIEventSource; - private readonly _leafletMap: UIEventSource; - private readonly _interpreterUrl: UIEventSource; - private readonly _timeout: UIEventSource; + private readonly state: { + readonly locationControl: UIEventSource, + readonly layoutToUse: UIEventSource, + readonly leafletMap: any, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + } /** * The most important layer should go first, as that one gets first pick for the questions */ constructor( - location: UIEventSource, - layoutToUse: UIEventSource, - leafletMap: UIEventSource, - interpreterUrl: UIEventSource, - timeout: UIEventSource, - maxZoom = undefined) { - this._location = location; - this._layoutToUse = layoutToUse; - this._leafletMap = leafletMap; - this._interpreterUrl = interpreterUrl; - this._timeout = timeout; + state: { + readonly locationControl: UIEventSource, + readonly layoutToUse: UIEventSource, + readonly leafletMap: any, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + readonly overpassMaxZoom: UIEventSource + }) { + + + this.state = state + this.relationsTracker = new RelationsTracker() + const location = state.locationControl const self = this; this.sufficientlyZoomed = location.map(location => { if (location?.zoom === undefined) { return false; } - let minzoom = Math.min(...layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); - if(location.zoom < minzoom){ + let minzoom = Math.min(...state.layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); + if (location.zoom < minzoom) { return false; } - if(maxZoom !== undefined && location.zoom > maxZoom){ + const maxZoom = state.overpassMaxZoom.data + if (maxZoom !== undefined && location.zoom > maxZoom) { return false; } - + return true; - }, [layoutToUse] + }, [state.layoutToUse] ); for (let i = 0; i < 25; i++) { // This update removes all data on all layers -> erase the map on lower levels too this._previousBounds.set(i, []); } - layoutToUse.addCallback(() => { + state.layoutToUse.addCallback(() => { self.update() }); location.addCallback(() => { self.update() }); - leafletMap.addCallbackAndRunD(_ => { + state.leafletMap.addCallbackAndRunD(_ => { self.update(); }) } @@ -97,11 +106,11 @@ export default class OverpassFeatureSource implements FeatureSource { private GetFilter(): Overpass { let filters: TagsFilter[] = []; let extraScripts: string[] = []; - for (const layer of this._layoutToUse.data.layers) { + for (const layer of this.state.layoutToUse.data.layers) { if (typeof (layer) === "string") { throw "A layer was not expanded!" } - if (this._location.data.zoom < layer.minzoom) { + if (this.state.locationControl.data.zoom < layer.minzoom) { continue; } if (layer.doNotDownload) { @@ -141,7 +150,7 @@ export default class OverpassFeatureSource implements FeatureSource { if (filters.length + extraScripts.length === 0) { return undefined; } - return new Overpass(new Or(filters), extraScripts, this._interpreterUrl, this._timeout); + return new Overpass(new Or(filters), extraScripts, this.state.overpassUrl, this.state.overpassTimeout, this.relationsTracker); } private update(): void { @@ -155,21 +164,22 @@ export default class OverpassFeatureSource implements FeatureSource { return; } - const bounds = this._leafletMap.data?.getBounds()?.pad( this._layoutToUse.data.widenFactor); + const bounds = this.state.leafletMap.data?.getBounds()?.pad(this.state.layoutToUse.data.widenFactor); if (bounds === undefined) { return; } - const n = Math.min(90, bounds.getNorth() ); - const e = Math.min(180, bounds.getEast() ); + const n = Math.min(90, bounds.getNorth()); + const e = Math.min(180, bounds.getEast()); const s = Math.max(-90, bounds.getSouth()); const w = Math.max(-180, bounds.getWest()); const queryBounds = {north: n, east: e, south: s, west: w}; - const z = Math.floor(this._location.data.zoom ?? 0); + const z = Math.floor(this.state.locationControl.data.zoom ?? 0); const self = this; const overpass = this.GetFilter(); + if (overpass === undefined) { return; } @@ -181,14 +191,18 @@ export default class OverpassFeatureSource implements FeatureSource { const features = data.features.map(f => ({feature: f, freshness: date})); SimpleMetaTagger.objectMetaInfo.addMetaTags(features) - self.features.setData(features); + try{ + self.features.setData(features); + }catch(e){ + console.error("Got the overpass response, but could not process it: ", e, e.stack) + } self.runningQuery.setData(false); }, function (reason) { self.retries.data++; self.ForceRefresh(); self.timeout.setData(self.retries.data * 5); - console.log(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to ${reason}`); + console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to ${reason}`); self.retries.ping(); self.runningQuery.setData(false); @@ -222,7 +236,7 @@ export default class OverpassFeatureSource implements FeatureSource { return false; } - const b = this._leafletMap.data.getBounds(); + const b = this.state.leafletMap.data.getBounds(); return b.getSouth() >= bounds.south && b.getNorth() <= bounds.north && b.getEast() <= bounds.east && diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts index 5a63d5cb2..07ff7f4a7 100644 --- a/Logic/Actors/SelectedFeatureHandler.ts +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -3,7 +3,7 @@ import FeatureSource from "../FeatureSource/FeatureSource"; import {OsmObject} from "../Osm/OsmObject"; import Loc from "../../Models/Loc"; import FeaturePipeline from "../FeatureSource/FeaturePipeline"; -import OsmApiFeatureSource from "../FeatureSource/OsmApiFeatureSource"; +import OsmApiFeatureSource from "../FeatureSource/Sources/OsmApiFeatureSource"; /** * Makes sure the hash shows the selected element and vice-versa. diff --git a/Logic/ContributorCount.ts b/Logic/ContributorCount.ts index 9c954bdfb..6a4c2da25 100644 --- a/Logic/ContributorCount.ts +++ b/Logic/ContributorCount.ts @@ -1,21 +1,49 @@ /// Given a feature source, calculates a list of OSM-contributors who mapped the latest versions import FeatureSource from "./FeatureSource/FeatureSource"; import {UIEventSource} from "./UIEventSource"; +import FeaturePipeline from "./FeatureSource/FeaturePipeline"; +import Loc from "../Models/Loc"; +import State from "../State"; +import {BBox} from "./GeoOperations"; export default class ContributorCount { - public readonly Contributors: UIEventSource>; + public readonly Contributors: UIEventSource> = new UIEventSource>(new Map()); + private readonly state: { featurePipeline: FeaturePipeline, currentBounds: UIEventSource, locationControl: UIEventSource }; - constructor(featureSource: FeatureSource) { - this.Contributors = featureSource.features.map(features => { - const hist = new Map(); - for (const feature of features) { - const contributor = feature.feature.properties["_last_edit:contributor"] + constructor(state: { featurePipeline: FeaturePipeline, currentBounds: UIEventSource, locationControl: UIEventSource }) { + this.state = state; + const self = this; + state.currentBounds.map(bbox => { + self.update(bbox) + }) + state.featurePipeline.runningQuery.addCallbackAndRun( + _ => self.update(state.currentBounds.data) + ) + + } + + private lastUpdate: Date = undefined; + + private update(bbox: BBox) { + if(bbox === undefined){ + return; + } + const now = new Date(); + if (this.lastUpdate !== undefined && ((now.getTime() - this.lastUpdate.getTime()) < 1000 * 60)) { + return; + } + console.log("Calculating contributors") + const featuresList = this.state.featurePipeline.GetAllFeaturesWithin(bbox) + const hist = new Map(); + for (const list of featuresList) { + for (const feature of list) { + const contributor = feature.properties["_last_edit:contributor"] const count = hist.get(contributor) ?? 0; hist.set(contributor, count + 1) } - return hist; - }) + } + this.Contributors.setData(hist) } } \ No newline at end of file diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index 4deb3e01f..dd75ac356 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -1,14 +1,24 @@ -import {GeoOperations} from "./GeoOperations"; +import {BBox, GeoOperations} from "./GeoOperations"; import Combine from "../UI/Base/Combine"; -import {Relation} from "./Osm/ExtractRelations"; +import RelationsTracker from "./Osm/RelationsTracker"; import State from "../State"; -import {Utils} from "../Utils"; import BaseUIElement from "../UI/BaseUIElement"; import List from "../UI/Base/List"; import Title from "../UI/Base/Title"; import {UIEventSourceTools} from "./UIEventSource"; import AspectedRouting from "./Osm/aspectedRouting"; +export interface ExtraFuncParams { + /** + * Gets all the features from the given layer within the given BBOX. + * Note that more features then requested can be given back. + * Format: [ [ geojson, geojson, geojson, ... ], [geojson, ...], ...] + */ + getFeaturesWithin: (layerId: string, bbox: BBox) => any[][], + memberships: RelationsTracker +} + + export class ExtraFunction { @@ -55,15 +65,20 @@ export class ExtraFunction { (params, feat) => { return (...layerIds: string[]) => { const result = [] + + const bbox = BBox.get(feat) + for (const layerId of layerIds) { - const otherLayer = params.featuresPerLayer.get(layerId); - if (otherLayer === undefined) { + const otherLayers = params.getFeaturesWithin(layerId, bbox) + if (otherLayers === undefined) { continue; } - if (otherLayer.length === 0) { + if (otherLayers.length === 0) { continue; } - result.push(...GeoOperations.calculateOverlap(feat, otherLayer)); + for (const otherLayer of otherLayers) { + result.push(...GeoOperations.calculateOverlap(feat, otherLayer)); + } } return result; } @@ -77,6 +92,9 @@ export class ExtraFunction { }, (featuresPerLayer, feature) => { return (arg0, lat) => { + if (arg0 === undefined) { + return undefined; + } if (typeof arg0 === "number") { // Feature._lon and ._lat is conveniently place by one of the other metatags return GeoOperations.distanceBetween([arg0, lat], [feature._lon, feature._lat]); @@ -103,7 +121,7 @@ export class ExtraFunction { args: ["list of features"] }, (params, feature) => { - return (features) => ExtraFunction.GetClosestNFeatures(params, feature, features)[0].feat + return (features) => ExtraFunction.GetClosestNFeatures(params, feature, features)?.[0]?.feat } ) @@ -113,12 +131,13 @@ 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. 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 laoded)\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", "amount of features", "unique tag key (optional)"] + args: ["list of features", "amount of features", "unique tag key (optional)", "maxDistanceInMeters (optional)"] }, (params, feature) => { - return (features, amount, uniqueTag) => ExtraFunction.GetClosestNFeatures(params, feature, features, { + return (features, amount, uniqueTag, maxDistanceInMeters) => ExtraFunction.GetClosestNFeatures(params, feature, features, { maxFeatures: Number(amount), - uniqueTag: uniqueTag + uniqueTag: uniqueTag, + maxDistance: Number(maxDistanceInMeters) }) } ) @@ -131,8 +150,10 @@ export class ExtraFunction { "For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')`", args: [] }, - (params, _) => { - return () => params.relations ?? []; + (params, feat) => { + return () => + params.memberships.knownRelations.data.get(feat.properties.id) ?? [] + } ) private static readonly AspectedRouting = new ExtraFunction( @@ -165,19 +186,19 @@ export class ExtraFunction { private readonly _name: string; private readonly _args: string[]; private readonly _doc: string; - private readonly _f: (params: { featuresPerLayer: Map, relations: { role: string, relation: Relation }[] }, feat: any) => any; + private readonly _f: (params: ExtraFuncParams, feat: any) => any; constructor(options: { name: string, doc: string, args: string[] }, - f: ((params: { featuresPerLayer: Map, relations: { role: string, relation: Relation }[] }, feat: any) => any)) { + f: ((params: ExtraFuncParams, feat: any) => any)) { this._name = options.name; this._doc = options.doc; this._args = options.args; this._f = f; } - public static FullPatchFeature(featuresPerLayer: Map, relations: { role: string, relation: Relation }[], feature) { + public static FullPatchFeature(params: ExtraFuncParams, feature) { for (const func of ExtraFunction.allFuncs) { - func.PatchFeature(featuresPerLayer, relations, feature); + func.PatchFeature(params, feature); } } @@ -198,121 +219,132 @@ export class ExtraFunction { } /** - * Gets the closes N features, sorted by ascending distance + * Gets the closes N features, sorted by ascending distance. + * + * @param params: The link to mapcomplete state + * @param feature: The central feature under consideration + * @param features: The other features + * @param options: maxFeatures: The maximum amount of features to be returned. Default: 1; uniqueTag: returned features are not allowed to have the same value for this key; maxDistance: stop searching if it is too far away (in meter). Default: 500m + * @constructor + * @private */ - private static GetClosestNFeatures(params, feature, features, options?: { maxFeatures?: number, uniqueTag?: string | undefined }): { feat: any, distance: number }[] { + private static GetClosestNFeatures(params: ExtraFuncParams, + feature: any, + features: string | any[], + options?: { maxFeatures?: number, uniqueTag?: string | undefined, maxDistance?: number }): { feat: any, distance: number }[] { const maxFeatures = options?.maxFeatures ?? 1 - const uniqueTag : string | undefined = options?.uniqueTag + const maxDistance = options?.maxDistance ?? 500 + const uniqueTag: string | undefined = options?.uniqueTag if (typeof features === "string") { const name = features - features = params.featuresPerLayer.get(features) - if (features === undefined) { - var keys = Utils.NoNull(Array.from(params.featuresPerLayer.keys())); - if (keys.length > 0) { - throw `No features defined for ${name}. Defined layers are ${keys.join(", ")}`; - } else { - // This is the first pass over an external dataset - // Other data probably still has to load! - return undefined; - } - - } + const bbox = GeoOperations.bbox(GeoOperations.buffer(GeoOperations.bbox(feature), maxDistance)) + features = params.getFeaturesWithin(name, new BBox(bbox.geometry.coordinates)) + }else{ + features = [features] + } + if (features === undefined) { + return; } let closestFeatures: { feat: any, distance: number }[] = []; - for (const otherFeature of features) { - if (otherFeature == feature || otherFeature.id == feature.id) { - continue; // We ignore self - } - let distance = undefined; - if (otherFeature._lon !== undefined && otherFeature._lat !== undefined) { - distance = GeoOperations.distanceBetween([otherFeature._lon, otherFeature._lat], [feature._lon, feature._lat]); - } else { - distance = GeoOperations.distanceBetween( - GeoOperations.centerpointCoordinates(otherFeature), - [feature._lon, feature._lat] - ) - } - if (distance === undefined) { - throw "Undefined distance!" - } + for(const featureList of features) { + for (const otherFeature of featureList) { + if (otherFeature == feature || otherFeature.id == feature.id) { + continue; // We ignore self + } + let distance = undefined; + if (otherFeature._lon !== undefined && otherFeature._lat !== undefined) { + distance = GeoOperations.distanceBetween([otherFeature._lon, otherFeature._lat], [feature._lon, feature._lat]); + } else { + distance = GeoOperations.distanceBetween( + GeoOperations.centerpointCoordinates(otherFeature), + [feature._lon, feature._lat] + ) + } + if (distance === undefined) { + throw "Undefined distance!" + } + if (distance > maxDistance) { + continue + } - if (closestFeatures.length === 0) { - closestFeatures.push({ - feat: otherFeature, - distance: distance - }) - continue; - } + if (closestFeatures.length === 0) { + closestFeatures.push({ + feat: otherFeature, + distance: distance + }) + continue; + } - if (closestFeatures.length >= maxFeatures && closestFeatures[maxFeatures - 1].distance < distance) { - // The last feature of the list (and thus the furthest away is still closer - // No use for checking, as we already have plenty of features! - continue - } + if (closestFeatures.length >= maxFeatures && closestFeatures[maxFeatures - 1].distance < distance) { + // The last feature of the list (and thus the furthest away is still closer + // No use for checking, as we already have plenty of features! + continue + } - let targetIndex = closestFeatures.length - for (let i = 0; i < closestFeatures.length; i++) { - const closestFeature = closestFeatures[i]; + let targetIndex = closestFeatures.length + for (let i = 0; i < closestFeatures.length; i++) { + const closestFeature = closestFeatures[i]; - if (uniqueTag !== undefined) { - const uniqueTagsMatch = otherFeature.properties[uniqueTag] !== undefined && - closestFeature.feat.properties[uniqueTag] === otherFeature.properties[uniqueTag] - if (uniqueTagsMatch) { - targetIndex = -1 - if (closestFeature.distance > distance) { - // This is a very special situation: - // We want to see the tag `uniquetag=some_value` only once in the entire list (e.g. to prevent road segements of identical names to fill up the list of 'names of nearby roads') - // AT this point, we have found a closer segment with the same, identical tag - // so we replace directly - closestFeatures[i] = {feat: otherFeature, distance: distance} + if (uniqueTag !== undefined) { + const uniqueTagsMatch = otherFeature.properties[uniqueTag] !== undefined && + closestFeature.feat.properties[uniqueTag] === otherFeature.properties[uniqueTag] + if (uniqueTagsMatch) { + targetIndex = -1 + if (closestFeature.distance > distance) { + // This is a very special situation: + // We want to see the tag `uniquetag=some_value` only once in the entire list (e.g. to prevent road segements of identical names to fill up the list of 'names of nearby roads') + // AT this point, we have found a closer segment with the same, identical tag + // so we replace directly + closestFeatures[i] = {feat: otherFeature, distance: distance} + } + break; + } + } + + if (closestFeature.distance > distance) { + targetIndex = i + + if (uniqueTag !== undefined) { + const uniqueValue = otherFeature.properties[uniqueTag] + // We might still have some other values later one with the same uniquetag that have to be cleaned + for (let j = i; j < closestFeatures.length; j++) { + if (closestFeatures[j].feat.properties[uniqueTag] === uniqueValue) { + closestFeatures.splice(j, 1) + } + } } break; } } - if (closestFeature.distance > distance) { - targetIndex = i + if (targetIndex == -1) { + continue; // value is already swapped by the unique tag + } - if (uniqueTag !== undefined) { - const uniqueValue = otherFeature.properties[uniqueTag] - // We might still have some other values later one with the same uniquetag that have to be cleaned - for (let j = i; j < closestFeatures.length; j++) { - if(closestFeatures[j].feat.properties[uniqueTag] === uniqueValue){ - closestFeatures.splice(j, 1) - } - } + if (targetIndex < maxFeatures) { + // insert and drop one + closestFeatures.splice(targetIndex, 0, { + feat: otherFeature, + distance: distance + }) + if (closestFeatures.length >= maxFeatures) { + closestFeatures.splice(maxFeatures, 1) + } + } else { + // Overwrite the last element + closestFeatures[targetIndex] = { + feat: otherFeature, + distance: distance } - break; - } - } - if (targetIndex == -1) { - continue; // value is already swapped by the unique tag - } - - if (targetIndex < maxFeatures) { - // insert and drop one - closestFeatures.splice(targetIndex, 0, { - feat: otherFeature, - distance: distance - }) - if (closestFeatures.length >= maxFeatures) { - closestFeatures.splice(maxFeatures, 1) } - } else { - // Overwrite the last element - closestFeatures[targetIndex] = { - feat: otherFeature, - distance: distance - } - } } return closestFeatures; } - public PatchFeature(featuresPerLayer: Map, relations: { role: string, relation: Relation }[], feature: any) { - feature[this._name] = this._f({featuresPerLayer: featuresPerLayer, relations: relations}, feature) + public PatchFeature(params: ExtraFuncParams, feature: any) { + feature[this._name] = this._f(params, feature) } } diff --git a/Logic/FeatureSource/Actors/LocalStorageSaverActor.ts b/Logic/FeatureSource/Actors/LocalStorageSaverActor.ts index 9d0cb6e3b..9cb9ce7f2 100644 --- a/Logic/FeatureSource/Actors/LocalStorageSaverActor.ts +++ b/Logic/FeatureSource/Actors/LocalStorageSaverActor.ts @@ -1,11 +1,11 @@ -import {FeatureSourceForLayer} from "./FeatureSource"; -import {Utils} from "../../Utils"; - /*** * Saves all the features that are passed in to localstorage, so they can be retrieved on the next run * * Technically, more an Actor then a featuresource, but it fits more neatly this ay */ +import {FeatureSourceForLayer} from "../FeatureSource"; +import {Utils} from "../../../Utils"; + export default class LocalStorageSaverActor { public static readonly storageKey: string = "cached-features"; @@ -21,7 +21,6 @@ export default class LocalStorageSaverActor { try { localStorage.setItem(key, JSON.stringify(features)); - console.log("Saved ", features.length, "elements to", key) localStorage.setItem(key + "-time", JSON.stringify(now)) } catch (e) { console.warn("Could not save the features to local storage:", e) diff --git a/Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts b/Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts index b3b921195..240846470 100644 --- a/Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts +++ b/Logic/FeatureSource/Actors/RegisteringAllFromFeatureSourceActor.ts @@ -1,6 +1,6 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import State from "../../State"; +import FeatureSource from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import State from "../../../State"; export default class RegisteringAllFromFeatureSourceActor { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; diff --git a/Logic/FeatureSource/ChangeApplicator.ts b/Logic/FeatureSource/ChangeApplicator.ts index 9b4f2271d..0507c733d 100644 --- a/Logic/FeatureSource/ChangeApplicator.ts +++ b/Logic/FeatureSource/ChangeApplicator.ts @@ -1,10 +1,35 @@ -import FeatureSource from "./FeatureSource"; +import FeatureSource, {IndexedFeatureSource} from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; import {Changes} from "../Osm/Changes"; import {ChangeDescription} from "../Osm/Actions/ChangeDescription"; import {Utils} from "../../Utils"; import {OsmNode, OsmRelation, OsmWay} from "../Osm/OsmObject"; +/** + * A feature source containing exclusively new elements + */ +export class NewGeometryChangeApplicatorFeatureSource implements FeatureSource{ + + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; + public readonly name: string = "newFeatures"; + constructor(changes: Changes) { + const seenChanges = new Set(); + changes.pendingChanges.addCallbackAndRunD(changes => { + for (const change of changes) { + if(seenChanges.has(change)){ + continue + } + seenChanges.add(change) + + if(change.id < 0){ + // This is a new object! + } + + } + }) + } + +} /** * Applies changes from 'Changes' onto a featureSource @@ -12,10 +37,18 @@ import {OsmNode, OsmRelation, OsmWay} from "../Osm/OsmObject"; export default class ChangeApplicator implements FeatureSource { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name: string; + private readonly source: IndexedFeatureSource; + private readonly changes: Changes; + private readonly mode?: { + generateNewGeometries: boolean + }; - constructor(source: FeatureSource, changes: Changes, mode?: { + constructor(source: IndexedFeatureSource, changes: Changes, mode?: { generateNewGeometries: boolean }) { + this.source = source; + this.changes = changes; + this.mode = mode; this.name = "ChangesApplied(" + source.name + ")" this.features = source.features @@ -26,7 +59,7 @@ export default class ChangeApplicator implements FeatureSource { if (runningUpdate) { return; // No need to ping again } - ChangeApplicator.ApplyChanges(features, changes.pendingChanges.data, mode) + self.ApplyChanges() seenChanges.clear() }) @@ -34,19 +67,20 @@ export default class ChangeApplicator implements FeatureSource { runningUpdate = true; changes = changes.filter(ch => !seenChanges.has(ch)) changes.forEach(c => seenChanges.add(c)) - ChangeApplicator.ApplyChanges(self.features.data, changes, mode) + self.ApplyChanges() source.features.ping() runningUpdate = false; }) - - } /** * Returns true if the geometry is changed and the source should be pinged */ - private static ApplyChanges(features: { feature: any; freshness: Date }[], cs: ChangeDescription[], mode: { generateNewGeometries: boolean }): boolean { + private ApplyChanges(): boolean { + const cs = this.changes.pendingChanges.data + const features = this.source.features.data + const loadedIds = this.source.containedIds if (cs.length === 0 || features === undefined) { return; } @@ -56,12 +90,18 @@ export default class ChangeApplicator implements FeatureSource { const changesPerId: Map = new Map() for (const c of cs) { const id = c.type + "/" + c.id + if (!loadedIds.has(id)) { + continue + } if (!changesPerId.has(id)) { changesPerId.set(id, []) } changesPerId.get(id).push(c) } - + if (changesPerId.size === 0) { + // The current feature source set doesn't contain any changed feature, so we can safely skip + return; + } const now = new Date() @@ -77,7 +117,7 @@ export default class ChangeApplicator implements FeatureSource { // First, create the new features - they have a negative ID // We don't set the properties yet though - if (mode?.generateNewGeometries) { + if (this.mode?.generateNewGeometries) { changesPerId.forEach(cs => { cs .forEach(change => { diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 5e91b4567..ec4a052be 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -1,95 +1,191 @@ -import FilteringFeatureSource from "../FeatureSource/FilteringFeatureSource"; -import FeatureSourceMerger from "../FeatureSource/FeatureSourceMerger"; -import RememberingSource from "../FeatureSource/RememberingSource"; -import WayHandlingApplyingFeatureSource from "../FeatureSource/WayHandlingApplyingFeatureSource"; -import FeatureDuplicatorPerLayer from "../FeatureSource/FeatureDuplicatorPerLayer"; -import FeatureSource from "../FeatureSource/FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import LocalStorageSaver from "./LocalStorageSaver"; -import LocalStorageSource from "./LocalStorageSource"; -import Loc from "../../Models/Loc"; -import GeoJsonSource from "./GeoJsonSource"; -import MetaTaggingFeatureSource from "./MetaTaggingFeatureSource"; -import RegisteringFeatureSource from "./RegisteringFeatureSource"; -import FilteredLayer from "../../Models/FilteredLayer"; -import {Changes} from "../Osm/Changes"; -import ChangeApplicator from "./ChangeApplicator"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import FilteringFeatureSource from "./Sources/FilteringFeatureSource"; +import PerLayerFeatureSourceSplitter from "./PerLayerFeatureSourceSplitter"; +import FeatureSource, {FeatureSourceForLayer, FeatureSourceState, Tiled} from "./FeatureSource"; +import TiledFeatureSource from "./TiledFeatureSource/TiledFeatureSource"; +import {UIEventSource} from "../UIEventSource"; +import {TileHierarchyTools} from "./TiledFeatureSource/TileHierarchy"; +import FilteredLayer from "../../Models/FilteredLayer"; +import MetaTagging from "../MetaTagging"; +import RememberingSource from "./Sources/RememberingSource"; +import OverpassFeatureSource from "../Actors/OverpassFeatureSource"; +import {Changes} from "../Osm/Changes"; +import GeoJsonSource from "./Sources/GeoJsonSource"; +import Loc from "../../Models/Loc"; +import WayHandlingApplyingFeatureSource from "./Sources/WayHandlingApplyingFeatureSource"; +import RegisteringAllFromFeatureSourceActor from "./Actors/RegisteringAllFromFeatureSourceActor"; +import {Utils} from "../../Utils"; +import TiledFromLocalStorageSource from "./TiledFeatureSource/TiledFromLocalStorageSource"; +import LocalStorageSaverActor from "./Actors/LocalStorageSaverActor"; +import DynamicGeoJsonTileSource from "./TiledFeatureSource/DynamicGeoJsonTileSource"; +import {BBox} from "../GeoOperations"; +import {TileHierarchyMerger} from "./TiledFeatureSource/TileHierarchyMerger"; +import RelationsTracker from "../Osm/RelationsTracker"; -export default class FeaturePipeline implements FeatureSource { - public features: UIEventSource<{ feature: any; freshness: Date }[]>; +export default class FeaturePipeline implements FeatureSourceState { - public readonly name = "FeaturePipeline" + public readonly sufficientlyZoomed: UIEventSource; + public readonly runningQuery: UIEventSource; + public readonly timeout: UIEventSource; + public readonly somethingLoaded: UIEventSource = new UIEventSource(false) - constructor(flayers: UIEventSource, - changes: Changes, - updater: FeatureSource, - fromOsmApi: FeatureSource, - layout: UIEventSource, - locationControl: UIEventSource, - selectedElement: UIEventSource) { + private readonly overpassUpdater: OverpassFeatureSource + private readonly relationTracker: RelationsTracker + private readonly perLayerHierarchy: Map; + constructor( + handleFeatureSource: (source: FeatureSourceForLayer) => void, + state: { + osmApiFeatureSource: FeatureSource, + filteredLayers: UIEventSource, + locationControl: UIEventSource, + selectedElement: UIEventSource, + changes: Changes, + layoutToUse: UIEventSource, + leafletMap: any, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + readonly overpassMaxZoom: UIEventSource; + }) { - const allLoadedFeatures = new UIEventSource<{ feature: any; freshness: Date }[]>([]) + const self = this + const updater = new OverpassFeatureSource(state); + this.overpassUpdater = updater; + this.sufficientlyZoomed = updater.sufficientlyZoomed + this.runningQuery = updater.runningQuery + this.timeout = updater.timeout + this.relationTracker = updater.relationsTracker + // Register everything in the state' 'AllElements' + new RegisteringAllFromFeatureSourceActor(updater) - // first we metatag, then we save to get the metatags into storage too - // Note that we need to register before we do metatagging (as it expects the event sources) + const perLayerHierarchy = new Map() + this.perLayerHierarchy = perLayerHierarchy - // AT last, the metaTagging also needs to be run _after_ the duplicatorPerLayer - const amendedOverpassSource = - new RememberingSource( - new LocalStorageSaver( - new MetaTaggingFeatureSource(allLoadedFeatures, - new FeatureDuplicatorPerLayer(flayers, - new RegisteringFeatureSource( - new ChangeApplicator( - updater, changes - )) - )), layout)); + const patchedHandleFeatureSource = function (src: FeatureSourceForLayer) { + // This will already contain the merged features for this tile. In other words, this will only be triggered once for every tile + const srcFiltered = + new FilteringFeatureSource(state, + new WayHandlingApplyingFeatureSource( + src, + ) + ) + handleFeatureSource(srcFiltered) + self.somethingLoaded.setData(true) + }; - const geojsonSources: FeatureSource [] = GeoJsonSource - .ConstructMultiSource(flayers.data, locationControl) - .map(geojsonSource => { - let source = new RegisteringFeatureSource( - new FeatureDuplicatorPerLayer(flayers, - new ChangeApplicator(geojsonSource, changes))); - if (!geojsonSource.isOsmCache) { - source = new MetaTaggingFeatureSource(allLoadedFeatures, source, updater.features); - } - return source - }); + function addToHierarchy(src: FeatureSource & Tiled, layerId: string) { + perLayerHierarchy.get(layerId).registerTile(src) + } - const amendedLocalStorageSource = - new RememberingSource(new RegisteringFeatureSource(new FeatureDuplicatorPerLayer(flayers, new ChangeApplicator(new LocalStorageSource(layout), changes)) - )); + for (const filteredLayer of state.filteredLayers.data) { + const hierarchy = new TileHierarchyMerger(filteredLayer, (tile, _) => patchedHandleFeatureSource(tile)) + const id = filteredLayer.layerDef.id + perLayerHierarchy.set(id, hierarchy) + const source = filteredLayer.layerDef.source - const amendedOsmApiSource = new RememberingSource( - new MetaTaggingFeatureSource(allLoadedFeatures, - new FeatureDuplicatorPerLayer(flayers, - new RegisteringFeatureSource(new ChangeApplicator(fromOsmApi, changes, - { - // We lump in the new points here - generateNewGeometries: true + if (source.geojsonSource === undefined) { + // This is an OSM layer + // We load the cached values and register them + // Getting data from upstream happens a bit lower + new TiledFromLocalStorageSource(filteredLayer, + (src) => { + new RegisteringAllFromFeatureSourceActor(src) + hierarchy.registerTile(src); + }, state) + continue + } + + if (source.geojsonZoomLevel === undefined) { + // This is a 'load everything at once' geojson layer + // We split them up into tiles + const src = new GeoJsonSource(filteredLayer) + TiledFeatureSource.createHierarchy(src, { + layer: src.layer, + registerTile: (tile) => { + new RegisteringAllFromFeatureSourceActor(tile) + addToHierarchy(tile, id) + } + }) + } else { + new DynamicGeoJsonTileSource( + filteredLayer, + src => TiledFeatureSource.createHierarchy(src, { + layer: src.layer, + registerTile: (tile) => { + new RegisteringAllFromFeatureSourceActor(tile) + addToHierarchy(tile, id) } - ))))); + }), + state + ) + } - const merged = - new FeatureSourceMerger([ - amendedOverpassSource, - amendedOsmApiSource, - amendedLocalStorageSource, - ...geojsonSources - ]); + } - merged.features.syncWith(allLoadedFeatures) + // Actually load data from the overpass source + + new PerLayerFeatureSourceSplitter(state.filteredLayers, + (source) => TiledFeatureSource.createHierarchy(source, { + layer: source.layer, + registerTile: (tile) => { + // We save the tile data for the given layer to local storage + const [z, x, y] = Utils.tile_from_index(tile.tileIndex) + new LocalStorageSaverActor(tile, x, y, z) + addToHierarchy(tile, source.layer.layerDef.id); + } + }), new RememberingSource(updater)) + + + // Whenever fresh data comes in, we need to update the metatagging + updater.features.addCallback(_ => { + self.updateAllMetaTagging() + }) - this.features = new WayHandlingApplyingFeatureSource(flayers, - new FilteringFeatureSource( - flayers, - locationControl, - selectedElement, - merged - )).features; } + private updateAllMetaTagging() { + console.log("Updating the meta tagging") + const self = this; + this.perLayerHierarchy.forEach(hierarchy => { + hierarchy.loadedTiles.forEach(src => { + MetaTagging.addMetatags( + src.features.data, + { + memberships: this.relationTracker, + getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox) + }, + src.layer.layerDef + ) + }) + }) + + } + + public GetAllFeaturesWithin(bbox: BBox): any[][]{ + const self = this + const tiles = [] + Array.from(this.perLayerHierarchy.keys()) + .forEach(key => tiles.push(...self.GetFeaturesWithin(key, bbox))) + return tiles; + } + + public GetFeaturesWithin(layerId: string, bbox: BBox): any[][]{ + const requestedHierarchy = this.perLayerHierarchy.get(layerId) + if (requestedHierarchy === undefined) { + return undefined; + } + return TileHierarchyTools.getTiles(requestedHierarchy, bbox) + .filter(featureSource => featureSource.features?.data !== undefined) + .map(featureSource => featureSource.features.data.map(fs => fs.feature)) + } + + public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void){ + Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => { + TileHierarchyTools.getTiles(hierarchy, bbox).forEach(handleTile) + }) + } + + public ForceRefresh() { + this.overpassUpdater.ForceRefresh() + } } \ No newline at end of file diff --git a/Logic/FeatureSource/FeatureSource.ts b/Logic/FeatureSource/FeatureSource.ts index 624658e79..791882e11 100644 --- a/Logic/FeatureSource/FeatureSource.ts +++ b/Logic/FeatureSource/FeatureSource.ts @@ -1,5 +1,7 @@ import {UIEventSource} from "../UIEventSource"; import {Utils} from "../../Utils"; +import FilteredLayer from "../../Models/FilteredLayer"; +import {BBox} from "../GeoOperations"; export default interface FeatureSource { features: UIEventSource<{ feature: any, freshness: Date }[]>; @@ -9,38 +11,30 @@ export default interface FeatureSource { name: string; } -export class FeatureSourceUtils { +export interface Tiled { + tileIndex: number, + bbox: BBox +} - /** - * Exports given featurePipeline as a geojson FeatureLists (downloads as a json) - * @param featurePipeline The FeaturePipeline you want to export - * @param options The options object - * @param options.metadata True if you want to include the MapComplete metadata, false otherwise - */ - public static extractGeoJson(featurePipeline: FeatureSource, options: { metadata?: boolean } = {}) { - let defaults = { - metadata: false, - } - options = Utils.setDefaults(options, defaults); +/** + * A feature source which only contains features for the defined layer + */ +export interface FeatureSourceForLayer extends FeatureSource{ + readonly layer: FilteredLayer +} - // Select all features, ignore the freshness and other data - let featureList: any[] = featurePipeline.features.data.map((feature) => - JSON.parse(JSON.stringify((feature.feature)))); // Make a deep copy! +/** + * A feature source which is aware of the indexes it contains + */ +export interface IndexedFeatureSource extends FeatureSource { + readonly containedIds: UIEventSource> +} - if (!options.metadata) { - for (let i = 0; i < featureList.length; i++) { - let feature = featureList[i]; - for (let property in feature.properties) { - if (property[0] == "_" && property !== "_lat" && property !== "_lon") { - delete featureList[i]["properties"][property]; - } - } - } - } - return {type: "FeatureCollection", features: featureList} - - - } - - -} \ No newline at end of file +/** + * A feature source which has some extra data about it's state + */ +export interface FeatureSourceState { + readonly sufficientlyZoomed: UIEventSource; + readonly runningQuery: UIEventSource; + readonly timeout: UIEventSource; +} diff --git a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts index 296f318b7..bb39660fa 100644 --- a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts +++ b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts @@ -1,8 +1,7 @@ -import FeatureSource from "./FeatureSource"; +import FeatureSource, {FeatureSourceForLayer} from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; import FilteredLayer from "../../Models/FilteredLayer"; -import OverpassFeatureSource from "../Actors/OverpassFeatureSource"; -import SimpleFeatureSource from "./SimpleFeatureSource"; +import SimpleFeatureSource from "./Sources/SimpleFeatureSource"; /** @@ -13,17 +12,17 @@ import SimpleFeatureSource from "./SimpleFeatureSource"; export default class PerLayerFeatureSourceSplitter { constructor(layers: UIEventSource, - handleLayerData: (source: FeatureSource) => void, - upstream: OverpassFeatureSource) { + handleLayerData: (source: FeatureSourceForLayer) => void, + upstream: FeatureSource) { - const knownLayers = new Map() + const knownLayers = new Map() function update() { const features = upstream.features.data; if (features === undefined) { return; } - if(layers.data === undefined){ + if (layers.data === undefined) { return; } @@ -69,19 +68,16 @@ export default class PerLayerFeatureSourceSplitter { if (featureSource === undefined) { // Not yet initialized - now is a good time featureSource = new SimpleFeatureSource(layer) + featureSource.features.setData(features) knownLayers.set(id, featureSource) handleLayerData(featureSource) + } else { + featureSource.features.setData(features) } - featureSource.features.setData(features) } - - - upstream.features.addCallbackAndRunD(_ => update()) - layers.addCallbackAndRunD(_ => update()) - } - - layers.addCallbackAndRunD(_ => update()) + + layers.addCallback(_ => update()) upstream.features.addCallbackAndRunD(_ => update()) } } \ No newline at end of file diff --git a/Logic/FeatureSource/Sources/FeatureSourceMerger.ts b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts index 17d6db690..574258455 100644 --- a/Logic/FeatureSource/Sources/FeatureSourceMerger.ts +++ b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts @@ -1,22 +1,28 @@ -import FeatureSource, {FeatureSourceForLayer} from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import FilteredLayer from "../../Models/FilteredLayer"; - /** * Merges features from different featureSources for a single layer * Uses the freshest feature available in the case multiple sources offer data with the same identifier */ -export default class FeatureSourceMerger implements FeatureSourceForLayer { +import {UIEventSource} from "../../UIEventSource"; +import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {BBox} from "../../GeoOperations"; +import {Utils} from "../../../Utils"; + +export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled { public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name; public readonly layer: FilteredLayer private readonly _sources: UIEventSource; + public readonly tileIndex: number; + public readonly bbox: BBox; - constructor(layer: FilteredLayer ,sources: UIEventSource) { + constructor(layer: FilteredLayer, tileIndex: number, bbox: BBox, sources: UIEventSource) { + this.tileIndex = tileIndex; + this.bbox = bbox; this._sources = sources; this.layer = layer; - this.name = "SourceMerger" + this.name = "FeatureSourceMerger("+layer.layerDef.id+", "+Utils.tile_from_index(tileIndex).join(",")+")" const self = this; const handledSources = new Set(); diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index 67a12af35..6848f9f26 100644 --- a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -1,13 +1,13 @@ -import {FeatureSourceForLayer} from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import Hash from "../Web/Hash"; -import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; -import FilteredLayer from "../../Models/FilteredLayer"; +import {UIEventSource} from "../../UIEventSource"; +import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer} from "../FeatureSource"; +import Hash from "../../Web/Hash"; export default class FilteringFeatureSource implements FeatureSourceForLayer { public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); - public readonly name = "FilteringFeatureSource"; + public readonly name; public readonly layer: FilteredLayer; constructor( @@ -18,6 +18,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer { upstream: FeatureSourceForLayer ) { const self = this; + this.name = "FilteringFeatureSource("+upstream.name+")" this.layer = upstream.layer; const layer = upstream.layer; diff --git a/Logic/FeatureSource/Sources/GeoJsonSource.ts b/Logic/FeatureSource/Sources/GeoJsonSource.ts index 165f672cf..e6d24fbca 100644 --- a/Logic/FeatureSource/Sources/GeoJsonSource.ts +++ b/Logic/FeatureSource/Sources/GeoJsonSource.ts @@ -1,14 +1,14 @@ -import {FeatureSourceForLayer} from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import {Utils} from "../../Utils"; -import FilteredLayer from "../../Models/FilteredLayer"; -import {control} from "leaflet"; - - /** * Fetches a geojson file somewhere and passes it along */ -export default class GeoJsonSource implements FeatureSourceForLayer { +import {UIEventSource} from "../../UIEventSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {Utils} from "../../../Utils"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {BBox} from "../../GeoOperations"; + + +export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name; @@ -17,6 +17,8 @@ export default class GeoJsonSource implements FeatureSourceForLayer { private readonly seenids: Set = new Set() public readonly layer: FilteredLayer; + public readonly tileIndex + public readonly bbox; public constructor(flayer: FilteredLayer, zxy?: [number, number, number]) { @@ -28,10 +30,16 @@ export default class GeoJsonSource implements FeatureSourceForLayer { this.layer = flayer; let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id); if (zxy !== undefined) { + const [z, x, y] = zxy; url = url - .replace('{z}', "" + zxy[0]) - .replace('{x}', "" + zxy[1]) - .replace('{y}', "" + zxy[2]) + .replace('{z}', "" + z) + .replace('{x}', "" + x) + .replace('{y}', "" + y) + this.tileIndex = Utils.tile_index(z, x, y) + this.bbox = BBox.fromTile(z, x, y) + } else { + this.tileIndex = Utils.tile_index(0, 0, 0) + this.bbox = BBox.global; } this.name = "GeoJsonSource of " + url; diff --git a/Logic/FeatureSource/Sources/OsmApiFeatureSource.ts b/Logic/FeatureSource/Sources/OsmApiFeatureSource.ts index de3157da2..c1c846602 100644 --- a/Logic/FeatureSource/Sources/OsmApiFeatureSource.ts +++ b/Logic/FeatureSource/Sources/OsmApiFeatureSource.ts @@ -1,9 +1,9 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import {OsmObject} from "../Osm/OsmObject"; -import {Utils} from "../../Utils"; -import Loc from "../../Models/Loc"; -import FilteredLayer from "../../Models/FilteredLayer"; +import FeatureSource from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import Loc from "../../../Models/Loc"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {Utils} from "../../../Utils"; +import {OsmObject} from "../../Osm/OsmObject"; export default class OsmApiFeatureSource implements FeatureSource { diff --git a/Logic/FeatureSource/Sources/RememberingSource.ts b/Logic/FeatureSource/Sources/RememberingSource.ts index 42b0b0ba3..99f422478 100644 --- a/Logic/FeatureSource/Sources/RememberingSource.ts +++ b/Logic/FeatureSource/Sources/RememberingSource.ts @@ -1,11 +1,10 @@ - -import FeatureSource, {FeatureSourceForLayer} from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import FilteredLayer from "../../Models/FilteredLayer"; /** * Every previously added point is remembered, but new points are added. * Data coming from upstream will always overwrite a previous value */ +import FeatureSource from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; + export default class RememberingSource implements FeatureSource { public readonly features: UIEventSource<{ feature: any, freshness: Date }[]>; diff --git a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts index 6237a2ddb..d4c316ec4 100644 --- a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts +++ b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts @@ -1,6 +1,6 @@ -import {FeatureSourceForLayer} from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import FilteredLayer from "../../Models/FilteredLayer"; +import {UIEventSource} from "../../UIEventSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer} from "../FeatureSource"; export default class SimpleFeatureSource implements FeatureSourceForLayer { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); diff --git a/Logic/FeatureSource/Sources/StaticFeatureSource.ts b/Logic/FeatureSource/Sources/StaticFeatureSource.ts index 7ffe1b02a..0cc58d656 100644 --- a/Logic/FeatureSource/Sources/StaticFeatureSource.ts +++ b/Logic/FeatureSource/Sources/StaticFeatureSource.ts @@ -8,12 +8,19 @@ export default class StaticFeatureSource implements FeatureSource { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name: string = "StaticFeatureSource" - constructor(features: any[]) { + constructor(features: any[] | UIEventSource, useFeaturesDirectly = false) { const now = new Date(); - this.features = new UIEventSource(features.map(f => ({ - feature: f, - freshness: now - }))) + if(useFeaturesDirectly){ + // @ts-ignore + this.features = features + }else if (features instanceof UIEventSource) { + this.features = features.map(features => features.map(f => ({feature: f, freshness: now}))) + } else { + this.features = new UIEventSource(features.map(f => ({ + feature: f, + freshness: now + }))) + } } diff --git a/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts index 54e1137a0..37ee94b8a 100644 --- a/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts +++ b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts @@ -1,18 +1,18 @@ -import {FeatureSourceForLayer} from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import {GeoOperations} from "../GeoOperations"; -import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; - /** * This is the part of the pipeline which introduces extra points at the center of an area (but only if this is demanded by the wayhandling) */ +import {UIEventSource} from "../../UIEventSource"; +import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; +import {GeoOperations} from "../../GeoOperations"; +import {FeatureSourceForLayer} from "../FeatureSource"; + export default class WayHandlingApplyingFeatureSource implements FeatureSourceForLayer { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name; public readonly layer; constructor(upstream: FeatureSourceForLayer) { - this.name = "Wayhandling of " + upstream.name; + this.name = "Wayhandling(" + upstream.name+")"; this.layer = upstream.layer const layer = upstream.layer.layerDef; diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts index e69de29bb..357db85d4 100644 --- a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts @@ -0,0 +1,63 @@ +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer} from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import Loc from "../../../Models/Loc"; +import DynamicTileSource from "./DynamicTileSource"; +import {Utils} from "../../../Utils"; +import GeoJsonSource from "../Sources/GeoJsonSource"; + +export default class DynamicGeoJsonTileSource extends DynamicTileSource { + constructor(layer: FilteredLayer, + registerLayer: (layer: FeatureSourceForLayer) => void, + state: { + locationControl: UIEventSource + leafletMap: any + }) { + const source = layer.layerDef.source + if (source.geojsonZoomLevel === undefined) { + throw "Invalid layer: geojsonZoomLevel expected" + } + if (source.geojsonSource === undefined) { + throw "Invalid layer: geojsonSource expected" + } + + const whitelistUrl = source.geojsonSource.replace("{z}_{x}_{y}.geojson", "overview.json") + .replace("{layer}",layer.layerDef.id) + + let whitelist = undefined + Utils.downloadJson(whitelistUrl).then( + json => { + const data = new Map>(); + for (const x in json) { + data.set(Number(x), new Set(json[x])) + } + whitelist = data + } + ).catch(err => { + console.warn("No whitelist found for ", layer.layerDef.id, err) + }) + + super( + layer, + source.geojsonZoomLevel, + (zxy) => { + if(whitelist !== undefined){ + const isWhiteListed = whitelist.get(zxy[1])?.has(zxy[2]) + if(!isWhiteListed){ + return undefined; + } + } + + const src = new GeoJsonSource( + layer, + zxy + ) + registerLayer(src) + return src + }, + state + ); + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts index 931b85d3c..43021587c 100644 --- a/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts @@ -1,22 +1,24 @@ -/*** - * A tiled source which dynamically loads the required tiles - */ + import State from "../../../State"; import FilteredLayer from "../../../Models/FilteredLayer"; -import {FeatureSourceForLayer} from "../FeatureSource"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import {Utils} from "../../../Utils"; import {UIEventSource} from "../../UIEventSource"; import Loc from "../../../Models/Loc"; +import TileHierarchy from "./TileHierarchy"; -export default class DynamicTileSource { +/*** + * A tiled source which dynamically loads the required tiles at a fixed zoom level + */ +export default class DynamicTileSource implements TileHierarchy { private readonly _loadedTiles = new Set(); - - public readonly existingTiles: Map> = new Map>() + + public readonly loadedTiles: Map; constructor( layer: FilteredLayer, zoomlevel: number, - constructTile: (xy: [number, number]) => FeatureSourceForLayer, + constructTile: (zxy: [number, number, number]) => (FeatureSourceForLayer & Tiled), state: { locationControl: UIEventSource leafletMap: any @@ -24,6 +26,8 @@ export default class DynamicTileSource { ) { state = State.state const self = this; + + this.loadedTiles = new Map() const neededTiles = state.locationControl.map( location => { if (!layer.isDisplayed.data) { @@ -45,28 +49,30 @@ export default class DynamicTileSource { const tileRange = Utils.TileRangeBetween(zoomlevel, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) const needed = Utils.MapRange(tileRange, (x, y) => Utils.tile_index(zoomlevel, x, y)).filter(i => !self._loadedTiles.has(i)) - if(needed.length === 0){ + if (needed.length === 0) { return undefined } return needed } , [layer.isDisplayed, state.leafletMap]).stabilized(250); - + neededTiles.addCallbackAndRunD(neededIndexes => { + console.log("Tiled geojson source ",layer.layerDef.id," needs", neededIndexes) + if (neededIndexes === undefined) { + return; + } for (const neededIndex of neededIndexes) { self._loadedTiles.add(neededIndex) - const xy = Utils.tile_from_index(zoomlevel, neededIndex) - const src = constructTile(xy) - let xmap = self.existingTiles.get(xy[0]) - if(xmap === undefined){ - xmap = new Map() - self.existingTiles.set(xy[0], xmap) + const src = constructTile( Utils.tile_from_index(neededIndex)) + if(src !== undefined){ + self.loadedTiles.set(neededIndex, src) } - xmap.set(xy[1], src) } }) } -} \ No newline at end of file + +} + diff --git a/Logic/FeatureSource/TiledFeatureSource/README.md b/Logic/FeatureSource/TiledFeatureSource/README.md index 36b0c1b01..93b51c6ad 100644 --- a/Logic/FeatureSource/TiledFeatureSource/README.md +++ b/Logic/FeatureSource/TiledFeatureSource/README.md @@ -1,3 +1,27 @@ Data in MapComplete can come from multiple sources. -In order to keep thins snappy, they are distributed over a tiled database \ No newline at end of file +Currently, they are: + +- The Overpass-API +- The OSM-API +- One or more GeoJSON files. This can be a single file or a set of tiled geojson files +- LocalStorage, containing features from a previous visit +- Changes made by the user introducing new features + +When the data enters from Overpass or from the OSM-API, they are first distributed per layer: + +OVERPASS | ---PerLayerFeatureSource---> FeatureSourceForLayer[] +OSM | + +The GeoJSon files (not tiled) are then added to this list + +A single FeatureSourcePerLayer is then further handled by splitting it into a tile hierarchy. + + + +In order to keep thins snappy, they are distributed over a tiled database per layer. + + +## Notes + +`cached-featuresbookcases` is the old key used `cahced-features{themeid}` and should be cleaned up \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts index e69de29bb..d905d2f65 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts @@ -0,0 +1,25 @@ +import FeatureSource, {Tiled} from "../FeatureSource"; +import {BBox} from "../../GeoOperations"; + +export default interface TileHierarchy { + + /** + * A mapping from 'tile_index' to the actual tile featrues + */ + loadedTiles: Map + +} + +export class TileHierarchyTools { + + public static getTiles(hierarchy: TileHierarchy, bbox: BBox): T[] { + const result = [] + hierarchy.loadedTiles.forEach((tile) => { + if (tile.bbox.overlapsWith(bbox)) { + result.push(tile) + } + }) + return result; + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts index 768a935fc..10d0c1742 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts @@ -1,10 +1,10 @@ -import TileHierarchy from "./TiledFeatureSource/TileHierarchy"; -import FeatureSource, {FeatureSourceForLayer, Tiled} from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import FilteredLayer from "../../Models/FilteredLayer"; -import FeatureSourceMerger from "./Sources/FeatureSourceMerger"; -import {BBox} from "../GeoOperations"; -import {Utils} from "../../Utils"; +import TileHierarchy from "./TileHierarchy"; +import {UIEventSource} from "../../UIEventSource"; +import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {Utils} from "../../../Utils"; +import {BBox} from "../../GeoOperations"; +import FeatureSourceMerger from "../Sources/FeatureSourceMerger"; export class TileHierarchyMerger implements TileHierarchy { public readonly loadedTiles: Map = new Map(); @@ -24,8 +24,9 @@ export class TileHierarchyMerger implements TileHierarchy { + public readonly z: number; + public readonly x: number; + public readonly y: number; + public readonly parent: TiledFeatureSource; + public readonly root: TiledFeatureSource + public readonly layer: FilteredLayer; + /* An index of all known tiles. allTiles[z][x][y].get('layerid') will yield the corresponding tile. + * Only defined on the root element! + */ + public readonly loadedTiles: Map = undefined; + + public readonly maxFeatureCount: number; + public readonly name; + public readonly features: UIEventSource<{ feature: any, freshness: Date }[]> + public readonly containedIds: UIEventSource> + + public readonly bbox: BBox; + private upper_left: TiledFeatureSource + private upper_right: TiledFeatureSource + private lower_left: TiledFeatureSource + private lower_right: TiledFeatureSource + private readonly maxzoom: number; + private readonly options: TiledFeatureSourceOptions + public readonly tileIndex: number; + + private constructor(z: number, x: number, y: number, parent: TiledFeatureSource, options?: TiledFeatureSourceOptions) { + this.z = z; + this.x = x; + this.y = y; + this.bbox = BBox.fromTile(z, x, y) + this.tileIndex = Utils.tile_index(z, x, y) + this.name = `TiledFeatureSource(${z},${x},${y})` + this.parent = parent; + this.layer = options.layer + options = options ?? {} + this.maxFeatureCount = options?.maxFeatureCount ?? 500; + this.maxzoom = options.maxZoomLevel ?? 18 + this.options = options; + if (parent === undefined) { + throw "Parent is not allowed to be undefined. Use null instead" + } + if (parent === null && z !== 0 && x !== 0 && y !== 0) { + throw "Invalid root tile: z, x and y should all be null" + } + if (parent === null) { + this.root = this; + this.loadedTiles = new Map() + } else { + this.root = this.parent.root; + this.loadedTiles = this.root.loadedTiles; + const i = Utils.tile_index(z, x, y) + this.root.loadedTiles.set(i, this) + } + this.features = new UIEventSource([]) + this.containedIds = this.features.map(features => { + if (features === undefined) { + return undefined; + } + return new Set(features.map(f => f.feature.properties.id)) + }) + + // We register this tile, but only when there is some data in it + if (this.options.registerTile !== undefined) { + this.features.addCallbackAndRunD(features => { + if (features.length === 0) { + return; + } + this.options.registerTile(this) + return true; + }) + } + + + } + + public static createHierarchy(features: FeatureSource, options?: TiledFeatureSourceOptions): TiledFeatureSource { + const root = new TiledFeatureSource(0, 0, 0, null, options) + features.features?.addCallbackAndRunD(feats => root.addFeatures(feats)) + return root; + } + + private isSplitNeeded(featureCount: number){ + if(this.upper_left !== undefined){ + // This tile has been split previously, so we keep on splitting + return true; + } + if(this.z >= this.maxzoom){ + // We are not allowed to split any further + return false + } + if(this.options.minZoomLevel !== undefined && this.z < this.options.minZoomLevel){ + // We must have at least this zoom level before we are allowed to start splitting + return true + } + + // To much features - we split + return featureCount > this.maxFeatureCount + + + } + + /*** + * Adds the list of features to this hierarchy. + * If there are too much features, the list will be broken down and distributed over the subtiles (only retaining features that don't fit a subtile on this level) + * @param features + * @private + */ + private addFeatures(features: { feature: any, freshness: Date }[]) { + if (features === undefined || features.length === 0) { + return; + } + + if (!this.isSplitNeeded(features.length)) { + this.features.setData(features) + return; + } + + if (this.upper_left === undefined) { + this.upper_left = new TiledFeatureSource(this.z + 1, this.x * 2, this.y * 2, this, this.options) + this.upper_right = new TiledFeatureSource(this.z + 1, this.x * 2 + 1, this.y * 2, this, this.options) + this.lower_left = new TiledFeatureSource(this.z + 1, this.x * 2, this.y * 2 + 1, this, this.options) + this.lower_right = new TiledFeatureSource(this.z + 1, this.x * 2 + 1, this.y * 2 + 1, this, this.options) + } + + const ulf = [] + const urf = [] + const llf = [] + const lrf = [] + const overlapsboundary = [] + + for (const feature of features) { + const bbox = BBox.get(feature.feature) + if (this.options.minZoomLevel === undefined) { + + + if (bbox.isContainedIn(this.upper_left.bbox)) { + ulf.push(feature) + } else if (bbox.isContainedIn(this.upper_right.bbox)) { + urf.push(feature) + } else if (bbox.isContainedIn(this.lower_left.bbox)) { + llf.push(feature) + } else if (bbox.isContainedIn(this.lower_right.bbox)) { + lrf.push(feature) + } else { + overlapsboundary.push(feature) + } + } else { + // We duplicate a feature on a boundary into every tile as we need to get to the minZoomLevel + if (bbox.overlapsWith(this.upper_left.bbox)) { + ulf.push(feature) + } + if (bbox.overlapsWith(this.upper_right.bbox)) { + urf.push(feature) + } + if (bbox.overlapsWith(this.lower_left.bbox)) { + llf.push(feature) + } + if (bbox.overlapsWith(this.lower_right.bbox)) { + lrf.push(feature) + } + } + } + this.upper_left.addFeatures(ulf) + this.upper_right.addFeatures(urf) + this.lower_left.addFeatures(llf) + this.lower_right.addFeatures(lrf) + this.features.setData(overlapsboundary) + + } +} + +export interface TiledFeatureSourceOptions { + readonly maxFeatureCount?: number, + readonly maxZoomLevel?: number, + readonly minZoomLevel?: number, + readonly registerTile?: (tile: TiledFeatureSource & Tiled) => void, + readonly layer?: FilteredLayer +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts index 58c1fcaeb..7f3e5776e 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -1,40 +1,102 @@ import FilteredLayer from "../../../Models/FilteredLayer"; -import {FeatureSourceForLayer} from "../FeatureSource"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import {UIEventSource} from "../../UIEventSource"; import Loc from "../../../Models/Loc"; -import GeoJsonSource from "../GeoJsonSource"; -import DynamicTileSource from "./DynamicTileSource"; +import TileHierarchy from "./TileHierarchy"; +import {Utils} from "../../../Utils"; +import LocalStorageSaverActor from "../Actors/LocalStorageSaverActor"; +import {BBox} from "../../GeoOperations"; + +export default class TiledFromLocalStorageSource implements TileHierarchy { + public loadedTiles: Map = new Map(); -export default class DynamicGeoJsonTileSource extends DynamicTileSource { constructor(layer: FilteredLayer, - registerLayer: (layer: FeatureSourceForLayer) => void, + handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void, state: { locationControl: UIEventSource leafletMap: any }) { - const source = layer.layerDef.source - if (source.geojsonZoomLevel === undefined) { - throw "Invalid layer: geojsonZoomLevel expected" - } - if (source.geojsonSource === undefined) { - throw "Invalid layer: geojsonSource expected" - } - super( - layer, - source.geojsonZoomLevel, - (xy) => { - const xyz: [number, number, number] = [xy[0], xy[1], source.geojsonZoomLevel] - const src = new GeoJsonSource( - layer, - xyz - ) - registerLayer(src) - return src - }, - state - ); + const prefix = LocalStorageSaverActor.storageKey + "-" + layer.layerDef.id + "-" + // @ts-ignore + const indexes: number[] = Object.keys(localStorage) + .filter(key => { + return key.startsWith(prefix) && !key.endsWith("-time"); + }) + .map(key => { + return Number(key.substring(prefix.length)); + }) + + console.log("Avaible datasets in local storage:", indexes) + + const zLevels = indexes.map(i => i % 100) + const indexesSet = new Set(indexes) + const maxZoom = Math.max(...zLevels) + const minZoom = Math.min(...zLevels) + const self = this; + + const neededTiles = state.locationControl.map( + location => { + if (!layer.isDisplayed.data) { + // No need to download! - the layer is disabled + return undefined; + } + + if (location.zoom < layer.layerDef.minzoom) { + // No need to download! - the layer is disabled + return undefined; + } + + // Yup, this is cheating to just get the bounds here + const bounds = state.leafletMap.data?.getBounds() + if (bounds === undefined) { + // We'll retry later + return undefined + } + + const needed = [] + for (let z = minZoom; z <= maxZoom; z++) { + + const tileRange = Utils.TileRangeBetween(z, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) + const neededZ = Utils.MapRange(tileRange, (x, y) => Utils.tile_index(z, x, y)) + .filter(i => !self.loadedTiles.has(i) && indexesSet.has(i)) + needed.push(...neededZ) + } + + if (needed.length === 0) { + return undefined + } + return needed + } + , [layer.isDisplayed, state.leafletMap]).stabilized(50); + + neededTiles.addCallbackAndRun(t => console.log("Tiles to load from localstorage:", t)) + + neededTiles.addCallbackAndRunD(neededIndexes => { + for (const neededIndex of neededIndexes) { + // We load the features from localStorage + try { + const key = LocalStorageSaverActor.storageKey + "-" + layer.layerDef.id + "-" + neededIndex + const data = localStorage.getItem(key) + const features = JSON.parse(data) + const src = { + layer: layer, + features: new UIEventSource<{ feature: any; freshness: Date }[]>(features), + name: "FromLocalStorage(" + key + ")", + tileIndex: neededIndex, + bbox: BBox.fromTile(...Utils.tile_from_index(neededIndex)) + } + handleFeatureSource(src, neededIndex) + self.loadedTiles.set(neededIndex, src) + } catch (e) { + console.error("Could not load data tile from local storage due to", e) + } + } + + + }) } + } \ No newline at end of file diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index 81cc3d066..9fde92799 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -1,4 +1,5 @@ import * as turf from '@turf/turf' +import {Utils} from "../Utils"; export class GeoOperations { @@ -184,6 +185,44 @@ export class GeoOperations { static lengthInMeters(feature: any) { return turf.length(feature) * 1000 } + + static buffer(feature: any, bufferSizeInMeter: number){ + return turf.buffer(feature, bufferSizeInMeter/1000, { + units: 'kilometers' + }) + } + + static bbox(feature: any){ + const [lon, lat, lon0, lat0] = turf.bbox(feature) + return { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + lon, + lat + ], + [ + lon0, + lat + ], + [ + lon0, + lat0 + ], + [ + lon, + lat0 + ], + [ + lon, + lat + ], + ] + } + } + } /** * Generates the closest point on a way from a given point @@ -340,6 +379,7 @@ export class BBox { readonly maxLon: number; readonly minLat: number; readonly minLon: number; + static global: BBox = new BBox([[-180,-90],[180,90]]); constructor(coordinates) { this.maxLat = Number.MIN_VALUE; @@ -361,12 +401,11 @@ export class BBox { return new BBox([[bounds.getWest(), bounds.getNorth()], [bounds.getEast(), bounds.getSouth()]]) } - static get(feature) { + static get(feature): BBox { if (feature.bbox?.overlapsWith === undefined) { const turfBbox: number[] = turf.bbox(feature) feature.bbox = new BBox([[turfBbox[0], turfBbox[1]], [turfBbox[2], turfBbox[3]]]); } - return feature.bbox; } @@ -407,4 +446,23 @@ export class BBox { } } + static fromTile(z: number, x: number, y: number) { + return new BBox( Utils.tile_bounds_lon_lat(z, x, y)) + } + + getEast() { + return this.maxLon + } + + getNorth() { + return this.maxLat + } + + getWest() { + return this.minLon + } + + getSouth() { + return this.minLat + } } \ No newline at end of file diff --git a/Logic/ImageProviders/Mapillary.ts b/Logic/ImageProviders/Mapillary.ts index 72a593d29..3f992dbce 100644 --- a/Logic/ImageProviders/Mapillary.ts +++ b/Logic/ImageProviders/Mapillary.ts @@ -32,7 +32,6 @@ export class Mapillary extends ImageAttributionSource { } const mapview = value.match(/https?:\/\/www.mapillary.com\/map\/im\/(.*)/) - console.log("Mapview matched ", value, mapview) if(mapview !== null){ const key = mapview[1] return {key:key, isApiv4: !isNaN(Number(key))}; diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index 1f614cfec..de1731447 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -1,15 +1,9 @@ import SimpleMetaTagger from "./SimpleMetaTagger"; -import {ExtraFunction} from "./ExtraFunction"; -import {Relation} from "./Osm/ExtractRelations"; +import {ExtraFuncParams, ExtraFunction} from "./ExtraFunction"; import {UIEventSource} from "./UIEventSource"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; -interface Params { - featuresPerLayer: Map, - memberships: Map -} - /** * Metatagging adds various tags to the elements, e.g. lat, lon, surface area, ... * @@ -22,13 +16,12 @@ export default class MetaTagging { private static readonly stopErrorOutputAt = 10; /** - * An actor which adds metatags on every feature in the given object - * The features are a list of geojson-features, with a "properties"-field and geometry + * This method (re)calculates all metatags and calculated tags on every given object. + * The given features should be part of the given layer */ static addMetatags(features: { feature: any; freshness: Date }[], - allKnownFeatures: UIEventSource<{ feature: any; freshness: Date }[]>, - relations: Map, - layers: LayerConfig[], + params: ExtraFuncParams, + layer: LayerConfig, includeDates = true) { if (features === undefined || features.length === 0) { @@ -44,66 +37,39 @@ export default class MetaTagging { metatag.addMetaTags(features); } catch (e) { console.error("Could not calculate metatag for ", metatag.keys.join(","), ":", e) - } } // The functions - per layer - which add the new keys - const layerFuncs = new Map void)>(); - for (const layer of layers) { - layerFuncs.set(layer.id, this.createRetaggingFunc(layer)); - } - - allKnownFeatures.addCallbackAndRunD(newFeatures => { - - const featuresPerLayer = new Map(); - const allFeatures = Array.from(new Set(features.concat(newFeatures))) - for (const feature of allFeatures) { - - const key = feature.feature._matching_layer_id; - if (!featuresPerLayer.has(key)) { - featuresPerLayer.set(key, []) - } - featuresPerLayer.get(key).push(feature.feature) - } + const layerFuncs = this.createRetaggingFunc(layer) + if (layerFuncs !== undefined) { for (const feature of features) { - // @ts-ignore - const key = feature.feature._matching_layer_id; - const f = layerFuncs.get(key); - if (f === undefined) { - continue; - } try { - f({featuresPerLayer: featuresPerLayer, memberships: relations}, feature.feature) + layerFuncs(params, feature.feature) } catch (e) { console.error(e) } - } - - - }) - - + } } private static createRetaggingFunc(layer: LayerConfig): - ((params: Params, feature: any) => void) { + ((params: ExtraFuncParams, feature: any) => void) { const calculatedTags: [string, string][] = layer.calculatedTags; if (calculatedTags === undefined) { return undefined; } - const functions: ((params: Params, feature: any) => void)[] = []; + const functions: ((params: ExtraFuncParams, feature: any) => void)[] = []; for (const entry of calculatedTags) { const key = entry[0] const code = entry[1]; if (code === undefined) { continue; } - + const func = new Function("feat", "return " + code + ";"); try { @@ -145,14 +111,13 @@ export default class MetaTagging { console.error("Could not create a dynamic function: ", e) } } - return (params: Params, feature) => { + return (params: ExtraFuncParams, feature) => { const tags = feature.properties if (tags === undefined) { return; } - const relations = params.memberships?.get(feature.properties.id) ?? [] - ExtraFunction.FullPatchFeature(params.featuresPerLayer, relations, feature); + ExtraFunction.FullPatchFeature(params, feature); try { for (const f of functions) { f(params, feature); diff --git a/Logic/Osm/Actions/ChangeDescription.ts b/Logic/Osm/Actions/ChangeDescription.ts index f91d794fa..575c114f7 100644 --- a/Logic/Osm/Actions/ChangeDescription.ts +++ b/Logic/Osm/Actions/ChangeDescription.ts @@ -1,15 +1,30 @@ +/** + * Represents a single change to an object + */ export interface ChangeDescription { + /** + * Identifier of the object + */ type: "node" | "way" | "relation", /** - * Negative for a new objects + * Identifier of the object + * Negative for new objects */ id: number, - /* - v = "" or v = undefined to erase this tag - */ + + /** + * All changes to tags + * v = "" or v = undefined to erase this tag + */ tags?: { k: string, v: string }[], + /** + * A change to the geometry: + * 1) Change of node location + * 2) Change of way geometry + * 3) Change of relation members (untested) + */ changes?: { lat: number, lon: number diff --git a/Logic/Osm/Geocoding.ts b/Logic/Osm/Geocoding.ts index 44915d4e1..55dd99681 100644 --- a/Logic/Osm/Geocoding.ts +++ b/Logic/Osm/Geocoding.ts @@ -11,7 +11,7 @@ export class Geocoding { osm_type: string, osm_id: string }[]) => void), onFail: (() => void)) { - const b = State.state.leafletMap.data.getBounds(); + const b = State.state.currentBounds.data; const url = Geocoding.host + "format=json&limit=1&viewbox=" + `${b.getEast()},${b.getNorth()},${b.getWest()},${b.getSouth()}` + "&accept-language=nl&q=" + query; diff --git a/Logic/Osm/Overpass.ts b/Logic/Osm/Overpass.ts index f6ef4c83e..2f82889e3 100644 --- a/Logic/Osm/Overpass.ts +++ b/Logic/Osm/Overpass.ts @@ -1,7 +1,7 @@ import * as OsmToGeoJson from "osmtogeojson"; import Bounds from "../../Models/Bounds"; import {TagsFilter} from "../Tags/TagsFilter"; -import ExtractRelations from "./ExtractRelations"; +import RelationsTracker from "./RelationsTracker"; import {Utils} from "../../Utils"; import {UIEventSource} from "../UIEventSource"; @@ -15,16 +15,20 @@ export class Overpass { private readonly _timeout: UIEventSource; private readonly _extraScripts: string[]; private _includeMeta: boolean; - + private _relationTracker: RelationsTracker; + + constructor(filter: TagsFilter, extraScripts: string[], interpreterUrl: UIEventSource, timeout: UIEventSource, + relationTracker: RelationsTracker, includeMeta = true) { this._timeout = timeout; this._interpreterUrl = interpreterUrl; this._filter = filter this._extraScripts = extraScripts; this._includeMeta = includeMeta; + this._relationTracker = relationTracker } queryGeoJson(bounds: Bounds, continuation: ((any, date: Date) => void), onFail: ((reason) => void)): void { @@ -35,6 +39,7 @@ export class Overpass { console.log("Using testing URL") query = Overpass.testUrl; } + const self = this; Utils.downloadJson(query) .then(json => { if (json.elements === [] && ((json.remarks ?? json.remark).indexOf("runtime error") >= 0)) { @@ -44,13 +49,15 @@ export class Overpass { } - ExtractRelations.RegisterRelations(json) + self._relationTracker.RegisterRelations(json) // @ts-ignore const geojson = OsmToGeoJson.default(json); const osmTime = new Date(json.osm3s.timestamp_osm_base); continuation(geojson, osmTime); - }).catch(onFail) + }).catch(e => { + onFail(e); + }) } buildQuery(bbox: string): string { diff --git a/Logic/Osm/RelationsTracker.ts b/Logic/Osm/RelationsTracker.ts index 735b49512..f0528e77d 100644 --- a/Logic/Osm/RelationsTracker.ts +++ b/Logic/Osm/RelationsTracker.ts @@ -1,4 +1,5 @@ import State from "../../State"; +import {UIEventSource} from "../UIEventSource"; export interface Relation { id: number, @@ -13,11 +14,15 @@ export interface Relation { properties: any } -export default class ExtractRelations { +export default class RelationsTracker { - public static RegisterRelations(overpassJson: any): void { - const memberships = ExtractRelations.BuildMembershipTable(ExtractRelations.GetRelationElements(overpassJson)) - State.state.knownRelations.setData(memberships) + public knownRelations = new UIEventSource>(new Map(), "Relation memberships"); + + constructor() { + } + + public RegisterRelations(overpassJson: any): void { + this.UpdateMembershipTable(RelationsTracker.GetRelationElements(overpassJson)) } /** @@ -25,7 +30,7 @@ export default class ExtractRelations { * @param overpassJson * @constructor */ - public static GetRelationElements(overpassJson: any): Relation[] { + private static GetRelationElements(overpassJson: any): Relation[] { const relations = overpassJson.elements .filter(element => element.type === "relation" && element.tags.type !== "multipolygon") for (const relation of relations) { @@ -39,12 +44,11 @@ export default class ExtractRelations { * @param relations * @constructor */ - public static BuildMembershipTable(relations: Relation[]): Map { - const memberships = new Map() - + private UpdateMembershipTable(relations: Relation[]): void { + const memberships = this.knownRelations.data + let changed = false; for (const relation of relations) { for (const member of relation.members) { - const role = { role: member.role, relation: relation @@ -53,11 +57,21 @@ export default class ExtractRelations { if (!memberships.has(key)) { memberships.set(key, []) } - memberships.get(key).push(role) + const knownRelations = memberships.get(key) + + const alreadyExists = knownRelations.some(knownRole => { + return knownRole.role === role.role && knownRole.relation === role.relation + }) + if (!alreadyExists) { + knownRelations.push(role) + changed = true; + } } } + if (changed) { + this.knownRelations.ping() + } - return memberships } } \ No newline at end of file diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 7440a067a..6e8f3e0fc 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -9,6 +9,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 CountryCoder from "latlon2country/index"; const cardinalDirections = { @@ -20,7 +21,7 @@ const cardinalDirections = { export default class SimpleMetaTagger { - static coder: any; + private static coder: CountryCoder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); public static readonly objectMetaInfo = new SimpleMetaTagger( { keys: ["_last_edit:contributor", @@ -84,7 +85,7 @@ export default class SimpleMetaTagger { }, (feature => { const units = Utils.NoNull([].concat(...State.state?.layoutToUse?.data?.layers?.map(layer => layer.units ?? []))); - if(units.length == 0){ + if (units.length == 0) { return; } let rewritten = false; @@ -93,7 +94,7 @@ export default class SimpleMetaTagger { continue; } for (const unit of units) { - if(unit.appliesToKeys === undefined){ + if (unit.appliesToKeys === undefined) { console.error("The unit ", unit, "has no appliesToKey defined") continue } @@ -148,7 +149,7 @@ export default class SimpleMetaTagger { const lat = centerPoint.geometry.coordinates[1]; const lon = centerPoint.geometry.coordinates[0]; - SimpleMetaTagger.GetCountryCodeFor(lon, lat, (countries) => { + SimpleMetaTagger.coder?.GetCountryCodeFor(lon, lat, (countries: string[]) => { try { const oldCountry = feature.properties["_country"]; feature.properties["_country"] = countries[0].trim().toLowerCase(); @@ -160,7 +161,7 @@ export default class SimpleMetaTagger { } catch (e) { console.warn(e) } - }); + }) } ) private static isOpen = new SimpleMetaTagger( @@ -426,11 +427,7 @@ export default class SimpleMetaTagger { } } - static GetCountryCodeFor(lon: number, lat: number, callback: (country: string) => void) { - SimpleMetaTagger.coder?.GetCountryCodeFor(lon, lat, callback) - } - - static HelpText(): BaseUIElement { + public static HelpText(): BaseUIElement { const subElements: (string | BaseUIElement)[] = [ new Combine([ new Title("Metatags", 1), @@ -453,7 +450,7 @@ export default class SimpleMetaTagger { return new Combine(subElements).SetClass("flex-col") } - addMetaTags(features: { feature: any, freshness: Date }[]) { + public addMetaTags(features: { feature: any, freshness: Date }[]) { for (let i = 0; i < features.length; i++) { let feature = features[i]; this._f(feature.feature, i, feature.freshness); diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index d1c85f226..705c6174a 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -81,9 +81,12 @@ export class UIEventSource { return this; } - public addCallbackAndRun(callback: ((latestData: T) => void)): UIEventSource { - callback(this.data); - return this.addCallback(callback); + public addCallbackAndRun(callback: ((latestData: T) => (boolean | void | any))): UIEventSource { + const doDeleteCallback = callback(this.data); + if (!doDeleteCallback) { + this.addCallback(callback); + } + return this; } public setData(t: T): UIEventSource { diff --git a/Models/Constants.ts b/Models/Constants.ts index 539ab8c6f..0b62cc0cc 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.9.12"; + public static vNumber = "0.10.0"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { @@ -26,12 +26,6 @@ export default class Constants { */ static updateTimeoutSec: number = 30; - /** - * If zoom >= useOsmApiAt, then the OSM api will be used directly. - * If undefined, use overpass exclusively - */ - static useOsmApiAt = undefined; - private static isRetina(): boolean { if (Utils.runningFromConsole) { return; diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index 3068d25fd..f337b1a19 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -59,10 +59,9 @@ export interface LayerConfigJson { * NOTE: the previous format was 'overpassTags: AndOrTagConfigJson | string', which is interpreted as a shorthand for source: {osmTags: "key=value"} * While still supported, this is considered deprecated */ - source: { osmTags: AndOrTagConfigJson | string } | - { osmTags: AndOrTagConfigJson | string, geoJson: string, geoJsonZoomLevel?: number, isOsmCache?: boolean } | - { osmTags: AndOrTagConfigJson | string, overpassScript: string } - + source: { osmTags: AndOrTagConfigJson | string, overpassScript?: string } | + { osmTags: AndOrTagConfigJson | string, geoJson: string, geoJsonZoomLevel?: number, isOsmCache?: boolean } + /** * * A list of extra tags to calculate, specified as "keyToAssignTo=javascript-expression". diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index b57b6bab3..112810112 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -246,14 +246,6 @@ export default class LayoutConfig { return icons } - public LayerIndex(): Map { - const index = new Map(); - for (const layer of this.layers) { - index.set(layer.id, layer) - } - return index; - } - /** * Replaces all the relative image-urls with a fixed image url * This is to fix loading from external sources diff --git a/Models/ThemeConfig/SourceConfig.ts b/Models/ThemeConfig/SourceConfig.ts index db9d21f4f..1d223bd2b 100644 --- a/Models/ThemeConfig/SourceConfig.ts +++ b/Models/ThemeConfig/SourceConfig.ts @@ -2,18 +2,18 @@ import {TagsFilter} from "../../Logic/Tags/TagsFilter"; export default class SourceConfig { - osmTags?: TagsFilter; - overpassScript?: string; - geojsonSource?: string; - geojsonZoomLevel?: number; - isOsmCacheLayer: boolean; + public readonly osmTags?: TagsFilter; + public readonly overpassScript?: string; + public readonly geojsonSource?: string; + public readonly geojsonZoomLevel?: number; + public readonly isOsmCacheLayer: boolean; constructor(params: { osmTags?: TagsFilter, overpassScript?: string, geojsonSource?: string, isOsmCache?: boolean, - geojsonSourceLevel?: number + geojsonSourceLevel?: number, }, context?: string) { let defined = 0; diff --git a/State.ts b/State.ts index 27e9a251f..8c08873cb 100644 --- a/State.ts +++ b/State.ts @@ -11,16 +11,14 @@ import InstalledThemes from "./Logic/Actors/InstalledThemes"; import BaseLayer from "./Models/BaseLayer"; import Loc from "./Models/Loc"; import Constants from "./Models/Constants"; - -import OverpassFeatureSource from "./Logic/Actors/OverpassFeatureSource"; import TitleHandler from "./Logic/Actors/TitleHandler"; import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader"; -import {Relation} from "./Logic/Osm/ExtractRelations"; -import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource"; +import OsmApiFeatureSource from "./Logic/FeatureSource/Sources/OsmApiFeatureSource"; import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; import FilteredLayer from "./Models/FilteredLayer"; import ChangeToElementsActor from "./Logic/Actors/ChangeToElementsActor"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; +import {BBox} from "./Logic/GeoOperations"; /** * Contains the global state: a bunch of UI-event sources @@ -57,8 +55,6 @@ export default class State { public favouriteLayers: UIEventSource; - public layerUpdater: OverpassFeatureSource; - public osmApiFeatureSource: OsmApiFeatureSource; public filteredLayers: UIEventSource = new UIEventSource([], "filteredLayers"); @@ -71,12 +67,6 @@ export default class State { "Selected element" ); - /** - * Keeps track of relations: which way is part of which other way? - * Set by the overpass-updater; used in the metatagging - */ - public readonly knownRelations = new UIEventSource>(undefined, "Relation memberships"); - public readonly featureSwitchUserbadge: UIEventSource; public readonly featureSwitchSearch: UIEventSource; public readonly featureSwitchBackgroundSlection: UIEventSource; @@ -96,6 +86,7 @@ export default class State { public readonly featureSwitchExportAsPdf: UIEventSource; public readonly overpassUrl: UIEventSource; public readonly overpassTimeout: UIEventSource; + public readonly overpassMaxZoom: UIEventSource = new UIEventSource(undefined); public featurePipeline: FeaturePipeline; @@ -104,6 +95,12 @@ export default class State { * The map location: currently centered lat, lon and zoom */ public readonly locationControl = new UIEventSource(undefined, "locationControl"); + + /** + * The current visible extent of the screen + */ + public readonly currentBounds = new UIEventSource(undefined) + public backgroundLayer; public readonly backgroundLayerId: UIEventSource; @@ -398,7 +395,7 @@ export default class State { new ChangeToElementsActor(this.changes, this.allElements) - this.osmApiFeatureSource = new OsmApiFeatureSource(Constants.useOsmApiAt, this) + this.osmApiFeatureSource = new OsmApiFeatureSource(this) new PendingChangesUploader(this.changes, this.selectedElement); diff --git a/UI/Base/Img.ts b/UI/Base/Img.ts index 947536b05..876265b86 100644 --- a/UI/Base/Img.ts +++ b/UI/Base/Img.ts @@ -10,6 +10,9 @@ export default class Img extends BaseUIElement { fallbackImage?: string }) { super(); + if(src === undefined || src === "undefined"){ + throw "Undefined src for image" + } this._src = src; this._rawSvg = rawSvg; this._options = options; diff --git a/UI/Base/Minimap.ts b/UI/Base/Minimap.ts index 0c8cf3600..26909a00e 100644 --- a/UI/Base/Minimap.ts +++ b/UI/Base/Minimap.ts @@ -1,208 +1,30 @@ import BaseUIElement from "../BaseUIElement"; -import * as L from "leaflet"; -import {Map} from "leaflet"; -import {UIEventSource} from "../../Logic/UIEventSource"; import Loc from "../../Models/Loc"; import BaseLayer from "../../Models/BaseLayer"; -import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; -import {Utils} from "../../Utils"; +import {BBox} from "../../Logic/GeoOperations"; +import {UIEventSource} from "../../Logic/UIEventSource"; -export default class Minimap extends BaseUIElement { +export interface MinimapOptions { + background?: UIEventSource, + location?: UIEventSource, + bounds?: UIEventSource, + allowMoving?: boolean, + leafletOptions?: any, + attribution?: BaseUIElement | boolean, + onFullyLoaded?: (leaflet: L.Map) => void, + leafletMap?: UIEventSource, + lastClickLocation?: UIEventSource<{ lat: number, lon: number }> +} - private static _nextId = 0; - public readonly leafletMap: UIEventSource - private readonly _id: string; - private readonly _background: UIEventSource; - private readonly _location: UIEventSource; - private _isInited = false; - private _allowMoving: boolean; - private readonly _leafletoptions: any; - private readonly _onFullyLoaded: (leaflet: L.Map) => void - private readonly _attribution: BaseUIElement | boolean; - private readonly _lastClickLocation: UIEventSource<{ lat: number; lon: number }>; +export default class Minimap { + /** + * A stub implementation. The actual implementation is injected later on, but only in the browser. + * importing leaflet crashes node-ts, which is pretty annoying considering the fact that a lot of scripts use it + */ - constructor(options?: { - background?: UIEventSource, - location?: UIEventSource, - allowMoving?: boolean, - leafletOptions?: any, - attribution?: BaseUIElement | boolean, - onFullyLoaded?: (leaflet: L.Map) => void, - leafletMap?: UIEventSource, - lastClickLocation?: UIEventSource<{ lat: number, lon: number }> - } - ) { - super() - options = options ?? {} - this.leafletMap = options.leafletMap ?? new UIEventSource(undefined) - this._background = options?.background ?? new UIEventSource(AvailableBaseLayers.osmCarto) - this._location = options?.location ?? new UIEventSource({lat: 0, lon: 0, zoom: 1}) - this._id = "minimap" + Minimap._nextId; - this._allowMoving = options.allowMoving ?? true; - this._leafletoptions = options.leafletOptions ?? {} - this._onFullyLoaded = options.onFullyLoaded - this._attribution = options.attribution - this._lastClickLocation = options.lastClickLocation; - Minimap._nextId++ + /** + * Construct a minimap + */ + public static createMiniMap: (options: MinimapOptions) => BaseUIElement & { readonly leafletMap: UIEventSource } - } - - protected InnerConstructElement(): HTMLElement { - const div = document.createElement("div") - div.id = this._id; - div.style.height = "100%" - div.style.width = "100%" - div.style.minWidth = "40px" - div.style.minHeight = "40px" - div.style.position = "relative" - const wrapper = document.createElement("div") - wrapper.appendChild(div) - const self = this; - // @ts-ignore - const resizeObserver = new ResizeObserver(_ => { - self.InitMap(); - self.leafletMap?.data?.invalidateSize() - }); - - resizeObserver.observe(div); - return wrapper; - - } - - private InitMap() { - if (this._constructedHtmlElement === undefined) { - // This element isn't initialized yet - return; - } - - if (document.getElementById(this._id) === null) { - // not yet attached, we probably got some other event - return; - } - - if (this._isInited) { - return; - } - this._isInited = true; - const location = this._location; - const self = this; - let currentLayer = this._background.data.layer() - const options = { - center: <[number, number]>[location.data?.lat ?? 0, location.data?.lon ?? 0], - zoom: location.data?.zoom ?? 2, - layers: [currentLayer], - zoomControl: false, - attributionControl: this._attribution !== undefined, - dragging: this._allowMoving, - scrollWheelZoom: this._allowMoving, - doubleClickZoom: this._allowMoving, - keyboard: this._allowMoving, - touchZoom: this._allowMoving, - // Disabling this breaks the geojson layer - don't ask me why! zoomAnimation: this._allowMoving, - fadeAnimation: this._allowMoving, - } - - Utils.Merge(this._leafletoptions, options) - - const map = L.map(this._id, options); - if (self._onFullyLoaded !== undefined) { - - currentLayer.on("load", () => { - console.log("Fully loaded all tiles!") - self._onFullyLoaded(map) - }) - } - - // Users are not allowed to zoom to the 'copies' on the left and the right, stuff goes wrong then - // We give a bit of leeway for people on the edges - // Also see: https://www.reddit.com/r/openstreetmap/comments/ih4zzc/mapcomplete_a_new_easytouse_editor/g31ubyv/ - - map.setMaxBounds( - [[-100, -200], [100, 200]] - ); - - if (this._attribution !== undefined) { - if (this._attribution === true) { - map.attributionControl.setPrefix(false) - } else { - map.attributionControl.setPrefix( - ""); - } - } - - this._background.addCallbackAndRun(layer => { - const newLayer = layer.layer() - if (currentLayer !== undefined) { - map.removeLayer(currentLayer); - } - currentLayer = newLayer; - if (self._onFullyLoaded !== undefined) { - - currentLayer.on("load", () => { - console.log("Fully loaded all tiles!") - self._onFullyLoaded(map) - }) - } - map.addLayer(newLayer); - map.setMaxZoom(layer.max_zoom ?? map.getMaxZoom()) - if (self._attribution !== true && self._attribution !== false) { - self._attribution?.AttachTo('leaflet-attribution') - } - - }) - - - let isRecursing = false; - map.on("moveend", function () { - if (isRecursing) { - return - } - if (map.getZoom() === location.data.zoom && - map.getCenter().lat === location.data.lat && - map.getCenter().lng === location.data.lon) { - return; - } - location.data.zoom = map.getZoom(); - location.data.lat = map.getCenter().lat; - location.data.lon = map.getCenter().lng; - isRecursing = true; - location.ping(); - isRecursing = false; // This is ugly, I know - }) - - - location.addCallback(loc => { - const mapLoc = map.getCenter() - const dlat = Math.abs(loc.lat - mapLoc[0]) - const dlon = Math.abs(loc.lon - mapLoc[1]) - - if (dlat < 0.000001 && dlon < 0.000001 && map.getZoom() === loc.zoom) { - return; - } - map.setView([loc.lat, loc.lon], loc.zoom) - }) - - location.map(loc => loc.zoom) - .addCallback(zoom => { - if (Math.abs(map.getZoom() - zoom) > 0.1) { - map.setZoom(zoom, {}); - } - }) - - - if (this._lastClickLocation) { - const lastClickLocation = this._lastClickLocation - map.on("click", function (e) { - // @ts-ignore - lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}) - }); - - map.on("contextmenu", function (e) { - // @ts-ignore - lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}); - }); - } - - this.leafletMap.setData(map) - } } \ No newline at end of file diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts index e69de29bb..a55bc7e38 100644 --- a/UI/Base/MinimapImplementation.ts +++ b/UI/Base/MinimapImplementation.ts @@ -0,0 +1,215 @@ +import {Utils} from "../../Utils"; +import BaseUIElement from "../BaseUIElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Loc from "../../Models/Loc"; +import BaseLayer from "../../Models/BaseLayer"; +import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; +import {BBox} from "../../Logic/GeoOperations"; +import * as L from "leaflet"; +import {Map} from "leaflet"; +import Minimap, {MinimapOptions} from "./Minimap"; + +export default class MinimapImplementation extends BaseUIElement { + private static _nextId = 0; + public readonly leafletMap: UIEventSource + private readonly _id: string; + private readonly _background: UIEventSource; + private readonly _location: UIEventSource; + private _isInited = false; + private _allowMoving: boolean; + private readonly _leafletoptions: any; + private readonly _onFullyLoaded: (leaflet: L.Map) => void + private readonly _attribution: BaseUIElement | boolean; + private readonly _lastClickLocation: UIEventSource<{ lat: number; lon: number }>; + private readonly _bounds: UIEventSource | undefined; + + private constructor(options: MinimapOptions) { + super() + options = options ?? {} + this.leafletMap = options.leafletMap ?? new UIEventSource(undefined) + this._background = options?.background ?? new UIEventSource(AvailableBaseLayers.osmCarto) + this._location = options?.location ?? new UIEventSource({lat: 0, lon: 0, zoom: 1}) + this._bounds = options?.bounds; + this._id = "minimap" + MinimapImplementation._nextId; + this._allowMoving = options.allowMoving ?? true; + this._leafletoptions = options.leafletOptions ?? {} + this._onFullyLoaded = options.onFullyLoaded + this._attribution = options.attribution + this._lastClickLocation = options.lastClickLocation; + MinimapImplementation._nextId++ + + } + + public static initialize() { + Minimap.createMiniMap = options => new MinimapImplementation(options) + } + + protected InnerConstructElement(): HTMLElement { + const div = document.createElement("div") + div.id = this._id; + div.style.height = "100%" + div.style.width = "100%" + div.style.minWidth = "40px" + div.style.minHeight = "40px" + div.style.position = "relative" + const wrapper = document.createElement("div") + wrapper.appendChild(div) + const self = this; + // @ts-ignore + const resizeObserver = new ResizeObserver(_ => { + self.InitMap(); + self.leafletMap?.data?.invalidateSize() + }); + + resizeObserver.observe(div); + return wrapper; + + } + + private InitMap() { + if (this._constructedHtmlElement === undefined) { + // This element isn't initialized yet + return; + } + + if (document.getElementById(this._id) === null) { + // not yet attached, we probably got some other event + return; + } + + if (this._isInited) { + return; + } + this._isInited = true; + const location = this._location; + const self = this; + let currentLayer = this._background.data.layer() + const options = { + center: <[number, number]>[location.data?.lat ?? 0, location.data?.lon ?? 0], + zoom: location.data?.zoom ?? 2, + layers: [currentLayer], + zoomControl: false, + attributionControl: this._attribution !== undefined, + dragging: this._allowMoving, + scrollWheelZoom: this._allowMoving, + doubleClickZoom: this._allowMoving, + keyboard: this._allowMoving, + touchZoom: this._allowMoving, + // Disabling this breaks the geojson layer - don't ask me why! zoomAnimation: this._allowMoving, + fadeAnimation: this._allowMoving, + } + + Utils.Merge(this._leafletoptions, options) + + const map = L.map(this._id, options); + if (self._onFullyLoaded !== undefined) { + + currentLayer.on("load", () => { + console.log("Fully loaded all tiles!") + self._onFullyLoaded(map) + }) + } + + // Users are not allowed to zoom to the 'copies' on the left and the right, stuff goes wrong then + // We give a bit of leeway for people on the edges + // Also see: https://www.reddit.com/r/openstreetmap/comments/ih4zzc/mapcomplete_a_new_easytouse_editor/g31ubyv/ + + map.setMaxBounds( + [[-100, -200], [100, 200]] + ); + + if (this._attribution !== undefined) { + if (this._attribution === true) { + map.attributionControl.setPrefix(false) + } else { + map.attributionControl.setPrefix( + ""); + } + } + + this._background.addCallbackAndRun(layer => { + const newLayer = layer.layer() + if (currentLayer !== undefined) { + map.removeLayer(currentLayer); + } + currentLayer = newLayer; + if (self._onFullyLoaded !== undefined) { + + currentLayer.on("load", () => { + console.log("Fully loaded all tiles!") + self._onFullyLoaded(map) + }) + } + map.addLayer(newLayer); + map.setMaxZoom(layer.max_zoom ?? map.getMaxZoom()) + if (self._attribution !== true && self._attribution !== false) { + self._attribution?.AttachTo('leaflet-attribution') + } + + }) + + + let isRecursing = false; + map.on("moveend", function () { + if (isRecursing) { + return + } + if (map.getZoom() === location.data.zoom && + map.getCenter().lat === location.data.lat && + map.getCenter().lng === location.data.lon) { + return; + } + location.data.zoom = map.getZoom(); + location.data.lat = map.getCenter().lat; + location.data.lon = map.getCenter().lng; + isRecursing = true; + location.ping(); + + if (self._bounds !== undefined) { + self._bounds.setData(BBox.fromLeafletBounds(map.getBounds())) + } + + + isRecursing = false; // This is ugly, I know + }) + + + location.addCallback(loc => { + const mapLoc = map.getCenter() + const dlat = Math.abs(loc.lat - mapLoc[0]) + const dlon = Math.abs(loc.lon - mapLoc[1]) + + if (dlat < 0.000001 && dlon < 0.000001 && map.getZoom() === loc.zoom) { + return; + } + map.setView([loc.lat, loc.lon], loc.zoom) + }) + + location.map(loc => loc.zoom) + .addCallback(zoom => { + if (Math.abs(map.getZoom() - zoom) > 0.1) { + map.setZoom(zoom, {}); + } + }) + + if (self._bounds !== undefined) { + self._bounds.setData(BBox.fromLeafletBounds(map.getBounds())) + } + + + if (this._lastClickLocation) { + const lastClickLocation = this._lastClickLocation + map.on("click", function (e) { + // @ts-ignore + lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}) + }); + + map.on("contextmenu", function (e) { + // @ts-ignore + lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}); + }); + } + + this.leafletMap.setData(map) + } +} \ No newline at end of file diff --git a/UI/BigComponents/AllDownloads.ts b/UI/BigComponents/AllDownloads.ts index 353a4c38b..2170eb496 100644 --- a/UI/BigComponents/AllDownloads.ts +++ b/UI/BigComponents/AllDownloads.ts @@ -32,7 +32,7 @@ export default class AllDownloads extends ScrollableFullScreen { freeDivId: "belowmap", background: State.state.backgroundLayer, location: State.state.locationControl, - features: State.state.featurePipeline.features, + features: State.state.featurePipeline, layout: State.state.layoutToUse, }).isRunning.addCallbackAndRun(isRunning => isExporting.setData(isRunning)) } diff --git a/UI/BigComponents/Attribution.ts b/UI/BigComponents/Attribution.ts index 2d322a961..7e70fa367 100644 --- a/UI/BigComponents/Attribution.ts +++ b/UI/BigComponents/Attribution.ts @@ -5,19 +5,19 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import UserDetails from "../../Logic/Osm/OsmConnection"; import Constants from "../../Models/Constants"; import Loc from "../../Models/Loc"; -import * as L from "leaflet" import {VariableUiElement} from "../Base/VariableUIElement"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {BBox} from "../../Logic/GeoOperations"; /** * The bottom right attribution panel in the leaflet map */ export default class Attribution extends Combine { - constructor(location: UIEventSource, + constructor(location: UIEventSource, userDetails: UIEventSource, layoutToUse: UIEventSource, - leafletMap: UIEventSource) { + currentBounds: UIEventSource) { const mapComplete = new Link(`Mapcomplete ${Constants.vNumber}`, 'https://github.com/pietervdvn/MapComplete', true); const reportBug = new Link(Svg.bug_ui().SetClass("small-image"), "https://github.com/pietervdvn/MapComplete/issues", true); @@ -43,7 +43,7 @@ export default class Attribution extends Combine { if (userDetails.csCount < Constants.userJourney.tagsVisibleAndWikiLinked) { return undefined; } - const bounds: any = leafletMap?.data?.getBounds(); + const bounds: any = currentBounds.data; if (bounds === undefined) { return undefined } @@ -55,7 +55,7 @@ export default class Attribution extends Combine { const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}` return new Link(Svg.josm_logo_ui().SetClass("small-image"), josmLink, true); }, - [location, leafletMap] + [location, currentBounds] ) ) super([mapComplete, reportBug, stats, editHere, editWithJosm, mapillary]); diff --git a/UI/BigComponents/AttributionPanel.ts b/UI/BigComponents/AttributionPanel.ts index 7a0bcef81..542326314 100644 --- a/UI/BigComponents/AttributionPanel.ts +++ b/UI/BigComponents/AttributionPanel.ts @@ -26,10 +26,13 @@ export default class AttributionPanel extends Combine { ((layoutToUse.data.maintainer ?? "") == "") ? "" : Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.data.maintainer}), layoutToUse.data.credits, "
", - new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.leafletMap), + new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.currentBounds), "
", new VariableUiElement(contributions.map(contributions => { + if(contributions === undefined){ + return "" + } const sorted = Array.from(contributions, ([name, value]) => ({ name, value diff --git a/UI/BigComponents/DownloadPanel.ts b/UI/BigComponents/DownloadPanel.ts index e291bf6b7..466b8d217 100644 --- a/UI/BigComponents/DownloadPanel.ts +++ b/UI/BigComponents/DownloadPanel.ts @@ -2,54 +2,113 @@ import {SubtleButton} from "../Base/SubtleButton"; import Svg from "../../Svg"; import Translations from "../i18n/Translations"; import State from "../../State"; -import {FeatureSourceUtils} from "../../Logic/FeatureSource/FeatureSource"; import {Utils} from "../../Utils"; import Combine from "../Base/Combine"; import CheckBoxes from "../Input/Checkboxes"; -import {GeoOperations} from "../../Logic/GeoOperations"; +import {BBox, GeoOperations} from "../../Logic/GeoOperations"; import Toggle from "../Input/Toggle"; import Title from "../Base/Title"; +import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import SimpleMetaTagger from "../../Logic/SimpleMetaTagger"; +import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {meta} from "@turf/turf"; export class DownloadPanel extends Toggle { + constructor() { + const state: { + featurePipeline: FeaturePipeline, + layoutToUse: UIEventSource, + currentBounds: UIEventSource + } = State.state + + const t = Translations.t.general.download - const somethingLoaded = State.state.featurePipeline.features.map(features => features.length > 0); + const name = State.state.layoutToUse.data.id; + const includeMetaToggle = new CheckBoxes([t.includeMetaData.Clone()]) const metaisIncluded = includeMetaToggle.GetValue().map(selected => selected.length > 0) + + const buttonGeoJson = new SubtleButton(Svg.floppy_ui(), new Combine([t.downloadGeojson.Clone().SetClass("font-bold"), t.downloadGeoJsonHelper.Clone()]).SetClass("flex flex-col")) .onClick(() => { - const geojson = FeatureSourceUtils.extractGeoJson(State.state.featurePipeline, {metadata: metaisIncluded.data}) - const name = State.state.layoutToUse.data.id; - Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson), + const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data) + Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson, null, " "), `MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.geojson`, { mimetype: "application/vnd.geo+json" }); }) + const buttonCSV = new SubtleButton(Svg.floppy_ui(), new Combine( [t.downloadCSV.Clone().SetClass("font-bold"), t.downloadCSVHelper.Clone()]).SetClass("flex flex-col")) .onClick(() => { - const geojson = FeatureSourceUtils.extractGeoJson(State.state.featurePipeline, {metadata: metaisIncluded.data}) + const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data) const csv = GeoOperations.toCSV(geojson.features) - const name = State.state.layoutToUse.data.id; Utils.offerContentsAsDownloadableFile(csv, `MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.csv`, { mimetype: "text/csv" }); - - }) + const downloadButtons = new Combine( - [new Title(t.title), buttonGeoJson, buttonCSV, includeMetaToggle, t.licenseInfo.Clone().SetClass("link-underline")]) + [new Title(t.title), + buttonGeoJson, + buttonCSV, + includeMetaToggle, + t.licenseInfo.Clone().SetClass("link-underline")]) .SetClass("w-full flex flex-col border-4 border-gray-300 rounded-3xl p-4") super( downloadButtons, t.noDataLoaded.Clone(), - somethingLoaded) + state.featurePipeline.somethingLoaded) + } + + private static getCleanGeoJson(state: { + featurePipeline: FeaturePipeline, + currentBounds: UIEventSource + }, includeMetaData: boolean) { + + const resultFeatures = [] + const featureList = state.featurePipeline.GetAllFeaturesWithin(state.currentBounds.data); + for (const tile of featureList) { + for (const feature of tile) { + const cleaned = { + type: feature.type, + geometry: feature.geometry, + properties: {...feature.properties} + } + + if (!includeMetaData) { + for (const key in cleaned.properties) { + if (key === "_lon" || key === "_lat") { + continue; + } + if (key.startsWith("_")) { + delete feature.properties[key] + } + } + } + + const datedKeys = [].concat(SimpleMetaTagger.metatags.filter(tagging => tagging.includesDates).map(tagging => tagging.keys)) + for (const key of datedKeys) { + delete feature.properties[key] + } + + resultFeatures.push(feature) + } + } + + return { + type:"FeatureCollection", + features: featureList + } + } } \ No newline at end of file diff --git a/UI/BigComponents/FullWelcomePaneWithTabs.ts b/UI/BigComponents/FullWelcomePaneWithTabs.ts index a73ac1ce5..eba8b60b3 100644 --- a/UI/BigComponents/FullWelcomePaneWithTabs.ts +++ b/UI/BigComponents/FullWelcomePaneWithTabs.ts @@ -15,6 +15,7 @@ import ScrollableFullScreen from "../Base/ScrollableFullScreen"; import BaseUIElement from "../BaseUIElement"; import Toggle from "../Input/Toggle"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {Utils} from "../../Utils"; export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { @@ -62,9 +63,15 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse, isShown) const tabsWithAboutMc = [...FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse, isShown)] + + const now = new Date() + const date = now.getFullYear()+"-"+Utils.TwoDigits(now.getMonth()+1)+"-"+Utils.TwoDigits(now.getDate()) + const osmcha_link = `https://osmcha.org/?filters=%7B%22date__gte%22%3A%5B%7B%22label%22%3A%22${date}%22%2C%22value%22%3A%222021-01-01%22%7D%5D%2C%22editor%22%3A%5B%7B%22label%22%3A%22mapcomplete%22%2C%22value%22%3A%22mapcomplete%22%7D%5D%7D` + tabsWithAboutMc.push({ header: Svg.help, - content: new Combine([Translations.t.general.aboutMapcomplete.Clone(), "
Version " + Constants.vNumber]) + content: new Combine([Translations.t.general.aboutMapcomplete.Clone() + .Subs({"osmcha_link": osmcha_link}), "
Version " + Constants.vNumber]) .SetClass("link-underline") } ); diff --git a/UI/BigComponents/ImportButton.ts b/UI/BigComponents/ImportButton.ts index 1d690bae6..f62b912fd 100644 --- a/UI/BigComponents/ImportButton.ts +++ b/UI/BigComponents/ImportButton.ts @@ -52,7 +52,7 @@ export default class ImportButton extends Toggle { const withLoadingCheck = new Toggle( t.stillLoading, new Combine([button, appliedTags]).SetClass("flex flex-col"), - State.state.layerUpdater.runningQuery + State.state.featurePipeline.runningQuery ) super(t.hasBeenImported, withLoadingCheck, isImported) } diff --git a/UI/BigComponents/LeftControls.ts b/UI/BigComponents/LeftControls.ts index db58abac6..b3e848d4d 100644 --- a/UI/BigComponents/LeftControls.ts +++ b/UI/BigComponents/LeftControls.ts @@ -9,18 +9,21 @@ import MapControlButton from "../MapControlButton"; import Svg from "../../Svg"; import AllDownloads from "./AllDownloads"; import FilterView from "./FilterView"; -import FeatureSource from "../../Logic/FeatureSource/FeatureSource"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; +import {BBox} from "../../Logic/GeoOperations"; +import Loc from "../../Models/Loc"; export default class LeftControls extends Combine { - constructor(featureSource: FeatureSource) { + constructor(state: {featurePipeline: FeaturePipeline, currentBounds: UIEventSource, locationControl: UIEventSource}) { const toggledCopyright = new ScrollableFullScreen( () => Translations.t.general.attribution.attributionTitle.Clone(), () => new AttributionPanel( State.state.layoutToUse, - new ContributorCount(featureSource).Contributors + new ContributorCount(state).Contributors ), undefined ); diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 6a3729bc5..673f1d325 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -65,10 +65,6 @@ export default class SimpleAddUI extends Toggle { State.state.selectedElement.setData(State.state.allElements.ContainingFeatures.get( newElementAction.newElementId )) - console.log("Did set selected element to", State.state.allElements.ContainingFeatures.get( - newElementAction.newElementId - )) - } const addUi = new VariableUiElement( @@ -104,7 +100,7 @@ export default class SimpleAddUI extends Toggle { new Toggle( Translations.t.general.add.stillLoading.Clone().SetClass("alert"), addUi, - State.state.layerUpdater.runningQuery + State.state.featurePipeline.runningQuery ), Translations.t.general.add.zoomInFurther.Clone().SetClass("alert"), State.state.locationControl.map(loc => loc.zoom >= Constants.userJourney.minZoomLevelToAddNewPoints) @@ -150,7 +146,6 @@ export default class SimpleAddUI extends Toggle { } const tags = TagUtils.KVtoProperties(preset.tags ?? []); - console.log("Opening precise input ", preset.preciseInput, "with tags", tags) preciseInput = new LocationInput({ mapBackground: backgroundLayer, centerLocation: locationSrc, @@ -215,10 +210,7 @@ export default class SimpleAddUI extends Toggle { const disableFiltersOrConfirm = new Toggle( openLayerOrConfirm, disableFilter, - preset.layerToAddTo.appliedFilters.map(filters => { - console.log("Current filters are ", filters) - return filters === undefined || filters.normalize().and.length === 0; - }) + preset.layerToAddTo.appliedFilters.map(filters => filters === undefined || filters.normalize().and.length === 0) ) diff --git a/UI/CenterMessageBox.ts b/UI/CenterMessageBox.ts index 62efc2ead..fe1ef5c65 100644 --- a/UI/CenterMessageBox.ts +++ b/UI/CenterMessageBox.ts @@ -6,7 +6,7 @@ export default class CenterMessageBox extends VariableUiElement { constructor() { const state = State.state; - const updater = State.state.layerUpdater; + const updater = State.state.featurePipeline; const t = Translations.t.centerMessage; const message = updater.runningQuery.map( isRunning => { diff --git a/UI/ExportPDF.ts b/UI/ExportPDF.ts index aea7561b0..f1a113daa 100644 --- a/UI/ExportPDF.ts +++ b/UI/ExportPDF.ts @@ -1,3 +1,19 @@ + + +import jsPDF from "jspdf"; +import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter"; +import {UIEventSource} from "../Logic/UIEventSource"; +import Minimap from "./Base/Minimap"; +import Loc from "../Models/Loc"; +import {BBox} from "../Logic/GeoOperations"; +import BaseLayer from "../Models/BaseLayer"; +import {FixedUiElement} from "./Base/FixedUiElement"; +import Translations from "./i18n/Translations"; +import State from "../State"; +import Constants from "../Models/Constants"; +import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; +import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline"; +import ShowDataLayer from "./ShowDataLayer/ShowDataLayer"; /** * Creates screenshoter to take png screenshot * Creates jspdf and downloads it @@ -8,21 +24,6 @@ * - add new layout in "PDFLayout" * -> in there are more instructions */ - -import jsPDF from "jspdf"; -import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter"; -import {UIEventSource} from "../Logic/UIEventSource"; -import Minimap from "./Base/Minimap"; -import Loc from "../Models/Loc"; -import {BBox} from "../Logic/GeoOperations"; -import ShowDataLayer from "./ShowDataLayer"; -import BaseLayer from "../Models/BaseLayer"; -import {FixedUiElement} from "./Base/FixedUiElement"; -import Translations from "./i18n/Translations"; -import State from "../State"; -import Constants from "../Models/Constants"; -import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; - export default class ExportPDF { // dimensions of the map in milimeter public isRunning = new UIEventSource(true) @@ -39,7 +40,7 @@ export default class ExportPDF { freeDivId: string, location: UIEventSource, background?: UIEventSource - features: UIEventSource<{ feature: any }[]>, + features: FeaturePipeline, layout: UIEventSource } ) { @@ -57,7 +58,7 @@ export default class ExportPDF { zoom: l.zoom + 1 } - const minimap = new Minimap({ + const minimap = Minimap.createMiniMap({ location: new UIEventSource(loc), // We remove the link between the old and the new UI-event source as moving the map while the export is running fucks up the screenshot background: options.background, allowMoving: false, @@ -83,24 +84,21 @@ export default class ExportPDF { minimap.AttachTo(options.freeDivId) // Next: we prepare the features. Only fully contained features are shown - const bounded = options.features.map(feats => { - - const leaflet = minimap.leafletMap.data; - if (leaflet === undefined) { - return feats - } + minimap.leafletMap .addCallbackAndRunD(leaflet => { const bounds = BBox.fromLeafletBounds(leaflet.getBounds().pad(0.2)) - return feats.filter(f => BBox.get(f.feature).isContainedIn(bounds)) - - }, [minimap.leafletMap]) - - // Add the features to the minimap - new ShowDataLayer( - bounded, - minimap.leafletMap, - options.layout, - false - ) + options.features.GetTilesPerLayerWithin(bounds, tile => { + console.log("REndering", tile.name) + new ShowDataLayer( + { + features: tile, + leafletMap: minimap.leafletMap, + layerToShow: tile.layer.layerDef, + enablePopups: false + } + ) + }) + + }) } diff --git a/UI/Image/AttributedImage.ts b/UI/Image/AttributedImage.ts index 69835e105..7614077c5 100644 --- a/UI/Image/AttributedImage.ts +++ b/UI/Image/AttributedImage.ts @@ -4,6 +4,7 @@ import Img from "../Base/Img"; import ImageAttributionSource from "../../Logic/ImageProviders/ImageAttributionSource"; import BaseUIElement from "../BaseUIElement"; import {VariableUiElement} from "../Base/VariableUIElement"; +import Loading from "../Base/Loading"; export class AttributedImage extends Combine { @@ -16,8 +17,13 @@ export class AttributedImage extends Combine { img = new Img(urlSource); attr = new Attribution(imgSource.GetAttributionFor(urlSource), imgSource.SourceIcon()) } else { - img = new VariableUiElement(preparedUrl.map(url => new Img(url, false, {fallbackImage: './assets/svg/blocked.svg'}))) - attr = new VariableUiElement(preparedUrl.map(url => new Attribution(imgSource.GetAttributionFor(urlSource), imgSource.SourceIcon()))) + img = new VariableUiElement(preparedUrl.map(url => { + if(url === undefined){ + return new Loading() + } + return new Img(url, false, {fallbackImage: './assets/svg/blocked.svg'}); + })) + attr = new VariableUiElement(preparedUrl.map(_ => new Attribution(imgSource.GetAttributionFor(urlSource), imgSource.SourceIcon()))) } diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index 6c6b15948..108065b68 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -6,13 +6,13 @@ import BaseUIElement from "../BaseUIElement"; import {FixedUiElement} from "../Base/FixedUiElement"; import {Utils} from "../../Utils"; import Loc from "../../Models/Loc"; +import Minimap from "../Base/Minimap"; /** * Selects a direction in degrees */ export default class DirectionInput extends InputElement { - public static constructMinimap: ((any) => BaseUIElement); public readonly IsSelected: UIEventSource = new UIEventSource(false); private readonly _location: UIEventSource; private readonly value: UIEventSource; @@ -40,7 +40,7 @@ export default class DirectionInput extends InputElement { let map: BaseUIElement = new FixedUiElement("") if (!Utils.runningFromConsole) { - map = DirectionInput.constructMinimap({ + map = Minimap.createMiniMap({ background: this.background, allowMoving: false, location: this._location diff --git a/UI/Input/LengthInput.ts b/UI/Input/LengthInput.ts index 3162f5a87..4eb39d7e9 100644 --- a/UI/Input/LengthInput.ts +++ b/UI/Input/LengthInput.ts @@ -5,7 +5,7 @@ import Svg from "../../Svg"; import {Utils} from "../../Utils"; import Loc from "../../Models/Loc"; import {GeoOperations} from "../../Logic/GeoOperations"; -import DirectionInput from "./DirectionInput"; +import Minimap from "../Base/Minimap"; /** @@ -41,7 +41,7 @@ export default class LengthInput extends InputElement { // @ts-ignore let map = undefined if (!Utils.runningFromConsole) { - map = DirectionInput.constructMinimap({ + map = Minimap.createMiniMap({ background: this.background, allowMoving: false, location: this._location, diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index 875fce9cb..a18c887f8 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -8,35 +8,31 @@ import Svg from "../../Svg"; import State from "../../State"; import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; import {GeoOperations} from "../../Logic/GeoOperations"; -import ShowDataLayer from "../ShowDataLayer"; -import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; import * as L from "leaflet"; +import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; +import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; export default class LocationInput extends InputElement { - private static readonly matchLayout = new UIEventSource(new LayoutConfig({ - description: "Matchpoint style", - icon: "./assets/svg/crosshair-empty.svg", - id: "matchpoint", - language: ["en"], - layers: [{ + private static readonly matchLayer = new LayerConfig( + { id: "matchpoint", source: { osmTags: {and: []} }, icon: "./assets/svg/crosshair-empty.svg" - }], - maintainer: "MapComplete", - startLat: 0, - startLon: 0, - startZoom: 0, - title: "Location input", - version: "0" - - })); + }, "matchpoint icon", true + ) + IsSelected: UIEventSource = new UIEventSource(false); public readonly snappedOnto: UIEventSource = new UIEventSource(undefined) private _centerLocation: UIEventSource; private readonly mapBackground: UIEventSource; + /** + * The features to which the input should be snapped + * @private + */ private readonly _snapTo: UIEventSource<{ feature: any }[]> private readonly _value: UIEventSource private readonly _snappedPoint: UIEventSource @@ -143,7 +139,7 @@ export default class LocationInput extends InputElement { protected InnerConstructElement(): HTMLElement { try { const clickLocation = new UIEventSource(undefined); - const map = new Minimap( + const map = Minimap.createMiniMap( { location: this._centerLocation, background: this.mapBackground, @@ -198,7 +194,6 @@ export default class LocationInput extends InputElement { }) if (this._snapTo !== undefined) { - new ShowDataLayer(this._snapTo, map.leafletMap, State.state.layoutToUse, false, false) const matchPoint = this._snappedPoint.map(loc => { if (loc === undefined) { @@ -207,16 +202,25 @@ export default class LocationInput extends InputElement { return [{feature: loc}]; }) if (this._snapTo) { - let layout = LocationInput.matchLayout - if (this._snappedPointTags !== undefined) { - layout = State.state.layoutToUse + if (this._snappedPointTags === undefined) { + // No special tags - we show a default crosshair + new ShowDataLayer({ + features: new StaticFeatureSource(matchPoint), + enablePopups: false, + zoomToFeatures: false, + leafletMap: map.leafletMap, + layerToShow: LocationInput.matchLayer + }) + }else{ + new ShowDataMultiLayer({ + features: new StaticFeatureSource(matchPoint), + enablePopups: false, + zoomToFeatures: false, + leafletMap: map.leafletMap, + layers: State.state.filteredLayers + } + ) } - new ShowDataLayer( - matchPoint, - map.leafletMap, - layout, - false, false - ) } } diff --git a/UI/Popup/SplitRoadWizard.ts b/UI/Popup/SplitRoadWizard.ts index 9185cf24c..ca0980592 100644 --- a/UI/Popup/SplitRoadWizard.ts +++ b/UI/Popup/SplitRoadWizard.ts @@ -4,7 +4,7 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import {SubtleButton} from "../Base/SubtleButton"; import Minimap from "../Base/Minimap"; import State from "../../State"; -import ShowDataLayer from "../ShowDataLayer"; +import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; import {GeoOperations} from "../../Logic/GeoOperations"; import {LeafletMouseEvent} from "leaflet"; import Combine from "../Base/Combine"; @@ -13,10 +13,16 @@ import Translations from "../i18n/Translations"; import SplitAction from "../../Logic/Osm/Actions/SplitAction"; import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; import Title from "../Base/Title"; -import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; export default class SplitRoadWizard extends Toggle { - private static splitLayout = new UIEventSource(SplitRoadWizard.GetSplitLayout()) + private static splitLayerStyling = new LayerConfig({ + id: "splitpositions", + source: {osmTags: "_cutposition=yes"}, + icon: "./assets/svg/plus.svg" + }, "(BUILTIN) SplitRoadWizard.ts", true) /** * A UI Element used for splitting roads @@ -36,7 +42,7 @@ export default class SplitRoadWizard extends Toggle { const splitClicked = new UIEventSource(false); // Minimap on which you can select the points to be splitted - const miniMap = new Minimap({background: State.state.backgroundLayer, allowMoving: false}); + const miniMap = Minimap.createMiniMap({background: State.state.backgroundLayer, allowMoving: false}); miniMap.SetStyle("width: 100%; height: 24rem;"); // Define how a cut is displayed on the map @@ -45,8 +51,20 @@ export default class SplitRoadWizard extends Toggle { const roadElement = State.state.allElements.ContainingFeatures.get(id) const roadEventSource = new UIEventSource([{feature: roadElement, freshness: new Date()}]); // Datalayer displaying the road and the cut points (if any) - new ShowDataLayer(roadEventSource, miniMap.leafletMap, State.state.layoutToUse, false, true); - new ShowDataLayer(splitPoints, miniMap.leafletMap, SplitRoadWizard.splitLayout, false, false) + new ShowDataMultiLayer({ + features: new StaticFeatureSource(roadEventSource, true), + layers: State.state.filteredLayers, + leafletMap: miniMap.leafletMap, + enablePopups: false, + zoomToFeatures: true + }) + new ShowDataLayer({ + features: new StaticFeatureSource(splitPoints, true), + leafletMap: miniMap.leafletMap, + zoomToFeatures: false, + enablePopups: false, + layerToShow: SplitRoadWizard.splitLayerStyling + }) /** * Handles a click on the overleaf map. @@ -135,21 +153,4 @@ export default class SplitRoadWizard extends Toggle { const confirm = new Toggle(mapView, splitToggle, splitClicked); super(t.hasBeenSplit.Clone(), confirm, hasBeenSplit) } - - private static GetSplitLayout(): LayoutConfig { - return new LayoutConfig({ - maintainer: "mapcomplete", - language: ["en"], - startLon: 0, - startLat: 0, - description: "Split points visualisations - built in at SplitRoadWizard.ts", - icon: "", startZoom: 0, - title: "Split locations", - version: "", - - id: "splitpositions", - layers: [{id: "splitpositions", source: {osmTags: "_cutposition=yes"}, icon: "./assets/svg/plus.svg"}] - }, true, "(BUILTIN) SplitRoadWizard.ts") - - } } \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 661e51d07..927a1008d 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -1,19 +1,11 @@ /** * The data layer shows all the given geojson elements with the appropriate icon etc */ -import {UIEventSource} from "../Logic/UIEventSource"; -import * as L from "leaflet" -import State from "../State"; -import FeatureInfoBox from "./Popup/FeatureInfoBox"; -import LayerConfig from "../Models/ThemeConfig/LayerConfig"; -import FeatureSource from "../Logic/FeatureSource/FeatureSource"; - -export interface ShowDataLayerOptions { - features: FeatureSource, - leafletMap: UIEventSource, - enablePopups?: true | boolean, - zoomToFeatures? : false | boolean, -} +import {UIEventSource} from "../../Logic/UIEventSource"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import FeatureInfoBox from "../Popup/FeatureInfoBox"; +import State from "../../State"; +import {ShowDataLayerOptions} from "./ShowDataLayerOptions"; export default class ShowDataLayer { diff --git a/UI/ShowDataLayer/ShowDataLayerOptions.ts b/UI/ShowDataLayer/ShowDataLayerOptions.ts index e69de29bb..349472351 100644 --- a/UI/ShowDataLayer/ShowDataLayerOptions.ts +++ b/UI/ShowDataLayer/ShowDataLayerOptions.ts @@ -0,0 +1,9 @@ +import FeatureSource from "../../Logic/FeatureSource/FeatureSource"; +import {UIEventSource} from "../../Logic/UIEventSource"; + +export interface ShowDataLayerOptions { + features: FeatureSource, + leafletMap: UIEventSource, + enablePopups?: true | boolean, + zoomToFeatures?: false | boolean, +} \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataMultiLayer.ts b/UI/ShowDataLayer/ShowDataMultiLayer.ts index 5bff575cb..924274586 100644 --- a/UI/ShowDataLayer/ShowDataMultiLayer.ts +++ b/UI/ShowDataLayer/ShowDataMultiLayer.ts @@ -1,11 +1,12 @@ -import {UIEventSource} from "../Logic/UIEventSource"; -import FilteredLayer from "../Models/FilteredLayer"; -import ShowDataLayer, {ShowDataLayerOptions} from "./ShowDataLayer/ShowDataLayer"; -import PerLayerFeatureSourceSplitter from "../Logic/FeatureSource/PerLayerFeatureSourceSplitter"; - /** * SHows geojson on the given leaflet map, but attempts to figure out the correct layer first */ +import {UIEventSource} from "../../Logic/UIEventSource"; +import ShowDataLayer from "./ShowDataLayer"; +import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"; +import FilteredLayer from "../../Models/FilteredLayer"; +import {ShowDataLayerOptions} from "./ShowDataLayerOptions"; + export default class ShowDataMultiLayer { constructor(options: ShowDataLayerOptions & { layers: UIEventSource }) { diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 6af9e5a57..7814cbbdc 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -5,7 +5,6 @@ import {ImageCarousel} from "./Image/ImageCarousel"; import Combine from "./Base/Combine"; import {FixedUiElement} from "./Base/FixedUiElement"; import {ImageUploadFlow} from "./Image/ImageUploadFlow"; - import ShareButton from "./BigComponents/ShareButton"; import Svg from "../Svg"; import ReviewElement from "./Reviews/ReviewElement"; @@ -13,7 +12,6 @@ import MangroveReviews from "../Logic/Web/MangroveReviews"; import Translations from "./i18n/Translations"; import ReviewForm from "./Reviews/ReviewForm"; import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"; - import State from "../State"; import {ImageSearcher} from "../Logic/Actors/ImageSearcher"; import BaseUIElement from "./BaseUIElement"; @@ -26,6 +24,9 @@ import BaseLayer from "../Models/BaseLayer"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; import ImportButton from "./BigComponents/ImportButton"; import {Tag} from "../Logic/Tags/Tag"; +import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; +import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer"; +import Minimap from "./Base/Minimap"; export interface SpecialVisualization { funcName: string, @@ -37,14 +38,6 @@ export interface SpecialVisualization { export default class SpecialVisualizations { - - static constructMiniMap: (options?: { - background?: UIEventSource, - location?: UIEventSource, - allowMoving?: boolean, - leafletOptions?: any - }) => BaseUIElement; - static constructShowDataLayer: (features: UIEventSource<{ feature: any; freshness: Date }[]>, leafletMap: UIEventSource, layoutToUse: UIEventSource, enablePopups?: boolean, zoomToFeatures?: boolean) => any; public static specialVisualizations: SpecialVisualization[] = [ { @@ -153,7 +146,7 @@ export default class SpecialVisualizations { lon: Number(properties._lon), zoom: zoom }) - const minimap = SpecialVisualizations.constructMiniMap( + const minimap = Minimap.createMiniMap( { background: state.backgroundLayer, location: locationSource, @@ -169,12 +162,14 @@ export default class SpecialVisualizations { } }) - SpecialVisualizations.constructShowDataLayer( - featuresToShow, - minimap["leafletMap"], - State.state.layoutToUse, - false, - true + new ShowDataMultiLayer( + { + leafletMap: minimap["leafletMap"], + enablePopups : false, + zoomToFeatures: true, + layers: State.state.filteredLayers, + features: new StaticFeatureSource(featuresToShow, true) + } ) diff --git a/Utils.ts b/Utils.ts index 4cfc52520..606dccb32 100644 --- a/Utils.ts +++ b/Utils.ts @@ -245,7 +245,6 @@ export class Utils { } dict.set(k, v()); return dict.get(k); - } /** @@ -259,6 +258,26 @@ export class Utils { return [[Utils.tile2lat(y, z), Utils.tile2long(x, z)], [Utils.tile2lat(y + 1, z), Utils.tile2long(x + 1, z)]] } + static tile_bounds_lon_lat(z: number, x: number, y: number): [[number, number], [number, number]] { + return [[Utils.tile2long(x, z),Utils.tile2lat(y, z)], [Utils.tile2long(x + 1, z), Utils.tile2lat(y + 1, z)]] + } + + static tile_index(z: number, x: number, y: number):number{ + return ((x * (2 << z)) + y) * 100 + z + } + + /** + * Given a tile index number, returns [z, x, y] + * @param index + * @returns 'zxy' + */ + static tile_from_index(index: number) : [number, number, number]{ + const z = index % 100; + const factor = 2 << z + index = Math.floor(index / 100) + return [z, Math.floor(index / factor), index % factor] + } + /** * Return x, y of the tile containing (lat, lon) on the given zoom level */ @@ -422,13 +441,6 @@ export class Utils { return bestColor ?? hex; } - public static setDefaults(options, defaults) { - for (let key in defaults) { - if (!(key in options)) options[key] = defaults[key]; - } - return options; - } - private static tile2long(x, z) { return (x / Math.pow(2, z) * 360 - 180); } diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 7663f584b..f6acd494b 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -48,7 +48,7 @@ } }, "calculatedTags": [ - "_closest_other_drinking_water_id=feat.closest('drinking_water').id", + "_closest_other_drinking_water_id=feat.closest('drinking_water')?.id", "_closest_other_drinking_water_distance=Math.floor(feat.distanceTo(feat.closest('drinking_water')) * 1000)" ], "minzoom": 13, diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index e70dc2b78..fd1043c8a 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -401,5 +401,43 @@ } ] } + ], + "filter": [ + { + "options": [ + { + "question": { + "en": "Wheelchair accessible" + }, + "osmTags": "wheelchair=yes" + } + ] + }, + { + "options": [ + { + "question": { + "en": "Has a changing table" + }, + "osmTags": "changing_table=yes" + } + ] + }, + { + "options": [ + { + "question": { + "en": "Free to use" + }, + "osmTags": { + "or": [ + "fee=no", + "fee=0", + "charge=0" + ] + } + } + ] + } ] } \ No newline at end of file diff --git a/assets/themes/bookcases/bookcases.json b/assets/themes/bookcases/bookcases.json index 625dfa0ea..0862fbb37 100644 --- a/assets/themes/bookcases/bookcases.json +++ b/assets/themes/bookcases/bookcases.json @@ -39,7 +39,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1, "roamingRenderings": [], "layers": [ "public_bookcase" diff --git a/assets/themes/drinking_water/drinking_water.json b/assets/themes/drinking_water/drinking_water.json index 9dee5a8a3..2134d0fba 100644 --- a/assets/themes/drinking_water/drinking_water.json +++ b/assets/themes/drinking_water/drinking_water.json @@ -34,7 +34,7 @@ "defaultBackgroundId": "CartoDB.Voyager", "startLon": 4.351697, "startZoom": 16, - "widenFactor": 0.05, + "widenFactor": 2, "layers": [ "drinking_water" ], diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index 4b45e9899..4616fae02 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -55,6 +55,7 @@ { "#": "Nature reserve overview from cache, points only, z < 13", "builtin": "nature_reserve", + "wayHandling": 1, "override": { "source": { "osmTags": { @@ -63,6 +64,7 @@ ] }, "geoJson": "https://pietervdvn.github.io/natuurpunt_cache/natuurpunt_nature_reserve_points.geojson", + "geoJsonZoomLevel": 0, "isOsmCache": "duplicate" }, "minzoom": 1, diff --git a/assets/themes/speelplekken/speelplekken.json b/assets/themes/speelplekken/speelplekken.json index 14e03b4d9..21cb17212 100644 --- a/assets/themes/speelplekken/speelplekken.json +++ b/assets/themes/speelplekken/speelplekken.json @@ -152,7 +152,7 @@ ] }, "geoJson": "https://pietervdvn.github.io/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson", - "geoJsonZoomLevel": 14, + "geoJsonZoomLevel": 11, "isOsmCache": true }, "title": { diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 552f9c1eb..654bb2177 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -131,7 +131,7 @@ ] }, "then": { - "en": "This object has no house number" + "en": "This building has no house number" } } ] diff --git a/index.ts b/index.ts index 06f2ea851..0614e40a2 100644 --- a/index.ts +++ b/index.ts @@ -8,31 +8,16 @@ import MoreScreen from "./UI/BigComponents/MoreScreen"; import State from "./State"; import Combine from "./UI/Base/Combine"; import Translations from "./UI/i18n/Translations"; - - -import CountryCoder from "latlon2country" - -import SimpleMetaTagger from "./Logic/SimpleMetaTagger"; -import Minimap from "./UI/Base/Minimap"; -import DirectionInput from "./UI/Input/DirectionInput"; -import SpecialVisualizations from "./UI/SpecialVisualizations"; -import ShowDataLayer from "./UI/ShowDataLayer"; -import * as L from "leaflet"; import ValidatedTextField from "./UI/Input/ValidatedTextField"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; import Constants from "./Models/Constants"; +import MinimapImplementation from "./UI/Base/MinimapImplementation"; +MinimapImplementation.initialize() // Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts -SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); -DirectionInput.constructMinimap = options => new Minimap(options) ValidatedTextField.bestLayerAt = (location, layerPref) => AvailableBaseLayers.SelectBestLayerAccordingTo(location, layerPref) -SpecialVisualizations.constructMiniMap = options => new Minimap(options) -SpecialVisualizations.constructShowDataLayer = (features: UIEventSource<{ feature: any, freshness: Date }[]>, - leafletMap: UIEventSource, - layoutToUse: UIEventSource, - enablePopups = true, - zoomToFeatures = false) => new ShowDataLayer(features, leafletMap, layoutToUse, enablePopups, zoomToFeatures) + let defaultLayout = "" // --------------------- Special actions based on the parameters ----------------- diff --git a/langs/en.json b/langs/en.json index 09709dc9f..fdfc46fe5 100644 --- a/langs/en.json +++ b/langs/en.json @@ -159,7 +159,7 @@ "noTagsSelected": "No tags selected", "testing": "Testing - changes won't be saved", "customThemeIntro": "

Custom themes

These are previously visited user-generated themes.", - "aboutMapcomplete": "

About MapComplete

With MapComplete you can enrich OpenStreetMap with information on a single theme. Answer a few questions, and within minutes your contributions will be available around the globe! The theme maintainer defines elements, questions and languages for the theme.

Find out more

MapComplete always offers the next step to learn more about OpenStreetMap.

  • When embedded in a website, the iframe links to a full-screen MapComplete
  • The full-screen version offers information about OpenStreetMap
  • Viewing works without login, but editing requires an OSM login.
  • If you are not logged in, you are asked to log in
  • Once you answered a single question, you can add new points to the map
  • After a while, actual OSM-tags are shown, later linking to the wiki


Did you notice an issue? Do you have a feature request? Want to help translate? Head over to the source code or issue tracker.

Want to see your progress? Follow the edit count on OsmCha.

", + "aboutMapcomplete": "

About MapComplete

With MapComplete you can enrich OpenStreetMap with information on a single theme. Answer a few questions, and within minutes your contributions will be available around the globe! The theme maintainer defines elements, questions and languages for the theme.

Find out more

MapComplete always offers the next step to learn more about OpenStreetMap.

  • When embedded in a website, the iframe links to a full-screen MapComplete
  • The full-screen version offers information about OpenStreetMap
  • Viewing works without login, but editing requires an OSM login.
  • If you are not logged in, you are asked to log in
  • Once you answered a single question, you can add new points to the map
  • After a while, actual OSM-tags are shown, later linking to the wiki


Did you notice an issue? Do you have a feature request? Want to help translate? Head over to the source code or issue tracker.

Want to see your progress? Follow the edit count on OsmCha.

", "backgroundMap": "Background map", "openTheMap": "Open the map", "loginOnlyNeededToEdit": "if you want to edit the map", diff --git a/langs/layers/en.json b/langs/layers/en.json index d7910039a..9149f1966 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -3021,6 +3021,29 @@ } }, "toilet": { + "filter": { + "0": { + "options": { + "0": { + "question": "Wheelchair accessible" + } + } + }, + "1": { + "options": { + "0": { + "question": "Has a changing table" + } + } + }, + "2": { + "options": { + "0": { + "question": "Free to use" + } + } + } + }, "name": "Toilets", "presets": { "0": { diff --git a/langs/nl.json b/langs/nl.json index d774fea79..3b24c50cd 100644 --- a/langs/nl.json +++ b/langs/nl.json @@ -192,7 +192,7 @@ "getStartedNewAccount": " of maak een nieuwe account aan", "noTagsSelected": "Geen tags geselecteerd", "customThemeIntro": "

Onofficiële thema's

De onderstaande thema's heb je eerder bezocht en zijn gemaakt door andere OpenStreetMappers.", - "aboutMapcomplete": "

Over MapComplete

Met MapComplete kun je OpenStreetMap verrijken met informatie over een bepaald thema. Beantwoord enkele vragen, en binnen een paar minuten is jouw bijdrage wereldwijd beschikbaar! De maker van het thema bepaalt de elementen, vragen en taalversies voor het thema.

Ontdek meer

MapComplete biedt altijd de volgende stap naar meer OpenStreetMap:

  • Indien ingebed in een website linkt het iframe naar de volledige MapComplete
  • De volledige versie heeft uitleg over OpenStreetMap
  • Bekijken kan altijd, maar wijzigen vereist een OSM-account
  • Als je niet aangemeld bent, wordt je gevraagd dit te doen
  • Als je minstens één vraag hebt beantwoord, kan je ook elementen toevoegen
  • Heb je genoeg changesets, dan verschijnen de OSM-tags, nog later links naar de wiki

Merk je een bug of wil je een extra feature? Wil je helpen vertalen? Bezoek dan de broncode en issue tracker.

Wil je je vorderingen zien? Volg de edits op OsmCha.

", + "aboutMapcomplete": "

Over MapComplete

Met MapComplete kun je OpenStreetMap verrijken met informatie over een bepaald thema. Beantwoord enkele vragen, en binnen een paar minuten is jouw bijdrage wereldwijd beschikbaar! De maker van het thema bepaalt de elementen, vragen en taalversies voor het thema.

Ontdek meer

MapComplete biedt altijd de volgende stap naar meer OpenStreetMap:

  • Indien ingebed in een website linkt het iframe naar de volledige MapComplete
  • De volledige versie heeft uitleg over OpenStreetMap
  • Bekijken kan altijd, maar wijzigen vereist een OSM-account
  • Als je niet aangemeld bent, wordt je gevraagd dit te doen
  • Als je minstens één vraag hebt beantwoord, kan je ook elementen toevoegen
  • Heb je genoeg changesets, dan verschijnen de OSM-tags, nog later links naar de wiki

Merk je een bug of wil je een extra feature? Wil je helpen vertalen? Bezoek dan de broncode en issue tracker.

Wil je je vorderingen zien? Volg de edits op OsmCha.

", "backgroundMap": "Achtergrondkaart", "layerSelection": { "zoomInToSeeThisLayer": "Vergroot de kaart om deze laag te zien", diff --git a/langs/themes/en.json b/langs/themes/en.json index 94eaf954b..e96960d80 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1351,6 +1351,11 @@ "title": { "render": "Known address" } + }, + "2": { + "title": { + "render": "{name}" + } } }, "shortDescription": "Help to build an open dataset of UK addresses", diff --git a/package.json b/package.json index 8baeb62fd..31f2aaa7d 100644 --- a/package.json +++ b/package.json @@ -20,10 +20,10 @@ "reset:translations": "ts-node scripts/generateTranslations.ts --ignore-weblate", "generate:layouts": "ts-node scripts/generateLayouts.ts", "generate:docs": "ts-node scripts/generateDocs.ts && ts-node scripts/generateTaginfoProjectFiles.ts", - "generate:cache:speelplekken:mini": "npm run generate:layeroverview && ts-node scripts/generateCache.ts speelplekken 14 ../pietervdvn.github.io/speelplekken_cache/ 51.181710380278176 4.423413276672363 51.193007664772495 4.444141387939452", + "generate:cache:speelplekken:mini": "ts-node scripts/generateCache.ts speelplekken 14 ../pietervdvn.github.io/speelplekken_cache_mini/ 51.181710380278176 4.423413276672363 51.193007664772495 4.444141387939452", "generate:cache:speelplekken": "npm run generate:layeroverview && ts-node scripts/generateCache.ts speelplekken 14 ../pietervdvn.github.io/speelplekken_cache/ 51.20 4.35 51.09 4.56", "generate:cache:natuurpunt": "npm run generate:layeroverview && ts-node scripts/generateCache.ts natuurpunt 12 ../pietervdvn.github.io/natuurpunt_cache/ 50.40 2.1 51.54 6.4 --generate-point-overview nature_reserve,visitor_information_centre", - "generate:layeroverview": "npm run generate:licenses && echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json && ts-node scripts/generateLayerOverview.ts --no-fail", + "generate:layeroverview": "echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json && ts-node scripts/generateLayerOverview.ts --no-fail", "generate:licenses": "ts-node scripts/generateLicenseInfo.ts --no-fail", "query:licenses": "ts-node scripts/generateLicenseInfo.ts --query", "generate:report": "cd Docs/Tools && ./compileStats.sh && git commit . -m 'New statistics ands graphs' && git push", diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts index 702c40f4d..d150215e7 100644 --- a/scripts/ScriptUtils.ts +++ b/scripts/ScriptUtils.ts @@ -7,7 +7,6 @@ import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; Utils.runningFromConsole = true - export default class ScriptUtils { diff --git a/scripts/generateCache.ts b/scripts/generateCache.ts index c043bc224..c894d5442 100644 --- a/scripts/generateCache.ts +++ b/scripts/generateCache.ts @@ -2,27 +2,32 @@ * Generates a collection of geojson files based on an overpass query for a given theme */ import {Utils} from "../Utils"; + Utils.runningFromConsole = true + import {Overpass} from "../Logic/Osm/Overpass"; -import * as fs from "fs"; import {existsSync, readFileSync, writeFileSync} from "fs"; import {TagsFilter} from "../Logic/Tags/TagsFilter"; import {Or} from "../Logic/Tags/Or"; import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; -import ExtractRelations from "../Logic/Osm/ExtractRelations"; +import RelationsTracker from "../Logic/Osm/RelationsTracker"; import * as OsmToGeoJson from "osmtogeojson"; import MetaTagging from "../Logic/MetaTagging"; -import {GeoOperations} from "../Logic/GeoOperations"; import {UIEventSource} from "../Logic/UIEventSource"; import {TileRange} from "../Models/TileRange"; import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; -import LayerConfig from "../Models/ThemeConfig/LayerConfig"; import ScriptUtils from "./ScriptUtils"; +import PerLayerFeatureSourceSplitter from "../Logic/FeatureSource/PerLayerFeatureSourceSplitter"; +import FilteredLayer from "../Models/FilteredLayer"; +import FeatureSource, {FeatureSourceForLayer} from "../Logic/FeatureSource/FeatureSource"; +import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; +import TiledFeatureSource from "../Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource"; + ScriptUtils.fixUtils() -function createOverpassObject(theme: LayoutConfig) { +function createOverpassObject(theme: LayoutConfig, relationTracker: RelationsTracker) { let filters: TagsFilter[] = []; let extraScripts: string[] = []; for (const layer of theme.layers) { @@ -54,7 +59,7 @@ function createOverpassObject(theme: LayoutConfig) { throw "Nothing to download! The theme doesn't declare anything to download" } return new Overpass(new Or(filters), extraScripts, new UIEventSource("https://overpass.kumi.systems/api/interpreter"), //https://overpass-api.de/api/interpreter"), - new UIEventSource(60)); + new UIEventSource(60), relationTracker); } function rawJsonName(targetDir: string, x: number, y: number, z: number): string { @@ -75,7 +80,7 @@ async function downloadRaw(targetdir: string, r: TileRange, overpass: Overpass)/ downloaded++; const filename = rawJsonName(targetdir, x, y, r.zoomlevel) if (existsSync(filename)) { - console.log("Already exists: ", filename) + console.log("Already exists (not downloading again): ", filename) skipped++ continue; } @@ -145,14 +150,16 @@ async function downloadExtraData(theme: LayoutConfig)/* : any[] */ { return allFeatures; } -function postProcess(targetdir: string, r: TileRange, theme: LayoutConfig, extraFeatures: any[]) { + +function loadAllTiles(targetdir: string, r: TileRange, theme: LayoutConfig, extraFeatures: any[]): FeatureSource { + + let allFeatures = [...extraFeatures] let processed = 0; - const layerIndex = theme.LayerIndex(); for (let x = r.xstart; x <= r.xend; x++) { for (let y = r.ystart; y <= r.yend; y++) { processed++; const filename = rawJsonName(targetdir, x, y, r.zoomlevel) - ScriptUtils.erasableLog(" Post processing", processed, "/", r.total, filename) + console.log(" Loading and processing", processed, "/", r.total, filename) if (!existsSync(filename)) { console.error("Not found - and not downloaded. Run this script again!: " + filename) continue; @@ -163,152 +170,97 @@ function postProcess(targetdir: string, r: TileRange, theme: LayoutConfig, extra // Create and save the geojson file - which is the main chunk of the data const geojson = OsmToGeoJson.default(rawOsm); - const osmTime = new Date(rawOsm.osm3s.timestamp_osm_base); - // And merge in the extra features - needed for the metatagging - geojson.features.push(...extraFeatures); - - for (const feature of geojson.features) { - - for (const layer of theme.layers) { - if (layer.source.osmTags.matchesProperties(feature.properties)) { - feature["_matching_layer_id"] = layer.id; - break; - } - } - } - const featuresFreshness = geojson.features.map(feature => { - return ({ - freshness: osmTime, - feature: feature - }); - }); - // Extract the relationship information - const relations = ExtractRelations.BuildMembershipTable(ExtractRelations.GetRelationElements(rawOsm)) - - MetaTagging.addMetatags(featuresFreshness, new UIEventSource<{ feature: any; freshness: Date }[]>(featuresFreshness), relations, theme.layers, false); - - - for (const feature of geojson.features) { - const layer = layerIndex.get(feature["_matching_layer_id"]) - if (layer === undefined) { - // Probably some extra, unneeded data, e.g. a point of a way - continue - } - - if (layer.wayHandling == LayerConfig.WAYHANDLING_CENTER_ONLY) { - - const centerpoint = GeoOperations.centerpointCoordinates(feature) - - feature.geometry.type = "Point" - feature.geometry["coordinates"] = centerpoint; - - } - } - for (const feature of geojson.features) { - // Some cleanup - delete feature["bbox"] - } - - const targetPath = geoJsonName(targetdir + ".unfiltered", x, y, r.zoomlevel) - // This is the geojson file containing all features - writeFileSync(targetPath, JSON.stringify(geojson, null, " ")) + allFeatures.push(...geojson.features) } } + return new StaticFeatureSource(allFeatures) } -function splitPerLayer(targetdir: string, r: TileRange, theme: LayoutConfig) { - const z = r.zoomlevel; - const generated = {} // layer --> x --> y[] - for (let x = r.xstart; x <= r.xend; x++) { - for (let y = r.ystart; y <= r.yend; y++) { - const file = readFileSync(geoJsonName(targetdir + ".unfiltered", x, y, z), "UTF8") +/** + * Load all the tiles into memory from disk + */ +function postProcess(allFeatures: FeatureSource, theme: LayoutConfig, relationsTracker: RelationsTracker, targetdir: string) { - for (const layer of theme.layers) { - if (!layer.source.isOsmCacheLayer) { - continue; - } - const geojson = JSON.parse(file) - const oldLength = geojson.features.length; - geojson.features = geojson.features - .filter(f => f._matching_layer_id === layer.id) - .filter(f => { - const isShown = layer.isShown.GetRenderValue(f.properties).txt - return isShown !== "no"; - }) - const new_path = geoJsonName(targetdir + "_" + layer.id, x, y, z); - ScriptUtils.erasableLog(new_path, " has ", geojson.features.length, " features after filtering (dropped ", oldLength - geojson.features.length, ")") - if (geojson.features.length == 0) { - continue; + function handleLayer(source: FeatureSourceForLayer) { + const layer = source.layer.layerDef; + const layerId = layer.id + if (layer.source.isOsmCacheLayer !== true) { + return; + } + console.log("Handling layer ", layerId, "which has", source.features.data.length, "features") + if (source.features.data.length === 0) { + return; + } + MetaTagging.addMetatags(source.features.data, + { + memberships: relationsTracker, + getFeaturesWithin: _ => { + return [allFeatures.features.data.map(f => f.feature)] } - writeFileSync(new_path, JSON.stringify(geojson, null, " ")) + }, + layer, + false); - if (generated[layer.id] === undefined) { - generated[layer.id] = {} + const createdTiles = [] + // At this point, we have all the features of the entire area. + // However, we want to export them per tile of a fixed size, so we use a dynamicTileSOurce to split it up + TiledFeatureSource.createHierarchy(source, { + minZoomLevel: 14, + maxZoomLevel: 14, + maxFeatureCount: undefined, + registerTile: tile => { + if (tile.z < 12) { + return; } - if (generated[layer.id][x] === undefined) { - generated[layer.id][x] = [] + if (tile.features.data.length === 0) { + return } - generated[layer.id][x].push(y) - + for (const feature of tile.features.data) { + // Some cleanup + delete feature.feature["bbox"] + } + // Lets save this tile! + const [z, x, y] = Utils.tile_from_index(tile.tileIndex) + console.log("Writing tile ", z, x, y, layerId) + const targetPath = geoJsonName(targetdir + "_" + layerId, x, y, z) + createdTiles.push(tile.tileIndex) + // This is the geojson file containing all features for this tile + writeFileSync(targetPath, JSON.stringify({ + type: "FeatureCollection", + features: tile.features.data.map(f => f.feature) + }, null, " ")) } - } - } - - for (const layer of theme.layers) { - const id = layer.id - const loaded = generated[id] - if (loaded === undefined) { - console.log("No features loaded for layer ", id) - continue; - } - writeFileSync(targetdir + "_" + id + "_overview.json", JSON.stringify(loaded)) + }) + + // All the tiles are written at this point + // Only thing left to do is to create the index + const path = targetdir + "_" + layerId + "_overview.json" + const perX = {} + createdTiles.map(i => Utils.tile_from_index(i)).forEach(([z, x, y]) => { + const key = "" + x + if (perX[key] === undefined) { + perX[key] = [] + } + perX[key].push(y) + }) + writeFileSync(path, JSON.stringify(perX)) + + } + new PerLayerFeatureSourceSplitter( + new UIEventSource(theme.layers.map(l => ({ + layerDef: l, + isDisplayed: new UIEventSource(true), + appliedFilters: new UIEventSource(undefined) + }))), + handleLayer, + allFeatures + ) } -async function createOverview(targetdir: string, r: TileRange, z: number, layername: string) { - const allFeatures = [] - for (let x = r.xstart; x <= r.xend; x++) { - for (let y = r.ystart; y <= r.yend; y++) { - const read_path = geoJsonName(targetdir + "_" + layername, x, y, z); - if (!fs.existsSync(read_path)) { - continue; - } - const features = JSON.parse(fs.readFileSync(read_path, "UTF-8")).features - const pointsOnly = features.map(f => { - - f.properties["_last_edit:timestamp"] = "1970-01-01" - - if (f.geometry.type === "Point") { - return f - } else { - return GeoOperations.centerpoint(f) - } - - }) - allFeatures.push(...pointsOnly) - } - } - - const featuresDedup = [] - const seen = new Set() - for (const feature of allFeatures) { - const id = feature.properties.id - if (seen.has(id)) { - continue - } - seen.add(id) - featuresDedup.push(feature) - } - - const geojson = { - "type": "FeatureCollection", - "features": featuresDedup - } - writeFileSync(targetdir + "_" + layername + "_points.geojson", JSON.stringify(geojson, null, " ")) -} async function main(args: string[]) { @@ -335,8 +287,8 @@ async function main(args: string[]) { console.error("The theme " + theme + " was not found; try one of ", keys); return } - - const overpass = createOverpassObject(theme) + const relationTracker = new RelationsTracker() + const overpass = createOverpassObject(theme, relationTracker) let failed = 0; do { @@ -348,21 +300,13 @@ async function main(args: string[]) { } while (failed > 0) const extraFeatures = await downloadExtraData(theme); - postProcess(targetdir, tileRange, theme, extraFeatures) - splitPerLayer(targetdir, tileRange, theme) + const allFeaturesSource = loadAllTiles(targetdir, tileRange, theme, extraFeatures) + postProcess(allFeaturesSource, theme, relationTracker, targetdir) - if (args[7] === "--generate-point-overview") { - const targetLayers = args[8].split(",") - for (const targetLayer of targetLayers) { - if (!theme.layers.some(l => l.id === targetLayer)) { - throw "Target layer " + targetLayer + " not found, did you mistype the name? Found layers are: " + theme.layers.map(l => l.id).join(",") - } - createOverview(targetdir, tileRange, zoomlevel, targetLayer) - } - } } let args = [...process.argv] args.splice(0, 2) -main(args); \ No newline at end of file +main(args); +console.log("All done!") \ No newline at end of file From 9e21ec1182841a9873906b87cc3a9fab41e345cb Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 21 Sep 2021 02:19:31 +0200 Subject: [PATCH 019/110] Fix download panel, add dog-access question --- UI/BigComponents/DownloadPanel.ts | 2 +- assets/layers/cafe_pub/cafe_pub.json | 3 ++- assets/layers/food/food.json | 1 + assets/tagRenderings/questions.json | 36 ++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/UI/BigComponents/DownloadPanel.ts b/UI/BigComponents/DownloadPanel.ts index 466b8d217..746715826 100644 --- a/UI/BigComponents/DownloadPanel.ts +++ b/UI/BigComponents/DownloadPanel.ts @@ -107,7 +107,7 @@ export class DownloadPanel extends Toggle { return { type:"FeatureCollection", - features: featureList + features: resultFeatures } } diff --git a/assets/layers/cafe_pub/cafe_pub.json b/assets/layers/cafe_pub/cafe_pub.json index be3f1d6e5..db93b4f8f 100644 --- a/assets/layers/cafe_pub/cafe_pub.json +++ b/assets/layers/cafe_pub/cafe_pub.json @@ -165,7 +165,8 @@ "email", "phone", "payment-options", - "wheelchair-access" + "wheelchair-access", + "dog-access" ], "filter": [ { diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 9dcb0f1dd..cb2aa258f 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -182,6 +182,7 @@ "phone", "payment-options", "wheelchair-access", + "dog-access", { "#": "Cuisine", "question": { diff --git a/assets/tagRenderings/questions.json b/assets/tagRenderings/questions.json index 4f2b63c2d..30aab5b27 100644 --- a/assets/tagRenderings/questions.json +++ b/assets/tagRenderings/questions.json @@ -137,6 +137,42 @@ } ] }, + "dog-access": { + "question": { + "en": "Are dogs allowed in this business?", + "nl": "Zijn honden toegelaten in deze zaak?" + }, + "mappings": [ + { + "if": "dog=yes", + "then": { + "en": "Dogs are allowed", + "nl": "honden zijn toegelaten" + } + }, + { + "if": "dog=no", + "then": { + "en": "Dogs are not allowed", + "nl": "honden zijn niet toegelaten" + } + }, + { + "if": "dog=leashed", + "then": { + "en": "Dogs are allowed, but they have to be leashed", + "nl": "honden zijn enkel aan de leiband welkom" + } + }, + { + "if": "dog=unleashed", + "then": { + "en": "Dogs are allowed and can run around freely", + "nl": "honden zijn welkom en mogen vrij rondlopen" + } + } + ] + }, "description": { "question": { "nl": "Zijn er nog andere relevante zaken die je niet in de bovenstaande vragen kwijt kon? Vul ze hier in.
Herhaal geen antwoorden die je reeds gaf", From e374bb355cf2632c008082ee6b7f206f3061c6b8 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 21 Sep 2021 03:10:15 +0200 Subject: [PATCH 020/110] Partial fix of opening the selected element --- InitUiElements.ts | 12 +- Logic/Actors/SelectedFeatureHandler.ts | 96 ++++--------- Logic/ContributorCount.ts | 4 +- Logic/FeatureSource/FeaturePipeline.ts | 1 - .../Sources/OsmApiFeatureSource.ts | 110 --------------- State.ts | 5 - UI/ShowDataLayer/ShowDataLayer.ts | 131 ++++++++++-------- 7 files changed, 100 insertions(+), 259 deletions(-) delete mode 100644 Logic/FeatureSource/Sources/OsmApiFeatureSource.ts diff --git a/InitUiElements.ts b/InitUiElements.ts index 353192dc3..3f591a3d1 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -35,6 +35,7 @@ import {LayoutConfigJson} from "./Models/ThemeConfig/Json/LayoutConfigJson"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; import LayerConfig from "./Models/ThemeConfig/LayerConfig"; import Minimap from "./UI/Base/Minimap"; +import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler"; export class InitUiElements { static InitAll( @@ -194,6 +195,8 @@ export class InitUiElements { State.state.locationControl.ping(); } + new SelectedFeatureHandler(Hash.hash, State.state) + // Reset the loading message once things are loaded new CenterMessageBox().AttachTo("centermessage"); document @@ -404,15 +407,6 @@ export class InitUiElements { }, state ); - /* const selectedFeatureHandler = new SelectedFeatureHandler( - Hash.hash, - State.state.selectedElement, - source, - State.state.osmApiFeatureSource - ); - selectedFeatureHandler.zoomToSelectedFeature( - State.state.locationControl - );*/ } private static setupAllLayerElements() { diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts index 07ff7f4a7..95a744df6 100644 --- a/Logic/Actors/SelectedFeatureHandler.ts +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -1,49 +1,45 @@ import {UIEventSource} from "../UIEventSource"; -import FeatureSource from "../FeatureSource/FeatureSource"; import {OsmObject} from "../Osm/OsmObject"; import Loc from "../../Models/Loc"; -import FeaturePipeline from "../FeatureSource/FeaturePipeline"; -import OsmApiFeatureSource from "../FeatureSource/Sources/OsmApiFeatureSource"; +import {ElementStorage} from "../ElementStorage"; /** * Makes sure the hash shows the selected element and vice-versa. */ export default class SelectedFeatureHandler { private static readonly _no_trigger_on = ["welcome", "copyright", "layers", "new"] - private readonly _featureSource: FeatureSource; - private readonly _hash: UIEventSource; - private readonly _selectedFeature: UIEventSource; - private readonly _osmApiSource: OsmApiFeatureSource; + hash: UIEventSource; + private readonly state: { + selectedElement: UIEventSource + } - constructor(hash: UIEventSource, - selectedFeature: UIEventSource, - featureSource: FeaturePipeline, - osmApiSource: OsmApiFeatureSource) { - this._hash = hash; - this._selectedFeature = selectedFeature; - this._featureSource = featureSource; - this._osmApiSource = osmApiSource; - const self = this; + constructor( + hash: UIEventSource, + state: { + selectedElement: UIEventSource, + allElements: ElementStorage; + } + ) { + this.hash = hash; + + this.state = state + + // Getting a blank hash clears the selected element hash.addCallback(h => { if (h === undefined || h === "") { - selectedFeature.setData(undefined); - } else { - self.selectFeature(); + state.selectedElement.setData(undefined); + }else{ + const feature = state.allElements.ContainingFeatures.get(h) + if(feature !== undefined){ + state.selectedElement.setData(feature) + } } }) - hash.addCallbackAndRunD(h => { - try { - self.downloadFeature(h) - } catch (e) { - console.error("Could not download feature, probably a weird hash") - } - }) - featureSource.features.addCallback(_ => self.selectFeature()); - - selectedFeature.addCallback(feature => { + state.selectedElement.addCallback(feature => { if (feature === undefined) { + console.trace("Resetting hash") if (SelectedFeatureHandler._no_trigger_on.indexOf(hash.data) < 0) { hash.setData("") } @@ -55,13 +51,11 @@ export default class SelectedFeatureHandler { } }) - this.selectFeature(); - } // If a feature is selected via the hash, zoom there public zoomToSelectedFeature(location: UIEventSource) { - const hash = this._hash.data; + const hash = this.hash.data; if (hash === undefined || SelectedFeatureHandler._no_trigger_on.indexOf(hash) >= 0) { return; // No valid feature selected } @@ -80,42 +74,4 @@ export default class SelectedFeatureHandler { } } - private downloadFeature(hash: string) { - if (hash === undefined || hash === "") { - return; - } - if (SelectedFeatureHandler._no_trigger_on.indexOf(hash) >= 0) { - return; - } - try { - - this._osmApiSource.load(hash) - } catch (e) { - console.log("Could not download feature, probably a weird hash:", hash) - } - } - - private selectFeature() { - const features = this._featureSource?.features?.data; - if (features === undefined) { - return; - } - if (this._selectedFeature.data?.properties?.id === this._hash.data) { - // Feature already selected - return; - } - - const hash = this._hash.data; - if (hash === undefined || hash === "" || hash === "#") { - return; - } - for (const feature of features) { - const id = feature.feature?.properties?.id; - if (id === hash) { - this._selectedFeature.setData(feature.feature); - break; - } - } - } - } \ No newline at end of file diff --git a/Logic/ContributorCount.ts b/Logic/ContributorCount.ts index 6a4c2da25..2dcd8450f 100644 --- a/Logic/ContributorCount.ts +++ b/Logic/ContributorCount.ts @@ -1,9 +1,7 @@ /// Given a feature source, calculates a list of OSM-contributors who mapped the latest versions -import FeatureSource from "./FeatureSource/FeatureSource"; import {UIEventSource} from "./UIEventSource"; import FeaturePipeline from "./FeatureSource/FeaturePipeline"; import Loc from "../Models/Loc"; -import State from "../State"; import {BBox} from "./GeoOperations"; export default class ContributorCount { @@ -33,7 +31,7 @@ export default class ContributorCount { if (this.lastUpdate !== undefined && ((now.getTime() - this.lastUpdate.getTime()) < 1000 * 60)) { return; } - console.log("Calculating contributors") + this.lastUpdate = now; const featuresList = this.state.featurePipeline.GetAllFeaturesWithin(bbox) const hist = new Map(); for (const list of featuresList) { diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index ec4a052be..80f1949f1 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -36,7 +36,6 @@ export default class FeaturePipeline implements FeatureSourceState { constructor( handleFeatureSource: (source: FeatureSourceForLayer) => void, state: { - osmApiFeatureSource: FeatureSource, filteredLayers: UIEventSource, locationControl: UIEventSource, selectedElement: UIEventSource, diff --git a/Logic/FeatureSource/Sources/OsmApiFeatureSource.ts b/Logic/FeatureSource/Sources/OsmApiFeatureSource.ts deleted file mode 100644 index c1c846602..000000000 --- a/Logic/FeatureSource/Sources/OsmApiFeatureSource.ts +++ /dev/null @@ -1,110 +0,0 @@ -import FeatureSource from "../FeatureSource"; -import {UIEventSource} from "../../UIEventSource"; -import Loc from "../../../Models/Loc"; -import FilteredLayer from "../../../Models/FilteredLayer"; -import {Utils} from "../../../Utils"; -import {OsmObject} from "../../Osm/OsmObject"; - - -export default class OsmApiFeatureSource implements FeatureSource { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); - public readonly name: string = "OsmApiFeatureSource"; - private readonly loadedTiles: Set = new Set(); - private readonly _state: { - leafletMap: UIEventSource; - locationControl: UIEventSource, filteredLayers: UIEventSource}; - - constructor(state: {locationControl: UIEventSource, filteredLayers: UIEventSource, leafletMap: UIEventSource, - overpassMaxZoom: UIEventSource}) { - this._state = state; - const self = this; - function update(){ - const minZoom = state.overpassMaxZoom.data; - const location = state.locationControl.data - if(minZoom === undefined || location === undefined){ - return; - } - if(minZoom < 14){ - throw "MinZoom should be at least 14 or higher, OSM-api won't work otherwise" - } - if(location.zoom > minZoom){ - return; - } - self.loadArea() - } - } - - - public load(id: string) { - if (id.indexOf("-") >= 0) { - // Newly added point - not yet in OSM - return; - } - console.debug("Downloading", id, "from the OSM-API") - OsmObject.DownloadObject(id).addCallbackAndRunD(element => { - try { - const geojson = element.asGeoJson(); - geojson.id = geojson.properties.id; - this.features.setData([{feature: geojson, freshness: element.timestamp}]) - } catch (e) { - console.error(e) - } - }) - } - - /** - * Loads the current inview-area - */ - public loadArea(z: number = 14): boolean { - const layers = this._state.filteredLayers.data; - - const disabledLayers = layers.filter(layer => layer.layerDef.source.overpassScript !== undefined || layer.layerDef.source.geojsonSource !== undefined) - if (disabledLayers.length > 0) { - return false; - } - if (this._state.leafletMap.data === undefined) { - return false; // Not yet inited - } - const bounds = this._state.leafletMap.data.getBounds() - const tileRange = Utils.TileRangeBetween(z, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) - const self = this; - Utils.MapRange(tileRange, (x, y) => { - const key = x + "/" + y; - if (self.loadedTiles.has(key)) { - return; - } - - self.loadedTiles.add(key); - - const bounds = Utils.tile_bounds(z, x, y); - console.log("Loading OSM data tile", z, x, y, " with bounds", bounds) - OsmObject.LoadArea(bounds, objects => { - const keptGeoJson: { feature: any, freshness: Date }[] = [] - // Which layer does the object match? - for (const object of objects) { - - for (const flayer of layers) { - const layer = flayer.layerDef; - const tags = object.tags - const doesMatch = layer.source.osmTags.matchesProperties(tags); - if (doesMatch) { - const geoJson = object.asGeoJson(); - geoJson._matching_layer_id = layer.id - keptGeoJson.push({feature: geoJson, freshness: object.timestamp}) - break; - } - - } - - } - - self.features.setData(keptGeoJson) - }); - - }); - - return true; - - } - -} \ No newline at end of file diff --git a/State.ts b/State.ts index 8c08873cb..54af37fd4 100644 --- a/State.ts +++ b/State.ts @@ -13,7 +13,6 @@ import Loc from "./Models/Loc"; import Constants from "./Models/Constants"; import TitleHandler from "./Logic/Actors/TitleHandler"; import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader"; -import OsmApiFeatureSource from "./Logic/FeatureSource/Sources/OsmApiFeatureSource"; import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; import FilteredLayer from "./Models/FilteredLayer"; import ChangeToElementsActor from "./Logic/Actors/ChangeToElementsActor"; @@ -55,8 +54,6 @@ export default class State { public favouriteLayers: UIEventSource; - public osmApiFeatureSource: OsmApiFeatureSource; - public filteredLayers: UIEventSource = new UIEventSource([], "filteredLayers"); /** @@ -395,8 +392,6 @@ export default class State { new ChangeToElementsActor(this.changes, this.allElements) - this.osmApiFeatureSource = new OsmApiFeatureSource(this) - new PendingChangesUploader(this.changes, this.selectedElement); this.mangroveIdentity = new MangroveIdentity( diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 927a1008d..73a68247c 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -16,11 +16,20 @@ export default class ShowDataLayer { // Used to generate a fresh ID when needed private _cleanCount = 0; - - constructor(options: ShowDataLayerOptions & { layerToShow: LayerConfig}) { + private geoLayer = undefined; + + /** + * If the selected element triggers, this is used to lookup the correct layer and to open the popup + * Used to avoid a lot of callbacks on the selected element + * @private + */ + private readonly leafletLayersPerId = new Map() + + + constructor(options: ShowDataLayerOptions & { layerToShow: LayerConfig }) { this._leafletMap = options.leafletMap; this._enablePopups = options.enablePopups ?? true; - if(options.features === undefined){ + if (options.features === undefined) { throw "Invalid ShowDataLayer invocation" } const features = options.features.features.map(featFreshes => featFreshes.map(ff => ff.feature)); @@ -28,58 +37,77 @@ export default class ShowDataLayer { this._layerToShow = options.layerToShow; const self = this; - let geoLayer = undefined; + features.addCallback(() => self.update(options)); + options.leafletMap.addCallback(() => self.update(options)); + this.update(options); - function update() { - if (features.data === undefined) { + + State.state.selectedElement.addCallbackAndRunD(selected => { + if (self._leafletMap.data === undefined) { return; } - const mp =options. leafletMap.data; - - if (mp === undefined) { + const v = self.leafletLayersPerId.get(selected.properties.id) + if(v === undefined){return;} + const leafletLayer = v.leafletlayer + const feature = v.feature + if (leafletLayer.getPopup().isOpen()) { return; } - - self._cleanCount++ - // clean all the old stuff away, if any - if (geoLayer !== undefined) { - mp.removeLayer(geoLayer); - } - - const allFeats = features.data; - geoLayer = self.CreateGeojsonLayer(); - for (const feat of allFeats) { - if (feat === undefined) { - continue + if (selected.properties.id === feature.properties.id) { + // A small sanity check to prevent infinite loops: + if (selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again + && feature.id === feature.properties.id // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too + ) { + leafletLayer.openPopup() } - // @ts-ignore - geoLayer.addData(feat); - } - - mp.addLayer(geoLayer) - - if (options.zoomToFeatures ?? false) { - try { - mp.fitBounds(geoLayer.getBounds(), {animate: false}) - } catch (e) { - console.error(e) + if (feature.id !== feature.properties.id) { + console.trace("Not opening the popup for", feature) } + } - if (self._enablePopups) { - State.state.selectedElement.ping() - } + }) + } + + private update(options) { + if (this._features.data === undefined) { + return; + } + const mp = options.leafletMap.data; + + if (mp === undefined) { + return; + } + this._cleanCount++ + // clean all the old stuff away, if any + if (this.geoLayer !== undefined) { + mp.removeLayer(this.geoLayer); } - features.addCallback(() => update()); - options.leafletMap.addCallback(() => update()); - update(); + const allFeats = this._features.data; + this.geoLayer = this.CreateGeojsonLayer(); + for (const feat of allFeats) { + if (feat === undefined) { + continue + } + this.geoLayer.addData(feat); + } + + mp.addLayer(this.geoLayer) + + if (options.zoomToFeatures ?? false) { + try { + mp.fitBounds(this.geoLayer.getBounds(), {animate: false}) + } catch (e) { + console.error(e) + } + } } private createStyleFor(feature) { const tagsSource = State.state.allElements.addOrGetElement(feature); // Every object is tied to exactly one layer - const layer = this._layerToShow + const layer = this._layerToShow return layer?.GenerateLeafletStyle(tagsSource, true); } @@ -88,7 +116,7 @@ export default class ShowDataLayer { // We have to convert them to the appropriate icon // Click handling is done in the next step - const layer: LayerConfig = this._layerToShow + const layer: LayerConfig = this._layerToShow if (layer === undefined) { return; } @@ -159,28 +187,9 @@ export default class ShowDataLayer { infobox.AttachTo(id) infobox.Activate(); }); - const self = this; - State.state.selectedElement.addCallbackAndRunD(selected => { - if (self._leafletMap.data === undefined) { - return; - } - if (leafletLayer.getPopup().isOpen()) { - return; - } - if (selected.properties.id === feature.properties.id) { - // A small sanity check to prevent infinite loops: - if (selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again - && feature.id === feature.properties.id // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too - ) { - leafletLayer.openPopup() - } - if (feature.id !== feature.properties.id) { - console.trace("Not opening the popup for", feature) - } - - } - }) + // Add the feature to the index to open the popup when needed + this.leafletLayersPerId.set(feature.properties.id, {feature: feature, leafletlayer: leafletLayer}) } private CreateGeojsonLayer(): L.Layer { From 1f939238208e7cbd9e63b7b8d46507d0d5ec160c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 22 Sep 2021 05:02:09 +0200 Subject: [PATCH 021/110] More work on splitting roads, WIP; refactoring tests --- Logic/Actors/SelectedFeatureHandler.ts | 39 ++- Logic/Actors/TitleHandler.ts | 73 ++---- Logic/ExtraFunction.ts | 8 +- ...ctor.ts => SaveTileToLocalStorageActor.ts} | 8 +- Logic/FeatureSource/ChangeApplicator.ts | 223 +++++------------- Logic/FeatureSource/FeaturePipeline.ts | 84 +++++-- .../PerLayerFeatureSourceSplitter.ts | 19 +- .../Sources/FeatureSourceMerger.ts | 10 +- .../NewGeometryFromChangesFeatureSource.ts | 90 +++++++ .../Sources/SimpleFeatureSource.ts | 11 +- .../TiledFeatureSource/TileHierarchyMerger.ts | 6 +- .../TiledFromLocalStorageSource.ts | 6 +- Logic/GeoOperations.ts | 35 ++- Logic/MetaTagging.ts | 23 +- Logic/Osm/Actions/ChangeDescription.ts | 28 ++- Logic/Osm/Actions/ChangeTagAction.ts | 2 +- Logic/Osm/Actions/CreateNewNodeAction.ts | 4 +- Logic/Osm/Actions/DeleteAction.ts | 7 +- Logic/Osm/Actions/OsmChangeAction.ts | 2 +- Logic/Osm/Actions/RelationSplitHandler.ts | 142 +++++++++++ Logic/Osm/Actions/RelationSplitlHandler.ts | 20 -- Logic/Osm/Actions/SplitAction.ts | 196 ++++++++------- Logic/Osm/Changes.ts | 16 +- Logic/Osm/OsmObject.ts | 142 +++++------ Logic/UIEventSource.ts | 15 +- Logic/Web/Hash.ts | 2 +- Logic/Web/QueryParameters.ts | 1 - Models/Constants.ts | 2 +- State.ts | 2 +- UI/Base/Minimap.ts | 7 +- UI/Base/MinimapImplementation.ts | 63 ++++- UI/BigComponents/FullWelcomePaneWithTabs.ts | 3 +- UI/BigComponents/SimpleAddUI.ts | 55 +++-- UI/Input/LocationInput.ts | 109 +++------ UI/Popup/FeatureInfoBox.ts | 2 +- UI/Popup/SplitRoadWizard.ts | 84 ++++--- UI/ShowDataLayer/ShowDataLayer.ts | 17 +- Utils.ts | 2 +- assets/layers/bench/bench.json | 2 +- assets/layers/crossings/crossings.json | 1 + .../layers/drinking_water/drinking_water.json | 2 +- assets/svg/license_info.json | 6 +- assets/svg/plus.svg | 73 ++++-- assets/svg/scissors.svg | 70 +----- assets/themes/cycle_infra/cycle_infra.json | 2 +- assets/themes/cyclestreets/cyclestreets.json | 10 +- css/mobile.css | 2 +- scripts/CycleHighwayFix.ts | 3 +- scripts/ScriptUtils.ts | 3 +- scripts/generateCache.ts | 5 +- test.ts | 62 +++-- test/ImageAttribution.spec.ts | 2 +- test/ImageSearcher.spec.ts | 2 +- test/OsmConnection.spec.ts | 2 +- test/OsmObject.spec.ts | 29 ++- test/RelationSplitHandler.spec.ts | 66 ++++++ test/Tag.spec.ts | 2 +- test/TestAll.ts | 54 +++-- test/TestHelper.ts | 24 +- test/Theme.spec.ts | 2 +- test/Units.spec.ts | 2 +- test/Utils.spec.ts | 2 +- 62 files changed, 1163 insertions(+), 823 deletions(-) rename Logic/FeatureSource/Actors/{LocalStorageSaverActor.ts => SaveTileToLocalStorageActor.ts} (71%) create mode 100644 Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts create mode 100644 Logic/Osm/Actions/RelationSplitHandler.ts delete mode 100644 Logic/Osm/Actions/RelationSplitlHandler.ts create mode 100644 test/RelationSplitHandler.spec.ts diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts index 95a744df6..c76febd41 100644 --- a/Logic/Actors/SelectedFeatureHandler.ts +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -2,12 +2,13 @@ import {UIEventSource} from "../UIEventSource"; import {OsmObject} from "../Osm/OsmObject"; import Loc from "../../Models/Loc"; import {ElementStorage} from "../ElementStorage"; +import FeaturePipeline from "../FeatureSource/FeaturePipeline"; /** * Makes sure the hash shows the selected element and vice-versa. */ export default class SelectedFeatureHandler { - private static readonly _no_trigger_on = ["welcome", "copyright", "layers", "new"] + private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "", undefined]) hash: UIEventSource; private readonly state: { selectedElement: UIEventSource @@ -17,30 +18,35 @@ export default class SelectedFeatureHandler { hash: UIEventSource, state: { selectedElement: UIEventSource, - allElements: ElementStorage; + allElements: ElementStorage, + featurePipeline: FeaturePipeline } ) { this.hash = hash; - this.state = state - - // Getting a blank hash clears the selected element - hash.addCallback(h => { + + + // If the hash changes, set the selected element correctly + function setSelectedElementFromHash(h){ if (h === undefined || h === "") { + // Hash has been cleared - we clear the selected element state.selectedElement.setData(undefined); }else{ + // we search the element to select const feature = state.allElements.ContainingFeatures.get(h) if(feature !== undefined){ state.selectedElement.setData(feature) } } - }) + } + + hash.addCallback(setSelectedElementFromHash) + // IF the selected element changes, set the hash correctly state.selectedElement.addCallback(feature => { if (feature === undefined) { - console.trace("Resetting hash") - if (SelectedFeatureHandler._no_trigger_on.indexOf(hash.data) < 0) { + if (SelectedFeatureHandler._no_trigger_on.has(hash.data)) { hash.setData("") } } @@ -50,13 +56,26 @@ export default class SelectedFeatureHandler { hash.setData(h) } }) + + state.featurePipeline.newDataLoadedSignal.addCallbackAndRunD(_ => { + // New data was loaded. In initial startup, the hash might be set (via the URL) but might not be selected yet + if(hash.data === undefined || SelectedFeatureHandler._no_trigger_on.has(hash.data)){ + // This is an invalid hash anyway + return; + } + if(state.selectedElement.data !== undefined){ + // We already have something selected + return; + } + setSelectedElementFromHash(hash.data) + }) } // If a feature is selected via the hash, zoom there public zoomToSelectedFeature(location: UIEventSource) { const hash = this.hash.data; - if (hash === undefined || SelectedFeatureHandler._no_trigger_on.indexOf(hash) >= 0) { + if (hash === undefined || SelectedFeatureHandler._no_trigger_on.has(hash)) { return; // No valid feature selected } // We should have a valid osm-ID and zoom to it... But we wrap it in try-catch to be sure diff --git a/Logic/Actors/TitleHandler.ts b/Logic/Actors/TitleHandler.ts index 520be0ed8..b5bc6f151 100644 --- a/Logic/Actors/TitleHandler.ts +++ b/Logic/Actors/TitleHandler.ts @@ -2,65 +2,38 @@ import {UIEventSource} from "../UIEventSource"; import Translations from "../../UI/i18n/Translations"; import Locale from "../../UI/i18n/Locale"; import TagRenderingAnswer from "../../UI/Popup/TagRenderingAnswer"; -import {ElementStorage} from "../ElementStorage"; import Combine from "../../UI/Base/Combine"; -import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; -class TitleElement extends UIEventSource { +export default class TitleHandler { + constructor(state) { + const currentTitle: UIEventSource = state.selectedElement.map( + selected => { + console.log("UPdating title") - private readonly _layoutToUse: UIEventSource; - private readonly _selectedFeature: UIEventSource; - private readonly _allElementsStorage: ElementStorage; - - constructor(layoutToUse: UIEventSource, - selectedFeature: UIEventSource, - allElementsStorage: ElementStorage) { - super("MapComplete"); - - this._layoutToUse = layoutToUse; - this._selectedFeature = selectedFeature; - this._allElementsStorage = allElementsStorage; - - this.syncWith( - this._selectedFeature.map( - selected => { - const defaultTitle = Translations.WT(this._layoutToUse.data?.title)?.txt ?? "MapComplete" - - if (selected === undefined) { - return defaultTitle - } - - const layout = layoutToUse.data; - const tags = selected.properties; - - - for (const layer of layout.layers) { - if (layer.title === undefined) { - continue; - } - if (layer.source.osmTags.matchesProperties(tags)) { - const tagsSource = allElementsStorage.getEventSourceById(tags.id) - const title = new TagRenderingAnswer(tagsSource, layer.title) - return new Combine([defaultTitle, " | ", title]).ConstructElement().innerText; - } - } + const layout = state.layoutToUse.data + const defaultTitle = Translations.WT(layout?.title)?.txt ?? "MapComplete" + if (selected === undefined) { return defaultTitle } - , [Locale.language, layoutToUse] - ) + + const tags = selected.properties; + for (const layer of layout.layers) { + if (layer.title === undefined) { + continue; + } + if (layer.source.osmTags.matchesProperties(tags)) { + const tagsSource = state.allElements.getEventSourceById(tags.id) + const title = new TagRenderingAnswer(tagsSource, layer.title) + return new Combine([defaultTitle, " | ", title]).ConstructElement().innerText; + } + } + return defaultTitle + }, [Locale.language, state.layoutToUse] ) - } - -} - -export default class TitleHandler { - constructor(layoutToUse: UIEventSource, - selectedFeature: UIEventSource, - allElementsStorage: ElementStorage) { - new TitleElement(layoutToUse, selectedFeature, allElementsStorage).addCallbackAndRunD(title => { + currentTitle.addCallbackAndRunD(title => { document.title = title }) } diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index dd75ac356..8b6834e09 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -88,7 +88,7 @@ export class ExtraFunction { { name: "distanceTo", doc: "Calculates the distance between the feature and a specified point in kilometer. The input should either be a pair of coordinates, a geojson feature or the ID of an object", - args: ["longitude", "latitude"] + args: ["feature OR featureID OR longitude", "undefined OR latitude"] }, (featuresPerLayer, feature) => { return (arg0, lat) => { @@ -128,10 +128,10 @@ export class ExtraFunction { private static readonly ClosestNObjectFunc = new ExtraFunction( { name: "closestn", - doc: "Given either a list of geojson features or a single layer name, gives the n closest objects which are nearest to the feature. 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 laoded)\n\n" + + 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", "amount of features", "unique tag key (optional)", "maxDistanceInMeters (optional)"] + args: ["list of features or layer name", "amount of features", "unique tag key (optional)", "maxDistanceInMeters (optional)"] }, (params, feature) => { return (features, amount, uniqueTag, maxDistanceInMeters) => ExtraFunction.GetClosestNFeatures(params, feature, features, { diff --git a/Logic/FeatureSource/Actors/LocalStorageSaverActor.ts b/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts similarity index 71% rename from Logic/FeatureSource/Actors/LocalStorageSaverActor.ts rename to Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts index 9cb9ce7f2..331168bb8 100644 --- a/Logic/FeatureSource/Actors/LocalStorageSaverActor.ts +++ b/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts @@ -4,15 +4,13 @@ * Technically, more an Actor then a featuresource, but it fits more neatly this ay */ import {FeatureSourceForLayer} from "../FeatureSource"; -import {Utils} from "../../../Utils"; -export default class LocalStorageSaverActor { +export default class SaveTileToLocalStorageActor { public static readonly storageKey: string = "cached-features"; - constructor(source: FeatureSourceForLayer, x: number, y: number, z: number) { + constructor(source: FeatureSourceForLayer, tileIndex: number) { source.features.addCallbackAndRunD(features => { - const index = Utils.tile_index(z, x, y) - const key = `${LocalStorageSaverActor.storageKey}-${source.layer.layerDef.id}-${index}` + const key = `${SaveTileToLocalStorageActor.storageKey}-${source.layer.layerDef.id}-${tileIndex}` const now = new Date().getTime() if (features.length == 0) { diff --git a/Logic/FeatureSource/ChangeApplicator.ts b/Logic/FeatureSource/ChangeApplicator.ts index 0507c733d..bab2225bf 100644 --- a/Logic/FeatureSource/ChangeApplicator.ts +++ b/Logic/FeatureSource/ChangeApplicator.ts @@ -1,202 +1,85 @@ -import FeatureSource, {IndexedFeatureSource} from "./FeatureSource"; +import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource} from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; import {Changes} from "../Osm/Changes"; -import {ChangeDescription} from "../Osm/Actions/ChangeDescription"; +import {ChangeDescription, ChangeDescriptionTools} from "../Osm/Actions/ChangeDescription"; import {Utils} from "../../Utils"; +import FilteredLayer from "../../Models/FilteredLayer"; import {OsmNode, OsmRelation, OsmWay} from "../Osm/OsmObject"; -/** - * A feature source containing exclusively new elements - */ -export class NewGeometryChangeApplicatorFeatureSource implements FeatureSource{ - - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - public readonly name: string = "newFeatures"; - constructor(changes: Changes) { - const seenChanges = new Set(); - changes.pendingChanges.addCallbackAndRunD(changes => { - for (const change of changes) { - if(seenChanges.has(change)){ - continue - } - seenChanges.add(change) - - if(change.id < 0){ - // This is a new object! - } - - } - }) - } - -} /** - * Applies changes from 'Changes' onto a featureSource + * Applies geometry changes from 'Changes' onto every feature of a featureSource */ -export default class ChangeApplicator implements FeatureSource { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; +export default class ChangeGeometryApplicator implements FeatureSourceForLayer { + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name: string; private readonly source: IndexedFeatureSource; private readonly changes: Changes; - private readonly mode?: { - generateNewGeometries: boolean - }; + public readonly layer: FilteredLayer - constructor(source: IndexedFeatureSource, changes: Changes, mode?: { - generateNewGeometries: boolean - }) { + constructor(source: (IndexedFeatureSource & FeatureSourceForLayer), changes: Changes) { this.source = source; this.changes = changes; - this.mode = mode; + this.layer = source.layer this.name = "ChangesApplied(" + source.name + ")" - this.features = source.features - const seenChanges = new Set(); + this.features = new UIEventSource<{ feature: any; freshness: Date }[]>(undefined) + const self = this; - let runningUpdate = false; - source.features.addCallbackAndRunD(features => { - if (runningUpdate) { - return; // No need to ping again - } - self.ApplyChanges() - seenChanges.clear() - }) + source.features.addCallbackAndRunD(_ => self.update()) + + changes.allChanges.addCallbackAndRunD(_ => self.update()) - changes.pendingChanges.addCallbackAndRunD(changes => { - runningUpdate = true; - changes = changes.filter(ch => !seenChanges.has(ch)) - changes.forEach(c => seenChanges.add(c)) - self.ApplyChanges() - source.features.ping() - runningUpdate = false; - }) } + private update() { + const upstreamFeatures = this.source.features.data + const upstreamIds = this.source.containedIds.data + const changesToApply = this.changes.allChanges.data + ?.filter(ch => + // Does upsteram have this element? If not, we skip + upstreamIds.has(ch.type + "/" + ch.id) && + // Are any (geometry) changes defined? + ch.changes !== undefined && + // Ignore new elements, they are handled by the NewGeometryFromChangesFeatureSource + ch.id > 0) - /** - * Returns true if the geometry is changed and the source should be pinged - */ - private ApplyChanges(): boolean { - const cs = this.changes.pendingChanges.data - const features = this.source.features.data - const loadedIds = this.source.containedIds - if (cs.length === 0 || features === undefined) { + if (changesToApply === undefined || changesToApply.length === 0) { + // No changes to apply! + // Pass the original feature and lets continue our day + this.features.setData(upstreamFeatures); return; } - console.log("Applying changes ", this.name, cs) - let geometryChanged = false; - const changesPerId: Map = new Map() - for (const c of cs) { - const id = c.type + "/" + c.id - if (!loadedIds.has(id)) { - continue + const changesPerId = new Map() + for (const ch of changesToApply) { + const key = ch.type + "/" + ch.id + if(changesPerId.has(key)){ + changesPerId.get(key).push(ch) + }else{ + changesPerId.set(key, [ch]) } - if (!changesPerId.has(id)) { - changesPerId.set(id, []) - } - changesPerId.get(id).push(c) } - if (changesPerId.size === 0) { - // The current feature source set doesn't contain any changed feature, so we can safely skip - return; - } - - const now = new Date() - - function add(feature) { - feature.id = feature.properties.id - features.push({ - feature: feature, - freshness: now - }) - console.log("Added a new feature: ", feature) - geometryChanged = true; - } - - // First, create the new features - they have a negative ID - // We don't set the properties yet though - if (this.mode?.generateNewGeometries) { - changesPerId.forEach(cs => { - cs - .forEach(change => { - if (change.id >= 0) { - return; // Nothing to do here, already created - } - - if (change.changes === undefined) { - // An update to the object - not the actual created - return; - } - - try { - - switch (change.type) { - case "node": - const n = new OsmNode(change.id) - n.lat = change.changes["lat"] - n.lon = change.changes["lon"] - const geojson = n.asGeoJson() - add(geojson) - break; - case "way": - const w = new OsmWay(change.id) - w.nodes = change.changes["nodes"] - add(w.asGeoJson()) - break; - case "relation": - const r = new OsmRelation(change.id) - r.members = change.changes["members"] - add(r.asGeoJson()) - break; - } - - } catch (e) { - console.error(e) - } - }) - }) - } - - for (const feature of features) { - const f = feature.feature; - const id = f.properties.id; - if (!changesPerId.has(id)) { + const newFeatures: { feature: any, freshness: Date }[] = [] + for (const feature of upstreamFeatures) { + const changesForFeature = changesPerId.get(feature.feature.properties.id) + if (changesForFeature === undefined) { + // No changes for this element + newFeatures.push(feature) continue; } - - - const changed = {} - // Copy all the properties - Utils.Merge(f, changed) - // play the changes onto the copied object - - for (const change of changesPerId.get(id)) { - for (const kv of change.tags ?? []) { - // Apply tag changes and ping the consumers - f.properties[kv.k] = kv.v; - } - - // Apply other changes to the object - if (change.changes !== undefined) { - geometryChanged = true; - switch (change.type) { - case "node": - // @ts-ignore - const coor: { lat, lon } = change.changes; - f.geometry.coordinates = [coor.lon, coor.lat] - break; - case "way": - f.geometry.coordinates = change.changes["locations"] - break; - case "relation": - console.error("Changes to relations are not yet supported") - break; - } - } + + // Allright! We have a feature to rewrite! + const copy = { + ...feature } + // We only apply the last change as that one'll have the latest geometry + const change = changesForFeature[changesForFeature.length - 1] + copy.feature.geometry = ChangeDescriptionTools.getGeojsonGeometry(change) + newFeatures.push(copy) } - return geometryChanged + this.features.setData(newFeatures) + } + } \ No newline at end of file diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 80f1949f1..2fe254eb1 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -1,7 +1,7 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import FilteringFeatureSource from "./Sources/FilteringFeatureSource"; import PerLayerFeatureSourceSplitter from "./PerLayerFeatureSourceSplitter"; -import FeatureSource, {FeatureSourceForLayer, FeatureSourceState, Tiled} from "./FeatureSource"; +import FeatureSource, {FeatureSourceForLayer, FeatureSourceState, IndexedFeatureSource, Tiled} from "./FeatureSource"; import TiledFeatureSource from "./TiledFeatureSource/TiledFeatureSource"; import {UIEventSource} from "../UIEventSource"; import {TileHierarchyTools} from "./TiledFeatureSource/TileHierarchy"; @@ -14,13 +14,14 @@ import GeoJsonSource from "./Sources/GeoJsonSource"; import Loc from "../../Models/Loc"; import WayHandlingApplyingFeatureSource from "./Sources/WayHandlingApplyingFeatureSource"; import RegisteringAllFromFeatureSourceActor from "./Actors/RegisteringAllFromFeatureSourceActor"; -import {Utils} from "../../Utils"; import TiledFromLocalStorageSource from "./TiledFeatureSource/TiledFromLocalStorageSource"; -import LocalStorageSaverActor from "./Actors/LocalStorageSaverActor"; +import SaveTileToLocalStorageActor from "./Actors/SaveTileToLocalStorageActor"; import DynamicGeoJsonTileSource from "./TiledFeatureSource/DynamicGeoJsonTileSource"; import {BBox} from "../GeoOperations"; import {TileHierarchyMerger} from "./TiledFeatureSource/TileHierarchyMerger"; import RelationsTracker from "../Osm/RelationsTracker"; +import {NewGeometryFromChangesFeatureSource} from "./Sources/NewGeometryFromChangesFeatureSource"; +import ChangeGeometryApplicator from "./ChangeApplicator"; export default class FeaturePipeline implements FeatureSourceState { @@ -29,10 +30,12 @@ export default class FeaturePipeline implements FeatureSourceState { public readonly runningQuery: UIEventSource; public readonly timeout: UIEventSource; public readonly somethingLoaded: UIEventSource = new UIEventSource(false) + public readonly newDataLoadedSignal: UIEventSource = new UIEventSource(undefined) private readonly overpassUpdater: OverpassFeatureSource private readonly relationTracker: RelationsTracker private readonly perLayerHierarchy: Map; + constructor( handleFeatureSource: (source: FeatureSourceForLayer) => void, state: { @@ -49,6 +52,7 @@ export default class FeaturePipeline implements FeatureSourceState { const self = this const updater = new OverpassFeatureSource(state); + updater.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(updater)) this.overpassUpdater = updater; this.sufficientlyZoomed = updater.sufficientlyZoomed this.runningQuery = updater.runningQuery @@ -56,16 +60,17 @@ export default class FeaturePipeline implements FeatureSourceState { this.relationTracker = updater.relationsTracker // Register everything in the state' 'AllElements' new RegisteringAllFromFeatureSourceActor(updater) + const perLayerHierarchy = new Map() this.perLayerHierarchy = perLayerHierarchy - const patchedHandleFeatureSource = function (src: FeatureSourceForLayer) { + const patchedHandleFeatureSource = function (src: FeatureSourceForLayer & IndexedFeatureSource) { // This will already contain the merged features for this tile. In other words, this will only be triggered once for every tile const srcFiltered = new FilteringFeatureSource(state, new WayHandlingApplyingFeatureSource( - src, + new ChangeGeometryApplicator(src, state.changes) ) ) handleFeatureSource(srcFiltered) @@ -90,6 +95,7 @@ export default class FeaturePipeline implements FeatureSourceState { (src) => { new RegisteringAllFromFeatureSourceActor(src) hierarchy.registerTile(src); + src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src)) }, state) continue } @@ -103,6 +109,7 @@ export default class FeaturePipeline implements FeatureSourceState { registerTile: (tile) => { new RegisteringAllFromFeatureSourceActor(tile) addToHierarchy(tile, id) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) } }) } else { @@ -113,6 +120,7 @@ export default class FeaturePipeline implements FeatureSourceState { registerTile: (tile) => { new RegisteringAllFromFeatureSourceActor(tile) addToHierarchy(tile, id) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) } }), state @@ -122,64 +130,90 @@ export default class FeaturePipeline implements FeatureSourceState { } // Actually load data from the overpass source - new PerLayerFeatureSourceSplitter(state.filteredLayers, (source) => TiledFeatureSource.createHierarchy(source, { layer: source.layer, registerTile: (tile) => { // We save the tile data for the given layer to local storage - const [z, x, y] = Utils.tile_from_index(tile.tileIndex) - new LocalStorageSaverActor(tile, x, y, z) + new SaveTileToLocalStorageActor(tile, tile.tileIndex) addToHierarchy(tile, source.layer.layerDef.id); } - }), new RememberingSource(updater)) + }), + new RememberingSource(updater)) + + + // Also load points/lines that are newly added. + const newGeometry = new NewGeometryFromChangesFeatureSource(state.changes) + new RegisteringAllFromFeatureSourceActor(newGeometry) + // A NewGeometryFromChangesFeatureSource does not split per layer, so we do this next + new PerLayerFeatureSourceSplitter(state.filteredLayers, + (perLayer) => { + // We don't bother to split them over tiles as it'll contain little features by default, so we simply add them like this + addToHierarchy(perLayer, perLayer.layer.layerDef.id) + // AT last, we always apply the metatags whenever possible + perLayer.features.addCallbackAndRunD(_ => self.applyMetaTags(perLayer)) + }, + newGeometry + ) // Whenever fresh data comes in, we need to update the metatagging - updater.features.addCallback(_ => { + self.newDataLoadedSignal.stabilized(1000).addCallback(src => { + console.log("Got an update from ", src.name) self.updateAllMetaTagging() }) } + + private applyMetaTags(src: FeatureSourceForLayer){ + const self = this + MetaTagging.addMetatags( + src.features.data, + { + memberships: this.relationTracker, + getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox) + }, + src.layer.layerDef, + { + includeDates: true, + // We assume that the non-dated metatags are already set by the cache generator + includeNonDates: !src.layer.layerDef.source.isOsmCacheLayer + } + ) + } private updateAllMetaTagging() { console.log("Updating the meta tagging") const self = this; this.perLayerHierarchy.forEach(hierarchy => { hierarchy.loadedTiles.forEach(src => { - MetaTagging.addMetatags( - src.features.data, - { - memberships: this.relationTracker, - getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox) - }, - src.layer.layerDef - ) + self.applyMetaTags(src) }) }) } - - public GetAllFeaturesWithin(bbox: BBox): any[][]{ + + public GetAllFeaturesWithin(bbox: BBox): any[][] { const self = this const tiles = [] Array.from(this.perLayerHierarchy.keys()) .forEach(key => tiles.push(...self.GetFeaturesWithin(key, bbox))) return tiles; } - - public GetFeaturesWithin(layerId: string, bbox: BBox): any[][]{ + + public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] { const requestedHierarchy = this.perLayerHierarchy.get(layerId) if (requestedHierarchy === undefined) { + console.warn("Layer ", layerId, "is not defined. Try one of ", Array.from(this.perLayerHierarchy.keys())) return undefined; } return TileHierarchyTools.getTiles(requestedHierarchy, bbox) .filter(featureSource => featureSource.features?.data !== undefined) .map(featureSource => featureSource.features.data.map(fs => fs.feature)) } - - public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void){ - Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => { + + public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void) { + Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => { TileHierarchyTools.getTiles(hierarchy, bbox).forEach(handleTile) }) } diff --git a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts index bb39660fa..d16e2c558 100644 --- a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts +++ b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts @@ -1,4 +1,4 @@ -import FeatureSource, {FeatureSourceForLayer} from "./FeatureSource"; +import FeatureSource, {FeatureSourceForLayer, Tiled} from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; import FilteredLayer from "../../Models/FilteredLayer"; import SimpleFeatureSource from "./Sources/SimpleFeatureSource"; @@ -12,10 +12,13 @@ import SimpleFeatureSource from "./Sources/SimpleFeatureSource"; export default class PerLayerFeatureSourceSplitter { constructor(layers: UIEventSource, - handleLayerData: (source: FeatureSourceForLayer) => void, - upstream: FeatureSource) { + handleLayerData: (source: FeatureSourceForLayer & Tiled) => void, + upstream: FeatureSource, + options?:{ + handleLeftovers?: (featuresWithoutLayer: any[]) => void + }) { - const knownLayers = new Map() + const knownLayers = new Map() function update() { const features = upstream.features.data; @@ -30,7 +33,7 @@ export default class PerLayerFeatureSourceSplitter { // Note that this splitter is only run when it is invoked by the overpass feature source, so we can't be sure in which layer it should go const featuresPerLayer = new Map(); - + const noLayerFound = [] function addTo(layer: FilteredLayer, feature: { feature, freshness }) { const id = layer.layerDef.id const list = featuresPerLayer.get(id) @@ -51,6 +54,7 @@ export default class PerLayerFeatureSourceSplitter { break; } } + noLayerFound.push(f) } } @@ -75,6 +79,11 @@ export default class PerLayerFeatureSourceSplitter { featureSource.features.setData(features) } } + + // AT last, the leftovers are handled + if(options?.handleLeftovers !== undefined && noLayerFound.length > 0){ + options.handleLeftovers(noLayerFound) + } } layers.addCallback(_ => update()) diff --git a/Logic/FeatureSource/Sources/FeatureSourceMerger.ts b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts index 574258455..fb349ae5d 100644 --- a/Logic/FeatureSource/Sources/FeatureSourceMerger.ts +++ b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts @@ -3,12 +3,12 @@ * Uses the freshest feature available in the case multiple sources offer data with the same identifier */ import {UIEventSource} from "../../UIEventSource"; -import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; import FilteredLayer from "../../../Models/FilteredLayer"; import {BBox} from "../../GeoOperations"; import {Utils} from "../../../Utils"; -export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled { +export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled, IndexedFeatureSource { public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name; @@ -16,6 +16,7 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled private readonly _sources: UIEventSource; public readonly tileIndex: number; public readonly bbox: BBox; + public readonly containedIds: UIEventSource> = new UIEventSource>(new Set()) constructor(layer: FilteredLayer, tileIndex: number, bbox: BBox, sources: UIEventSource) { this.tileIndex = tileIndex; @@ -54,7 +55,7 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled // We seed the dictionary with the previously loaded features const oldValues = this.features.data ?? []; for (const oldValue of oldValues) { - all.set(oldValue.feature.id + oldValue.feature._matching_layer_id, oldValue) + all.set(oldValue.feature.id, oldValue) } for (const source of this._sources.data) { @@ -62,7 +63,7 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled continue; } for (const f of source.features.data) { - const id = f.feature.properties.id + f.feature._matching_layer_id; + const id = f.feature.properties.id; if (!all.has(id)) { // This is a new feature somethingChanged = true; @@ -90,6 +91,7 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled all.forEach((value, _) => { newList.push(value) }) + this.containedIds.setData(new Set(all.keys())) this.features.setData(newList); } diff --git a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts new file mode 100644 index 000000000..f4619b2aa --- /dev/null +++ b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts @@ -0,0 +1,90 @@ +import {Changes} from "../../Osm/Changes"; +import {OsmNode, OsmRelation, OsmWay} from "../../Osm/OsmObject"; +import FeatureSource from "../FeatureSource"; +import {UIEventSource} from "../../UIEventSource"; +import {ChangeDescription} from "../../Osm/Actions/ChangeDescription"; + +export class NewGeometryFromChangesFeatureSource implements FeatureSource { + // This class name truly puts the 'Java' into 'Javascript' + + /** + * A feature source containing exclusively new elements + */ + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name: string = "newFeatures"; + + constructor(changes: Changes) { + + const seenChanges = new Set(); + const features = this.features.data; + const self = this; + + changes.pendingChanges + .map(changes => changes.filter(ch => + // only new objects allowed + ch.id < 0 && + // The change is an update to the object (e.g. tags or geometry) - not the actual create + ch.changes !== undefined && + // If tags is undefined, this is probably a new point that is part of a split road + ch.tags !== undefined && + // Already handled + !seenChanges.has(ch))) + .addCallbackAndRunD(changes => { + + if (changes.length === 0) { + return; + } + + const now = new Date(); + + function add(feature) { + feature.id = feature.properties.id + features.push({ + feature: feature, + freshness: now + }) + console.warn("Added a new feature: ", JSON.stringify(feature)) + } + + for (const change of changes) { + seenChanges.add(change) + try { + const tags = {} + for (const kv of change.tags) { + tags[kv.k] = kv.v + } + tags["id"] = change.type+"/"+change.id + switch (change.type) { + case "node": + const n = new OsmNode(change.id) + n.tags = tags + n.lat = change.changes["lat"] + n.lon = change.changes["lon"] + const geojson = n.asGeoJson() + add(geojson) + break; + case "way": + const w = new OsmWay(change.id) + w.tags = tags + w.nodes = change.changes["nodes"] + w.coordinates = change.changes["coordinates"].map(coor => coor.reverse()) + add(w.asGeoJson()) + break; + case "relation": + const r = new OsmRelation(change.id) + r.tags = tags + r.members = change.changes["members"] + add(r.asGeoJson()) + break; + } + } catch (e) { + console.error("Could not generate a new geometry to render on screen for:", e) + } + + } + + self.features.ping() + }) + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts index d4c316ec4..ad8d7be5d 100644 --- a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts +++ b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts @@ -1,16 +1,19 @@ import {UIEventSource} from "../../UIEventSource"; import FilteredLayer from "../../../Models/FilteredLayer"; -import {FeatureSourceForLayer} from "../FeatureSource"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {BBox} from "../../GeoOperations"; +import {Utils} from "../../../Utils"; -export default class SimpleFeatureSource implements FeatureSourceForLayer { +export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name: string = "SimpleFeatureSource"; public readonly layer: FilteredLayer; + public readonly bbox: BBox = BBox.global; + public readonly tileIndex: number = Utils.tile_index(0, 0, 0); constructor(layer: FilteredLayer) { - this.name = "SimpleFeatureSource("+layer.layerDef.id+")" + this.name = "SimpleFeatureSource(" + layer.layerDef.id + ")" this.layer = layer } - } \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts index 10d0c1742..7b9f44b9c 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts @@ -1,6 +1,6 @@ import TileHierarchy from "./TileHierarchy"; import {UIEventSource} from "../../UIEventSource"; -import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; import FilteredLayer from "../../../Models/FilteredLayer"; import {Utils} from "../../../Utils"; import {BBox} from "../../GeoOperations"; @@ -11,9 +11,9 @@ export class TileHierarchyMerger implements TileHierarchy> = new Map>(); public readonly layer: FilteredLayer; - private _handleTile: (src: FeatureSourceForLayer, index: number) => void; + private _handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource, index: number) => void; - constructor(layer: FilteredLayer, handleTile: (src: FeatureSourceForLayer, index: number) => void) { + constructor(layer: FilteredLayer, handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource, index: number) => void) { this.layer = layer; this._handleTile = handleTile; } diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts index 7f3e5776e..0e4e091a3 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -4,7 +4,7 @@ import {UIEventSource} from "../../UIEventSource"; import Loc from "../../../Models/Loc"; import TileHierarchy from "./TileHierarchy"; import {Utils} from "../../../Utils"; -import LocalStorageSaverActor from "../Actors/LocalStorageSaverActor"; +import SaveTileToLocalStorageActor from "../Actors/SaveTileToLocalStorageActor"; import {BBox} from "../../GeoOperations"; export default class TiledFromLocalStorageSource implements TileHierarchy { @@ -17,7 +17,7 @@ export default class TiledFromLocalStorageSource implements TileHierarchy { @@ -76,7 +76,7 @@ export default class TiledFromLocalStorageSource implements TileHierarchy coor.reverse()) + return w.asGeoJson().geometry + case "relation": + const r = new OsmRelation(change.id) + r.members = change.changes["members"] + return r.asGeoJson().geometry + } + } } \ No newline at end of file diff --git a/Logic/Osm/Actions/ChangeTagAction.ts b/Logic/Osm/Actions/ChangeTagAction.ts index 36bafcbee..784f4d4dc 100644 --- a/Logic/Osm/Actions/ChangeTagAction.ts +++ b/Logic/Osm/Actions/ChangeTagAction.ts @@ -37,7 +37,7 @@ export default class ChangeTagAction extends OsmChangeAction { return {k: key.trim(), v: value.trim()}; } - CreateChangeDescriptions(changes: Changes): ChangeDescription [] { + async CreateChangeDescriptions(changes: Changes): Promise { const changedTags: { k: string, v: string }[] = this._tagsFilter.asChange(this._currentTags).map(ChangeTagAction.checkChange) const typeId = this._elementId.split("/") const type = typeId[0] diff --git a/Logic/Osm/Actions/CreateNewNodeAction.ts b/Logic/Osm/Actions/CreateNewNodeAction.ts index 86cacab20..4841180bd 100644 --- a/Logic/Osm/Actions/CreateNewNodeAction.ts +++ b/Logic/Osm/Actions/CreateNewNodeAction.ts @@ -27,7 +27,7 @@ export default class CreateNewNodeAction extends OsmChangeAction { this._reusePointDistance = options?.reusePointWithinMeters ?? 1 } - CreateChangeDescriptions(changes: Changes): ChangeDescription[] { + async CreateChangeDescriptions(changes: Changes): Promise { const id = changes.getNewID() const properties = { id: "node/" + id @@ -97,7 +97,7 @@ export default class CreateNewNodeAction extends OsmChangeAction { type: "way", id: this._snapOnto.id, changes: { - locations: locations, + coordinates: locations, nodes: ids } } diff --git a/Logic/Osm/Actions/DeleteAction.ts b/Logic/Osm/Actions/DeleteAction.ts index 178a26927..25542bf18 100644 --- a/Logic/Osm/Actions/DeleteAction.ts +++ b/Logic/Osm/Actions/DeleteAction.ts @@ -159,7 +159,7 @@ export default class DeleteAction { canBeDeleted: false, reason: t.notEnoughExperience }) - return; + return true; // unregister this caller! } if (!useTheInternet) { @@ -167,13 +167,14 @@ export default class DeleteAction { } // All right! We have arrived at a point that we should query OSM again to check that the point isn't a part of ways or relations - OsmObject.DownloadReferencingRelations(id).addCallbackAndRunD(rels => { + OsmObject.DownloadReferencingRelations(id).then(rels => { hasRelations.setData(rels.length > 0) }) - OsmObject.DownloadReferencingWays(id).addCallbackAndRunD(ways => { + OsmObject.DownloadReferencingWays(id).then(ways => { hasWays.setData(ways.length > 0) }) + return true; // unregister to only run once }) diff --git a/Logic/Osm/Actions/OsmChangeAction.ts b/Logic/Osm/Actions/OsmChangeAction.ts index 0308ca8f6..784b192ba 100644 --- a/Logic/Osm/Actions/OsmChangeAction.ts +++ b/Logic/Osm/Actions/OsmChangeAction.ts @@ -17,7 +17,7 @@ export default abstract class OsmChangeAction { return this.CreateChangeDescriptions(changes) } - protected abstract CreateChangeDescriptions(changes: Changes): ChangeDescription[] + protected abstract CreateChangeDescriptions(changes: Changes): Promise } \ No newline at end of file diff --git a/Logic/Osm/Actions/RelationSplitHandler.ts b/Logic/Osm/Actions/RelationSplitHandler.ts new file mode 100644 index 000000000..dfca2d0fa --- /dev/null +++ b/Logic/Osm/Actions/RelationSplitHandler.ts @@ -0,0 +1,142 @@ +import OsmChangeAction from "./OsmChangeAction"; +import {Changes} from "../Changes"; +import {ChangeDescription} from "./ChangeDescription"; +import {OsmObject, OsmRelation, OsmWay} from "../OsmObject"; + +export interface RelationSplitInput { + relation: OsmRelation, + originalWayId: number, + allWayIdsInOrder: number[], + originalNodes: number[], + allWaysNodesInOrder: number[][] +} + +/** + * When a way is split and this way is part of a relation, the relation should be updated too to have the new segment if relevant. + */ +export default class RelationSplitHandler extends OsmChangeAction { + + constructor(input: RelationSplitInput) { + super() + } + + async CreateChangeDescriptions(changes: Changes): Promise { + return []; + } + + +} + + +/** + * A simple strategy to split relations: + * -> Download the way members just before and just after the original way + * -> Make sure they are still aligned + * + * Note that the feature might appear multiple times. + */ +export class InPlaceReplacedmentRTSH extends OsmChangeAction { + private readonly _input: RelationSplitInput; + + constructor(input: RelationSplitInput) { + super(); + this._input = input; + } + + /** + * Returns which node should border the member at the given index + */ + private async targetNodeAt(i: number, first: boolean) { + const member = this._input.relation.members[i] + if (member === undefined) { + return undefined + } + if (member.type === "node") { + return member.ref + } + if (member.type === "way") { + const osmWay = await OsmObject.DownloadObjectAsync("way/" + member.ref) + const nodes = osmWay.nodes + if (first) { + return nodes[0] + } else { + return nodes[nodes.length - 1] + } + } + if (member.type === "relation") { + return undefined + } + return undefined; + } + + async CreateChangeDescriptions(changes: Changes): Promise { + + const wayId = this._input.originalWayId + const relation = this._input.relation + const members = relation.members + const originalNodes = this._input.originalNodes; + const firstNode = originalNodes[0] + const lastNode = originalNodes[originalNodes.length - 1] + const newMembers: { type: "node" | "way" | "relation", ref: number, role: string }[] = [] + + for (let i = 0; i < members.length; i++) { + const member = members[i]; + if (member.type !== "way" || member.ref !== wayId) { + newMembers.push(member) + continue; + } + + const nodeIdBefore = await this.targetNodeAt(i - 1, false) + const nodeIdAfter = await this.targetNodeAt(i + 1, true) + + const firstNodeMatches = nodeIdBefore === undefined || nodeIdBefore === firstNode + const lastNodeMatches =nodeIdAfter === undefined || nodeIdAfter === lastNode + + if (firstNodeMatches && lastNodeMatches) { + // We have a classic situation, forward situation + for (const wId of this._input.allWayIdsInOrder) { + newMembers.push({ + ref: wId, + type: "way", + role: member.role + }) + } + continue; + } + + const firstNodeMatchesRev = nodeIdBefore === undefined || nodeIdBefore === lastNode + const lastNodeMatchesRev =nodeIdAfter === undefined || nodeIdAfter === firstNode + if (firstNodeMatchesRev || lastNodeMatchesRev) { + // We (probably) have a reversed situation, backward situation + for (let i1 = this._input.allWayIdsInOrder.length - 1; i1 >= 0; i1--){ + // Iterate BACKWARDS + const wId = this._input.allWayIdsInOrder[i1]; + newMembers.push({ + ref: wId, + type: "way", + role: member.role + }) + } + continue; + } + + // Euhm, allright... Something weird is going on, but let's not care too much + // Lets pretend this is forward going + for (const wId of this._input.allWayIdsInOrder) { + newMembers.push({ + ref: wId, + type: "way", + role: member.role + }) + } + + } + + return [{ + id: relation.id, + type: "relation", + changes: {members: newMembers} + }]; + } + +} \ No newline at end of file diff --git a/Logic/Osm/Actions/RelationSplitlHandler.ts b/Logic/Osm/Actions/RelationSplitlHandler.ts deleted file mode 100644 index 591051fca..000000000 --- a/Logic/Osm/Actions/RelationSplitlHandler.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * The logic to handle relations after a way within - */ -import OsmChangeAction from "./OsmChangeAction"; -import {Changes} from "../Changes"; -import {ChangeDescription} from "./ChangeDescription"; -import {OsmRelation} from "../OsmObject"; - -export default class RelationSplitlHandler extends OsmChangeAction { - - constructor(partOf: OsmRelation[], newWayIds: number[], originalNodes: number[]) { - super() - } - - CreateChangeDescriptions(changes: Changes): ChangeDescription[] { - return []; - } - - -} \ No newline at end of file diff --git a/Logic/Osm/Actions/SplitAction.ts b/Logic/Osm/Actions/SplitAction.ts index 8ee1731e4..cee912dca 100644 --- a/Logic/Osm/Actions/SplitAction.ts +++ b/Logic/Osm/Actions/SplitAction.ts @@ -1,9 +1,9 @@ -import {OsmRelation, OsmWay} from "../OsmObject"; +import {OsmObject, OsmWay} from "../OsmObject"; import {Changes} from "../Changes"; import {GeoOperations} from "../../GeoOperations"; import OsmChangeAction from "./OsmChangeAction"; import {ChangeDescription} from "./ChangeDescription"; -import RelationSplitlHandler from "./RelationSplitlHandler"; +import RelationSplitHandler from "./RelationSplitHandler"; interface SplitInfo { originalIndex?: number, // or negative for new elements @@ -12,17 +12,13 @@ interface SplitInfo { } export default class SplitAction extends OsmChangeAction { - private readonly roadObject: any; - private readonly osmWay: OsmWay; - private _partOf: OsmRelation[]; - private readonly _splitPoints: any[]; + private readonly wayId: string; + private readonly _splitPointsCoordinates: [number, number] []// lon, lat - constructor(osmWay: OsmWay, wayGeoJson: any, partOf: OsmRelation[], splitPoints: any[]) { + constructor(wayId: string, splitPointCoordinates: [number, number][]) { super() - this.osmWay = osmWay; - this.roadObject = wayGeoJson; - this._partOf = partOf; - this._splitPoints = splitPoints; + this.wayId = wayId; + this._splitPointsCoordinates = splitPointCoordinates } private static SegmentSplitInfo(splitInfo: SplitInfo[]): SplitInfo[][] { @@ -42,26 +38,17 @@ export default class SplitAction extends OsmChangeAction { return wayParts.filter(wp => wp.length > 0) } - CreateChangeDescriptions(changes: Changes): ChangeDescription[] { - const splitPoints = this._splitPoints - // We mark the new split points with a new id - console.log(splitPoints) - for (const splitPoint of splitPoints) { - splitPoint.properties["_is_split_point"] = true - } - - + async CreateChangeDescriptions(changes: Changes): Promise { const self = this; - const partOf = this._partOf - const originalElement = this.osmWay - const originalNodes = this.osmWay.nodes; + const originalElement = await OsmObject.DownloadObjectAsync(this.wayId) + const originalNodes = originalElement.nodes; // First, calculate splitpoints and remove points close to one another - const splitInfo = self.CalculateSplitCoordinates(splitPoints) + const splitInfo = self.CalculateSplitCoordinates(originalElement) // Now we have a list with e.g. // [ { originalIndex: 0}, {originalIndex: 1, doSplit: true}, {originalIndex: 2}, {originalIndex: undefined, doSplit: true}, {originalIndex: 3}] - // Lets change 'originalIndex' to the actual node id first: + // Lets change 'originalIndex' to the actual node id first (or assign a new id if needed): for (const element of splitInfo) { if (element.originalIndex >= 0) { element.originalIndex = originalElement.nodes[element.originalIndex] @@ -102,25 +89,30 @@ export default class SplitAction extends OsmChangeAction { }) } - const newWayIds: number[] = [] + // The ids of all the ways (including the original) + const allWayIdsInOrder: number[] = [] + + const allWaysNodesInOrder: number[][] = [] // Lets create OsmWays based on them for (const wayPart of wayParts) { let isOriginal = wayPart === longest if (isOriginal) { // We change the actual element! + const nodeIds = wayPart.map(p => p.originalIndex) changeDescription.push({ type: "way", id: originalElement.id, changes: { - locations: wayPart.map(p => p.lngLat), - nodes: wayPart.map(p => p.originalIndex) + coordinates: wayPart.map(p => p.lngLat), + nodes: nodeIds } }) + allWayIdsInOrder.push(originalElement.id) + allWaysNodesInOrder.push(nodeIds) } else { let id = changes.getNewID(); - newWayIds.push(id) - + // Copy the tags from the original object onto the new const kv = [] for (const k in originalElement.tags) { if (!originalElement.tags.hasOwnProperty(k)) { @@ -131,22 +123,35 @@ export default class SplitAction extends OsmChangeAction { } kv.push({k: k, v: originalElement.tags[k]}) } + const nodeIds = wayPart.map(p => p.originalIndex) changeDescription.push({ type: "way", id: id, tags: kv, changes: { - locations: wayPart.map(p => p.lngLat), - nodes: wayPart.map(p => p.originalIndex) + coordinates: wayPart.map(p => p.lngLat), + nodes: nodeIds } }) - } + allWayIdsInOrder.push(id) + allWaysNodesInOrder.push(nodeIds) + } } // At last, we still have to check that we aren't part of a relation... // At least, the order of the ways is identical, so we can keep the same roles - changeDescription.push(...new RelationSplitlHandler(partOf, newWayIds, originalNodes).CreateChangeDescriptions(changes)) + const relations = await OsmObject.DownloadReferencingRelations(this.wayId) + for (const relation of relations) { + const changDescrs = await new RelationSplitHandler({ + relation: relation, + allWayIdsInOrder: allWayIdsInOrder, + originalNodes: originalNodes, + allWaysNodesInOrder: allWaysNodesInOrder, + originalWayId: originalElement.id + }).CreateChangeDescriptions(changes) + changeDescription.push(...changDescrs) + } // And we have our objects! // Time to upload @@ -158,75 +163,96 @@ export default class SplitAction extends OsmChangeAction { * Calculates the actual points to split * If another point is closer then ~5m, we reuse that point */ - private CalculateSplitCoordinates( - splitPoints: any[], - toleranceInM = 5): SplitInfo[] { + private CalculateSplitCoordinates(osmWay: OsmWay, toleranceInM = 5): SplitInfo[] { + const wayGeoJson = osmWay.asGeoJson() + // Should be [lon, lat][] + const originalPoints = osmWay.coordinates.map(c => <[number, number]>c.reverse()) + const allPoints: { + coordinates: [number, number], + isSplitPoint: boolean, + originalIndex?: number, // Original index + dist: number, // Distance from the nearest point on the original line + location: number // Distance from the start of the way + }[] = this._splitPointsCoordinates.map(c => { + // From the turf.js docs: + // The properties object will contain three values: + // - `index`: closest point was found on nth line part, + // - `dist`: distance between pt and the closest point, + // `location`: distance along the line between start and the closest point. + let projected = GeoOperations.nearestPoint(wayGeoJson, c) + return ({ + coordinates: c, + isSplitPoint: true, + dist: projected.properties.dist, + location: projected.properties.location + }); + }) - const allPoints = [...splitPoints]; - // We have a bunch of coordinates here: [ [lat, lon], [lat, lon], ...] ... - const originalPoints: [number, number][] = this.roadObject.geometry.coordinates - // We project them onto the line (which should yield pretty much the same point + // We have a bunch of coordinates here: [ [lon, lon], [lat, lon], ...] ... + // We project them onto the line (which should yield pretty much the same point and add them to allPoints for (let i = 0; i < originalPoints.length; i++) { let originalPoint = originalPoints[i]; - let projected = GeoOperations.nearestPoint(this.roadObject, originalPoint) - projected.properties["_is_split_point"] = false - projected.properties["_original_index"] = i - allPoints.push(projected) + let projected = GeoOperations.nearestPoint(wayGeoJson, originalPoint) + allPoints.push({ + coordinates: originalPoint, + isSplitPoint: false, + location: projected.properties.location, + originalIndex: i, + dist: projected.properties.dist + }) } // At this point, we have a list of both the split point and the old points, with some properties to discriminate between them // We sort this list so that the new points are at the same location - allPoints.sort((a, b) => a.properties.location - b.properties.location) + allPoints.sort((a, b) => a.location - b.location) - // When this is done, we check that no now point is too close to an already existing point and no very small segments get created - /* for (let i = allPoints.length - 1; i > 0; i--) { - - const point = allPoints[i]; - if (point.properties._original_index !== undefined) { - // This point is already in OSM - we have to keep it! - continue; - } - - if (i != allPoints.length - 1) { - const prevPoint = allPoints[i + 1] - const diff = Math.abs(point.properties.location - prevPoint.properties.location) * 1000 - if (diff <= toleranceInM) { - // To close to the previous point! We delete this point... - allPoints.splice(i, 1) - // ... and mark the previous point as a split point - prevPoint.properties._is_split_point = true - continue; - } - } - - if (i > 0) { - const nextPoint = allPoints[i - 1] - const diff = Math.abs(point.properties.location - nextPoint.properties.location) * 1000 - if (diff <= toleranceInM) { - // To close to the next point! We delete this point... - allPoints.splice(i, 1) - // ... and mark the next point as a split point - nextPoint.properties._is_split_point = true - // noinspection UnnecessaryContinueJS - continue; - } - } - // We don't have to remove this point... - }*/ + for (let i = allPoints.length - 2; i >= 1; i--) { + // We 'merge' points with already existing nodes if they are close enough to avoid closeby elements + + // Note the loop bounds: we skip the first two and last two elements: + // The first and last element are always part of the original way and should be kept + // Furthermore, we run in reverse order as we'll delete elements on the go + + const point = allPoints[i] + if (point.originalIndex !== undefined) { + // We keep the original points + continue + } + if (point.dist * 1000 >= toleranceInM) { + // No need to remove this one + continue + } + + // At this point, 'dist' told us the point is pretty close to an already existing point. + // Lets see which (already existing) point is closer and mark it as splitpoint + const nextPoint = allPoints[i + 1] + const prevPoint = allPoints[i - 1] + const distToNext = nextPoint.location - point.location + const distToPrev = prevPoint.location - point.location + let closest = nextPoint + if (distToNext > distToPrev) { + closest = prevPoint + } + + // Ok, we have a closest point! + closest.isSplitPoint = true; + allPoints.splice(i, 1) + + } const splitInfo: SplitInfo[] = [] - let nextId = -1 + let nextId = -1 // Note: these IDs are overwritten later on, no need to use a global counter here for (const p of allPoints) { - let index = p.properties._original_index + let index = p.originalIndex if (index === undefined) { index = nextId; nextId--; } const splitInfoElement = { originalIndex: index, - lngLat: p.geometry.coordinates, - doSplit: p.properties._is_split_point + lngLat: p.coordinates, + doSplit: p.isSplitPoint } splitInfo.push(splitInfoElement) } diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index f36ffa4e0..aac1f08e9 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -21,12 +21,15 @@ export class Changes { */ public features = new UIEventSource<{ feature: any, freshness: Date }[]>([]); - public readonly pendingChanges = LocalStorageSource.GetParsed("pending-changes", []) + public readonly pendingChanges: UIEventSource = LocalStorageSource.GetParsed("pending-changes", []) + public readonly allChanges = new UIEventSource(undefined) private readonly isUploading = new UIEventSource(false); private readonly previouslyCreated: OsmObject[] = [] constructor() { + // We keep track of all changes just as well + this.allChanges.setData([...this.pendingChanges.data]) } private static createChangesetFor(csId: string, @@ -146,10 +149,13 @@ export class Changes { } public applyAction(action: OsmChangeAction) { - const changes = action.Perform(this) - console.log("Received changes:", changes) - this.pendingChanges.data.push(...changes); - this.pendingChanges.ping(); + action.Perform(this).then(changes => { + console.log("Received changes:", changes) + this.pendingChanges.data.push(...changes); + this.pendingChanges.ping(); + this.allChanges.data.push(...changes) + this.allChanges.ping() + }) } private CreateChangesetObjects(changes: ChangeDescription[], downloadedOsmObjects: OsmObject[]): { diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index a09c1b25c..a10ec8d0c 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -1,6 +1,7 @@ import {Utils} from "../../Utils"; import * as polygon_features from "../../assets/polygon-features.json"; import {UIEventSource} from "../UIEventSource"; +import {BBox} from "../GeoOperations"; export abstract class OsmObject { @@ -9,11 +10,12 @@ export abstract class OsmObject { protected static backendURL = OsmObject.defaultBackend; private static polygonFeatures = OsmObject.constructPolygonFeatures() private static objectCache = new Map>(); - private static referencingWaysCache = new Map>(); - private static referencingRelationsCache = new Map>(); private static historyCache = new Map>(); type: string; id: number; + /** + * The OSM tags as simple object + */ tags: {} = {}; version: number; public changed: boolean = false; @@ -37,7 +39,7 @@ export abstract class OsmObject { this.backendURL = url; } - static DownloadObject(id: string, forceRefresh: boolean = false): UIEventSource { + public static DownloadObject(id: string, forceRefresh: boolean = false): UIEventSource { let src: UIEventSource; if (OsmObject.objectCache.has(id)) { src = OsmObject.objectCache.get(id) @@ -47,80 +49,62 @@ export abstract class OsmObject { return src; } } else { - src = new UIEventSource(undefined) + src = UIEventSource.FromPromise(OsmObject.DownloadObjectAsync(id)) } + + OsmObject.objectCache.set(id, src); + return src; + } + + static async DownloadObjectAsync(id: string): Promise { const splitted = id.split("/"); const type = splitted[0]; const idN = Number(splitted[1]); if (idN < 0) { - return; + return undefined; } - - OsmObject.objectCache.set(id, src); - const newContinuation = (element: OsmObject) => { - src.setData(element) - } - switch (type) { case("node"): - new OsmNode(idN).Download(newContinuation); - break; + return await new OsmNode(idN).Download(); case("way"): - new OsmWay(idN).Download(newContinuation); - break; + return await new OsmWay(idN).Download(); case("relation"): - new OsmRelation(idN).Download(newContinuation); - break; + return await new OsmRelation(idN).Download(); default: - throw "Invalid object type:" + type + id; + throw ("Invalid object type:" + type + id); } - return src; } + /** * Downloads the ways that are using this node. * Beware: their geometry will be incomplete! */ - public static DownloadReferencingWays(id: string): UIEventSource { - if (OsmObject.referencingWaysCache.has(id)) { - return OsmObject.referencingWaysCache.get(id); - } - const waysSrc = new UIEventSource([]) - OsmObject.referencingWaysCache.set(id, waysSrc); - Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/ways`) - .then(data => { - const ways = data.elements.map(wayInfo => { + public static DownloadReferencingWays(id: string): Promise { + return Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/ways`).then( + data => { + return data.elements.map(wayInfo => { const way = new OsmWay(wayInfo.id) way.LoadData(wayInfo) return way }) - waysSrc.setData(ways) - }) - return waysSrc; + } + ) } /** * Downloads the relations that are using this feature. * Beware: their geometry will be incomplete! */ - public static DownloadReferencingRelations(id: string): UIEventSource { - if (OsmObject.referencingRelationsCache.has(id)) { - return OsmObject.referencingRelationsCache.get(id); - } - const relsSrc = new UIEventSource(undefined) - OsmObject.referencingRelationsCache.set(id, relsSrc); - Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/relations`) - .then(data => { - const rels = data.elements.map(wayInfo => { - const rel = new OsmRelation(wayInfo.id) - rel.LoadData(wayInfo) - rel.SaveExtraData(wayInfo) - return rel - }) - relsSrc.setData(rels) - }) - return relsSrc; + public static async DownloadReferencingRelations(id: string): Promise { + const data = await Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/relations`) + return data.elements.map(wayInfo => { + const rel = new OsmRelation(wayInfo.id) + rel.LoadData(wayInfo) + rel.SaveExtraData(wayInfo) + return rel + }) } public static DownloadHistory(id: string): UIEventSource { @@ -158,18 +142,11 @@ export abstract class OsmObject { } // bounds should be: [[maxlat, minlon], [minlat, maxlon]] (same as Utils.tile_bounds) - public static LoadArea(bounds: [[number, number], [number, number]], callback: (objects: OsmObject[]) => void) { - const minlon = bounds[0][1] - const maxlon = bounds[1][1] - const minlat = bounds[1][0] - const maxlat = bounds[0][0]; - const url = `${OsmObject.backendURL}api/0.6/map.json?bbox=${minlon},${minlat},${maxlon},${maxlat}` - Utils.downloadJson(url).then(data => { - const elements: any[] = data.elements; - const objects = OsmObject.ParseObjects(elements) - callback(objects); - - }) + public static async LoadArea(bbox: BBox): Promise { + const url = `${OsmObject.backendURL}api/0.6/map.json?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}` + const data = await Utils.downloadJson(url) + const elements: any[] = data.elements; + return OsmObject.ParseObjects(elements); } public static DownloadAll(neededIds, forceRefresh = true): UIEventSource { @@ -283,39 +260,34 @@ export abstract class OsmObject { return tags; } - Download(continuation: ((element: OsmObject, meta: OsmObjectMeta) => void)) { + /** + * Downloads the object, a full download for ways and relations + * @constructor + */ + async Download(): Promise { const self = this; const full = this.type !== "way" ? "" : "/full"; const url = `${OsmObject.backendURL}api/0.6/${this.type}/${this.id}${full}`; - Utils.downloadJson(url).then(data => { - + return await Utils.downloadJson(url).then(data => { const element = data.elements.pop(); - let nodes = [] if (self.type === "way" && data.elements.length >= 0) { nodes = OsmObject.ParseObjects(data.elements) } + if (self.type === "rellation") { + throw "We should save some extra data" + } + self.LoadData(element) self.SaveExtraData(element, nodes); - const meta = { - "_last_edit:contributor": element.user, - "_last_edit:contributor:uid": element.uid, - "_last_edit:changeset": element.changeset, - "_last_edit:timestamp": new Date(element.timestamp), - "_version_number": element.version - } - if (OsmObject.backendURL !== OsmObject.defaultBackend) { self.tags["_backend"] = OsmObject.backendURL - meta["_backend"] = OsmObject.backendURL; } - - continuation(self, meta); + return this; } ); - return this; } @@ -389,18 +361,10 @@ export class OsmNode extends OsmObject { } } -export interface OsmObjectMeta { - "_last_edit:contributor": string, - "_last_edit:contributor:uid": number, - "_last_edit:changeset": number, - "_last_edit:timestamp": Date, - "_version_number": number - -} - export class OsmWay extends OsmObject { nodes: number[]; + // The coordinates of the way, [lat, lon][] coordinates: [number, number][] = [] lat: number; lon: number; @@ -455,12 +419,16 @@ export class OsmWay extends OsmObject { } public asGeoJson() { + let coordinates : ([number, number][] | [number, number][][]) = this.coordinates.map(c => <[number, number]>c.reverse()); + if(this.isPolygon()){ + coordinates = [coordinates] + } return { "type": "Feature", "properties": this.tags, "geometry": { "type": this.isPolygon() ? "Polygon" : "LineString", - "coordinates": this.coordinates.map(c => [c[1], c[0]]) + "coordinates": coordinates } } } @@ -511,7 +479,7 @@ ${members}${tags} this.members = element.members; } - asGeoJson() { + asGeoJson(): any { throw "Not Implemented" } } \ No newline at end of file diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index 705c6174a..42282ea2a 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -60,7 +60,12 @@ export class UIEventSource { run(); return source; - + } + + public static FromPromise(promise : Promise): UIEventSource{ + const src = new UIEventSource(undefined) + promise.then(d => src.setData(d)) + return src } /** @@ -191,6 +196,14 @@ export class UIEventSource { } }) } + + addCallbackD(callback: (data: T) => void) { + this.addCallback(data => { + if (data !== undefined && data !== null) { + return callback(data) + } + }) + } } export class UIEventSourceTools { diff --git a/Logic/Web/Hash.ts b/Logic/Web/Hash.ts index ff1df3616..0c463c689 100644 --- a/Logic/Web/Hash.ts +++ b/Logic/Web/Hash.ts @@ -9,7 +9,7 @@ export default class Hash { public static hash: UIEventSource = Hash.Get(); /** - * Gets the current string, including the pound sign + * Gets the current string, including the pound sign if there is any * @constructor */ public static Current(): string { diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts index 065766e47..36ecc7680 100644 --- a/Logic/Web/QueryParameters.ts +++ b/Logic/Web/QueryParameters.ts @@ -127,7 +127,6 @@ export class QueryParameters { parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(QueryParameters.knownSources[key].data)) } // Don't pollute the history every time a parameter changes - history.replaceState(null, "", "?" + parts.join("&") + Hash.Current()); } diff --git a/Models/Constants.ts b/Models/Constants.ts index 0b62cc0cc..b3f31280e 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.0"; + public static vNumber = "0.10.0-alpha-0"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/State.ts b/State.ts index 54af37fd4..105e7ef05 100644 --- a/State.ts +++ b/State.ts @@ -433,7 +433,7 @@ export default class State { }) .ping(); - new TitleHandler(this.layoutToUse, this.selectedElement, this.allElements); + new TitleHandler(this); } private static asFloat(source: UIEventSource): UIEventSource { diff --git a/UI/Base/Minimap.ts b/UI/Base/Minimap.ts index 26909a00e..963d39db1 100644 --- a/UI/Base/Minimap.ts +++ b/UI/Base/Minimap.ts @@ -16,6 +16,11 @@ export interface MinimapOptions { lastClickLocation?: UIEventSource<{ lat: number, lon: number }> } +export interface MinimapObj { + readonly leafletMap: UIEventSource, + installBounds(factor: number | BBox, showRange?: boolean) : void +} + export default class Minimap { /** * A stub implementation. The actual implementation is injected later on, but only in the browser. @@ -25,6 +30,6 @@ export default class Minimap { /** * Construct a minimap */ - public static createMiniMap: (options: MinimapOptions) => BaseUIElement & { readonly leafletMap: UIEventSource } + public static createMiniMap: (options: MinimapOptions) => (BaseUIElement & MinimapObj) } \ No newline at end of file diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts index a55bc7e38..5ebf74fc7 100644 --- a/UI/Base/MinimapImplementation.ts +++ b/UI/Base/MinimapImplementation.ts @@ -7,9 +7,9 @@ import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; import {BBox} from "../../Logic/GeoOperations"; import * as L from "leaflet"; import {Map} from "leaflet"; -import Minimap, {MinimapOptions} from "./Minimap"; +import Minimap, {MinimapObj, MinimapOptions} from "./Minimap"; -export default class MinimapImplementation extends BaseUIElement { +export default class MinimapImplementation extends BaseUIElement implements MinimapObj { private static _nextId = 0; public readonly leafletMap: UIEventSource private readonly _id: string; @@ -44,6 +44,65 @@ export default class MinimapImplementation extends BaseUIElement { Minimap.createMiniMap = options => new MinimapImplementation(options) } + public installBounds(factor: number | BBox, showRange?: boolean) { + this.leafletMap.addCallbackD(leaflet => { + console.log("Installing max bounds") + + let bounds; + if (typeof factor === "number") { + bounds = leaflet.getBounds() + leaflet.setMaxBounds(bounds.pad(factor)) + }else{ + // @ts-ignore + leaflet.setMaxBounds(factor.toLeaflet()) + bounds = leaflet.getBounds() + } + + if (showRange) { + const data = { + type: "FeatureCollection", + features: [{ + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + bounds.getEast(), + bounds.getNorth() + ], + [ + bounds.getWest(), + bounds.getNorth() + ], + [ + bounds.getWest(), + bounds.getSouth() + ], + + [ + bounds.getEast(), + bounds.getSouth() + ], + [ + bounds.getEast(), + bounds.getNorth() + ] + ] + } + }] + } + // @ts-ignore + L.geoJSON(data, { + style: { + color: "#f00", + weight: 2, + opacity: 0.4 + } + }).addTo(leaflet) + } + }) + } + protected InnerConstructElement(): HTMLElement { const div = document.createElement("div") div.id = this._id; diff --git a/UI/BigComponents/FullWelcomePaneWithTabs.ts b/UI/BigComponents/FullWelcomePaneWithTabs.ts index eba8b60b3..a4a3e826a 100644 --- a/UI/BigComponents/FullWelcomePaneWithTabs.ts +++ b/UI/BigComponents/FullWelcomePaneWithTabs.ts @@ -65,7 +65,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { const tabsWithAboutMc = [...FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse, isShown)] const now = new Date() - const date = now.getFullYear()+"-"+Utils.TwoDigits(now.getMonth()+1)+"-"+Utils.TwoDigits(now.getDate()) + const lastWeek = new Date(now.getDate() - 7 * 24 * 60 * 60 * 1000) + const date = lastWeek.getFullYear()+"-"+Utils.TwoDigits(lastWeek.getMonth()+1)+"-"+Utils.TwoDigits(lastWeek.getDate()) const osmcha_link = `https://osmcha.org/?filters=%7B%22date__gte%22%3A%5B%7B%22label%22%3A%22${date}%22%2C%22value%22%3A%222021-01-01%22%7D%5D%2C%22editor%22%3A%5B%7B%22label%22%3A%22mapcomplete%22%2C%22value%22%3A%22mapcomplete%22%7D%5D%7D` tabsWithAboutMc.push({ diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 673f1d325..57f651e9a 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -20,6 +20,7 @@ import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; import PresetConfig from "../../Models/ThemeConfig/PresetConfig"; import FilteredLayer from "../../Models/FilteredLayer"; import {And} from "../../Logic/Tags/And"; +import {BBox} from "../../Logic/GeoOperations"; /* * The SimpleAddUI is a single panel, which can have multiple states: @@ -39,8 +40,6 @@ interface PresetInfo extends PresetConfig { export default class SimpleAddUI extends Toggle { constructor(isShown: UIEventSource) { - - const loginButton = new SubtleButton(Svg.osm_logo_ui(), Translations.t.general.add.pleaseLogin.Clone()) .onClick(() => State.state.osmConnection.AttemptLogin()); const readYourMessages = new Combine([ @@ -52,7 +51,8 @@ export default class SimpleAddUI extends Toggle { const selectedPreset = new UIEventSource(undefined); isShown.addCallback(_ => selectedPreset.setData(undefined)) // Clear preset selection when the UI is closed/opened - + State.state.LastClickLocation.addCallback( _ => selectedPreset.setData(undefined)) + const presetsOverview = SimpleAddUI.CreateAllPresetsPanel(selectedPreset) @@ -82,11 +82,7 @@ export default class SimpleAddUI extends Toggle { return true; }) } - - }, - - () => { selectedPreset.setData(undefined) }) @@ -98,9 +94,9 @@ export default class SimpleAddUI extends Toggle { new Toggle( new Toggle( new Toggle( - Translations.t.general.add.stillLoading.Clone().SetClass("alert"), addUi, - State.state.featurePipeline.runningQuery + Translations.t.general.add.stillLoading.Clone().SetClass("alert"), + State.state.featurePipeline.somethingLoaded ), Translations.t.general.add.zoomInFurther.Clone().SetClass("alert"), State.state.locationControl.map(loc => loc.zoom >= Constants.userJourney.minZoomLevelToAddNewPoints) @@ -126,6 +122,7 @@ export default class SimpleAddUI extends Toggle { let location = State.state.LastClickLocation; let preciseInput: LocationInput = undefined if (preset.preciseInput !== undefined) { + // We uncouple the event source const locationSrc = new UIEventSource({ lat: location.data.lat, lon: location.data.lon, @@ -137,24 +134,48 @@ export default class SimpleAddUI extends Toggle { backgroundLayer = AvailableBaseLayers.SelectBestLayerAccordingTo(locationSrc, new UIEventSource(preset.preciseInput.preferredBackground)) } - let features: UIEventSource<{ feature: any }[]> = undefined + let snapToFeatures: UIEventSource<{ feature: any }[]> = undefined + let mapBounds: UIEventSource = undefined if (preset.preciseInput.snapToLayers) { - // We have to snap to certain layers. - // Lets fetch tehm - const asSet = new Set(preset.preciseInput.snapToLayers) - features = State.state.featurePipeline.features.map(f => f.filter(feat => asSet.has(feat.feature._matching_layer_id))) + snapToFeatures = new UIEventSource<{ feature: any }[]>([]) + mapBounds = new UIEventSource(undefined) } + + const tags = TagUtils.KVtoProperties(preset.tags ?? []); preciseInput = new LocationInput({ mapBackground: backgroundLayer, centerLocation: locationSrc, - snapTo: features, + snapTo: snapToFeatures, snappedPointTags: tags, - maxSnapDistance: preset.preciseInput.maxSnapDistance - + maxSnapDistance: preset.preciseInput.maxSnapDistance, + bounds: mapBounds }) preciseInput.SetClass("h-32 rounded-xl overflow-hidden border border-gray").SetStyle("height: 12rem;") + + + if (preset.preciseInput.snapToLayers) { + // We have to snap to certain layers. + // Lets fetch them + + let loadedBbox : BBox= undefined + mapBounds?.addCallbackAndRunD(bbox => { + if(loadedBbox !== undefined && bbox.isContainedIn(loadedBbox)){ + // All is already there + // return; + } + + bbox = bbox.pad(2); + loadedBbox = bbox; + const allFeatures: {feature: any}[] = [] + preset.preciseInput.snapToLayers.forEach(layerId => { + State.state.featurePipeline.GetFeaturesWithin(layerId, bbox).forEach(feats => allFeatures.push(...feats.map(f => ({feature :f})))) + }) + snapToFeatures.setData(allFeatures) + }) + } + } diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index a18c887f8..a350e38d3 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -7,7 +7,7 @@ import Combine from "../Base/Combine"; import Svg from "../../Svg"; import State from "../../State"; import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; -import {GeoOperations} from "../../Logic/GeoOperations"; +import {BBox, GeoOperations} from "../../Logic/GeoOperations"; import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; import * as L from "leaflet"; import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; @@ -38,6 +38,8 @@ export default class LocationInput extends InputElement { private readonly _snappedPoint: UIEventSource private readonly _maxSnapDistance: number private readonly _snappedPointTags: any; + private readonly _bounds: UIEventSource; + public readonly _matching_layer: UIEventSource; constructor(options: { mapBackground?: UIEventSource, @@ -46,32 +48,33 @@ export default class LocationInput extends InputElement { snappedPointTags?: any, requiresSnapping?: boolean, centerLocation: UIEventSource, + bounds?: UIEventSource }) { super(); this._snapTo = options.snapTo?.map(features => features?.filter(feat => feat.feature.geometry.type !== "Point")) this._maxSnapDistance = options.maxSnapDistance this._centerLocation = options.centerLocation; this._snappedPointTags = options.snappedPointTags + this._bounds = options.bounds; if (this._snapTo === undefined) { this._value = this._centerLocation; } else { const self = this; - let matching_layer: UIEventSource if (self._snappedPointTags !== undefined) { - matching_layer = State.state.layoutToUse.map(layout => { + this._matching_layer = State.state.layoutToUse.map(layout => { for (const layer of layout.layers) { if (layer.source.osmTags.matchesProperties(self._snappedPointTags)) { - return layer.id + return layer } } console.error("No matching layer found for tags ", self._snappedPointTags) - return "matchpoint" + return LocationInput.matchLayer }) } else { - matching_layer = new UIEventSource("matchpoint") + this._matching_layer = new UIEventSource(LocationInput.matchLayer) } this._snappedPoint = options.centerLocation.map(loc => { @@ -83,7 +86,7 @@ export default class LocationInput extends InputElement { let min = undefined; let matchedWay = undefined; - for (const feature of self._snapTo.data) { + for (const feature of self._snapTo.data ?? []) { const nearestPointOnLine = GeoOperations.nearestPoint(feature.feature, [loc.lon, loc.lat]) if (min === undefined) { min = nearestPointOnLine @@ -98,19 +101,17 @@ export default class LocationInput extends InputElement { } } - if (min.properties.dist * 1000 > self._maxSnapDistance) { + if (min === undefined || min.properties.dist * 1000 > self._maxSnapDistance) { if (options.requiresSnapping) { return undefined } else { return { "type": "Feature", - "_matching_layer_id": matching_layer.data, "properties": options.snappedPointTags ?? min.properties, "geometry": {"type": "Point", "coordinates": [loc.lon, loc.lat]} } } } - min._matching_layer_id = matching_layer?.data ?? "matchpoint" min.properties = options.snappedPointTags ?? min.properties self.snappedOnto.setData(matchedWay) return min @@ -144,84 +145,40 @@ export default class LocationInput extends InputElement { location: this._centerLocation, background: this.mapBackground, attribution: this.mapBackground !== State.state.backgroundLayer, - lastClickLocation: clickLocation + lastClickLocation: clickLocation, + bounds: this._bounds } ) clickLocation.addCallbackAndRunD(location => this._centerLocation.setData(location)) - map.leafletMap.addCallbackAndRunD(leaflet => { - const bounds = leaflet.getBounds() - leaflet.setMaxBounds(bounds.pad(0.15)) - const data = { - type: "FeatureCollection", - features: [{ - "type": "Feature", - "geometry": { - "type": "LineString", - "coordinates": [ - [ - bounds.getEast(), - bounds.getNorth() - ], - [ - bounds.getWest(), - bounds.getNorth() - ], - [ - bounds.getWest(), - bounds.getSouth() - ], - [ - bounds.getEast(), - bounds.getSouth() - ], - [ - bounds.getEast(), - bounds.getNorth() - ] - ] - } - }] - } - // @ts-ignore - L.geoJSON(data, { - style: { - color: "#f00", - weight: 2, - opacity: 0.4 - } - }).addTo(leaflet) - }) + map.installBounds(0.15, true); if (this._snapTo !== undefined) { - + + // Show the lines to snap to + new ShowDataMultiLayer({ + features: new StaticFeatureSource(this._snapTo, true), + enablePopups: false, + zoomToFeatures: false, + leafletMap: map.leafletMap, + layers: State.state.filteredLayers + } + ) + // Show the central point const matchPoint = this._snappedPoint.map(loc => { if (loc === undefined) { return [] } return [{feature: loc}]; }) - if (this._snapTo) { - if (this._snappedPointTags === undefined) { - // No special tags - we show a default crosshair - new ShowDataLayer({ - features: new StaticFeatureSource(matchPoint), - enablePopups: false, - zoomToFeatures: false, - leafletMap: map.leafletMap, - layerToShow: LocationInput.matchLayer - }) - }else{ - new ShowDataMultiLayer({ - features: new StaticFeatureSource(matchPoint), - enablePopups: false, - zoomToFeatures: false, - leafletMap: map.leafletMap, - layers: State.state.filteredLayers - } - ) - } - } + new ShowDataLayer({ + features: new StaticFeatureSource(matchPoint, true), + enablePopups: false, + zoomToFeatures: false, + leafletMap: map.leafletMap, + layerToShow: this._matching_layer.data + }) + } this.mapBackground.map(layer => { diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index 694ef48cb..b7e33546b 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -130,7 +130,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen { if (!userbadge) { return undefined } - return new Combine(editElements) + return new Combine(editElements).SetClass("flex flex-col") } )) renderings.push(editors) diff --git a/UI/Popup/SplitRoadWizard.ts b/UI/Popup/SplitRoadWizard.ts index ca0980592..01fb00d05 100644 --- a/UI/Popup/SplitRoadWizard.ts +++ b/UI/Popup/SplitRoadWizard.ts @@ -5,13 +5,12 @@ import {SubtleButton} from "../Base/SubtleButton"; import Minimap from "../Base/Minimap"; import State from "../../State"; import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; -import {GeoOperations} from "../../Logic/GeoOperations"; +import {BBox, GeoOperations} from "../../Logic/GeoOperations"; import {LeafletMouseEvent} from "leaflet"; import Combine from "../Base/Combine"; import {Button} from "../Base/Button"; import Translations from "../i18n/Translations"; import SplitAction from "../../Logic/Osm/Actions/SplitAction"; -import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; import Title from "../Base/Title"; import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; @@ -21,9 +20,12 @@ export default class SplitRoadWizard extends Toggle { private static splitLayerStyling = new LayerConfig({ id: "splitpositions", source: {osmTags: "_cutposition=yes"}, - icon: "./assets/svg/plus.svg" + icon: {render: "circle:white;./assets/svg/scissors.svg"}, + iconSize: {render: "30,30,center"}, }, "(BUILTIN) SplitRoadWizard.ts", true) + public dialogIsOpened: UIEventSource + /** * A UI Element used for splitting roads * @@ -40,30 +42,40 @@ export default class SplitRoadWizard extends Toggle { // Toggle variable between show split button and map const splitClicked = new UIEventSource(false); + // Load the road with given id on the minimap + const roadElement = State.state.allElements.ContainingFeatures.get(id) // Minimap on which you can select the points to be splitted - const miniMap = Minimap.createMiniMap({background: State.state.backgroundLayer, allowMoving: false}); - miniMap.SetStyle("width: 100%; height: 24rem;"); + const miniMap = Minimap.createMiniMap( + { + background: State.state.backgroundLayer, + allowMoving: true, + leafletOptions: { + minZoom: 14 + } + }); + miniMap.SetStyle("width: 100%; height: 24rem") + .SetClass("rounded-xl overflow-hidden"); + + miniMap.installBounds(BBox.get(roadElement)) // Define how a cut is displayed on the map - // Load the road with given id on the minimap - const roadElement = State.state.allElements.ContainingFeatures.get(id) - const roadEventSource = new UIEventSource([{feature: roadElement, freshness: new Date()}]); // Datalayer displaying the road and the cut points (if any) - new ShowDataMultiLayer({ - features: new StaticFeatureSource(roadEventSource, true), - layers: State.state.filteredLayers, - leafletMap: miniMap.leafletMap, - enablePopups: false, - zoomToFeatures: true - }) new ShowDataLayer({ features: new StaticFeatureSource(splitPoints, true), leafletMap: miniMap.leafletMap, zoomToFeatures: false, enablePopups: false, - layerToShow: SplitRoadWizard.splitLayerStyling + layerToShow: SplitRoadWizard.splitLayerStyling + }) + + new ShowDataMultiLayer({ + features: new StaticFeatureSource([roadElement]), + layers: State.state.filteredLayers, + leafletMap: miniMap.leafletMap, + enablePopups: false, + zoomToFeatures: true }) /** @@ -72,12 +84,25 @@ export default class SplitRoadWizard extends Toggle { * @param coordinates Clicked location, [lon, lat] */ function onMapClick(coordinates) { + // First, we check if there is another, already existing point nearby + const points = splitPoints.data.map((f, i) => [f.feature, i]) + .filter(p => GeoOperations.distanceBetween(p[0].geometry.coordinates, coordinates) * 1000 < 5) + .map(p => p[1]) + .sort() + .reverse() + if (points.length > 0) { + for (const point of points) { + splitPoints.data.splice(point, 1) + } + splitPoints.ping() + return; + } + // Get nearest point on the road const pointOnRoad = GeoOperations.nearestPoint(roadElement, coordinates); // pointOnRoad is a geojson // Update point properties to let it match the layer pointOnRoad.properties._cutposition = "yes"; - pointOnRoad["_matching_layer_id"] = "splitpositions"; // let the state remember the point, to be able to retrieve it later by id State.state.allElements.addOrGetElement(pointOnRoad); @@ -94,7 +119,7 @@ export default class SplitRoadWizard extends Toggle { })) // Toggle between splitmap - const splitButton = new SubtleButton(Svg.scissors_ui(), t.inviteToSplit.Clone()); + const splitButton = new SubtleButton(Svg.scissors_ui(), t.inviteToSplit.Clone().SetClass("text-lg font-bold")); splitButton.onClick( () => { splitClicked.setData(true) @@ -110,27 +135,9 @@ export default class SplitRoadWizard extends Toggle { // Save button const saveButton = new Button(t.split.Clone(), () => { hasBeenSplit.setData(true) - const way = OsmObject.DownloadObject(id) - const partOfSrc = OsmObject.DownloadReferencingRelations(id); - let hasRun = false - way.map(way => { - const partOf = partOfSrc.data - if (way === undefined || partOf === undefined) { - return; - } - if (hasRun) { - return - } - hasRun = true - const splitAction = new SplitAction( - way, way.asGeoJson(), partOf, splitPoints.data.map(ff => ff.feature) - ) - State.state.changes.applyAction(splitAction) + State.state.changes.applyAction(new SplitAction(id, splitPoints.data.map(ff => ff.feature.geometry.coordinates))) + }) - }, [partOfSrc]) - - - }); saveButton.SetClass("btn btn-primary mr-3"); const disabledSaveButton = new Button("Split", undefined); disabledSaveButton.SetClass("btn btn-disabled mr-3"); @@ -152,5 +159,6 @@ export default class SplitRoadWizard extends Toggle { mapView.SetClass("question") const confirm = new Toggle(mapView, splitToggle, splitClicked); super(t.hasBeenSplit.Clone(), confirm, hasBeenSplit) + this.dialogIsOpened = splitClicked } } \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 73a68247c..cdda7889b 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -37,8 +37,8 @@ export default class ShowDataLayer { this._layerToShow = options.layerToShow; const self = this; - features.addCallback(() => self.update(options)); - options.leafletMap.addCallback(() => self.update(options)); + features.addCallback(_ => self.update(options)); + options.leafletMap.addCallback(_ => self.update(options)); this.update(options); @@ -83,13 +83,17 @@ export default class ShowDataLayer { mp.removeLayer(this.geoLayer); } + this.geoLayer= this.CreateGeojsonLayer() const allFeats = this._features.data; - this.geoLayer = this.CreateGeojsonLayer(); for (const feat of allFeats) { if (feat === undefined) { continue } - this.geoLayer.addData(feat); + try{ + this.geoLayer.addData(feat); + }catch(e){ + console.error("Could not add ", feat, "to the geojson layer in leaflet") + } } mp.addLayer(this.geoLayer) @@ -122,7 +126,8 @@ export default class ShowDataLayer { } const tagSource = feature.properties.id === undefined ? new UIEventSource(feature.properties) : State.state.allElements.getEventSourceById(feature.properties.id) - const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0)); + const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) + const style = layer.GenerateLeafletStyle(tagSource, clickable); const baseElement = style.icon.html; if (!this._enablePopups) { baseElement.SetStyle("cursor: initial !important") @@ -132,7 +137,7 @@ export default class ShowDataLayer { html: baseElement.ConstructElement(), className: style.icon.className, iconAnchor: style.icon.iconAnchor, - iconUrl: style.icon.iconUrl, + iconUrl: style.icon.iconUrl ?? "./assets/svg/bug.svg", popupAnchor: style.icon.popupAnchor, iconSize: style.icon.iconSize }) diff --git a/Utils.ts b/Utils.ts index 606dccb32..f3e82d574 100644 --- a/Utils.ts +++ b/Utils.ts @@ -8,7 +8,7 @@ export class Utils { * 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; + public static runningFromConsole = typeof window === "undefined"; public static readonly assets_path = "./assets/svg/"; public static externalDownloadFunction: (url: string) => Promise; private static knownKeys = ["addExtraTags", "and", "calculatedTags", "changesetmessage", "clustering", "color", "condition", "customCss", "dashArray", "defaultBackgroundId", "description", "descriptionTail", "doNotDownload", "enableAddNewPoints", "enableBackgroundLayerSelection", "enableGeolocation", "enableLayers", "enableMoreQuests", "enableSearch", "enableShareScreen", "enableUserBadge", "freeform", "hideFromOverview", "hideInAnswer", "icon", "iconOverlays", "iconSize", "id", "if", "ifnot", "isShown", "key", "language", "layers", "lockLocation", "maintainer", "mappings", "maxzoom", "maxZoom", "minNeededElements", "minzoom", "multiAnswer", "name", "or", "osmTags", "passAllFeatures", "presets", "question", "render", "roaming", "roamingRenderings", "rotation", "shortDescription", "socialImage", "source", "startLat", "startLon", "startZoom", "tagRenderings", "tags", "then", "title", "titleIcons", "type", "version", "wayHandling", "widenFactor", "width"] diff --git a/assets/layers/bench/bench.json b/assets/layers/bench/bench.json index a2cf274dd..7f2c93fcf 100644 --- a/assets/layers/bench/bench.json +++ b/assets/layers/bench/bench.json @@ -606,7 +606,7 @@ "fi": "Lisää uusi penkki", "pl": "Dodaj nową ławkę" }, - "presiceInput": { + "preciseInput": { "preferredBackground": "photo" } } diff --git a/assets/layers/crossings/crossings.json b/assets/layers/crossings/crossings.json index 876656d1f..0d033f2d7 100644 --- a/assets/layers/crossings/crossings.json +++ b/assets/layers/crossings/crossings.json @@ -97,6 +97,7 @@ } ], "tagRenderings": [ + "images", { "question": { "en": "What kind of crossing is this?", diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index f6acd494b..3645da855 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -49,7 +49,7 @@ }, "calculatedTags": [ "_closest_other_drinking_water_id=feat.closest('drinking_water')?.id", - "_closest_other_drinking_water_distance=Math.floor(feat.distanceTo(feat.closest('drinking_water')) * 1000)" + "_closest_other_drinking_water_distance=Math.floor(feat.distanceTo(feat.closest('drinking_water')).distance * 1000)" ], "minzoom": 13, "wayHandling": 1, diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index f8e8840db..0bee39135 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -1153,10 +1153,10 @@ "path": "scissors.svg", "license": "CC-BY 3.0", "authors": [ - "The noun project - Basith Ibrahi" + "The noun project - Icons8" ], "sources": [ - "https://commons.wikimedia.org/wiki/File:Media-floppy.svg" + "https://commons.wikimedia.org/wiki/File:Scissors_-_The_Noun_Project.svg" ] }, { @@ -1377,4 +1377,4 @@ "https://www.wikipedia.org/" ] } -] \ No newline at end of file +] diff --git a/assets/svg/plus.svg b/assets/svg/plus.svg index dcff18237..385b17152 100644 --- a/assets/svg/plus.svg +++ b/assets/svg/plus.svg @@ -1,20 +1,59 @@ - - - + 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="97.287025" + height="97.287033" + viewBox="0 0 97.287025 97.287033" + version="1.1" + id="svg132" + style="fill:none" + sodipodi:docname="plus.svg" + inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> + + + + image/svg+xml + + + + + + + + diff --git a/assets/svg/scissors.svg b/assets/svg/scissors.svg index 1b4c786d4..7c8df5cd0 100644 --- a/assets/svg/scissors.svg +++ b/assets/svg/scissors.svg @@ -1,69 +1 @@ - -image/svg+xml - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/assets/themes/cycle_infra/cycle_infra.json b/assets/themes/cycle_infra/cycle_infra.json index b91804ea9..7446095d9 100644 --- a/assets/themes/cycle_infra/cycle_infra.json +++ b/assets/themes/cycle_infra/cycle_infra.json @@ -24,7 +24,7 @@ "startLat": 51, "startLon": 3.75, "startZoom": 11, - "widenFactor": 0.05, + "widenFactor": 1, "socialImage": "./assets/themes/cycle_infra/cycle-infra.svg", "enableDownload": true, "layers": [ diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index 8b607ea37..e9d9b4e95 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -68,7 +68,7 @@ ] }, "then": { - "nl": "Deze straat i een fietsstraat", + "nl": "Deze straat is een fietsstraat", "en": "This street is a cyclestreet", "ja": "この通りはcyclestreetだ", "nb_NO": "Denne gaten er en sykkelvei" @@ -276,8 +276,10 @@ }, "tagRenderings": [ "images" - ], - "allowSplit": false + ] } - ] + ], + "overrideAll": { + "allowSplit": true + } } \ No newline at end of file diff --git a/css/mobile.css b/css/mobile.css index f863483dd..a0b6cb896 100644 --- a/css/mobile.css +++ b/css/mobile.css @@ -39,7 +39,7 @@ Contains tweaks for small screens } @media only screen and (max-width: 768px) { - .leaflet-control-attribution { + #leafletDiv .leaflet-control-attribution { display: none; } diff --git a/scripts/CycleHighwayFix.ts b/scripts/CycleHighwayFix.ts index 94a3db1b4..b6e4509d6 100644 --- a/scripts/CycleHighwayFix.ts +++ b/scripts/CycleHighwayFix.ts @@ -10,7 +10,7 @@ writeFileSync("cycleHighwayFix.osc", "", "utf8") const ids = JSON.parse(readFileSync("export.geojson", "utf-8")).features.map(f => f.properties["@id"]) console.log(ids) -ids.map(id => OsmObject.DownloadReferencingRelations(id).addCallbackAndRunD(relations => { +ids.map(id => OsmObject.DownloadReferencingRelations(id).then(relations => { console.log(relations) const changeparts = relations.filter(relation => relation.tags["cycle_highway"] == "yes" && relation.tags["note:state"] == undefined) .map(relation => { @@ -18,5 +18,4 @@ ids.map(id => OsmObject.DownloadReferencingRelations(id).addCallbackAndRunD(rela return relation.ChangesetXML(undefined) }) appendFileSync("cycleHighwayFix.osc", changeparts.join("\n"), "utf8") - return true; })) \ No newline at end of file diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts index d150215e7..3551ba5f8 100644 --- a/scripts/ScriptUtils.ts +++ b/scripts/ScriptUtils.ts @@ -56,7 +56,7 @@ export default class ScriptUtils { const headers = options?.headers ?? {} headers.accept = "application/json" - + console.log("Fetching", url) const urlObj = new URL(url) https.get({ host: urlObj.host, @@ -75,6 +75,7 @@ export default class ScriptUtils { res.addListener('end', function () { const result = parts.join("") try { + console.log("Fetched", result) resolve(JSON.parse(result)) } catch (e) { console.error("Could not parse the following as JSON:", result) diff --git a/scripts/generateCache.ts b/scripts/generateCache.ts index c894d5442..5a8377125 100644 --- a/scripts/generateCache.ts +++ b/scripts/generateCache.ts @@ -201,7 +201,10 @@ function postProcess(allFeatures: FeatureSource, theme: LayoutConfig, relationsT } }, layer, - false); + { + includeDates: false, + includeNonDates: true + }); const createdTiles = [] // At this point, we have all the features of the entire area. diff --git a/test.ts b/test.ts index c34a6c303..86a1c8f6e 100644 --- a/test.ts +++ b/test.ts @@ -1,16 +1,50 @@ -const client_token = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" +import SplitRoadWizard from "./UI/Popup/SplitRoadWizard"; +import State from "./State"; +import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; +import MinimapImplementation from "./UI/Base/MinimapImplementation"; +import {UIEventSource} from "./Logic/UIEventSource"; +import FilteredLayer from "./Models/FilteredLayer"; +import {And} from "./Logic/Tags/And"; -const image_id = '196804715753265'; -const api_url = 'https://graph.mapillary.com/' + image_id + '?fields=thumb_1024_url&&access_token=' + client_token; -fetch(api_url, - { - headers: {'Authorization': 'OAuth ' + client_token} +const layout = AllKnownLayouts.allKnownLayouts.get("cyclestreets") +State.state = new State(layout) +MinimapImplementation.initialize() +const feature = { + "type": "Feature", + "properties": { + id: "way/1234", + "highway":"residential", + "cyclestreet":"yes" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 3.2207107543945312, + 51.21978729870313 + ], + [ + 3.2198524475097656, + 51.21899435057332 + ], + [ + 3.2155394554138184, + 51.21617188199714 + ] + ] } -).then(response => { - return response.json() -}).then( - json => { - const thumbnail_url = json["thumb_1024"] - console.log(thumbnail_url) - } -) \ No newline at end of file +} + +State.state.allElements.addOrGetElement(feature) +State.state.filteredLayers = new UIEventSource( + layout.layers.map( l => ({ + layerDef :l, + appliedFilters: new UIEventSource(undefined), + isDisplayed: new UIEventSource(undefined) + })) +) + +const splitroad = new SplitRoadWizard("way/1234") + splitroad.AttachTo("maindiv") + +splitroad.dialogIsOpened.setData(true) diff --git a/test/ImageAttribution.spec.ts b/test/ImageAttribution.spec.ts index c83d9efa1..ac2109644 100644 --- a/test/ImageAttribution.spec.ts +++ b/test/ImageAttribution.spec.ts @@ -10,7 +10,7 @@ Utils.runningFromConsole = true; export default class ImageAttributionSpec extends T { constructor() { super( - "ImageAttribution Tests", [ + "imageattribution", [ [ "Should find all the images", () => { diff --git a/test/ImageSearcher.spec.ts b/test/ImageSearcher.spec.ts index 49254fd62..9b3d713ac 100644 --- a/test/ImageSearcher.spec.ts +++ b/test/ImageSearcher.spec.ts @@ -8,7 +8,7 @@ Utils.runningFromConsole = true; export default class ImageSearcherSpec extends T { constructor() { - super("ImageSearcher", [ + super("imagesearcher", [ [ "Should find images", () => { diff --git a/test/OsmConnection.spec.ts b/test/OsmConnection.spec.ts index a2f7cca47..0b6cf3b47 100644 --- a/test/OsmConnection.spec.ts +++ b/test/OsmConnection.spec.ts @@ -12,7 +12,7 @@ export default class OsmConnectionSpec extends T { private static _osm_token = "LJFmv2nUicSNmBNsFeyCHx5KKx6Aiesx8pXPbX4n" constructor() { - super("OsmConnectionSpec-test", [ + super("osmconnection", [ ["login on dev", () => { const osmConn = new OsmConnection(false, false, diff --git a/test/OsmObject.spec.ts b/test/OsmObject.spec.ts index 563c5ade6..e26dae4d6 100644 --- a/test/OsmObject.spec.ts +++ b/test/OsmObject.spec.ts @@ -1,27 +1,26 @@ import T from "./TestHelper"; import {OsmObject} from "../Logic/Osm/OsmObject"; import ScriptUtils from "../scripts/ScriptUtils"; +import {UIEventSource} from "../Logic/UIEventSource"; export default class OsmObjectSpec extends T { + private static async runTest(){ + const ways = await OsmObject.DownloadReferencingWays("node/1124134958") + if(ways === undefined){ + throw "Did not get the ways" + } + if (ways.length !== 4) { + throw "Expected 4 ways but got "+ways.length + } + } + + constructor() { - super("OsmObject", [ + super("osmobject", [ [ "Download referencing ways", () => { - let downloaded = false; - OsmObject.DownloadReferencingWays("node/1124134958").addCallbackAndRunD(ways => { - downloaded = true; - console.log(ways) - }) - let timeout = 10 - while (!downloaded && timeout >= 0) { - ScriptUtils.sleep(1000) - - timeout--; - } - if (!downloaded) { - throw "Timeout: referencing ways not found" - } + OsmObjectSpec.runTest().then(_ => console.log("Referencing ways test is done (async)")) } ] diff --git a/test/RelationSplitHandler.spec.ts b/test/RelationSplitHandler.spec.ts new file mode 100644 index 000000000..8a5174da3 --- /dev/null +++ b/test/RelationSplitHandler.spec.ts @@ -0,0 +1,66 @@ +import T from "./TestHelper"; +import {InPlaceReplacedmentRTSH} from "../Logic/Osm/Actions/RelationSplitHandler"; +import {OsmObject, OsmRelation} from "../Logic/Osm/OsmObject"; +import {Changes} from "../Logic/Osm/Changes"; +import {equal} from "assert"; + +export default class RelationSplitHandlerSpec extends T { + + private static async split(): Promise { + // Lets mimick a split action of https://www.openstreetmap.org/way/295132739 + + const relation: OsmRelation = await OsmObject.DownloadObjectAsync("relation/9572808") + const originalNodeIds = [5273988967, + 170497153, + 1507524582, + 4524321710, + 170497155, + 170497157, + 170497158, + 3208166179, + 1507524610, + 170497160, + 3208166178, + 1507524573, + 1575932830, + 6448669326] + + const withSplit = [[5273988967, + 170497153, + 1507524582, + 4524321710, + 170497155, + 170497157, + 170497158], + [ + 3208166179, + 1507524610, + 170497160, + 3208166178, + 1507524573, + 1575932830, + 6448669326]] + + const splitter = new InPlaceReplacedmentRTSH( + { + relation: relation, + originalWayId: 295132739, + allWayIdsInOrder: [295132739, -1], + originalNodes: originalNodeIds, + allWaysNodesInOrder: withSplit + }) + const changeDescription = await splitter.CreateChangeDescriptions(new Changes()) + const allIds = changeDescription[0].changes["members"].map(m => m.ref).join(",") + const expected = "687866206,295132739,-1,690497698" + if (allIds.indexOf(expected) < 0) { + throw "Invalid order or the split ways. If this suddenly breaks, the parent relation at https://osm.org/relation/9572808 has probably changed and the test must be updated" + } + } + + constructor() { + super("relationsplithandler", [ + ["split 295132739", + () => RelationSplitHandlerSpec.split().then(_ => console.log("OK"))] + ]); + } +} \ No newline at end of file diff --git a/test/Tag.spec.ts b/test/Tag.spec.ts index 96afa16fc..a85d3c58d 100644 --- a/test/Tag.spec.ts +++ b/test/Tag.spec.ts @@ -16,7 +16,7 @@ Utils.runningFromConsole = true; export default class TagSpec extends T { constructor() { - super("Tags", [ + super("tag", [ ["Tag replacement works in translation", () => { const tr = new Translation({ "en": "Test {key} abc" diff --git a/test/TestAll.ts b/test/TestAll.ts index 8eff8b56d..bd0ec5657 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -1,36 +1,16 @@ -import {Utils} from "../Utils"; -Utils.runningFromConsole = true; import TagSpec from "./Tag.spec"; import ImageAttributionSpec from "./ImageAttribution.spec"; import GeoOperationsSpec from "./GeoOperations.spec"; import ImageSearcherSpec from "./ImageSearcher.spec"; import ThemeSpec from "./Theme.spec"; import UtilsSpec from "./Utils.spec"; -import OsmConnectionSpec from "./OsmConnection.spec"; -import T from "./TestHelper"; -import {FixedUiElement} from "../UI/Base/FixedUiElement"; -import Combine from "../UI/Base/Combine"; import OsmObjectSpec from "./OsmObject.spec"; import ScriptUtils from "../scripts/ScriptUtils"; import UnitsSpec from "./Units.spec"; +import RelationSplitHandlerSpec from "./RelationSplitHandler.spec"; -export default class TestAll { - private needsBrowserTests: T[] = [new OsmConnectionSpec()] - - public testAll(): void { - Utils.runningFromConsole = false - for (const test of this.needsBrowserTests.concat(allTests)) { - if (test.failures.length > 0) { - new Combine([new FixedUiElement("TEST FAILED: " + test.name).SetStyle("background: red"), - ...test.failures]) - .AttachTo("maindiv") - throw "Some test failed" - } - } - } -} ScriptUtils.fixUtils() const allTests = [ new OsmObjectSpec(), @@ -40,12 +20,34 @@ const allTests = [ new ImageSearcherSpec(), new ThemeSpec(), new UtilsSpec(), - new UnitsSpec() + new UnitsSpec(), + new RelationSplitHandlerSpec() ] +let args = [...process.argv] +args.splice(0, 2) +args = args.map(a => a.toLowerCase()) -for (const test of allTests) { - if (test.failures.length > 0) { - throw "Some test failed: " + test.failures.join(", ") +const allFailures: { testsuite: string, name: string, msg: string } [] = [] +let testsToRun = allTests +if (args.length > 0) { + testsToRun = allTests.filter(t => args.indexOf(t.name) >= 0) +} + +if(testsToRun.length == 0){ + throw "No tests found" +} + +for (let i = 0; i < testsToRun.length; i++){ + const test = testsToRun[i]; + ScriptUtils.erasableLog(" Running test", i, "/", allTests.length) + allFailures.push(...(test.Run() ?? [])) + +} +if (allFailures.length > 0) { + for (const failure of allFailures) { + console.error(" !! " + failure.testsuite + "." + failure.name + " failed due to: " + failure.msg) } -} \ No newline at end of file + throw "Some test failed" +} +console.log("All tests successful: ", allTests.map(t => t.name).join(", ")) diff --git a/test/TestHelper.ts b/test/TestHelper.ts index f080c0aa6..971b64396 100644 --- a/test/TestHelper.ts +++ b/test/TestHelper.ts @@ -1,23 +1,31 @@ export default class T { - public readonly failures: string[] = [] public readonly name: string; + private readonly _tests: [string, (() => void)][]; constructor(testsuite: string, tests: [string, () => void][]) { this.name = testsuite - for (const [name, test] of tests) { + this._tests = tests; + } + + /** + * RUns the test, returns the error messages. + * Returns an empty list if successful + * @constructor + */ + public Run() : ({testsuite: string, name: string, msg: string} []) { + const failures: {testsuite: string, name: string, msg: string} [] = [] + for (const [name, test] of this._tests) { try { test(); } catch (e) { - this.failures.push(name); - console.warn(`>>> Failed test in ${this.name}: ${name}because${e}`); + failures.push({testsuite: this.name, name: name, msg: ""+e}); } } - if (this.failures.length == 0) { - console.log(`All tests of ${testsuite} done!`) + if (failures.length == 0) { + return undefined } else { - console.warn(this.failures.length, `tests of ${testsuite} failed :(`) - console.log("Failed tests: ", this.failures.join(",")) + return failures } } diff --git a/test/Theme.spec.ts b/test/Theme.spec.ts index d4b071d6e..cb6cc2756 100644 --- a/test/Theme.spec.ts +++ b/test/Theme.spec.ts @@ -8,7 +8,7 @@ Utils.runningFromConsole = true; export default class ThemeSpec extends T { constructor() { - super("Theme tests", + super("theme", [ ["Nested overrides work", () => { diff --git a/test/Units.spec.ts b/test/Units.spec.ts index 3d316535a..e0ffdee10 100644 --- a/test/Units.spec.ts +++ b/test/Units.spec.ts @@ -6,7 +6,7 @@ import {Denomination} from "../Models/Denomination"; export default class UnitsSpec extends T { constructor() { - super("Units", [ + super("units", [ ["Simple canonicalize", () => { const unit = new Denomination({ diff --git a/test/Utils.spec.ts b/test/Utils.spec.ts index 0975621e1..fd630257d 100644 --- a/test/Utils.spec.ts +++ b/test/Utils.spec.ts @@ -39,7 +39,7 @@ export default class UtilsSpec extends T { } constructor() { - super("Utils", [ + super("utils", [ ["Sort object keys", () => { const o = { x: 'x', From bef684aec7fe642be7b58314d539161c303671d0 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 22 Sep 2021 16:07:56 +0200 Subject: [PATCH 022/110] More cleanup, first somewhat working version of #171 --- Logic/Actors/GeoLocationHandler.ts | 5 +- Logic/Actors/OverpassFeatureSource.ts | 10 +- Logic/Actors/TitleHandler.ts | 2 - Logic/FeatureSource/FeaturePipeline.ts | 4 +- .../ChangeGeometryApplicator.ts} | 17 +- .../TiledFromLocalStorageSource.ts | 2 +- Logic/Osm/Actions/RelationSplitHandler.ts | 4 +- Logic/Osm/Actions/SplitAction.ts | 20 +- Logic/Osm/Changes.ts | 6 +- Logic/Osm/OsmObject.ts | 76 ++--- Logic/Osm/OsmPreferences.ts | 8 +- Logic/Osm/Overpass.ts | 30 +- Models/ThemeConfig/Json/LayoutConfigJson.ts | 2 +- Models/ThemeConfig/LayoutConfig.ts | 2 +- Utils.ts | 62 ++-- assets/themes/cyclestreets/cyclestreets.json | 2 +- test/RelationSplitHandler.spec.ts | 23 ++ test/SplitAction.spec.ts | 274 ++++++++++++++++++ test/TestAll.ts | 25 +- 19 files changed, 439 insertions(+), 135 deletions(-) rename Logic/FeatureSource/{ChangeApplicator.ts => Sources/ChangeGeometryApplicator.ts} (87%) create mode 100644 test/SplitAction.spec.ts diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index e3e2455bf..01e09a717 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -230,7 +230,7 @@ export default class GeoLocationHandler extends VariableUiElement { navigator?.permissions ?.query({name: "geolocation"}) ?.then(function (status) { - console.log("Geolocation is already", status); + console.log("Geolocation permission is ", status.state); if (status.state === "granted") { self.StartGeolocating(forceZoom); } @@ -289,7 +289,6 @@ export default class GeoLocationHandler extends VariableUiElement { private StartGeolocating(zoomToGPS = true) { const self = this; - console.log("Starting geolocation"); this._lastUserRequest = zoomToGPS ? new Date() : new Date(0); if (self._permission.data === "denied") { @@ -301,8 +300,6 @@ export default class GeoLocationHandler extends VariableUiElement { this.MoveToCurrentLoction(16); } - console.log("Searching location using GPS"); - if (self._isActive.data) { return; } diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index e788c7616..1e7ace7cd 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -184,8 +184,8 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour return; } this.runningQuery.setData(true); - overpass.queryGeoJson(queryBounds, - function (data, date) { + overpass.queryGeoJson(queryBounds). + then(([data, date]) => { self._previousBounds.get(z).push(queryBounds); self.retries.setData(0); const features = data.features.map(f => ({feature: f, freshness: date})); @@ -197,12 +197,12 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour console.error("Got the overpass response, but could not process it: ", e, e.stack) } self.runningQuery.setData(false); - }, - function (reason) { + }) + .catch((reason) => { self.retries.data++; self.ForceRefresh(); self.timeout.setData(self.retries.data * 5); - console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to ${reason}`); + console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to`, reason); self.retries.ping(); self.runningQuery.setData(false); diff --git a/Logic/Actors/TitleHandler.ts b/Logic/Actors/TitleHandler.ts index b5bc6f151..49661f289 100644 --- a/Logic/Actors/TitleHandler.ts +++ b/Logic/Actors/TitleHandler.ts @@ -8,8 +8,6 @@ export default class TitleHandler { constructor(state) { const currentTitle: UIEventSource = state.selectedElement.map( selected => { - console.log("UPdating title") - const layout = state.layoutToUse.data const defaultTitle = Translations.WT(layout?.title)?.txt ?? "MapComplete" diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 2fe254eb1..c6206eac4 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -21,7 +21,7 @@ import {BBox} from "../GeoOperations"; import {TileHierarchyMerger} from "./TiledFeatureSource/TileHierarchyMerger"; import RelationsTracker from "../Osm/RelationsTracker"; import {NewGeometryFromChangesFeatureSource} from "./Sources/NewGeometryFromChangesFeatureSource"; -import ChangeGeometryApplicator from "./ChangeApplicator"; +import ChangeGeometryApplicator from "./Sources/ChangeGeometryApplicator"; export default class FeaturePipeline implements FeatureSourceState { @@ -159,7 +159,6 @@ export default class FeaturePipeline implements FeatureSourceState { // Whenever fresh data comes in, we need to update the metatagging self.newDataLoadedSignal.stabilized(1000).addCallback(src => { - console.log("Got an update from ", src.name) self.updateAllMetaTagging() }) @@ -183,7 +182,6 @@ export default class FeaturePipeline implements FeatureSourceState { } private updateAllMetaTagging() { - console.log("Updating the meta tagging") const self = this; this.perLayerHierarchy.forEach(hierarchy => { hierarchy.loadedTiles.forEach(src => { diff --git a/Logic/FeatureSource/ChangeApplicator.ts b/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts similarity index 87% rename from Logic/FeatureSource/ChangeApplicator.ts rename to Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts index bab2225bf..a35879d96 100644 --- a/Logic/FeatureSource/ChangeApplicator.ts +++ b/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts @@ -1,15 +1,13 @@ -import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource} from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import {Changes} from "../Osm/Changes"; -import {ChangeDescription, ChangeDescriptionTools} from "../Osm/Actions/ChangeDescription"; -import {Utils} from "../../Utils"; -import FilteredLayer from "../../Models/FilteredLayer"; -import {OsmNode, OsmRelation, OsmWay} from "../Osm/OsmObject"; - - /** * Applies geometry changes from 'Changes' onto every feature of a featureSource */ +import {Changes} from "../../Osm/Changes"; +import {UIEventSource} from "../../UIEventSource"; +import {FeatureSourceForLayer, IndexedFeatureSource} from "../FeatureSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {ChangeDescription, ChangeDescriptionTools} from "../../Osm/Actions/ChangeDescription"; + + export default class ChangeGeometryApplicator implements FeatureSourceForLayer { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name: string; @@ -76,6 +74,7 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer { // We only apply the last change as that one'll have the latest geometry const change = changesForFeature[changesForFeature.length - 1] copy.feature.geometry = ChangeDescriptionTools.getGeojsonGeometry(change) + console.log("Applying a geometry change onto ", feature, change, copy) newFeatures.push(copy) } this.features.setData(newFeatures) diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts index 0e4e091a3..e88a1d82d 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -27,7 +27,7 @@ export default class TiledFromLocalStorageSource implements TileHierarchy Utils.tile_from_index(i).join("/")).join(", ")) const zLevels = indexes.map(i => i % 100) const indexesSet = new Set(indexes) diff --git a/Logic/Osm/Actions/RelationSplitHandler.ts b/Logic/Osm/Actions/RelationSplitHandler.ts index dfca2d0fa..42562c035 100644 --- a/Logic/Osm/Actions/RelationSplitHandler.ts +++ b/Logic/Osm/Actions/RelationSplitHandler.ts @@ -15,13 +15,15 @@ export interface RelationSplitInput { * When a way is split and this way is part of a relation, the relation should be updated too to have the new segment if relevant. */ export default class RelationSplitHandler extends OsmChangeAction { + private readonly _input: RelationSplitInput; constructor(input: RelationSplitInput) { super() + this._input = input; } async CreateChangeDescriptions(changes: Changes): Promise { - return []; + return new InPlaceReplacedmentRTSH(this._input).CreateChangeDescriptions(changes) } diff --git a/Logic/Osm/Actions/SplitAction.ts b/Logic/Osm/Actions/SplitAction.ts index cee912dca..085aab413 100644 --- a/Logic/Osm/Actions/SplitAction.ts +++ b/Logic/Osm/Actions/SplitAction.ts @@ -15,6 +15,11 @@ export default class SplitAction extends OsmChangeAction { private readonly wayId: string; private readonly _splitPointsCoordinates: [number, number] []// lon, lat + /** + * + * @param wayId + * @param splitPointCoordinates: lon, lat + */ constructor(wayId: string, splitPointCoordinates: [number, number][]) { super() this.wayId = wayId; @@ -39,12 +44,11 @@ export default class SplitAction extends OsmChangeAction { } async CreateChangeDescriptions(changes: Changes): Promise { - const self = this; - const originalElement = await OsmObject.DownloadObjectAsync(this.wayId) + const originalElement = await OsmObject.DownloadObjectAsync(this.wayId) const originalNodes = originalElement.nodes; // First, calculate splitpoints and remove points close to one another - const splitInfo = self.CalculateSplitCoordinates(originalElement) + const splitInfo = this.CalculateSplitCoordinates(originalElement) // Now we have a list with e.g. // [ { originalIndex: 0}, {originalIndex: 1, doSplit: true}, {originalIndex: 2}, {originalIndex: undefined, doSplit: true}, {originalIndex: 3}] @@ -166,8 +170,9 @@ export default class SplitAction extends OsmChangeAction { private CalculateSplitCoordinates(osmWay: OsmWay, toleranceInM = 5): SplitInfo[] { const wayGeoJson = osmWay.asGeoJson() // Should be [lon, lat][] - const originalPoints = osmWay.coordinates.map(c => <[number, number]>c.reverse()) + const originalPoints : [number, number][] = osmWay.coordinates.map(c => [c[1], c[0]]) const allPoints: { + // lon, lat coordinates: [number, number], isSplitPoint: boolean, originalIndex?: number, // Original index @@ -180,6 +185,7 @@ export default class SplitAction extends OsmChangeAction { // - `dist`: distance between pt and the closest point, // `location`: distance along the line between start and the closest point. let projected = GeoOperations.nearestPoint(wayGeoJson, c) + // c is lon lat return ({ coordinates: c, isSplitPoint: true, @@ -233,8 +239,12 @@ export default class SplitAction extends OsmChangeAction { if (distToNext > distToPrev) { closest = prevPoint } - // Ok, we have a closest point! + + if(closest.originalIndex === 0 || closest.originalIndex === originalPoints.length){ + // We can not split on the first or last points... + continue + } closest.isSplitPoint = true; allPoints.splice(i, 1) diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index aac1f08e9..83545b30a 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -14,7 +14,7 @@ import {LocalStorageSource} from "../Web/LocalStorageSource"; export class Changes { - private static _nextId = -1; // Newly assigned ID's are negative + private _nextId : number = -1; // Newly assigned ID's are negative public readonly name = "Newly added features" /** * All the newly created features as featureSource + all the modified features @@ -30,6 +30,8 @@ export class Changes { constructor() { // We keep track of all changes just as well this.allChanges.setData([...this.pendingChanges.data]) + // If a pending change contains a negative ID, we save that + this._nextId = Math.min(-1, ...this.pendingChanges.data?.map(pch => pch.id) ?? []) } private static createChangesetFor(csId: string, @@ -77,7 +79,7 @@ export class Changes { * Returns a new ID and updates the value for the next ID */ public getNewID() { - return Changes._nextId--; + return this._nextId--; } /** diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index a10ec8d0c..8027a18d9 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -63,17 +63,26 @@ export abstract class OsmObject { if (idN < 0) { return undefined; } - switch (type) { - case("node"): - return await new OsmNode(idN).Download(); - case("way"): - return await new OsmWay(idN).Download(); - case("relation"): - return await new OsmRelation(idN).Download(); - default: - throw ("Invalid object type:" + type + id); + const full = !id.startsWith("way") ? "" : "/full"; + const url = `${OsmObject.backendURL}api/0.6/${id}${full}`; + const rawData = await Utils.downloadJson(url) + // A full query might contain more then just the requested object (e.g. nodes that are part of a way, where we only want the way) + const parsed = OsmObject.ParseObjects(rawData.elements); + // Lets fetch the object we need + for (const osmObject of parsed) { + if(osmObject.type !== type){ + continue; + } + if(osmObject.id !== idN){ + continue + } + // Found the one! + return osmObject } + throw "PANIC: requested object is not part of the response" + + } @@ -205,6 +214,7 @@ export abstract class OsmObject { private static ParseObjects(elements: any[]): OsmObject[] { const objects: OsmObject[] = []; const allNodes: Map = new Map() + for (const element of elements) { const type = element.type; const idN = element.id; @@ -226,6 +236,11 @@ export abstract class OsmObject { osmObject.SaveExtraData(element, []) break; } + + if (osmObject !== undefined && OsmObject.backendURL !== OsmObject.defaultBackend) { + osmObject.tags["_backend"] = OsmObject.backendURL + } + osmObject?.LoadData(element) objects.push(osmObject) } @@ -237,7 +252,7 @@ export abstract class OsmObject { public abstract asGeoJson(): any; - abstract SaveExtraData(element: any, allElements: any[]); + abstract SaveExtraData(element: any, allElements: OsmObject[]); /** * Generates the changeset-XML for tags @@ -260,37 +275,6 @@ export abstract class OsmObject { return tags; } - /** - * Downloads the object, a full download for ways and relations - * @constructor - */ - async Download(): Promise { - const self = this; - const full = this.type !== "way" ? "" : "/full"; - const url = `${OsmObject.backendURL}api/0.6/${this.type}/${this.id}${full}`; - return await Utils.downloadJson(url).then(data => { - const element = data.elements.pop(); - let nodes = [] - if (self.type === "way" && data.elements.length >= 0) { - nodes = OsmObject.ParseObjects(data.elements) - } - - if (self.type === "rellation") { - throw "We should save some extra data" - } - - self.LoadData(element) - self.SaveExtraData(element, nodes); - - if (OsmObject.backendURL !== OsmObject.defaultBackend) { - self.tags["_backend"] = OsmObject.backendURL - } - return this; - } - ); - } - - abstract ChangesetXML(changesetId: string): string; protected VersionXML() { @@ -363,7 +347,7 @@ export class OsmNode extends OsmObject { export class OsmWay extends OsmObject { - nodes: number[]; + nodes: number[] = []; // The coordinates of the way, [lat, lon][] coordinates: [number, number][] = [] lat: number; @@ -400,6 +384,10 @@ export class OsmWay extends OsmObject { nodeDict.set(node.id, node) } + if (element.nodes === undefined) { + console.log("PANIC") + } + for (const nodeId of element.nodes) { const node = nodeDict.get(nodeId) if (node === undefined) { @@ -419,8 +407,8 @@ export class OsmWay extends OsmObject { } public asGeoJson() { - let coordinates : ([number, number][] | [number, number][][]) = this.coordinates.map(c => <[number, number]>c.reverse()); - if(this.isPolygon()){ + let coordinates: ([number, number][] | [number, number][][]) = this.coordinates.map(c => [c[1], c[0]]); + if (this.isPolygon()) { coordinates = [coordinates] } return { diff --git a/Logic/Osm/OsmPreferences.ts b/Logic/Osm/OsmPreferences.ts index 291b49a4f..fadd9c105 100644 --- a/Logic/Osm/OsmPreferences.ts +++ b/Logic/Osm/OsmPreferences.ts @@ -145,14 +145,14 @@ export class OsmPreferences { private SetPreference(k: string, v: string) { if (!this.userDetails.data.loggedIn) { - console.log(`Not saving preference ${k}: user not logged in`); + console.debug(`Not saving preference ${k}: user not logged in`); return; } if (this.preferences.data[k] === v) { return; } - console.log("Updating preference", k, " to ", Utils.EllipsesAfter(v, 15)); + console.debug("Updating preference", k, " to ", Utils.EllipsesAfter(v, 15)); if (v === undefined || v === "") { this.auth.xhr({ @@ -161,10 +161,10 @@ export class OsmPreferences { options: {header: {'Content-Type': 'text/plain'}}, }, function (error) { if (error) { - console.log("Could not remove preference", error); + console.warn("Could not remove preference", error); return; } - console.log("Preference ", k, "removed!"); + console.debug("Preference ", k, "removed!"); }); return; diff --git a/Logic/Osm/Overpass.ts b/Logic/Osm/Overpass.ts index 2f82889e3..de6b65287 100644 --- a/Logic/Osm/Overpass.ts +++ b/Logic/Osm/Overpass.ts @@ -31,7 +31,7 @@ export class Overpass { this._relationTracker = relationTracker } - queryGeoJson(bounds: Bounds, continuation: ((any, date: Date) => void), onFail: ((reason) => void)): void { + public async queryGeoJson(bounds: Bounds): Promise<[any, Date]> { let query = this.buildQuery("[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]") @@ -40,24 +40,18 @@ export class Overpass { query = Overpass.testUrl; } const self = this; - Utils.downloadJson(query) - .then(json => { - if (json.elements === [] && ((json.remarks ?? json.remark).indexOf("runtime error") >= 0)) { - console.log("Timeout or other runtime error"); - onFail("Runtime error (timeout)") - return; - } + const json = await Utils.downloadJson(query) + + if (json.elements === [] && ((json.remarks ?? json.remark).indexOf("runtime error") >= 0)) { + console.log("Timeout or other runtime error"); + throw("Runtime error (timeout)") + } - - self._relationTracker.RegisterRelations(json) - // @ts-ignore - const geojson = OsmToGeoJson.default(json); - const osmTime = new Date(json.osm3s.timestamp_osm_base); - - continuation(geojson, osmTime); - }).catch(e => { - onFail(e); - }) + self._relationTracker.RegisterRelations(json) + // @ts-ignore + const geojson = OsmToGeoJson.default(json); + const osmTime = new Date(json.osm3s.timestamp_osm_base); + return [geojson, osmTime]; } buildQuery(bbox: string): string { diff --git a/Models/ThemeConfig/Json/LayoutConfigJson.ts b/Models/ThemeConfig/Json/LayoutConfigJson.ts index 3c312ce2e..856ab2736 100644 --- a/Models/ThemeConfig/Json/LayoutConfigJson.ts +++ b/Models/ThemeConfig/Json/LayoutConfigJson.ts @@ -100,7 +100,7 @@ export interface LayoutConfigJson { * However, users tend to pan and zoom a lot. It is pretty annoying if every single pan means a reloading of the data. * For this, the bounds are widened in order to make a small pan still within bounds of the loaded data. * - * IF widenfactor is 0, this feature is disabled. A recommended value is between 0.5 and 0.01 (the latter for very dense queries) + * IF widenfactor is 1, this feature is disabled. A recommended value is between 1 and 3 */ widenFactor?: number; diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index 112810112..dd0d472ed 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -87,7 +87,7 @@ export default class LayoutConfig { this.startZoom = json.startZoom; this.startLat = json.startLat; this.startLon = json.startLon; - this.widenFactor = json.widenFactor ?? 0.05; + this.widenFactor = json.widenFactor ?? 1.5; this.roamingRenderings = (json.roamingRenderings ?? []).map((tr, i) => { if (typeof tr === "string") { if (SharedTagRenderings.SharedTagRendering.get(tr) !== undefined) { diff --git a/Utils.ts b/Utils.ts index f3e82d574..9f1373498 100644 --- a/Utils.ts +++ b/Utils.ts @@ -259,10 +259,10 @@ export class Utils { } static tile_bounds_lon_lat(z: number, x: number, y: number): [[number, number], [number, number]] { - return [[Utils.tile2long(x, z),Utils.tile2lat(y, z)], [Utils.tile2long(x + 1, z), Utils.tile2lat(y + 1, z)]] + return [[Utils.tile2long(x, z), Utils.tile2lat(y, z)], [Utils.tile2long(x + 1, z), Utils.tile2lat(y + 1, z)]] } - - static tile_index(z: number, x: number, y: number):number{ + + static tile_index(z: number, x: number, y: number): number { return ((x * (2 << z)) + y) * 100 + z } @@ -271,7 +271,7 @@ export class Utils { * @param index * @returns 'zxy' */ - static tile_from_index(index: number) : [number, number, number]{ + static tile_from_index(index: number): [number, number, number] { const z = index % 100; const factor = 2 << z index = Math.floor(index / 100) @@ -356,35 +356,43 @@ export class Utils { return result; } + private static injectedDownloads = {} + + public static injectJsonDownloadForTests(url: string, data) { + Utils.injectedDownloads[url] = data + } + public static downloadJson(url: string): Promise { + + const injected = Utils.injectedDownloads[url] + if (injected !== undefined) { + console.log("Using injected resource for test for URL", url) + return new Promise((resolve, _) => resolve(injected)) + } + if (this.externalDownloadFunction !== undefined) { return this.externalDownloadFunction(url) } - return new Promise( - (resolve, reject) => { - try { - const xhr = new XMLHttpRequest(); - xhr.onload = () => { - if (xhr.status == 200) { - try { - resolve(JSON.parse(xhr.response)) - } catch (e) { - reject("Not a valid json: " + xhr.response) - } - } else { - reject(xhr.statusText) + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.onload = () => { + if (xhr.status == 200) { + try { + console.log("Got a response! Parsing now...") + resolve(JSON.parse(xhr.response)) + } catch (e) { + reject("Not a valid json: " + xhr.response) } - }; - xhr.open('GET', url); - xhr.setRequestHeader("accept", "application/json") - xhr.send(); - } catch (e) { - reject(e) - } + } else { + reject(xhr.statusText) + } + }; + xhr.open('GET', url); + xhr.setRequestHeader("accept", "application/json") + xhr.send(); } ) - } /** @@ -486,12 +494,12 @@ export class Utils { } static sortKeys(o: any) { - const copy = {} + const copy = {} let keys = Object.keys(o) keys = keys.sort() for (const key of keys) { let v = o[key] - if(typeof v === "object"){ + if (typeof v === "object") { v = Utils.sortKeys(v) } copy[key] = v diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index e9d9b4e95..9d09f0c7a 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -34,7 +34,7 @@ "startZoom": 14, "startLon": 3.2228, "maintainer": "MapComplete", - "widenfactor": 0.01, + "widenfactor": 2, "roamingRenderings": [ { "question": { diff --git a/test/RelationSplitHandler.spec.ts b/test/RelationSplitHandler.spec.ts index 8a5174da3..0b5261f13 100644 --- a/test/RelationSplitHandler.spec.ts +++ b/test/RelationSplitHandler.spec.ts @@ -3,6 +3,7 @@ import {InPlaceReplacedmentRTSH} from "../Logic/Osm/Actions/RelationSplitHandler import {OsmObject, OsmRelation} from "../Logic/Osm/OsmObject"; import {Changes} from "../Logic/Osm/Changes"; import {equal} from "assert"; +import {Utils} from "../Utils"; export default class RelationSplitHandlerSpec extends T { @@ -58,6 +59,28 @@ export default class RelationSplitHandlerSpec extends T { } constructor() { + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/node/1124134958/ways", + {"version":"0.6","generator":"CGImap 0.8.5 (2937646 spike-07.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"way","id":97038428,"timestamp":"2019-06-19T12:26:24Z","version":6,"changeset":71399984,"user":"Pieter Vander Vennet","uid":3818858,"nodes":[1124134958,323729212,323729351,2542460408,187073405],"tags":{"highway":"residential","name":"Brugs-Kerkhofstraat","sett:pattern":"arc","surface":"sett"}},{"type":"way","id":97038434,"timestamp":"2019-06-19T12:26:24Z","version":5,"changeset":71399984,"user":"Pieter Vander Vennet","uid":3818858,"nodes":[1124134958,1124135024,187058607],"tags":{"bicycle":"use_sidepath","highway":"residential","name":"Kerkhofblommenstraat","sett:pattern":"arc","surface":"sett"}},{"type":"way","id":97038435,"timestamp":"2017-12-21T21:41:08Z","version":4,"changeset":54826837,"user":"Jakka","uid":2403313,"nodes":[1124134958,2576628889,1124135035,5298371485,5298371495],"tags":{"bicycle":"use_sidepath","highway":"residential","name":"Kerkhofblommenstraat"}},{"type":"way","id":251446313,"timestamp":"2019-01-07T19:22:47Z","version":4,"changeset":66106872,"user":"M!dgard","uid":763799,"nodes":[1124134958,5243143198,4555715455],"tags":{"foot":"yes","highway":"service"}}]} + ) + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/relation/9572808", + {"version":"0.6","generator":"CGImap 0.8.5 (3128319 spike-07.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"relation","id":9572808,"timestamp":"2021-08-12T12:44:06Z","version":11,"changeset":109573204,"user":"A67-A67","uid":553736,"members":[{"type":"way","ref":173662702,"role":""},{"type":"way","ref":467606230,"role":""},{"type":"way","ref":126267167,"role":""},{"type":"way","ref":301897426,"role":""},{"type":"way","ref":687866206,"role":""},{"type":"way","ref":295132739,"role":""},{"type":"way","ref":690497698,"role":""},{"type":"way","ref":627893684,"role":""},{"type":"way","ref":295132741,"role":""},{"type":"way","ref":301903120,"role":""},{"type":"way","ref":672541156,"role":""},{"type":"way","ref":126264330,"role":""},{"type":"way","ref":280440853,"role":""},{"type":"way","ref":838499667,"role":""},{"type":"way","ref":838499663,"role":""},{"type":"way","ref":690497623,"role":""},{"type":"way","ref":301902946,"role":""},{"type":"way","ref":280460715,"role":""},{"type":"way","ref":972534369,"role":""},{"type":"way","ref":695680702,"role":""},{"type":"way","ref":690497860,"role":""},{"type":"way","ref":295410363,"role":""},{"type":"way","ref":823864063,"role":""},{"type":"way","ref":663172088,"role":""},{"type":"way","ref":659950322,"role":""},{"type":"way","ref":659950323,"role":""},{"type":"way","ref":230180094,"role":""},{"type":"way","ref":690497912,"role":""},{"type":"way","ref":39588765,"role":""}],"tags":{"distance":"13 km","name":"Abdijenroute","network":"lcn","old_name":"Spoorlijn 58","operator":"Toerisme West-Vlaanderen","railway":"abandoned","route":"bicycle","type":"route","wikipedia":"nl:Spoorlijn 58"}}]} + ) + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/687866206/full", + {"version":"0.6","generator":"CGImap 0.8.5 (2601512 spike-07.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":5273988959,"lat":51.1811406,"lon":3.2427712,"timestamp":"2021-07-29T21:14:53Z","version":6,"changeset":108847202,"user":"kaart_fietser","uid":11022240,"tags":{"network:type":"node_network","rwn_ref":"32"}},{"type":"node","id":6448669326,"lat":51.1811346,"lon":3.242891,"timestamp":"2019-05-04T22:44:12Z","version":1,"changeset":69891295,"user":"Pieter Vander Vennet","uid":3818858,"tags":{"barrier":"bollard"}},{"type":"way","id":687866206,"timestamp":"2019-05-06T20:52:20Z","version":2,"changeset":69951497,"user":"noelbov","uid":8054928,"nodes":[6448669326,5273988959],"tags":{"highway":"cycleway","name":"Abdijenroute","railway":"abandoned","surface":"asphalt"}}]} + ) + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/690497698/full" , + {"version":"0.6","generator":"CGImap 0.8.5 (3023311 spike-07.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":170497152,"lat":51.1832353,"lon":3.2498759,"timestamp":"2018-04-24T00:29:37Z","version":7,"changeset":58357376,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":2988218625,"lat":51.1835053,"lon":3.2503067,"timestamp":"2018-09-24T21:48:46Z","version":2,"changeset":62895918,"user":"A67-A67","uid":553736},{"type":"node","id":5273988967,"lat":51.182659,"lon":3.249004,"timestamp":"2017-12-09T18:40:21Z","version":1,"changeset":54493533,"user":"CacherB","uid":1999108},{"type":"way","id":690497698,"timestamp":"2021-07-29T21:14:53Z","version":3,"changeset":108847202,"user":"kaart_fietser","uid":11022240,"nodes":[2988218625,170497152,5273988967],"tags":{"highway":"cycleway","lit":"no","name":"Abdijenroute","oneway":"no","railway":"abandoned","surface":"compacted"}}]} + ) + + super("relationsplithandler", [ ["split 295132739", () => RelationSplitHandlerSpec.split().then(_ => console.log("OK"))] diff --git a/test/SplitAction.spec.ts b/test/SplitAction.spec.ts new file mode 100644 index 000000000..6e4ed77a9 --- /dev/null +++ b/test/SplitAction.spec.ts @@ -0,0 +1,274 @@ +import T from "./TestHelper"; +import {Changes} from "../Logic/Osm/Changes"; +import SplitAction from "../Logic/Osm/Actions/SplitAction"; +import {equal} from "assert"; +import {Utils} from "../Utils"; + +export default class SplitActionSpec extends T { + + + private static async split(): Promise { + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/295132739/full", + { + "version": "0.6", + "generator": "CGImap 0.8.5 (3138407 spike-07.openstreetmap.org)", + "copyright": "OpenStreetMap and contributors", + "attribution": "http://www.openstreetmap.org/copyright", + "license": "http://opendatacommons.org/licenses/odbl/1-0/", + "elements": [{ + "type": "node", + "id": 170497153, + "lat": 51.1825167, + "lon": 3.2487885, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 170497155, + "lat": 51.1817632, + "lon": 3.2472706, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 170497157, + "lat": 51.1815203, + "lon": 3.2465569, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 170497158, + "lat": 51.1812261, + "lon": 3.2454261, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 170497160, + "lat": 51.1810957, + "lon": 3.2443030, + "timestamp": "2011-11-18T16:33:43Z", + "version": 5, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 1507524573, + "lat": 51.1810778, + "lon": 3.2437148, + "timestamp": "2011-11-18T16:33:36Z", + "version": 1, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 1507524582, + "lat": 51.1821130, + "lon": 3.2481284, + "timestamp": "2011-11-18T16:33:37Z", + "version": 1, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 1507524610, + "lat": 51.1811645, + "lon": 3.2450828, + "timestamp": "2011-11-18T16:33:38Z", + "version": 1, + "changeset": 9865255, + "user": "TripleBee", + "uid": 497177 + }, { + "type": "node", + "id": 1575932830, + "lat": 51.1811153, + "lon": 3.2431503, + "timestamp": "2019-05-04T22:44:13Z", + "version": 2, + "changeset": 69891295, + "user": "Pieter Vander Vennet", + "uid": 3818858 + }, { + "type": "node", + "id": 3208166178, + "lat": 51.1810837, + "lon": 3.2439090, + "timestamp": "2014-11-27T20:23:10Z", + "version": 1, + "changeset": 27076816, + "user": "JanFi", + "uid": 672253 + }, { + "type": "node", + "id": 3208166179, + "lat": 51.1812062, + "lon": 3.2453151, + "timestamp": "2014-11-27T20:23:10Z", + "version": 1, + "changeset": 27076816, + "user": "JanFi", + "uid": 672253 + }, { + "type": "node", + "id": 4524321710, + "lat": 51.1820656, + "lon": 3.2480253, + "timestamp": "2017-12-09T18:56:37Z", + "version": 2, + "changeset": 54493928, + "user": "CacherB", + "uid": 1999108 + }, { + "type": "node", + "id": 5273988967, + "lat": 51.1826590, + "lon": 3.2490040, + "timestamp": "2017-12-09T18:40:21Z", + "version": 1, + "changeset": 54493533, + "user": "CacherB", + "uid": 1999108 + }, { + "type": "node", + "id": 6448669326, + "lat": 51.1811346, + "lon": 3.2428910, + "timestamp": "2019-05-04T22:44:12Z", + "version": 1, + "changeset": 69891295, + "user": "Pieter Vander Vennet", + "uid": 3818858, + "tags": {"barrier": "bollard"} + }, { + "type": "way", + "id": 295132739, + "timestamp": "2021-07-29T21:14:53Z", + "version": 17, + "changeset": 108847202, + "user": "kaart_fietser", + "uid": 11022240, + "nodes": [5273988967, 170497153, 1507524582, 4524321710, 170497155, 170497157, 170497158, 3208166179, 1507524610, 170497160, 3208166178, 1507524573, 1575932830, 6448669326], + "tags": { + "highway": "cycleway", + "name": "Abdijenroute", + "railway": "abandoned", + "surface": "compacted" + } + }] + }) + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/295132739/relations", + // Mimick that there are no relations relation is missing + { + "version": "0.6", + "generator": "CGImap 0.8.5 (2935793 spike-07.openstreetmap.org)", + "copyright": "OpenStreetMap and contributors", + "attribution": "http://www.openstreetmap.org/copyright", + "license": "http://opendatacommons.org/licenses/odbl/1-0/", + "elements": [] + } + ) + + // Lets split road https://www.openstreetmap.org/way/295132739 + const id = "way/295132739" + const splitPoint: [number, number] = [3.246733546257019, 51.181710380278176] + const splitter = new SplitAction(id, [splitPoint]) + const changeDescription = await splitter.CreateChangeDescriptions(new Changes()) + + equal(changeDescription[0].type, "node") + equal(changeDescription[0].id, -1) + equal(changeDescription[0].changes["lat"], 51.181710380278176) + equal(changeDescription[0].changes["lon"], 3.246733546257019) + + equal(changeDescription[1].type, "way") + equal(changeDescription[1].id, -2) + equal(changeDescription[1].changes["coordinates"].length, 6) + equal(changeDescription[1].changes["coordinates"][5][0], splitPoint[0]) + equal(changeDescription[1].changes["coordinates"][5][1], splitPoint[1]) + + equal(changeDescription[2].type, "way") + equal(changeDescription[2].id, 295132739) + equal(changeDescription[2].changes["coordinates"].length, 10) + equal(changeDescription[2].changes["coordinates"][0][0], splitPoint[0]) + equal(changeDescription[2].changes["coordinates"][0][1], splitPoint[1]) + } + + + private static async SplitHoutkaai() : Promise{ + + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/61435323/full" , + {"version":"0.6","generator":"CGImap 0.8.5 (53092 spike-08.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":766990983,"lat":51.2170219,"lon":3.2022337,"timestamp":"2021-04-26T15:48:22Z","version":6,"changeset":103647857,"user":"M!dgard","uid":763799},{"type":"node","id":766990985,"lat":51.2169574,"lon":3.2017548,"timestamp":"2016-07-05T22:41:12Z","version":6,"changeset":40511250,"user":"M!dgard","uid":763799},{"type":"node","id":8669018379,"lat":51.2169592,"lon":3.2017683,"timestamp":"2021-04-26T15:48:22Z","version":1,"changeset":103647857,"user":"M!dgard","uid":763799},{"type":"way","id":61435323,"timestamp":"2021-08-21T12:24:13Z","version":7,"changeset":110026637,"user":"Thibault Rommel","uid":5846458,"nodes":[766990983,8669018379,766990985],"tags":{"bicycle":"yes","bridge":"yes","cycleway":"shared_lane","highway":"unclassified","layer":"1","maxspeed":"50","name":"Houtkaai","surface":"asphalt","zone:traffic":"BE-VLG:urban"}}]} + ) + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/61435323/relations" , + {"version":"0.6","generator":"CGImap 0.8.5 (3622541 spike-06.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"relation","id":1723870,"timestamp":"2021-09-18T06:29:31Z","version":183,"changeset":111362343,"user":"emvee","uid":5211,"members":[{"type":"way","ref":261428947,"role":""},{"type":"way","ref":162774622,"role":""},{"type":"way","ref":317060244,"role":""},{"type":"way","ref":81155378,"role":""},{"type":"way","ref":99749583,"role":""},{"type":"way","ref":131332113,"role":""},{"type":"way","ref":949518831,"role":""},{"type":"way","ref":99749584,"role":""},{"type":"way","ref":129133519,"role":""},{"type":"way","ref":73241312,"role":""},{"type":"way","ref":785514256,"role":""},{"type":"way","ref":58509643,"role":""},{"type":"way","ref":73241332,"role":""},{"type":"way","ref":58509653,"role":""},{"type":"way","ref":100044097,"role":""},{"type":"way","ref":946999067,"role":""},{"type":"way","ref":73241327,"role":""},{"type":"way","ref":58509617,"role":""},{"type":"way","ref":58509627,"role":""},{"type":"way","ref":69990655,"role":""},{"type":"way","ref":73241311,"role":""},{"type":"way","ref":123142336,"role":""},{"type":"way","ref":249671053,"role":""},{"type":"way","ref":73241324,"role":""},{"type":"way","ref":66706953,"role":""},{"type":"way","ref":112679357,"role":""},{"type":"way","ref":112679358,"role":""},{"type":"way","ref":53105113,"role":""},{"type":"way","ref":66706952,"role":""},{"type":"way","ref":64083661,"role":""},{"type":"way","ref":53105162,"role":""},{"type":"way","ref":249671070,"role":""},{"type":"way","ref":249671064,"role":""},{"type":"way","ref":101498587,"role":""},{"type":"way","ref":69001236,"role":""},{"type":"way","ref":101498585,"role":""},{"type":"way","ref":70909444,"role":""},{"type":"way","ref":73241314,"role":""},{"type":"way","ref":69001235,"role":""},{"type":"way","ref":113150200,"role":""},{"type":"way","ref":137305843,"role":""},{"type":"way","ref":936827687,"role":""},{"type":"way","ref":936827688,"role":""},{"type":"way","ref":112952373,"role":""},{"type":"way","ref":930798379,"role":""},{"type":"way","ref":930798378,"role":""},{"type":"way","ref":112951439,"role":""},{"type":"way","ref":445541591,"role":""},{"type":"way","ref":103843896,"role":""},{"type":"way","ref":23734118,"role":""},{"type":"way","ref":103840557,"role":""},{"type":"way","ref":433852210,"role":""},{"type":"way","ref":313604670,"role":""},{"type":"way","ref":103839402,"role":""},{"type":"way","ref":23736061,"role":""},{"type":"way","ref":73241328,"role":""},{"type":"way","ref":295392689,"role":""},{"type":"way","ref":297168171,"role":""},{"type":"way","ref":297168170,"role":""},{"type":"way","ref":433852205,"role":""},{"type":"way","ref":295392695,"role":""},{"type":"way","ref":663268954,"role":""},{"type":"way","ref":663267598,"role":""},{"type":"way","ref":292478843,"role":""},{"type":"way","ref":981853853,"role":""},{"type":"way","ref":663270140,"role":""},{"type":"way","ref":981853854,"role":""},{"type":"way","ref":295392703,"role":""},{"type":"way","ref":663304916,"role":""},{"type":"way","ref":297169116,"role":""},{"type":"way","ref":295400810,"role":""},{"type":"way","ref":981853855,"role":""},{"type":"way","ref":663304806,"role":""},{"type":"way","ref":516452870,"role":""},{"type":"way","ref":66459239,"role":""},{"type":"way","ref":791430504,"role":""},{"type":"way","ref":178926037,"role":""},{"type":"way","ref":864799431,"role":""},{"type":"way","ref":178926107,"role":""},{"type":"way","ref":663320459,"role":""},{"type":"way","ref":62033993,"role":""},{"type":"way","ref":62283023,"role":""},{"type":"way","ref":62283057,"role":""},{"type":"way","ref":62283032,"role":""},{"type":"way","ref":490551085,"role":""},{"type":"way","ref":435318979,"role":""},{"type":"way","ref":371750677,"role":""},{"type":"way","ref":371750670,"role":""},{"type":"way","ref":371750673,"role":""},{"type":"way","ref":371750675,"role":""},{"type":"way","ref":459885691,"role":""},{"type":"way","ref":371750669,"role":""},{"type":"way","ref":371750668,"role":""},{"type":"way","ref":371750667,"role":""},{"type":"way","ref":428848639,"role":""},{"type":"way","ref":371750666,"role":""},{"type":"way","ref":371750665,"role":""},{"type":"way","ref":825496473,"role":""},{"type":"way","ref":371750664,"role":""},{"type":"way","ref":371750662,"role":""},{"type":"way","ref":371750663,"role":""},{"type":"way","ref":371750660,"role":""},{"type":"way","ref":371750658,"role":""},{"type":"way","ref":40507374,"role":""},{"type":"way","ref":165878356,"role":""},{"type":"way","ref":165878355,"role":""},{"type":"way","ref":8494219,"role":""},{"type":"way","ref":5023947,"role":""},{"type":"way","ref":5023939,"role":""},{"type":"way","ref":26718843,"role":""},{"type":"way","ref":79437029,"role":""},{"type":"way","ref":87522151,"role":""},{"type":"way","ref":26718848,"role":""},{"type":"way","ref":233169831,"role":""},{"type":"way","ref":85934460,"role":""},{"type":"way","ref":145892210,"role":""},{"type":"way","ref":79434764,"role":""},{"type":"way","ref":127079185,"role":""},{"type":"way","ref":67794715,"role":""},{"type":"way","ref":85934250,"role":""},{"type":"way","ref":421566302,"role":""},{"type":"way","ref":123445537,"role":""},{"type":"way","ref":308077683,"role":""},{"type":"way","ref":308077684,"role":""},{"type":"way","ref":972955357,"role":""},{"type":"way","ref":308077682,"role":""},{"type":"way","ref":659880052,"role":""},{"type":"way","ref":308077681,"role":""},{"type":"way","ref":66364130,"role":""},{"type":"way","ref":51086959,"role":""},{"type":"way","ref":51086961,"role":""},{"type":"way","ref":102154586,"role":""},{"type":"way","ref":102154589,"role":""},{"type":"way","ref":703008376,"role":""},{"type":"way","ref":703008375,"role":""},{"type":"way","ref":54435150,"role":""},{"type":"way","ref":115913100,"role":""},{"type":"way","ref":79433785,"role":""},{"type":"way","ref":51204355,"role":""},{"type":"way","ref":422395066,"role":""},{"type":"way","ref":116628138,"role":""},{"type":"way","ref":690189323,"role":""},{"type":"way","ref":132068368,"role":""},{"type":"way","ref":690220771,"role":""},{"type":"way","ref":690220772,"role":""},{"type":"way","ref":690226744,"role":""},{"type":"way","ref":690226745,"role":""},{"type":"way","ref":60253953,"role":""},{"type":"way","ref":690195774,"role":""},{"type":"way","ref":688104939,"role":""},{"type":"way","ref":422395064,"role":"forward"},{"type":"way","ref":422309497,"role":"forward"},{"type":"way","ref":25677204,"role":"forward"},{"type":"way","ref":51570941,"role":""},{"type":"way","ref":807329786,"role":""},{"type":"way","ref":165500495,"role":""},{"type":"way","ref":689494106,"role":""},{"type":"way","ref":131476435,"role":""},{"type":"way","ref":689493508,"role":""},{"type":"way","ref":12126873,"role":""},{"type":"way","ref":32789519,"role":""},{"type":"way","ref":27288122,"role":""},{"type":"way","ref":116717060,"role":""},{"type":"way","ref":176380249,"role":""},{"type":"way","ref":116717052,"role":""},{"type":"way","ref":176380250,"role":""},{"type":"way","ref":421998791,"role":""},{"type":"way","ref":34562745,"role":""},{"type":"way","ref":130473931,"role":""},{"type":"way","ref":136487196,"role":""},{"type":"way","ref":23792223,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":509668834,"role":""},{"type":"way","ref":130473917,"role":""},{"type":"way","ref":369929894,"role":""},{"type":"way","ref":805247467,"role":"forward"},{"type":"way","ref":840210016,"role":"forward"},{"type":"way","ref":539026983,"role":"forward"},{"type":"way","ref":539037793,"role":"forward"},{"type":"way","ref":244428576,"role":"forward"},{"type":"way","ref":243333119,"role":"forward"},{"type":"way","ref":243333108,"role":"forward"},{"type":"way","ref":243333106,"role":"forward"},{"type":"way","ref":243333110,"role":"forward"},{"type":"way","ref":230511503,"role":"forward"},{"type":"way","ref":510520445,"role":"forward"},{"type":"way","ref":688103605,"role":"forward"},{"type":"way","ref":668577053,"role":"forward"},{"type":"way","ref":4332489,"role":"forward"},{"type":"way","ref":668577051,"role":"forward"},{"type":"way","ref":185476761,"role":"forward"},{"type":"way","ref":100774483,"role":"forward"},{"type":"way","ref":668672434,"role":"backward"},{"type":"way","ref":488558133,"role":"backward"},{"type":"way","ref":13943237,"role":"forward"},{"type":"way","ref":840241791,"role":"forward"},{"type":"way","ref":805247468,"role":"forward"},{"type":"way","ref":539040946,"role":"forward"},{"type":"way","ref":539026103,"role":"forward"},{"type":"way","ref":539037781,"role":"forward"},{"type":"way","ref":28942112,"role":"forward"},{"type":"way","ref":699841535,"role":"forward"},{"type":"way","ref":635374201,"role":"forward"},{"type":"way","ref":28942118,"role":"forward"},{"type":"way","ref":185476755,"role":"forward"},{"type":"way","ref":78794903,"role":"forward"},{"type":"way","ref":688103599,"role":"forward"},{"type":"way","ref":688103600,"role":"backward"},{"type":"way","ref":32699077,"role":"backward"},{"type":"way","ref":249092420,"role":"backward"},{"type":"way","ref":540048295,"role":""},{"type":"way","ref":13942938,"role":""},{"type":"way","ref":827705395,"role":""},{"type":"way","ref":72492953,"role":""},{"type":"way","ref":61435342,"role":""},{"type":"way","ref":95106180,"role":""},{"type":"way","ref":182691326,"role":""},{"type":"way","ref":180915274,"role":""},{"type":"way","ref":61435340,"role":""},{"type":"way","ref":95506626,"role":""},{"type":"way","ref":183330864,"role":""},{"type":"way","ref":318631002,"role":""},{"type":"way","ref":4332470,"role":""},{"type":"way","ref":318631014,"role":""},{"type":"way","ref":337969633,"role":""},{"type":"way","ref":668566903,"role":""},{"type":"way","ref":668566904,"role":""},{"type":"way","ref":248228679,"role":""},{"type":"way","ref":419296358,"role":""},{"type":"way","ref":601005356,"role":""},{"type":"way","ref":497802656,"role":""},{"type":"way","ref":948484806,"role":""},{"type":"way","ref":756223825,"role":""},{"type":"way","ref":23206884,"role":""},{"type":"way","ref":157436856,"role":""},{"type":"way","ref":829398288,"role":""},{"type":"way","ref":829398289,"role":""},{"type":"way","ref":674490354,"role":""},{"type":"way","ref":131704173,"role":""},{"type":"way","ref":120976014,"role":""},{"type":"way","ref":38864144,"role":""},{"type":"way","ref":38864143,"role":""},{"type":"way","ref":32147475,"role":""},{"type":"way","ref":962256846,"role":""},{"type":"way","ref":32147479,"role":""},{"type":"way","ref":32147481,"role":""},{"type":"way","ref":49486734,"role":""},{"type":"way","ref":829394351,"role":""},{"type":"way","ref":829394349,"role":""},{"type":"way","ref":235193261,"role":""},{"type":"way","ref":130495866,"role":""},{"type":"way","ref":978366962,"role":""},{"type":"way","ref":39588752,"role":""},{"type":"way","ref":436528651,"role":""},{"type":"way","ref":27370335,"role":""},{"type":"way","ref":157558803,"role":""},{"type":"way","ref":39590466,"role":""},{"type":"way","ref":157558804,"role":""},{"type":"way","ref":27370165,"role":""},{"type":"way","ref":970841665,"role":""}],"tags":{"name":"Euroroute R1 - part Belgium","name:de":"Europaradweg R1 - Abschnitt Belgien","name:nl":"Euroroute R1 - deel België","network":"icn","ref":"R1","route":"bicycle","type":"route"}},{"type":"relation","id":1757007,"timestamp":"2020-10-13T01:31:44Z","version":10,"changeset":92380204,"user":"Diabolix","uid":2123963,"members":[{"type":"way","ref":509668834,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":23792223,"role":""}],"tags":{"network":"rcn","network:type":"node_network","ref":"4-36","route":"bicycle","type":"route"}},{"type":"relation","id":5150189,"timestamp":"2021-09-09T20:15:58Z","version":44,"changeset":110993632,"user":"JosV","uid":170722,"members":[{"type":"way","ref":13943237,"role":""},{"type":"way","ref":488558133,"role":""},{"type":"way","ref":369929894,"role":""},{"type":"way","ref":130473917,"role":""},{"type":"way","ref":509668834,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":23792223,"role":""},{"type":"way","ref":136487196,"role":""},{"type":"way","ref":130473931,"role":""},{"type":"way","ref":34562745,"role":""},{"type":"way","ref":421998791,"role":""},{"type":"way","ref":126996864,"role":""},{"type":"way","ref":126996861,"role":""},{"type":"way","ref":170989337,"role":""},{"type":"way","ref":72482534,"role":""},{"type":"way","ref":58913500,"role":""},{"type":"way","ref":72482539,"role":""},{"type":"way","ref":246969243,"role":""},{"type":"way","ref":153150902,"role":""},{"type":"way","ref":116748588,"role":""},{"type":"way","ref":72482544,"role":""},{"type":"way","ref":72482542,"role":""},{"type":"way","ref":337013552,"role":""},{"type":"way","ref":132790401,"role":""},{"type":"way","ref":105166767,"role":""},{"type":"way","ref":720356345,"role":""},{"type":"way","ref":197829999,"role":""},{"type":"way","ref":105166552,"role":""},{"type":"way","ref":61979075,"role":""},{"type":"way","ref":197830184,"role":""},{"type":"way","ref":61979070,"role":""},{"type":"way","ref":948826013,"role":""},{"type":"way","ref":197830182,"role":""},{"type":"way","ref":672535497,"role":""},{"type":"way","ref":672535498,"role":""},{"type":"way","ref":948826015,"role":""},{"type":"way","ref":11378674,"role":""},{"type":"way","ref":672535496,"role":""},{"type":"way","ref":70023921,"role":""},{"type":"way","ref":948826017,"role":""},{"type":"way","ref":197830260,"role":""},{"type":"way","ref":152210843,"role":""},{"type":"way","ref":33748055,"role":""},{"type":"way","ref":344701437,"role":""},{"type":"way","ref":422150672,"role":""},{"type":"way","ref":156228338,"role":""},{"type":"way","ref":422150674,"role":""},{"type":"way","ref":223674432,"role":""},{"type":"way","ref":223674437,"role":""},{"type":"way","ref":156228327,"role":""},{"type":"way","ref":223674372,"role":""},{"type":"way","ref":592937889,"role":""},{"type":"way","ref":592937890,"role":""},{"type":"way","ref":422099666,"role":""},{"type":"way","ref":422100304,"role":""},{"type":"way","ref":948826022,"role":""},{"type":"way","ref":15092930,"role":""},{"type":"way","ref":948826024,"role":""},{"type":"way","ref":105182226,"role":""},{"type":"way","ref":133606215,"role":""},{"type":"way","ref":533395656,"role":""},{"type":"way","ref":187115987,"role":""},{"type":"way","ref":105182230,"role":""},{"type":"way","ref":105182232,"role":""},{"type":"way","ref":196011634,"role":""},{"type":"way","ref":153273480,"role":""},{"type":"way","ref":153273481,"role":""},{"type":"way","ref":881767783,"role":""},{"type":"way","ref":153273479,"role":""},{"type":"way","ref":13462242,"role":""},{"type":"way","ref":498093425,"role":""},{"type":"way","ref":70009137,"role":""},{"type":"way","ref":12086805,"role":""},{"type":"way","ref":52523332,"role":""},{"type":"way","ref":70009138,"role":""},{"type":"way","ref":592937884,"role":""},{"type":"way","ref":15071942,"role":""},{"type":"way","ref":180798233,"role":""},{"type":"way","ref":70010670,"role":""},{"type":"way","ref":15802818,"role":""},{"type":"way","ref":15802809,"role":""},{"type":"way","ref":70011254,"role":""},{"type":"way","ref":671368756,"role":""},{"type":"way","ref":840241791,"role":""},{"type":"way","ref":369929367,"role":""},{"type":"way","ref":539038988,"role":""},{"type":"way","ref":80130513,"role":""},{"type":"way","ref":540214122,"role":""},{"type":"way","ref":765795083,"role":""},{"type":"way","ref":13943005,"role":""},{"type":"way","ref":72492950,"role":""},{"type":"way","ref":183330864,"role":""},{"type":"way","ref":318631002,"role":""},{"type":"way","ref":4332470,"role":""},{"type":"way","ref":318631014,"role":""},{"type":"way","ref":337969633,"role":""},{"type":"way","ref":668566903,"role":""},{"type":"way","ref":668566904,"role":""},{"type":"way","ref":248228679,"role":""},{"type":"way","ref":419296358,"role":""},{"type":"way","ref":601005356,"role":""},{"type":"way","ref":497802656,"role":""},{"type":"way","ref":948484806,"role":""},{"type":"way","ref":100323579,"role":""},{"type":"way","ref":100708215,"role":""},{"type":"way","ref":124559834,"role":""},{"type":"way","ref":124559835,"role":""},{"type":"way","ref":239484694,"role":""},{"type":"way","ref":972646812,"role":""},{"type":"way","ref":124559832,"role":""},{"type":"way","ref":361686157,"role":""},{"type":"way","ref":361686155,"role":""},{"type":"way","ref":239484693,"role":""},{"type":"way","ref":19861731,"role":""},{"type":"way","ref":967906429,"role":""},{"type":"way","ref":126402539,"role":""},{"type":"way","ref":94427058,"role":""},{"type":"way","ref":126402541,"role":""},{"type":"way","ref":313693839,"role":""},{"type":"way","ref":313693838,"role":""},{"type":"way","ref":970740536,"role":""},{"type":"way","ref":361719175,"role":""},{"type":"way","ref":663186012,"role":""},{"type":"way","ref":744625794,"role":""},{"type":"way","ref":94569877,"role":""},{"type":"way","ref":188973964,"role":""},{"type":"way","ref":948484822,"role":""},{"type":"way","ref":28857260,"role":""},{"type":"way","ref":948484821,"role":""},{"type":"way","ref":219185860,"role":""},{"type":"way","ref":948484818,"role":""},{"type":"way","ref":219185861,"role":""},{"type":"way","ref":229885580,"role":""},{"type":"way","ref":28857247,"role":""},{"type":"way","ref":128813937,"role":""},{"type":"way","ref":32148201,"role":""},{"type":"way","ref":829398290,"role":""},{"type":"way","ref":829398288,"role":""},{"type":"way","ref":157436856,"role":""},{"type":"way","ref":23206887,"role":""},{"type":"way","ref":657081380,"role":""},{"type":"way","ref":948484817,"role":""},{"type":"way","ref":657081379,"role":""},{"type":"way","ref":657083379,"role":""},{"type":"way","ref":657083378,"role":""},{"type":"way","ref":72492956,"role":""},{"type":"way","ref":183763716,"role":""},{"type":"way","ref":497802654,"role":""},{"type":"way","ref":497802655,"role":""},{"type":"way","ref":348402994,"role":""},{"type":"way","ref":497802653,"role":""},{"type":"way","ref":948484813,"role":""},{"type":"way","ref":272353449,"role":"forward"},{"type":"way","ref":497802652,"role":"forward"},{"type":"way","ref":948484811,"role":""},{"type":"way","ref":948484810,"role":""},{"type":"way","ref":136564089,"role":""},{"type":"way","ref":970740538,"role":""},{"type":"way","ref":970740539,"role":""},{"type":"way","ref":433455263,"role":""},{"type":"way","ref":23206893,"role":""},{"type":"way","ref":95506626,"role":""},{"type":"way","ref":61435340,"role":""},{"type":"way","ref":180915274,"role":""},{"type":"way","ref":182691326,"role":""},{"type":"way","ref":95106180,"role":""},{"type":"way","ref":61435342,"role":""},{"type":"way","ref":72492953,"role":""},{"type":"way","ref":827705395,"role":""},{"type":"way","ref":13942938,"role":""},{"type":"way","ref":540048295,"role":""},{"type":"way","ref":249092420,"role":""},{"type":"way","ref":32699077,"role":""},{"type":"way","ref":688103600,"role":""},{"type":"way","ref":654338684,"role":"forward"},{"type":"way","ref":11018710,"role":"forward"},{"type":"way","ref":510825612,"role":"forward"},{"type":"way","ref":70011248,"role":"forward"},{"type":"way","ref":654338685,"role":"forward"},{"type":"way","ref":14626290,"role":""},{"type":"way","ref":70011250,"role":""},{"type":"way","ref":12295471,"role":""},{"type":"way","ref":397097504,"role":""},{"type":"way","ref":12295484,"role":""},{"type":"way","ref":41990436,"role":""},{"type":"way","ref":70011252,"role":""},{"type":"way","ref":61503690,"role":""},{"type":"way","ref":182978284,"role":""},{"type":"way","ref":790820260,"role":"forward"},{"type":"way","ref":592937894,"role":"forward"},{"type":"way","ref":926028042,"role":"forward"},{"type":"way","ref":592937902,"role":"forward"},{"type":"way","ref":592937901,"role":"forward"},{"type":"way","ref":182978255,"role":"forward"},{"type":"way","ref":592937903,"role":"forward"},{"type":"way","ref":12123659,"role":"forward"},{"type":"way","ref":666877213,"role":"forward"},{"type":"way","ref":790820259,"role":"forward"},{"type":"way","ref":510825618,"role":""},{"type":"way","ref":13496412,"role":""},{"type":"way","ref":654338689,"role":""},{"type":"way","ref":740935312,"role":""},{"type":"way","ref":52288671,"role":""},{"type":"way","ref":52288667,"role":""},{"type":"way","ref":12123458,"role":""},{"type":"way","ref":508681905,"role":""},{"type":"way","ref":15071314,"role":""},{"type":"way","ref":61503700,"role":""},{"type":"way","ref":41989874,"role":""},{"type":"way","ref":328002077,"role":""},{"type":"way","ref":396377151,"role":""},{"type":"way","ref":396377150,"role":""},{"type":"way","ref":396377125,"role":""},{"type":"way","ref":328985990,"role":""},{"type":"way","ref":328985992,"role":""},{"type":"way","ref":328985993,"role":""},{"type":"way","ref":328985991,"role":""},{"type":"way","ref":632506298,"role":""},{"type":"way","ref":101191104,"role":""},{"type":"way","ref":499129522,"role":""},{"type":"way","ref":15071174,"role":""},{"type":"way","ref":297023609,"role":""},{"type":"way","ref":297023610,"role":""},{"type":"way","ref":297023608,"role":""},{"type":"way","ref":112695115,"role":""},{"type":"way","ref":584024902,"role":""},{"type":"way","ref":243543197,"role":""},{"type":"way","ref":101191119,"role":"forward"},{"type":"way","ref":173530022,"role":"forward"},{"type":"way","ref":265137637,"role":"forward"},{"type":"way","ref":160627684,"role":"forward"},{"type":"way","ref":657163351,"role":"forward"},{"type":"way","ref":160627682,"role":"forward"},{"type":"way","ref":160632906,"role":"forward"},{"type":"way","ref":176870850,"role":"forward"},{"type":"way","ref":173662701,"role":"forward"},{"type":"way","ref":173662702,"role":""},{"type":"way","ref":467606230,"role":""},{"type":"way","ref":126267167,"role":""},{"type":"way","ref":301897426,"role":""},{"type":"way","ref":687866206,"role":""},{"type":"way","ref":295132739,"role":""},{"type":"way","ref":690497698,"role":""},{"type":"way","ref":627893684,"role":""},{"type":"way","ref":295132741,"role":""},{"type":"way","ref":301903120,"role":""},{"type":"way","ref":672541156,"role":""},{"type":"way","ref":126264330,"role":""},{"type":"way","ref":280440853,"role":""},{"type":"way","ref":838499667,"role":""},{"type":"way","ref":838499663,"role":""},{"type":"way","ref":690497623,"role":""},{"type":"way","ref":301902946,"role":""},{"type":"way","ref":280460715,"role":""},{"type":"way","ref":972534369,"role":""},{"type":"way","ref":588764361,"role":""},{"type":"way","ref":981365419,"role":""},{"type":"way","ref":188979882,"role":""},{"type":"way","ref":578030518,"role":""},{"type":"way","ref":124559857,"role":""},{"type":"way","ref":284568605,"role":""},{"type":"way","ref":126405025,"role":""},{"type":"way","ref":188978777,"role":""},{"type":"way","ref":272353445,"role":"forward"},{"type":"way","ref":221443952,"role":"forward"},{"type":"way","ref":172708119,"role":"forward"},{"type":"way","ref":173061662,"role":"forward"},{"type":"way","ref":441663456,"role":"forward"},{"type":"way","ref":160627680,"role":"forward"},{"type":"way","ref":176870852,"role":"forward"},{"type":"way","ref":39588762,"role":"forward"},{"type":"way","ref":172709466,"role":"forward"},{"type":"way","ref":598459103,"role":"forward"},{"type":"way","ref":688054392,"role":"forward"},{"type":"way","ref":155986859,"role":"forward"}],"tags":{"name":"Groene Gordel Brugge","network":"lcn","ref":"GGB","route":"bicycle","type":"route"}},{"type":"relation","id":8369765,"timestamp":"2021-08-23T14:22:45Z","version":19,"changeset":110120188,"user":"Pieter Vander Vennet","uid":3818858,"members":[{"type":"way","ref":539038988,"role":""},{"type":"way","ref":369929367,"role":""},{"type":"way","ref":840241791,"role":""},{"type":"way","ref":488558133,"role":""},{"type":"way","ref":369929894,"role":""},{"type":"way","ref":130473917,"role":""},{"type":"way","ref":509668834,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":23792223,"role":""},{"type":"way","ref":136487196,"role":""},{"type":"way","ref":130473931,"role":""},{"type":"way","ref":34562745,"role":""},{"type":"way","ref":421998791,"role":""},{"type":"way","ref":176380250,"role":""},{"type":"way","ref":116717052,"role":""},{"type":"way","ref":176380249,"role":""},{"type":"way","ref":116717060,"role":""},{"type":"way","ref":27288122,"role":""},{"type":"way","ref":32789519,"role":""},{"type":"way","ref":12126873,"role":""},{"type":"way","ref":689493508,"role":""},{"type":"way","ref":131476435,"role":""},{"type":"way","ref":689494106,"role":""},{"type":"way","ref":165500495,"role":""},{"type":"way","ref":807329786,"role":""},{"type":"way","ref":51570941,"role":""},{"type":"way","ref":422309497,"role":""},{"type":"way","ref":240869981,"role":""},{"type":"way","ref":240869873,"role":""},{"type":"way","ref":240869980,"role":""},{"type":"way","ref":165503767,"role":""},{"type":"way","ref":165503764,"role":""},{"type":"way","ref":421566315,"role":""},{"type":"way","ref":165503768,"role":""},{"type":"way","ref":245236630,"role":""},{"type":"way","ref":658500046,"role":"forward"},{"type":"way","ref":646903393,"role":"forward"},{"type":"way","ref":245236632,"role":"forward"},{"type":"way","ref":245236633,"role":"forward"},{"type":"way","ref":90485426,"role":""},{"type":"way","ref":596073878,"role":""},{"type":"way","ref":10898401,"role":"backward"},{"type":"way","ref":658500044,"role":"forward"},{"type":"way","ref":474253371,"role":"forward"},{"type":"way","ref":474253369,"role":"forward"},{"type":"way","ref":474253376,"role":"forward"},{"type":"way","ref":165845350,"role":"backward"},{"type":"way","ref":130697218,"role":""},{"type":"way","ref":61565721,"role":""},{"type":"way","ref":497202210,"role":""},{"type":"way","ref":130697226,"role":""},{"type":"way","ref":227617858,"role":""},{"type":"way","ref":227617857,"role":""},{"type":"way","ref":681804956,"role":""},{"type":"way","ref":165881675,"role":""},{"type":"way","ref":806146504,"role":""},{"type":"way","ref":806146505,"role":""},{"type":"way","ref":659762284,"role":""}],"tags":{"alt_name":"Fietssnelweg F30 Brugge - Oostende","bicycle:type":"utility","cycle_highway":"yes","cycle_network":"BE-VLG:cycle_highway","name":"F30 Fietssnelweg Brugge - Oostende","network":"ncn","operator":"Provincie West-Vlaanderen","ref":"F30","route":"bicycle","state":"proposed","type":"route","website":"https://fietssnelwegen.be/f30","wikidata":"Q107485732"}},{"type":"relation","id":13060733,"timestamp":"2021-09-19T18:08:57Z","version":5,"changeset":111419581,"user":"L'imaginaire","uid":654234,"members":[{"type":"way","ref":23792223,"role":""},{"type":"way","ref":23775021,"role":""},{"type":"way","ref":560506339,"role":""},{"type":"way","ref":337945886,"role":""},{"type":"way","ref":61435332,"role":""},{"type":"way","ref":61435323,"role":""},{"type":"way","ref":509668834,"role":""},{"type":"way","ref":839596136,"role":""},{"type":"way","ref":840488274,"role":""},{"type":"way","ref":839596137,"role":""},{"type":"way","ref":146172188,"role":""},{"type":"way","ref":749212030,"role":""},{"type":"way","ref":799479035,"role":""},{"type":"way","ref":130473928,"role":""},{"type":"way","ref":61414103,"role":""},{"type":"way","ref":539672618,"role":""},{"type":"way","ref":799479034,"role":""},{"type":"way","ref":539672617,"role":""},{"type":"way","ref":539672616,"role":""},{"type":"way","ref":539671786,"role":""},{"type":"way","ref":172317285,"role":""},{"type":"way","ref":35328157,"role":""},{"type":"way","ref":249119335,"role":""},{"type":"way","ref":584214875,"role":""},{"type":"way","ref":584217798,"role":""},{"type":"way","ref":676801473,"role":""},{"type":"way","ref":456588356,"role":""},{"type":"way","ref":456589109,"role":""},{"type":"way","ref":456588496,"role":""},{"type":"way","ref":487199906,"role":""},{"type":"way","ref":299450868,"role":""},{"type":"way","ref":165548222,"role":""},{"type":"way","ref":4329135,"role":""},{"type":"way","ref":4329771,"role":""},{"type":"way","ref":155149803,"role":""},{"type":"way","ref":305625031,"role":""},{"type":"way","ref":100842624,"role":""},{"type":"way","ref":18102445,"role":""},{"type":"way","ref":541116658,"role":""},{"type":"way","ref":591094005,"role":""},{"type":"way","ref":591094004,"role":""},{"type":"way","ref":184684947,"role":""},{"type":"way","ref":34945088,"role":""},{"type":"way","ref":235195315,"role":""},{"type":"way","ref":497849660,"role":""}],"tags":{"colour":"#e40613","cycle_network":"BE-VLG:icoonroutes","description":"segment 2 van de Kunststedenroute","fixme":"incomplete","from":"Oostende","name":"Kunststedenroute - 02 - Oostende - Brugge","network":"ncn","operator":"Toerisme Vlaanderen","ref":"Kunst","route":"bicycle","to":"Brugge","type":"route","website":"https://www.vlaanderenmetdefiets.be/routes/kunststeden.html","wikidata":"Q106529274","wikipedia":"nl:LF Kunststedenroute"}}]} + ) + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/61435332/full" , + {"version":"0.6","generator":"CGImap 0.8.5 (3819319 spike-06.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":766990985,"lat":51.2169574,"lon":3.2017548,"timestamp":"2016-07-05T22:41:12Z","version":6,"changeset":40511250,"user":"M!dgard","uid":763799},{"type":"node","id":3450208876,"lat":51.2169482,"lon":3.2016802,"timestamp":"2016-07-05T22:41:11Z","version":2,"changeset":40511250,"user":"M!dgard","uid":763799},{"type":"way","id":61435332,"timestamp":"2021-08-21T12:24:13Z","version":8,"changeset":110026637,"user":"Thibault Rommel","uid":5846458,"nodes":[766990985,3450208876],"tags":{"bicycle":"yes","cycleway":"shared_lane","highway":"unclassified","maxspeed":"50","name":"Houtkaai","surface":"asphalt","zone:traffic":"BE-VLG:urban"}}]} + ) + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/509668834/full" , + {"version":"0.6","generator":"CGImap 0.8.5 (3735280 spike-06.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","elements":[{"type":"node","id":131917824,"lat":51.2170327,"lon":3.2023577,"timestamp":"2019-09-16T09:48:28Z","version":17,"changeset":74521581,"user":"Peter Elderson","uid":7103674,"tags":{"network:type":"node_network","rcn_ref":"4","rcn_region":"Brugse Ommeland"}},{"type":"node","id":766990983,"lat":51.2170219,"lon":3.2022337,"timestamp":"2021-04-26T15:48:22Z","version":6,"changeset":103647857,"user":"M!dgard","uid":763799},{"type":"way","id":509668834,"timestamp":"2021-08-21T12:24:13Z","version":5,"changeset":110026637,"user":"Thibault Rommel","uid":5846458,"nodes":[131917824,766990983],"tags":{"bicycle":"yes","cycleway":"shared_lane","highway":"residential","lit":"yes","maxspeed":"30","name":"Houtkaai","sidewalk":"both","surface":"paving_stones","zone:maxspeed":"BE:30","zone:traffic":"BE-VLG:urban"}}]} + ) + + + const id = "way/61435323" + const splitPoint: [number, number] = [ 3.2021324336528774, 51.2170001600597] + const splitter = new SplitAction(id, [splitPoint]) + const changeDescription = await splitter.CreateChangeDescriptions(new Changes()) + + // Should be a new node + equal(changeDescription[0].type ,"node") + equal(changeDescription[3].type , "relation") + } + + private static async splitWithPointReuse(): Promise { + // Lets split road near an already existing point https://www.openstreetmap.org/way/295132739 + const id = "way/295132739" + const splitPoint: [number, number] = [3.2451081275939937, 51.18116898253599] + const splitter = new SplitAction(id, [splitPoint]) + const changeDescription = await splitter.CreateChangeDescriptions(new Changes()) + + equal(2, changeDescription.length) + const ch0 = changeDescription[0] + const ch1 = changeDescription[1] + const nodes0: number[] = ch0.changes["nodes"] + const nodes1: number[] = ch1.changes["nodes"] + equal(ch0.type, "way") + equal(ch1.type, "way") + equal(nodes0[nodes0.length - 1], nodes1[0]) + equal(3208166179, nodes1[0]) + } + + constructor() { + super("splitaction", [ + ["split 295132739", + () => SplitActionSpec.split().then(_ => console.log("OK"))], + ["split 295132739 on already existing node", + () => SplitActionSpec.splitWithPointReuse().then(_ => console.log("OK"))], + ["split 61435323 on already existing node", + () => SplitActionSpec.SplitHoutkaai().then(_ => console.log("OK"))] + ]); + } +} \ No newline at end of file diff --git a/test/TestAll.ts b/test/TestAll.ts index bd0ec5657..1a25a0afc 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -8,7 +8,8 @@ import OsmObjectSpec from "./OsmObject.spec"; import ScriptUtils from "../scripts/ScriptUtils"; import UnitsSpec from "./Units.spec"; import RelationSplitHandlerSpec from "./RelationSplitHandler.spec"; - +import SplitActionSpec from "./SplitAction.spec"; +import {Utils} from "../Utils"; ScriptUtils.fixUtils() @@ -21,9 +22,19 @@ const allTests = [ new ThemeSpec(), new UtilsSpec(), new UnitsSpec(), - new RelationSplitHandlerSpec() + new RelationSplitHandlerSpec(), + new SplitActionSpec() ] +Utils.externalDownloadFunction = async (url) => { + console.error("Fetching ", url, "blocked in tests, use Utils.injectJsonDownloadForTests") + const data = await ScriptUtils.DownloadJSON(url) + console.log("\n\n ----------- \nBLOCKED DATA\n Utils.injectJsonDownloadForTests(\n" + + " ", JSON.stringify(url),", \n", + " ", JSON.stringify(data), "\n )\n------------------\n\n") + throw "Detected internet access for URL " + url + ", please inject it with Utils.injectJsonDownloadForTests" +} + let args = [...process.argv] args.splice(0, 2) args = args.map(a => a.toLowerCase()) @@ -34,15 +45,15 @@ if (args.length > 0) { testsToRun = allTests.filter(t => args.indexOf(t.name) >= 0) } -if(testsToRun.length == 0){ +if (testsToRun.length == 0) { throw "No tests found" } -for (let i = 0; i < testsToRun.length; i++){ +for (let i = 0; i < testsToRun.length; i++) { const test = testsToRun[i]; - ScriptUtils.erasableLog(" Running test", i, "/", allTests.length) + console.log(" Running test", i, "/", allTests.length, test.name) allFailures.push(...(test.Run() ?? [])) - + console.log("OK!") } if (allFailures.length > 0) { for (const failure of allFailures) { @@ -50,4 +61,4 @@ if (allFailures.length > 0) { } throw "Some test failed" } -console.log("All tests successful: ", allTests.map(t => t.name).join(", ")) +console.log("All tests successful: ", testsToRun.map(t => t.name).join(", ")) From 355c9384d9aa86ffb3cc170ad3b72e5bb2d01590 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 22 Sep 2021 16:31:50 +0200 Subject: [PATCH 023/110] Fix translations --- assets/layers/crossings/crossings.json | 1 - assets/layers/food/food.json | 4 ++-- assets/themes/cyclestreets/cyclestreets.json | 2 +- langs/shared-questions/en.json | 17 +++++++++++++++++ langs/shared-questions/nl.json | 17 +++++++++++++++++ scripts/generateTranslations.ts | 4 ++++ 6 files changed, 41 insertions(+), 4 deletions(-) diff --git a/assets/layers/crossings/crossings.json b/assets/layers/crossings/crossings.json index 0d033f2d7..876656d1f 100644 --- a/assets/layers/crossings/crossings.json +++ b/assets/layers/crossings/crossings.json @@ -97,7 +97,6 @@ } ], "tagRenderings": [ - "images", { "question": { "en": "What kind of crossing is this?", diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index cb2aa258f..9c5ce26fd 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -182,7 +182,6 @@ "phone", "payment-options", "wheelchair-access", - "dog-access", { "#": "Cuisine", "question": { @@ -551,7 +550,8 @@ } ], "condition": "cuisine=friture" - } + }, + "dog-access" ], "filter": [ { diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index 9d09f0c7a..45ad1a90c 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -68,7 +68,7 @@ ] }, "then": { - "nl": "Deze straat is een fietsstraat", + "nl": "Deze straat i een fietsstraat", "en": "This street is a cyclestreet", "ja": "この通りはcyclestreetだ", "nb_NO": "Denne gaten er en sykkelvei" diff --git a/langs/shared-questions/en.json b/langs/shared-questions/en.json index 9821d4039..709534fcc 100644 --- a/langs/shared-questions/en.json +++ b/langs/shared-questions/en.json @@ -3,6 +3,23 @@ "description": { "question": "Is there still something relevant you couldn't give in the previous questions? Add it here.
Don't repeat already stated facts" }, + "dog-access": { + "mappings": { + "0": { + "then": "Dogs are allowed" + }, + "1": { + "then": "Dogs are not allowed" + }, + "2": { + "then": "Dogs are allowed, but they have to be leashed" + }, + "3": { + "then": "Dogs are allowed and can run around freely" + } + }, + "question": "Are dogs allowed in this business?" + }, "email": { "question": "What is the email address of {name}?" }, diff --git a/langs/shared-questions/nl.json b/langs/shared-questions/nl.json index b6749039b..8c7d6b514 100644 --- a/langs/shared-questions/nl.json +++ b/langs/shared-questions/nl.json @@ -3,6 +3,23 @@ "description": { "question": "Zijn er nog andere relevante zaken die je niet in de bovenstaande vragen kwijt kon? Vul ze hier in.
Herhaal geen antwoorden die je reeds gaf" }, + "dog-access": { + "mappings": { + "0": { + "then": "honden zijn toegelaten" + }, + "1": { + "then": "honden zijn niet toegelaten" + }, + "2": { + "then": "honden zijn enkel aan de leiband welkom" + }, + "3": { + "then": "honden zijn welkom en mogen vrij rondlopen" + } + }, + "question": "Zijn honden toegelaten in deze zaak?" + }, "email": { "question": "Wat is het e-mailadres van {name}?" }, diff --git a/scripts/generateTranslations.ts b/scripts/generateTranslations.ts index f5f59afd8..2bdc852c9 100644 --- a/scripts/generateTranslations.ts +++ b/scripts/generateTranslations.ts @@ -248,7 +248,11 @@ function MergeTranslation(source: any, target: any, language: string, context: s } if (typeof sourceV === "object") { if (targetV === undefined) { + try{ target[language] = sourceV; + }catch(e){ + throw `At context${context}: Could not add a translation in language ${language} due to ${e}` + } } else { MergeTranslation(sourceV, targetV, language, context + "." + key); } From 285ca2e508025967555aab9d551fdd1b2bab76ec Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 22 Sep 2021 16:57:07 +0200 Subject: [PATCH 024/110] Fix deploy --- UI/Base/MinimapImplementation.ts | 2 -- assets/svg/license_info.json | 2 +- package.json | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts index 5ebf74fc7..00fbc0f09 100644 --- a/UI/Base/MinimapImplementation.ts +++ b/UI/Base/MinimapImplementation.ts @@ -46,8 +46,6 @@ export default class MinimapImplementation extends BaseUIElement implements Mini public installBounds(factor: number | BBox, showRange?: boolean) { this.leafletMap.addCallbackD(leaflet => { - console.log("Installing max bounds") - let bounds; if (typeof factor === "number") { bounds = leaflet.getBounds() diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 0bee39135..b3b653d4f 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -1377,4 +1377,4 @@ "https://www.wikipedia.org/" ] } -] +] \ No newline at end of file diff --git a/package.json b/package.json index 60103264f..c8b17cc73 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "validate:licenses": "ts-node scripts/generateLicenseInfo.ts --report", "optimize-images": "cd assets/generated/ && find -name '*.png' -exec optipng '{}' \\; && echo 'PNGs are optimized'", "reset:layeroverview": "echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json", - "generate": "mkdir -p ./assets/generated && npm run reset:layeroverview && npm run generate:images && npm run generate:translations && npm run generate:layeroverview", + "generate": "mkdir -p ./assets/generated && npm run reset:layeroverview && npm run generate:images && npm run generate:translations && npm run generate:licenses && npm run generate:layeroverview", "build": "rm -rf dist/ && npm run generate && parcel build --public-url ./ *.html assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*", "generate:charging-stations": "cd ./assets/layers/charging_station && ts-node csvToJson.ts && cd -", "prepare-deploy": "npm run generate && npm run test && npm run generate:editor-layer-index && npm run generate:charging-stations && npm run generate:layeroverview && npm run generate:layouts && npm run build && rm -rf .cache", From e47224ecb812bc7be23c578f9c50105f99c564a5 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 22 Sep 2021 17:15:30 +0200 Subject: [PATCH 025/110] Small fixes to the UK-addresses theme --- Logic/ExtraFunction.ts | 17 ++++++++++++----- assets/themes/uk_addresses/uk_addresses.json | 12 ++++++++---- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index 8b6834e09..04f7a64f6 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -134,11 +134,18 @@ export class ExtraFunction { args: ["list of features or layer name", "amount of features", "unique tag key (optional)", "maxDistanceInMeters (optional)"] }, (params, feature) => { - return (features, amount, uniqueTag, maxDistanceInMeters) => ExtraFunction.GetClosestNFeatures(params, feature, features, { - maxFeatures: Number(amount), - uniqueTag: uniqueTag, - maxDistance: Number(maxDistanceInMeters) - }) + + return (features, amount, uniqueTag, maxDistanceInMeters) => { + let distance : number = Number(maxDistanceInMeters) + if(isNaN(distance)){ + distance = undefined + } + return ExtraFunction.GetClosestNFeatures(params, feature, features, { + maxFeatures: Number(amount), + uniqueTag: uniqueTag, + maxDistance: distance + }); + } } ) diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 654bb2177..e43f867ed 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -149,17 +149,17 @@ "mappings": [ { "if": "addr:street:={_closest_street:0:name}", - "then": "{_closest_street:0:name} {_closest_street:0:distance}m", + "then": "Located in {_closest_street:0:name} (~{_closest_street:0:distance}m away)", "hideInAnswer": "_closest_street:0:name=" }, { "if": "addr:street:={_closest_street:1:name}", - "then": "{_closest_street:1:name} {_closest_street:1:distance}m", + "then": "Located in {_closest_street:1:name} (~{_closest_street:1:distance}m away)", "hideInAnswer": "_closest_street:1:name=" }, { "if": "addr:street:={_closest_street:2:name}", - "then": "{_closest_street:2:name} {_closest_street:2:distance}m", + "then": "Located in {_closest_street:2:name} (~{_closest_street:2:distance}m away)", "hideInAnswer": "_closest_street:2:name=" } ], @@ -214,7 +214,11 @@ } ] }, - "presets": [] + "presets": [ + { + + } + ] }, { "id": "named_streets", From b9f96155defc760f390d9b94015b1903801e5b22 Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Wed, 22 Sep 2021 18:53:08 +0200 Subject: [PATCH 026/110] Dev Instructions: Install `wget` `npm run init` requires `wget` to be installed but the error message is tiny and easy to miss. Without wget preset, the init tasks does not all it needs to do. Documenting it in the instructions explicitly works around this issue. --- Docs/Development_deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Docs/Development_deployment.md b/Docs/Development_deployment.md index 9fa81f836..f95b7d284 100644 --- a/Docs/Development_deployment.md +++ b/Docs/Development_deployment.md @@ -30,6 +30,7 @@ To develop and build MapComplete, you 0. Make a fork and clone the repository. 1. Install `npm`. Linux: `sudo apt install npm` (or your favourite package manager), Windows: install nodeJS: https://nodejs.org/en/download/ +0. Install `wget`, `brew install wget` 3. Run `npm run init` and generate some additional dependencies and generated files. Note that it'll install the dependencies too 4. Run `npm run start` to host a local testversion at http://localhost:1234/index.html @@ -125,4 +126,3 @@ Overview of package.json-scripts - `deploy:staging`,`deploy:pietervdvn`, `deploy:production`: deploy the latest code on various locations - `lint`: get depressed by the amount of warnings - `clean`: remove some generated files which are annoying in the repo - From cdb3daad4f97ab158f999d5489d5aec0f39ee42f Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Wed, 22 Sep 2021 18:56:51 +0200 Subject: [PATCH 027/110] Use node16 and asdf Introduce .tool-versions with newest nodejs 16 and update documentation to reference this file. This way the current node version is saved in one place that can be read by runtime version managing software, eg. https://asdf-vm.com/ Also small restructuring of the installation instruction list, including https://github.com/pietervdvn/MapComplete/pull/483. --- .tool-versions | 2 +- Docs/Development_deployment.md | 16 +- package-lock.json | 15598 ++++++++++++++++++++++++++++++- 3 files changed, 15570 insertions(+), 46 deletions(-) diff --git a/.tool-versions b/.tool-versions index 8ac21489c..132d0eed6 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 14.14.0 \ No newline at end of file +nodejs 16.9.1 \ No newline at end of file diff --git a/Docs/Development_deployment.md b/Docs/Development_deployment.md index 9fa81f836..292f3f994 100644 --- a/Docs/Development_deployment.md +++ b/Docs/Development_deployment.md @@ -26,14 +26,17 @@ Devcontainer (see more details later). To develop and build MapComplete, you -0. Make sure you have a recent version of nodejs - at least 12.0, preferably 15 0. Make a fork and clone the repository. -1. Install `npm`. Linux: `sudo apt install npm` (or your favourite package manager), Windows: install +0. Install the nodejs version specified in [.tool-versions](./.tool-versions) + - You can [use asdf to manage your runtime versions](https://asdf-vm.com/). +0. Install `npm`. Linux: `sudo apt install npm` (or your favourite package manager), Windows: install nodeJS: https://nodejs.org/en/download/ -3. Run `npm run init` and generate some additional dependencies and generated files. Note that it'll install the - dependencies too -4. Run `npm run start` to host a local testversion at http://localhost:1234/index.html -5. By default, a landing page with available themes is served. In order to load a single theme, use `layout=themename` +0. Install `wget`, `brew install wget` +0. Run `npm run init` which … + - runs `npm install` + - generates some additional dependencies and files +0. Run `npm run start` to host a local testversion at http://localhost:1234/index.html +0. By default, a landing page with available themes is served. In order to load a single theme, use `layout=themename` or `userlayout=true#` as [Query parameter](URL_Parameters.md). Note that the shorter URLs ( e.g. `bookcases.html`, `aed.html`, ...) _don't_ exist on the development version. @@ -125,4 +128,3 @@ Overview of package.json-scripts - `deploy:staging`,`deploy:pietervdvn`, `deploy:production`: deploy the latest code on various locations - `lint`: get depressed by the amount of warnings - `clean`: remove some generated files which are annoying in the repo - diff --git a/package-lock.json b/package-lock.json index 3c57957da..780388999 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,15528 @@ { "name": "mapcomplete", "version": "0.0.5", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "mapcomplete", + "version": "0.0.5", + "license": "GPL", + "dependencies": { + "@babel/preset-env": "7.13.8", + "@tailwindcss/postcss7-compat": "^2.0.2", + "@turf/buffer": "^6.3.0", + "@turf/collect": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/length": "^6.3.0", + "@turf/turf": "^6.3.0", + "@types/jquery": "^3.5.5", + "@types/leaflet-markercluster": "^1.0.3", + "@types/leaflet-providers": "^1.2.0", + "@types/leaflet.markercluster": "^1.4.3", + "@types/lz-string": "^1.3.34", + "@types/prompt-sync": "^4.1.0", + "autoprefixer": "^9.8.6", + "country-language": "^0.1.7", + "email-validator": "^2.0.4", + "escape-html": "^1.0.3", + "i18next-client": "^1.11.4", + "jquery": "^3.6.0", + "jspdf": "^2.3.1", + "latlon2country": "^1.1.3", + "leaflet": "^1.7.1", + "leaflet-providers": "^1.10.2", + "leaflet-simple-map-screenshoter": "^0.4.4", + "leaflet.markercluster": "^1.4.1", + "libphonenumber": "0.0.10", + "libphonenumber-js": "^1.7.55", + "lz-string": "^1.4.4", + "mangrove-reviews": "^0.1.3", + "moment": "^2.29.0", + "opening_hours": "^3.6.0", + "osm-auth": "^1.0.2", + "osmtogeojson": "^3.0.0-beta.4", + "parcel": "^1.2.4", + "postcss": "^7.0.36", + "prompt-sync": "^4.2.0", + "tslint": "^6.1.3" + }, + "devDependencies": { + "@babel/polyfill": "^7.10.4", + "@types/node": "^7.0.5", + "assert": "^2.0.0", + "fs": "0.0.1-security", + "marked": "^2.0.0", + "read-file": "^0.2.0", + "sharp": "^0.28.3", + "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.7", + "ts-node": "^9.0.0", + "ts-node-dev": "^1.0.0-pre.63", + "tslint-no-circular-imports": "^0.7.0", + "typescript": "^3.9.7", + "write-file": "^1.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dependencies": { + "@babel/highlight": "^7.12.13" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", + "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==" + }, + "node_modules/@babel/core": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.0.tgz", + "integrity": "sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.14.0", + "@babel/helper-compilation-targets": "^7.13.16", + "@babel/helper-module-transforms": "^7.14.0", + "@babel/helpers": "^7.14.0", + "@babel/parser": "^7.14.0", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/generator": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.1.tgz", + "integrity": "sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ==", + "dependencies": { + "@babel/types": "^7.14.1", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", + "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", + "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", + "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", + "dependencies": { + "@babel/compat-data": "^7.13.15", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.1.tgz", + "integrity": "sha512-r8rsUahG4ywm0QpGcCrLaUSOuNAISR3IZCg4Fx05Ozq31aCUrQsTLH6KPxy0N5ULoQ4Sn9qjNdGNtbPWAC6hYg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-member-expression-to-functions": "^7.13.12", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-split-export-declaration": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz", + "integrity": "sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "regexpu-core": "^4.7.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz", + "integrity": "sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz", + "integrity": "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==", + "dependencies": { + "@babel/types": "^7.13.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dependencies": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz", + "integrity": "sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==", + "dependencies": { + "@babel/traverse": "^7.13.15", + "@babel/types": "^7.13.16" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", + "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", + "dependencies": { + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", + "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", + "dependencies": { + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz", + "integrity": "sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw==", + "dependencies": { + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-simple-access": "^7.13.12", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.14.0", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz", + "integrity": "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-wrap-function": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", + "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.13.12", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", + "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", + "dependencies": { + "@babel/types": "^7.13.12" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "dependencies": { + "@babel/types": "^7.12.1" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==" + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz", + "integrity": "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==", + "dependencies": { + "@babel/helper-function-name": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", + "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", + "dependencies": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.1.tgz", + "integrity": "sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz", + "integrity": "sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-remap-async-to-generator": "^7.13.0", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", + "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz", + "integrity": "sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", + "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz", + "integrity": "sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", + "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz", + "integrity": "sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", + "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", + "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", + "dependencies": { + "@babel/compat-data": "^7.13.8", + "@babel/helper-compilation-targets": "^7.13.8", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz", + "integrity": "sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz", + "integrity": "sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz", + "integrity": "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", + "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.13.tgz", + "integrity": "sha512-J/RYxnlSLXZLVR7wTRsozxKT8qbsx1mNKJzXEEjQ0Kjx1ZACcyHgbanNWNCFtc36IzuWhYWPpvJFFoexoOWFmA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", + "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", + "integrity": "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz", + "integrity": "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==", + "dependencies": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-remap-async-to-generator": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", + "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.1.tgz", + "integrity": "sha512-2mQXd0zBrwfp0O1moWIhPpEeTKDvxyHcnma3JATVP1l+CctWBuot6OJG8LQ4DnBj4ZZPSmlb/fm4mu47EOAnVA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz", + "integrity": "sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-split-export-declaration": "^7.12.13", + "globals": "^11.1.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz", + "integrity": "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", + "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", + "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", + "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", + "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.13.0.tgz", + "integrity": "sha512-EXAGFMJgSX8gxWD7PZtW/P6M+z74jpx3wm/+9pn+c2dOawPpBkUX7BrfyPvo6ZpXbgRIEuwgwDb/MGlKvu2pOg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-flow": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz", + "integrity": "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", + "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", + "dependencies": { + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", + "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", + "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.0.tgz", + "integrity": "sha512-CF4c5LX4LQ03LebQxJ5JZes2OYjzBuk1TdiF7cG7d5dK4lAdw9NZmaxq5K/mouUdNeqwz3TNjnW6v01UqUNgpQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.14.0", + "@babel/helper-plugin-utils": "^7.13.0", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz", + "integrity": "sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.14.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-simple-access": "^7.13.12", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz", + "integrity": "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.13.0", + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-identifier": "^7.12.11", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz", + "integrity": "sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==", + "dependencies": { + "@babel/helper-module-transforms": "^7.14.0", + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", + "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", + "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", + "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", + "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", + "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz", + "integrity": "sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/types": "^7.13.12" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz", + "integrity": "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==", + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", + "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", + "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz", + "integrity": "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", + "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz", + "integrity": "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", + "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", + "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", + "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/polyfill": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz", + "integrity": "sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==", + "deprecated": "🚨 This package has been deprecated in favor of separate inclusion of a polyfill and regenerator-runtime (when needed). See the @babel/polyfill docs (https://babeljs.io/docs/en/babel-polyfill) for more information.", + "dev": true, + "dependencies": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.8.tgz", + "integrity": "sha512-Sso1xOpV4S3ofnxW2DsWTE5ziRk62jEAKLGuQ+EJHC+YHTbFG38QUTixO3JVa1cYET9gkJhO1pMu+/+2dDhKvw==", + "dependencies": { + "@babel/compat-data": "^7.13.8", + "@babel/helper-compilation-targets": "^7.13.8", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-option": "^7.12.17", + "@babel/plugin-proposal-async-generator-functions": "^7.13.8", + "@babel/plugin-proposal-class-properties": "^7.13.0", + "@babel/plugin-proposal-dynamic-import": "^7.13.8", + "@babel/plugin-proposal-export-namespace-from": "^7.12.13", + "@babel/plugin-proposal-json-strings": "^7.13.8", + "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", + "@babel/plugin-proposal-numeric-separator": "^7.12.13", + "@babel/plugin-proposal-object-rest-spread": "^7.13.8", + "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", + "@babel/plugin-proposal-optional-chaining": "^7.13.8", + "@babel/plugin-proposal-private-methods": "^7.13.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.12.13", + "@babel/plugin-transform-arrow-functions": "^7.13.0", + "@babel/plugin-transform-async-to-generator": "^7.13.0", + "@babel/plugin-transform-block-scoped-functions": "^7.12.13", + "@babel/plugin-transform-block-scoping": "^7.12.13", + "@babel/plugin-transform-classes": "^7.13.0", + "@babel/plugin-transform-computed-properties": "^7.13.0", + "@babel/plugin-transform-destructuring": "^7.13.0", + "@babel/plugin-transform-dotall-regex": "^7.12.13", + "@babel/plugin-transform-duplicate-keys": "^7.12.13", + "@babel/plugin-transform-exponentiation-operator": "^7.12.13", + "@babel/plugin-transform-for-of": "^7.13.0", + "@babel/plugin-transform-function-name": "^7.12.13", + "@babel/plugin-transform-literals": "^7.12.13", + "@babel/plugin-transform-member-expression-literals": "^7.12.13", + "@babel/plugin-transform-modules-amd": "^7.13.0", + "@babel/plugin-transform-modules-commonjs": "^7.13.8", + "@babel/plugin-transform-modules-systemjs": "^7.13.8", + "@babel/plugin-transform-modules-umd": "^7.13.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", + "@babel/plugin-transform-new-target": "^7.12.13", + "@babel/plugin-transform-object-super": "^7.12.13", + "@babel/plugin-transform-parameters": "^7.13.0", + "@babel/plugin-transform-property-literals": "^7.12.13", + "@babel/plugin-transform-regenerator": "^7.12.13", + "@babel/plugin-transform-reserved-words": "^7.12.13", + "@babel/plugin-transform-shorthand-properties": "^7.12.13", + "@babel/plugin-transform-spread": "^7.13.0", + "@babel/plugin-transform-sticky-regex": "^7.12.13", + "@babel/plugin-transform-template-literals": "^7.13.0", + "@babel/plugin-transform-typeof-symbol": "^7.12.13", + "@babel/plugin-transform-unicode-escapes": "^7.12.13", + "@babel/plugin-transform-unicode-regex": "^7.12.13", + "@babel/preset-modules": "^0.1.4", + "@babel/types": "^7.13.0", + "babel-plugin-polyfill-corejs2": "^0.1.4", + "babel-plugin-polyfill-corejs3": "^0.1.3", + "babel-plugin-polyfill-regenerator": "^0.1.2", + "core-js-compat": "^3.9.0", + "semver": "^6.3.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz", + "integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.8.tgz", + "integrity": "sha512-4dMD5QRBkumn45oweR0SxoNtt15oz3BUBAQ8cIx7HJqZTtE8zjpM0My8aHJHVnyf4XfRg6DNzaE1080WLBiC1w==", + "optional": true, + "dependencies": { + "core-js-pure": "^3.15.0", + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/traverse": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.0.tgz", + "integrity": "sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.14.0", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.14.0", + "@babel/types": "^7.14.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "node_modules/@babel/types": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.1.tgz", + "integrity": "sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.0", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@fullhuman/postcss-purgecss": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.1.3.tgz", + "integrity": "sha512-kwOXw8fZ0Lt1QmeOOrd+o4Ibvp4UTEBFQbzvWldjlKv5n+G9sXfIPn1hh63IQIL8K8vbvv1oYMJiIUbuy9bGaA==", + "dependencies": { + "purgecss": "^3.1.3" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, + "node_modules/@mapbox/geojson-area": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-area/-/geojson-area-0.2.2.tgz", + "integrity": "sha1-GNeBSqNr8j+7zDefjiaiKSfevxA=", + "dependencies": { + "wgs84": "0.0.0" + } + }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.4.0.tgz", + "integrity": "sha512-b+1uPWBERW4Pet/969BNu61ZPDyH2ilIxBjJDFzxyS9TyszF9UrTQyYIl/G38clux3rtpAGGFSGTCSF/qR6UjA==", + "dependencies": { + "@mapbox/geojson-area": "0.2.2", + "concat-stream": "~1.6.0", + "minimist": "1.2.0", + "sharkdown": "^0.1.0" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/geojson-rewind/node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/@mapbox/geojson-rewind/node_modules/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "node_modules/@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dependencies": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/fs": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-1.11.0.tgz", + "integrity": "sha512-86RyEqULbbVoeo8OLcv+LQ1Vq2PKBAvWTU9fCgALxuCTbbs5Ppcvll4Vr+Ko1AnmMzja/k++SzNAwJfeQXVlpA==", + "dependencies": { + "@parcel/utils": "^1.11.0", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@parcel/logger": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-1.11.1.tgz", + "integrity": "sha512-9NF3M6UVeP2udOBDILuoEHd8VrF4vQqoWHEafymO1pfSoOMfxrSJZw1MfyAAIUN/IFp9qjcpDCUbDZB+ioVevA==", + "dependencies": { + "@parcel/workers": "^1.11.0", + "chalk": "^2.1.0", + "grapheme-breaker": "^0.3.2", + "ora": "^2.1.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@parcel/utils": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-1.11.0.tgz", + "integrity": "sha512-cA3p4jTlaMeOtAKR/6AadanOPvKeg8VwgnHhOyfi0yClD0TZS/hi9xu12w4EzA/8NtHu0g6o4RDfcNjqN8l1AQ==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-1.12.1.tgz", + "integrity": "sha512-od+uCtCxC/KoNQAIE1vWx1YTyKYY+7CTrxBJPRh3cDWw/C0tCtlBMVlrbplscGoEpt6B27KhJDCv82PBxOERNA==", + "dependencies": { + "@parcel/utils": "^1.11.0", + "chokidar": "^2.1.5" + } + }, + "node_modules/@parcel/watcher/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/@parcel/watcher/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.", + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/@parcel/watcher/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/@parcel/watcher/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/@parcel/watcher/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@parcel/watcher/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/workers": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-1.11.0.tgz", + "integrity": "sha512-USSjRAAQYsZFlv43FUPdD+jEGML5/8oLF0rUzPQTtK4q9kvaXr49F5ZplyLz5lox78cLZ0TxN2bIDQ1xhOkulQ==", + "dependencies": { + "@parcel/utils": "^1.11.0", + "physical-cpu-count": "^2.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@tailwindcss/postcss7-compat": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.1.2.tgz", + "integrity": "sha512-bH2kw6uyqLnDMP8wzDUsis5ovrsRzfHEyiL1McADvqlW54g6y0KVHX1xzO7PH8Fl5s0Sq8vDOAp4+3V8MEcZ9g==", + "dependencies": { + "@fullhuman/postcss-purgecss": "^3.1.3", + "autoprefixer": "^9", + "bytes": "^3.0.0", + "chalk": "^4.1.0", + "chokidar": "^3.5.1", + "color": "^3.1.3", + "detective": "^5.2.0", + "didyoumean": "^1.2.1", + "dlv": "^1.1.3", + "fast-glob": "^3.2.5", + "fs-extra": "^9.1.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.21", + "lodash.topath": "^4.5.2", + "modern-normalize": "^1.0.0", + "node-emoji": "^1.8.1", + "normalize-path": "^3.0.0", + "object-hash": "^2.1.1", + "parse-glob": "^3.0.4", + "postcss": "^7", + "postcss-functions": "^3", + "postcss-js": "^2", + "postcss-nested": "^4", + "postcss-selector-parser": "^6.0.4", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "quick-lru": "^5.1.1", + "reduce-css-calc": "^2.1.8", + "resolve": "^1.20.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@tailwindcss/postcss7-compat/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@tailwindcss/postcss7-compat/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@tailwindcss/postcss7-compat/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@tailwindcss/postcss7-compat/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@tailwindcss/postcss7-compat/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@tailwindcss/postcss7-compat/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@turf/along": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-6.3.0.tgz", + "integrity": "sha512-2j0nHp38IuzESyv5/9hLYM2MuUe155Kw390lkQtiLjhRtTeYQNEaRy+uhZhf3/DWrjGULH1HatLc5j0CmiwrJA==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/angle": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/angle/-/angle-6.3.0.tgz", + "integrity": "sha512-wCWoK+7JKGYPZKYxdWwJJfqm1IQbUdOf4j5SENO6WJryXViM/ogRu2eAEqrmyrMYO84vonMSqiuPEuGoLqo9Xg==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0" + } + }, + "node_modules/@turf/area": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.3.0.tgz", + "integrity": "sha512-Y1cYyAQ2fk94npdgOeMF4msc2uabHY1m7A7ntixf1I8rkyDd6/iHh1IMy1QsM+VZXAEwDwsXhu+ZFYd3Jkeg4A==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/bbox": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.3.0.tgz", + "integrity": "sha512-N4ue5Xopu1qieSHP2MA/CJGWHPKaTrVXQJjzHRNcY1vtsO126xbSaJhWUrFc5x5vVkXp0dcucGryO0r5m4o/KA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/bbox-clip": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-6.3.0.tgz", + "integrity": "sha512-DCFs1MdX3P7SzZiBjT1kWBp4g0cfv8Yn2/Ccq3JP4iVaqNQJujPfe0WwZjjTdXLbLLFTjoxnCJBjy3WZDmLvlw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/bbox-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-6.3.0.tgz", + "integrity": "sha512-CCyTBM8LzGRu/lReNlgDyjRO8NojtJ7EPPvSl3bdKQbNFsCm25gwe7Y3xsaCkWLNn5g89lQJI9Izf9xdEsENjQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/bearing": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.3.0.tgz", + "integrity": "sha512-apuUm9xN6VQLO33m7F2mmzlm3dHfeesJjMSzh9iehGtgmp1IaVndjdcIvs0ieiwm8bN9UhwXpfPtO3pV0n9SFw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/bezier-spline": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-6.3.0.tgz", + "integrity": "sha512-5kJv7zLjuZPhjO8Z/eNT68UHwiDru6ihn2He0VFrnSJQJZI8V/TFXCob7GxncYFlKk7uHru8iMXGxFe3Y3P44w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-clockwise": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-6.3.0.tgz", + "integrity": "sha512-zW0j8uPjBS5QJqNmJIeatTH02E1S7OCuBNBvkoOUPifC/c2xJ120a1r73prBj1zMFr6k3UCjwG9V8whUMxIAYA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-contains": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-6.3.0.tgz", + "integrity": "sha512-1MW7B5G5tIu1lnAv3pXyFzl75wfBYnbA2GhwHDb4okIXMhloy/r5uIqAZHo0fOXykKVJS/gIfA/MioKIftoTug==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/boolean-point-on-line": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-crosses": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-6.3.0.tgz", + "integrity": "sha512-ajCuNSSqQPN2p3Y1ERX4E/wEsNn5JANI2uNgGOpVAeNX48prQGCBANcG2FTMMB+WVqq9iIdQ4eB5mEg6I8TS4w==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/polygon-to-line": "^6.3.0" + } + }, + "node_modules/@turf/boolean-disjoint": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-6.3.0.tgz", + "integrity": "sha512-bVAwAJF05QPH0tf+qjR3kUcCyqTgYcCbXSMgXl6LQF6mSGuOutzNq1gCyRLCOdOcZtw4Oh4dqeP3ykwv8kDibw==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/polygon-to-line": "^6.3.0" + } + }, + "node_modules/@turf/boolean-equal": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-6.3.0.tgz", + "integrity": "sha512-eXr3oSHTvJYGyu/v57uNg0tnDHFnu+triwAaXtBh7lozt4d2riU8Ow71B+tjT9mBe/JRFfXIDsBWjbyB37y/6w==", + "dependencies": { + "@turf/clean-coords": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "geojson-equality": "0.1.6" + } + }, + "node_modules/@turf/boolean-intersects": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-intersects/-/boolean-intersects-6.3.0.tgz", + "integrity": "sha512-2pHOYqHSKDo0rzHTiqwdAaxa+tHLwr4NaTAjOpuN2hipv9bErzGtv3e5IYceJBnT0u4akK17NTn6qAr7/7g2aQ==", + "dependencies": { + "@turf/boolean-disjoint": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/boolean-overlap": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-6.3.0.tgz", + "integrity": "sha512-rWh8JKTqlJ1m27FY8YeWcGoXutLyCVfSi2/8AOkXi2F+36P9GM4tHz19yKY3btbnHJTgSZf1xO2YhX2d0BmNqg==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/line-overlap": "^6.3.0", + "@turf/meta": "^6.3.0", + "geojson-equality": "0.1.6" + } + }, + "node_modules/@turf/boolean-parallel": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-6.3.0.tgz", + "integrity": "sha512-p5YcKtVON6fTE3+pffw16QZyg3uXRmZ8CNxZM7lhGrJrPnny7BD2Kz1z2fp+8EElf00kjX2vFbDjDftte4Xh3g==", + "dependencies": { + "@turf/clean-coords": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0" + } + }, + "node_modules/@turf/boolean-point-in-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.3.0.tgz", + "integrity": "sha512-NqFSsoE6OwhDK19IllDQRhEQEkF7UVEOlqH9vgS1fGg4T6NcyKvACJs05c9457tL7QSbV9ZS53f2qiLneFL+qg==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-point-on-line": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-6.3.0.tgz", + "integrity": "sha512-eScH8sfKJVjfbEX5Hgkt1nA7A8DUoiYD1riUVqTp2xikujrMfnYRjFpL/UAo01v33cPKZlhCXp7NE86bdOSrYg==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/boolean-within": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-6.3.0.tgz", + "integrity": "sha512-8XtVbzPp6J+lqZtDWVyIwSyVAVcnuie82ub56JEAhCf9w8FX5Db3qXQ76pFcOyy/woeXLZY/nIR58Q79PusrRw==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/boolean-point-on-line": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/buffer": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-6.3.0.tgz", + "integrity": "sha512-B0GWgJzmTaaw1GvTd+Df+ToKSYphz9d6hPCOwXbE2vS5DdZryoxBfxQ32LSX/hW/vx7TLf7E4M0VJBb+Sn1DKA==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/center": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/projection": "^6.3.0", + "d3-geo": "1.7.1", + "turf-jsts": "*" + } + }, + "node_modules/@turf/center": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-6.3.0.tgz", + "integrity": "sha512-41g/ZYwoBs2PK7tpAHhf4D6llHdRvY827HLXCld5D0IOnzsWPqDk7WnV8P5uq4g/gyH1/WfKQYn5SgfSj4sSfw==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/center-mean": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-6.3.0.tgz", + "integrity": "sha512-BZsqThJmc7wUTxPj7/RYztaegPntR2bBFDPTJ/C+qN8lnRhCccCZ81npYunriwMQC1kyXd1BChGMwjFh3jfB+Q==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/center-median": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-6.3.0.tgz", + "integrity": "sha512-jMQzp4YLIPDWKAMpvyRmNOLcoCHy/OMsLIv6odmfBJc6q+5GkulXz4QW61a5o6XZNDkZiYe9f0QgNGaKH+HTWg==", + "dependencies": { + "@turf/center-mean": "^6.3.0", + "@turf/centroid": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/center-of-mass": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-6.3.0.tgz", + "integrity": "sha512-dbiNo4VjNOskK/9hlifmb+cIsFgLqru3m/U1b+btDrliLzrFw3BEeLquZf3IZkOGMpVdIi5/F7IbkrPPz7HgWw==", + "dependencies": { + "@turf/centroid": "^6.3.0", + "@turf/convex": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/centroid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.3.0.tgz", + "integrity": "sha512-7KTyqhUEqXDoyR/nf/jAXiW8ZVszEnrp5XZkgYyrf2GWdSovSO0iCN1J3bE2jkJv7IWyeDmGYL61GGzuTSZS2Q==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/circle": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-6.3.0.tgz", + "integrity": "sha512-5N3J4YQr1efidvPgvtIQYpxb7gBVEoo00IFC0JNH6KqIVBMttFZw3Wsqor34ya91m58A5m6HTiz9Cdm1ktrEdw==", + "dependencies": { + "@turf/destination": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/clean-coords": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-6.3.0.tgz", + "integrity": "sha512-Ns7+vXHigKTclzqlFrUnXsXjtEWAu2YYurDxD5mrKXcncuisUIoKbFM55ZxeiiBj0ji8c1huR1xSqs8GVxZJJA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/clone": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.3.0.tgz", + "integrity": "sha512-GAgN89/9GCqUKECB1oY2hcTs0K2rZj+a2tY6VfM0ef9wwckuQZCKi+kKGUzhKVrmHee15jKV8n6DY0er8OndKg==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/clusters": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-6.3.0.tgz", + "integrity": "sha512-NIT6LZ/zawt1nN7eC0VEII8J1QUx5qvUahtPKsADxHP27vDJDjnmGvUXvvC0XmibXt/RR9VRM5Rej04yn53g0A==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/clusters-dbscan": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-6.3.0.tgz", + "integrity": "sha512-EHWHMEBSGf4dvobfvifMl2G9p9KATP9TSeSf1WY+ajLRPfn3slUPSM9hP+7eisDBgb/tS+wqQNcl7pEoo72pnw==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "density-clustering": "1.3.0" + } + }, + "node_modules/@turf/clusters-kmeans": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-6.3.0.tgz", + "integrity": "sha512-cyHtW5nsOcs1p8l3mflX2805fOxR99FanXCP95U+001S4AwVSgxiOfTg8PUHg9nui2Qcq/PMBRQz80exb2UzyA==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "skmeans": "0.9.7" + } + }, + "node_modules/@turf/collect": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-6.3.0.tgz", + "integrity": "sha512-alkKujZ02m2wYNixYjF4AFSzXTMbewf1QnJRrtog3snJHFN/tZB9iU3ZcwvxOSbO2Zwrw89A90HLe8k7oGUqXw==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "rbush": "2.x" + } + }, + "node_modules/@turf/combine": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-6.3.0.tgz", + "integrity": "sha512-/FKUxUvQhKDDBJ4CTr49rvanYbdrtlsbr+7p6H8Vv0EyfeWqwJ3qA8lRuAjPtK0StviYg2t6XTucvKd/3PPX3Q==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/concave": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-6.3.0.tgz", + "integrity": "sha512-9BPctrW2Oy9K2jjKv80tR26RQEJjwAAFwgG8JEBK8hSF9zdqa07fzx7Ncj+8hM9+3vF30f2TvQ8yxvoH7HSvXA==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/tin": "^6.3.0", + "topojson": "3.x" + } + }, + "node_modules/@turf/convex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-6.3.0.tgz", + "integrity": "sha512-YpiLKRu1suwbI/knCOd7Fg7LojV6Beonu8gQjCoaPdkBEz0/W3XqNpfWQhcqp+XR10a2g4RK5mi6bUUejToFBw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "concaveman": "*" + } + }, + "node_modules/@turf/destination": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-6.3.0.tgz", + "integrity": "sha512-aLt3U/XkJWyZW08Ln1qZwBNAGh27yhmYLu892+dBj3gKP6UUiR6ZopXxrBwjBVe00A6k2ktftKDn79qe0hptuw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/difference": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-6.3.0.tgz", + "integrity": "sha512-f4P0ra0jBOFk4HO8n/9FZ3NEmOX7FHCXHy/4Z1RSUUQsUQDCkx6/cyqbi8BCy2ZSDUSCGHV+iPgs4fRphMzCHQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "polygon-clipping": "^0.15.2" + } + }, + "node_modules/@turf/difference/node_modules/polygon-clipping": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz", + "integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==", + "dependencies": { + "splaytree": "^3.1.0" + } + }, + "node_modules/@turf/difference/node_modules/splaytree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.0.tgz", + "integrity": "sha512-gvUGR7xnOy0fLKTCxDeUZYgU/I1Tdf8M/lM1Qrf8L2TIOR5ipZjGk02uYcdv0o2x7WjVRgpm3iS2clLyuVAt0Q==" + }, + "node_modules/@turf/dissolve": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-6.3.0.tgz", + "integrity": "sha512-DxFH+3MQpBo3rIZSh9gjcdl00ZkyHAEK0DzTLq6JOS4vTHpYvFvDT07j/Vr+9cqfvWrAjGpQg92I8zMzh4XA6Q==", + "dependencies": { + "@turf/boolean-overlap": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/union": "^6.3.0", + "geojson-rbush": "3.x", + "get-closest": "*" + } + }, + "node_modules/@turf/distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.3.0.tgz", + "integrity": "sha512-basi24ssNFnH3iXPFjp/aNUrukjObiFWoIyDRqKyBJxVwVOwAWvfk4d38QQyBj5nDo5IahYRq/Q+T47/5hSs9w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/distance-weight": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/distance-weight/-/distance-weight-6.3.0.tgz", + "integrity": "sha512-o85n4q3WM0L292FV7ZKBtSdTzn20JRqcZSSktkJoxeuQJMHXlstRwviLiF5pTl5oDXO/mRdq6aPecvWkMAaiCQ==", + "dependencies": { + "@turf/centroid": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/ellipse": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-6.3.0.tgz", + "integrity": "sha512-r+EvUK+IGgc3shvS/T1Wof2uCptS2fYmtcwMSFHnHjRnmUyrD4YFjPZT7ygxcDB91+UClZ6cdozR6vqBYzPAog==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0", + "@turf/transform-rotate": "^6.3.0" + } + }, + "node_modules/@turf/envelope": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-6.3.0.tgz", + "integrity": "sha512-9xmDTCogXJsAO0TrARA/lniMSEtAil9HIKXHDJ5N6zlZ2K5wfRdD2zDlqkgDT3t9oSvttSP3ltBf03fjMDt6Wg==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/bbox-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/explode": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-6.3.0.tgz", + "integrity": "sha512-J3vOGwf2EJXfh1gifFtxAuuhVYWAMTRQL6jE3h9a8osNLO1nj8JGVxaL6fmJgdZ/A9cFPv1OYUndBzi86UYZvw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/flatten": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-6.3.0.tgz", + "integrity": "sha512-0V3qxOGqb0NulEpADPCs/+i/AUQuNSChGA4oy/YGicfMHjnMNapZfOVg3LJEAkd/Kqpw2eJjjKe0gaX5aXo/1w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/flip": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-6.3.0.tgz", + "integrity": "sha512-VTST1oaJFRyHOAbvY9kt8yKKCQt6aXpXeyVQRjhNESzlYLIQlTx3v+lI+eSSu+sc+SX4EDQltB1UdaVk7BIRJg==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/great-circle": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-6.3.0.tgz", + "integrity": "sha512-dpGJcRf2TLzBvVUZa0Eej3edXOQofLcp9qgotqDHK68spqYK8lnrXrdyyqzLlTHx3nxZkHvFUOl1lqj8G4NraQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/helpers": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.3.0.tgz", + "integrity": "sha512-kr6KuD4Z0GZ30tblTEvi90rvvVNlKieXuMC8CTzE/rVQb0/f/Cb29zCXxTD7giQTEQY/P2nRW23wEqqyNHulCg==" + }, + "node_modules/@turf/hex-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-6.3.0.tgz", + "integrity": "sha512-adqOgpBJB+87bjnm5EKVklDuWsYtCrETlLrXpOw4CVyaqYEE2/Mvid25se/0TeGDfvIcnvIQvrApYL5O/sDaMw==", + "dependencies": { + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/intersect": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/interpolate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-6.3.0.tgz", + "integrity": "sha512-2gVMSj/Ri8l5KGkCTyTJTqSbZwfWco6tWGMZyG0fqcB61PA6pEedU+TShBOOEKu7eBlpSyHlkS7+uii1bEGUCA==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/centroid": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/hex-grid": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/point-grid": "^6.3.0", + "@turf/square-grid": "^6.3.0", + "@turf/triangle-grid": "^6.3.0" + } + }, + "node_modules/@turf/intersect": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-6.3.0.tgz", + "integrity": "sha512-1YCIkyKjuTlX7HaTjtyE7ZRxLCmcu0BYr6jqoVl7TjyF2NUiNpPm3m4X1ZrSF6MfjIt5NFSGYCdNMEPgREq19w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "polygon-clipping": "^0.15.2" + } + }, + "node_modules/@turf/intersect/node_modules/polygon-clipping": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz", + "integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==", + "dependencies": { + "splaytree": "^3.1.0" + } + }, + "node_modules/@turf/intersect/node_modules/splaytree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.0.tgz", + "integrity": "sha512-gvUGR7xnOy0fLKTCxDeUZYgU/I1Tdf8M/lM1Qrf8L2TIOR5ipZjGk02uYcdv0o2x7WjVRgpm3iS2clLyuVAt0Q==" + }, + "node_modules/@turf/invariant": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.3.0.tgz", + "integrity": "sha512-2OFOi9p+QOrcIMySEnr+WlOiKaFZ1bY56jA98YyECewJHfhPFWUBZEhc4nWGRT0ahK08Vus9+gcuBX8QIpCIIw==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/isobands": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-6.3.0.tgz", + "integrity": "sha512-Ikk8LyVQJKsLH6nFYKEeUi9sShMVP9S63zy5CPMPvwRhZf0ix59tAEBfnk6DOfd0EzLLmEdfaAM2U0cRhkh9jA==", + "dependencies": { + "@turf/area": "^6.3.0", + "@turf/bbox": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/explode": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "object-assign": "*" + } + }, + "node_modules/@turf/isolines": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-6.3.0.tgz", + "integrity": "sha512-z5hUIUcSaInGUhrx+vDZcCNWLS3MawzQGfc0TOUVDe03bO5sqUlaNyvx7C09Js4LEzsqqZ1GPIUvFPjePaXaVQ==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "object-assign": "*" + } + }, + "node_modules/@turf/kinks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-6.3.0.tgz", + "integrity": "sha512-BLWvbl2/fa4SeJzVMbleT6Vo1cmzwmzRfxL2xxMei2jmf6JSvqDoMJFwIHGXrLZXvhOCb1b2C+MhBfhtc7kYkQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/length": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-6.3.0.tgz", + "integrity": "sha512-91MHtigpV7mbrMW3xyaPVtLWQU3p487t3YHU4vdxih03p+dFI512dX/FtWbd9LNgrtBt4PM1uo1WmafGvfStKA==", + "dependencies": { + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/line-arc": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-6.3.0.tgz", + "integrity": "sha512-WAAUgAWGf+U02GhXWrplODyUm3X6LZnYyn4VJQ9BPsKyawfK+NtjP7KsZ1MipIgtixNq3Ceexep0AHGHos4Prw==", + "dependencies": { + "@turf/circle": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/line-chunk": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-6.3.0.tgz", + "integrity": "sha512-Xfja7H6XEgFPaK37sg7WBb0pIiA9hfjXtF7A1QPrh8z+JFyuVJzveBG2mYvin5UKTwsMKXuby6s4FUvmoEFqjQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/length": "^6.3.0", + "@turf/line-slice-along": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/line-intersect": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-6.3.0.tgz", + "integrity": "sha512-3naxR7XpkPd2vst3Mw6DFry4C9m3o0/f2n/xu5UAyxb88Ie4m2k+1eqkhzMMx/0L+E6iThWpLx7DASM6q6o9ow==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/meta": "^6.3.0", + "geojson-rbush": "3.x" + } + }, + "node_modules/@turf/line-offset": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-6.3.0.tgz", + "integrity": "sha512-yzgmNc/8miyn+pH2ubT4rZb9uAPY6oLqkwmEdzy2fuU4yUFnCNN/nWvYP4acGdgaSfprJd+4MdlLFzWBJxSplw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/line-overlap": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-6.3.0.tgz", + "integrity": "sha512-fVyXfTpr/A+ZXZWG6PbuYz5rAGbTQWyrMZveCl2049SbOXSkVXGjUfpnLaklP0p+adw7eRR0LhZn6FGz9CQaFg==", + "dependencies": { + "@turf/boolean-point-on-line": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/nearest-point-on-line": "^6.3.0", + "deep-equal": "1.x", + "geojson-rbush": "3.x" + } + }, + "node_modules/@turf/line-segment": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-6.3.0.tgz", + "integrity": "sha512-M+aDy83V+E7jYWNaf+b+A88yhnMrJhyg/lhAj6mU6UeB2PbruXB2qgSmmVDSE2dIknOvZZuIWNzEzUI07RO2kw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/line-slice": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-6.3.0.tgz", + "integrity": "sha512-HEgVY7TcoRxh59DCb/7SUlX6x3RJWSEBspIfsxCv+2lhgb3aRekn+aELvr3VeY9fWPCXvOfELBH3PNjMhJMY2Q==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/nearest-point-on-line": "^6.3.0" + } + }, + "node_modules/@turf/line-slice-along": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-6.3.0.tgz", + "integrity": "sha512-3s6vGTxGgCTb3Wd1seyir49rRc0GsX6OZXiRP5VdlT3Aq0cuuCNJycgHCH+H8LiYrEQDUhNUWbGljreCH0/JCg==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/line-split": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-6.3.0.tgz", + "integrity": "sha512-Q0nUJ0vczy11piyEz0FaKScFwSQtb1HJ2RPEMCw1coUJhTCB02KBWQLImhYqwsD3uLg+H/fxaJ1Gva6EPWoDNQ==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/nearest-point-on-line": "^6.3.0", + "@turf/square": "^6.3.0", + "@turf/truncate": "^6.3.0", + "geojson-rbush": "3.x" + } + }, + "node_modules/@turf/line-to-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-6.3.0.tgz", + "integrity": "sha512-754ywhQzcAylVSqQQwlv0TUMC5nCHp4nDle3X48tkHIKcnn4fJkW8O0YNhhQCE8p6NDcs0Ayi4qR0uHLPTzUWQ==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/mask": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-6.3.0.tgz", + "integrity": "sha512-2DbaHvmxz0ueQpGCo+6nXHhLqlmTjzGDkUL/ys6rgWTXj40udKakPwMNa2WrvzqHwowJsXWaWDp2GogRT5foDA==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/union": "^6.3.0", + "rbush": "^2.0.1" + } + }, + "node_modules/@turf/meta": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.3.0.tgz", + "integrity": "sha512-qBJjaAJS9H3ap0HlGXyF/Bzfl0qkA9suafX/jnDsZvWMfVLt+s+o6twKrXOGk5t7nnNON2NFRC8+czxpu104EQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/midpoint": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-6.3.0.tgz", + "integrity": "sha512-ImiYK5l/QZh5aCynxCyHoaJYn4j1VhorVyw2XihHuwAtebTc+KRaBJpWSD2eJxo3Q3J+QepWMiiMvQFJgQ5uCQ==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/moran-index": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/moran-index/-/moran-index-6.3.0.tgz", + "integrity": "sha512-qRsSqmYtvnKiGFbz3aU1up8Q8jY9MCflRdvKeTOJ2E3Z4xOIyOLXOrNvpLIM8CFcLwY06IInMRoaKi/CVOC54g==", + "dependencies": { + "@turf/distance-weight": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/nearest-point": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-6.3.0.tgz", + "integrity": "sha512-eovLuWxO2cQaKETbf1OhnWYkRYYgwuDhJAvLU9ZpXnqk2tNE06gt/2C5oJJiSlh4ZksDM8ryHZicswaXrYz+qA==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/nearest-point-on-line": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-6.3.0.tgz", + "integrity": "sha512-b4C9Md1VbGn9chMgdSj2grJD4w4t0owEWOKEBwOZfdhrcksyOedVvKB7XqOFdj/8Jitel40EKAC5LQTNu24kEQ==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/nearest-point-to-line": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-6.3.0.tgz", + "integrity": "sha512-1ut4u1KXHwXc6qdnDLkhTdPUdeHOmdmysMBxnNNFH7UTefi3XfR8BF/NOxNP8g7OKJrZ2vhDeR4PCL5xAsVH5A==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/point-to-line-distance": "^6.3.0", + "object-assign": "*" + } + }, + "node_modules/@turf/planepoint": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-6.3.0.tgz", + "integrity": "sha512-RDfzUiwB3P3bGeRBZf/czZdtQsqUIVQePaAU5ijCqTBdR1V0TuVbRig1WE0XD4j5dM242OEezHJ3Xqgo71Nzww==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/point-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-6.3.0.tgz", + "integrity": "sha512-1ERghdRXtA/5Z/To7X1Y9D1cvej3+ZCZXNZnM/0c+3sAioohjK5IXv2enR23p1ftA6Z3H7wug5IB4YmVzs4MaA==", + "dependencies": { + "@turf/boolean-within": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/point-on-feature": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-6.3.0.tgz", + "integrity": "sha512-zN35KN/IUAgOyVtlEQg1j71U8eoav2JPZOdWlEFHsjYQVm9cF+AKOkvBdm6LQWMWvCtwSqqghwe/zRKvzJPynw==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/center": "^6.3.0", + "@turf/explode": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/nearest-point": "^6.3.0" + } + }, + "node_modules/@turf/point-to-line-distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-6.3.0.tgz", + "integrity": "sha512-AqCcj4A0GPzKb3w+q+C9ex0r5mC+u+Ee6VN2jY1p25dxBQJNpMZKDE5LcWtaXeD+pAk3ZGmvea8LR5S0AJukxA==", + "dependencies": { + "@turf/bearing": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/projection": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0", + "@turf/rhumb-distance": "^6.3.0" + } + }, + "node_modules/@turf/points-within-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-6.3.0.tgz", + "integrity": "sha512-ES/tLj5oZR7TBg7FSOy8bypBvXALwl2f36MmQ3AJfK0KvAeQ+mxFXTGslAK3ewL9fVVxWLsmbP9bPLSzWeuPAw==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/polygon-smooth": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-smooth/-/polygon-smooth-6.3.0.tgz", + "integrity": "sha512-60aMw3d57DXqdFyWU43c5gHaumCZ9jn6K5GqgeKTfmElIumdSspg9MEIW7d7z6qkPufPY34FczJ9yapMih5SIQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/polygon-tangents": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-6.3.0.tgz", + "integrity": "sha512-QEXsXgZKWV3mPPqxERIQ+DzBSvnO0R1c9FsHuHE0F49Cic+CRMPjEpnzQj39cOUQfwPlQl2ThuaKAljlQ5QNMQ==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/boolean-within": "^6.3.0", + "@turf/explode": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/nearest-point": "^6.3.0" + } + }, + "node_modules/@turf/polygon-to-line": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-6.3.0.tgz", + "integrity": "sha512-KFGlQlGOBayBvELz+tip1zCa3eB8xyZePZUZ3I3OnU7mk0FFzJzvLTmPUc7MupgqORT4LkNGmyKSVWaz38NTig==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/polygonize": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-6.3.0.tgz", + "integrity": "sha512-v1w5ibIJ5to3+nuitVNyukPMMY+z++y3e55TBuot1vkAEyCi538Kc8Qz0eWONPGZKzwYtQtkve2NIp0BBeNd5g==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/envelope": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/projection": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-6.3.0.tgz", + "integrity": "sha512-IpSs7Q6G6xi47ynVlYYVegPLy6Jc0yo3/DcIm83jaJa4NnzPFXIFZT0v9Fe1N8MraHZqiqaSPbVnJXCGwR12lg==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/random": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/random/-/random-6.3.0.tgz", + "integrity": "sha512-jSKNqLCOc/xUPoQp8jZLUYTrtID1PNJV7eLXMbJdHdcYwU7d6dTkrdgI08ZU/Nc4qJv1ZAlWO/xEyKGtC1RgrQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/rectangle-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rectangle-grid/-/rectangle-grid-6.3.0.tgz", + "integrity": "sha512-XQAjpprUhGA9aoVH8H6lqZb0Dk8SZ2djKAPD6dDplFgrufdmP1Fe1BfbsdBgjyfPrdR7hSffLyEAwC3bhfJo2w==", + "dependencies": { + "@turf/boolean-intersects": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/rewind": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-6.3.0.tgz", + "integrity": "sha512-56HwvOZ4r4/wXr8l8zCpdjZ3bxY6Ee7aokuJr/+BlVqikHdRHRx+FJpLGpykZU1YWdO7IiLK7ajX+clYPaqRKg==", + "dependencies": { + "@turf/boolean-clockwise": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/rhumb-bearing": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-6.3.0.tgz", + "integrity": "sha512-/c/BE3huEUrwN6gx7Bg2FzfJqeU+TWk/slQPDHpbVunlIPbS6L28brqSVD+KXfMG8HQIzynz6Pm4Y+j5Iv4aWA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/rhumb-destination": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-6.3.0.tgz", + "integrity": "sha512-MaQf5wldfERfn8cjtbkD/6GUurAwD+sjedvDgV/chZ83yx7kXmRgrVMpRSGUbmGQ3Ww8dn38sUCapnM6M07+Rg==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/rhumb-distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-6.3.0.tgz", + "integrity": "sha512-wMIQVvznusonnp/POeucFdA4Rubn0NrkcEMdxdcCgFK7OmTz0zU4CEnNONF2IUGkQ5WwoKiuS7MOTQ8OuCjSfQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/@turf/sample": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-6.3.0.tgz", + "integrity": "sha512-CmUkpoLIi+57jxBmYh4KW7S4Vculty84NC2ERNFZrLkVquewVYSppwKsaZtc0Hbap6a1N7hP4C80e2bPzRC4fg==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/sector": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-6.3.0.tgz", + "integrity": "sha512-bHaDlHzCKEl5G+EEXdMTk3MFC8Yl5QjwrMVakF2Usi0P0c7hp6r10QVOjq9nmn6jvZHTPaiG2A4z9unkWIFxIg==", + "dependencies": { + "@turf/circle": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/line-arc": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/shortest-path": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-6.3.0.tgz", + "integrity": "sha512-dc50vcgb6G/nyljCdfxS4T3tGb2f45MkKEFdz6sVTYqjNakPnRoJao8xvInVsf1i2J53dWNU635oZhW9P1nqKg==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/bbox-polygon": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/clean-coords": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/transform-scale": "^6.3.0" + } + }, + "node_modules/@turf/simplify": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-6.3.0.tgz", + "integrity": "sha512-6a+9oKwZpZk3Oohz9koQZGXh1qb+/UgUz2yW2bunjjlKpBdBFhRbEKi0KeprgPGFLLTMjf0tybhO1rFwiz6S1w==", + "dependencies": { + "@turf/clean-coords": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/square": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-6.3.0.tgz", + "integrity": "sha512-/nRGsV0DlUcOYv+gKAkIADSf+HooNLbOLBTUdhq9Piy3LuAWIXT+Rt5XN+NuNZP+84Al34GA1fR+BxqQ4reh7w==", + "dependencies": { + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/square-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-6.3.0.tgz", + "integrity": "sha512-ZCgThI5hPLJNVErCB9zkJ3w3OpW6BbrOqyrxFbwlYGZrZ6uj52/j8PWQtwnmiqdv0k8+Cbxrap7E6//Oks4jIw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/rectangle-grid": "^6.3.0" + } + }, + "node_modules/@turf/standard-deviational-ellipse": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-6.3.0.tgz", + "integrity": "sha512-e8CeSUv5FLpzlJxiOr9lDtJIY3e/JKW4is+gBO8rMTQNFbWyrqXtzhyTfrwXEPKmaeei1DK9ixxj/oRDna25Hw==", + "dependencies": { + "@turf/center-mean": "^6.3.0", + "@turf/ellipse": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/points-within-polygon": "^6.3.0" + } + }, + "node_modules/@turf/tag": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-6.3.0.tgz", + "integrity": "sha512-3L//rLql+ILeFuZ5L/sPm0f5NcHrNgUnGiB1hSIp3kdhhIIiZUpcktJUbksTvID67JJlP3smfyIQiU++LZW21w==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/tesselate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-6.3.0.tgz", + "integrity": "sha512-SkBHJCci/ergp/Y1TIfBRavdEJgFatQDz+ySdggXHT+mBiJEOEia3N+8V89RVOnORXTCDsjzWOWwftCS/J2sKQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "earcut": "^2.0.0" + } + }, + "node_modules/@turf/tin": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-6.3.0.tgz", + "integrity": "sha512-obk9vyzKo3o3Dy4fPlb8IROb9LdMlz4LvKZ63DNtQsxwrWsc+og0EOh2mpvZrCIeoObx3ah5SnuAh14xH4JybA==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/@turf/transform-rotate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-6.3.0.tgz", + "integrity": "sha512-6CPfmDdaXjbBoPeyHkui704vz6MD3MoI09LGRVJ/RIo1uH/OL6RDSlCfLxFtkE33FJ7VV4giczc3LF1UP5Oh9w==", + "dependencies": { + "@turf/centroid": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0", + "@turf/rhumb-distance": "^6.3.0" + } + }, + "node_modules/@turf/transform-scale": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-6.3.0.tgz", + "integrity": "sha512-UnLWEXAUdZy7JYbylMjYczPUkxXlUK1nMgv7zEzQ+8mczysPVsgB/FDyiexY2bgVEEBMeDqFSHtqLRavXljI0A==", + "dependencies": { + "@turf/bbox": "^6.3.0", + "@turf/center": "^6.3.0", + "@turf/centroid": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0", + "@turf/rhumb-distance": "^6.3.0" + } + }, + "node_modules/@turf/transform-translate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-6.3.0.tgz", + "integrity": "sha512-ZGAK3T6wdYLOIKr/FHl+i09b1vhPV3XWHw4/M27xA6US2rNcO6/jkLjskdME/3JzJDFmGa8F2vlPqlhtWWoRSw==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0" + } + }, + "node_modules/@turf/triangle-grid": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-6.3.0.tgz", + "integrity": "sha512-2AExXl7pTvRKOyGowuvvUm0tTyLQl+xzvv+mgWgNyg84qQptGN3HFH/QS4quoQdEzOyHNLFHgloNn6cWFX9v4A==", + "dependencies": { + "@turf/distance": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/intersect": "^6.3.0" + } + }, + "node_modules/@turf/truncate": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-6.3.0.tgz", + "integrity": "sha512-fvzR3BUODPciEBELLqqAggEEeb1L0d79WZYb9HKaoSB0GKTTgNrEbkTXiiGEjGJ1s1FMqXOEp0DKsLvvb1h4OA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/@turf/turf": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-6.3.0.tgz", + "integrity": "sha512-6CcUammJKsn6mI7/+DlnXqf1iAk5HZ86/wmHIVG6VTmmPBP5drWSjoRUcaiXQADzLLuR9eZ3kl11KEOdvn9DmQ==", + "dependencies": { + "@turf/along": "^6.3.0", + "@turf/angle": "^6.3.0", + "@turf/area": "^6.3.0", + "@turf/bbox": "^6.3.0", + "@turf/bbox-clip": "^6.3.0", + "@turf/bbox-polygon": "^6.3.0", + "@turf/bearing": "^6.3.0", + "@turf/bezier-spline": "^6.3.0", + "@turf/boolean-clockwise": "^6.3.0", + "@turf/boolean-contains": "^6.3.0", + "@turf/boolean-crosses": "^6.3.0", + "@turf/boolean-disjoint": "^6.3.0", + "@turf/boolean-equal": "^6.3.0", + "@turf/boolean-intersects": "^6.3.0", + "@turf/boolean-overlap": "^6.3.0", + "@turf/boolean-parallel": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/boolean-point-on-line": "^6.3.0", + "@turf/boolean-within": "^6.3.0", + "@turf/buffer": "^6.3.0", + "@turf/center": "^6.3.0", + "@turf/center-mean": "^6.3.0", + "@turf/center-median": "^6.3.0", + "@turf/center-of-mass": "^6.3.0", + "@turf/centroid": "^6.3.0", + "@turf/circle": "^6.3.0", + "@turf/clean-coords": "^6.3.0", + "@turf/clone": "^6.3.0", + "@turf/clusters": "^6.3.0", + "@turf/clusters-dbscan": "^6.3.0", + "@turf/clusters-kmeans": "^6.3.0", + "@turf/collect": "^6.3.0", + "@turf/combine": "^6.3.0", + "@turf/concave": "^6.3.0", + "@turf/convex": "^6.3.0", + "@turf/destination": "^6.3.0", + "@turf/difference": "^6.3.0", + "@turf/dissolve": "^6.3.0", + "@turf/distance": "^6.3.0", + "@turf/distance-weight": "^6.3.0", + "@turf/ellipse": "^6.3.0", + "@turf/envelope": "^6.3.0", + "@turf/explode": "^6.3.0", + "@turf/flatten": "^6.3.0", + "@turf/flip": "^6.3.0", + "@turf/great-circle": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/hex-grid": "^6.3.0", + "@turf/interpolate": "^6.3.0", + "@turf/intersect": "^6.3.0", + "@turf/invariant": "^6.3.0", + "@turf/isobands": "^6.3.0", + "@turf/isolines": "^6.3.0", + "@turf/kinks": "^6.3.0", + "@turf/length": "^6.3.0", + "@turf/line-arc": "^6.3.0", + "@turf/line-chunk": "^6.3.0", + "@turf/line-intersect": "^6.3.0", + "@turf/line-offset": "^6.3.0", + "@turf/line-overlap": "^6.3.0", + "@turf/line-segment": "^6.3.0", + "@turf/line-slice": "^6.3.0", + "@turf/line-slice-along": "^6.3.0", + "@turf/line-split": "^6.3.0", + "@turf/line-to-polygon": "^6.3.0", + "@turf/mask": "^6.3.0", + "@turf/meta": "^6.3.0", + "@turf/midpoint": "^6.3.0", + "@turf/moran-index": "^6.3.0", + "@turf/nearest-point": "^6.3.0", + "@turf/nearest-point-on-line": "^6.3.0", + "@turf/nearest-point-to-line": "^6.3.0", + "@turf/planepoint": "^6.3.0", + "@turf/point-grid": "^6.3.0", + "@turf/point-on-feature": "^6.3.0", + "@turf/point-to-line-distance": "^6.3.0", + "@turf/points-within-polygon": "^6.3.0", + "@turf/polygon-smooth": "^6.3.0", + "@turf/polygon-tangents": "^6.3.0", + "@turf/polygon-to-line": "^6.3.0", + "@turf/polygonize": "^6.3.0", + "@turf/projection": "^6.3.0", + "@turf/random": "^6.3.0", + "@turf/rewind": "^6.3.0", + "@turf/rhumb-bearing": "^6.3.0", + "@turf/rhumb-destination": "^6.3.0", + "@turf/rhumb-distance": "^6.3.0", + "@turf/sample": "^6.3.0", + "@turf/sector": "^6.3.0", + "@turf/shortest-path": "^6.3.0", + "@turf/simplify": "^6.3.0", + "@turf/square": "^6.3.0", + "@turf/square-grid": "^6.3.0", + "@turf/standard-deviational-ellipse": "^6.3.0", + "@turf/tag": "^6.3.0", + "@turf/tesselate": "^6.3.0", + "@turf/tin": "^6.3.0", + "@turf/transform-rotate": "^6.3.0", + "@turf/transform-scale": "^6.3.0", + "@turf/transform-translate": "^6.3.0", + "@turf/triangle-grid": "^6.3.0", + "@turf/truncate": "^6.3.0", + "@turf/union": "^6.3.0", + "@turf/unkink-polygon": "^6.3.0", + "@turf/voronoi": "^6.3.0" + } + }, + "node_modules/@turf/union": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/union/-/union-6.3.0.tgz", + "integrity": "sha512-m8yh13Q5E0Y+YC10+iI/Qq0Txt7UmSIFByc7DfNVlMMGTceqLFa8xGwSVdFuB/d6MWwKuzKonQMl1PUx/Vd2Iw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "polygon-clipping": "^0.15.2" + } + }, + "node_modules/@turf/union/node_modules/polygon-clipping": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz", + "integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==", + "dependencies": { + "splaytree": "^3.1.0" + } + }, + "node_modules/@turf/union/node_modules/splaytree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.0.tgz", + "integrity": "sha512-gvUGR7xnOy0fLKTCxDeUZYgU/I1Tdf8M/lM1Qrf8L2TIOR5ipZjGk02uYcdv0o2x7WjVRgpm3iS2clLyuVAt0Q==" + }, + "node_modules/@turf/unkink-polygon": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-6.3.0.tgz", + "integrity": "sha512-XBUJkuDEr2R8cHpl+sHtV15J1S28/HCxhAHqfV+As3bTi81KhVhBK9EBwFGYCu9aerVgBK129FjRKXjnTYqtDw==", + "dependencies": { + "@turf/area": "^6.3.0", + "@turf/boolean-point-in-polygon": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0", + "rbush": "^2.0.1" + } + }, + "node_modules/@turf/voronoi": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-6.3.0.tgz", + "integrity": "sha512-M0C6Kfo+qvKk4veRD7xW1PjMitJ0vqN6F4OOczxyX3tkj/oMyhWg+YbWk7mo/wKdSo9gCvHhnIVNkPsSSaFmyQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0", + "d3-voronoi": "1.1.2" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.7", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==" + }, + "node_modules/@types/jquery": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.5.tgz", + "integrity": "sha512-6RXU9Xzpc6vxNrS6FPPapN1SxSHgQ336WC6Jj/N8q30OiaBZ00l1GBgeP7usjVZPivSkGUfL1z/WW6TX989M+w==", + "dependencies": { + "@types/sizzle": "*" + } + }, + "node_modules/@types/leaflet": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.7.0.tgz", + "integrity": "sha512-ltv5jR+VjKSMtoDkxH61Rsbo0zLU7iqyOXpVPkAX4F+79fg2eymC7t0msWsfNaEZO1FGTIQATCCCQe+ijWoicg==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/leaflet-markercluster": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/leaflet-markercluster/-/leaflet-markercluster-1.0.3.tgz", + "integrity": "sha1-ZBUb5FP2SQ6HUVAEgt65YQZOeCw=", + "deprecated": "'@types/leaflet-markercluster' is now '@types/leaflet.markercluster'.", + "dependencies": { + "@types/leaflet": "*" + } + }, + "node_modules/@types/leaflet-providers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/leaflet-providers/-/leaflet-providers-1.2.0.tgz", + "integrity": "sha512-xuIUo0rEtuKdKIWwtLH0mD+dgSy/CRspVPa0nI/SuPLS29H7q+GPO4Qluxzkk+u9qvMtTjxqkrJjtnsx96+aPQ==", + "dependencies": { + "@types/leaflet": "*" + } + }, + "node_modules/@types/leaflet.markercluster": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.4.4.tgz", + "integrity": "sha512-BQAilNWlBpYl4+PrsJXLOh4vyv7KfWi5kh3Fclg5y4gEeNeXKqhS6y1zzBB4+wcTuVUnMWfm2G0MfqA4yA5A5A==", + "dependencies": { + "@types/leaflet": "*" + } + }, + "node_modules/@types/lz-string": { + "version": "1.3.34", + "resolved": "https://registry.npmjs.org/@types/lz-string/-/lz-string-1.3.34.tgz", + "integrity": "sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow==" + }, + "node_modules/@types/node": { + "version": "7.10.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.14.tgz", + "integrity": "sha512-29GS75BE8asnTno3yB6ubOJOO0FboExEqNJy4bpz0GSmW/8wPTNL4h9h63c6s1uTrOopCmJYe/4yJLh5r92ZUA==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/prompt-sync": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.1.0.tgz", + "integrity": "sha1-utMynv9bQRXjTvRpgjckTUEdRHA=" + }, + "node_modules/@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" + }, + "node_modules/@types/raf": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz", + "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==", + "optional": true + }, + "node_modules/@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==" + }, + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dependencies": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals/node_modules/acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/affine-hull": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz", + "integrity": "sha1-dj/x040GPOt+Jy8X7k17vK+QXF0=", + "dependencies": { + "robust-orientation": "^1.1.3" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" + }, + "node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-to-html": { + "version": "0.6.14", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.14.tgz", + "integrity": "sha512-7ZslfB1+EnFSDO5Ju+ue5Y6It19DRnZXWv8jrGHgIlPna5Mh4jz7BV5jCbQneXNFurQcKoolaaAjHtgSBfOIuA==", + "dependencies": { + "entities": "^1.1.2" + }, + "bin": { + "ansi-to-html": "bin/ansi-to-html" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ansicolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", + "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=" + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" + }, + "node_modules/array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "dev": true, + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assert/node_modules/util": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", + "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", + "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "colorette": "^1.2.1", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "dev": true, + "dependencies": { + "array-filter": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "node_modules/axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410", + "dependencies": { + "follow-redirects": "1.5.10" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.1.10.tgz", + "integrity": "sha512-DO95wD4g0A8KRaHKi0D51NdGXzvpqVLnLu5BTvDlpqUEpTmeEtypgC1xqesORaWmiUOQI14UHKlzNd9iZ2G3ZA==", + "dependencies": { + "@babel/compat-data": "^7.13.0", + "@babel/helper-define-polyfill-provider": "^0.1.5", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz", + "integrity": "sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.1.5", + "core-js-compat": "^3.8.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.1.6.tgz", + "integrity": "sha512-OUrYG9iKPKz8NxswXbRAdSwF0GhRdIEMTloQATJi4bDuFqrXaXcCUT/VGNrr8pBcjMh1RxZ7Xt9cytVJTJfvMg==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.1.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/babel-runtime/node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "node_modules/babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dependencies": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "node_modules/babel-types/node_modules/to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babylon-walk": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babylon-walk/-/babylon-walk-1.0.2.tgz", + "integrity": "sha1-OxWl3btIKni0zpwByLoYFwLZ1s4=", + "dependencies": { + "babel-runtime": "^6.11.6", + "babel-types": "^6.15.0", + "lodash.clone": "^4.5.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-arraybuffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz", + "integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==", + "optional": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha1-DGwfq+KyPRcXPZpht7cJPrnhdp4=" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", + "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", + "dependencies": { + "quote-stream": "^1.0.1", + "resolve": "^1.1.5", + "static-module": "^2.2.0", + "through2": "^2.0.0" + }, + "bin": { + "brfs": "bin/cmd.js" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-rsa/node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserify-zlib/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dependencies": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "node_modules/builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "engines": { + "node": ">=4" + } + }, + "node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "dependencies": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001223", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001223.tgz", + "integrity": "sha512-k/RYs6zc/fjbxTjaWZemeSmOjO0JJV+KguOBA3NwPup8uzxM1cMhR2BD9XmO86GuqaqTCO8CgkgH9Rz//vdDiA==" + }, + "node_modules/canvg": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.7.tgz", + "integrity": "sha512-4sq6iL5Q4VOXS3PL1BapiXIZItpxYyANVzsAKpTPS5oq4u3SKbGfUcbZh2gdLCQ3jWpG/y5wRkMlBBAJhXeiZA==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.9.6", + "@types/raf": "^3.4.0", + "raf": "^3.4.1", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^5.0.5" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cardinal": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-0.4.4.tgz", + "integrity": "sha1-ylu2iltRG5D+k7ms6km97lwyv+I=", + "dependencies": { + "ansicolors": "~0.2.1", + "redeyed": "~0.4.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-spinners": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", + "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/closure": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/closure/-/closure-1.0.3.tgz", + "integrity": "sha1-5BD7MJWJaIGHfTeLjhSV93S2HEk=", + "engines": { + "node": ">=0.3.2" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "dependencies": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/color-string": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concaveman": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/concaveman/-/concaveman-1.2.0.tgz", + "integrity": "sha512-OcqechF2/kubbffomKqjGEkb0ndlYhEbmyg/fxIGqdfYp5AZjD2Kl5hc97Hh3ngEuHU2314Z4KDbxL7qXGWrQQ==", + "dependencies": { + "point-in-polygon": "^1.0.1", + "rbush": "^3.0.0", + "robust-predicates": "^2.0.4", + "tinyqueue": "^2.0.3" + } + }, + "node_modules/concaveman/node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, + "node_modules/concaveman/node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dependencies": { + "quickselect": "^2.0.0" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/convex-hull": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/convex-hull/-/convex-hull-1.0.3.tgz", + "integrity": "sha1-IKOqbOh/St6i/30XlxyfwcZ+H/8=", + "dependencies": { + "affine-hull": "^1.0.0", + "incremental-convex-hull": "^1.0.1", + "monotone-convex-hull-2d": "^1.0.1" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/core-js-compat": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.12.0.tgz", + "integrity": "sha512-vvaN8EOvYBEjrr+MN3vCKrMNc/xdYZI+Rt/uPMROi4T5Hj8Fz6TiPQm2mrB9aZoQVW1lCFHYmMrv99aUct9mkg==", + "dependencies": { + "browserslist": "^4.16.6", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-js-pure": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.15.2.tgz", + "integrity": "sha512-D42L7RYh1J2grW8ttxoY1+17Y4wXZeKe7uyplAI3FkNQyI5OgBIAjUfFiTPfL1rs0qLpxaabITNbjKl1Sp82tA==", + "hasInstallScript": true, + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/country-language": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/country-language/-/country-language-0.1.7.tgz", + "integrity": "sha1-eHD0uhJduaYHHxlze9nvk0OuNds=", + "dependencies": { + "underscore": "~1.7.0", + "underscore.deep": "~0.5.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dependencies": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "engines": { + "node": ">4" + } + }, + "node_modules/css-line-break": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-1.1.1.tgz", + "integrity": "sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==", + "optional": true, + "dependencies": { + "base64-arraybuffer": "^0.2.0" + } + }, + "node_modules/css-modules-loader-core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", + "integrity": "sha1-WQhmgpShvs0mGuCkziGwtVHyHRY=", + "dependencies": { + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.1", + "postcss-modules-extract-imports": "1.1.0", + "postcss-modules-local-by-default": "1.2.0", + "postcss-modules-scope": "1.1.0", + "postcss-modules-values": "1.3.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/postcss": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.1.tgz", + "integrity": "sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=", + "dependencies": { + "chalk": "^1.1.3", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "node_modules/css-select/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, + "node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "dependencies": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "dependencies": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "node_modules/cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dependencies": { + "cssom": "0.3.x" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "node_modules/d3-geo": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.7.1.tgz", + "integrity": "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw==", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/d3-voronoi": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", + "integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw=" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dependencies": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + } + }, + "node_modules/dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + }, + "bin": { + "dateformat": "bin/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/deasync": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.21.tgz", + "integrity": "sha512-kUmM8Y+PZpMpQ+B4AuOW9k2Pfx/mSupJtxOsLzmnHY2WqZUYRFccFn2RhzPAqt3Xb+sorK/badW2D4zNzqZz5w==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^1.7.1" + }, + "engines": { + "node": ">=0.11.0" + } + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "node_modules/density-clustering": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/density-clustering/-/density-clustering-1.3.0.tgz", + "integrity": "sha1-3J9ZyPCrl+FiSsZJMP0xlIF9ysU=" + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dependencies": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz", + "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/dom-to-image-more": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/dom-to-image-more/-/dom-to-image-more-2.8.0.tgz", + "integrity": "sha512-YqlHI1i+TMuaKwkFRO5oDPjC3eWf+6Hln9rHZcnFYvmoXwCrGZmZ7BYXBJOjw5utYg2Lp+QF9YO96F7CsDC4eQ==" + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dependencies": { + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/domhandler": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.2.1.tgz", + "integrity": "sha1-Wd+dzSJ+gIs2Wuc+H2aErD2Ub8I=", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/dompurify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.0.tgz", + "integrity": "sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw==", + "optional": true + }, + "node_modules/domutils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.3.0.tgz", + "integrity": "sha1-mtTVm1r2ymhMYv5tdo7xcOcN8ZI=", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz", + "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==", + "engines": { + "node": ">=4.6.0" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + } + }, + "node_modules/earcut": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.2.tgz", + "integrity": "sha512-eZoZPPJcUHnfRZ0PjLvx2qBordSiO8ofC3vt+qACLM95u+4DovnbYNpQtJh0DNsWj8RnxrQytD4WA8gj5cRIaQ==" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/electron-to-chromium": { + "version": "1.3.727", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", + "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/email-validator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz", + "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==", + "engines": { + "node": ">4.0" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "node_modules/es-abstract": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", + "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.2", + "is-string": "^1.0.5", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract/node_modules/object-inspect": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz", + "integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", + "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "dependencies": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/falafel": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.4.tgz", + "integrity": "sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ==", + "dependencies": { + "acorn": "^7.1.1", + "foreach": "^2.0.5", + "isarray": "^2.0.1", + "object-keys": "^1.0.6" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/falafel/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" + }, + "node_modules/fastq": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", + "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dependencies": { + "debug": "=3.1.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/follow-redirects/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/follow-redirects/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=", + "dev": true + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/geojson-area": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/geojson-area/-/geojson-area-0.2.1.tgz", + "integrity": "sha1-JTewmC24YwnyHSxCikJXx6YoLMY=", + "deprecated": "This module is now under the @mapbox namespace: install @mapbox/geojson-area instead", + "dependencies": { + "wgs84": "0.0.0" + } + }, + "node_modules/geojson-equality": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/geojson-equality/-/geojson-equality-0.1.6.tgz", + "integrity": "sha1-oXE3TvBD5dR5eZWEC65GSOB1LXI=", + "dependencies": { + "deep-equal": "^1.0.0" + } + }, + "node_modules/geojson-normalize": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/geojson-normalize/-/geojson-normalize-0.0.0.tgz", + "integrity": "sha1-Lbw2eM0bMbgXnodr2nDNEg3eNcA=", + "deprecated": "This module is now under the @mapbox namespace: install @mapbox/geojson-normalize instead" + }, + "node_modules/geojson-numeric": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/geojson-numeric/-/geojson-numeric-0.2.1.tgz", + "integrity": "sha512-rvItMp3W7pe16o2EQTnRw54v6WHdiE4bYjUsdr3FZskFb6oPC7gjLe4zginP+Wd1B/HLl2acTukfn16Lmwn7lg==", + "dependencies": { + "concat-stream": "2.0.0", + "optimist": "~0.3.5" + }, + "bin": { + "geojson-numeric": "geojson-numeric" + } + }, + "node_modules/geojson-random": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/geojson-random/-/geojson-random-0.2.2.tgz", + "integrity": "sha1-q0g48SatxeFvj5TmVd74IPkRnbw=", + "bin": { + "geojson-random": "geojson-random" + } + }, + "node_modules/geojson-rbush": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-3.1.2.tgz", + "integrity": "sha512-grkfdg3HIeTjwTfiJe5FT8+fGU3fABCc+vRJDBwdQz9kkLF0Sbif2gs2JUzjewwgmnvLGy9fInySDeADoNuk7w==", + "dependencies": { + "@turf/bbox": "*", + "@turf/helpers": "6.x", + "@turf/meta": "6.x", + "rbush": "^2.0.0" + } + }, + "node_modules/get-closest": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/get-closest/-/get-closest-0.0.4.tgz", + "integrity": "sha1-JprHdtHmAiqg/Vht1wjop9Miaa8=" + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=", + "engines": { + "node": ">=4" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "dev": true + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dependencies": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-base/node_modules/glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dependencies": { + "is-glob": "^2.0.0" + } + }, + "node_modules/glob-base/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-base/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "node_modules/grapheme-breaker": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/grapheme-breaker/-/grapheme-breaker-0.3.2.tgz", + "integrity": "sha1-W55reMODJFLSuiuxy4MPlidkEKw=", + "dependencies": { + "brfs": "^1.2.0", + "unicode-trie": "^0.3.1" + } + }, + "node_modules/growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" + }, + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" + }, + "node_modules/html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dependencies": { + "whatwg-encoding": "^1.0.1" + } + }, + "node_modules/html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/html2canvas": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.1.4.tgz", + "integrity": "sha512-uHgQDwrXsRmFdnlOVFvHin9R7mdjjZvoBoXxicPR+NnucngkaLa5zIDW9fzMkiip0jSffyTyWedE8iVogYOeWg==", + "optional": true, + "dependencies": { + "css-line-break": "1.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/htmlnano": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-0.2.9.tgz", + "integrity": "sha512-jWTtP3dCd7R8x/tt9DK3pvpcQd7HDMcRPUqPxr/i9989q2k5RHIhmlRDFeyQ/LSd8IKrteG8Ce5g0Ig4eGIipg==", + "dependencies": { + "cssnano": "^4.1.11", + "posthtml": "^0.15.1", + "purgecss": "^2.3.0", + "relateurl": "^0.2.7", + "srcset": "^3.0.0", + "svgo": "^1.3.2", + "terser": "^5.6.1", + "timsort": "^0.3.0", + "uncss": "^0.17.3" + } + }, + "node_modules/htmlnano/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/htmlnano/node_modules/dom-serializer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", + "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/htmlnano/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/htmlnano/node_modules/domhandler": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", + "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlnano/node_modules/domutils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", + "integrity": "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/htmlnano/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/htmlnano/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlnano/node_modules/postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + }, + "node_modules/htmlnano/node_modules/posthtml": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.15.2.tgz", + "integrity": "sha512-YugEJ5ze/0DLRIVBjCpDwANWL4pPj1kHJ/2llY8xuInr0nbkon3qTiMPe5LQa+cCwNjxS7nAZZTp+1M+6mT4Zg==", + "dependencies": { + "posthtml-parser": "^0.7.2", + "posthtml-render": "^1.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/htmlnano/node_modules/posthtml-parser": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.7.2.tgz", + "integrity": "sha512-LjEEG/3fNcWZtBfsOE3Gbyg1Li4CmsZRkH1UmbMR7nKdMXVMYI3B4/ZMiCpaq8aI1Aym4FRMMW9SAOLSwOnNsQ==", + "dependencies": { + "htmlparser2": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlnano/node_modules/purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-2.3.0.tgz", + "integrity": "sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ==", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.0.0", + "postcss": "7.0.32", + "postcss-selector-parser": "^6.0.2" + }, + "bin": { + "purgecss": "bin/purgecss" + } + }, + "node_modules/htmlnano/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/htmlnano/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/htmlnano/node_modules/terser": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz", + "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==", + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlnano/node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/htmlnano/node_modules/terser/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/htmlparser2": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.5.1.tgz", + "integrity": "sha1-b0L3ZX3RnBP31l3pEYQXOUoL5tA=", + "dependencies": { + "domelementtype": "1", + "domhandler": "2.2", + "domutils": "1.3", + "readable-stream": "1.1" + } + }, + "node_modules/htmlparser2/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/htmlparser2/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/htmlparser2/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "node_modules/http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "node_modules/i18next": { + "version": "20.3.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-20.3.2.tgz", + "integrity": "sha512-e8CML2R9Ng2sSQOM80wb/PrM2j8mDm84o/T4Amzn9ArVyNX5/ENWxxAXkRpZdTQNDaxKImF93Wep4mAoozFrKw==", + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.2.tgz", + "integrity": "sha512-YDzIGHhMRvr7M+c8B3EQUKyiMBhfqox4o1qkFvt4QXuu5V2cxf74+NCr+VEkUuU0y+RwcupA238eeolW1Yn80g==", + "dependencies": { + "@babel/runtime": "^7.14.6" + } + }, + "node_modules/i18next-browser-languagedetector/node_modules/@babel/runtime": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", + "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/i18next-client": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/i18next-client/-/i18next-client-1.11.4.tgz", + "integrity": "sha1-BILrG2Q+z3qEBPe1kuujKOXwmuc=", + "deprecated": "you can use npm install i18next from version 2.0.0", + "engines": { + "node": ">=0.4.12" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "dependencies": { + "import-from": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/incremental-convex-hull": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/incremental-convex-hull/-/incremental-convex-hull-1.0.1.tgz", + "integrity": "sha1-UUKMFMudmmFEv+abKFH7N3M0vh4=", + "dependencies": { + "robust-orientation": "^1.1.2", + "simplicial-complex": "^1.0.0" + } + }, + "node_modules/indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dependencies": { + "call-bind": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "dependencies": { + "call-bind": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dependencies": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz", + "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.3.tgz", + "integrity": "sha512-tDpEUInNcy2Yw3lNSepK3Wdw1RnXLcIVienz6Ou631Acl15cJyRWK4dgA1vCmOEgIbtOV0W7MHg+AR2Gdg1NXQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.9.tgz", + "integrity": "sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-html": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-html/-/is-html-1.1.0.tgz", + "integrity": "sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ=", + "dependencies": { + "html-tags": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-html/node_modules/html-tags": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-1.2.0.tgz", + "integrity": "sha1-x43mW1Zjqll5id0rerSSANfk25g=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" + }, + "node_modules/is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", + "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.0-next.2", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "node_modules/jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "deprecated": "Jade has been renamed to pug, please install the latest version of pug instead of jade", + "dependencies": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "bin": { + "jade": "bin/jade" + } + }, + "node_modules/jade/node_modules/commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", + "engines": { + "node": ">= 0.4.x" + } + }, + "node_modules/jade/node_modules/mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "engines": { + "node": "*" + } + }, + "node_modules/jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "node_modules/jsdom": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz", + "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==", + "dependencies": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.1.3", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsdom/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/jsdom/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/jsdom/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jsdom/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jshashes": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/jshashes/-/jshashes-1.0.8.tgz", + "integrity": "sha512-btmQZ/w1rj8Lb6nEwvhjM7nBYoj54yaEFo2PWh3RkxZ8qNwuvOxvQYN/JxVuwoMmdIluL+XwYVJ+pEEZoSYybQ==", + "bin": { + "hashes": "bin/hashes" + }, + "engines": { + "node": "*" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=", + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.0.tgz", + "integrity": "sha1-78Ri1aW8lOwAf0siVxrNf28q4BM=", + "dependencies": { + "jsonparse": "0.0.5", + "through": "~2.2.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/JSONStream/node_modules/through": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", + "integrity": "sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0=" + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/jspdf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.3.1.tgz", + "integrity": "sha512-1vp0USP1mQi1h7NKpwxjFgQkJ5ncZvtH858aLpycUc/M+r/RpWJT8PixAU7Cw/3fPd4fpC8eB/Bj42LnsR21YQ==", + "dependencies": { + "atob": "^2.1.2", + "btoa": "^1.2.1", + "fflate": "^0.4.8" + }, + "optionalDependencies": { + "canvg": "^3.0.6", + "core-js": "^3.6.0", + "dompurify": "^2.2.0", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jspdf/node_modules/core-js": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.2.tgz", + "integrity": "sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==", + "hasInstallScript": true, + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/jsts": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/jsts/-/jsts-1.1.2.tgz", + "integrity": "sha1-0gXSzIOTCB2eSErjYoIRBpXtwjA=", + "engines": { + "node": ">= 4" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwk-to-pem": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.5.tgz", + "integrity": "sha512-L90jwellhO8jRKYwbssU9ifaMVqajzj3fpRjDKcsDzrslU9syRbFqfkXtT4B89HYAap+xsxNcxgBSB09ig+a7A==", + "dependencies": { + "asn1.js": "^5.3.0", + "elliptic": "^6.5.4", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/latlon2country": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/latlon2country/-/latlon2country-1.1.3.tgz", + "integrity": "sha512-jk4xlaG7jkt4qPBZlhjUK60yo9qYs5LrAqf0hDTfHV6KXuR7qp84CgIq34Zm1xhH6PEQG4TknLWJrJWk23qNVA==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^6.0.1", + "@turf/turf": "^5.1.6", + "@types/node": "^14.14.10", + "jquery": "^3.5.1", + "ts-node": "^9.1.1", + "turf": "^3.0.14" + } + }, + "node_modules/latlon2country/node_modules/@turf/along": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-5.1.5.tgz", + "integrity": "sha1-YdbmplhKzdq1asVYTge/jL5fi+s=", + "dependencies": { + "@turf/bearing": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/area": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-5.1.5.tgz", + "integrity": "sha1-79iZv9Jgzb0VQbKjwVX4pdLu+h0=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bbox": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-5.1.5.tgz", + "integrity": "sha1-MFHfUUrUxQ9KT5uKLRX9i2hA7aM=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bbox-clip": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-5.1.5.tgz", + "integrity": "sha1-M2S1Mo3/nzz0HZ4C7a/zdNFQzIQ=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "lineclip": "^1.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bbox-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-5.1.5.tgz", + "integrity": "sha1-auuk7VHYXSluD3w4uIwznwHu4CQ=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bearing": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-5.1.5.tgz", + "integrity": "sha1-egt5ATbE70eX8CRjBdRcvi0ns/c=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/bezier-spline": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-5.1.5.tgz", + "integrity": "sha1-WaJ7ul17l+8Vqz/VpA+9I4cEm8o=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-clockwise": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-5.1.5.tgz", + "integrity": "sha1-MwK32sYsXikaB4nimvcoM4f6nes=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-contains": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-5.1.5.tgz", + "integrity": "sha1-WW1jruY2961T7pn5/yTJaZSg7xQ=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/boolean-point-on-line": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-contains/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-crosses": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-5.1.5.tgz", + "integrity": "sha1-Ab+uollvFk3kpNMlCU3HwlXHFdY=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/polygon-to-line": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-crosses/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-disjoint": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-5.1.6.tgz", + "integrity": "sha512-KHvUS6SBNYHBCLIJEJrg04pF5Oy+Fqn8V5G9U+9pti5vI9tyX7Ln2g7RSB7iJ1Cxsz8QAi6OukhXjEF2/8ZpGg==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/polygon-to-line": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-disjoint/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-equal": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-5.1.5.tgz", + "integrity": "sha1-Kfj21gu4RQff12WzIlTbjnLJOKQ=", + "dependencies": { + "@turf/clean-coords": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "geojson-equality": "0.1.6" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-overlap": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-5.1.5.tgz", + "integrity": "sha1-DU5kxSx3CijpPZ7834qLg3OsznU=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/line-overlap": "^5.1.5", + "@turf/meta": "^5.1.5", + "geojson-equality": "0.1.6" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-parallel": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-5.1.5.tgz", + "integrity": "sha1-c5NYR16ltlx+GCejw+DopofTqF0=", + "dependencies": { + "@turf/clean-coords": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/rhumb-bearing": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-point-on-line": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-5.1.5.tgz", + "integrity": "sha1-9jPF/4Aq0ku48Vja269v9KAj3Xs=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-within": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-5.1.5.tgz", + "integrity": "sha1-RxBdVtB1Kp0Pv81Dw2pfkUnchpc=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/boolean-point-on-line": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/boolean-within/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/buffer": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-5.1.5.tgz", + "integrity": "sha1-hByWJ8+5dLEirE4alW8EZrwCMcQ=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/center": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/projection": "^5.1.5", + "d3-geo": "1.7.1", + "turf-jsts": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/center": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-5.1.5.tgz", + "integrity": "sha1-RKss2VT2PA03dX9xWKmcPvURS4A=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/center-mean": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-5.1.5.tgz", + "integrity": "sha1-jI6YdTkeXwnw5uePXWYbiLIQigo=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/center-median": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-5.1.5.tgz", + "integrity": "sha1-u0Yb/noqSGAdikcnaFcYcjoUqHI=", + "dependencies": { + "@turf/center-mean": "^5.1.5", + "@turf/centroid": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/center-of-mass": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-5.1.5.tgz", + "integrity": "sha1-TTvXnYhJjbq4Mk1PafAyL2Uguco=", + "dependencies": { + "@turf/centroid": "^5.1.5", + "@turf/convex": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/centroid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-5.1.5.tgz", + "integrity": "sha1-d4radCFjNQIa2P0OemWoNJ1Tx2k=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/circle": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-5.1.5.tgz", + "integrity": "sha1-mxV3g1UIq1L7HBCypQZcuiuHtqU=", + "dependencies": { + "@turf/destination": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/clean-coords": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-5.1.5.tgz", + "integrity": "sha1-EoAKmKeMmkUqcuxChJPEOs8q2h8=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/clone": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-5.1.5.tgz", + "integrity": "sha1-JT6NNUdxgZduM636tQoPAqfw42c=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/clusters": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-5.1.5.tgz", + "integrity": "sha1-ZzpeXxsZycq6vFfJCO6t1oIiTdQ=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/clusters-dbscan": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-5.1.5.tgz", + "integrity": "sha1-V4H7TmVsdHoLjpk333MYHAMJ4m8=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "density-clustering": "1.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/clusters-kmeans": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-5.1.5.tgz", + "integrity": "sha1-/W3+qLEzuovcI3CsPKzuFYejAvE=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "skmeans": "0.9.7" + } + }, + "node_modules/latlon2country/node_modules/@turf/collect": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-5.1.5.tgz", + "integrity": "sha1-/pjJqMIY7PJP/DPXApUXt8GbKj4=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "rbush": "^2.0.1" + } + }, + "node_modules/latlon2country/node_modules/@turf/collect/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/combine": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-5.1.5.tgz", + "integrity": "sha1-uxS976VVBDVxlfwaEkzX1TqMiQU=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/concave": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-5.1.5.tgz", + "integrity": "sha1-I7uqw4fQNLlldKG9cNBZI3qdIRA=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/tin": "^5.1.5", + "topojson-client": "3.x", + "topojson-server": "3.x" + } + }, + "node_modules/latlon2country/node_modules/@turf/convex": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-5.1.5.tgz", + "integrity": "sha1-Dfk3fdACIWzpghsH9wXgN9rj4B0=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5", + "concaveman": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/destination": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-5.1.5.tgz", + "integrity": "sha1-7TU4G9zoO73cvQei4rzivd/7zCY=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/difference": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-5.1.5.tgz", + "integrity": "sha1-ok1pCnvKgD8QkKnuO52Qb8Q3H0I=", + "dependencies": { + "@turf/area": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "turf-jsts": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/dissolve": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-5.1.5.tgz", + "integrity": "sha1-LPEzqQIdIWODHD16lY1lB/nYGTg=", + "dependencies": { + "@turf/boolean-overlap": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/union": "^5.1.5", + "geojson-rbush": "2.1.0", + "get-closest": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/distance": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-5.1.5.tgz", + "integrity": "sha1-Oc8YIEu/h1h9cH5gmmARiQkVZAk=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/ellipse": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-5.1.5.tgz", + "integrity": "sha1-1XyrhTmFkgzeYCKKeNgEWAJcVL4=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/rhumb-destination": "^5.1.5", + "@turf/transform-rotate": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/envelope": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-5.1.5.tgz", + "integrity": "sha1-UBMwnFP91D369LWIplw/7X28EIo=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/bbox-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/explode": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-5.1.5.tgz", + "integrity": "sha1-sSsvd0AEobSPYrqVsgocZVo94Rg=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/flatten": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-5.1.5.tgz", + "integrity": "sha1-2iknBnEz7WFpsLnWB7khVoiqE1g=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/flip": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-5.1.5.tgz", + "integrity": "sha1-Q29kOnIvDKU7n85jjkaT2zYIpoo=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/great-circle": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-5.1.5.tgz", + "integrity": "sha1-3r+2cc5HVQnLY3MBwV/PzPo1mpM=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/helpers": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", + "integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8=" + }, + "node_modules/latlon2country/node_modules/@turf/hex-grid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-5.1.5.tgz", + "integrity": "sha1-m3ul/s9QUfHoWJL3E/zlxVBQKmo=", + "dependencies": { + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/intersect": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/interpolate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-5.1.5.tgz", + "integrity": "sha1-DxLwq3VtbdEK+ykMpuh3ve8BPqo=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/centroid": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/hex-grid": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/point-grid": "^5.1.5", + "@turf/square-grid": "^5.1.5", + "@turf/triangle-grid": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/intersect": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-5.1.6.tgz", + "integrity": "sha512-KXyNv/GXdoGAOy03qZF53rgtXC2tNhF/4jLwTKiVRrBQH6kcEpipGStdJ+QkYIlarQPa8f7I9UlVAB19et4MfQ==", + "dependencies": { + "@turf/clean-coords": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/truncate": "^5.1.5", + "turf-jsts": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/invariant": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.1.5.tgz", + "integrity": "sha1-9Z9P76CSJLFdzhZR+QPIaNV6JOE=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/isobands": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-5.1.5.tgz", + "integrity": "sha1-a0TO9YTVUaMTBBh68jtKFYLj8I0=", + "dependencies": { + "@turf/area": "^5.1.5", + "@turf/bbox": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/explode": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/isobands/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/isolines": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-5.1.5.tgz", + "integrity": "sha1-irTn9Cuz38VGFOW/FVln9+VdLeE=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/kinks": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-5.1.5.tgz", + "integrity": "sha1-irtpYdm7AQchO63fLCwmQNAlaYA=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/length": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-5.1.5.tgz", + "integrity": "sha1-86X4ZMK5lqi7RxeUU1ofrxLuvvs=", + "dependencies": { + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-arc": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-5.1.5.tgz", + "integrity": "sha1-AHinRHg1oSrkFKIR+aZNEYYVDhU=", + "dependencies": { + "@turf/circle": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-chunk": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-5.1.5.tgz", + "integrity": "sha1-kQqFwFwG2dD5w4l3oF4IGNUIXEI=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/length": "^5.1.5", + "@turf/line-slice-along": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-intersect": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-5.1.5.tgz", + "integrity": "sha1-DikHGuQDKV5JFyO8SfXPrI0R3fM=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/meta": "^5.1.5", + "geojson-rbush": "2.1.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-offset": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-5.1.5.tgz", + "integrity": "sha1-KrWy8In4yRPiMdmUN4553KkLWh4=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-overlap": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-5.1.5.tgz", + "integrity": "sha1-lDxvh6A4bcQ9+sEdKz/5wRLNP2A=", + "dependencies": { + "@turf/boolean-point-on-line": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/nearest-point-on-line": "^5.1.5", + "geojson-rbush": "2.1.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-segment": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-5.1.5.tgz", + "integrity": "sha1-Mgeq7lRqskw9jcPMY/kcdwuAE+U=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-slice": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-5.1.5.tgz", + "integrity": "sha1-Hs/OFGKjeFeXVM7fRGTN4mgp8rU=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/nearest-point-on-line": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-slice-along": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-5.1.5.tgz", + "integrity": "sha1-7drQoh70efKWihG9LdcomiEy6aU=", + "dependencies": { + "@turf/bearing": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-split": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-5.1.5.tgz", + "integrity": "sha1-Wy30w3YZty73JbUWPPmSbVVArLc=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/nearest-point-on-line": "^5.1.5", + "@turf/square": "^5.1.5", + "@turf/truncate": "^5.1.5", + "geojson-rbush": "2.1.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/line-to-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-5.1.5.tgz", + "integrity": "sha1-ITz0Gmj4Ikd4ujnTGH3sPouBhlo=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/mask": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-5.1.5.tgz", + "integrity": "sha1-mrD+8aJyyY/j70kvn/thggayQtU=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/union": "^5.1.5", + "rbush": "^2.0.1" + } + }, + "node_modules/latlon2country/node_modules/@turf/meta": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-5.1.6.tgz", + "integrity": "sha1-wgqGPt7Qhp+yhUje6Ik0G8y0akY=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/midpoint": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-5.1.5.tgz", + "integrity": "sha1-4mH2srDqgSTM7/VSomLdRlydBfA=", + "dependencies": { + "@turf/bearing": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-5.1.5.tgz", + "integrity": "sha1-EgUN5Bw5hEMiTHl43g9iE5ANNPs=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-on-line": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-5.1.5.tgz", + "integrity": "sha1-VgauKX8VlHUkvqUaKp71HsG/nDY=", + "dependencies": { + "@turf/bearing": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-to-line": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-5.1.6.tgz", + "integrity": "sha512-ZSvDIEiHhifn/vNwLXZI/E8xmEz5yBPqfUR7BVHRZrB1cP7jLhKZvkbidjG//uW8Fr1Ulc+PFOXczLspIcx/lw==", + "dependencies": { + "@turf/helpers": "6.x", + "@turf/invariant": "6.x", + "@turf/meta": "6.x", + "@turf/point-to-line-distance": "^5.1.5", + "object-assign": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-to-line/node_modules/@turf/helpers": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.3.0.tgz", + "integrity": "sha512-kr6KuD4Z0GZ30tblTEvi90rvvVNlKieXuMC8CTzE/rVQb0/f/Cb29zCXxTD7giQTEQY/P2nRW23wEqqyNHulCg==" + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-to-line/node_modules/@turf/invariant": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.3.0.tgz", + "integrity": "sha512-2OFOi9p+QOrcIMySEnr+WlOiKaFZ1bY56jA98YyECewJHfhPFWUBZEhc4nWGRT0ahK08Vus9+gcuBX8QIpCIIw==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/nearest-point-to-line/node_modules/@turf/meta": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.3.0.tgz", + "integrity": "sha512-qBJjaAJS9H3ap0HlGXyF/Bzfl0qkA9suafX/jnDsZvWMfVLt+s+o6twKrXOGk5t7nnNON2NFRC8+czxpu104EQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/planepoint": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-5.1.5.tgz", + "integrity": "sha1-GLvfAG91ne9eQsagBsn53oGyt/8=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-grid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-5.1.5.tgz", + "integrity": "sha1-MFFBJI9Quv42zn5mukuX56sjaIc=", + "dependencies": { + "@turf/boolean-within": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-on-feature": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-5.1.5.tgz", + "integrity": "sha1-MMfwMkMCd8ZBjZbSieRba/shP+c=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/center": "^5.1.5", + "@turf/explode": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/nearest-point": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-on-feature/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-5.1.6.tgz", + "integrity": "sha512-PE3hiTeeDEi4ZLPtI8XAzFYW9nHo1EVsZGm/4ZVV8jo39d3X1oLVHxY3e1PkCmWwRapXy4QLqvnTQ7nU4wspNw==", + "dependencies": { + "@turf/bearing": "6.x", + "@turf/distance": "6.x", + "@turf/helpers": "6.x", + "@turf/invariant": "6.x", + "@turf/meta": "6.x", + "@turf/projection": "6.x", + "@turf/rhumb-bearing": "6.x", + "@turf/rhumb-distance": "6.x" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/bearing": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-6.3.0.tgz", + "integrity": "sha512-apuUm9xN6VQLO33m7F2mmzlm3dHfeesJjMSzh9iehGtgmp1IaVndjdcIvs0ieiwm8bN9UhwXpfPtO3pV0n9SFw==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/clone": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.3.0.tgz", + "integrity": "sha512-GAgN89/9GCqUKECB1oY2hcTs0K2rZj+a2tY6VfM0ef9wwckuQZCKi+kKGUzhKVrmHee15jKV8n6DY0er8OndKg==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.3.0.tgz", + "integrity": "sha512-basi24ssNFnH3iXPFjp/aNUrukjObiFWoIyDRqKyBJxVwVOwAWvfk4d38QQyBj5nDo5IahYRq/Q+T47/5hSs9w==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/helpers": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.3.0.tgz", + "integrity": "sha512-kr6KuD4Z0GZ30tblTEvi90rvvVNlKieXuMC8CTzE/rVQb0/f/Cb29zCXxTD7giQTEQY/P2nRW23wEqqyNHulCg==" + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/invariant": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.3.0.tgz", + "integrity": "sha512-2OFOi9p+QOrcIMySEnr+WlOiKaFZ1bY56jA98YyECewJHfhPFWUBZEhc4nWGRT0ahK08Vus9+gcuBX8QIpCIIw==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/meta": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.3.0.tgz", + "integrity": "sha512-qBJjaAJS9H3ap0HlGXyF/Bzfl0qkA9suafX/jnDsZvWMfVLt+s+o6twKrXOGk5t7nnNON2NFRC8+czxpu104EQ==", + "dependencies": { + "@turf/helpers": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/projection": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-6.3.0.tgz", + "integrity": "sha512-IpSs7Q6G6xi47ynVlYYVegPLy6Jc0yo3/DcIm83jaJa4NnzPFXIFZT0v9Fe1N8MraHZqiqaSPbVnJXCGwR12lg==", + "dependencies": { + "@turf/clone": "^6.3.0", + "@turf/helpers": "^6.3.0", + "@turf/meta": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/rhumb-bearing": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-6.3.0.tgz", + "integrity": "sha512-/c/BE3huEUrwN6gx7Bg2FzfJqeU+TWk/slQPDHpbVunlIPbS6L28brqSVD+KXfMG8HQIzynz6Pm4Y+j5Iv4aWA==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/point-to-line-distance/node_modules/@turf/rhumb-distance": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-6.3.0.tgz", + "integrity": "sha512-wMIQVvznusonnp/POeucFdA4Rubn0NrkcEMdxdcCgFK7OmTz0zU4CEnNONF2IUGkQ5WwoKiuS7MOTQ8OuCjSfQ==", + "dependencies": { + "@turf/helpers": "^6.3.0", + "@turf/invariant": "^6.3.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/points-within-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-5.1.5.tgz", + "integrity": "sha1-K4VaXfOq2lfC7oIKB1SrlJKKIzc=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/points-within-polygon/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/polygon-tangents": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-5.1.5.tgz", + "integrity": "sha1-K/AJkUcwJbF44lDcfLmuVAm71lI=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/polygon-to-line": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-5.1.5.tgz", + "integrity": "sha1-I7tEjYTcTGUZmaxhGjbZHFklA2o=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/polygonize": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-5.1.5.tgz", + "integrity": "sha1-BJP6EYefOdELmtAs5qI+lC0IqjI=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/envelope": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/polygonize/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/projection": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-5.1.5.tgz", + "integrity": "sha1-JFF+7rLzaBa6n3EueubWo2jt91c=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/random": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/random/-/random-5.1.5.tgz", + "integrity": "sha1-sy78k0Vgroulfo67UfJBw5+6Lns=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/rewind": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-5.1.5.tgz", + "integrity": "sha1-nqPbSmi3PB/R3RH1djGxQ8/vock=", + "dependencies": { + "@turf/boolean-clockwise": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/rhumb-bearing": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-5.1.5.tgz", + "integrity": "sha1-rPalAkJ+uMSeGM2mrg7/qwxd3NI=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/rhumb-destination": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-5.1.5.tgz", + "integrity": "sha1-sbKuuSFUfyrAwamUtqEw+SRjx0I=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/rhumb-distance": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-5.1.5.tgz", + "integrity": "sha1-GAaFdiX0IlOE2tQT5p85U4/192U=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/sample": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-5.1.5.tgz", + "integrity": "sha1-6ctEikeJzFbuPeLdZ4HiNDQ1tBE=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/sector": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-5.1.5.tgz", + "integrity": "sha1-rCu5TBPt1gNPb9wrZwCBNdIPXgc=", + "dependencies": { + "@turf/circle": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-arc": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/shortest-path": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-5.1.5.tgz", + "integrity": "sha1-hUroCW9rw+EwD6ynfz6PZ9j5Nas=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/bbox-polygon": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/clean-coords": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/transform-scale": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/shortest-path/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/simplify": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-5.1.5.tgz", + "integrity": "sha1-Csjyei60IYGD7dmZjDJ1q+QIuSY=", + "dependencies": { + "@turf/clean-coords": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/square": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-5.1.5.tgz", + "integrity": "sha1-qnsh5gM8ySUsOlvW89iNq9b+0YA=", + "dependencies": { + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/square-grid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-5.1.5.tgz", + "integrity": "sha1-G9X3uesU8LYLwjH+/nNR0aMvGlE=", + "dependencies": { + "@turf/boolean-contains": "^5.1.5", + "@turf/boolean-overlap": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/intersect": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/standard-deviational-ellipse": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-5.1.5.tgz", + "integrity": "sha1-hc0oO14ayljyG9ZkEuQUtW2FIyQ=", + "dependencies": { + "@turf/center-mean": "^5.1.5", + "@turf/ellipse": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/points-within-polygon": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/tag": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-5.1.5.tgz", + "integrity": "sha1-0e4aUIjs/UoUEQGcmCOczypJfSA=", + "dependencies": { + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/tag/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/tesselate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-5.1.5.tgz", + "integrity": "sha1-MqWU6cIaAEIKn5DSxD3z4RZgYc0=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "earcut": "^2.0.0" + } + }, + "node_modules/latlon2country/node_modules/@turf/tin": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-5.1.5.tgz", + "integrity": "sha1-KCI+r8X76a6azKgc3P6l0UJMkX0=", + "dependencies": { + "@turf/helpers": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/transform-rotate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-5.1.5.tgz", + "integrity": "sha1-0Jbt2eMA/jFQadVNjkWMQJIh7fs=", + "dependencies": { + "@turf/centroid": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/rhumb-bearing": "^5.1.5", + "@turf/rhumb-destination": "^5.1.5", + "@turf/rhumb-distance": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/transform-scale": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-5.1.5.tgz", + "integrity": "sha1-cP064BhWz3uunxWtVhzf6PiQAbk=", + "dependencies": { + "@turf/bbox": "^5.1.5", + "@turf/center": "^5.1.5", + "@turf/centroid": "^5.1.5", + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/rhumb-bearing": "^5.1.5", + "@turf/rhumb-destination": "^5.1.5", + "@turf/rhumb-distance": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/transform-translate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-5.1.5.tgz", + "integrity": "sha1-Uwolf7Hccmja3Ks05nkB6yo97GM=", + "dependencies": { + "@turf/clone": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/rhumb-destination": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/triangle-grid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-5.1.5.tgz", + "integrity": "sha1-ezZ2IQhVTBTyjK/zxIsc/ILI3IE=", + "dependencies": { + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/intersect": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/truncate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-5.1.5.tgz", + "integrity": "sha1-nu37Oxi6gfLJjT6tCUMcyhiErYk=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/turf": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-5.1.6.tgz", + "integrity": "sha1-wxIlkoh+0jS3VGi4qMRb+Ib7+PY=", + "dependencies": { + "@turf/along": "5.1.x", + "@turf/area": "5.1.x", + "@turf/bbox": "5.1.x", + "@turf/bbox-clip": "5.1.x", + "@turf/bbox-polygon": "5.1.x", + "@turf/bearing": "5.1.x", + "@turf/bezier-spline": "5.1.x", + "@turf/boolean-clockwise": "5.1.x", + "@turf/boolean-contains": "5.1.x", + "@turf/boolean-crosses": "5.1.x", + "@turf/boolean-disjoint": "5.1.x", + "@turf/boolean-equal": "5.1.x", + "@turf/boolean-overlap": "5.1.x", + "@turf/boolean-parallel": "5.1.x", + "@turf/boolean-point-in-polygon": "5.1.x", + "@turf/boolean-point-on-line": "5.1.x", + "@turf/boolean-within": "5.1.x", + "@turf/buffer": "5.1.x", + "@turf/center": "5.1.x", + "@turf/center-mean": "5.1.x", + "@turf/center-median": "5.1.x", + "@turf/center-of-mass": "5.1.x", + "@turf/centroid": "5.1.x", + "@turf/circle": "5.1.x", + "@turf/clean-coords": "5.1.x", + "@turf/clone": "5.1.x", + "@turf/clusters": "5.1.x", + "@turf/clusters-dbscan": "5.1.x", + "@turf/clusters-kmeans": "5.1.x", + "@turf/collect": "5.1.x", + "@turf/combine": "5.1.x", + "@turf/concave": "5.1.x", + "@turf/convex": "5.1.x", + "@turf/destination": "5.1.x", + "@turf/difference": "5.1.x", + "@turf/dissolve": "5.1.x", + "@turf/distance": "5.1.x", + "@turf/ellipse": "5.1.x", + "@turf/envelope": "5.1.x", + "@turf/explode": "5.1.x", + "@turf/flatten": "5.1.x", + "@turf/flip": "5.1.x", + "@turf/great-circle": "5.1.x", + "@turf/helpers": "5.1.x", + "@turf/hex-grid": "5.1.x", + "@turf/interpolate": "5.1.x", + "@turf/intersect": "5.1.x", + "@turf/invariant": "5.1.x", + "@turf/isobands": "5.1.x", + "@turf/isolines": "5.1.x", + "@turf/kinks": "5.1.x", + "@turf/length": "5.1.x", + "@turf/line-arc": "5.1.x", + "@turf/line-chunk": "5.1.x", + "@turf/line-intersect": "5.1.x", + "@turf/line-offset": "5.1.x", + "@turf/line-overlap": "5.1.x", + "@turf/line-segment": "5.1.x", + "@turf/line-slice": "5.1.x", + "@turf/line-slice-along": "5.1.x", + "@turf/line-split": "5.1.x", + "@turf/line-to-polygon": "5.1.x", + "@turf/mask": "5.1.x", + "@turf/meta": "5.1.x", + "@turf/midpoint": "5.1.x", + "@turf/nearest-point": "5.1.x", + "@turf/nearest-point-on-line": "5.1.x", + "@turf/nearest-point-to-line": "5.1.x", + "@turf/planepoint": "5.1.x", + "@turf/point-grid": "5.1.x", + "@turf/point-on-feature": "5.1.x", + "@turf/point-to-line-distance": "5.1.x", + "@turf/points-within-polygon": "5.1.x", + "@turf/polygon-tangents": "5.1.x", + "@turf/polygon-to-line": "5.1.x", + "@turf/polygonize": "5.1.x", + "@turf/projection": "5.1.x", + "@turf/random": "5.1.x", + "@turf/rewind": "5.1.x", + "@turf/rhumb-bearing": "5.1.x", + "@turf/rhumb-destination": "5.1.x", + "@turf/rhumb-distance": "5.1.x", + "@turf/sample": "5.1.x", + "@turf/sector": "5.1.x", + "@turf/shortest-path": "5.1.x", + "@turf/simplify": "5.1.x", + "@turf/square": "5.1.x", + "@turf/square-grid": "5.1.x", + "@turf/standard-deviational-ellipse": "5.1.x", + "@turf/tag": "5.1.x", + "@turf/tesselate": "5.1.x", + "@turf/tin": "5.1.x", + "@turf/transform-rotate": "5.1.x", + "@turf/transform-scale": "5.1.x", + "@turf/transform-translate": "5.1.x", + "@turf/triangle-grid": "5.1.x", + "@turf/truncate": "5.1.x", + "@turf/union": "5.1.x", + "@turf/unkink-polygon": "5.1.x", + "@turf/voronoi": "5.1.x" + } + }, + "node_modules/latlon2country/node_modules/@turf/turf/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/union": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/union/-/union-5.1.5.tgz", + "integrity": "sha1-UyhbYJQEf8WNlqrA6pCGXsNNRUs=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "turf-jsts": "*" + } + }, + "node_modules/latlon2country/node_modules/@turf/unkink-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-5.1.5.tgz", + "integrity": "sha1-ewGEfFD7V0riV54Z5Ey6hSbSE8M=", + "dependencies": { + "@turf/area": "^5.1.5", + "@turf/boolean-point-in-polygon": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5", + "rbush": "^2.0.1" + } + }, + "node_modules/latlon2country/node_modules/@turf/unkink-polygon/node_modules/@turf/boolean-point-in-polygon": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-5.1.5.tgz", + "integrity": "sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + } + }, + "node_modules/latlon2country/node_modules/@turf/voronoi": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-5.1.5.tgz", + "integrity": "sha1-6FbpQG3MLyXWbdyJhYTifC6/ymY=", + "dependencies": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "d3-voronoi": "1.1.2" + } + }, + "node_modules/latlon2country/node_modules/@types/node": { + "version": "14.14.44", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.44.tgz", + "integrity": "sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA==" + }, + "node_modules/latlon2country/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/latlon2country/node_modules/geojson-rbush": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-2.1.0.tgz", + "integrity": "sha1-O9c745H8ELCuaT2bis6iquC4Oo0=", + "dependencies": { + "@turf/helpers": "*", + "@turf/meta": "*", + "rbush": "*" + } + }, + "node_modules/latlon2country/node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/latlon2country/node_modules/topojson-server": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.1.tgz", + "integrity": "sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw==", + "dependencies": { + "commander": "2" + }, + "bin": { + "geo2topo": "bin/geo2topo" + } + }, + "node_modules/latlon2country/node_modules/turf": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/turf/-/turf-3.0.14.tgz", + "integrity": "sha1-6y9KgKLVg7jGSGvHtccZBGaGbCc=", + "deprecated": "This package has moved: use @turf/turf instead", + "dependencies": { + "turf-along": "^3.0.12", + "turf-area": "^3.0.12", + "turf-bbox": "^3.0.12", + "turf-bbox-polygon": "^3.0.12", + "turf-bearing": "^3.0.12", + "turf-bezier": "^3.0.12", + "turf-buffer": "^3.0.12", + "turf-center": "^3.0.12", + "turf-centroid": "^3.0.12", + "turf-circle": "^3.0.12", + "turf-collect": "^3.0.12", + "turf-combine": "^3.0.12", + "turf-concave": "^3.0.12", + "turf-convex": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-difference": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-envelope": "^3.0.12", + "turf-explode": "^3.0.12", + "turf-flip": "^3.0.12", + "turf-helpers": "^3.0.12", + "turf-hex-grid": "^3.0.12", + "turf-inside": "^3.0.12", + "turf-intersect": "^3.0.12", + "turf-isolines": "^3.0.12", + "turf-kinks": "^3.0.12", + "turf-line-distance": "^3.0.12", + "turf-line-slice": "^3.0.12", + "turf-meta": "^3.0.12", + "turf-midpoint": "^3.0.12", + "turf-nearest": "^3.0.12", + "turf-planepoint": "^3.0.12", + "turf-point-grid": "^3.0.12", + "turf-point-on-line": "^3.0.12", + "turf-point-on-surface": "^3.0.12", + "turf-random": "^3.0.12", + "turf-sample": "^3.0.12", + "turf-simplify": "^3.0.12", + "turf-square": "^3.0.12", + "turf-square-grid": "^3.0.12", + "turf-tag": "^3.0.12", + "turf-tesselate": "^3.0.12", + "turf-tin": "^3.0.12", + "turf-triangle-grid": "^3.0.12", + "turf-union": "^3.0.12", + "turf-within": "^3.0.12" + } + }, + "node_modules/leaflet": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.7.1.tgz", + "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==" + }, + "node_modules/leaflet-providers": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.12.0.tgz", + "integrity": "sha512-pU/mR4B+NbayBGCg5/88dmRq7t1EGiNPhsVGV3yqHuDn594vIwus4CiPVW0RtiKJNKg8Vf1pILAbFl0i+yk+lQ==" + }, + "node_modules/leaflet-simple-map-screenshoter": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/leaflet-simple-map-screenshoter/-/leaflet-simple-map-screenshoter-0.4.4.tgz", + "integrity": "sha512-n5r04/PxXvqPQUJH+kP+vYj1Sg231YITPwoPMmdHwe+nSB+NJtQS0emEh9BaXXIbkZxubxeWQ1mKXpJYOxCAmw==", + "dependencies": { + "dom-to-image-more": "^2.8.0", + "file-saver": "^2.0.2" + } + }, + "node_modules/leaflet.markercluster": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.0.tgz", + "integrity": "sha512-Fvf/cq4o806mJL50n+fZW9+QALDDLPvt7vuAjlD2vfnxx3srMDs2vWINJze4nKYJYRY45OC6tM/669C3pLwMCA==", + "peerDependencies": { + "leaflet": "^1.3.1" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libphonenumber": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/libphonenumber/-/libphonenumber-0.0.10.tgz", + "integrity": "sha1-54u/ZgGYnNCfpDUEdkaI1BeUFIw=", + "dependencies": { + "closure": "1.0.3", + "mocha": "^2.4.5" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.9.17", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.17.tgz", + "integrity": "sha512-ElJki901OynMg1l+evooPH1VyHrECuLqpgc12z2BkK25dFU5lUKTuMHEYV2jXxvtns/PIuJax56cBeoSK7ANow==" + }, + "node_modules/lilconfig": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", + "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lineclip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/lineclip/-/lineclip-1.1.5.tgz", + "integrity": "sha1-K/JgZ9lDVP6r+R5CdoI221YW/RM=" + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "node_modules/lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" + }, + "node_modules/lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", + "dependencies": { + "vlq": "^0.2.2" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/mangrove-reviews": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/mangrove-reviews/-/mangrove-reviews-0.1.4.tgz", + "integrity": "sha512-HRef9CXlIgxmTSBx9dikyfO8DxTYQ6baQ9AU9ZOK71lLgV7OzbW4jqnhgUfHAIsteA3scdl4QPHyKtUNgw8edg==", + "dependencies": { + "axios": "^0.19.2", + "jsonwebtoken": "^8.5.1", + "jwk-to-pem": "^2.0.3" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/marked": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz", + "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==", + "dev": true, + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 8.16.2" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "dependencies": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-source-map": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", + "integrity": "sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=", + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "dependencies": { + "mime-db": "1.47.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/mkdirp/node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "node_modules/mocha": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", + "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", + "dependencies": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.11", + "growl": "1.9.2", + "jade": "0.26.3", + "mkdirp": "0.5.1", + "supports-color": "1.2.0", + "to-iso-string": "0.0.2" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 0.8.x" + } + }, + "node_modules/mocha/node_modules/commander": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dependencies": { + "ms": "0.7.1" + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dependencies": { + "inherits": "2", + "minimatch": "0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "deprecated": "Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue", + "dependencies": { + "lru-cache": "2", + "sigmund": "~1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", + "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", + "bin": { + "supports-color": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/modern-normalize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", + "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/monotone-convex-hull-2d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz", + "integrity": "sha1-R/Xa6t88Sv03dkuqGqh4ekDu4Iw=", + "dependencies": { + "robust-orientation": "^1.1.3" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node_modules/node-abi": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.0.tgz", + "integrity": "sha512-g6bZh3YCKQRdwuO/tSZZYJAw622SjsRfJ2X0Iy4sSOHZ34/sPPdVBn8fev2tj7njzLwuqPw9uMtGsGkO5kIQvg==", + "dev": true, + "dependencies": { + "semver": "^5.4.1" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==" + }, + "node_modules/node-emoji": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", + "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "dependencies": { + "lodash.toarray": "^4.4.0" + } + }, + "node_modules/node-forge": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz", + "integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==", + "engines": { + "node": "*" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/node-libs-browser/node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/node-releases": { + "version": "1.1.71", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", + "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz", + "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.4.1.tgz", + "integrity": "sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw==" + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", + "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ohauth": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ohauth/-/ohauth-1.0.0.tgz", + "integrity": "sha1-wui/877wrkkUR5IiQ+G6cFEX3ks=", + "dependencies": { + "jshashes": "~1.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opening_hours": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/opening_hours/-/opening_hours-3.6.0.tgz", + "integrity": "sha512-ETHEchqvpZxJiLznNSdYHiGyeIMikJVfYEjMjYe0oRAxcQejilyXWWGjJBcIOXLwgU6LaATeFb6LRTgEguz0yw==", + "dependencies": { + "i18next": "^20.2.1", + "i18next-browser-languagedetector": "^6.1.0", + "suncalc": "^1.8.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-2.1.0.tgz", + "integrity": "sha512-hNNlAd3gfv/iPmsNxYoAPLvxg7HuPozww7fFonMZvL84tP6Ox5igfk5j/+a9rtJJwqMgKK+JgWsAQik5o0HTLA==", + "dependencies": { + "chalk": "^2.3.1", + "cli-cursor": "^2.1.0", + "cli-spinners": "^1.1.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^4.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "node_modules/osm-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-1.1.0.tgz", + "integrity": "sha512-e/ecarlh2N/FMfiNa/ZChUIZN8q1LqK4F0iPV4g1MmfHi3/VJ+STJ0qE7ALL3OgHR5IQc3JuI63SCDqHNGt4ew==", + "dependencies": { + "ohauth": "~1.0.0", + "resolve-url": "~0.2.1", + "store": "~2.0.4", + "xtend": "~4.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/osm-polygon-features": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/osm-polygon-features/-/osm-polygon-features-0.9.2.tgz", + "integrity": "sha1-IK5BEwxIbkmjsqPCtYoUGcSYZ3g=" + }, + "node_modules/osmtogeojson": { + "version": "3.0.0-beta.4", + "resolved": "https://registry.npmjs.org/osmtogeojson/-/osmtogeojson-3.0.0-beta.4.tgz", + "integrity": "sha512-GwNy2w5JKOplOBspagcNhCDhBRV6Du2BCvcLkaA7nX12U86Dl2Ciw9zs/VzFFTXfyZlaK+7bGCWN2SNlfn/jOA==", + "dependencies": { + "@mapbox/geojson-rewind": "0.4.0", + "concat-stream": "2.0.0", + "geojson-numeric": "0.2.1", + "htmlparser2": "3.5.1", + "JSONStream": "0.8.0", + "optimist": "~0.3.5", + "osm-polygon-features": "^0.9.1", + "tiny-osmpbf": "^0.1.0", + "xmldom": "~0.1.16" + }, + "bin": { + "osmtogeojson": "osmtogeojson" + }, + "engines": { + "node": ">=0.5" + }, + "optionalDependencies": { + "@types/geojson": "^1.0.2" + } + }, + "node_modules/osmtogeojson/node_modules/@types/geojson": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.6.tgz", + "integrity": "sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w==", + "optional": true + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "node_modules/parcel": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/parcel/-/parcel-1.12.4.tgz", + "integrity": "sha512-qfc74e2/R4pCoU6L/ZZnK9k3iDS6ir4uHea0e9th9w52eehcAGf2ido/iABq9PBXdsIOe4NSY3oUm7Khe7+S3w==", + "hasInstallScript": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/core": "^7.4.4", + "@babel/generator": "^7.4.4", + "@babel/parser": "^7.4.4", + "@babel/plugin-transform-flow-strip-types": "^7.4.4", + "@babel/plugin-transform-modules-commonjs": "^7.4.4", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/preset-env": "^7.4.4", + "@babel/runtime": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4", + "@iarna/toml": "^2.2.0", + "@parcel/fs": "^1.11.0", + "@parcel/logger": "^1.11.1", + "@parcel/utils": "^1.11.0", + "@parcel/watcher": "^1.12.1", + "@parcel/workers": "^1.11.0", + "ansi-to-html": "^0.6.4", + "babylon-walk": "^1.0.2", + "browserslist": "^4.1.0", + "chalk": "^2.1.0", + "clone": "^2.1.1", + "command-exists": "^1.2.6", + "commander": "^2.11.0", + "core-js": "^2.6.5", + "cross-spawn": "^6.0.4", + "css-modules-loader-core": "^1.1.0", + "cssnano": "^4.0.0", + "deasync": "^0.1.14", + "dotenv": "^5.0.0", + "dotenv-expand": "^5.1.0", + "envinfo": "^7.3.1", + "fast-glob": "^2.2.2", + "filesize": "^3.6.0", + "get-port": "^3.2.0", + "htmlnano": "^0.2.2", + "is-glob": "^4.0.0", + "is-url": "^1.2.2", + "js-yaml": "^3.10.0", + "json5": "^1.0.1", + "micromatch": "^3.0.4", + "mkdirp": "^0.5.1", + "node-forge": "^0.7.1", + "node-libs-browser": "^2.0.0", + "opn": "^5.1.0", + "postcss": "^7.0.11", + "postcss-value-parser": "^3.3.1", + "posthtml": "^0.11.2", + "posthtml-parser": "^0.4.0", + "posthtml-render": "^1.1.3", + "resolve": "^1.4.0", + "semver": "^5.4.1", + "serialize-to-js": "^3.0.0", + "serve-static": "^1.12.4", + "source-map": "0.6.1", + "terser": "^3.7.3", + "v8-compile-cache": "^2.0.0", + "ws": "^5.1.1" + }, + "bin": { + "parcel": "bin/cli.js" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/parcel/node_modules/@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/parcel/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/parcel/node_modules/fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dependencies": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/parcel/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/parcel/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/parcel/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parcel/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parcel/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module/node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dependencies": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-glob/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-glob/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "node_modules/physical-cpu-count": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz", + "integrity": "sha1-GN4vl+S/epVRrXURlCtUlverpmA=" + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + }, + "node_modules/point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==" + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dependencies": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dependencies": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-colormin/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-functions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", + "integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=", + "dependencies": { + "glob": "^7.1.2", + "object-assign": "^4.1.1", + "postcss": "^6.0.9", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/postcss-functions/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-functions/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", + "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "dependencies": { + "camelcase-css": "^2.0.1", + "postcss": "^7.0.18" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "dev": true, + "dependencies": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dependencies": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", + "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", + "dependencies": { + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-extract-imports/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-extract-imports/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dependencies": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-values/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-nested": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-4.2.3.tgz", + "integrity": "sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw==", + "dependencies": { + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dependencies": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz", + "integrity": "sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dependencies": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/posthtml": { + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.11.6.tgz", + "integrity": "sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw==", + "dependencies": { + "posthtml-parser": "^0.4.1", + "posthtml-render": "^1.1.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.4.2.tgz", + "integrity": "sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg==", + "dependencies": { + "htmlparser2": "^3.9.2" + } + }, + "node_modules/posthtml-parser/node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/posthtml-parser/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/posthtml-parser/node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/posthtml-parser/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/posthtml-render": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-1.3.1.tgz", + "integrity": "sha512-eSToKjNLu0FiF76SSGMHjOFXYzAc/CJqi677Sq6hYvcvFCBtD6de/W5l+0IYPf7ypscqAfjCttxvTdMJt5Gj8Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.3.tgz", + "integrity": "sha512-iqqSR84tNYQUQHRXalSKdIaM8Ov1QxOVuBNWI7+BzZWv6Ih9k75wOnH1rGQ9WWTaaLkTpxWKIciOF0KyfM74+Q==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/prompt-sync/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompt-sync/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/protocol-buffers-schema": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.5.1.tgz", + "integrity": "sha512-YVCvdhxWNDP8/nJDyXLuM+UFsuPk4+1PB7WGPVDzm3HTHbzFLxQYeW2iZpS4mmnXrQJGBzt230t/BbEb7PrQaw==" + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/purgecss": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-3.1.3.tgz", + "integrity": "sha512-hRSLN9mguJ2lzlIQtW4qmPS2kh6oMnA9RxdIYK8sz18QYqd6ePp4GNDl18oWHA1f2v2NEQIh51CO8s/E3YGckQ==", + "dependencies": { + "commander": "^6.0.0", + "glob": "^7.0.0", + "postcss": "^8.2.1", + "postcss-selector-parser": "^6.0.2" + }, + "bin": { + "purgecss": "bin/purgecss.js" + } + }, + "node_modules/purgecss/node_modules/postcss": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.14.tgz", + "integrity": "sha512-+jD0ZijcvyCqPQo/m/CW0UcARpdFylq04of+Q7RKX6f/Tu+dvpUI/9Sp81+i6/vJThnOBX09Quw0ZLOVwpzX3w==", + "dependencies": { + "colorette": "^1.2.2", + "nanoid": "^3.1.22", + "source-map": "^0.6.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/purgecss/node_modules/postcss/node_modules/nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/purgecss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quickselect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==" + }, + "node_modules/quote-stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", + "integrity": "sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=", + "dependencies": { + "buffer-equal": "0.0.1", + "minimist": "^1.1.3", + "through2": "^2.0.0" + }, + "bin": { + "quote-stream": "bin/cmd.js" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rbush": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz", + "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==", + "dependencies": { + "quickselect": "^1.0.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/read-file": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/read-file/-/read-file-0.2.0.tgz", + "integrity": "sha1-cMa6+IQux9FUD5gf0Oau1Mgb1UU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "dependencies": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redeyed": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-0.4.4.tgz", + "integrity": "sha1-N+mQpvKyGyoRwuakj9QTVpjLqX8=", + "dependencies": { + "esprima": "~1.0.4" + } + }, + "node_modules/reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dependencies": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/reduce-css-calc/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dependencies": { + "regenerate": "^1.4.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "dependencies": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + }, + "node_modules/regjsparser": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", + "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated" + }, + "node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" + }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha1-1lBezbMEplldom+ktDMHMGd1lF0=", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/robust-orientation": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/robust-orientation/-/robust-orientation-1.1.3.tgz", + "integrity": "sha1-2v9bANO+TmByLw6cAVbvln8cIEk=", + "dependencies": { + "robust-scale": "^1.0.2", + "robust-subtract": "^1.0.0", + "robust-sum": "^1.0.0", + "two-product": "^1.0.2" + } + }, + "node_modules/robust-predicates": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz", + "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==" + }, + "node_modules/robust-scale": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/robust-scale/-/robust-scale-1.0.2.tgz", + "integrity": "sha1-d1Ey7QlULQKOWLLMecBikLz3jDI=", + "dependencies": { + "two-product": "^1.0.2", + "two-sum": "^1.0.0" + } + }, + "node_modules/robust-subtract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-subtract/-/robust-subtract-1.0.0.tgz", + "integrity": "sha1-4LFk4e2LpOOl3aRaEgODSNvtPpo=" + }, + "node_modules/robust-sum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-sum/-/robust-sum-1.0.0.tgz", + "integrity": "sha1-FmRuUlKStNJdgnV6KGlV4Lv6U9k=" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dependencies": { + "xmlchars": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serialize-to-js": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-3.1.1.tgz", + "integrity": "sha512-F+NGU0UHMBO4Q965tjw7rvieNVjlH6Lqi2emq/Lc9LUURYJbiCzmpi4Cy1OOjjVPtxu0c+NE85LU6968Wko5ZA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" + }, + "node_modules/sharkdown": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sharkdown/-/sharkdown-0.1.1.tgz", + "integrity": "sha512-exwooSpmo5s45lrexgz6Q0rFQM574wYIX3iDZ7RLLqOb7IAoQZu9nxlZODU972g19sR69OIpKP2cpHTzU+PHIg==", + "dependencies": { + "cardinal": "~0.4.2", + "minimist": "0.0.5", + "split": "~0.2.10" + }, + "bin": { + "sharkdown": "sharkdown" + } + }, + "node_modules/sharkdown/node_modules/minimist": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", + "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=" + }, + "node_modules/sharp": { + "version": "0.28.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.28.3.tgz", + "integrity": "sha512-21GEP45Rmr7q2qcmdnjDkNP04Ooh5v0laGS5FDpojOO84D1DJwUijLiSq8XNNM6e8aGXYtoYRh3sVNdm8NodMA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "color": "^3.1.3", + "detect-libc": "^1.0.3", + "node-addon-api": "^3.2.0", + "prebuild-install": "^6.1.2", + "semver": "^7.3.5", + "simple-get": "^3.1.0", + "tar-fs": "^2.1.1", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/sharp/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "dev": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simplicial-complex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simplicial-complex/-/simplicial-complex-1.0.0.tgz", + "integrity": "sha1-bDOk7Wn81Nkbe8rdOzC2NoPq4kE=", + "dependencies": { + "bit-twiddle": "^1.0.0", + "union-find": "^1.0.0" + } + }, + "node_modules/simplify-js": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/simplify-js/-/simplify-js-1.2.4.tgz", + "integrity": "sha512-vITfSlwt7h/oyrU42R83mtzFpwYk3+mkH9bOHqq/Qw6n8rtR7aE3NZQ5fbcyCUVVmuMJR6ynsAhOfK2qoah8Jg==" + }, + "node_modules/skmeans": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", + "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg==" + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, + "node_modules/split": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", + "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "node_modules/srcset": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-3.0.1.tgz", + "integrity": "sha512-MM8wDGg5BQJEj94tDrZDrX9wrC439/Eoeg3sgmVLPMjHgrAFeXAKk3tmFlCbKw5k+yOEhPXRpPlRcisQmqWVSQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "node_modules/stackblur-canvas": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz", + "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, + "node_modules/static-eval": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.0.tgz", + "integrity": "sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw==", + "dependencies": { + "escodegen": "^1.11.1" + } + }, + "node_modules/static-eval/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/static-eval/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/static-eval/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-module": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/static-module/-/static-module-2.2.5.tgz", + "integrity": "sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ==", + "dependencies": { + "concat-stream": "~1.6.0", + "convert-source-map": "^1.5.1", + "duplexer2": "~0.1.4", + "escodegen": "~1.9.0", + "falafel": "^2.1.0", + "has": "^1.0.1", + "magic-string": "^0.22.4", + "merge-source-map": "1.0.4", + "object-inspect": "~1.4.0", + "quote-stream": "~1.0.2", + "readable-stream": "~2.3.3", + "shallow-copy": "~0.0.1", + "static-eval": "^2.0.0", + "through2": "~2.0.3" + } + }, + "node_modules/static-module/node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/store": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz", + "integrity": "sha1-jFNOKguDH3K3X8XxEZhXxE711ZM=", + "engines": { + "node": "*" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/stylehacks/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/suncalc": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.8.0.tgz", + "integrity": "sha1-HZiYEJVjB4dQ9JlKlZ5lTYdqy/U=" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svg-pathdata": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-5.0.5.tgz", + "integrity": "sha512-TAAvLNSE3fEhyl/Da19JWfMAdhSXTYeviXsLSoDT1UM76ADj5ndwAPX1FKQEgB/gFMPavOy6tOqfalXKUiXrow==", + "optional": true, + "engines": { + "node": ">=6.9.5" + } + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/tailwindcss": { + "name": "@tailwindcss/postcss7-compat", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.7.tgz", + "integrity": "sha512-1QkWUEeLV1AoNipMCE6IlL7XYScGb+DAzaXy35ooMDvl0G8kCMHBNqGxyVAnTcK8gyJNUzkKXExkUnbjAndd/g==", + "dev": true, + "dependencies": { + "arg": "^5.0.0", + "autoprefixer": "^9", + "bytes": "^3.0.0", + "chalk": "^4.1.1", + "chokidar": "^3.5.2", + "color": "^3.2.0", + "cosmiconfig": "^7.0.0", + "detective": "^5.2.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.7", + "fs-extra": "^10.0.0", + "glob-parent": "^6.0.0", + "html-tags": "^3.1.0", + "is-glob": "^4.0.1", + "lodash": "^4.17.21", + "lodash.topath": "^4.5.2", + "modern-normalize": "^1.1.0", + "node-emoji": "^1.8.1", + "normalize-path": "^3.0.0", + "object-hash": "^2.2.0", + "postcss": "^7", + "postcss-functions": "^3", + "postcss-js": "^2", + "postcss-load-config": "^3.1.0", + "postcss-nested": "^4", + "postcss-selector-parser": "^6.0.6", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "purgecss": "^4.0.3", + "quick-lru": "^5.1.1", + "reduce-css-calc": "^2.1.8", + "resolve": "^1.20.0", + "tmp": "^0.2.1" + } + }, + "node_modules/tailwindcss/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tailwindcss/node_modules/arg": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.0.tgz", + "integrity": "sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ==", + "dev": true + }, + "node_modules/tailwindcss/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tailwindcss/node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/tailwindcss/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tailwindcss/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/tailwindcss/node_modules/color-string": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/tailwindcss/node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/tailwindcss/node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/tailwindcss/node_modules/cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tailwindcss/node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/tailwindcss/node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss/node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.1.tgz", + "integrity": "sha512-kEVjS71mQazDBHKcsq4E9u/vUzaLcw1A8EtUeydawvIWQCJM0qQ08G1H7/XTjFUulla6XQiDOG6MXSaG0HDKog==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss/node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tailwindcss/node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tailwindcss/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tailwindcss/node_modules/purgecss": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz", + "integrity": "sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==", + "dev": true, + "dependencies": { + "commander": "^6.0.0", + "glob": "^7.0.0", + "postcss": "^8.2.1", + "postcss-selector-parser": "^6.0.2" + }, + "bin": { + "purgecss": "bin/purgecss.js" + } + }, + "node_modules/tailwindcss/node_modules/purgecss/node_modules/postcss": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", + "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", + "dev": true, + "dependencies": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/tailwindcss/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tailwindcss/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/terser": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", + "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", + "dependencies": { + "commander": "^2.19.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.10" + }, + "bin": { + "terser": "bin/uglifyjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, + "node_modules/tiny-osmpbf": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tiny-osmpbf/-/tiny-osmpbf-0.1.0.tgz", + "integrity": "sha1-ColXFxE+vmquNjxL5e76js2vuSc=", + "dependencies": { + "pbf": "^3.0.4", + "tiny-inflate": "^1.0.2" + } + }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-iso-string": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", + "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", + "deprecated": "to-iso-string has been deprecated, use @segment/to-iso-string instead." + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/topojson": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/topojson/-/topojson-3.0.2.tgz", + "integrity": "sha512-u3zeuL6WEVL0dmsRn7uHZKc4Ao4gpW3sORUv+N3ezLTvY3JdCuyg0hvpWiIfFw8p/JwVN++SvAsFgcFEeR15rQ==", + "deprecated": "Use topojson-client, topojson-server or topojson-simplify directly.", + "dependencies": { + "topojson-client": "3.0.0", + "topojson-server": "3.0.0", + "topojson-simplify": "3.0.2" + }, + "bin": { + "geo2topo": "node_modules/topojson-server/bin/geo2topo", + "topo2geo": "node_modules/topojson-client/bin/topo2geo", + "topomerge": "node_modules/topojson-client/bin/topomerge", + "topoquantize": "node_modules/topojson-client/bin/topoquantize", + "toposimplify": "node_modules/topojson-simplify/bin/toposimplify" + } + }, + "node_modules/topojson/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/topojson/node_modules/topojson-client": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.0.0.tgz", + "integrity": "sha1-H5kpOnfvQqRI0DKoGqmCtz82DS8=", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/topojson/node_modules/topojson-server": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.0.tgz", + "integrity": "sha1-N4546Hw5cqe1vixdYENptrrmnF4=", + "dependencies": { + "commander": "2" + }, + "bin": { + "geo2topo": "bin/geo2topo" + } + }, + "node_modules/topojson/node_modules/topojson-simplify": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/topojson-simplify/-/topojson-simplify-3.0.2.tgz", + "integrity": "sha512-gyYSVRt4jO/0RJXKZQPzTDQRWV+D/nOfiljNUv0HBXslFLtq3yxRHrl7jbrjdbda5Ytdr7M8BZUI4OxU7tnbRQ==", + "dependencies": { + "commander": "2", + "topojson-client": "3" + }, + "bin": { + "toposimplify": "bin/toposimplify" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dependencies": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, + "node_modules/ts-node-dev": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.6.tgz", + "integrity": "sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.1", + "dateformat": "~1.0.4-1.2.3", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^9.0.0", + "tsconfig": "^7.0.0" + }, + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/ts-node-dev/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "node_modules/tsconfig/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + }, + "peerDependencies": { + "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" + } + }, + "node_modules/tslint-no-circular-imports": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/tslint-no-circular-imports/-/tslint-no-circular-imports-0.7.0.tgz", + "integrity": "sha512-k3wxpeMC4ef40UbpfBVHEHIzKfNZq5/SCtAO1YjGsaNTklo+K53/TWLrym+poA65RJFDiYgYNWvkeIIkJNA0Vw==", + "dev": true, + "peerDependencies": { + "tslint": ">=5.0.0", + "typescript": ">=2.1.0" + } + }, + "node_modules/tslint/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/tslint/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/tslint/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dependencies": { + "tslib": "^1.8.1" + }, + "peerDependencies": { + "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/turf-along": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-along/-/turf-along-3.0.12.tgz", + "integrity": "sha1-5iK956S9E4wJZH1LFKoOpwBIXeY=", + "deprecated": "Turf packages are now namespaced: please use @turf/along instead", + "dependencies": { + "turf-bearing": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-area": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-area/-/turf-area-3.0.12.tgz", + "integrity": "sha1-m35Gnvn7VY/RR7sMIUgjJjvb8Tw=", + "deprecated": "Turf packages are now namespaced: please use @turf/area instead", + "dependencies": { + "geojson-area": "^0.2.1" + } + }, + "node_modules/turf-bbox": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-bbox/-/turf-bbox-3.0.12.tgz", + "integrity": "sha1-P6BhF8hEOGDsgKxg/V0vEyC/sb4=", + "deprecated": "Turf packages are now namespaced: please use @turf/bbox instead", + "dependencies": { + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-bbox-polygon": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-bbox-polygon/-/turf-bbox-polygon-3.0.12.tgz", + "integrity": "sha1-Mw3AuzgyLWFUXflmzmyA9oWs9PI=", + "deprecated": "Turf packages are now namespaced: please use @turf/bbox-polygon instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-bearing": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-bearing/-/turf-bearing-3.0.12.tgz", + "integrity": "sha1-ZfYJ3YUOc2THdxqm3th7DhkX/SA=", + "deprecated": "Turf packages are now namespaced: please use @turf/bearing instead", + "dependencies": { + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-bezier": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-bezier/-/turf-bezier-3.0.12.tgz", + "integrity": "sha1-EC791KY7Jl7pyMFydjGSCzb03QI=", + "deprecated": "Turf packages are now namespaced: please use @turf/bezier instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-buffer": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-buffer/-/turf-buffer-3.0.12.tgz", + "integrity": "sha1-IIQP58aqZ7JL4cq3/8xagv1r2XE=", + "deprecated": "Turf packages are now namespaced: please use @turf/buffer instead", + "dependencies": { + "geojson-normalize": "0.0.0", + "jsts": "1.1.2", + "turf-combine": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-center": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-center/-/turf-center-3.0.12.tgz", + "integrity": "sha1-Rd1sFym7hnKR4+AC6cdQb4xEAZY=", + "deprecated": "Turf packages are now namespaced: please use @turf/center instead", + "dependencies": { + "turf-bbox": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-centroid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-centroid/-/turf-centroid-3.0.12.tgz", + "integrity": "sha1-6u4NaYIEtX/DOZS7G8hnuNopP48=", + "deprecated": "Turf packages are now namespaced: please use @turf/centroid instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-circle": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-circle/-/turf-circle-3.0.12.tgz", + "integrity": "sha1-FAshy0lQ8tPLxw0t8BKTaGf1iTA=", + "deprecated": "Turf packages are now namespaced: please use @turf/circle instead", + "dependencies": { + "turf-destination": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-collect": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-collect/-/turf-collect-3.0.12.tgz", + "integrity": "sha1-bphtGnB9oxnMg+cjjQvN8Zqjx/I=", + "deprecated": "Turf packages are now namespaced: please use @turf/collect instead", + "dependencies": { + "turf-inside": "^3.0.12" + } + }, + "node_modules/turf-combine": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-combine/-/turf-combine-3.0.12.tgz", + "integrity": "sha1-FnB0bw/c4NHqiqain/5UONRGz3M=", + "deprecated": "Turf packages are now namespaced: please use @turf/combine instead", + "dependencies": { + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-concave": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-concave/-/turf-concave-3.0.12.tgz", + "integrity": "sha1-/KtgVpZbCoMZ9s2AJgEJXy/TqOs=", + "deprecated": "Turf packages are now namespaced: please use @turf/concave instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-meta": "^3.0.12", + "turf-tin": "^3.0.12", + "turf-union": "^3.0.12" + } + }, + "node_modules/turf-convex": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-convex/-/turf-convex-3.0.12.tgz", + "integrity": "sha1-qI3cPiLRy2WHlqnIXTraO9Pso1c=", + "deprecated": "Turf packages are now namespaced: please use @turf/convex instead", + "dependencies": { + "convex-hull": "^1.0.3", + "turf-helpers": "^3.0.12", + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-destination": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-destination/-/turf-destination-3.0.12.tgz", + "integrity": "sha1-fdb7+X6G+DGibIPvLVovjR2KbeI=", + "deprecated": "Turf packages are now namespaced: please use @turf/destination instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-difference": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-difference/-/turf-difference-3.0.12.tgz", + "integrity": "sha1-nD0NdjBCEAW4slt/Bo7Z77S8bqc=", + "deprecated": "Turf packages are now namespaced: please use @turf/difference instead", + "dependencies": { + "jsts": "1.1.2", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-distance": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-distance/-/turf-distance-3.0.12.tgz", + "integrity": "sha1-+5e4cF+s2ZOxReAUtBhiYQ7spEk=", + "deprecated": "Turf packages are now namespaced: please use @turf/distance instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-envelope": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-envelope/-/turf-envelope-3.0.12.tgz", + "integrity": "sha1-lpIdJ4zIxmRpLjIOJUO5FAgNeGs=", + "deprecated": "Turf packages are now namespaced: please use @turf/envelope instead", + "dependencies": { + "turf-bbox": "^3.0.12", + "turf-bbox-polygon": "^3.0.12" + } + }, + "node_modules/turf-explode": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-explode/-/turf-explode-3.0.12.tgz", + "integrity": "sha1-xa4owoTNAGxWUR7H1AjEilQU7P4=", + "deprecated": "Turf packages are now namespaced: please use @turf/explode instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-flip": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-flip/-/turf-flip-3.0.12.tgz", + "integrity": "sha1-3rhoF3uf87sxDF1BqqxhqRVqPLs=", + "deprecated": "Turf packages are now namespaced: please use @turf/flip instead", + "dependencies": { + "turf-meta": "^3.0.12" + } + }, + "node_modules/turf-grid": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/turf-grid/-/turf-grid-1.0.1.tgz", + "integrity": "sha1-uQSrxWS5ObYnpmrBXrFuBTgpuA8=", + "dependencies": { + "turf-point": "^2.0.0" + } + }, + "node_modules/turf-helpers": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-helpers/-/turf-helpers-3.0.12.tgz", + "integrity": "sha1-3UJy50s618lu7LeuXFf+jspUS3s=", + "deprecated": "Turf packages are now namespaced: please use @turf/helpers instead" + }, + "node_modules/turf-hex-grid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-hex-grid/-/turf-hex-grid-3.0.12.tgz", + "integrity": "sha1-BpjvZpAguzHY6cwgVtCr/K/ITo8=", + "deprecated": "Turf packages are now namespaced: please use @turf/hex-grid instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-inside": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-inside/-/turf-inside-3.0.12.tgz", + "integrity": "sha1-m6QPpu7WO+x+fYiqZCdiLE3wcGY=", + "deprecated": "Turf packages are now namespaced: please use @turf/inside instead", + "dependencies": { + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-intersect": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-intersect/-/turf-intersect-3.0.12.tgz", + "integrity": "sha1-wNf7MFhDoZJ1ZwBXo50mixeDDYM=", + "deprecated": "Turf packages are now namespaced: please use @turf/intersect instead", + "dependencies": { + "jsts": "1.1.2" + } + }, + "node_modules/turf-invariant": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-invariant/-/turf-invariant-3.0.12.tgz", + "integrity": "sha1-O5UlOVOZHr2WLdNdT2cEwofejr4=", + "deprecated": "Turf packages are now namespaced: please use @turf/invariant instead" + }, + "node_modules/turf-isolines": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-isolines/-/turf-isolines-3.0.12.tgz", + "integrity": "sha1-ALIz3+LuvU7LR6lPySPG7OyJx6s=", + "deprecated": "Turf packages are now namespaced: please use @turf/isolines instead", + "dependencies": { + "turf-bbox": "^3.0.12", + "turf-grid": "1.0.1", + "turf-helpers": "^3.0.12", + "turf-inside": "^3.0.12", + "turf-planepoint": "^3.0.12", + "turf-square": "^3.0.12", + "turf-tin": "^3.0.12" + } + }, + "node_modules/turf-jsts": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/turf-jsts/-/turf-jsts-1.2.3.tgz", + "integrity": "sha512-Ja03QIJlPuHt4IQ2FfGex4F4JAr8m3jpaHbFbQrgwr7s7L6U8ocrHiF3J1+wf9jzhGKxvDeaCAnGDot8OjGFyA==" + }, + "node_modules/turf-kinks": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-kinks/-/turf-kinks-3.0.12.tgz", + "integrity": "sha1-6cmo26VyTZjyNQ/FveugaewzN1U=", + "deprecated": "Turf packages are now namespaced: please use @turf/kinks instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-line-distance": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-line-distance/-/turf-line-distance-3.0.12.tgz", + "integrity": "sha1-cQj1smkH97jC3Rs5l4Zt06YOj18=", + "deprecated": "Turf packages are now namespaced: please use @turf/line-distance instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-line-slice": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-line-slice/-/turf-line-slice-3.0.12.tgz", + "integrity": "sha1-9fGszJKtrmnqGsCynwdSmijd6RY=", + "deprecated": "Turf packages are now namespaced: please use @turf/line-slice instead", + "dependencies": { + "turf-bearing": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12", + "turf-point-on-line": "^3.0.12" + } + }, + "node_modules/turf-meta": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-meta/-/turf-meta-3.0.12.tgz", + "integrity": "sha1-CqmhyvgrKloI1U4IMLW1o/oOijg=", + "deprecated": "Turf packages are now namespaced: please use @turf/meta instead" + }, + "node_modules/turf-midpoint": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-midpoint/-/turf-midpoint-3.0.12.tgz", + "integrity": "sha1-sSdlroms3uhVb9XibJxfoEGgLL4=", + "deprecated": "Turf packages are now namespaced: please use @turf/midpoint instead", + "dependencies": { + "turf-bearing": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-invariant": "^3.0.12" + } + }, + "node_modules/turf-nearest": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-nearest/-/turf-nearest-3.0.12.tgz", + "integrity": "sha1-cAIH9EQ/BQlvhs0kb5KfFw369G0=", + "deprecated": "Turf packages are now namespaced: please use @turf/nearest instead", + "dependencies": { + "turf-distance": "^3.0.12" + } + }, + "node_modules/turf-planepoint": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-planepoint/-/turf-planepoint-3.0.12.tgz", + "integrity": "sha1-LDeuDxf8sw22448NWe5sDdbKqa8=", + "deprecated": "Turf packages are now namespaced: please use @turf/planepoint instead" + }, + "node_modules/turf-point": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/turf-point/-/turf-point-2.0.1.tgz", + "integrity": "sha1-otzDCi0g9Ez1xicd97riwOIUYGk=", + "dependencies": { + "minimist": "^1.1.0" + }, + "bin": { + "turf-point": "bin/point.js" + } + }, + "node_modules/turf-point-grid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-point-grid/-/turf-point-grid-3.0.12.tgz", + "integrity": "sha1-1gSXi+ELyeUzBq4CzvcJhDHbSXE=", + "deprecated": "Turf packages are now namespaced: please use @turf/point-grid instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-point-on-line": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-point-on-line/-/turf-point-on-line-3.0.12.tgz", + "integrity": "sha1-HYZjNU5wNy2xhj5iU+kEDEcSew8=", + "deprecated": "Turf packages are now namespaced: please use @turf/point-on-line instead", + "dependencies": { + "turf-bearing": "^3.0.12", + "turf-destination": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-point-on-surface": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-point-on-surface/-/turf-point-on-surface-3.0.12.tgz", + "integrity": "sha1-m+UFtrC6eOmFZQAd47OkJnEVJAo=", + "deprecated": "Turf packages are now namespaced: please use @turf/point-on-surface instead", + "dependencies": { + "turf-center": "^3.0.12", + "turf-distance": "^3.0.12", + "turf-explode": "^3.0.12", + "turf-helpers": "^3.0.12", + "turf-inside": "^3.0.12" + } + }, + "node_modules/turf-random": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-random/-/turf-random-3.0.12.tgz", + "integrity": "sha1-NNuxQcPx6urhQk/Wxeq6H2+5seg=", + "deprecated": "Turf packages are now namespaced: please use @turf/random instead", + "dependencies": { + "geojson-random": "^0.2.2" + } + }, + "node_modules/turf-sample": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-sample/-/turf-sample-3.0.12.tgz", + "integrity": "sha1-eUn4YgYSBH4TFMHO2H6ZwUJGPNI=", + "deprecated": "Turf packages are now namespaced: please use @turf/sample instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-simplify": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-simplify/-/turf-simplify-3.0.12.tgz", + "integrity": "sha1-heRDyLRqordSY4lETHOB2qKtGec=", + "deprecated": "Turf packages are now namespaced: please use @turf/simplify instead", + "dependencies": { + "simplify-js": "^1.2.1" + } + }, + "node_modules/turf-square": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-square/-/turf-square-3.0.12.tgz", + "integrity": "sha1-Gjix4PsF/+D8qkMYji83lCpRW2Q=", + "deprecated": "Turf packages are now namespaced: please use @turf/square instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-square-grid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-square-grid/-/turf-square-grid-3.0.12.tgz", + "integrity": "sha1-PB2ArBRVbGgTtHi9oBJRLtS5Psg=", + "deprecated": "Turf packages are now namespaced: please use @turf/square-grid instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-tag": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-tag/-/turf-tag-3.0.12.tgz", + "integrity": "sha1-IoT/8Oih6Son1Kx/10cbPEjd0ag=", + "deprecated": "Turf packages are now namespaced: please use @turf/tag instead", + "dependencies": { + "turf-inside": "^3.0.12" + } + }, + "node_modules/turf-tesselate": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-tesselate/-/turf-tesselate-3.0.12.tgz", + "integrity": "sha1-QUdLe1s4ILzyc/tx4YlNjDzUDTU=", + "deprecated": "Turf packages are now namespaced: please use @turf/tesselate instead", + "dependencies": { + "earcut": "^2.0.0", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-tin": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-tin/-/turf-tin-3.0.12.tgz", + "integrity": "sha1-tlNGRHY6zhyd8kHJWNI4SFUlc4U=", + "deprecated": "Turf packages are now namespaced: please use @turf/tin instead", + "dependencies": { + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-triangle-grid": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-triangle-grid/-/turf-triangle-grid-3.0.12.tgz", + "integrity": "sha1-gGR+V9r+CTRoeaKaGKDmKUrPEVk=", + "deprecated": "Turf packages are now namespaced: please use @turf/triangle-grid instead", + "dependencies": { + "turf-distance": "^3.0.12", + "turf-helpers": "^3.0.12" + } + }, + "node_modules/turf-union": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-union/-/turf-union-3.0.12.tgz", + "integrity": "sha1-3+0OVUC4woVeSZTBRiHjpgyCnI4=", + "deprecated": "Turf packages are now namespaced: please use @turf/union instead", + "dependencies": { + "jsts": "1.1.2" + } + }, + "node_modules/turf-within": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/turf-within/-/turf-within-3.0.12.tgz", + "integrity": "sha1-937q83cjhWG3+xM4526dEph0H5Q=", + "deprecated": "Turf packages are now namespaced: please use @turf/within instead", + "dependencies": { + "turf-helpers": "^3.0.12", + "turf-inside": "^3.0.12" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "node_modules/two-product": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/two-product/-/two-product-1.0.2.tgz", + "integrity": "sha1-Z9ldSyV6kh4stL16+VEfkIhSLqo=" + }, + "node_modules/two-sum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/two-sum/-/two-sum-1.0.0.tgz", + "integrity": "sha1-MdPzIjnk9zHsqd+RVeKyl/AIq2Q=" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typescript": { + "version": "3.9.9", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", + "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uncss": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/uncss/-/uncss-0.17.3.tgz", + "integrity": "sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog==", + "dependencies": { + "commander": "^2.20.0", + "glob": "^7.1.4", + "is-absolute-url": "^3.0.1", + "is-html": "^1.1.0", + "jsdom": "^14.1.0", + "lodash": "^4.17.15", + "postcss": "^7.0.17", + "postcss-selector-parser": "6.0.2", + "request": "^2.88.0" + }, + "bin": { + "uncss": "bin/uncss" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/uncss/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/uncss/node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/uncss/node_modules/postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "dependencies": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" + }, + "node_modules/underscore.deep": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/underscore.deep/-/underscore.deep-0.5.1.tgz", + "integrity": "sha1-ByZx9I1oc1w0Ij/P72PmnlJ2zCs=", + "engines": { + "node": ">=0.10.x" + }, + "peerDependencies": { + "underscore": "1.x" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-trie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-0.3.1.tgz", + "integrity": "sha1-1nHd3YkQGgi6w3tqUWEBBgIFIIU=", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/union-find": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/union-find/-/union-find-1.0.2.tgz", + "integrity": "sha1-KSusQV5q06iVNdI3AQ20pTYoTlg=" + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated" + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vlq": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", + "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==" + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "dependencies": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/wgs84": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/wgs84/-/wgs84-0.0.0.tgz", + "integrity": "sha1-NP3FVZF7blfPKigu0ENxDASc3HY=" + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/write-file/-/write-file-1.0.0.tgz", + "integrity": "sha1-Xi2sf9QMYxZIkfoN8GC4EEsFgGk=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.4", + "mkdirp": "^0.5.1" + } + }, + "node_modules/ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xmldom": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", + "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==", + "deprecated": "Deprecated due to CVE-2021-21366 resolved in 0.5.0", + "engines": { + "node": ">=0.1" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.12.13", @@ -2763,22 +18283,6 @@ "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, - "JSONStream": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.0.tgz", - "integrity": "sha1-78Ri1aW8lOwAf0siVxrNf28q4BM=", - "requires": { - "jsonparse": "0.0.5", - "through": "~2.2.7" - }, - "dependencies": { - "through": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", - "integrity": "sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0=" - } - } - }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -6410,6 +21914,22 @@ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=" }, + "JSONStream": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.0.tgz", + "integrity": "sha1-78Ri1aW8lOwAf0siVxrNf28q4BM=", + "requires": { + "jsonparse": "0.0.5", + "through": "~2.2.7" + }, + "dependencies": { + "through": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz", + "integrity": "sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0=" + } + } + }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -8052,7 +23572,8 @@ "leaflet.markercluster": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.0.tgz", - "integrity": "sha512-Fvf/cq4o806mJL50n+fZW9+QALDDLPvt7vuAjlD2vfnxx3srMDs2vWINJze4nKYJYRY45OC6tM/669C3pLwMCA==" + "integrity": "sha512-Fvf/cq4o806mJL50n+fZW9+QALDDLPvt7vuAjlD2vfnxx3srMDs2vWINJze4nKYJYRY45OC6tM/669C3pLwMCA==", + "requires": {} }, "levn": { "version": "0.3.0", @@ -8954,10 +24475,10 @@ "requires": { "@mapbox/geojson-rewind": "0.4.0", "@types/geojson": "^1.0.2", - "JSONStream": "0.8.0", "concat-stream": "2.0.0", "geojson-numeric": "0.2.1", "htmlparser2": "3.5.1", + "JSONStream": "0.8.0", "optimist": "~0.3.5", "osm-polygon-features": "^0.9.1", "tiny-osmpbf": "^0.1.0", @@ -11305,6 +26826,21 @@ "xtend": "^4.0.0" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -11351,21 +26887,6 @@ "define-properties": "^1.1.3" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -12137,7 +27658,8 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/tslint-no-circular-imports/-/tslint-no-circular-imports-0.7.0.tgz", "integrity": "sha512-k3wxpeMC4ef40UbpfBVHEHIzKfNZq5/SCtAO1YjGsaNTklo+K53/TWLrym+poA65RJFDiYgYNWvkeIIkJNA0Vw==", - "dev": true + "dev": true, + "requires": {} }, "tsutils": { "version": "2.29.0", @@ -12623,8 +28145,7 @@ "typescript": { "version": "3.9.9", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", - "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", - "dev": true + "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==" }, "unbox-primitive": { "version": "1.0.1", @@ -12683,7 +28204,8 @@ "underscore.deep": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/underscore.deep/-/underscore.deep-0.5.1.tgz", - "integrity": "sha1-ByZx9I1oc1w0Ij/P72PmnlJ2zCs=" + "integrity": "sha1-ByZx9I1oc1w0Ij/P72PmnlJ2zCs=", + "requires": {} }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", From 80dd69a301214acbd9241bb50c5fb1dd2931ad5c Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Wed, 22 Sep 2021 19:53:34 +0200 Subject: [PATCH 028/110] Remove unused .space-between Class was not used but there is also Tailwind .justify-between --- index.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/index.css b/index.css index bf49576ee..377dd430d 100644 --- a/index.css +++ b/index.css @@ -130,10 +130,6 @@ btn { width: 4rem !important; } -.space-between { - justify-content: space-between; -} - .link-underline a { text-decoration: underline 1px #0078a855;; color: #0078A8; From 6ceb589f0052bea8692670195ef219e810697654 Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Wed, 22 Sep 2021 19:53:47 +0200 Subject: [PATCH 029/110] Tailwind .justify-between instead of inline style --- UI/OpeningHours/OpeningHoursRange.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/OpeningHours/OpeningHoursRange.ts b/UI/OpeningHours/OpeningHoursRange.ts index 439aac490..6142ea9a2 100644 --- a/UI/OpeningHours/OpeningHoursRange.ts +++ b/UI/OpeningHours/OpeningHoursRange.ts @@ -37,7 +37,7 @@ export default class OpeningHoursRange extends BaseUIElement { let content: BaseUIElement; if (height > 2) { - content = new Combine([startTime, deleteRange, endTime]).SetClass("flex flex-col h-full").SetStyle("justify-content: space-between;"); + content = new Combine([startTime, deleteRange, endTime]).SetClass("flex flex-col h-full justify-between"); } else { content = new Combine([deleteRange]).SetClass("flex flex-col h-full").SetStyle("flex-content: center; overflow-x: unset;") } @@ -61,4 +61,4 @@ export default class OpeningHoursRange extends BaseUIElement { } -} \ No newline at end of file +} From 45e8b96f9f492a636cc8ed067e48726b1f380908 Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Wed, 22 Sep 2021 19:56:05 +0200 Subject: [PATCH 030/110] Introduce TailwindCSS CLI with JIT-Mode Remove Tailwind Compatibility Mode and PostCSS. Install newest tailwind and configure it to use JIT-Mode. Setup npm run scripts to run TailwindCSS CLI and Parcel in parallel. The Tailwind index.css and all .html/.ts files are scanned live and generated to /css/index-tailwind-output which is in turn included in index.html. --- css/index-tailwind-output.css | 2317 ++++++++++++++++++++ index.css | 20 +- index.html | 2 +- package-lock.json | 3734 ++++++++++++++++++++++----------- package.json | 11 +- postcss.config.js | 6 - tailwind.config.js | 11 +- 7 files changed, 4871 insertions(+), 1230 deletions(-) create mode 100644 css/index-tailwind-output.css delete mode 100644 postcss.config.js diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css new file mode 100644 index 000000000..e3d669e7c --- /dev/null +++ b/css/index-tailwind-output.css @@ -0,0 +1,2317 @@ +/* + TailwindCSS JIT-Mode Input file. + Use TailwindCSS functions and directives here – https://tailwindcss.com/docs/functions-and-directives + About JIT-Mode: https://tailwindcss.com/docs/just-in-time-mode#styles-rebuild-in-an-infinite-loop + + TailwindCSS CLI generates the css/index-tailwind-output.css file based on this file. + It is not used directly in the app. +*/ + +/* + ! tailwindcss v2.2.15 | MIT License | https://tailwindcss.com +*/ + +/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ + +/* +Document +======== +*/ + +/** +Use a better box model (opinionated). +*/ + +*, +::before, +::after { + box-sizing: border-box; +} + +/** +Use a more readable tab size (opinionated). +*/ + +html { + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; +} + +/** +1. Correct the line height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +*/ + +html { + line-height: 1.15; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ +} + +/* +Sections +======== +*/ + +/** +Remove the margin in all browsers. +*/ + +body { + margin: 0; +} + +/** +Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +*/ + +body { + font-family: + system-ui, + -apple-system, /* Firefox supports this but not yet `system-ui` */ + 'Segoe UI', + Roboto, + Helvetica, + Arial, + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji'; +} + +/* +Grouping content +================ +*/ + +/** +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ +} + +/* +Text-level semantics +==================== +*/ + +/** +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr[title] { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/** +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/** +1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +2. Correct the odd 'em' font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: + ui-monospace, + SFMono-Regular, + Consolas, + 'Liberation Mono', + Menlo, + monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/** +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/** +Prevent 'sub' and 'sup' elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +Tabular data +============ +*/ + +/** +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ +} + +/* +Forms +===== +*/ + +/** +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + line-height: 1.15; + /* 1 */ + margin: 0; + /* 2 */ +} + +/** +Remove the inheritance of text transform in Edge and Firefox. +1. Remove the inheritance of text transform in Firefox. +*/ + +button, +select { + /* 1 */ + text-transform: none; +} + +/** +Correct the inability to style clickable types in iOS and Safari. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +/** +Remove the inner border and padding in Firefox. +*/ + +::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** +Restore the focus styles unset by the previous rule. +*/ + +:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** +Remove the additional ':invalid' styles in Firefox. +See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737 +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/** +Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers. +*/ + +legend { + padding: 0; +} + +/** +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/** +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/** +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/** +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to 'inherit' in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Interactive +=========== +*/ + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/** + * Manually forked from SUIT CSS Base: https://github.com/suitcss/base + * A thin layer on top of normalize.css that provides a starting point more + * suitable for web applications. + */ + +/** + * Removes the default spacing and border for appropriate elements. + */ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +button { + background-color: transparent; + background-image: none; +} + +fieldset { + margin: 0; + padding: 0; +} + +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} + +/** + * Tailwind custom reset styles + */ + +/** + * 1. Use the user's configured `sans` font-family (with Tailwind's default + * sans-serif font stack as a fallback) as a sane default. + * 2. Use Tailwind's default "normal" line-height so the user isn't forced + * to override it to ensure consistency even when using the default theme. + */ + +html { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 1 */ + line-height: 1.5; + /* 2 */ +} + +/** + * Inherit font-family and line-height from `html` so users can set them as + * a class directly on the `html` element. + */ + +body { + font-family: inherit; + line-height: inherit; +} + +/** + * 1. Prevent padding and border from affecting element width. + * + * We used to set this in the html element and inherit from + * the parent element for everything else. This caused issues + * in shadow-dom-enhanced elements like
where the content + * is wrapped by a div with box-sizing set to `content-box`. + * + * https://github.com/mozdevs/cssremedy/issues/4 + * + * + * 2. Allow adding a border to an element by just adding a border-width. + * + * By default, the way the browser specifies that an element should have no + * border is by setting it's border-style to `none` in the user-agent + * stylesheet. + * + * In order to easily add borders to elements by just setting the `border-width` + * property, we change the default border-style for all elements to `solid`, and + * use border-width to hide them instead. This way our `border` utilities only + * need to set the `border-width` property instead of the entire `border` + * shorthand, making our border utilities much more straightforward to compose. + * + * https://github.com/tailwindcss/tailwindcss/pull/116 + */ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: currentColor; + /* 2 */ +} + +/* + * Ensure horizontal rules are visible by default + */ + +hr { + border-top-width: 1px; +} + +/** + * Undo the `border-style: none` reset that Normalize applies to images so that + * our `border-{width}` utilities have the expected effect. + * + * The Normalize reset is unnecessary for us since we default the border-width + * to 0 on all elements. + * + * https://github.com/tailwindcss/tailwindcss/issues/362 + */ + +img { + border-style: solid; +} + +textarea { + resize: vertical; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + color: #9ca3af; +} + +input:-ms-input-placeholder, textarea:-ms-input-placeholder { + opacity: 1; + color: #9ca3af; +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + color: #9ca3af; +} + +button, +[role="button"] { + cursor: pointer; +} + +/** + * Override legacy focus reset from Normalize with modern Firefox focus styles. + * + * This is actually an improvement over the new defaults in Firefox in our testing, + * as it triggers the better focus styles even for links, which still use a dotted + * outline in Firefox by default. + */ + +:-moz-focusring { + outline: auto; +} + +table { + border-collapse: collapse; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/** + * Reset links to optimize for opt-in styling instead of + * opt-out. + */ + +a { + color: inherit; + text-decoration: inherit; +} + +/** + * Reset form element properties that are easy to forget to + * style explicitly so you don't inadvertently introduce + * styles that deviate from your design system. These styles + * supplement a partial reset that is already applied by + * normalize.css. + */ + +button, +input, +optgroup, +select, +textarea { + padding: 0; + line-height: inherit; + color: inherit; +} + +/** + * Use the configured 'mono' font family for elements that + * are expected to be rendered with a monospace font, falling + * back to the system monospace stack if there is no configured + * 'mono' font family. + */ + +pre, +code, +kbd, +samp { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +/** + * 1. Make replaced elements `display: block` by default as that's + * the behavior you want almost all of the time. Inspired by + * CSS Remedy, with `svg` added as well. + * + * https://github.com/mozdevs/cssremedy/issues/14 + * + * 2. Add `vertical-align: middle` to align replaced elements more + * sensibly by default when overriding `display` by adding a + * utility like `inline`. + * + * This can trigger a poorly considered linting error in some + * tools but is included by design. + * + * https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210 + */ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/** + * Constrain images and videos to the parent width and preserve + * their intrinsic aspect ratio. + * + * https://github.com/mozdevs/cssremedy/issues/14 + */ + +img, +video { + max-width: 100%; + height: auto; +} + +/** + * Ensure the default browser behavior of the `hidden` attribute. + */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + --tw-border-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-border-opacity)); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-blur: var(--tw-empty,/*!*/ /*!*/); + --tw-brightness: var(--tw-empty,/*!*/ /*!*/); + --tw-contrast: var(--tw-empty,/*!*/ /*!*/); + --tw-grayscale: var(--tw-empty,/*!*/ /*!*/); + --tw-hue-rotate: var(--tw-empty,/*!*/ /*!*/); + --tw-invert: var(--tw-empty,/*!*/ /*!*/); + --tw-saturate: var(--tw-empty,/*!*/ /*!*/); + --tw-sepia: var(--tw-empty,/*!*/ /*!*/); + --tw-drop-shadow: var(--tw-empty,/*!*/ /*!*/); + --tw-filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.pointer-events-none { + pointer-events: none; +} + +.pointer-events-auto { + pointer-events: auto; +} + +.visible { + visibility: visible; +} + +.invisible { + visibility: hidden; +} + +.static { + position: static; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.sticky { + position: sticky; +} + +.inset-0 { + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} + +.bottom-3 { + bottom: 0.75rem; +} + +.left-3 { + left: 0.75rem; +} + +.right-2 { + right: 0.5rem; +} + +.left-24 { + left: 6rem; +} + +.right-24 { + right: 6rem; +} + +.top-56 { + top: 14rem; +} + +.top-0 { + top: 0px; +} + +.left-0 { + left: 0px; +} + +.right-0 { + right: 0px; +} + +.top-2 { + top: 0.5rem; +} + +.right-3 { + right: 0.75rem; +} + +.bottom-0 { + bottom: 0px; +} + +.isolate { + isolation: isolate; +} + +.z-10 { + z-index: 10; +} + +.z-0 { + z-index: 0; +} + +.float-right { + float: right; +} + +.float-left { + float: left; +} + +.float-none { + float: none; +} + +.m-8 { + margin: 2rem; +} + +.m-11 { + margin: 2.75rem; +} + +.m-1 { + margin: 0.25rem; +} + +.m-5 { + margin: 1.25rem; +} + +.m-0\.5 { + margin: 0.125rem; +} + +.m-0 { + margin: 0px; +} + +.m-2 { + margin: 0.5rem; +} + +.m-3 { + margin: 0.75rem; +} + +.m-4 { + margin: 1rem; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.mx-10 { + margin-left: 2.5rem; + margin-right: 2.5rem; +} + +.my-3 { + margin-top: 0.75rem; + margin-bottom: 0.75rem; +} + +.-ml-1 { + margin-left: -0.25rem; +} + +.mr-3 { + margin-right: 0.75rem; +} + +.ml-3 { + margin-left: 0.75rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mr-4 { + margin-right: 1rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.ml-8 { + margin-left: 2rem; +} + +.mr-2 { + margin-right: 0.5rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mb-10 { + margin-bottom: 2.5rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-0 { + margin-top: 0px; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.mr-0 { + margin-right: 0px; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.box-border { + box-sizing: border-box; +} + +.box-content { + box-sizing: content-box; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.table { + display: table; +} + +.grid { + display: grid; +} + +.contents { + display: contents; +} + +.hidden { + display: none; +} + +.h-full { + height: 100%; +} + +.h-5 { + height: 1.25rem; +} + +.h-24 { + height: 6rem; +} + +.h-10 { + height: 2.5rem; +} + +.h-8 { + height: 2rem; +} + +.h-12 { + height: 3rem; +} + +.h-screen { + height: 100vh; +} + +.h-11 { + height: 2.75rem; +} + +.h-32 { + height: 8rem; +} + +.h-16 { + height: 4rem; +} + +.h-0 { + height: 0px; +} + +.h-6 { + height: 1.5rem; +} + +.h-3 { + height: 0.75rem; +} + +.max-h-20vh { + max-height: 20vh; +} + +.max-h-32 { + max-height: 8rem; +} + +.max-h-4 { + max-height: 1rem; +} + +.w-full { + width: 100%; +} + +.w-5 { + width: 1.25rem; +} + +.w-10 { + width: 2.5rem; +} + +.w-8 { + width: 2rem; +} + +.w-12 { + width: 3rem; +} + +.w-screen { + width: 100vw; +} + +.w-11 { + width: 2.75rem; +} + +.w-0 { + width: 0px; +} + +.w-16 { + width: 4rem; +} + +.w-min { + width: -webkit-min-content; + width: -moz-min-content; + width: min-content; +} + +.w-max { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + +.w-6 { + width: 1.5rem; +} + +.w-1\/2 { + width: 50%; +} + +.min-w-min { + min-width: -webkit-min-content; + min-width: -moz-min-content; + min-width: min-content; +} + +.min-w-\[20em\] { + min-width: 20em; +} + +.max-w-full { + max-width: 100%; +} + +.flex-none { + flex: none; +} + +.flex-auto { + flex: 1 1 auto; +} + +.flex-shrink-0 { + flex-shrink: 0; +} + +.flex-grow { + flex-grow: 1; +} + +.border-collapse { + border-collapse: collapse; +} + +.transform { + transform: var(--tw-transform); +} + +@-webkit-keyframes spin { + to { + transform: rotate(360deg); + } +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.animate-spin { + -webkit-animation: spin 1s linear infinite; + animation: spin 1s linear infinite; +} + +@-webkit-keyframes pulse { + 50% { + opacity: .5; + } +} + +@keyframes pulse { + 50% { + opacity: .5; + } +} + +.animate-pulse { + -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +.cursor-wait { + cursor: wait; +} + +.cursor-pointer { + cursor: pointer; +} + +.resize { + resize: both; +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-end { + align-items: flex-end; +} + +.items-center { + align-items: center; +} + +.items-baseline { + align-items: baseline; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-4 { + gap: 1rem; +} + +.overflow-auto { + overflow: auto; +} + +.overflow-hidden { + overflow: hidden; +} + +.overflow-y-auto { + overflow-y: auto; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.overflow-ellipsis { + text-overflow: ellipsis; +} + +.whitespace-nowrap { + white-space: nowrap; +} + +.break-normal { + overflow-wrap: normal; + word-break: normal; +} + +.break-words { + overflow-wrap: break-word; +} + +.break-all { + word-break: break-all; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-3xl { + border-radius: 1.5rem; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-xl { + border-radius: 0.75rem; +} + +.border { + border-width: 1px; +} + +.border-4 { + border-width: 4px; +} + +.border-2 { + border-width: 2px; +} + +.border-b-2 { + border-bottom-width: 2px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-black { + --tw-border-opacity: 1; + border-color: rgba(0, 0, 0, var(--tw-border-opacity)); +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgba(209, 213, 219, var(--tw-border-opacity)); +} + +.border-gray-400 { + --tw-border-opacity: 1; + border-color: rgba(156, 163, 175, var(--tw-border-opacity)); +} + +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-border-opacity)); +} + +.border-opacity-50 { + --tw-border-opacity: 0.5; +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); +} + +.bg-blue-50 { + --tw-bg-opacity: 1; + background-color: rgba(239, 246, 255, var(--tw-bg-opacity)); +} + +.bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgba(219, 234, 254, var(--tw-bg-opacity)); +} + +.bg-gray-400 { + --tw-bg-opacity: 1; + background-color: rgba(156, 163, 175, var(--tw-bg-opacity)); +} + +.bg-gray-300 { + --tw-bg-opacity: 1; + background-color: rgba(209, 213, 219, var(--tw-bg-opacity)); +} + +.bg-black { + --tw-bg-opacity: 1; + background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); +} + +.bg-indigo-100 { + --tw-bg-opacity: 1; + background-color: rgba(224, 231, 255, var(--tw-bg-opacity)); +} + +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgba(243, 244, 246, var(--tw-bg-opacity)); +} + +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgba(239, 68, 68, var(--tw-bg-opacity)); +} + +.bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgba(254, 202, 202, var(--tw-bg-opacity)); +} + +.p-1\.5 { + padding: 0.375rem; +} + +.p-1 { + padding: 0.25rem; +} + +.p-3 { + padding: 0.75rem; +} + +.p-4 { + padding: 1rem; +} + +.p-2 { + padding: 0.5rem; +} + +.p-0\.5 { + padding: 0.125rem; +} + +.p-0 { + padding: 0px; +} + +.pt-3 { + padding-top: 0.75rem; +} + +.pb-3 { + padding-bottom: 0.75rem; +} + +.pl-4 { + padding-left: 1rem; +} + +.pb-0 { + padding-bottom: 0px; +} + +.pl-1 { + padding-left: 0.25rem; +} + +.pr-1 { + padding-right: 0.25rem; +} + +.pt-6 { + padding-top: 1.5rem; +} + +.pl-2 { + padding-left: 0.5rem; +} + +.pl-5 { + padding-left: 1.25rem; +} + +.pr-3 { + padding-right: 0.75rem; +} + +.pr-4 { + padding-right: 1rem; +} + +.pl-3 { + padding-left: 0.75rem; +} + +.pr-0 { + padding-right: 0px; +} + +.pb-2 { + padding-bottom: 0.5rem; +} + +.pt-0\.5 { + padding-top: 0.125rem; +} + +.pt-0 { + padding-top: 0px; +} + +.pr-2 { + padding-right: 0.5rem; +} + +.text-center { + text-align: center; +} + +.align-baseline { + vertical-align: baseline; +} + +.align-middle { + vertical-align: middle; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + +.font-bold { + font-weight: 700; +} + +.font-extrabold { + font-weight: 800; +} + +.font-semibold { + font-weight: 600; +} + +.font-medium { + font-weight: 500; +} + +.uppercase { + text-transform: uppercase; +} + +.lowercase { + text-transform: lowercase; +} + +.italic { + font-style: italic; +} + +.ordinal, .slashed-zero, .lining-nums, .oldstyle-nums, .proportional-nums, .tabular-nums, .diagonal-fractions, .stacked-fractions { + --tw-ordinal: var(--tw-empty,/*!*/ /*!*/); + --tw-slashed-zero: var(--tw-empty,/*!*/ /*!*/); + --tw-numeric-figure: var(--tw-empty,/*!*/ /*!*/); + --tw-numeric-spacing: var(--tw-empty,/*!*/ /*!*/); + --tw-numeric-fraction: var(--tw-empty,/*!*/ /*!*/); + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction); +} + +.ordinal { + --tw-ordinal: ordinal; +} + +.leading-6 { + line-height: 1.5rem; +} + +.tracking-tight { + letter-spacing: -0.025em; +} + +.text-white { + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgba(17, 24, 39, var(--tw-text-opacity)); +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgba(31, 41, 55, var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgba(107, 114, 128, var(--tw-text-opacity)); +} + +.text-green-600 { + --tw-text-opacity: 1; + color: rgba(5, 150, 105, var(--tw-text-opacity)); +} + +.underline { + text-decoration: underline; +} + +.line-through { + text-decoration: line-through; +} + +.opacity-0 { + opacity: 0; +} + +.opacity-40 { + opacity: 0.4; +} + +.shadow { + --tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.blur { + --tw-blur: blur(8px); + filter: var(--tw-filter); +} + +.drop-shadow { + --tw-drop-shadow: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1)) drop-shadow(0 1px 1px rgba(0, 0, 0, 0.06)); + filter: var(--tw-filter); +} + +.invert { + --tw-invert: invert(100%); + filter: var(--tw-filter); +} + +.filter { + filter: var(--tw-filter); +} + +.transition { + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.\!transition { + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter !important; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter !important; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter !important; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important; + transition-duration: 150ms !important; +} + +.duration-500 { + transition-duration: 500ms; +} + +.ease-in { + transition-timing-function: cubic-bezier(0.4, 0, 1, 1); +} + +.z-above-map { + z-index: 10000 +} + +.z-above-controls { + z-index: 10001 +} + +.btn { + display: inline-flex; + justify-content: center; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + padding-right: 1rem; + border-width: 1px; + border-color: transparent; + --tw-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + border-radius: 1.5rem; + --tw-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 1; + --tw-ring-color: rgba(191, 219, 254, var(--tw-ring-opacity)); +} + +.btn:hover { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(147, 197, 253, var(--tw-ring-opacity)); +} + +.btn { + margin-top: 0.25rem; + margin-right: 0.25rem; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + --tw-bg-opacity: 1; + background-color: rgba(37, 99, 235, var(--tw-bg-opacity)); +} + +.btn:hover { + --tw-bg-opacity: 1; + background-color: rgba(29, 78, 216, var(--tw-bg-opacity)); +} + +.btn:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-opacity: 1; + --tw-ring-color: rgba(29, 78, 216, var(--tw-ring-opacity)); +} + +.btn-secondary { + --tw-bg-opacity: 1; + background-color: rgba(75, 85, 99, var(--tw-bg-opacity)); +} + +.btn-secondary:hover { + --tw-bg-opacity: 1; + background-color: rgba(55, 65, 81, var(--tw-bg-opacity)); +} + +.btn-disabled { + --tw-bg-opacity: 1; + background-color: rgba(107, 114, 128, var(--tw-bg-opacity)); +} + +.btn-disabled:hover { + --tw-bg-opacity: 1; + background-color: rgba(107, 114, 128, var(--tw-bg-opacity)); +} + +.btn-disabled { + --tw-text-opacity: 1; + color: rgba(209, 213, 219, var(--tw-text-opacity)); + --tw-ring-opacity: 1; + --tw-ring-color: rgba(229, 231, 235, var(--tw-ring-opacity)); +} + +.btn-disabled:hover { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(229, 231, 235, var(--tw-ring-opacity)); +} + +.btn-disabled:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(229, 231, 235, var(--tw-ring-opacity)); +} + +.btn-disabled { + cursor: default; +} + +:root { + --subtle-detail-color: #e5f5ff; + --subtle-detail-color-contrast: black; + --subtle-detail-color-light-contrast: lightgrey; + --catch-detail-color: #3a3aeb; + --catch-detail-color-contrast: white; + --alert-color: #fee4d1; + --background-color: white; + --foreground-color: black; + --popup-border: white; + --shadow-color: #00000066; + --variable-title-height: 0px; + /* Set by javascript */ + --return-to-the-map-height: 2em; + --image-carousel-height: 350px; +} + +html, body { + height: 100%; + min-height: 100vh; + min-height: -webkit-fill-available; + margin: 0; + padding: 0; + background-color: var(--background-color); + color: var(--foreground-color); + font-family: 'Helvetica Neue', Arial, sans-serif; +} + +.leaflet-overlay-pane .leaflet-zoom-animated { + /* Another workaround to keep leaflet working */ + width: initial !important; + height: initial !important; + box-sizing: initial !important; +} + +.leaflet-control-attribution { + display: block ruby; +} + +svg, img { + box-sizing: content-box; + width: 100%; + height: 100%; + display: unset; +} + +.mapcontrol svg path { + fill: var(--subtle-detail-color-contrast) !important; +} + +.red-svg svg path { + stroke: #d71010 !important; +} + +a { + color: var(--foreground-color); +} + +btn { + margin-top: 0.25rem; + margin-right: 0.25rem; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + --tw-text-opacity: 1; + color: var(--catch-detail-color-contrast); + --tw-bg-opacity: 1; + background-color: var(--catch-detail-color); +} + +.h-min { + height: -webkit-min-content; + height: -moz-min-content; + height: min-content; +} + +.w-min { + width: -webkit-min-content; + width: -moz-min-content; + width: min-content; +} + +.w-16-imp { + width: 4rem !important; +} + +.link-underline a { + -webkit-text-decoration: underline 1px #0078a855; + text-decoration: underline 1px #0078a855; + color: #0078A8; +} + +.link-no-underline a { + text-decoration: none; +} + +li { + margin-left: 0.5em; + padding-left: 0.2em; + margin-top: 0.1em; +} + +h2 { + font-size: large; + margin-top: 0.5em; + margin-bottom: 0.3em; + font-weight: bold; +} + +h3 { + font-size: larger; + margin-top: 0.6em; + margin-bottom: 0; + font-weight: bold; + font-size: larger; + margin-top: 0.6em; + margin-bottom: 0; + font-weight: bolder; +} + +p { + padding-top: 0.1em; +} + +li::marker { + content: "•" +} + +.subtle-background { + background: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); +} + +.subtle-lighter { + color: var(--subtle-detail-color-light-contrast); +} + +.border-attention-catch { + border: 5px solid var(--catch-detail-color); +} + +.direction-svg svg path { + fill: var(--catch-detail-color) !important; +} + +#leafletDiv { + height: 100%; +} + +.leaflet-popup-content-wrapper { + background-color: var(--background-color); + color: var(--foreground-color); + border: 2px solid var(--popup-border); + box-shadow: 0 3px 14px var(--shadow-color) !important; +} + +.leaflet-container { + background-color: var(--background-color) !important; +} + +.leaflet-popup-tip { + background-color: var(--popup-border) !important; + color: var(--popup-border) !important; + box-shadow: 0 3px 14px var(--shadow-color) !important; +} + +.single-layer-selection-toggle { + position: relative; + width: 2em; + height: 2em; + flex-shrink: 0; +} + +.single-layer-selection-toggle img { + max-height: 2em !important; + max-width: 2em !important; +} + +.single-layer-selection-toggle svg { + max-height: 2em !important; + max-width: 2em !important; +} + +/**************** GENERIC ****************/ + +.alert { + background-color: var(--alert-color); + font-weight: bold; + border-radius: 1em; + margin: 0.25em; + text-align: center; + padding: 0.15em 0.3em; +} + +.question form { + display: inline-block; + max-width: 90vw; + width: 100%; +} + +.invalid { + box-shadow: 0 0 10px #ff5353; + height: -webkit-min-content; + height: -moz-min-content; + height: min-content; +} + +.shadow { + box-shadow: 0 0 10px var(--shadow-color); +} + +.title-font span { + font-size: xx-large !important; + font-weight: bold; +} + +.soft { + background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); + font-weight: bold; + border-radius: 1em; + margin: 0.25em; + text-align: center; + padding: 0.15em 0.3em; +} + +.subtle { + color: #999; +} + +.link-underline .subtle a { + -webkit-text-decoration: underline 1px #7193bb88; + text-decoration: underline 1px #7193bb88; + color: #7193bb; +} + +.thanks { + background-color: #43d904; + font-weight: bold; + border-radius: 1em; + margin: 0.25em; + text-align: center; + padding: 0.15em 0.3em; +} + +.clickable { + pointer-events: all; +} + +.unclickable { + pointer-events: none !important; +} + +@-webkit-keyframes slide { + from { + transform: translateX(0%); + } + + to { + transform: translateX(calc(-100% + 42px)); + } +} + +@keyframes slide { + from { + transform: translateX(0%); + } + + to { + transform: translateX(calc(-100% + 42px)); + } +} + +/**************************************/ + +#topleft-tools { + display: block; + position: absolute; + z-index: 5000; + transition: all 500ms linear; + left: 0; + right: 0; +} + +.welcomeMessage { + display: block; + max-width: calc(100vw - 5em); + width: 40em; + max-height: calc(100vh - 15em); + overflow-y: auto; + border-radius: 1em; + background-color: var(--background-color); + color: var(--foreground-color); +} + +/***************** Info box (box containing features and questions ******************/ + +.leaflet-popup-content { + width: 45em !important; +} + +.leaflet-div-icon { + background-color: unset !important; + border: unset !important; +} + +.leaflet-div-icon svg { + width: calc(100%); + height: calc(100%); +} + +/****** ShareScreen *****/ + +.literal-code { + display: inline-block; + background-color: lightgray; + padding: 0.5em; + word-break: break-word; + color: black; + box-sizing: border-box; +} + +/** Switch layout **/ + +.small-image img { + height: 1em; + max-width: 1em; +} + +.small-image { + height: 1em; + max-width: 1em; +} + +.slideshow-item img { + height: var(--image-carousel-height); + width: unset; +} + +.hover\:bg-blue-200:hover { + --tw-bg-opacity: 1; + background-color: rgba(191, 219, 254, var(--tw-bg-opacity)); +} + +.hover\:bg-indigo-200:hover { + --tw-bg-opacity: 1; + background-color: rgba(199, 210, 254, var(--tw-bg-opacity)); +} + +.hover\:text-blue-800:hover { + --tw-text-opacity: 1; + color: rgba(30, 64, 175, var(--tw-text-opacity)); +} + +.hover\:shadow-xl:hover { + --tw-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.group:hover .group-hover\:text-blue-800 { + --tw-text-opacity: 1; + color: rgba(30, 64, 175, var(--tw-text-opacity)); +} + +.group:hover .group-hover\:text-blue-900 { + --tw-text-opacity: 1; + color: rgba(30, 58, 138, var(--tw-text-opacity)); +} + +@media (min-width: 640px) { + .sm\:mx-auto { + margin-left: auto; + margin-right: auto; + } + + .sm\:mt-5 { + margin-top: 1.25rem; + } + + .sm\:h-24 { + height: 6rem; + } + + .sm\:w-24 { + width: 6rem; + } + + .sm\:w-auto { + width: auto; + } + + .sm\:max-w-sm { + max-width: 24rem; + } + + .sm\:max-w-xl { + max-width: 36rem; + } + + .sm\:flex-row { + flex-direction: row; + } + + .sm\:flex-wrap { + flex-wrap: wrap; + } + + .sm\:items-start { + align-items: flex-start; + } + + .sm\:justify-between { + justify-content: space-between; + } + + .sm\:p-0\.5 { + padding: 0.125rem; + } + + .sm\:p-1\.5 { + padding: 0.375rem; + } + + .sm\:p-0 { + padding: 0px; + } + + .sm\:p-1 { + padding: 0.25rem; + } + + .sm\:pl-2 { + padding-left: 0.5rem; + } + + .sm\:pt-1 { + padding-top: 0.25rem; + } + + .sm\:text-center { + text-align: center; + } + + .sm\:text-xl { + font-size: 1.25rem; + line-height: 1.75rem; + } + + .sm\:text-5xl { + font-size: 3rem; + line-height: 1; + } + + .sm\:text-lg { + font-size: 1.125rem; + line-height: 1.75rem; + } +} + +@media (min-width: 768px) { + .md\:relative { + position: relative; + } + + .md\:m-1 { + margin: 0.25rem; + } + + .md\:m-2 { + margin: 0.5rem; + } + + .md\:mt-5 { + margin-top: 1.25rem; + } + + .md\:mt-4 { + margin-top: 1rem; + } + + .md\:block { + display: block; + } + + .md\:grid { + display: grid; + } + + .md\:hidden { + display: none; + } + + .md\:h-12 { + height: 3rem; + } + + .md\:max-h-65vh { + max-height: 65vh; + } + + .md\:w-auto { + width: auto; + } + + .md\:grid-flow-row { + grid-auto-flow: row; + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:p-0 { + padding: 0px; + } + + .md\:p-1 { + padding: 0.25rem; + } + + .md\:p-2 { + padding: 0.5rem; + } + + .md\:pt-4 { + padding-top: 1rem; + } + + .md\:text-2xl { + font-size: 1.5rem; + line-height: 2rem; + } + + .md\:text-6xl { + font-size: 3.75rem; + line-height: 1; + } + + .md\:text-xl { + font-size: 1.25rem; + line-height: 1.75rem; + } + + .md\:shadow-none { + --tw-shadow: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } +} + +@media (min-width: 1024px) { + .lg\:mx-0 { + margin-left: 0px; + margin-right: 0px; + } + + .lg\:ml-40 { + margin-left: 10rem; + } + + .lg\:w-3\/4 { + width: 75%; + } + + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .lg\:text-left { + text-align: left; + } +} + +@media (min-width: 1280px) { + .xl\:inline { + display: inline; + } +} diff --git a/index.css b/index.css index 377dd430d..bb063beec 100644 --- a/index.css +++ b/index.css @@ -1,17 +1,18 @@ +/* + TailwindCSS JIT-Mode Input file. + Use TailwindCSS functions and directives here – https://tailwindcss.com/docs/functions-and-directives + About JIT-Mode: https://tailwindcss.com/docs/just-in-time-mode#styles-rebuild-in-an-infinite-loop + + TailwindCSS CLI generates the css/index-tailwind-output.css file based on this file. + It is not used directly in the app. +*/ + @tailwind base; @tailwind components; @tailwind utilities; @layer utilities { @variants responsive { - .max-h-65vh { - max-height: 65vh; - } - - .max-h-20vh { - max-height: 20vh; - } - .z-above-map { z-index: 10000 } @@ -19,7 +20,6 @@ .z-above-controls { z-index: 10001 } - } .btn { @@ -381,4 +381,4 @@ li::marker { .slideshow-item img { height: var(--image-carousel-height); width: unset; -} \ No newline at end of file +} diff --git a/index.html b/index.html index 0eccf7729..777dd921c 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,6 @@ - @@ -14,6 +13,7 @@ + diff --git a/package-lock.json b/package-lock.json index 780388999..8981f9c64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "GPL", "dependencies": { "@babel/preset-env": "7.13.8", - "@tailwindcss/postcss7-compat": "^2.0.2", "@turf/buffer": "^6.3.0", "@turf/collect": "^6.3.0", "@turf/distance": "^6.3.0", @@ -22,7 +21,6 @@ "@types/leaflet.markercluster": "^1.4.3", "@types/lz-string": "^1.3.34", "@types/prompt-sync": "^4.1.0", - "autoprefixer": "^9.8.6", "country-language": "^0.1.7", "email-validator": "^2.0.4", "escape-html": "^1.0.3", @@ -39,12 +37,13 @@ "lz-string": "^1.4.4", "mangrove-reviews": "^0.1.3", "moment": "^2.29.0", + "npm-run-all": "^4.1.5", "opening_hours": "^3.6.0", "osm-auth": "^1.0.2", "osmtogeojson": "^3.0.0-beta.4", "parcel": "^1.2.4", - "postcss": "^7.0.36", "prompt-sync": "^4.2.0", + "tailwindcss": "^2.2.15", "tslint": "^6.1.3" }, "devDependencies": { @@ -55,7 +54,6 @@ "marked": "^2.0.0", "read-file": "^0.2.0", "sharp": "^0.28.3", - "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.7", "ts-node": "^9.0.0", "ts-node-dev": "^1.0.0-pre.63", "tslint-no-circular-imports": "^0.7.0", @@ -1262,14 +1260,6 @@ "to-fast-properties": "^2.0.0" } }, - "node_modules/@fullhuman/postcss-purgecss": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.1.3.tgz", - "integrity": "sha512-kwOXw8fZ0Lt1QmeOOrd+o4Ibvp4UTEBFQbzvWldjlKv5n+G9sXfIPn1hh63IQIL8K8vbvv1oYMJiIUbuy9bGaA==", - "dependencies": { - "purgecss": "^3.1.3" - } - }, "node_modules/@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", @@ -1642,113 +1632,6 @@ "node": ">= 6.0.0" } }, - "node_modules/@tailwindcss/postcss7-compat": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.1.2.tgz", - "integrity": "sha512-bH2kw6uyqLnDMP8wzDUsis5ovrsRzfHEyiL1McADvqlW54g6y0KVHX1xzO7PH8Fl5s0Sq8vDOAp4+3V8MEcZ9g==", - "dependencies": { - "@fullhuman/postcss-purgecss": "^3.1.3", - "autoprefixer": "^9", - "bytes": "^3.0.0", - "chalk": "^4.1.0", - "chokidar": "^3.5.1", - "color": "^3.1.3", - "detective": "^5.2.0", - "didyoumean": "^1.2.1", - "dlv": "^1.1.3", - "fast-glob": "^3.2.5", - "fs-extra": "^9.1.0", - "html-tags": "^3.1.0", - "lodash": "^4.17.21", - "lodash.topath": "^4.5.2", - "modern-normalize": "^1.0.0", - "node-emoji": "^1.8.1", - "normalize-path": "^3.0.0", - "object-hash": "^2.1.1", - "parse-glob": "^3.0.4", - "postcss": "^7", - "postcss-functions": "^3", - "postcss-js": "^2", - "postcss-nested": "^4", - "postcss-selector-parser": "^6.0.4", - "postcss-value-parser": "^4.1.0", - "pretty-hrtime": "^1.0.3", - "quick-lru": "^5.1.1", - "reduce-css-calc": "^2.1.8", - "resolve": "^1.20.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/@tailwindcss/postcss7-compat/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@tailwindcss/postcss7-compat/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@tailwindcss/postcss7-compat/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@tailwindcss/postcss7-compat/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@tailwindcss/postcss7-compat/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@tailwindcss/postcss7-compat/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@turf/along": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@turf/along/-/along-6.3.0.tgz", @@ -3098,8 +2981,7 @@ "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "node_modules/@types/prompt-sync": { "version": "4.1.0", @@ -3431,14 +3313,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -3451,24 +3325,30 @@ } }, "node_modules/autoprefixer": { - "version": "9.8.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", - "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.5.tgz", + "integrity": "sha512-2H5kQSsyoOMdIehTzIt/sC9ZDIgWqlkG/dbevm9B9xQZ1TDPBHpNUDW5ENqqQQzuaBWEo75JkV0LJe+o5Lnr5g==", + "peer": true, "dependencies": { - "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001109", - "colorette": "^1.2.1", + "browserslist": "^4.17.1", + "caniuse-lite": "^1.0.30001259", + "fraction.js": "^4.1.1", + "nanocolors": "^0.1.5", "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.32", "postcss-value-parser": "^4.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" }, + "engines": { + "node": "^10 || ^12 || >=14" + }, "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, "node_modules/available-typed-arrays": { @@ -3919,15 +3799,15 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, "node_modules/browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", + "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", + "caniuse-lite": "^1.0.30001259", + "electron-to-chromium": "^1.3.846", "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "nanocolors": "^0.1.5", + "node-releases": "^1.1.76" }, "bin": { "browserslist": "cli.js" @@ -4113,9 +3993,13 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001223", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001223.tgz", - "integrity": "sha512-k/RYs6zc/fjbxTjaWZemeSmOjO0JJV+KguOBA3NwPup8uzxM1cMhR2BD9XmO86GuqaqTCO8CgkgH9Rz//vdDiA==" + "version": "1.0.30001259", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", + "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } }, "node_modules/canvg": { "version": "3.0.7", @@ -4165,23 +4049,23 @@ } }, "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dependencies": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { - "fsevents": "~2.3.1" + "fsevents": "~2.3.2" } }, "node_modules/chownr": { @@ -4316,19 +4200,14 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "node_modules/color-string": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", - "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, - "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4634,6 +4513,42 @@ "node": ">4" } }, + "node_modules/css-declaration-sorter/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/css-declaration-sorter/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-declaration-sorter/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/css-line-break": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-1.1.1.tgz", @@ -4873,6 +4788,42 @@ "node": ">=6.9.0" } }, + "node_modules/cssnano-preset-default/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/cssnano-preset-default/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/cssnano-util-get-arguments": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", @@ -4900,6 +4851,42 @@ "node": ">=6.9.0" } }, + "node_modules/cssnano-util-raw-cache/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/cssnano-util-same-parent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", @@ -4908,6 +4895,42 @@ "node": ">=6.9.0" } }, + "node_modules/cssnano/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/cssnano/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/csso": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", @@ -5261,9 +5284,9 @@ } }, "node_modules/didyoumean": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz", - "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "node_modules/diff": { "version": "4.0.2", @@ -5434,9 +5457,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/electron-to-chromium": { - "version": "1.3.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", - "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==" + "version": "1.3.846", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.846.tgz", + "integrity": "sha512-2jtSwgyiRzybHRxrc2nKI+39wH3AwQgn+sogQ+q814gv8hIFwrcZbV07Ea9f8AmK0ufPVZUvvAG1uZJ+obV4Jw==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -5878,16 +5901,15 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" }, "engines": { "node": ">=8" @@ -6021,6 +6043,19 @@ "node": ">= 0.12" } }, + "node_modules/fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", + "peer": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, "node_modules/fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -6052,20 +6087,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6264,45 +6285,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dependencies": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-base/node_modules/glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dependencies": { - "is-glob": "^2.0.0" - } - }, - "node_modules/glob-base/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-base/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -6541,8 +6523,7 @@ "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "node_modules/hsl-regex": { "version": "1.0.0", @@ -6926,7 +6907,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, "dependencies": { "import-from": "^3.0.0" }, @@ -6950,7 +6930,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, "dependencies": { "resolve-from": "^5.0.0" }, @@ -6962,7 +6941,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, "engines": { "node": ">=8" } @@ -7197,14 +7175,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -7667,8 +7637,7 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema": { "version": "0.2.3", @@ -9408,7 +9377,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", - "dev": true, "engines": { "node": ">=10" } @@ -9421,8 +9389,7 @@ "node_modules/lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" }, "node_modules/load-json-file": { "version": "1.1.0", @@ -9512,11 +9479,6 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" }, - "node_modules/lodash.toarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", - "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" - }, "node_modules/lodash.topath": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", @@ -9642,6 +9604,14 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", @@ -9960,11 +9930,18 @@ "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "optional": true }, + "node_modules/nanocolors": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.6.tgz", + "integrity": "sha512-2pvTw6vYRaBLGir2xR7MxaJtyWkrn+C53EpW8yPotG+pdAwBvt0Xwk4VJ6VHLY0aLthVZPvDfm9TdZvrvAm5UQ==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", - "dev": true, + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -10028,11 +10005,11 @@ "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==" }, "node_modules/node-emoji": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", "dependencies": { - "lodash.toarray": "^4.4.0" + "lodash": "^4.17.21" } }, "node_modules/node-forge": { @@ -10101,15 +10078,14 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "node_modules/node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==" + "version": "1.1.76", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", + "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==" }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -10121,7 +10097,6 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, "bin": { "semver": "bin/semver" } @@ -10138,6 +10113,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10150,6 +10126,84 @@ "node": ">=6" } }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "engines": { + "node": ">=4" + } + }, "node_modules/npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", @@ -10170,11 +10224,6 @@ "boolbase": "~1.0.0" } }, - "node_modules/num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" - }, "node_modules/number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -10241,9 +10290,9 @@ } }, "node_modules/object-hash": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz", - "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", "engines": { "node": ">= 6" } @@ -10734,6 +10783,23 @@ "node": ">=0.10.0" } }, + "node_modules/parcel/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/parcel/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", @@ -10755,6 +10821,17 @@ "node": ">=0.10.0" } }, + "node_modules/parcel/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parcel/node_modules/to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -10771,7 +10848,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -10783,7 +10859,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -10800,39 +10875,6 @@ "safe-buffer": "^5.1.1" } }, - "node_modules/parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dependencies": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-glob/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-glob/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -10971,6 +11013,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -11020,6 +11073,33 @@ } }, "node_modules/postcss": { + "version": "8.3.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.7.tgz", + "integrity": "sha512-9SaY7nnyQ63/WittqZYAvkkYPyKxchMKH71UDzeTmWuLSvxTRpeEeABZAzlCi55cuGcoFyoV/amX2BdsafQidQ==", + "dependencies": { + "nanocolors": "^0.1.5", + "nanoid": "^3.1.25", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dependencies": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/postcss-calc/node_modules/postcss": { "version": "7.0.36", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", @@ -11036,14 +11116,23 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-calc": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", - "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "node_modules/postcss-calc/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-calc/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dependencies": { - "postcss": "^7.0.27", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/postcss-colormin": { @@ -11061,11 +11150,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-colormin/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-colormin/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-colormin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-convert-values": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", @@ -11078,11 +11203,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-convert-values/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-convert-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-convert-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-discard-comments": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", @@ -11094,6 +11255,42 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-discard-comments/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-comments/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-discard-duplicates": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", @@ -11105,6 +11302,42 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-discard-duplicates/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-discard-empty": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", @@ -11116,6 +11349,42 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-discard-empty/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-empty/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-discard-overridden": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", @@ -11127,36 +11396,24 @@ "node": ">=6.9.0" } }, - "node_modules/postcss-functions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", - "integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=", + "node_modules/postcss-discard-overridden/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "dependencies": { - "glob": "^7.1.2", - "object-assign": "^4.1.1", - "postcss": "^6.0.9", - "postcss-value-parser": "^3.3.0" - } - }, - "node_modules/postcss-functions/node_modules/postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dependencies": { - "chalk": "^2.4.1", + "chalk": "^2.4.2", "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "supports-color": "^6.1.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-functions/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - }, - "node_modules/postcss-functions/node_modules/source-map": { + "node_modules/postcss-discard-overridden/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", @@ -11164,39 +11421,15 @@ "node": ">=0.10.0" } }, - "node_modules/postcss-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", - "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "node_modules/postcss-discard-overridden/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dependencies": { - "camelcase-css": "^2.0.1", - "postcss": "^7.0.18" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", - "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", - "dev": true, - "dependencies": { - "import-cwd": "^3.0.0", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "has-flag": "^3.0.0" }, "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } + "node": ">=6" } }, "node_modules/postcss-merge-longhand": { @@ -11213,11 +11446,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-merge-longhand/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-merge-longhand/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-merge-rules": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", @@ -11234,6 +11503,23 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-merge-rules/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", @@ -11247,6 +11533,25 @@ "node": ">=8" } }, + "node_modules/postcss-merge-rules/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-minify-font-values": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", @@ -11259,11 +11564,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-minify-font-values/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-minify-font-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-minify-gradients": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", @@ -11278,11 +11619,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-minify-gradients/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-minify-gradients/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-minify-params": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", @@ -11299,11 +11676,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-minify-params/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-minify-params/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-params/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-minify-selectors": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", @@ -11318,6 +11731,23 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-minify-selectors/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", @@ -11331,6 +11761,25 @@ "node": ">=8" } }, + "node_modules/postcss-minify-selectors/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-modules-extract-imports": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", @@ -11450,15 +11899,6 @@ "node": ">=0.10.0" } }, - "node_modules/postcss-nested": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-4.2.3.tgz", - "integrity": "sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw==", - "dependencies": { - "postcss": "^7.0.32", - "postcss-selector-parser": "^6.0.2" - } - }, "node_modules/postcss-normalize-charset": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", @@ -11470,6 +11910,42 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-normalize-charset/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-charset/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-normalize-display-values": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", @@ -11483,11 +11959,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-normalize-display-values/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-normalize-display-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-normalize-positions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", @@ -11502,11 +12014,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-normalize-positions/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-normalize-positions/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-normalize-repeat-style": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", @@ -11521,11 +12069,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-normalize-repeat-style/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-normalize-string": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", @@ -11539,11 +12123,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-normalize-string/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-normalize-string/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-normalize-timing-functions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", @@ -11557,11 +12177,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-normalize-timing-functions/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-normalize-unicode": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", @@ -11575,11 +12231,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-normalize-unicode/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-normalize-unicode/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-normalize-url": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", @@ -11594,11 +12286,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-normalize-url/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-normalize-url/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-normalize-whitespace": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", @@ -11611,11 +12339,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-normalize-whitespace/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-ordered-values": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", @@ -11629,11 +12393,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-ordered-values/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-ordered-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-reduce-initial": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", @@ -11648,6 +12448,42 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-reduce-initial/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-reduce-initial/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-reduce-transforms": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", @@ -11662,15 +12498,51 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-reduce-transforms/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-reduce-transforms/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-selector-parser": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz", - "integrity": "sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -11692,11 +12564,47 @@ "node": ">=6.9.0" } }, + "node_modules/postcss-svgo/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss-svgo/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "node_modules/postcss-svgo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/postcss-unique-selectors": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", @@ -11710,12 +12618,24 @@ "node": ">=6.9.0" } }, - "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "node_modules/postcss-unique-selectors/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } }, - "node_modules/postcss/node_modules/source-map": { + "node_modules/postcss-unique-selectors/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", @@ -11723,7 +12643,7 @@ "node": ">=0.10.0" } }, - "node_modules/postcss/node_modules/supports-color": { + "node_modules/postcss-unique-selectors/node_modules/supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", @@ -11734,6 +12654,11 @@ "node": ">=6" } }, + "node_modules/postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + }, "node_modules/posthtml": { "version": "0.11.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.11.6.tgz", @@ -11929,56 +12854,6 @@ "node": ">=6" } }, - "node_modules/purgecss": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-3.1.3.tgz", - "integrity": "sha512-hRSLN9mguJ2lzlIQtW4qmPS2kh6oMnA9RxdIYK8sz18QYqd6ePp4GNDl18oWHA1f2v2NEQIh51CO8s/E3YGckQ==", - "dependencies": { - "commander": "^6.0.0", - "glob": "^7.0.0", - "postcss": "^8.2.1", - "postcss-selector-parser": "^6.0.2" - }, - "bin": { - "purgecss": "bin/purgecss.js" - } - }, - "node_modules/purgecss/node_modules/postcss": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.14.tgz", - "integrity": "sha512-+jD0ZijcvyCqPQo/m/CW0UcARpdFylq04of+Q7RKX6f/Tu+dvpUI/9Sp81+i6/vJThnOBX09Quw0ZLOVwpzX3w==", - "dependencies": { - "colorette": "^1.2.2", - "nanoid": "^3.1.22", - "source-map": "^0.6.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/purgecss/node_modules/postcss/node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/purgecss/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -12174,9 +13049,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dependencies": { "picomatch": "^2.2.1" }, @@ -12847,6 +13722,11 @@ "node": ">=0.10.0" } }, + "node_modules/shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, "node_modules/sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -13061,7 +13941,6 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -13104,7 +13983,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -13113,14 +13991,12 @@ "node_modules/spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -13129,8 +14005,7 @@ "node_modules/spdx-license-ids": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==" }, "node_modules/split": { "version": "0.2.10", @@ -13409,6 +14284,22 @@ "node": ">=0.10.0" } }, + "node_modules/string.prototype.padend": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz", + "integrity": "sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -13493,6 +14384,23 @@ "node": ">=6.9.0" } }, + "node_modules/stylehacks/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/stylehacks/node_modules/postcss-selector-parser": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", @@ -13506,6 +14414,25 @@ "node": ">=8" } }, + "node_modules/stylehacks/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylehacks/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/suncalc": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.8.0.tgz", @@ -13563,38 +14490,34 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, "node_modules/tailwindcss": { - "name": "@tailwindcss/postcss7-compat", - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.7.tgz", - "integrity": "sha512-1QkWUEeLV1AoNipMCE6IlL7XYScGb+DAzaXy35ooMDvl0G8kCMHBNqGxyVAnTcK8gyJNUzkKXExkUnbjAndd/g==", - "dev": true, + "version": "2.2.15", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.15.tgz", + "integrity": "sha512-WgV41xTMbnSoTNMNnJvShQZ+8GmY86DmXTrCgnsveNZJdlybfwCItV8kAqjYmU49YiFr+ofzmT1JlAKajBZboQ==", "dependencies": { - "arg": "^5.0.0", - "autoprefixer": "^9", + "arg": "^5.0.1", "bytes": "^3.0.0", - "chalk": "^4.1.1", + "chalk": "^4.1.2", "chokidar": "^3.5.2", - "color": "^3.2.0", - "cosmiconfig": "^7.0.0", + "color": "^4.0.1", + "cosmiconfig": "^7.0.1", "detective": "^5.2.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.7", "fs-extra": "^10.0.0", - "glob-parent": "^6.0.0", + "glob-parent": "^6.0.1", "html-tags": "^3.1.0", + "is-color-stop": "^1.1.0", "is-glob": "^4.0.1", "lodash": "^4.17.21", "lodash.topath": "^4.5.2", "modern-normalize": "^1.1.0", - "node-emoji": "^1.8.1", + "node-emoji": "^1.11.0", "normalize-path": "^3.0.0", "object-hash": "^2.2.0", - "postcss": "^7", - "postcss-functions": "^3", - "postcss-js": "^2", + "postcss-js": "^3.0.3", "postcss-load-config": "^3.1.0", - "postcss-nested": "^4", + "postcss-nested": "5.0.6", "postcss-selector-parser": "^6.0.6", "postcss-value-parser": "^4.1.0", "pretty-hrtime": "^1.0.3", @@ -13603,13 +14526,23 @@ "reduce-css-calc": "^2.1.8", "resolve": "^1.20.0", "tmp": "^0.2.1" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "autoprefixer": "^10.0.2", + "postcss": "^8.0.9" } }, "node_modules/tailwindcss/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -13621,16 +14554,14 @@ } }, "node_modules/tailwindcss/node_modules/arg": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.0.tgz", - "integrity": "sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ==", - "dev": true + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", + "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" }, "node_modules/tailwindcss/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -13642,46 +14573,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/tailwindcss/node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/tailwindcss/node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dev": true, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/color/-/color-4.0.1.tgz", + "integrity": "sha512-rpZjOKN5O7naJxkH2Rx1sZzzBgaiWECc6BYXjeCE6kF0kcASJYbUq02u7JqIHwCb/j3NhV+QhRL2683aICeGZA==", "dependencies": { - "color-convert": "^1.9.3", + "color-convert": "^2.0.1", "color-string": "^1.6.0" } }, @@ -13689,7 +14586,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -13700,39 +14596,12 @@ "node_modules/tailwindcss/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/tailwindcss/node_modules/color-string": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", - "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", - "dev": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/tailwindcss/node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/tailwindcss/node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/tailwindcss/node_modules/cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -13744,45 +14613,10 @@ "node": ">=10" } }, - "node_modules/tailwindcss/node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "node_modules/tailwindcss/node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss/node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/tailwindcss/node_modules/fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -13796,7 +14630,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.1.tgz", "integrity": "sha512-kEVjS71mQazDBHKcsq4E9u/vUzaLcw1A8EtUeydawvIWQCJM0qQ08G1H7/XTjFUulla6XQiDOG6MXSaG0HDKog==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -13808,7 +14641,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -13817,7 +14649,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -13829,20 +14660,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tailwindcss/node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/tailwindcss/node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -13860,29 +14681,73 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/tailwindcss/node_modules/postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", - "dev": true, + "node_modules/tailwindcss/node_modules/postcss-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", + "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "camelcase-css": "^2.0.1", + "postcss": "^8.1.6" }, "engines": { - "node": ">=4" + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "dependencies": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/tailwindcss/node_modules/postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "dependencies": { + "postcss-selector-parser": "^6.0.6" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" } }, "node_modules/tailwindcss/node_modules/purgecss": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz", "integrity": "sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==", - "dev": true, "dependencies": { "commander": "^6.0.0", "glob": "^7.0.0", @@ -13893,41 +14758,10 @@ "purgecss": "bin/purgecss.js" } }, - "node_modules/tailwindcss/node_modules/purgecss/node_modules/postcss": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", - "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", - "dev": true, - "dependencies": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", - "source-map-js": "^0.6.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/tailwindcss/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/tailwindcss/node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -13936,7 +14770,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -14068,7 +14901,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, "dependencies": { "rimraf": "^3.0.0" }, @@ -14080,7 +14912,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -15025,6 +15856,23 @@ "node": ">=8" } }, + "node_modules/uncss/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/uncss/node_modules/postcss-selector-parser": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", @@ -15038,6 +15886,25 @@ "node": ">=4" } }, + "node_modules/uncss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uncss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/underscore": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", @@ -15280,7 +16147,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -15509,7 +16375,6 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, "engines": { "node": ">= 6" } @@ -16498,14 +17363,6 @@ "to-fast-properties": "^2.0.0" } }, - "@fullhuman/postcss-purgecss": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.1.3.tgz", - "integrity": "sha512-kwOXw8fZ0Lt1QmeOOrd+o4Ibvp4UTEBFQbzvWldjlKv5n+G9sXfIPn1hh63IQIL8K8vbvv1oYMJiIUbuy9bGaA==", - "requires": { - "purgecss": "^3.1.3" - } - }, "@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", @@ -16812,87 +17669,6 @@ "physical-cpu-count": "^2.0.0" } }, - "@tailwindcss/postcss7-compat": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.1.2.tgz", - "integrity": "sha512-bH2kw6uyqLnDMP8wzDUsis5ovrsRzfHEyiL1McADvqlW54g6y0KVHX1xzO7PH8Fl5s0Sq8vDOAp4+3V8MEcZ9g==", - "requires": { - "@fullhuman/postcss-purgecss": "^3.1.3", - "autoprefixer": "^9", - "bytes": "^3.0.0", - "chalk": "^4.1.0", - "chokidar": "^3.5.1", - "color": "^3.1.3", - "detective": "^5.2.0", - "didyoumean": "^1.2.1", - "dlv": "^1.1.3", - "fast-glob": "^3.2.5", - "fs-extra": "^9.1.0", - "html-tags": "^3.1.0", - "lodash": "^4.17.21", - "lodash.topath": "^4.5.2", - "modern-normalize": "^1.0.0", - "node-emoji": "^1.8.1", - "normalize-path": "^3.0.0", - "object-hash": "^2.1.1", - "parse-glob": "^3.0.4", - "postcss": "^7", - "postcss-functions": "^3", - "postcss-js": "^2", - "postcss-nested": "^4", - "postcss-selector-parser": "^6.0.4", - "postcss-value-parser": "^4.1.0", - "pretty-hrtime": "^1.0.3", - "quick-lru": "^5.1.1", - "reduce-css-calc": "^2.1.8", - "resolve": "^1.20.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "@turf/along": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@turf/along/-/along-6.3.0.tgz", @@ -18247,8 +19023,7 @@ "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "@types/prompt-sync": { "version": "4.1.0", @@ -18526,27 +19301,22 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "autoprefixer": { - "version": "9.8.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", - "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.5.tgz", + "integrity": "sha512-2H5kQSsyoOMdIehTzIt/sC9ZDIgWqlkG/dbevm9B9xQZ1TDPBHpNUDW5ENqqQQzuaBWEo75JkV0LJe+o5Lnr5g==", + "peer": true, "requires": { - "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001109", - "colorette": "^1.2.1", + "browserslist": "^4.17.1", + "caniuse-lite": "^1.0.30001259", + "fraction.js": "^4.1.1", + "nanocolors": "^0.1.5", "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.32", "postcss-value-parser": "^4.1.0" } }, @@ -18929,15 +19699,15 @@ } }, "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", + "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", + "caniuse-lite": "^1.0.30001259", + "electron-to-chromium": "^1.3.846", "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "nanocolors": "^0.1.5", + "node-releases": "^1.1.76" } }, "btoa": { @@ -19074,9 +19844,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001223", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001223.tgz", - "integrity": "sha512-k/RYs6zc/fjbxTjaWZemeSmOjO0JJV+KguOBA3NwPup8uzxM1cMhR2BD9XmO86GuqaqTCO8CgkgH9Rz//vdDiA==" + "version": "1.0.30001259", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz", + "integrity": "sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==" }, "canvg": { "version": "3.0.7", @@ -19117,18 +19887,18 @@ } }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, "chownr": { @@ -19238,19 +20008,14 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", - "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -19514,6 +20279,31 @@ "requires": { "postcss": "^7.0.1", "timsort": "^0.3.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "css-line-break": { @@ -19676,6 +20466,31 @@ "cssnano-preset-default": "^4.0.8", "is-resolvable": "^1.0.0", "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "cssnano-preset-default": { @@ -19713,6 +20528,31 @@ "postcss-reduce-transforms": "^4.0.2", "postcss-svgo": "^4.0.3", "postcss-unique-selectors": "^4.0.1" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "cssnano-util-get-arguments": { @@ -19731,6 +20571,31 @@ "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", "requires": { "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "cssnano-util-same-parent": { @@ -20013,9 +20878,9 @@ } }, "didyoumean": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz", - "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "diff": { "version": "4.0.2", @@ -20166,9 +21031,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", - "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==" + "version": "1.3.846", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.846.tgz", + "integrity": "sha512-2jtSwgyiRzybHRxrc2nKI+39wH3AwQgn+sogQ+q814gv8hIFwrcZbV07Ea9f8AmK0ufPVZUvvAG1uZJ+obV4Jw==" }, "elliptic": { "version": "6.5.4", @@ -20511,16 +21376,15 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" } }, "fast-json-stable-stringify": { @@ -20632,6 +21496,12 @@ "mime-types": "^2.1.12" } }, + "fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", + "peer": true + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -20657,17 +21527,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -20826,38 +21685,6 @@ "path-is-absolute": "^1.0.0" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "requires": { - "is-glob": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - } - } - }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -21049,8 +21876,7 @@ "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "hsl-regex": { "version": "1.0.0", @@ -21343,7 +22169,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, "requires": { "import-from": "^3.0.0" } @@ -21361,7 +22186,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, "requires": { "resolve-from": "^5.0.0" }, @@ -21369,8 +22193,7 @@ "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" } } }, @@ -21550,11 +22373,6 @@ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -21874,8 +22692,7 @@ "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema": { "version": "0.2.3", @@ -23601,8 +24418,7 @@ "lilconfig": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", - "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", - "dev": true + "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==" }, "lineclip": { "version": "1.1.5", @@ -23612,8 +24428,7 @@ "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" }, "load-json-file": { "version": "1.1.0", @@ -23699,11 +24514,6 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" }, - "lodash.toarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", - "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" - }, "lodash.topath": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", @@ -23805,6 +24615,11 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, "meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", @@ -24045,11 +24860,15 @@ "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", "optional": true }, + "nanocolors": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.6.tgz", + "integrity": "sha512-2pvTw6vYRaBLGir2xR7MxaJtyWkrn+C53EpW8yPotG+pdAwBvt0Xwk4VJ6VHLY0aLthVZPvDfm9TdZvrvAm5UQ==" + }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", - "dev": true + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==" }, "nanomatch": { "version": "1.2.13", @@ -24103,11 +24922,11 @@ "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==" }, "node-emoji": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", "requires": { - "lodash.toarray": "^4.4.0" + "lodash": "^4.17.21" } }, "node-forge": { @@ -24177,15 +24996,14 @@ } }, "node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==" + "version": "1.1.76", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", + "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==" }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -24196,8 +25014,7 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" } } }, @@ -24209,13 +25026,71 @@ "normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "peer": true }, "normalize-url": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + } + } + }, "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", @@ -24236,11 +25111,6 @@ "boolbase": "~1.0.0" } }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -24291,9 +25161,9 @@ } }, "object-hash": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz", - "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" }, "object-inspect": { "version": "1.4.1", @@ -24692,6 +25562,16 @@ "to-regex": "^3.0.2" } }, + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", @@ -24707,6 +25587,14 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -24722,7 +25610,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "requires": { "callsites": "^3.0.0" }, @@ -24730,8 +25617,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" } } }, @@ -24747,32 +25633,6 @@ "safe-buffer": "^5.1.1" } }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - } - } - }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -24878,6 +25738,11 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==" + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -24915,15 +25780,35 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "version": "8.3.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.7.tgz", + "integrity": "sha512-9SaY7nnyQ63/WittqZYAvkkYPyKxchMKH71UDzeTmWuLSvxTRpeEeABZAzlCi55cuGcoFyoV/amX2BdsafQidQ==", "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "nanocolors": "^0.1.5", + "nanoid": "^3.1.25", + "source-map-js": "^0.6.2" + } + }, + "postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -24939,16 +25824,6 @@ } } }, - "postcss-calc": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", - "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", - "requires": { - "postcss": "^7.0.27", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - } - }, "postcss-colormin": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", @@ -24961,10 +25836,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -24976,65 +25874,15 @@ "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-functions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", - "integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=", - "requires": { - "glob": "^7.1.2", - "object-assign": "^4.1.1", - "postcss": "^6.0.9", - "postcss-value-parser": "^3.3.0" - }, "dependencies": { "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "requires": { - "chalk": "^2.4.1", + "chalk": "^2.4.2", "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "supports-color": "^6.1.0" } }, "postcss-value-parser": { @@ -25046,27 +25894,147 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, - "postcss-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", - "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", "requires": { - "camelcase-css": "^2.0.1", - "postcss": "^7.0.18" + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "postcss-load-config": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", - "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", - "dev": true, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", "requires": { - "import-cwd": "^3.0.0", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-merge-longhand": { @@ -25080,10 +26048,33 @@ "stylehacks": "^4.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25100,6 +26091,16 @@ "vendors": "^1.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-selector-parser": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", @@ -25109,6 +26110,19 @@ "indexes-of": "^1.0.1", "uniq": "^1.0.1" } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25121,10 +26135,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25139,10 +26176,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25159,10 +26219,33 @@ "uniqs": "^2.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25177,6 +26260,16 @@ "postcss-selector-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-selector-parser": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", @@ -25186,6 +26279,19 @@ "indexes-of": "^1.0.1", "uniq": "^1.0.1" } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25292,21 +26398,37 @@ } } }, - "postcss-nested": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-4.2.3.tgz", - "integrity": "sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw==", - "requires": { - "postcss": "^7.0.32", - "postcss-selector-parser": "^6.0.2" - } - }, "postcss-normalize-charset": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", "requires": { "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-normalize-display-values": { @@ -25319,10 +26441,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25337,10 +26482,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25355,10 +26523,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25372,10 +26563,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25389,10 +26603,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25406,10 +26643,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25424,10 +26684,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25440,10 +26723,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25457,10 +26763,33 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25473,6 +26802,31 @@ "caniuse-api": "^3.0.0", "has": "^1.0.0", "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-reduce-transforms": { @@ -25486,17 +26840,40 @@ "postcss-value-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, "postcss-selector-parser": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz", - "integrity": "sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -25512,10 +26889,33 @@ "svgo": "^1.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -25527,6 +26927,31 @@ "alphanum-sort": "^1.0.0", "postcss": "^7.0.0", "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-value-parser": { @@ -25700,41 +27125,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, - "purgecss": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-3.1.3.tgz", - "integrity": "sha512-hRSLN9mguJ2lzlIQtW4qmPS2kh6oMnA9RxdIYK8sz18QYqd6ePp4GNDl18oWHA1f2v2NEQIh51CO8s/E3YGckQ==", - "requires": { - "commander": "^6.0.0", - "glob": "^7.0.0", - "postcss": "^8.2.1", - "postcss-selector-parser": "^6.0.2" - }, - "dependencies": { - "postcss": { - "version": "8.2.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.14.tgz", - "integrity": "sha512-+jD0ZijcvyCqPQo/m/CW0UcARpdFylq04of+Q7RKX6f/Tu+dvpUI/9Sp81+i6/vJThnOBX09Quw0ZLOVwpzX3w==", - "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.22", - "source-map": "^0.6.1" - }, - "dependencies": { - "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -25880,9 +27270,9 @@ } }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "requires": { "picomatch": "^2.2.1" } @@ -26411,6 +27801,11 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, "sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -26583,8 +27978,7 @@ "source-map-js": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", - "dev": true + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==" }, "source-map-resolve": { "version": "0.5.3", @@ -26623,7 +28017,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -26632,14 +28025,12 @@ "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -26648,8 +28039,7 @@ "spdx-license-ids": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==" }, "split": { "version": "0.2.10", @@ -26869,6 +28259,16 @@ } } }, + "string.prototype.padend": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz", + "integrity": "sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + } + }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -26929,6 +28329,16 @@ "postcss-selector-parser": "^3.0.0" }, "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-selector-parser": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", @@ -26938,6 +28348,19 @@ "indexes-of": "^1.0.1", "uniq": "^1.0.1" } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -26986,37 +28409,34 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, "tailwindcss": { - "version": "npm:@tailwindcss/postcss7-compat@2.2.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.7.tgz", - "integrity": "sha512-1QkWUEeLV1AoNipMCE6IlL7XYScGb+DAzaXy35ooMDvl0G8kCMHBNqGxyVAnTcK8gyJNUzkKXExkUnbjAndd/g==", - "dev": true, + "version": "2.2.15", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.15.tgz", + "integrity": "sha512-WgV41xTMbnSoTNMNnJvShQZ+8GmY86DmXTrCgnsveNZJdlybfwCItV8kAqjYmU49YiFr+ofzmT1JlAKajBZboQ==", "requires": { - "arg": "^5.0.0", - "autoprefixer": "^9", + "arg": "^5.0.1", "bytes": "^3.0.0", - "chalk": "^4.1.1", + "chalk": "^4.1.2", "chokidar": "^3.5.2", - "color": "^3.2.0", - "cosmiconfig": "^7.0.0", + "color": "^4.0.1", + "cosmiconfig": "^7.0.1", "detective": "^5.2.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.7", "fs-extra": "^10.0.0", - "glob-parent": "^6.0.0", + "glob-parent": "^6.0.1", "html-tags": "^3.1.0", + "is-color-stop": "^1.1.0", "is-glob": "^4.0.1", "lodash": "^4.17.21", "lodash.topath": "^4.5.2", "modern-normalize": "^1.1.0", - "node-emoji": "^1.8.1", + "node-emoji": "^1.11.0", "normalize-path": "^3.0.0", "object-hash": "^2.2.0", - "postcss": "^7", - "postcss-functions": "^3", - "postcss-js": "^2", + "postcss-js": "^3.0.3", "postcss-load-config": "^3.1.0", - "postcss-nested": "^4", + "postcss-nested": "5.0.6", "postcss-selector-parser": "^6.0.6", "postcss-value-parser": "^4.1.0", "pretty-hrtime": "^1.0.3", @@ -27031,86 +28451,37 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } }, "arg": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.0.tgz", - "integrity": "sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ==", - "dev": true + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", + "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dev": true, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/color/-/color-4.0.1.tgz", + "integrity": "sha512-rpZjOKN5O7naJxkH2Rx1sZzzBgaiWECc6BYXjeCE6kF0kcASJYbUq02u7JqIHwCb/j3NhV+QhRL2683aICeGZA==", "requires": { - "color-convert": "^1.9.3", + "color-convert": "^2.0.1", "color-string": "^1.6.0" - }, - "dependencies": { - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - } } }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -27118,24 +28489,12 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "color-string": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", - "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "requires": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -27144,41 +28503,10 @@ "yaml": "^1.10.0" } }, - "didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -27189,7 +28517,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.1.tgz", "integrity": "sha512-kEVjS71mQazDBHKcsq4E9u/vUzaLcw1A8EtUeydawvIWQCJM0qQ08G1H7/XTjFUulla6XQiDOG6MXSaG0HDKog==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -27197,30 +28524,21 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, - "object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "dev": true - }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -27231,64 +28549,55 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, - "postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", - "dev": true, + "postcss-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", + "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "camelcase-css": "^2.0.1", + "postcss": "^8.1.6" + } + }, + "postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "requires": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "requires": { + "postcss-selector-parser": "^6.0.6" } }, "purgecss": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz", "integrity": "sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw==", - "dev": true, "requires": { "commander": "^6.0.0", "glob": "^7.0.0", "postcss": "^8.2.1", "postcss-selector-parser": "^6.0.2" - }, - "dependencies": { - "postcss": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", - "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", - "dev": true, - "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", - "source-map-js": "^0.6.2" - } - } - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" } }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -27405,7 +28714,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, "requires": { "rimraf": "^3.0.0" }, @@ -27414,7 +28722,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -28184,6 +29491,16 @@ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" }, + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, "postcss-selector-parser": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", @@ -28193,6 +29510,19 @@ "indexes-of": "^1.0.1", "uniq": "^1.0.1" } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -28396,7 +29726,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -28592,8 +29921,7 @@ "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, "yn": { "version": "3.1.1", diff --git a/package.json b/package.json index c8b17cc73..f7fd84e3d 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,10 @@ "main": "index.js", "scripts": { "increase-memory": "export NODE_OPTIONS=--max_old_space_size=4096", - "start": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory && parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.png vendor/* vendor/*/*", + "start": "npm run start:prepare && npm-run-all --parallel start:parallel:*", + "start:prepare": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory", + "start:parallel:parcel": "parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.png vendor/* vendor/*/*", + "start:parallel:tailwindcli": "tailwindcss -i index.css -o css/index-tailwind-output.css --watch", "test": "ts-node test/TestAll.ts", "init": "npm ci && npm run generate && npm run generate:editor-layer-index && npm run generate:layouts && npm run clean", "add-weblate-upstream": "git remote add weblate-layers https://hosted.weblate.org/git/mapcomplete/layer-translations/ ; git remote update weblate-layers", @@ -51,7 +54,6 @@ "license": "GPL", "dependencies": { "@babel/preset-env": "7.13.8", - "@tailwindcss/postcss7-compat": "^2.0.2", "@turf/buffer": "^6.3.0", "@turf/collect": "^6.3.0", "@turf/distance": "^6.3.0", @@ -63,7 +65,6 @@ "@types/leaflet.markercluster": "^1.4.3", "@types/lz-string": "^1.3.34", "@types/prompt-sync": "^4.1.0", - "autoprefixer": "^9.8.6", "country-language": "^0.1.7", "email-validator": "^2.0.4", "escape-html": "^1.0.3", @@ -80,12 +81,13 @@ "lz-string": "^1.4.4", "mangrove-reviews": "^0.1.3", "moment": "^2.29.0", + "npm-run-all": "^4.1.5", "opening_hours": "^3.6.0", "osm-auth": "^1.0.2", "osmtogeojson": "^3.0.0-beta.4", "parcel": "^1.2.4", - "postcss": "^7.0.36", "prompt-sync": "^4.2.0", + "tailwindcss": "^2.2.15", "tslint": "^6.1.3" }, "devDependencies": { @@ -96,7 +98,6 @@ "marked": "^2.0.0", "read-file": "^0.2.0", "sharp": "^0.28.3", - "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.7", "ts-node": "^9.0.0", "ts-node-dev": "^1.0.0-pre.63", "tslint-no-circular-imports": "^0.7.0", diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index ca0c0cdaa..000000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - } -} \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 7d9320cc4..fe744d0b2 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,9 +1,10 @@ const plugin = require('tailwindcss/plugin') module.exports = { + mode: 'jit', purge: [ - // './**/*.html', - // './**/*.js', + './**/*.html', + './**/*.ts', ], darkMode: false, // or 'media' or 'class' theme: { @@ -20,9 +21,9 @@ module.exports = { } }, plugins: [ - plugin(function ({addVariant, e}) { - addVariant('landscape', ({modifySelectors, separator}) => { - modifySelectors(({className}) => { + plugin(function ({ addVariant, e }) { + addVariant('landscape', ({ modifySelectors, separator }) => { + modifySelectors(({ className }) => { return `.${e(`landscape${separator}${className}`)}:landscape` }) }) From 3c459f00fd84b27581bc16e61eae6b1310a46d62 Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Wed, 22 Sep 2021 19:56:36 +0200 Subject: [PATCH 031/110] Update .userbadge-login to Tailwind Migrate some custom styles to Tailwind to test new setup. --- UI/BigComponents/UserBadge.ts | 4 ++-- css/userbadge.css | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/UI/BigComponents/UserBadge.ts b/UI/BigComponents/UserBadge.ts index 62f74e0ae..297e470e3 100644 --- a/UI/BigComponents/UserBadge.ts +++ b/UI/BigComponents/UserBadge.ts @@ -18,7 +18,7 @@ export default class UserBadge extends Toggle { const loginButton = Translations.t.general.loginWithOpenStreetMap .Clone() - .SetClass("userbadge-login pt-3 w-full h-full") + .SetClass("userbadge-login inline-flex justify-center items-center w-full h-full text-lg font-bold min-w-[20em]") .onClick(() => State.state.osmConnection.AttemptLogin()); @@ -138,4 +138,4 @@ export default class UserBadge extends Toggle { } -} \ No newline at end of file +} diff --git a/css/userbadge.css b/css/userbadge.css index 316bb7aa7..3b272cc48 100644 --- a/css/userbadge.css +++ b/css/userbadge.css @@ -33,16 +33,7 @@ } .userbadge-login { - font-weight: bold; - font-size: large; background-color: var(--subtle-detail-color) !important; color: var(--subtle-detail-color-contrast); height: 3em; - - display: inline-block; - text-align: center; - margin: 0; - - min-width: 20em; - pointer-events: all; } From b1900cdbc660fe4d8d9ece956d2d48c1990ac204 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 22 Sep 2021 20:44:53 +0200 Subject: [PATCH 032/110] Small robustifications --- Models/ThemeConfig/LayerConfig.ts | 9 ++++++--- UI/Base/MinimapImplementation.ts | 16 ++++++++++++---- .../charging_station/charging_station.protojson | 4 +++- assets/themes/uk_addresses/uk_addresses.json | 7 +------ 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index c3cadc1e0..18c9fe3b4 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -154,6 +154,9 @@ export default class LayerConfig { this.minzoom = json.minzoom ?? 0; this.minzoomVisible = json.minzoomVisible ?? this.minzoom; this.wayHandling = json.wayHandling ?? 0; + if(json.presets !== undefined && json.presets?.map === undefined){ + throw "Presets should be a list of items (at "+context+")" + } this.presets = (json.presets ?? []).map((pr, i) => { let preciseInput = undefined; @@ -492,8 +495,8 @@ export default class LayerConfig { const iconUrlStatic = render(this.icon); const self = this; - function genHtmlFromString(sourcePart: string, rotation: string): BaseUIElement { - const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`; + function genHtmlFromString(sourcePart: string, rotation: string, style?: string): BaseUIElement { + style = style ?? `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`; let html: BaseUIElement = new FixedUiElement( `` ); @@ -537,7 +540,7 @@ export default class LayerConfig { .filter((prt) => prt != ""); for (const badgePartStr of partDefs) { - badgeParts.push(genHtmlFromString(badgePartStr, "0")); + badgeParts.push(genHtmlFromString(badgePartStr, "0", `width:unset;height:100%;display:block;`)); } const badgeCompound = new Combine(badgeParts).SetStyle( diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts index 00fbc0f09..1761d1ada 100644 --- a/UI/Base/MinimapImplementation.ts +++ b/UI/Base/MinimapImplementation.ts @@ -50,7 +50,7 @@ export default class MinimapImplementation extends BaseUIElement implements Mini if (typeof factor === "number") { bounds = leaflet.getBounds() leaflet.setMaxBounds(bounds.pad(factor)) - }else{ + } else { // @ts-ignore leaflet.setMaxBounds(factor.toLeaflet()) bounds = leaflet.getBounds() @@ -114,8 +114,12 @@ export default class MinimapImplementation extends BaseUIElement implements Mini const self = this; // @ts-ignore const resizeObserver = new ResizeObserver(_ => { - self.InitMap(); - self.leafletMap?.data?.invalidateSize() + try { + self.InitMap(); + self.leafletMap?.data?.invalidateSize() + } catch (e) { + console.error("Could not construct a minimap:", e) + } }); resizeObserver.observe(div); @@ -141,8 +145,12 @@ export default class MinimapImplementation extends BaseUIElement implements Mini const location = this._location; const self = this; let currentLayer = this._background.data.layer() + let latLon = <[number, number]>[location.data?.lat ?? 0, location.data?.lon ?? 0] + if(isNaN(latLon[0]) || isNaN(latLon[1])){ + latLon = [0,0] + } const options = { - center: <[number, number]>[location.data?.lat ?? 0, location.data?.lon ?? 0], + center: latLon, zoom: location.data?.zoom ?? 2, layers: [currentLayer], zoomControl: false, diff --git a/assets/layers/charging_station/charging_station.protojson b/assets/layers/charging_station/charging_station.protojson index 39714ef8a..f33620e80 100644 --- a/assets/layers/charging_station/charging_station.protojson +++ b/assets/layers/charging_station/charging_station.protojson @@ -538,7 +538,9 @@ } }, { - "if": "amenity=charging_station", + "if": { + "and": ["amenity=charging_station","operational_status="] + }, "then": { "en": "This charging station works", "nl": "Dit oplaadpunt werkt" diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index e43f867ed..b89d25e60 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -213,12 +213,7 @@ "then": "#ff0" } ] - }, - "presets": [ - { - - } - ] + } }, { "id": "named_streets", From c5e9448720f5b1afd0ea8b380492be3300dc2a1d Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 26 Sep 2021 17:36:39 +0200 Subject: [PATCH 033/110] Add initial clustering per tile, very broken --- Docs/Tools/GenerateSeries.ts | 4 +- InitUiElements.ts | 118 +++++-- Logic/Actors/OverpassFeatureSource.ts | 79 +++-- Logic/ExtraFunction.ts | 5 +- Logic/FeatureSource/FeaturePipeline.ts | 31 +- .../Sources/FeatureSourceMerger.ts | 3 +- .../Sources/FilteringFeatureSource.ts | 26 +- Logic/FeatureSource/Sources/GeoJsonSource.ts | 6 +- .../Sources/RememberingSource.ts | 14 +- .../Sources/SimpleFeatureSource.ts | 3 +- .../Sources/StaticFeatureSource.ts | 7 +- .../WayHandlingApplyingFeatureSource.ts | 5 +- .../DynamicGeoJsonTileSource.ts | 4 +- .../TiledFeatureSource/DynamicTileSource.ts | 7 +- .../TiledFeatureSource/TileHierarchyMerger.ts | 5 +- .../TiledFeatureSource/TiledFeatureSource.ts | 17 +- .../TiledFromLocalStorageSource.ts | 13 +- Logic/GeoOperations.ts | 11 +- .../ImageProviders/ImageAttributionSource.ts | 9 +- Logic/ImageProviders/Imgur.ts | 52 +-- Logic/ImageProviders/Mapillary.ts | 53 ++- Logic/ImageProviders/Wikimedia.ts | 46 +-- Logic/MetaTagging.ts | 66 ++-- Logic/Osm/OsmConnection.ts | 1 + Logic/SimpleMetaTagger.ts | 52 +-- Models/Constants.ts | 1 + Models/ThemeConfig/LayerConfig.ts | 12 +- Models/ThemeConfig/LayoutConfig.ts | 4 +- Models/TileRange.ts | 102 ++++++ UI/Base/ScrollableFullScreen.ts | 2 + UI/Base/VariableUIElement.ts | 22 +- UI/BaseUIElement.ts | 2 - UI/Image/Attribution.ts | 11 +- UI/Image/DeleteImage.ts | 2 +- UI/Input/LocationInput.ts | 1 - UI/Popup/EditableTagRendering.ts | 6 +- UI/Popup/SplitRoadWizard.ts | 2 +- UI/ShowDataLayer/PerTileCountAggregator.ts | 156 +++++++++ UI/ShowDataLayer/ShowDataLayer.ts | 37 ++- UI/ShowDataLayer/ShowDataLayerOptions.ts | 1 + UI/ShowDataLayer/ShowTileInfo.ts | 79 +++++ Utils.ts | 105 +----- .../charging_station/charging_station.json | 136 +++++--- .../layers/drinking_water/drinking_water.json | 307 +++++++++--------- assets/layers/food/food.json | 1 + .../public_bookcase/public_bookcase.json | 2 +- assets/themes/benches/benches.json | 2 +- assets/themes/bicyclelib/bicyclelib.json | 2 +- .../bike_monitoring_stations.json | 2 +- assets/themes/binoculars/binoculars.json | 2 +- assets/themes/buurtnatuur/buurtnatuur.json | 2 +- .../themes/cafes_and_pubs/cafes_and_pubs.json | 2 +- assets/themes/campersite/campersite.json | 2 +- .../charging_stations/charging_stations.json | 2 +- assets/themes/climbing/climbing.json | 2 +- .../themes/cycle_highways/cycle_highways.json | 2 +- assets/themes/cycle_infra/cycle_infra.json | 2 +- assets/themes/cyclofix/cyclofix.json | 2 +- .../themes/facadegardens/facadegardens.json | 2 +- assets/themes/food/food.json | 2 +- assets/themes/fritures/fritures.json | 2 +- assets/themes/fruit_trees/fruit_trees.json | 2 +- assets/themes/ghostbikes/ghostbikes.json | 2 +- assets/themes/grb.json | 2 +- assets/themes/hackerspaces/hackerspaces.json | 2 +- assets/themes/hailhydrant/hailhydrant.json | 2 +- assets/themes/maps/maps.json | 2 +- assets/themes/nature/nature.json | 2 +- assets/themes/natuurpunt/natuurpunt.json | 2 +- .../observation_towers.json | 2 +- assets/themes/parkings/parkings.json | 2 +- assets/themes/personal/personal.json | 2 +- assets/themes/play_forests/play_forests.json | 2 +- assets/themes/playgrounds/playgrounds.json | 2 +- assets/themes/shops/shops.json | 2 +- assets/themes/speelplekken/speelplekken.json | 2 +- .../speelplekken/speelplekken_temp.json | 2 +- .../themes/sport_pitches/sport_pitches.json | 2 +- assets/themes/surveillance/surveillance.json | 2 +- .../toerisme_vlaanderen.json | 2 +- assets/themes/toilets/toilets.json | 2 +- assets/themes/trees/trees.json | 2 +- assets/themes/uk_addresses/uk_addresses.json | 2 +- assets/themes/waste_basket/waste_basket.json | 2 +- assets/themes/widths/width.json | 2 +- scripts/ScriptUtils.ts | 7 +- scripts/generateCache.ts | 12 +- scripts/generateTranslations.ts | 6 +- 88 files changed, 1080 insertions(+), 651 deletions(-) create mode 100644 UI/ShowDataLayer/PerTileCountAggregator.ts create mode 100644 UI/ShowDataLayer/ShowTileInfo.ts diff --git a/Docs/Tools/GenerateSeries.ts b/Docs/Tools/GenerateSeries.ts index 5cced5a6b..73e97e9a4 100644 --- a/Docs/Tools/GenerateSeries.ts +++ b/Docs/Tools/GenerateSeries.ts @@ -75,9 +75,7 @@ class StatsDownloader { while (url) { ScriptUtils.erasableLog(`Downloading stats for ${year}-${month}, page ${page} ${url}`) - const result = await ScriptUtils.DownloadJSON(url, { - headers: headers - }) + const result = await ScriptUtils.DownloadJSON(url, headers) page++; allFeatures.push(...result.features) if (result.features === undefined) { diff --git a/InitUiElements.ts b/InitUiElements.ts index c36b22b77..0bc6506d1 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -15,7 +15,6 @@ import Link from "./UI/Base/Link"; import * as personal from "./assets/themes/personal/personal.json"; import * as L from "leaflet"; import Img from "./UI/Base/Img"; -import UserDetails from "./Logic/Osm/OsmConnection"; import Attribution from "./UI/BigComponents/Attribution"; import BackgroundLayerResetter from "./Logic/Actors/BackgroundLayerResetter"; import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs"; @@ -38,6 +37,9 @@ import Minimap from "./UI/Base/Minimap"; import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler"; import Combine from "./UI/Base/Combine"; import {SubtleButton} from "./UI/Base/SubtleButton"; +import ShowTileInfo from "./UI/ShowDataLayer/ShowTileInfo"; +import {Tiles} from "./Models/TileRange"; +import PerTileCountAggregator from "./UI/ShowDataLayer/PerTileCountAggregator"; export class InitUiElements { static InitAll( @@ -167,22 +169,38 @@ export class InitUiElements { ).AttachTo("messagesbox"); } - State.state.osmConnection.userDetails - .map((userDetails: UserDetails) => userDetails?.home) - .addCallbackAndRunD((home) => { - const color = getComputedStyle(document.body).getPropertyValue( - "--subtle-detail-color" - ); - const icon = L.icon({ - iconUrl: Img.AsData( - Svg.home_white_bg.replace(/#ffffff/g, color) - ), - iconSize: [30, 30], - iconAnchor: [15, 15], - }); - const marker = L.marker([home.lat, home.lon], {icon: icon}); - marker.addTo(State.state.leafletMap.data); + function addHomeMarker() { + const userDetails = State.state.osmConnection.userDetails.data; + if (userDetails === undefined) { + return false; + } + console.log("Adding home location of ", userDetails) + const home = userDetails.home; + if (home === undefined) { + return userDetails.loggedIn; // If logged in, the home is not set and we unregister. If not logged in, we stay registered if a login still comes + } + const leaflet = State.state.leafletMap.data; + if (leaflet === undefined) { + return false; + } + const color = getComputedStyle(document.body).getPropertyValue( + "--subtle-detail-color" + ); + const icon = L.icon({ + iconUrl: Img.AsData( + Svg.home_white_bg.replace(/#ffffff/g, color) + ), + iconSize: [30, 30], + iconAnchor: [15, 15], }); + const marker = L.marker([home.lat, home.lon], {icon: icon}); + marker.addTo(leaflet); + return true; + } + + State.state.osmConnection.userDetails + .addCallbackAndRunD(_ => addHomeMarker()); + State.state.leafletMap.addCallbackAndRunD(_ => addHomeMarker()) if (layoutToUse.id === personal.id) { updateFavs(); @@ -209,7 +227,7 @@ export class InitUiElements { static LoadLayoutFromHash( userLayoutParam: UIEventSource ): [LayoutConfig, string] { - let hash = location.hash.substr(1); + let hash = location.hash.substr(1); try { const layoutFromBase64 = userLayoutParam.data; // layoutFromBase64 contains the name of the theme. This is partly to do tracking with goat counter @@ -249,18 +267,18 @@ export class InitUiElements { userLayoutParam.setData(layoutToUse.id); return [layoutToUse, btoa(Utils.MinifyJSON(JSON.stringify(json)))]; } catch (e) { - - if(hash === undefined || hash.length < 10){ + + if (hash === undefined || hash.length < 10) { e = "Did you effectively add a theme? It seems no data could be found." } - + new Combine([ "Error: could not parse the custom layout:", - new FixedUiElement(""+e).SetClass("alert"), - new SubtleButton("./assets/svg/mapcomplete_logo.svg", - "Go back to the theme overview", - {url: window.location.protocol+"//"+ window.location.hostname+"/index.html", newTab: false}) - + new FixedUiElement("" + e).SetClass("alert"), + new SubtleButton("./assets/svg/mapcomplete_logo.svg", + "Go back to the theme overview", + {url: window.location.protocol + "//" + window.location.hostname + "/index.html", newTab: false}) + ]) .SetClass("flex flex-col") .AttachTo("centermessage"); @@ -361,12 +379,12 @@ export class InitUiElements { const layout = State.state.layoutToUse.data; if (layout.lockLocation) { if (layout.lockLocation === true) { - const tile = Utils.embedded_tile( + const tile = Tiles.embedded_tile( layout.startLat, layout.startLon, layout.startZoom - 1 ); - const bounds = Utils.tile_bounds(tile.z, tile.x, tile.y); + const bounds = Tiles.tile_bounds(tile.z, tile.x, tile.y); // We use the bounds to get a sense of distance for this zoom level const latDiff = bounds[0][0] - bounds[1][0]; const lonDiff = bounds[0][1] - bounds[1][1]; @@ -402,6 +420,9 @@ export class InitUiElements { const flayer = { isDisplayed: isDisplayed, layerDef: layer, + isSufficientlyZoomed: state.locationControl.map(l => { + return l.zoom >= (layer.minzoomVisible ?? layer.minzoom) + }), appliedFilters: new UIEventSource(undefined), }; flayers.push(flayer); @@ -409,13 +430,54 @@ export class InitUiElements { return flayers; }); + const clusterCounter = new PerTileCountAggregator(State.state.locationControl.map(l => { + const z = l.zoom + 1 + if(z < 7){ + return 7 + } + return z + })) + const clusterShow = Math.min(...State.state.layoutToUse.data.layers.map(layer => layer.minzoomVisible ?? layer.minzoom)) + new ShowDataLayer({ + features: clusterCounter, + leafletMap: State.state.leafletMap, + layerToShow: ShowTileInfo.styling, + doShowLayer: State.state.locationControl.map(l => l.zoom < clusterShow) + }) State.state.featurePipeline = new FeaturePipeline( source => { + const clustering = State.state.layoutToUse.data.clustering + const doShowFeatures = source.features.map( + f => { + const z = State.state.locationControl.data.zoom + if(z >= clustering.maxZoom){ + return true + } + if(z < source.layer.layerDef.minzoom){ + return false; + } + if(f.length > clustering.minNeededElements){ + console.log("Activating clustering for tile ", Tiles.tile_from_index(source.tileIndex)," as it has ", f.length, "features (clustering starts at)", clustering.minNeededElements) + return false + } + + return true + }, [State.state.locationControl] + ) + clusterCounter.addTile(source, doShowFeatures.map(b => !b)) + + /* + new ShowTileInfo({source: source, + leafletMap: State.state.leafletMap, + layer: source.layer.layerDef, + doShowLayer: doShowFeatures.map(b => !b) + })*/ new ShowDataLayer( { features: source, leafletMap: State.state.leafletMap, - layerToShow: source.layer.layerDef + layerToShow: source.layer.layerDef, + doShowLayer: doShowFeatures } ); }, state diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index 1e7ace7cd..7ddafacd8 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -24,9 +24,9 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour public readonly sufficientlyZoomed: UIEventSource; public readonly runningQuery: UIEventSource = new UIEventSource(false); public readonly timeout: UIEventSource = new UIEventSource(0); - + public readonly relationsTracker: RelationsTracker; - + private readonly retries: UIEventSource = new UIEventSource(0); /** @@ -44,7 +44,6 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; } - /** * The most important layer should go first, as that one gets first pick for the questions */ @@ -57,6 +56,7 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour readonly overpassTimeout: UIEventSource; readonly overpassMaxZoom: UIEventSource }) { + console.trace("Initializing an overpass FS") this.state = state @@ -153,7 +153,12 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour return new Overpass(new Or(filters), extraScripts, this.state.overpassUrl, this.state.overpassTimeout, this.relationsTracker); } - private update(): void { + private update() { + this.updateAsync().then(_ => { + }) + } + + private async updateAsync(): Promise { if (this.runningQuery.data) { console.log("Still running a query, not updating"); return; @@ -179,54 +184,46 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour const self = this; const overpass = this.GetFilter(); - + if (overpass === undefined) { return; } this.runningQuery.setData(true); - overpass.queryGeoJson(queryBounds). - then(([data, date]) => { - self._previousBounds.get(z).push(queryBounds); - self.retries.setData(0); - const features = data.features.map(f => ({feature: f, freshness: date})); - SimpleMetaTagger.objectMetaInfo.addMetaTags(features) - try{ - self.features.setData(features); - }catch(e){ - console.error("Got the overpass response, but could not process it: ", e, e.stack) - } - self.runningQuery.setData(false); - }) - .catch((reason) => { + let data: any = undefined + let date: Date = undefined + + do { + + try { + [data, date] = await overpass.queryGeoJson(queryBounds) + } catch (e) { + console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to`, e); + self.retries.data++; - self.ForceRefresh(); - self.timeout.setData(self.retries.data * 5); - console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to`, reason); self.retries.ping(); + + self.timeout.setData(self.retries.data * 5); self.runningQuery.setData(false); - function countDown() { - window?.setTimeout( - function () { - if (self.timeout.data > 1) { - self.timeout.setData(self.timeout.data - 1); - window.setTimeout( - countDown, - 1000 - ) - } else { - self.timeout.setData(0); - self.update() - } - }, 1000 - ) + while (self.timeout.data > 0) { + await Utils.waitFor(1000) + self.timeout.data-- + self.timeout.ping(); } - - countDown(); - } - ); + } while (data === undefined); + + self._previousBounds.get(z).push(queryBounds); + self.retries.setData(0); + + try { + data.features.forEach(feature => SimpleMetaTagger.objectMetaInfo.applyMetaTagsOnFeature(feature, date)); + self.features.setData(data.features.map(f => ({feature: f, freshness: date}))); + } catch (e) { + console.error("Got the overpass response, but could not process it: ", e, e.stack) + } + self.runningQuery.setData(false); } diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index 04f7a64f6..5839fca08 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -256,7 +256,7 @@ export class ExtraFunction { let closestFeatures: { feat: any, distance: number }[] = []; for(const featureList of features) { for (const otherFeature of featureList) { - if (otherFeature == feature || otherFeature.id == feature.id) { + if (otherFeature === feature || otherFeature.id === feature.id) { continue; // We ignore self } let distance = undefined; @@ -268,7 +268,8 @@ export class ExtraFunction { [feature._lon, feature._lat] ) } - if (distance === undefined) { + if (distance === undefined || distance === null) { + console.error("Could not calculate the distance between", feature, "and", otherFeature) throw "Undefined distance!" } if (distance > maxDistance) { diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index c6206eac4..1e033dad7 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -37,7 +37,7 @@ export default class FeaturePipeline implements FeatureSourceState { private readonly perLayerHierarchy: Map; constructor( - handleFeatureSource: (source: FeatureSourceForLayer) => void, + handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void, state: { filteredLayers: UIEventSource, locationControl: UIEventSource, @@ -52,7 +52,6 @@ export default class FeaturePipeline implements FeatureSourceState { const self = this const updater = new OverpassFeatureSource(state); - updater.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(updater)) this.overpassUpdater = updater; this.sufficientlyZoomed = updater.sufficientlyZoomed this.runningQuery = updater.runningQuery @@ -65,14 +64,15 @@ export default class FeaturePipeline implements FeatureSourceState { const perLayerHierarchy = new Map() this.perLayerHierarchy = perLayerHierarchy - const patchedHandleFeatureSource = function (src: FeatureSourceForLayer & IndexedFeatureSource) { + const patchedHandleFeatureSource = function (src: FeatureSourceForLayer & IndexedFeatureSource & Tiled) { // This will already contain the merged features for this tile. In other words, this will only be triggered once for every tile const srcFiltered = - new FilteringFeatureSource(state, + new FilteringFeatureSource(state, src.tileIndex, new WayHandlingApplyingFeatureSource( new ChangeGeometryApplicator(src, state.changes) ) ) + handleFeatureSource(srcFiltered) self.somethingLoaded.setData(true) }; @@ -102,10 +102,12 @@ export default class FeaturePipeline implements FeatureSourceState { if (source.geojsonZoomLevel === undefined) { // This is a 'load everything at once' geojson layer - // We split them up into tiles + // We split them up into tiles anyway const src = new GeoJsonSource(filteredLayer) TiledFeatureSource.createHierarchy(src, { layer: src.layer, + minZoomLevel:14, + dontEnforceMinZoom: true, registerTile: (tile) => { new RegisteringAllFromFeatureSourceActor(tile) addToHierarchy(tile, id) @@ -115,14 +117,11 @@ export default class FeaturePipeline implements FeatureSourceState { } else { new DynamicGeoJsonTileSource( filteredLayer, - src => TiledFeatureSource.createHierarchy(src, { - layer: src.layer, - registerTile: (tile) => { + tile => { new RegisteringAllFromFeatureSourceActor(tile) addToHierarchy(tile, id) tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) - } - }), + }, state ) } @@ -133,13 +132,17 @@ export default class FeaturePipeline implements FeatureSourceState { new PerLayerFeatureSourceSplitter(state.filteredLayers, (source) => TiledFeatureSource.createHierarchy(source, { layer: source.layer, + minZoomLevel: 14, + dontEnforceMinZoom: true, registerTile: (tile) => { // We save the tile data for the given layer to local storage new SaveTileToLocalStorageActor(tile, tile.tileIndex) - addToHierarchy(tile, source.layer.layerDef.id); + addToHierarchy(new RememberingSource(tile), source.layer.layerDef.id); + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + } }), - new RememberingSource(updater)) + updater) // Also load points/lines that are newly added. @@ -152,6 +155,8 @@ export default class FeaturePipeline implements FeatureSourceState { addToHierarchy(perLayer, perLayer.layer.layerDef.id) // AT last, we always apply the metatags whenever possible perLayer.features.addCallbackAndRunD(_ => self.applyMetaTags(perLayer)) + perLayer.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(perLayer)) + }, newGeometry ) @@ -166,6 +171,7 @@ export default class FeaturePipeline implements FeatureSourceState { private applyMetaTags(src: FeatureSourceForLayer){ const self = this + console.log("Applying metatagging onto ", src.name) MetaTagging.addMetatags( src.features.data, { @@ -183,6 +189,7 @@ export default class FeaturePipeline implements FeatureSourceState { private updateAllMetaTagging() { const self = this; + console.log("Reupdating all metatagging") this.perLayerHierarchy.forEach(hierarchy => { hierarchy.loadedTiles.forEach(src => { self.applyMetaTags(src) diff --git a/Logic/FeatureSource/Sources/FeatureSourceMerger.ts b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts index fb349ae5d..44ae28543 100644 --- a/Logic/FeatureSource/Sources/FeatureSourceMerger.ts +++ b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts @@ -7,6 +7,7 @@ import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from import FilteredLayer from "../../../Models/FilteredLayer"; import {BBox} from "../../GeoOperations"; import {Utils} from "../../../Utils"; +import {Tiles} from "../../../Models/TileRange"; export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled, IndexedFeatureSource { @@ -23,7 +24,7 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled this.bbox = bbox; this._sources = sources; this.layer = layer; - this.name = "FeatureSourceMerger("+layer.layerDef.id+", "+Utils.tile_from_index(tileIndex).join(",")+")" + this.name = "FeatureSourceMerger("+layer.layerDef.id+", "+Tiles.tile_from_index(tileIndex).join(",")+")" const self = this; const handledSources = new Set(); diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index 6848f9f26..70d5a566c 100644 --- a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -1,24 +1,29 @@ import {UIEventSource} from "../../UIEventSource"; import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; import FilteredLayer from "../../../Models/FilteredLayer"; -import {FeatureSourceForLayer} from "../FeatureSource"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import Hash from "../../Web/Hash"; +import {BBox} from "../../GeoOperations"; -export default class FilteringFeatureSource implements FeatureSourceForLayer { +export default class FilteringFeatureSource implements FeatureSourceForLayer , Tiled { public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name; public readonly layer: FilteredLayer; - +public readonly tileIndex : number + public readonly bbox : BBox constructor( state: { locationControl: UIEventSource<{ zoom: number }>, selectedElement: UIEventSource, }, + tileIndex, upstream: FeatureSourceForLayer ) { const self = this; this.name = "FilteringFeatureSource("+upstream.name+")" + this.tileIndex = tileIndex + this.bbox = BBox.fromTileIndex(tileIndex) this.layer = upstream.layer; const layer = upstream.layer; @@ -51,7 +56,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer { return false; } } - if (!FilteringFeatureSource.showLayer(layer, state.locationControl.data)) { + if (!layer.isDisplayed) { // The layer itself is either disabled or hidden due to zoom constraints // We should return true, but it might still match some other layer return false; @@ -66,10 +71,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer { update(); }); - let isShown = state.locationControl.map((l) => FilteringFeatureSource.showLayer(layer, l), - [layer.isDisplayed]) - - isShown.addCallback(isShown => { + layer.isDisplayed.addCallback(isShown => { if (isShown) { update(); } else { @@ -78,7 +80,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer { }); layer.appliedFilters.addCallback(_ => { - if(!isShown.data){ + if(!layer.isDisplayed.data){ // Currently not shown. // Note that a change in 'isSHown' will trigger an update as well, so we don't have to watch it another time return; @@ -93,10 +95,8 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer { layer: { isDisplayed: UIEventSource; layerDef: LayerConfig; - }, - location: { zoom: number }) { - return layer.isDisplayed.data && - layer.layerDef.minzoomVisible <= location.zoom; + }) { + return layer.isDisplayed.data; } } diff --git a/Logic/FeatureSource/Sources/GeoJsonSource.ts b/Logic/FeatureSource/Sources/GeoJsonSource.ts index e6d24fbca..6222fc729 100644 --- a/Logic/FeatureSource/Sources/GeoJsonSource.ts +++ b/Logic/FeatureSource/Sources/GeoJsonSource.ts @@ -6,6 +6,7 @@ import FilteredLayer from "../../../Models/FilteredLayer"; import {Utils} from "../../../Utils"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import {BBox} from "../../GeoOperations"; +import {Tiles} from "../../../Models/TileRange"; export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { @@ -35,10 +36,10 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { .replace('{z}', "" + z) .replace('{x}', "" + x) .replace('{y}', "" + y) - this.tileIndex = Utils.tile_index(z, x, y) + this.tileIndex = Tiles.tile_index(z, x, y) this.bbox = BBox.fromTile(z, x, y) } else { - this.tileIndex = Utils.tile_index(0, 0, 0) + this.tileIndex = Tiles.tile_index(0, 0, 0) this.bbox = BBox.global; } @@ -89,7 +90,6 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { newFeatures.push({feature: feature, freshness: freshness}) } - console.debug("Downloaded " + newFeatures.length + " new features and " + skipped + " already seen features from " + url); if (newFeatures.length == 0) { return; diff --git a/Logic/FeatureSource/Sources/RememberingSource.ts b/Logic/FeatureSource/Sources/RememberingSource.ts index 99f422478..b31097061 100644 --- a/Logic/FeatureSource/Sources/RememberingSource.ts +++ b/Logic/FeatureSource/Sources/RememberingSource.ts @@ -2,17 +2,23 @@ * Every previously added point is remembered, but new points are added. * Data coming from upstream will always overwrite a previous value */ -import FeatureSource from "../FeatureSource"; +import FeatureSource, {Tiled} from "../FeatureSource"; import {UIEventSource} from "../../UIEventSource"; +import {BBox} from "../../GeoOperations"; -export default class RememberingSource implements FeatureSource { +export default class RememberingSource implements FeatureSource , Tiled{ public readonly features: UIEventSource<{ feature: any, freshness: Date }[]>; public readonly name; - - constructor(source: FeatureSource) { + public readonly tileIndex : number + public readonly bbox : BBox + + constructor(source: FeatureSource & Tiled) { const self = this; this.name = "RememberingSource of " + source.name; + this.tileIndex= source.tileIndex + this.bbox = source.bbox; + const empty = []; this.features = source.features.map(features => { const oldFeatures = self.features?.data ?? empty; diff --git a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts index ad8d7be5d..b3a476f5f 100644 --- a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts +++ b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts @@ -3,13 +3,14 @@ import FilteredLayer from "../../../Models/FilteredLayer"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import {BBox} from "../../GeoOperations"; import {Utils} from "../../../Utils"; +import {Tiles} from "../../../Models/TileRange"; export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name: string = "SimpleFeatureSource"; public readonly layer: FilteredLayer; public readonly bbox: BBox = BBox.global; - public readonly tileIndex: number = Utils.tile_index(0, 0, 0); + public readonly tileIndex: number = Tiles.tile_index(0, 0, 0); constructor(layer: FilteredLayer) { this.name = "SimpleFeatureSource(" + layer.layerDef.id + ")" diff --git a/Logic/FeatureSource/Sources/StaticFeatureSource.ts b/Logic/FeatureSource/Sources/StaticFeatureSource.ts index 0cc58d656..d5795a1d5 100644 --- a/Logic/FeatureSource/Sources/StaticFeatureSource.ts +++ b/Logic/FeatureSource/Sources/StaticFeatureSource.ts @@ -8,12 +8,13 @@ export default class StaticFeatureSource implements FeatureSource { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; public readonly name: string = "StaticFeatureSource" - constructor(features: any[] | UIEventSource, useFeaturesDirectly = false) { + constructor(features: any[] | UIEventSource>, useFeaturesDirectly) { const now = new Date(); - if(useFeaturesDirectly){ + if (useFeaturesDirectly) { // @ts-ignore this.features = features - }else if (features instanceof UIEventSource) { + } else if (features instanceof UIEventSource) { + // @ts-ignore this.features = features.map(features => features.map(f => ({feature: f, freshness: now}))) } else { this.features = new UIEventSource(features.map(f => ({ diff --git a/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts index 37ee94b8a..cb36c4b21 100644 --- a/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts +++ b/Logic/FeatureSource/Sources/WayHandlingApplyingFeatureSource.ts @@ -12,10 +12,11 @@ export default class WayHandlingApplyingFeatureSource implements FeatureSourceFo public readonly layer; constructor(upstream: FeatureSourceForLayer) { - this.name = "Wayhandling(" + upstream.name+")"; + + this.name = "Wayhandling(" + upstream.name + ")"; this.layer = upstream.layer const layer = upstream.layer.layerDef; - + if (layer.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) { // We don't have to do anything fancy // lets just wire up the upstream diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts index 357db85d4..8843b182a 100644 --- a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts @@ -1,5 +1,5 @@ import FilteredLayer from "../../../Models/FilteredLayer"; -import {FeatureSourceForLayer} from "../FeatureSource"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import {UIEventSource} from "../../UIEventSource"; import Loc from "../../../Models/Loc"; import DynamicTileSource from "./DynamicTileSource"; @@ -8,7 +8,7 @@ import GeoJsonSource from "../Sources/GeoJsonSource"; export default class DynamicGeoJsonTileSource extends DynamicTileSource { constructor(layer: FilteredLayer, - registerLayer: (layer: FeatureSourceForLayer) => void, + registerLayer: (layer: FeatureSourceForLayer & Tiled) => void, state: { locationControl: UIEventSource leafletMap: any diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts index 43021587c..a6ef04458 100644 --- a/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts @@ -6,6 +6,7 @@ import {Utils} from "../../../Utils"; import {UIEventSource} from "../../UIEventSource"; import Loc from "../../../Models/Loc"; import TileHierarchy from "./TileHierarchy"; +import {Tiles} from "../../../Models/TileRange"; /*** * A tiled source which dynamically loads the required tiles at a fixed zoom level @@ -46,9 +47,9 @@ export default class DynamicTileSource implements TileHierarchy Utils.tile_index(zoomlevel, x, y)).filter(i => !self._loadedTiles.has(i)) + const needed = Tiles.MapRange(tileRange, (x, y) => Tiles.tile_index(zoomlevel, x, y)).filter(i => !self._loadedTiles.has(i)) if (needed.length === 0) { return undefined } @@ -63,7 +64,7 @@ export default class DynamicTileSource implements TileHierarchy { public readonly loadedTiles: Map = new Map(); @@ -13,7 +14,7 @@ export class TileHierarchyMerger implements TileHierarchy void; - constructor(layer: FilteredLayer, handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource, index: number) => void) { + constructor(layer: FilteredLayer, handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource & Tiled, index: number) => void) { this.layer = layer; this._handleTile = handleTile; } @@ -37,7 +38,7 @@ export class TileHierarchyMerger implements TileHierarchy([src]) this.sources.set(index, sources) - const merger = new FeatureSourceMerger(this.layer, index, BBox.fromTile(...Utils.tile_from_index(index)), sources) + const merger = new FeatureSourceMerger(this.layer, index, BBox.fromTile(...Tiles.tile_from_index(index)), sources) this.loadedTiles.set(index, merger) this._handleTile(merger, index) } diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts index 62a7c9e5e..2dcf3e6d2 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts @@ -4,7 +4,7 @@ import {Utils} from "../../../Utils"; import {BBox} from "../../GeoOperations"; import FilteredLayer from "../../../Models/FilteredLayer"; import TileHierarchy from "./TileHierarchy"; -import {feature} from "@turf/turf"; +import {Tiles} from "../../../Models/TileRange"; /** * Contains all features in a tiled fashion. @@ -41,12 +41,12 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource, this.x = x; this.y = y; this.bbox = BBox.fromTile(z, x, y) - this.tileIndex = Utils.tile_index(z, x, y) + this.tileIndex = Tiles.tile_index(z, x, y) this.name = `TiledFeatureSource(${z},${x},${y})` this.parent = parent; this.layer = options.layer options = options ?? {} - this.maxFeatureCount = options?.maxFeatureCount ?? 500; + this.maxFeatureCount = options?.maxFeatureCount ?? 250; this.maxzoom = options.maxZoomLevel ?? 18 this.options = options; if (parent === undefined) { @@ -61,7 +61,7 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource, } else { this.root = this.parent.root; this.loadedTiles = this.root.loadedTiles; - const i = Utils.tile_index(z, x, y) + const i = Tiles.tile_index(z, x, y) this.root.loadedTiles.set(i, this) } this.features = new UIEventSource([]) @@ -143,9 +143,7 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource, for (const feature of features) { const bbox = BBox.get(feature.feature) - if (this.options.minZoomLevel === undefined) { - - + if (this.options.dontEnforceMinZoom || this.options.minZoomLevel === undefined) { if (bbox.isContainedIn(this.upper_left.bbox)) { ulf.push(feature) } else if (bbox.isContainedIn(this.upper_right.bbox)) { @@ -186,6 +184,11 @@ export interface TiledFeatureSourceOptions { readonly maxFeatureCount?: number, readonly maxZoomLevel?: number, readonly minZoomLevel?: number, + /** + * IF minZoomLevel is set, and if a feature runs through a tile boundary, it would normally be duplicated. + * Setting 'dontEnforceMinZoomLevel' will still allow bigger zoom levels for those features + */ + readonly dontEnforceMinZoom?: boolean, readonly registerTile?: (tile: TiledFeatureSource & Tiled) => void, readonly layer?: FilteredLayer } \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts index e88a1d82d..3e879bd4a 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -6,6 +6,7 @@ import TileHierarchy from "./TileHierarchy"; import {Utils} from "../../../Utils"; import SaveTileToLocalStorageActor from "../Actors/SaveTileToLocalStorageActor"; import {BBox} from "../../GeoOperations"; +import {Tiles} from "../../../Models/TileRange"; export default class TiledFromLocalStorageSource implements TileHierarchy { public loadedTiles: Map = new Map(); @@ -17,6 +18,7 @@ export default class TiledFromLocalStorageSource implements TileHierarchy() const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" // @ts-ignore const indexes: number[] = Object.keys(localStorage) @@ -27,7 +29,7 @@ export default class TiledFromLocalStorageSource implements TileHierarchy Utils.tile_from_index(i).join("/")).join(", ")) + console.log("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Tiles.tile_from_index(i).join("/")).join(", ")) const zLevels = indexes.map(i => i % 100) const indexesSet = new Set(indexes) @@ -57,9 +59,9 @@ export default class TiledFromLocalStorageSource implements TileHierarchy Utils.tile_index(z, x, y)) - .filter(i => !self.loadedTiles.has(i) && indexesSet.has(i)) + const tileRange = Tiles.TileRangeBetween(z, bounds.getNorth(), bounds.getEast(), bounds.getSouth(), bounds.getWest()) + const neededZ = Tiles.MapRange(tileRange, (x, y) => Tiles.tile_index(z, x, y)) + .filter(i => !self.loadedTiles.has(i) && !undefinedTiles.has(i) && indexesSet.has(i)) needed.push(...neededZ) } @@ -84,12 +86,13 @@ export default class TiledFromLocalStorageSource implements TileHierarchy(features), name: "FromLocalStorage(" + key + ")", tileIndex: neededIndex, - bbox: BBox.fromTile(...Utils.tile_from_index(neededIndex)) + bbox: BBox.fromTileIndex(neededIndex) } handleFeatureSource(src, neededIndex) self.loadedTiles.set(neededIndex, src) } catch (e) { console.error("Could not load data tile from local storage due to", e) + undefinedTiles.add(neededIndex) } } diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index 66fd90053..dd806ded4 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -1,5 +1,6 @@ import * as turf from '@turf/turf' import {Utils} from "../Utils"; +import {Tiles} from "../Models/TileRange"; export class GeoOperations { @@ -8,7 +9,7 @@ export class GeoOperations { } /** - * Converts a GeoJSon feature to a point feature + * Converts a GeoJson feature to a point GeoJson feature * @param feature */ static centerpoint(feature: any) { @@ -451,8 +452,12 @@ export class BBox { } } - static fromTile(z: number, x: number, y: number) { - return new BBox(Utils.tile_bounds_lon_lat(z, x, y)) + static fromTile(z: number, x: number, y: number): BBox { + return new BBox(Tiles.tile_bounds_lon_lat(z, x, y)) + } + + static fromTileIndex(i: number): BBox { + return BBox.fromTile(...Tiles.tile_from_index(i)) } getEast() { diff --git a/Logic/ImageProviders/ImageAttributionSource.ts b/Logic/ImageProviders/ImageAttributionSource.ts index 7841c899b..1f0f097b7 100644 --- a/Logic/ImageProviders/ImageAttributionSource.ts +++ b/Logic/ImageProviders/ImageAttributionSource.ts @@ -12,8 +12,11 @@ export default abstract class ImageAttributionSource { if (cached !== undefined) { return cached; } - const src = this.DownloadAttribution(url) + const src = new UIEventSource(undefined) this._cache.set(url, src) + this.DownloadAttribution(url).then(license => + src.setData(license)) + .catch(e => console.error("Could not download license information for ", url, " due to", e)) return src; } @@ -21,10 +24,10 @@ export default abstract class ImageAttributionSource { public abstract SourceIcon(backlinkSource?: string): BaseUIElement; /*Converts a value to a URL. Can return null if not applicable*/ - public PrepareUrl(value: string): string | UIEventSource{ + public PrepareUrl(value: string): string | UIEventSource { return value; } - protected abstract DownloadAttribution(url: string): UIEventSource; + protected abstract DownloadAttribution(url: string): Promise; } \ No newline at end of file diff --git a/Logic/ImageProviders/Imgur.ts b/Logic/ImageProviders/Imgur.ts index 4325d3e37..f85f3228a 100644 --- a/Logic/ImageProviders/Imgur.ts +++ b/Logic/ImageProviders/Imgur.ts @@ -2,8 +2,9 @@ import $ from "jquery" import {LicenseInfo} from "./Wikimedia"; import ImageAttributionSource from "./ImageAttributionSource"; -import {UIEventSource} from "../UIEventSource"; import BaseUIElement from "../../UI/BaseUIElement"; +import {Utils} from "../../Utils"; +import Constants from "../../Models/Constants"; export class Imgur extends ImageAttributionSource { @@ -86,50 +87,27 @@ export class Imgur extends ImageAttributionSource { return undefined; } - protected DownloadAttribution(url: string): UIEventSource { - const src = new UIEventSource(undefined) - - + protected async DownloadAttribution(url: string): Promise { const hash = url.substr("https://i.imgur.com/".length).split(".jpg")[0]; const apiUrl = 'https://api.imgur.com/3/image/' + hash; - const apiKey = '7070e7167f0a25a'; + const response = await Utils.downloadJson(apiUrl, {Authorization: 'Client-ID ' + Constants.ImgurApiKey}) - const settings = { - async: true, - crossDomain: true, - processData: false, - contentType: false, - type: 'GET', - url: apiUrl, - headers: { - Authorization: 'Client-ID ' + apiKey, - Accept: 'application/json', - }, - }; - // @ts-ignore - $.ajax(settings).done(function (response) { - const descr: string = response.data.description ?? ""; - const data: any = {}; - for (const tag of descr.split("\n")) { - const kv = tag.split(":"); - const k = kv[0]; - data[k] = kv[1].replace("\r", ""); - } + const descr: string = response.data.description ?? ""; + const data: any = {}; + for (const tag of descr.split("\n")) { + const kv = tag.split(":"); + const k = kv[0]; + data[k] = kv[1]?.replace("\r", ""); + } - const licenseInfo = new LicenseInfo(); + const licenseInfo = new LicenseInfo(); - licenseInfo.licenseShortName = data.license; - licenseInfo.artist = data.author; + licenseInfo.licenseShortName = data.license; + licenseInfo.artist = data.author; - src.setData(licenseInfo) - - }).fail((reason) => { - console.log("Getting metadata from to IMGUR failed", reason) - }); - - return src; + return licenseInfo } diff --git a/Logic/ImageProviders/Mapillary.ts b/Logic/ImageProviders/Mapillary.ts index 3f992dbce..ae5808a01 100644 --- a/Logic/ImageProviders/Mapillary.ts +++ b/Logic/ImageProviders/Mapillary.ts @@ -8,7 +8,7 @@ import {Utils} from "../../Utils"; export class Mapillary extends ImageAttributionSource { public static readonly singleton = new Mapillary(); - + private static readonly v4_cached_urls = new Map>(); private static readonly client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' @@ -23,8 +23,8 @@ export class Mapillary extends ImageAttributionSource { isApiv4?: boolean } { if (value.startsWith("https://a.mapillary.com")) { - const key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1) - return {key:key, isApiv4: !isNaN(Number(key))}; + const key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1) + return {key: key, isApiv4: !isNaN(Number(key))}; } const newApiFormat = value.match(/https?:\/\/www.mapillary.com\/app\/\?pKey=([0-9]*)/) if (newApiFormat !== null) { @@ -32,11 +32,11 @@ export class Mapillary extends ImageAttributionSource { } const mapview = value.match(/https?:\/\/www.mapillary.com\/map\/im\/(.*)/) - if(mapview !== null){ + if (mapview !== null) { const key = mapview[1] - return {key:key, isApiv4: !isNaN(Number(key))}; + return {key: key, isApiv4: !isNaN(Number(key))}; } - + if (value.toLowerCase().startsWith("https://www.mapillary.com/map/im/")) { // Extract the key of the image @@ -62,48 +62,45 @@ export class Mapillary extends ImageAttributionSource { return `https://images.mapillary.com/${keyV.key}/thumb-640.jpg?client_id=${Mapillary.client_token_v3}` } else { const key = keyV.key; - if(Mapillary.v4_cached_urls.has(key)){ + if (Mapillary.v4_cached_urls.has(key)) { return Mapillary.v4_cached_urls.get(key) } - const metadataUrl ='https://graph.mapillary.com/' + key + '?fields=thumb_1024_url&&access_token=' + Mapillary.client_token_v4; + const metadataUrl = 'https://graph.mapillary.com/' + key + '?fields=thumb_1024_url&&access_token=' + Mapillary.client_token_v4; const source = new UIEventSource(undefined) Mapillary.v4_cached_urls.set(key, source) Utils.downloadJson(metadataUrl).then( - json => { - console.warn("Got response on mapillary image", json, json["thumb_1024_url"]) - return source.setData(json["thumb_1024_url"]); - } + json => { + console.warn("Got response on mapillary image", json, json["thumb_1024_url"]) + return source.setData(json["thumb_1024_url"]); + } ) return source } } - protected DownloadAttribution(url: string): UIEventSource { + protected async DownloadAttribution(url: string): Promise { const keyV = Mapillary.ExtractKeyFromURL(url) - if(keyV.isApiv4){ + if (keyV.isApiv4) { const license = new LicenseInfo() license.artist = "Contributor name unavailable"; license.license = "CC BY-SA 4.0"; // license.license = "Creative Commons Attribution-ShareAlike 4.0 International License"; license.attributionRequired = true; - return new UIEventSource(license) - + return license + } const key = keyV.key - - const metadataURL = `https://a.mapillary.com/v3/images/${key}?client_id=TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2` - const source = new UIEventSource(undefined) - Utils.downloadJson(metadataURL).then(data => { - const license = new LicenseInfo(); - license.artist = data.properties?.username; - license.licenseShortName = "CC BY-SA 4.0"; - license.license = "Creative Commons Attribution-ShareAlike 4.0 International License"; - license.attributionRequired = true; - source.setData(license); - }) - return source + const metadataURL = `https://a.mapillary.com/v3/images/${key}?client_id=TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2` + const data = await Utils.downloadJson(metadataURL) + const license = new LicenseInfo(); + license.artist = data.properties?.username; + license.licenseShortName = "CC BY-SA 4.0"; + license.license = "Creative Commons Attribution-ShareAlike 4.0 International License"; + license.attributionRequired = true; + + return license } } \ No newline at end of file diff --git a/Logic/ImageProviders/Wikimedia.ts b/Logic/ImageProviders/Wikimedia.ts index 32c762523..cd5aea5d5 100644 --- a/Logic/ImageProviders/Wikimedia.ts +++ b/Logic/ImageProviders/Wikimedia.ts @@ -1,7 +1,6 @@ import ImageAttributionSource from "./ImageAttributionSource"; import BaseUIElement from "../../UI/BaseUIElement"; import Svg from "../../Svg"; -import {UIEventSource} from "../UIEventSource"; import Link from "../../UI/Base/Link"; import {Utils} from "../../Utils"; @@ -124,43 +123,34 @@ export class Wikimedia extends ImageAttributionSource { .replace(/'/g, '%27'); } - protected DownloadAttribution(filename: string): UIEventSource { - - const source = new UIEventSource(undefined); - + protected async DownloadAttribution(filename: string): Promise { filename = Wikimedia.ExtractFileName(filename) if (filename === "") { - return source; + return undefined; } const url = "https://en.wikipedia.org/w/" + "api.php?action=query&prop=imageinfo&iiprop=extmetadata&" + "titles=" + filename + "&format=json&origin=*"; - Utils.downloadJson(url).then( - data => { - const licenseInfo = new LicenseInfo(); - const license = (data.query.pages[-1].imageinfo ?? [])[0]?.extmetadata; - if (license === undefined) { - console.error("This file has no usable metedata or license attached... Please fix the license info file yourself!") - source.setData(null) - return; - } + const data = await Utils.downloadJson(url) + const licenseInfo = new LicenseInfo(); + const license = (data.query.pages[-1].imageinfo ?? [])[0]?.extmetadata; + if (license === undefined) { + console.error("This file has no usable metedata or license attached... Please fix the license info file yourself!") + return undefined; + } - licenseInfo.artist = license.Artist?.value; - licenseInfo.license = license.License?.value; - licenseInfo.copyrighted = license.Copyrighted?.value; - licenseInfo.attributionRequired = license.AttributionRequired?.value; - licenseInfo.usageTerms = license.UsageTerms?.value; - licenseInfo.licenseShortName = license.LicenseShortName?.value; - licenseInfo.credit = license.Credit?.value; - licenseInfo.description = license.ImageDescription?.value; - source.setData(licenseInfo); - } - ) - - return source; + licenseInfo.artist = license.Artist?.value; + licenseInfo.license = license.License?.value; + licenseInfo.copyrighted = license.Copyrighted?.value; + licenseInfo.attributionRequired = license.AttributionRequired?.value; + licenseInfo.usageTerms = license.UsageTerms?.value; + licenseInfo.licenseShortName = license.LicenseShortName?.value; + licenseInfo.credit = license.Credit?.value; + licenseInfo.description = license.ImageDescription?.value; + return licenseInfo; } diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index 5745fa4ad..c7eda184b 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -2,6 +2,7 @@ import SimpleMetaTagger from "./SimpleMetaTagger"; import {ExtraFuncParams, ExtraFunction} from "./ExtraFunction"; import {UIEventSource} from "./UIEventSource"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; +import State from "../State"; /** @@ -20,50 +21,68 @@ export default class MetaTagging { * The given features should be part of the given layer */ public static addMetatags(features: { feature: any; freshness: Date }[], - params: ExtraFuncParams, - layer: LayerConfig, - options?: { - includeDates?: true | boolean, - includeNonDates?: true | boolean - }) { + params: ExtraFuncParams, + layer: LayerConfig, + options?: { + includeDates?: true | boolean, + includeNonDates?: true | boolean + }) { if (features === undefined || features.length === 0) { return; } - for (const metatag of SimpleMetaTagger.metatags) { - try { + const metatagsToApply: SimpleMetaTagger [] = [] + for (const metatag of SimpleMetaTagger.metatags) { if (metatag.includesDates) { if (options.includeDates ?? true) { - metatag.addMetaTags(features); + metatagsToApply.push(metatag) } } else { if (options.includeNonDates ?? true) { - metatag.addMetaTags(features); + metatagsToApply.push(metatag) } } - - } catch (e) { - console.error("Could not calculate metatag for ", metatag.keys.join(","), ":", e) } - } - // The functions - per layer - which add the new keys + // The calculated functions - per layer - which add the new keys const layerFuncs = this.createRetaggingFunc(layer) - if (layerFuncs !== undefined) { - for (const feature of features) { - try { - layerFuncs(params, feature.feature) - } catch (e) { - console.error(e) + for (let i = 0; i < features.length; i++) { + const ff = features[i]; + const feature = ff.feature + const freshness = ff.freshness + let somethingChanged = false + for (const metatag of metatagsToApply) { + try { + if(!metatag.keys.some(key => feature.properties[key] === undefined)){ + // All keys are already defined, we probably already ran this one + continue + } + somethingChanged = somethingChanged || metatag.applyMetaTagsOnFeature(feature, freshness) + } catch (e) { + console.error("Could not calculate metatag for ", metatag.keys.join(","), ":", e) + } + } + + if(layerFuncs !== undefined){ + try { + layerFuncs(params, feature) + } catch (e) { + console.error(e) + } + somethingChanged = true + } + + if(somethingChanged){ + State.state.allElements.getEventSourceById(feature.properties.id).ping() } } - } } + private static createRetaggingFunc(layer: LayerConfig): ((params: ExtraFuncParams, feature: any) => void) { const calculatedTags: [string, string][] = layer.calculatedTags; @@ -92,11 +111,13 @@ export default class MetaTagging { d = JSON.stringify(d); } feature.properties[key] = d; + console.log("Written a delayed calculated tag onto ", feature.properties.id, ": ", key, ":==", d) }) result = result.data } if (result === undefined || result === "") { + console.log("Calculated tag for", key, "gave undefined", feature.properties.id) return; } if (typeof result !== "string") { @@ -104,6 +125,7 @@ export default class MetaTagging { result = JSON.stringify(result); } feature.properties[key] = result; + console.log("Written a calculated tag onto ", feature.properties.id, ": ", key, ":==", result) } catch (e) { if (MetaTagging.errorPrintCount < MetaTagging.stopErrorOutputAt) { console.warn("Could not calculate a calculated tag defined by " + code + " due to " + e + ". This is code defined in the theme. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", e) diff --git a/Logic/Osm/OsmConnection.ts b/Logic/Osm/OsmConnection.ts index 01d18f8cd..5a5e82612 100644 --- a/Logic/Osm/OsmConnection.ts +++ b/Logic/Osm/OsmConnection.ts @@ -94,6 +94,7 @@ export class OsmConnection { self.AttemptLogin() } }); + this.isLoggedIn.addCallbackAndRunD(li => console.log("User is logged in!", li)) this._dryRun = dryRun; this.updateAuthObject(); diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 6e8f3e0fc..4a2dac23b 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -31,7 +31,7 @@ export default class SimpleMetaTagger { "_version_number"], doc: "Information about the last edit of this object." }, - (feature) => {/*Note: also handled by 'UpdateTagsFromOsmAPI'*/ + (feature) => {/*Note: also called by 'UpdateTagsFromOsmAPI'*/ const tgs = feature.properties; @@ -48,6 +48,7 @@ export default class SimpleMetaTagger { move("changeset", "_last_edit:changeset") move("timestamp", "_last_edit:timestamp") move("version", "_version_number") + return true; } ) private static latlon = new SimpleMetaTagger({ @@ -62,6 +63,7 @@ export default class SimpleMetaTagger { feature.properties["_lon"] = "" + lon; feature._lon = lon; // This is dirty, I know feature._lat = lat; + return true; }) ); private static surfaceArea = new SimpleMetaTagger( @@ -74,6 +76,7 @@ export default class SimpleMetaTagger { feature.properties["_surface"] = "" + sqMeters; feature.properties["_surface:ha"] = "" + Math.floor(sqMeters / 1000) / 10; feature.area = sqMeters; + return true; }) ); @@ -118,9 +121,7 @@ export default class SimpleMetaTagger { } } - if (rewritten) { - State.state.allElements.getEventSourceById(feature.id).ping(); - } + return rewritten }) ) @@ -135,6 +136,7 @@ export default class SimpleMetaTagger { const km = Math.floor(l / 1000) const kmRest = Math.round((l - km * 1000) / 100) feature.properties["_length:km"] = "" + km + "." + kmRest + return true; }) ) private static country = new SimpleMetaTagger( @@ -144,7 +146,6 @@ export default class SimpleMetaTagger { }, feature => { - let centerPoint: any = GeoOperations.centerpoint(feature); const lat = centerPoint.geometry.coordinates[1]; const lon = centerPoint.geometry.coordinates[0]; @@ -157,11 +158,11 @@ export default class SimpleMetaTagger { const tagsSource = State.state.allElements.getEventSourceById(feature.properties.id); tagsSource.ping(); } - } catch (e) { console.warn(e) } }) + return false; } ) private static isOpen = new SimpleMetaTagger( @@ -174,7 +175,7 @@ export default class SimpleMetaTagger { if (Utils.runningFromConsole) { // We are running from console, thus probably creating a cache // isOpen is irrelevant - return + return false } const tagsSource = State.state.allElements.getEventSourceById(feature.properties.id); @@ -199,7 +200,7 @@ export default class SimpleMetaTagger { if (oldNextChange > (new Date()).getTime() && tags["_isOpen:oldvalue"] === tags["opening_hours"]) { // Already calculated and should not yet be triggered - return; + return false; } tags["_isOpen"] = oh.getState() ? "yes" : "no"; @@ -227,6 +228,7 @@ export default class SimpleMetaTagger { } } updateTags(); + return true; } catch (e) { console.warn("Error while parsing opening hours of ", tags.id, e); tags["_isOpen"] = "parse_error"; @@ -244,11 +246,11 @@ export default class SimpleMetaTagger { const tags = feature.properties; const direction = tags["camera:direction"] ?? tags["direction"]; if (direction === undefined) { - return; + return false; } const n = cardinalDirections[direction] ?? Number(direction); if (isNaN(n)) { - return; + return false; } // The % operator has range (-360, 360). We apply a trick to get [0, 360). @@ -256,7 +258,7 @@ export default class SimpleMetaTagger { tags["_direction:numerical"] = normalized; tags["_direction:leftright"] = normalized <= 180 ? "right" : "left"; - + return true; }) ) private static carriageWayWidth = new SimpleMetaTagger( @@ -268,7 +270,7 @@ export default class SimpleMetaTagger { const properties = feature.properties; if (properties["width:carriageway"] === undefined) { - return; + return false; } const carWidth = 2; @@ -366,7 +368,7 @@ export default class SimpleMetaTagger { properties["_width:difference"] = Utils.Round(targetWidth - width); properties["_width:difference:no_pedestrians"] = Utils.Round(targetWidthIgnoringPedestrians - width); - + return true; } ); private static currentTime = new SimpleMetaTagger( @@ -375,7 +377,7 @@ export default class SimpleMetaTagger { doc: "Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely", includesDates: true }, - (feature, _, freshness) => { + (feature, freshness) => { const now = new Date(); if (typeof freshness === "string") { @@ -394,7 +396,7 @@ export default class SimpleMetaTagger { feature.properties["_now:datetime"] = datetime(now); feature.properties["_loaded:date"] = date(freshness); feature.properties["_loaded:datetime"] = datetime(freshness); - + return true; } ) public static metatags = [ @@ -413,12 +415,18 @@ export default class SimpleMetaTagger { public readonly keys: string[]; public readonly doc: string; public readonly includesDates: boolean - private readonly _f: (feature: any, index: number, freshness: Date) => void; + public readonly applyMetaTagsOnFeature: (feature: any, freshness: Date) => boolean; - constructor(docs: { keys: string[], doc: string, includesDates?: boolean }, f: ((feature: any, index: number, freshness: Date) => void)) { + /*** + * A function that adds some extra data to a feature + * @param docs: what does this extra data do? + * @param f: apply the changes. Returns true if something changed + */ + constructor(docs: { keys: string[], doc: string, includesDates?: boolean }, + f: ((feature: any, freshness: Date) => boolean)) { this.keys = docs.keys; this.doc = docs.doc; - this._f = f; + this.applyMetaTagsOnFeature = f; this.includesDates = docs.includesDates ?? false; for (const key of docs.keys) { if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) { @@ -450,12 +458,4 @@ export default class SimpleMetaTagger { return new Combine(subElements).SetClass("flex-col") } - public addMetaTags(features: { feature: any, freshness: Date }[]) { - for (let i = 0; i < features.length; i++) { - let feature = features[i]; - this._f(feature.feature, i, feature.freshness); - } - } - - } diff --git a/Models/Constants.ts b/Models/Constants.ts index d6f212ac6..66ca7227b 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -3,6 +3,7 @@ import {Utils} from "../Utils"; export default class Constants { public static vNumber = "0.10.0-alpha-1"; + public static ImgurApiKey = '7070e7167f0a25a' // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 18c9fe3b4..2ed02ae6d 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -18,6 +18,7 @@ import FilterConfig from "./FilterConfig"; import {Unit} from "../Unit"; import DeleteConfig from "./DeleteConfig"; import Svg from "../../Svg"; +import Img from "../../UI/Base/Img"; export default class LayerConfig { static WAYHANDLING_DEFAULT = 0; @@ -495,19 +496,20 @@ export default class LayerConfig { const iconUrlStatic = render(this.icon); const self = this; - function genHtmlFromString(sourcePart: string, rotation: string, style?: string): BaseUIElement { - style = style ?? `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`; + function genHtmlFromString(sourcePart: string, rotation: string): BaseUIElement { + const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`; let html: BaseUIElement = new FixedUiElement( `` ); const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/); if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) { - html = new Combine([ + html = new Img( (Svg.All[match[1] + ".svg"] as string).replace( /#000000/g, match[2] ), - ]).SetStyle(style); + true + ).SetStyle(style); } return html; } @@ -540,7 +542,7 @@ export default class LayerConfig { .filter((prt) => prt != ""); for (const badgePartStr of partDefs) { - badgeParts.push(genHtmlFromString(badgePartStr, "0", `width:unset;height:100%;display:block;`)); + badgeParts.push(genHtmlFromString(badgePartStr, "0")); } const badgeCompound = new Combine(badgeParts).SetStyle( diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index dd0d472ed..b632d8a29 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -5,7 +5,6 @@ import SharedTagRenderings from "../../Customizations/SharedTagRenderings"; import AllKnownLayers from "../../Customizations/AllKnownLayers"; import {Utils} from "../../Utils"; import LayerConfig from "./LayerConfig"; -import {Unit} from "../Unit"; import {LayerConfigJson} from "./Json/LayerConfigJson"; export default class LayoutConfig { @@ -87,6 +86,9 @@ export default class LayoutConfig { this.startZoom = json.startZoom; this.startLat = json.startLat; this.startLon = json.startLon; + if(json.widenFactor < 1){ + throw "Widenfactor too small" + } this.widenFactor = json.widenFactor ?? 1.5; this.roamingRenderings = (json.roamingRenderings ?? []).map((tr, i) => { if (typeof tr === "string") { diff --git a/Models/TileRange.ts b/Models/TileRange.ts index e1dba5532..215d5a76f 100644 --- a/Models/TileRange.ts +++ b/Models/TileRange.ts @@ -5,4 +5,106 @@ export interface TileRange { yend: number, total: number, zoomlevel: number +} + +export class Tiles { + + public static MapRange(tileRange: TileRange, f: (x: number, y: number) => T): T[] { + const result: T[] = [] + for (let x = tileRange.xstart; x <= tileRange.xend; x++) { + for (let y = tileRange.ystart; y <= tileRange.yend; y++) { + const t = f(x, y); + result.push(t) + } + } + return result; + } + + + private static tile2long(x, z) { + return (x / Math.pow(2, z) * 360 - 180); + } + + private static tile2lat(y, z) { + const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); + return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); + } + + private static lon2tile(lon, zoom) { + return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom))); + } + + private static lat2tile(lat, zoom) { + return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom))); + } + + /** + * Calculates the tile bounds of the + * @param z + * @param x + * @param y + * @returns [[maxlat, minlon], [minlat, maxlon]] + */ + static tile_bounds(z: number, x: number, y: number): [[number, number], [number, number]] { + return [[Tiles.tile2lat(y, z), Tiles.tile2long(x, z)], [Tiles.tile2lat(y + 1, z), Tiles.tile2long(x + 1, z)]] + } + + + static tile_bounds_lon_lat(z: number, x: number, y: number): [[number, number], [number, number]] { + return [[Tiles.tile2long(x, z), Tiles.tile2lat(y, z)], [Tiles.tile2long(x + 1, z), Tiles.tile2lat(y + 1, z)]] + } + + /** + * Returns the centerpoint [lon, lat] of the specified tile + * @param z + * @param x + * @param y + */ + static centerPointOf(z: number, x: number, y: number): [number, number]{ + return [(Tiles.tile2long(x, z) + Tiles.tile2long(x+1, z)) / 2, (Tiles.tile2lat(y, z) + Tiles.tile2lat(y+1, z)) / 2] + } + + static tile_index(z: number, x: number, y: number): number { + return ((x * (2 << z)) + y) * 100 + z + } + /** + * Given a tile index number, returns [z, x, y] + * @param index + * @returns 'zxy' + */ + static tile_from_index(index: number): [number, number, number] { + const z = index % 100; + const factor = 2 << z + index = Math.floor(index / 100) + const x = Math.floor(index / factor) + return [z, x, index % factor] + } + + /** + * Return x, y of the tile containing (lat, lon) on the given zoom level + */ + static embedded_tile(lat: number, lon: number, z: number): { x: number, y: number, z: number } { + return {x: Tiles.lon2tile(lon, z), y: Tiles.lat2tile(lat, z), z: z} + } + + static TileRangeBetween(zoomlevel: number, lat0: number, lon0: number, lat1: number, lon1: number): TileRange { + const t0 = Tiles.embedded_tile(lat0, lon0, zoomlevel) + const t1 = Tiles.embedded_tile(lat1, lon1, zoomlevel) + + const xstart = Math.min(t0.x, t1.x) + const xend = Math.max(t0.x, t1.x) + const ystart = Math.min(t0.y, t1.y) + const yend = Math.max(t0.y, t1.y) + const total = (1 + xend - xstart) * (1 + yend - ystart) + + return { + xstart: xstart, + xend: xend, + ystart: ystart, + yend: yend, + total: total, + zoomlevel: zoomlevel + } + } + } \ No newline at end of file diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts index 4a158ec0a..da8114f8c 100644 --- a/UI/Base/ScrollableFullScreen.ts +++ b/UI/Base/ScrollableFullScreen.ts @@ -36,6 +36,8 @@ export default class ScrollableFullScreen extends UIElement { this._component = this.BuildComponent(title("desktop"), content("desktop"), isShown) .SetClass("hidden md:block"); this._fullscreencomponent = this.BuildComponent(title("mobile"), content("mobile"), isShown); + + const self = this; isShown.addCallback(isShown => { if (isShown) { diff --git a/UI/Base/VariableUIElement.ts b/UI/Base/VariableUIElement.ts index fbc3bb564..cf53daf10 100644 --- a/UI/Base/VariableUIElement.ts +++ b/UI/Base/VariableUIElement.ts @@ -2,22 +2,23 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import BaseUIElement from "../BaseUIElement"; export class VariableUiElement extends BaseUIElement { - private _element: HTMLElement; + private readonly _contents: UIEventSource; - constructor( - contents: UIEventSource - ) { + constructor(contents: UIEventSource) { super(); + this._contents = contents; - this._element = document.createElement("span"); - const el = this._element; - contents.addCallbackAndRun((contents) => { + } + + protected InnerConstructElement(): HTMLElement { + const el = document.createElement("span"); + this._contents.addCallbackAndRun((contents) => { while (el.firstChild) { el.removeChild(el.lastChild); } if (contents === undefined) { - return el; + return; } if (typeof contents === "string") { el.innerHTML = contents; @@ -35,9 +36,6 @@ export class VariableUiElement extends BaseUIElement { } } }); - } - - protected InnerConstructElement(): HTMLElement { - return this._element; + return el; } } diff --git a/UI/BaseUIElement.ts b/UI/BaseUIElement.ts index 99462acbd..a11db9319 100644 --- a/UI/BaseUIElement.ts +++ b/UI/BaseUIElement.ts @@ -100,8 +100,6 @@ export default abstract class BaseUIElement { throw "ERROR! This is not a correct baseUIElement: " + this.constructor.name } try { - - const el = this.InnerConstructElement(); if (el === undefined) { diff --git a/UI/Image/Attribution.ts b/UI/Image/Attribution.ts index 30040482f..9afd9b815 100644 --- a/UI/Image/Attribution.ts +++ b/UI/Image/Attribution.ts @@ -13,17 +13,16 @@ export default class Attribution extends VariableUiElement { } super( license.map((license: LicenseInfo) => { - - if (license?.artist === undefined) { - return undefined; + if(license === undefined){ + return undefined } - + return new Combine([ icon?.SetClass("block left").SetStyle("height: 2em; width: 2em; padding-right: 0.5em;"), new Combine([ - Translations.W(license.artist).SetClass("block font-bold"), - Translations.W((license.license ?? "") === "" ? "CC0" : (license.license ?? "")) + Translations.W(license?.artist ?? ".").SetClass("block font-bold"), + Translations.W((license?.license ?? "") === "" ? "CC0" : (license?.license ?? "")) ]).SetClass("flex flex-col") ]).SetClass("flex flex-row bg-black text-white text-sm absolute bottom-0 left-0 p-0.5 pl-5 pr-3 rounded-lg") diff --git a/UI/Image/DeleteImage.ts b/UI/Image/DeleteImage.ts index a5ee19266..fe3f2cf09 100644 --- a/UI/Image/DeleteImage.ts +++ b/UI/Image/DeleteImage.ts @@ -48,7 +48,7 @@ export default class DeleteImage extends Toggle { tags.map(tags => (tags[key] ?? "") !== "") ), undefined /*Login (and thus editing) is disabled*/, - State.state?.featureSwitchUserbadge ?? new UIEventSource(true) + State.state.osmConnection.isLoggedIn ) this.SetClass("cursor-pointer") } diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index a350e38d3..0621eccc8 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -9,7 +9,6 @@ import State from "../../State"; import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; import {BBox, GeoOperations} from "../../Logic/GeoOperations"; import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; -import * as L from "leaflet"; import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 4e5c313f5..4c5a13ea5 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -31,8 +31,10 @@ export default class EditableTagRendering extends Toggle { const answerWithEditButton = new Combine([answer, - new Toggle(editButton, undefined, State.state.osmConnection.isLoggedIn)]) - .SetClass("flex justify-between w-full") + new Toggle(editButton, + undefined, + State.state.osmConnection.isLoggedIn) + ]).SetClass("flex justify-between w-full") const cancelbutton = diff --git a/UI/Popup/SplitRoadWizard.ts b/UI/Popup/SplitRoadWizard.ts index 01fb00d05..b1df0a5fa 100644 --- a/UI/Popup/SplitRoadWizard.ts +++ b/UI/Popup/SplitRoadWizard.ts @@ -71,7 +71,7 @@ export default class SplitRoadWizard extends Toggle { }) new ShowDataMultiLayer({ - features: new StaticFeatureSource([roadElement]), + features: new StaticFeatureSource([roadElement], false), layers: State.state.filteredLayers, leafletMap: miniMap.leafletMap, enablePopups: false, diff --git a/UI/ShowDataLayer/PerTileCountAggregator.ts b/UI/ShowDataLayer/PerTileCountAggregator.ts new file mode 100644 index 000000000..82e247f74 --- /dev/null +++ b/UI/ShowDataLayer/PerTileCountAggregator.ts @@ -0,0 +1,156 @@ +import FeatureSource, {FeatureSourceForLayer, Tiled} from "../../Logic/FeatureSource/FeatureSource"; +import {BBox} from "../../Logic/GeoOperations"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import {Tiles} from "../../Models/TileRange"; + + +/** + * A feature source containing meta features. + * It will contain exactly one point for every tile of the specified (dynamic) zoom level + */ +export default class PerTileCountAggregator implements FeatureSource { + public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name: string = "PerTileCountAggregator" + + private readonly perTile: Map = new Map() + private readonly _requestedZoomLevel: UIEventSource; + + constructor(requestedZoomLevel: UIEventSource) { + this._requestedZoomLevel = requestedZoomLevel; + const self = this; + this._requestedZoomLevel.addCallbackAndRun(_ => self.update()) + } + + private update() { + const now = new Date() + const allCountsAsFeatures : {feature: any, freshness: Date}[] = [] + const aggregate = this.calculatePerTileCount() + aggregate.forEach((totalsPerLayer, tileIndex) => { + const totals = {} + let totalCount = 0 + totalsPerLayer.forEach((total, layerId) => { + totals[layerId] = total + totalCount += total + }) + totals["tileId"] = tileIndex + totals["count"] = totalCount + const feature = { + "type": "Feature", + "properties": totals, + "geometry": { + "type": "Point", + "coordinates": Tiles.centerPointOf(...Tiles.tile_from_index(tileIndex)) + } + } + allCountsAsFeatures.push({feature: feature, freshness: now}) + + const bbox= BBox.fromTileIndex(tileIndex) + const box = { + "type": "Feature", + "properties":totals, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [bbox.minLon, bbox.minLat], + [bbox.minLon, bbox.maxLat], + [bbox.maxLon, bbox.maxLat], + [bbox.maxLon, bbox.minLat], + [bbox.minLon, bbox.minLat] + ] + ] + } + } + allCountsAsFeatures.push({feature:box, freshness: now}) + }) + this.features.setData(allCountsAsFeatures) + } + + /** + * Calculates an aggregate count per tile and per subtile + * @private + */ + private calculatePerTileCount() { + const perTileCount = new Map>() + const targetZoom = this._requestedZoomLevel.data; + // We only search for tiles of the same zoomlevel or a higher zoomlevel, which is embedded + for (const singleTileCounter of Array.from(this.perTile.values())) { + + let tileZ = singleTileCounter.z + let tileX = singleTileCounter.x + let tileY = singleTileCounter.y + if (tileZ < targetZoom) { + continue; + } + + while (tileZ > targetZoom) { + tileX = Math.floor(tileX / 2) + tileY = Math.floor(tileY / 2) + tileZ-- + } + const tileI = Tiles.tile_index(tileZ, tileX, tileY) + let counts = perTileCount.get(tileI) + if (counts === undefined) { + counts = new Map() + perTileCount.set(tileI, counts) + } + singleTileCounter.countsPerLayer.data.forEach((count, layerId) => { + if (counts.has(layerId)) { + counts.set(layerId, count + counts.get(layerId)) + } else { + counts.set(layerId, count) + } + }) + } + return perTileCount; + } + + public addTile(tile: FeatureSourceForLayer & Tiled, shouldBeCounted: UIEventSource) { + let counter = this.perTile.get(tile.tileIndex) + if (counter === undefined) { + counter = new SingleTileCounter(tile.tileIndex) + this.perTile.set(tile.tileIndex, counter) + // We do **NOT** add a callback on the perTile index, even though we could! It'll update just fine without it + } + counter.addTileCount(tile, shouldBeCounted) + } + + +} + +/** + * Keeps track of a single tile + */ +class SingleTileCounter implements Tiled { + public readonly bbox: BBox; + public readonly tileIndex: number; + public readonly countsPerLayer: UIEventSource> = new UIEventSource>(new Map()) + private readonly registeredLayers: Map = new Map(); + public readonly z: number + public readonly x: number + public readonly y: number + + constructor(tileIndex: number) { + this.tileIndex = tileIndex + this.bbox = BBox.fromTileIndex(tileIndex) + const [z, x, y] = Tiles.tile_from_index(tileIndex) + this.z = z; + this.x = x; + this.y = y + } + + public addTileCount(source: FeatureSourceForLayer, shouldBeCounted: UIEventSource) { + const layer = source.layer.layerDef + this.registeredLayers.set(layer.id, layer) + const self = this + source.features.map(f => { + /*if (!shouldBeCounted.data) { + return; + }*/ + self.countsPerLayer.data.set(layer.id, f.length) + self.countsPerLayer.ping() + }, [shouldBeCounted]) + } + +} \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index cdda7889b..278ea3597 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -41,13 +41,14 @@ export default class ShowDataLayer { options.leafletMap.addCallback(_ => self.update(options)); this.update(options); - State.state.selectedElement.addCallbackAndRunD(selected => { if (self._leafletMap.data === undefined) { return; } const v = self.leafletLayersPerId.get(selected.properties.id) - if(v === undefined){return;} + if (v === undefined) { + return; + } const leafletLayer = v.leafletlayer const feature = v.feature if (leafletLayer.getPopup().isOpen()) { @@ -66,6 +67,21 @@ export default class ShowDataLayer { } }) + + options.doShowLayer?.addCallbackAndRun(doShow => { + const mp = options.leafletMap.data; + if (this.geoLayer == undefined || mp == undefined) { + return; + } + if (doShow) { + mp.addLayer(this.geoLayer) + } else { + mp.removeLayer(this.geoLayer) + } + + + }) + } private update(options) { @@ -83,21 +99,19 @@ export default class ShowDataLayer { mp.removeLayer(this.geoLayer); } - this.geoLayer= this.CreateGeojsonLayer() + this.geoLayer = this.CreateGeojsonLayer() const allFeats = this._features.data; for (const feat of allFeats) { if (feat === undefined) { continue } - try{ + try { this.geoLayer.addData(feat); - }catch(e){ + } catch (e) { console.error("Could not add ", feat, "to the geojson layer in leaflet") } } - mp.addLayer(this.geoLayer) - if (options.zoomToFeatures ?? false) { try { mp.fitBounds(this.geoLayer.getBounds(), {animate: false}) @@ -105,6 +119,10 @@ export default class ShowDataLayer { console.error(e) } } + + if (options.doShowLayer?.data ?? true) { + mp.addLayer(this.geoLayer) + } } @@ -125,7 +143,8 @@ export default class ShowDataLayer { return; } - const tagSource = feature.properties.id === undefined ? new UIEventSource(feature.properties) : State.state.allElements.getEventSourceById(feature.properties.id) + const tagSource = feature.properties.id === undefined ? new UIEventSource(feature.properties) : + State.state.allElements.getEventSourceById(feature.properties.id) const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) const style = layer.GenerateLeafletStyle(tagSource, clickable); const baseElement = style.icon.html; @@ -193,8 +212,10 @@ export default class ShowDataLayer { infobox.Activate(); }); + // Add the feature to the index to open the popup when needed this.leafletLayersPerId.set(feature.properties.id, {feature: feature, leafletlayer: leafletLayer}) + } private CreateGeojsonLayer(): L.Layer { diff --git a/UI/ShowDataLayer/ShowDataLayerOptions.ts b/UI/ShowDataLayer/ShowDataLayerOptions.ts index 349472351..2323ce067 100644 --- a/UI/ShowDataLayer/ShowDataLayerOptions.ts +++ b/UI/ShowDataLayer/ShowDataLayerOptions.ts @@ -6,4 +6,5 @@ export interface ShowDataLayerOptions { leafletMap: UIEventSource, enablePopups?: true | boolean, zoomToFeatures?: false | boolean, + doShowLayer?: UIEventSource } \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowTileInfo.ts b/UI/ShowDataLayer/ShowTileInfo.ts new file mode 100644 index 000000000..a2fa322b6 --- /dev/null +++ b/UI/ShowDataLayer/ShowTileInfo.ts @@ -0,0 +1,79 @@ +import FeatureSource, {Tiled} from "../../Logic/FeatureSource/FeatureSource"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import {Utils} from "../../Utils"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import ShowDataLayer from "./ShowDataLayer"; +import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +import {GeoOperations} from "../../Logic/GeoOperations"; +import {Tiles} from "../../Models/TileRange"; + +export default class ShowTileInfo { + public static readonly styling = new LayerConfig({ + id: "tileinfo_styling", + title: { + render: "Tile {z}/{x}/{y}" + }, + tagRenderings: [ + "all_tags" + ], + source: { + osmTags: "tileId~*" + }, + color: {"render": "#3c3"}, + width: { + "render": "1" + }, + label: { + render: "
{count}
" + } + }, "tileinfo", true) + + constructor(options: { + source: FeatureSource & Tiled, leafletMap: UIEventSource, layer?: LayerConfig, + doShowLayer?: UIEventSource + }) { + + + const source = options.source + const metaFeature: UIEventSource = + source.features.map(features => { + const bbox = source.bbox + const [z, x, y] = Tiles.tile_from_index(source.tileIndex) + const box = { + "type": "Feature", + "properties": { + "z": z, + "x": x, + "y": y, + "tileIndex": source.tileIndex, + "source": source.name, + "count": features.length, + tileId: source.name + "/" + source.tileIndex + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [bbox.minLon, bbox.minLat], + [bbox.minLon, bbox.maxLat], + [bbox.maxLon, bbox.maxLat], + [bbox.maxLon, bbox.minLat], + [bbox.minLon, bbox.minLat] + ] + ] + } + } + const center = GeoOperations.centerpoint(box) + return [box, center] + }) + + new ShowDataLayer({ + layerToShow: ShowTileInfo.styling, + features: new StaticFeatureSource(metaFeature, false), + leafletMap: options.leafletMap, + doShowLayer: options.doShowLayer + }) + + } + +} \ No newline at end of file diff --git a/Utils.ts b/Utils.ts index 9f1373498..477598f79 100644 --- a/Utils.ts +++ b/Utils.ts @@ -10,7 +10,7 @@ export class Utils { */ public static runningFromConsole = typeof window === "undefined"; public static readonly assets_path = "./assets/svg/"; - public static externalDownloadFunction: (url: string) => Promise; + public static externalDownloadFunction: (url: string, headers?: any) => Promise; private static knownKeys = ["addExtraTags", "and", "calculatedTags", "changesetmessage", "clustering", "color", "condition", "customCss", "dashArray", "defaultBackgroundId", "description", "descriptionTail", "doNotDownload", "enableAddNewPoints", "enableBackgroundLayerSelection", "enableGeolocation", "enableLayers", "enableMoreQuests", "enableSearch", "enableShareScreen", "enableUserBadge", "freeform", "hideFromOverview", "hideInAnswer", "icon", "iconOverlays", "iconSize", "id", "if", "ifnot", "isShown", "key", "language", "layers", "lockLocation", "maintainer", "mappings", "maxzoom", "maxZoom", "minNeededElements", "minzoom", "multiAnswer", "name", "or", "osmTags", "passAllFeatures", "presets", "question", "render", "roaming", "roamingRenderings", "rotation", "shortDescription", "socialImage", "source", "startLat", "startLon", "startZoom", "tagRenderings", "tags", "then", "title", "titleIcons", "type", "version", "wayHandling", "widenFactor", "width"] private static extraKeys = ["nl", "en", "fr", "de", "pt", "es", "name", "phone", "email", "amenity", "leisure", "highway", "building", "yes", "no", "true", "false"] @@ -247,64 +247,6 @@ export class Utils { return dict.get(k); } - /** - * Calculates the tile bounds of the - * @param z - * @param x - * @param y - * @returns [[maxlat, minlon], [minlat, maxlon]] - */ - static tile_bounds(z: number, x: number, y: number): [[number, number], [number, number]] { - return [[Utils.tile2lat(y, z), Utils.tile2long(x, z)], [Utils.tile2lat(y + 1, z), Utils.tile2long(x + 1, z)]] - } - - static tile_bounds_lon_lat(z: number, x: number, y: number): [[number, number], [number, number]] { - return [[Utils.tile2long(x, z), Utils.tile2lat(y, z)], [Utils.tile2long(x + 1, z), Utils.tile2lat(y + 1, z)]] - } - - static tile_index(z: number, x: number, y: number): number { - return ((x * (2 << z)) + y) * 100 + z - } - - /** - * Given a tile index number, returns [z, x, y] - * @param index - * @returns 'zxy' - */ - static tile_from_index(index: number): [number, number, number] { - const z = index % 100; - const factor = 2 << z - index = Math.floor(index / 100) - return [z, Math.floor(index / factor), index % factor] - } - - /** - * Return x, y of the tile containing (lat, lon) on the given zoom level - */ - static embedded_tile(lat: number, lon: number, z: number): { x: number, y: number, z: number } { - return {x: Utils.lon2tile(lon, z), y: Utils.lat2tile(lat, z), z: z} - } - - static TileRangeBetween(zoomlevel: number, lat0: number, lon0: number, lat1: number, lon1: number): TileRange { - const t0 = Utils.embedded_tile(lat0, lon0, zoomlevel) - const t1 = Utils.embedded_tile(lat1, lon1, zoomlevel) - - const xstart = Math.min(t0.x, t1.x) - const xend = Math.max(t0.x, t1.x) - const ystart = Math.min(t0.y, t1.y) - const yend = Math.max(t0.y, t1.y) - const total = (1 + xend - xstart) * (1 + yend - ystart) - - return { - xstart: xstart, - xend: xend, - ystart: ystart, - yend: yend, - total: total, - zoomlevel: zoomlevel - } - } - public static MinifyJSON(stringified: string): string { stringified = stringified.replace(/\|/g, "||"); @@ -345,16 +287,7 @@ export class Utils { return result; } - public static MapRange(tileRange: TileRange, f: (x: number, y: number) => T): T[] { - const result: T[] = [] - for (let x = tileRange.xstart; x <= tileRange.xend; x++) { - for (let y = tileRange.ystart; y <= tileRange.yend; y++) { - const t = f(x, y); - result.push(t) - } - } - return result; - } + private static injectedDownloads = {} @@ -362,7 +295,7 @@ export class Utils { Utils.injectedDownloads[url] = data } - public static downloadJson(url: string): Promise { + public static downloadJson(url: string, headers?: any): Promise { const injected = Utils.injectedDownloads[url] if (injected !== undefined) { @@ -371,7 +304,7 @@ export class Utils { } if (this.externalDownloadFunction !== undefined) { - return this.externalDownloadFunction(url) + return this.externalDownloadFunction(url, headers) } return new Promise((resolve, reject) => { @@ -379,7 +312,6 @@ export class Utils { xhr.onload = () => { if (xhr.status == 200) { try { - console.log("Got a response! Parsing now...") resolve(JSON.parse(xhr.response)) } catch (e) { reject("Not a valid json: " + xhr.response) @@ -390,6 +322,13 @@ export class Utils { }; xhr.open('GET', url); xhr.setRequestHeader("accept", "application/json") + if (headers !== undefined) { + + for (const key in headers) { + xhr.setRequestHeader(key, headers[key]) + } + } + xhr.send(); } ) @@ -449,22 +388,6 @@ export class Utils { return bestColor ?? hex; } - private static tile2long(x, z) { - return (x / Math.pow(2, z) * 360 - 180); - } - - private static tile2lat(y, z) { - const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); - return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); - } - - private static lon2tile(lon, zoom) { - return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom))); - } - - private static lat2tile(lat, zoom) { - return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom))); - } private static colorDiff(c0: { r: number, g: number, b: number }, c1: { r: number, g: number, b: number }) { return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) + Math.abs(c0.b - c1.b); @@ -506,5 +429,11 @@ export class Utils { } return copy } + + public static async waitFor(timeMillis: number): Promise { + return new Promise((resolve) => { + window.setTimeout(resolve, timeMillis); + }) + } } diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index c726b52dd..03dc010ee 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1592,8 +1592,13 @@ { "#": "plugs-9", "question": { - "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" + "en": "What kind of authentication is available at the charging station?", + "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" }, "render": { "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", @@ -1608,17 +1613,52 @@ "socket:type2_cable~*", "socket:type2_cable!=0" ] + }, + "en": { + "0": { + "then": "Authentication by a membership card" + }, + "1": { + "then": "Authentication by an app" + }, + "2": { + "then": "Authentication via phone call is available" + }, + "3": { + "then": "Authentication via phone call is available" + }, + "4": { + "then": "Authentication via NFC is available" + }, + "5": { + "then": "Authentication via Money Card is available" + }, + "6": { + "then": "Authentication via debit card is available" + }, + "7": { + "then": "No authentication is needed" + } } }, { "#": "voltage-9", "question": { - "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " + "en": "What's the phone number for authentication call or SMS?", + "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) ", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" }, "render": { - "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" }, "freeform": { "key": "socket:type2_cable:voltage", @@ -1650,7 +1690,7 @@ { "#": "current-9", "question": { - "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", + "en": "When is this charging station opened?", "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" }, "render": { @@ -1665,7 +1705,7 @@ { "if": "socket:socket:type2_cable:current=16 A", "then": { - "en": "Type 2 with cable (mennekes) outputs at most 16 A", + "en": "24/7 opened (including holidays)", "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" } }, @@ -1687,12 +1727,12 @@ { "#": "power-output-9", "question": { - "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" }, "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" }, "freeform": { "key": "socket:type2_cable:output", @@ -1702,8 +1742,8 @@ { "if": "socket:socket:type2_cable:output=11 kw", "then": { - "en": "Type 2 with cable (mennekes) outputs at most 11 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" + "en": "Free to use", + "nl": "Gratis te gebruiken" } }, { @@ -1740,17 +1780,31 @@ "socket:tesla_supercharger_ccs~*", "socket:tesla_supercharger_ccs!=0" ] + }, + "en": { + "mappings+": { + "0": { + "then": "Payment is done using a dedicated app" + } + } + }, + "nl": { + "mappings+": { + "0": { + "then": "Betalen via een app van het netwerk" + } + } } }, { "#": "voltage-10", "question": { - "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke spanning levert de stekker van type " + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" }, "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": " heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" }, "freeform": { "key": "socket:tesla_supercharger_ccs:voltage", @@ -1760,8 +1814,8 @@ { "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", - "nl": " heeft een spanning van 500 volt" + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" } }, { @@ -1782,11 +1836,11 @@ { "#": "current-10", "question": { - "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", + "en": "Is this charging station part of a network?", "nl": "Welke stroom levert de stekker van type ?" }, "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", + "en": "Part of the network {network}", "nl": " levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" }, "freeform": { @@ -1797,14 +1851,14 @@ { "if": "socket:socket:tesla_supercharger_ccs:current=125 A", "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", + "en": "Not part of a bigger network", "nl": " levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:tesla_supercharger_ccs:current=350 A", "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", + "en": "Not part of a bigger network", "nl": " levert een stroom van maximaal 350 A" } } @@ -1849,11 +1903,11 @@ { "#": "plugs-11", "question": { - "en": "How much plugs of type Tesla Supercharger (destination) are available here?", + "en": "What number can one call if there is a problem with this charging station?", "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" }, "render": { - "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", + "en": "In case of problems, call {phone}", "nl": "Hier zijn stekkers van het type " }, "freeform": { @@ -1870,11 +1924,11 @@ { "#": "voltage-11", "question": { - "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", + "en": "What is the email address of the operator?", "nl": "Welke spanning levert de stekker van type " }, "render": { - "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", + "en": "In case of problems, send an email to {email}", "nl": " heeft een spanning van {socket:tesla_destination:voltage} volt" }, "freeform": { @@ -1900,11 +1954,11 @@ { "#": "current-11", "question": { - "en": "What current do the plugs with Tesla Supercharger (destination) offer?", + "en": "What is the website of the operator?", "nl": "Welke stroom levert de stekker van type ?" }, "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", + "en": "More info on {website}", "nl": " levert een stroom van maximaal {socket:tesla_destination:current}A" }, "freeform": { @@ -1981,7 +2035,7 @@ { "#": "plugs-12", "question": { - "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", + "en": "What is the reference number of this charging station?", "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" }, "render": { @@ -2002,8 +2056,8 @@ { "#": "voltage-12", "question": { - "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke spanning levert de stekker van type " + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" }, "render": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", @@ -2017,15 +2071,15 @@ { "if": "socket:socket:tesla_destination:voltage=230 V", "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", - "nl": " heeft een spanning van 230 volt" + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" } }, { "if": "socket:socket:tesla_destination:voltage=400 V", "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", - "nl": " heeft een spanning van 400 volt" + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" } } ], @@ -2296,6 +2350,14 @@ "en": "Payment is done using a dedicated app", "nl": "Betalen via een app van het netwerk" } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } } ], "mappings": [ diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 3645da855..8b0651933 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -1,61 +1,6 @@ { - "id": "drinking_water", - "name": { - "en": "Drinking water", - "nl": "Drinkbaar water", - "fr": "Eau potable", - "gl": "Auga potábel", - "de": "Trinkwasser", - "it": "Acqua potabile", - "ru": "Питьевая вода", - "id": "Air minum" - }, - "title": { - "render": { - "en": "Drinking water", - "nl": "Drinkbaar water", - "fr": "Eau potable", - "gl": "Auga potábel", - "de": "Trinkwasser", - "it": "Acqua potabile", - "ru": "Питьевая вода", - "id": "Air minum" - } - }, - "icon": { - "render": "pin:#6BC4F7;./assets/layers/drinking_water/drips.svg" - }, - "iconOverlays": [ - { - "if": { - "or": [ - "operational_status=broken", - "operational_status=closed" - ] - }, - "then": "close:#c33", - "badge": true - } - ], - "iconSize": "40,40,bottom", - "source": { - "osmTags": { - "and": [ - "amenity=drinking_water", - "access!=permissive", - "access!=private" - ] - } - }, - "calculatedTags": [ - "_closest_other_drinking_water_id=feat.closest('drinking_water')?.id", - "_closest_other_drinking_water_distance=Math.floor(feat.distanceTo(feat.closest('drinking_water')).distance * 1000)" - ], - "minzoom": 13, - "wayHandling": 1, - "presets": [ - { - "title": { + "id": "drinking_water", + "name": { "en": "Drinking water", "nl": "Drinkbaar water", "fr": "Eau potable", @@ -64,105 +9,161 @@ "it": "Acqua potabile", "ru": "Питьевая вода", "id": "Air minum" - }, - "tags": [ - "amenity=drinking_water" - ] - } - ], - "color": "#6bc4f7", - "tagRenderings": [ - "images", - { - "#": "Still in use?", - "question": { - "en": "Is this drinking water spot still operational?", - "nl": "Is deze drinkwaterkraan nog steeds werkende?", - "it": "Questo punto di acqua potabile è sempre funzionante?", - "fr": "Ce point d'eau potable est-il toujours opérationnel ?", - "de": "Ist diese Trinkwasserstelle noch in Betrieb?" - }, - "render": { - "en": "The operational status is {operational_status", - "nl": "Deze waterkraan-status is {operational_status}", - "it": "Lo stato operativo è {operational_status}", - "fr": "L'état opérationnel est {operational_status", - "de": "Der Betriebsstatus ist {operational_status" - }, - "freeform": { - "key": "operational_status" - }, - "mappings": [ - { - "if": "operational_status=", - "then": { - "en": "This drinking water works", - "nl": "Deze drinkwaterfontein werkt", - "it": "La fontanella funziona", - "fr": "Cette fontaine fonctionne" - } - }, - { - "if": "operational_status=broken", - "then": { - "en": "This drinking water is broken", - "nl": "Deze drinkwaterfontein is kapot", - "it": "La fontanella è guasta", - "fr": "Cette fontaine est cassée" - } - }, - { - "if": "operational_status=closed", - "then": { - "en": "This drinking water is closed", - "nl": "Deze drinkwaterfontein is afgesloten", - "it": "La fontanella è chiusa", - "fr": "Cette fontaine est fermée" - } - } - ] }, - { - "#": "Bottle refill", - "question": { - "en": "How easy is it to fill water bottles?", - "nl": "Hoe gemakkelijk is het om drinkbussen bij te vullen?", - "de": "Wie einfach ist es, Wasserflaschen zu füllen?", - "it": "Quanto è facile riempire d’acqua le bottiglie?", - "fr": "Est-il facile de remplir des bouteilles d'eau ?" - }, - "mappings": [ + "title": { + "render": { + "en": "Drinking water", + "nl": "Drinkbaar water", + "fr": "Eau potable", + "gl": "Auga potábel", + "de": "Trinkwasser", + "it": "Acqua potabile", + "ru": "Питьевая вода", + "id": "Air minum" + } + }, + "icon": { + "render": "pin:#6BC4F7;./assets/layers/drinking_water/drips.svg" + }, + "iconOverlays": [ { - "if": "bottle=yes", - "then": { - "en": "It is easy to refill water bottles", - "nl": "Een drinkbus bijvullen gaat makkelijk", - "de": "Es ist einfach, Wasserflaschen nachzufüllen", - "it": "È facile riempire d’acqua le bottiglie", - "fr": "Il est facile de remplir les bouteilles d'eau" - } + "if": { + "or": [ + "operational_status=broken", + "operational_status=closed" + ] + }, + "then": "close:#c33", + "badge": true + } + ], + "iconSize": "40,40,bottom", + "source": { + "osmTags": { + "and": [ + "amenity=drinking_water", + "access!=permissive", + "access!=private" + ] + } + }, + "calculatedTags": [ + "_closest_other_drinking_water=feat.closestn('drinking_water', 1, 500).map(f => ({id: f.feat.id, distance: f.distance}))[0]", + "_closest_other_drinking_water_id=JSON.parse(feat.properties._closest_other_drinking_water)?.id", + "_closest_other_drinking_water_distance=Math.floor(JSON.parse(feat.properties._closest_other_drinking_water)?.distance * 1000)" + ], + "minzoom": 13, + "wayHandling": 1, + "presets": [ + { + "title": { + "en": "Drinking water", + "nl": "Drinkbaar water", + "fr": "Eau potable", + "gl": "Auga potábel", + "de": "Trinkwasser", + "it": "Acqua potabile", + "ru": "Питьевая вода", + "id": "Air minum" + }, + "tags": [ + "amenity=drinking_water" + ] + } + ], + "color": "#6bc4f7", + "tagRenderings": [ + "images", + { + "#": "Still in use?", + "question": { + "en": "Is this drinking water spot still operational?", + "nl": "Is deze drinkwaterkraan nog steeds werkende?", + "it": "Questo punto di acqua potabile è sempre funzionante?", + "fr": "Ce point d'eau potable est-il toujours opérationnel ?", + "de": "Ist diese Trinkwasserstelle noch in Betrieb?" + }, + "render": { + "en": "The operational status is {operational_status", + "nl": "Deze waterkraan-status is {operational_status}", + "it": "Lo stato operativo è {operational_status}", + "fr": "L'état opérationnel est {operational_status", + "de": "Der Betriebsstatus ist {operational_status" + }, + "freeform": { + "key": "operational_status" + }, + "mappings": [ + { + "if": "operational_status=", + "then": { + "en": "This drinking water works", + "nl": "Deze drinkwaterfontein werkt", + "it": "La fontanella funziona", + "fr": "Cette fontaine fonctionne" + } + }, + { + "if": "operational_status=broken", + "then": { + "en": "This drinking water is broken", + "nl": "Deze drinkwaterfontein is kapot", + "it": "La fontanella è guasta", + "fr": "Cette fontaine est cassée" + } + }, + { + "if": "operational_status=closed", + "then": { + "en": "This drinking water is closed", + "nl": "Deze drinkwaterfontein is afgesloten", + "it": "La fontanella è chiusa", + "fr": "Cette fontaine est fermée" + } + } + ] }, { - "if": "bottle=no", - "then": { - "en": "Water bottles may not fit", - "nl": "Een drinkbus past moeilijk", - "de": "Wasserflaschen passen möglicherweise nicht", - "it": "Le bottiglie d’acqua potrebbero non entrare", - "fr": "Les bouteilles d'eau peuvent ne pas passer" - } + "#": "Bottle refill", + "question": { + "en": "How easy is it to fill water bottles?", + "nl": "Hoe gemakkelijk is het om drinkbussen bij te vullen?", + "de": "Wie einfach ist es, Wasserflaschen zu füllen?", + "it": "Quanto è facile riempire d’acqua le bottiglie?", + "fr": "Est-il facile de remplir des bouteilles d'eau ?" + }, + "mappings": [ + { + "if": "bottle=yes", + "then": { + "en": "It is easy to refill water bottles", + "nl": "Een drinkbus bijvullen gaat makkelijk", + "de": "Es ist einfach, Wasserflaschen nachzufüllen", + "it": "È facile riempire d’acqua le bottiglie", + "fr": "Il est facile de remplir les bouteilles d'eau" + } + }, + { + "if": "bottle=no", + "then": { + "en": "Water bottles may not fit", + "nl": "Een drinkbus past moeilijk", + "de": "Wasserflaschen passen möglicherweise nicht", + "it": "Le bottiglie d’acqua potrebbero non entrare", + "fr": "Les bouteilles d'eau peuvent ne pas passer" + } + } + ] + }, + { + "render": { + "en": "There is another drinking water fountain at {_closest_other_drinking_water_distance} meter", + "nl": "Er bevindt zich een ander drinkwaterpunt op {_closest_other_drinking_water_distance} meter", + "it": "C’è un’altra fontanella a {_closest_other_drinking_water_distance} metri", + "de": "Ein weiterer Trinkwasserbrunnen befindet sich in {_closest_other_drinking_water_distance} Meter", + "fr": "Une autre source d’eau potable est à {_closest_other_drinking_water_distance} mètres a>" + }, + "condition": "_closest_other_drinking_water_id~*" } - ] - }, - { - "render": { - "en": "There is another drinking water fountain at {_closest_other_drinking_water_distance} meter", - "nl": "Er bevindt zich een ander drinkwaterpunt op {_closest_other_drinking_water_distance} meter", - "it": "C’è un’altra fontanella a {_closest_other_drinking_water_distance} metri", - "de": "Ein weiterer Trinkwasserbrunnen befindet sich in {_closest_other_drinking_water_distance} Meter", - "fr": "Une autre source d’eau potable est à {_closest_other_drinking_water_distance} mètres a>" - }, - "condition": "_closest_other_drinking_water_id~*" - } - ] + ] } \ No newline at end of file diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 26dbcc65a..64b18a306 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -12,6 +12,7 @@ ] } }, + "minzoom": 12, "wayHandling": 1, "icon": { "render": "circle:white;./assets/layers/food/restaurant.svg", diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index 786d7e220..a4715ce5a 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -19,7 +19,7 @@ "source": { "osmTags": "amenity=public_bookcase" }, - "minzoom": 12, + "minzoom": 10, "wayHandling": 2, "title": { "render": { diff --git a/assets/themes/benches/benches.json b/assets/themes/benches/benches.json index a43292dbf..d94f88597 100644 --- a/assets/themes/benches/benches.json +++ b/assets/themes/benches/benches.json @@ -52,7 +52,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ "bench", diff --git a/assets/themes/bicyclelib/bicyclelib.json b/assets/themes/bicyclelib/bicyclelib.json index fe40e2d34..3a8fb3e8e 100644 --- a/assets/themes/bicyclelib/bicyclelib.json +++ b/assets/themes/bicyclelib/bicyclelib.json @@ -40,7 +40,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "roamingRenderings": [], "layers": [ "bicycle_library" diff --git a/assets/themes/bike_monitoring_station/bike_monitoring_stations.json b/assets/themes/bike_monitoring_station/bike_monitoring_stations.json index 5703e2ed5..f64339938 100644 --- a/assets/themes/bike_monitoring_station/bike_monitoring_stations.json +++ b/assets/themes/bike_monitoring_station/bike_monitoring_stations.json @@ -47,7 +47,7 @@ "startLat": 50.8435, "startLon": 4.3688, "startZoom": 14, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ "bike_monitoring_station" diff --git a/assets/themes/binoculars/binoculars.json b/assets/themes/binoculars/binoculars.json index 0f9817e46..75622d3a5 100644 --- a/assets/themes/binoculars/binoculars.json +++ b/assets/themes/binoculars/binoculars.json @@ -22,7 +22,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ "binocular" diff --git a/assets/themes/buurtnatuur/buurtnatuur.json b/assets/themes/buurtnatuur/buurtnatuur.json index 9c82e4d44..9c4f2a189 100644 --- a/assets/themes/buurtnatuur/buurtnatuur.json +++ b/assets/themes/buurtnatuur/buurtnatuur.json @@ -25,7 +25,7 @@ "startLat": 50.8435, "startLon": 4.3688, "startZoom": 16, - "widenFactor": 0.01, + "widenFactor": 1.2, "socialImage": "./assets/themes/buurtnatuur/social_image.jpg", "layers": [ { diff --git a/assets/themes/cafes_and_pubs/cafes_and_pubs.json b/assets/themes/cafes_and_pubs/cafes_and_pubs.json index 1f9b704f8..c7ec682d1 100644 --- a/assets/themes/cafes_and_pubs/cafes_and_pubs.json +++ b/assets/themes/cafes_and_pubs/cafes_and_pubs.json @@ -18,7 +18,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ "cafe_pub" diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index 7b24124e5..592b9f544 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -47,7 +47,7 @@ "startLat": 43.14, "startLon": 3.14, "startZoom": 14, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "./assets/themes/campersite/Bar%C3%9Fel_Wohnmobilstellplatz.jpg", "layers": [ { diff --git a/assets/themes/charging_stations/charging_stations.json b/assets/themes/charging_stations/charging_stations.json index b193b4daf..16745dc60 100644 --- a/assets/themes/charging_stations/charging_stations.json +++ b/assets/themes/charging_stations/charging_stations.json @@ -39,7 +39,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "defaultBackgroundId": "CartoDB.Voyager", "layers": [ diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index 44a2520db..143fcddc3 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -48,7 +48,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ { diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 85a91aa7d..09312a9ea 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -21,7 +21,7 @@ "clustering": { "maxZoom": 1 }, - "widenFactor": 0.005, + "widenFactor": 1.1, "enableDownload": true, "enablePdfDownload": true, "layers": [ diff --git a/assets/themes/cycle_infra/cycle_infra.json b/assets/themes/cycle_infra/cycle_infra.json index 7446095d9..8af56b886 100644 --- a/assets/themes/cycle_infra/cycle_infra.json +++ b/assets/themes/cycle_infra/cycle_infra.json @@ -24,7 +24,7 @@ "startLat": 51, "startLon": 3.75, "startZoom": 11, - "widenFactor": 1, + "widenFactor": 1.5, "socialImage": "./assets/themes/cycle_infra/cycle-infra.svg", "enableDownload": true, "layers": [ diff --git a/assets/themes/cyclofix/cyclofix.json b/assets/themes/cyclofix/cyclofix.json index 2e1016d77..47a626736 100644 --- a/assets/themes/cyclofix/cyclofix.json +++ b/assets/themes/cyclofix/cyclofix.json @@ -40,7 +40,7 @@ "defaultBackgroundId": "CartoDB.Voyager", "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "assets/themes/cyclofix/logo.svg", "layers": [ "bike_cafe", diff --git a/assets/themes/facadegardens/facadegardens.json b/assets/themes/facadegardens/facadegardens.json index f3e28a1ef..08a87063c 100644 --- a/assets/themes/facadegardens/facadegardens.json +++ b/assets/themes/facadegardens/facadegardens.json @@ -38,7 +38,7 @@ "startLat": 51.02768, "startLon": 4.480705, "startZoom": 15, - "widenFactor": 0.05, + "widenFactor": 1.5, "socialImage": "", "layers": [ { diff --git a/assets/themes/food/food.json b/assets/themes/food/food.json index 416f9d161..fed26b560 100644 --- a/assets/themes/food/food.json +++ b/assets/themes/food/food.json @@ -18,7 +18,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ "food" diff --git a/assets/themes/fritures/fritures.json b/assets/themes/fritures/fritures.json index 33d83c0f5..fec9038cc 100644 --- a/assets/themes/fritures/fritures.json +++ b/assets/themes/fritures/fritures.json @@ -24,7 +24,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ { diff --git a/assets/themes/fruit_trees/fruit_trees.json b/assets/themes/fruit_trees/fruit_trees.json index 60d6e8a23..58b350f89 100644 --- a/assets/themes/fruit_trees/fruit_trees.json +++ b/assets/themes/fruit_trees/fruit_trees.json @@ -18,7 +18,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.001, + "widenFactor": 2, "socialImage": "", "hideFromOverview": true, "layers": [ diff --git a/assets/themes/ghostbikes/ghostbikes.json b/assets/themes/ghostbikes/ghostbikes.json index 229a32751..7c9aec38b 100644 --- a/assets/themes/ghostbikes/ghostbikes.json +++ b/assets/themes/ghostbikes/ghostbikes.json @@ -52,7 +52,7 @@ "startZoom": 1, "startLat": 0, "startLon": 0, - "widenFactor": 0.1, + "widenFactor": 5, "layers": [ "ghost_bike" ], diff --git a/assets/themes/grb.json b/assets/themes/grb.json index 37efacae0..dee3ca22f 100644 --- a/assets/themes/grb.json +++ b/assets/themes/grb.json @@ -18,7 +18,7 @@ "startLat": 51.2132, "startLon": 3.231, "startZoom": 14, - "widenFactor": 0.05, + "widenFactor": 2, "cacheTimeout": 3600, "socialImage": "", "layers": [ diff --git a/assets/themes/hackerspaces/hackerspaces.json b/assets/themes/hackerspaces/hackerspaces.json index 538bc0414..98bf59574 100644 --- a/assets/themes/hackerspaces/hackerspaces.json +++ b/assets/themes/hackerspaces/hackerspaces.json @@ -18,7 +18,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 5, "socialImage": "", "layers": [ { diff --git a/assets/themes/hailhydrant/hailhydrant.json b/assets/themes/hailhydrant/hailhydrant.json index c5cdd3795..c6e7a1f84 100644 --- a/assets/themes/hailhydrant/hailhydrant.json +++ b/assets/themes/hailhydrant/hailhydrant.json @@ -36,7 +36,7 @@ "startLat": 13.67801, "startLon": 121.6625, "startZoom": 6, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ { diff --git a/assets/themes/maps/maps.json b/assets/themes/maps/maps.json index e34cdce30..52d757a9d 100644 --- a/assets/themes/maps/maps.json +++ b/assets/themes/maps/maps.json @@ -36,7 +36,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 5, "socialImage": "", "layers": [ "map" diff --git a/assets/themes/nature/nature.json b/assets/themes/nature/nature.json index 9aeb9e498..0f8004e2c 100644 --- a/assets/themes/nature/nature.json +++ b/assets/themes/nature/nature.json @@ -18,7 +18,7 @@ "startLat": 51.20875, "startLon": 3.22435, "startZoom": 12, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "layers": [ "drinking_water", diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index 4616fae02..f0e921d38 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -23,7 +23,7 @@ "startLat": 51.20875, "startLon": 3.22435, "startZoom": 15, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "defaultBackgroundId": "CartoDB.Positron", "enablePdfDownload": true, diff --git a/assets/themes/observation_towers/observation_towers.json b/assets/themes/observation_towers/observation_towers.json index 2bdd0cbad..3ab5a6b58 100644 --- a/assets/themes/observation_towers/observation_towers.json +++ b/assets/themes/observation_towers/observation_towers.json @@ -22,7 +22,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 5, "socialImage": "", "layers": [ "observation_tower" diff --git a/assets/themes/parkings/parkings.json b/assets/themes/parkings/parkings.json index bb25e8bfd..ca322052c 100644 --- a/assets/themes/parkings/parkings.json +++ b/assets/themes/parkings/parkings.json @@ -22,7 +22,7 @@ "startLat": 51.20875, "startLon": 3.22435, "startZoom": 12, - "widenFactor": 0.05, + "widenFactor": 1.2, "socialImage": "", "layers": [ "parking" diff --git a/assets/themes/personal/personal.json b/assets/themes/personal/personal.json index 02e26cdbd..d9df14a7b 100644 --- a/assets/themes/personal/personal.json +++ b/assets/themes/personal/personal.json @@ -41,7 +41,7 @@ "startLat": 0, "startLon": 0, "startZoom": 16, - "widenFactor": 0.05, + "widenFactor": 3, "layers": [], "roamingRenderings": [] } \ No newline at end of file diff --git a/assets/themes/play_forests/play_forests.json b/assets/themes/play_forests/play_forests.json index bd824ee67..5c65a702a 100644 --- a/assets/themes/play_forests/play_forests.json +++ b/assets/themes/play_forests/play_forests.json @@ -19,7 +19,7 @@ "startLon": 0, "startZoom": 1, "hideFromOverview": true, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ "play_forest" diff --git a/assets/themes/playgrounds/playgrounds.json b/assets/themes/playgrounds/playgrounds.json index 418aa36bc..2908a88b4 100644 --- a/assets/themes/playgrounds/playgrounds.json +++ b/assets/themes/playgrounds/playgrounds.json @@ -38,7 +38,7 @@ "startLat": 50.535, "startLon": 4.399, "startZoom": 13, - "widenFactor": 0.05, + "widenFactor": 5, "socialImage": "", "layers": [ "playground" diff --git a/assets/themes/shops/shops.json b/assets/themes/shops/shops.json index 09209f3f4..706268e3d 100644 --- a/assets/themes/shops/shops.json +++ b/assets/themes/shops/shops.json @@ -34,7 +34,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 3, "socialImage": "", "layers": [ { diff --git a/assets/themes/speelplekken/speelplekken.json b/assets/themes/speelplekken/speelplekken.json index 21cb17212..a87466742 100644 --- a/assets/themes/speelplekken/speelplekken.json +++ b/assets/themes/speelplekken/speelplekken.json @@ -22,7 +22,7 @@ "startLat": 51.17174, "startLon": 4.449462, "startZoom": 12, - "widenFactor": 0.05, + "widenFactor": 1.2, "socialImage": "./assets/themes/speelplekken/social_image.jpg", "defaultBackgroundId": "CartoDB.Positron", "layers": [ diff --git a/assets/themes/speelplekken/speelplekken_temp.json b/assets/themes/speelplekken/speelplekken_temp.json index 867298215..752d793b3 100644 --- a/assets/themes/speelplekken/speelplekken_temp.json +++ b/assets/themes/speelplekken/speelplekken_temp.json @@ -20,7 +20,7 @@ "startLat": 51.17174, "startLon": 4.449462, "startZoom": 12, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "defaultBackgroundId": "CartoDB.Positron", "layers": [ diff --git a/assets/themes/sport_pitches/sport_pitches.json b/assets/themes/sport_pitches/sport_pitches.json index 7f1fdeebd..4db24cdd6 100644 --- a/assets/themes/sport_pitches/sport_pitches.json +++ b/assets/themes/sport_pitches/sport_pitches.json @@ -37,7 +37,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "layers": [ "sport_pitch" diff --git a/assets/themes/surveillance/surveillance.json b/assets/themes/surveillance/surveillance.json index 001045fd1..a61633b23 100644 --- a/assets/themes/surveillance/surveillance.json +++ b/assets/themes/surveillance/surveillance.json @@ -37,7 +37,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "defaultBackgroundId": "osm", "layers": [ diff --git a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json index be5567d5d..c09b548e8 100644 --- a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json +++ b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json @@ -20,7 +20,7 @@ "startZoom": 8, "startLat": 50.8536, "startLon": 4.433, - "widenFactor": 0.2, + "widenFactor": 2, "layers": [ { "builtin": [ diff --git a/assets/themes/toilets/toilets.json b/assets/themes/toilets/toilets.json index e343d9ea2..1d1567de4 100644 --- a/assets/themes/toilets/toilets.json +++ b/assets/themes/toilets/toilets.json @@ -35,7 +35,7 @@ "startZoom": 12, "startLat": 51.2095, "startLon": 3.2222, - "widenFactor": 0.05, + "widenFactor": 3, "icon": "./assets/themes/toilets/toilets.svg", "layers": [ "toilet" diff --git a/assets/themes/trees/trees.json b/assets/themes/trees/trees.json index ea26fdeca..52961293a 100644 --- a/assets/themes/trees/trees.json +++ b/assets/themes/trees/trees.json @@ -45,7 +45,7 @@ "startLat": 50.642, "startLon": 4.482, "startZoom": 8, - "widenFactor": 0.01, + "widenFactor": 1.5, "socialImage": "./assets/themes/trees/logo.svg", "clustering": { "maxZoom": 18 diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index b89d25e60..7de4c3a36 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -18,7 +18,7 @@ "startLat": -0.08528530407, "startLon": 51.52103754846, "startZoom": 18, - "widenFactor": 0.5, + "widenFactor": 1.5, "socialImage": "", "layers": [ { diff --git a/assets/themes/waste_basket/waste_basket.json b/assets/themes/waste_basket/waste_basket.json index 2a1ecef84..eb95d85eb 100644 --- a/assets/themes/waste_basket/waste_basket.json +++ b/assets/themes/waste_basket/waste_basket.json @@ -22,7 +22,7 @@ "startLat": 0, "startLon": 0, "startZoom": 1, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "layers": [ { diff --git a/assets/themes/widths/width.json b/assets/themes/widths/width.json index 5ee75eb7e..6e56d5f21 100644 --- a/assets/themes/widths/width.json +++ b/assets/themes/widths/width.json @@ -25,7 +25,7 @@ "startLat": 51.20875, "startLon": 3.22435, "startZoom": 14, - "widenFactor": 0.05, + "widenFactor": 2, "socialImage": "", "layers": [ { diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts index 3551ba5f8..22cdd0d93 100644 --- a/scripts/ScriptUtils.ts +++ b/scripts/ScriptUtils.ts @@ -48,13 +48,10 @@ export default class ScriptUtils { }) } - public static DownloadJSON(url, options?: { - headers: any - }): Promise { + public static DownloadJSON(url, headers?: any): Promise { return new Promise((resolve, reject) => { try { - - const headers = options?.headers ?? {} + headers = headers ?? {} headers.accept = "application/json" console.log("Fetching", url) const urlObj = new URL(url) diff --git a/scripts/generateCache.ts b/scripts/generateCache.ts index 5a8377125..a5a915bde 100644 --- a/scripts/generateCache.ts +++ b/scripts/generateCache.ts @@ -14,7 +14,7 @@ import RelationsTracker from "../Logic/Osm/RelationsTracker"; import * as OsmToGeoJson from "osmtogeojson"; import MetaTagging from "../Logic/MetaTagging"; import {UIEventSource} from "../Logic/UIEventSource"; -import {TileRange} from "../Models/TileRange"; +import {TileRange, Tiles} from "../Models/TileRange"; import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; import ScriptUtils from "./ScriptUtils"; import PerLayerFeatureSourceSplitter from "../Logic/FeatureSource/PerLayerFeatureSourceSplitter"; @@ -86,7 +86,7 @@ async function downloadRaw(targetdir: string, r: TileRange, overpass: Overpass)/ } console.log("x:", (x - r.xstart), "/", (r.xend - r.xstart), "; y:", (y - r.ystart), "/", (r.yend - r.ystart), "; total: ", downloaded, "/", r.total, "failed: ", failed, "skipped: ", skipped) - const boundsArr = Utils.tile_bounds(r.zoomlevel, x, y) + const boundsArr = Tiles.tile_bounds(r.zoomlevel, x, y) const bounds = { north: Math.max(boundsArr[0][0], boundsArr[1][0]), south: Math.min(boundsArr[0][0], boundsArr[1][0]), @@ -174,7 +174,7 @@ function loadAllTiles(targetdir: string, r: TileRange, theme: LayoutConfig, extr allFeatures.push(...geojson.features) } } - return new StaticFeatureSource(allFeatures) + return new StaticFeatureSource(allFeatures, false) } /** @@ -225,7 +225,7 @@ function postProcess(allFeatures: FeatureSource, theme: LayoutConfig, relationsT delete feature.feature["bbox"] } // Lets save this tile! - const [z, x, y] = Utils.tile_from_index(tile.tileIndex) + const [z, x, y] = Tiles.tile_from_index(tile.tileIndex) console.log("Writing tile ", z, x, y, layerId) const targetPath = geoJsonName(targetdir + "_" + layerId, x, y, z) createdTiles.push(tile.tileIndex) @@ -241,7 +241,7 @@ function postProcess(allFeatures: FeatureSource, theme: LayoutConfig, relationsT // Only thing left to do is to create the index const path = targetdir + "_" + layerId + "_overview.json" const perX = {} - createdTiles.map(i => Utils.tile_from_index(i)).forEach(([z, x, y]) => { + createdTiles.map(i => Tiles.tile_from_index(i)).forEach(([z, x, y]) => { const key = "" + x if (perX[key] === undefined) { perX[key] = [] @@ -279,7 +279,7 @@ async function main(args: string[]) { const lat1 = Number(args[5]) const lon1 = Number(args[6]) - const tileRange = Utils.TileRangeBetween(zoomlevel, lat0, lon0, lat1, lon1) + const tileRange = Tiles.TileRangeBetween(zoomlevel, lat0, lon0, lat1, lon1) const theme = AllKnownLayouts.allKnownLayouts.get(themeName) if (theme === undefined) { diff --git a/scripts/generateTranslations.ts b/scripts/generateTranslations.ts index ce05cf32b..6fda20b0b 100644 --- a/scripts/generateTranslations.ts +++ b/scripts/generateTranslations.ts @@ -33,7 +33,7 @@ class TranslationPart { } const v = translations[translationsKey] if (typeof (v) != "string") { - console.error("Non-string object in translation: ", translations[translationsKey]) + console.error("Non-string object in translation while trying to add more translations to '", translationsKey ,"': ", v) throw "Error in an object depicting a translation: a non-string object was found. (" + context + ")\n You probably put some other section accidentally in the translation" } this.contents.set(translationsKey, v) @@ -41,9 +41,7 @@ class TranslationPart { } recursiveAdd(object: any, context: string) { - - - const isProbablyTranslationObject = knownLanguages.map(l => object.hasOwnProperty(l)).filter(x => x).length > 0; + const isProbablyTranslationObject = knownLanguages.some(l => object.hasOwnProperty(l)); if (isProbablyTranslationObject) { this.addTranslationObject(object, context) return; From f749cb0963c63341d1b566a9b7237a776f1c4065 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 26 Sep 2021 17:49:47 +0200 Subject: [PATCH 034/110] Small changes --- Logic/MetaTagging.ts | 5 +---- assets/layers/drinking_water/drinking_water.json | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index c7eda184b..c02ed04e4 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -111,13 +111,11 @@ export default class MetaTagging { d = JSON.stringify(d); } feature.properties[key] = d; - console.log("Written a delayed calculated tag onto ", feature.properties.id, ": ", key, ":==", d) }) result = result.data } if (result === undefined || result === "") { - console.log("Calculated tag for", key, "gave undefined", feature.properties.id) return; } if (typeof result !== "string") { @@ -125,10 +123,9 @@ export default class MetaTagging { result = JSON.stringify(result); } feature.properties[key] = result; - console.log("Written a calculated tag onto ", feature.properties.id, ": ", key, ":==", result) } catch (e) { if (MetaTagging.errorPrintCount < MetaTagging.stopErrorOutputAt) { - console.warn("Could not calculate a calculated tag defined by " + code + " due to " + e + ". This is code defined in the theme. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", e) + console.warn("Could not calculate a calculated tag defined by " + code + " due to " + e + ". This is code defined in the theme. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", e,e.stack) MetaTagging.errorPrintCount++; if (MetaTagging.errorPrintCount == MetaTagging.stopErrorOutputAt) { console.error("Got ", MetaTagging.stopErrorOutputAt, " errors calculating this metatagging - stopping output now") diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 8b0651933..abd282403 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -48,9 +48,9 @@ } }, "calculatedTags": [ - "_closest_other_drinking_water=feat.closestn('drinking_water', 1, 500).map(f => ({id: f.feat.id, distance: f.distance}))[0]", + "_closest_other_drinking_water=feat.closestn('drinking_water', 1, undefined, 5000).map(f => ({id: f.feat.id, distance: ''+f.distance}))[0]", "_closest_other_drinking_water_id=JSON.parse(feat.properties._closest_other_drinking_water)?.id", - "_closest_other_drinking_water_distance=Math.floor(JSON.parse(feat.properties._closest_other_drinking_water)?.distance * 1000)" + "_closest_other_drinking_water_distance=Math.floor(Number(JSON.parse(feat.properties._closest_other_drinking_water)?.distance) * 1000)" ], "minzoom": 13, "wayHandling": 1, From a3c16d62977c9bc59c932c33140f73fc19176556 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 26 Sep 2021 23:35:26 +0200 Subject: [PATCH 035/110] Stabilize adding new points --- Logic/Actors/OverpassFeatureSource.ts | 2 - .../NewGeometryFromChangesFeatureSource.ts | 4 + Logic/Osm/Changes.ts | 89 ++--- Logic/Osm/ChangesetHandler.ts | 354 +++++++++--------- Logic/Osm/OsmConnection.ts | 13 +- Logic/Osm/OsmObject.ts | 17 - State.ts | 11 +- UI/BigComponents/SimpleAddUI.ts | 1 - assets/themes/uk_addresses/uk_addresses.json | 6 + langs/en.json | 3 +- langs/themes/en.json | 6 +- 11 files changed, 249 insertions(+), 257 deletions(-) diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index 7ddafacd8..20181fc14 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -56,8 +56,6 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour readonly overpassTimeout: UIEventSource; readonly overpassMaxZoom: UIEventSource }) { - console.trace("Initializing an overpass FS") - this.state = state this.relationsTracker = new RelationsTracker() diff --git a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts index f4619b2aa..0a1c67f41 100644 --- a/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts +++ b/Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource.ts @@ -3,6 +3,7 @@ import {OsmNode, OsmRelation, OsmWay} from "../../Osm/OsmObject"; import FeatureSource from "../FeatureSource"; import {UIEventSource} from "../../UIEventSource"; import {ChangeDescription} from "../../Osm/Actions/ChangeDescription"; +import State from "../../../State"; export class NewGeometryFromChangesFeatureSource implements FeatureSource { // This class name truly puts the 'Java' into 'Javascript' @@ -54,6 +55,9 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource { tags[kv.k] = kv.v } tags["id"] = change.type+"/"+change.id + + tags["_backend"] = State.state.osmConnection._oauth_config.url + switch (change.type) { case "node": const n = new OsmNode(change.id) diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index 83545b30a..828ba7d78 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -14,7 +14,7 @@ import {LocalStorageSource} from "../Web/LocalStorageSource"; export class Changes { - private _nextId : number = -1; // Newly assigned ID's are negative + private _nextId: number = -1; // Newly assigned ID's are negative public readonly name = "Newly added features" /** * All the newly created features as featureSource + all the modified features @@ -31,7 +31,10 @@ export class Changes { // We keep track of all changes just as well this.allChanges.setData([...this.pendingChanges.data]) // If a pending change contains a negative ID, we save that - this._nextId = Math.min(-1, ...this.pendingChanges.data?.map(pch => pch.id) ?? []) + this._nextId = Math.min(-1, ...this.pendingChanges.data?.map(pch => pch.id) ?? []) + + // Note: a changeset might be reused which was opened just before and might have already used some ids + // This doesn't matter however, as the '-1' is per piecewise upload, not global per changeset } private static createChangesetFor(csId: string, @@ -90,62 +93,58 @@ export class Changes { if (this.pendingChanges.data.length === 0) { return; } - if (this.isUploading.data) { console.log("Is already uploading... Abort") return; } - - this.isUploading.setData(true) + this.flushChangesAsync(flushreason) + .then(_ => { + this.isUploading.setData(false) + console.log("Changes flushed!"); + }) + .catch(e => { + this.isUploading.setData(false) + console.error("Flushing changes failed due to", e); + }) + } + + private async flushChangesAsync(flushreason: string = undefined): Promise { console.log("Beginning upload... " + flushreason ?? ""); // At last, we build the changeset and upload const self = this; const pending = self.pendingChanges.data; const neededIds = Changes.GetNeededIds(pending) - console.log("Needed ids", neededIds) - OsmObject.DownloadAll(neededIds, true).addCallbackAndRunD(osmObjects => { - console.log("Got the fresh objects!", osmObjects, "pending: ", pending) - try { - - - const changes: { - newObjects: OsmObject[], - modifiedObjects: OsmObject[] - deletedObjects: OsmObject[] - - } = self.CreateChangesetObjects(pending, osmObjects) - if (changes.newObjects.length + changes.deletedObjects.length + changes.modifiedObjects.length === 0) { - console.log("No changes to be made") - self.pendingChanges.setData([]) - self.isUploading.setData(false) - return true; // Unregister the callback - } - - - State.state.osmConnection.UploadChangeset( - State.state.layoutToUse.data, - State.state.allElements, - (csId) => Changes.createChangesetFor(csId, changes), - () => { - console.log("Upload successfull!") - self.pendingChanges.setData([]); - self.isUploading.setData(false) - }, - () => { - console.log("Upload failed - trying again later") - return self.isUploading.setData(false); - } // Failed - mark to try again - ) - } catch (e) { - console.error("Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", e) + const osmObjects = await Promise.all(neededIds.map(id => OsmObject.DownloadObjectAsync(id))); + console.log("Got the fresh objects!", osmObjects, "pending: ", pending) + try { + const changes: { + newObjects: OsmObject[], + modifiedObjects: OsmObject[] + deletedObjects: OsmObject[] + } = self.CreateChangesetObjects(pending, osmObjects) + if (changes.newObjects.length + changes.deletedObjects.length + changes.modifiedObjects.length === 0) { + console.log("No changes to be made") self.pendingChanges.setData([]) self.isUploading.setData(false) } - return true; - }); + await State.state.osmConnection.UploadChangeset( + State.state.layoutToUse.data, + State.state.allElements, + (csId) => Changes.createChangesetFor(csId, changes), + ) + + console.log("Upload successfull!") + this.pendingChanges.setData([]); + this.isUploading.setData(false) + + } catch (e) { + console.error("Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", e) + self.pendingChanges.setData([]) + self.isUploading.setData(false) + } } @@ -311,4 +310,8 @@ export class Changes { return result } + + public registerIdRewrites(mappings: Map): void { + + } } \ No newline at end of file diff --git a/Logic/Osm/ChangesetHandler.ts b/Logic/Osm/ChangesetHandler.ts index dcf0d135a..aae51d4ee 100644 --- a/Logic/Osm/ChangesetHandler.ts +++ b/Logic/Osm/ChangesetHandler.ts @@ -8,15 +8,23 @@ import Locale from "../../UI/i18n/Locale"; import Constants from "../../Models/Constants"; import {OsmObject} from "./OsmObject"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {Changes} from "./Changes"; export class ChangesetHandler { public readonly currentChangeset: UIEventSource; + private readonly allElements: ElementStorage; + private readonly changes: Changes; private readonly _dryRun: boolean; private readonly userDetails: UIEventSource; private readonly auth: any; - constructor(layoutName: string, dryRun: boolean, osmConnection: OsmConnection, auth) { + constructor(layoutName: string, dryRun: boolean, osmConnection: OsmConnection, + allElements: ElementStorage, + changes: Changes, + auth) { + this.allElements = allElements; + this.changes = changes; this._dryRun = dryRun; this.userDetails = osmConnection.userDetails; this.auth = auth; @@ -27,35 +35,55 @@ export class ChangesetHandler { } } - private static parseUploadChangesetResponse(response: XMLDocument, allElements: ElementStorage): void { + private handleIdRewrite(node: any, type: string): [string, string] { + const oldId = parseInt(node.attributes.old_id.value); + if (node.attributes.new_id === undefined) { + // We just removed this point! + const element =this. allElements.getEventSourceById("node/" + oldId); + element.data._deleted = "yes" + element.ping(); + return; + } + + const newId = parseInt(node.attributes.new_id.value); + const result: [string, string] = [type + "/" + oldId, type + "/" + newId] + if (!(oldId !== undefined && newId !== undefined && + !isNaN(oldId) && !isNaN(newId))) { + return undefined; + } + if (oldId == newId) { + return undefined; + } + console.log("Rewriting id: ", type + "/" + oldId, "-->", type + "/" + newId); + const element = this.allElements.getEventSourceById("node/" + oldId); + element.data.id = type + "/" + newId; + this.allElements.addElementById(type + "/" + newId, element); + this.allElements.ContainingFeatures.set(type + "/" + newId, this.allElements.ContainingFeatures.get(type + "/" + oldId)) + element.ping(); + return result; + } + + private parseUploadChangesetResponse(response: XMLDocument): void { const nodes = response.getElementsByTagName("node"); + const mappings = new Map() // @ts-ignore for (const node of nodes) { - const oldId = parseInt(node.attributes.old_id.value); - if (node.attributes.new_id === undefined) { - // We just removed this point! - const element = allElements.getEventSourceById("node/" + oldId); - element.data._deleted = "yes" - element.ping(); - continue; + const mapping = this.handleIdRewrite(node, "node") + if (mapping !== undefined) { + mappings.set(mapping[0], mapping[1]) } - - const newId = parseInt(node.attributes.new_id.value); - if (oldId !== undefined && newId !== undefined && - !isNaN(oldId) && !isNaN(newId)) { - if (oldId == newId) { - continue; - } - console.log("Rewriting id: ", oldId, "-->", newId); - const element = allElements.getEventSourceById("node/" + oldId); - element.data.id = "node/" + newId; - allElements.addElementById("node/" + newId, element); - element.ping(); - - } - - } + + const ways = response.getElementsByTagName("way"); + // @ts-ignore + for (const way of ways) { + const mapping = this.handleIdRewrite(way, "way") + if (mapping !== undefined) { + mappings.set(mapping[0], mapping[1]) + } + } + this.changes.registerIdRewrites(mappings) + } /** @@ -68,13 +96,9 @@ export class ChangesetHandler { * If 'dryrun' is specified, the changeset XML will be printed to console instead of being uploaded * */ - public UploadChangeset( + public async UploadChangeset( layout: LayoutConfig, - allElements: ElementStorage, - generateChangeXML: (csid: string) => string, - whenDone: (csId: string) => void, - onFail: () => void) { - + generateChangeXML: (csid: string) => string): Promise { if (this.userDetails.data.csCount == 0) { // The user became a contributor! this.userDetails.data.csCount = 1; @@ -84,46 +108,36 @@ export class ChangesetHandler { if (this._dryRun) { const changesetXML = generateChangeXML("123456"); console.log(changesetXML); - whenDone("123456") return; } - const self = this; - if (this.currentChangeset.data === undefined || this.currentChangeset.data === "") { // We have to open a new changeset - this.OpenChangeset(layout, (csId) => { + try { + const csId = await this.OpenChangeset(layout) this.currentChangeset.setData(csId); const changeset = generateChangeXML(csId); - console.log(changeset); - self.AddChange(csId, changeset, - allElements, - whenDone, - (e) => { - console.error("UPLOADING FAILED!", e) - onFail() - } - ) - }, { - onFail: onFail - }) + console.log("Current changeset is:", changeset); + await this.AddChange(csId, changeset) + } catch (e) { + console.error("Could not open/upload changeset due to ", e) + this.currentChangeset.setData("") + } } else { // There still exists an open changeset (or at least we hope so) const csId = this.currentChangeset.data; - self.AddChange( - csId, - generateChangeXML(csId), - allElements, - whenDone, - (e) => { - console.warn("Could not upload, changeset is probably closed: ", e); - // Mark the CS as closed... - this.currentChangeset.setData(""); - // ... and try again. As the cs is closed, no recursive loop can exist - self.UploadChangeset(layout, allElements, generateChangeXML, whenDone, onFail); - } - ) + try { + await this.AddChange( + csId, + generateChangeXML(csId)) + } catch (e) { + console.warn("Could not upload, changeset is probably closed: ", e); + // Mark the CS as closed... + this.currentChangeset.setData(""); + // ... and try again. As the cs is closed, no recursive loop can exist + await this.UploadChangeset(layout, generateChangeXML) + } } } @@ -143,6 +157,13 @@ export class ChangesetHandler { reason: string, allElements: ElementStorage, continuation: () => void) { + return this.DeleteElementAsync(object, layout, reason, allElements).then(continuation) + } + + public async DeleteElementAsync(object: OsmObject, + layout: LayoutConfig, + reason: string, + allElements: ElementStorage): Promise { function generateChangeXML(csId: string) { let [lat, lon] = object.centerpoint(); @@ -151,9 +172,7 @@ export class ChangesetHandler { changes += `<${object.type} id="${object.id}" version="${object.version}" changeset="${csId}" lat="${lat}" lon="${lon}" />`; changes += ""; - continuation() return changes; - } @@ -163,143 +182,122 @@ export class ChangesetHandler { return; } - const self = this; - this.OpenChangeset(layout, (csId: string) => { - - // The cs is open - let us actually upload! - const changes = generateChangeXML(csId) - - self.AddChange(csId, changes, allElements, (csId) => { - console.log("Successfully deleted ", object.id) - self.CloseChangeset(csId, continuation) - }, (csId) => { - alert("Deletion failed... Should not happend") - // FAILED - self.CloseChangeset(csId, continuation) - }) - }, { - isDeletionCS: true, - deletionReason: reason - } - ) + const csId = await this.OpenChangeset(layout, { + isDeletionCS: true, + deletionReason: reason + }) + // The cs is open - let us actually upload! + const changes = generateChangeXML(csId) + await this.AddChange(csId, changes) + await this.CloseChangeset(csId) } - private CloseChangeset(changesetId: string = undefined, continuation: (() => void) = () => { - }) { - if (changesetId === undefined) { - changesetId = this.currentChangeset.data; - } - if (changesetId === undefined) { - return; - } - console.log("closing changeset", changesetId); - this.currentChangeset.setData(""); - this.auth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/' + changesetId + '/close', - }, function (err, response) { - if (response == null) { - - console.log("err", err); + private async CloseChangeset(changesetId: string = undefined): Promise { + const self = this + return new Promise(function (resolve, reject) { + if (changesetId === undefined) { + changesetId = self.currentChangeset.data; } - console.log("Closed changeset ", changesetId) - - if (continuation !== undefined) { - continuation(); + if (changesetId === undefined) { + return; } - }); + console.log("closing changeset", changesetId); + self.currentChangeset.setData(""); + self.auth.xhr({ + method: 'PUT', + path: '/api/0.6/changeset/' + changesetId + '/close', + }, function (err, response) { + if (response == null) { + + console.log("err", err); + } + console.log("Closed changeset ", changesetId) + resolve() + }); + }) } private OpenChangeset( layout: LayoutConfig, - continuation: (changesetId: string) => void, options?: { isDeletionCS?: boolean, deletionReason?: string, - onFail?: () => void } - ) { - options = options ?? {} - options.isDeletionCS = options.isDeletionCS ?? false - const commentExtra = layout.changesetmessage !== undefined ? " - " + layout.changesetmessage : ""; - let comment = `Adding data with #MapComplete for theme #${layout.id}${commentExtra}` - if (options.isDeletionCS) { - comment = `Deleting a point with #MapComplete for theme #${layout.id}${commentExtra}` - if (options.deletionReason) { - comment += ": " + options.deletionReason; - } - } - - let path = window.location.pathname; - path = path.substr(1, path.lastIndexOf("/")); - const metadata = [ - ["created_by", `MapComplete ${Constants.vNumber}`], - ["comment", comment], - ["deletion", options.isDeletionCS ? "yes" : undefined], - ["theme", layout.id], - ["language", Locale.language.data], - ["host", window.location.host], - ["path", path], - ["source", State.state.currentGPSLocation.data !== undefined ? "survey" : undefined], - ["imagery", State.state.backgroundLayer.data.id], - ["theme-creator", layout.maintainer] - ] - .filter(kv => (kv[1] ?? "") !== "") - .map(kv => ``) - .join("\n") - - this.auth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/create', - options: {header: {'Content-Type': 'text/xml'}}, - content: [``, - metadata, - ``].join("") - }, function (err, response) { - if (response === undefined) { - console.log("err", err); - if (options.onFail) { - options.onFail() + ): Promise { + const self = this; + return new Promise(function (resolve, reject) { + options = options ?? {} + options.isDeletionCS = options.isDeletionCS ?? false + const commentExtra = layout.changesetmessage !== undefined ? " - " + layout.changesetmessage : ""; + let comment = `Adding data with #MapComplete for theme #${layout.id}${commentExtra}` + if (options.isDeletionCS) { + comment = `Deleting a point with #MapComplete for theme #${layout.id}${commentExtra}` + if (options.deletionReason) { + comment += ": " + options.deletionReason; } - return; - } else { - continuation(response); } - }); + + let path = window.location.pathname; + path = path.substr(1, path.lastIndexOf("/")); + const metadata = [ + ["created_by", `MapComplete ${Constants.vNumber}`], + ["comment", comment], + ["deletion", options.isDeletionCS ? "yes" : undefined], + ["theme", layout.id], + ["language", Locale.language.data], + ["host", window.location.host], + ["path", path], + ["source", State.state.currentGPSLocation.data !== undefined ? "survey" : undefined], + ["imagery", State.state.backgroundLayer.data.id], + ["theme-creator", layout.maintainer] + ] + .filter(kv => (kv[1] ?? "") !== "") + .map(kv => ``) + .join("\n") + + + self.auth.xhr({ + method: 'PUT', + path: '/api/0.6/changeset/create', + options: {header: {'Content-Type': 'text/xml'}}, + content: [``, + metadata, + ``].join("") + }, function (err, response) { + if (response === undefined) { + console.log("err", err); + reject(err) + } else { + resolve(response); + } + }); + }) + } /** * Upload a changesetXML - * @param changesetId - * @param changesetXML - * @param allElements - * @param continuation - * @param onFail - * @constructor - * @private */ private AddChange(changesetId: string, - changesetXML: string, - allElements: ElementStorage, - continuation: ((changesetId: string) => void), - onFail: ((changesetId: string, reason: string) => void) = undefined) { - this.auth.xhr({ - method: 'POST', - options: {header: {'Content-Type': 'text/xml'}}, - path: '/api/0.6/changeset/' + changesetId + '/upload', - content: changesetXML - }, function (err, response) { - if (response == null) { - console.log("err", err); - if (onFail) { - onFail(changesetId, err); + changesetXML: string): Promise { + const self = this; + return new Promise(function (resolve, reject) { + self.auth.xhr({ + method: 'POST', + options: {header: {'Content-Type': 'text/xml'}}, + path: '/api/0.6/changeset/' + changesetId + '/upload', + content: changesetXML + }, function (err, response) { + if (response == null) { + console.log("err", err); + reject(err); } - return; - } - ChangesetHandler.parseUploadChangesetResponse(response, allElements); - console.log("Uploaded changeset ", changesetId); - continuation(changesetId); - }); + self.parseUploadChangesetResponse(response); + console.log("Uploaded changeset ", changesetId); + resolve(changesetId); + }); + }) + } diff --git a/Logic/Osm/OsmConnection.ts b/Logic/Osm/OsmConnection.ts index 5a5e82612..5d955eb04 100644 --- a/Logic/Osm/OsmConnection.ts +++ b/Logic/Osm/OsmConnection.ts @@ -9,6 +9,7 @@ import Img from "../../UI/Base/Img"; import {Utils} from "../../Utils"; import {OsmObject} from "./OsmObject"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {Changes} from "./Changes"; export default class UserDetails { @@ -54,7 +55,7 @@ export class OsmConnection { private _onLoggedIn: ((userDetails: UserDetails) => void)[] = []; private readonly _iframeMode: Boolean | boolean; private readonly _singlePage: boolean; - private readonly _oauth_config: { + public readonly _oauth_config: { oauth_consumer_key: string, oauth_secret: string, url: string @@ -63,6 +64,8 @@ export class OsmConnection { constructor(dryRun: boolean, fakeUser: boolean, + allElements: ElementStorage, + changes: Changes, oauth_token: UIEventSource, // Used to keep multiple changesets open and to write to the correct changeset layoutName: string, @@ -101,7 +104,7 @@ export class OsmConnection { this.preferencesHandler = new OsmPreferences(this.auth, this); - this.changesetHandler = new ChangesetHandler(layoutName, dryRun, this, this.auth); + this.changesetHandler = new ChangesetHandler(layoutName, dryRun, this, allElements, changes, this.auth); if (oauth_token.data !== undefined) { console.log(oauth_token.data) const self = this; @@ -124,10 +127,8 @@ export class OsmConnection { public UploadChangeset( layout: LayoutConfig, allElements: ElementStorage, - generateChangeXML: (csid: string) => string, - whenDone: (csId: string) => void, - onFail: () => {}) { - this.changesetHandler.UploadChangeset(layout, allElements, generateChangeXML, whenDone, onFail); + generateChangeXML: (csid: string) => string): Promise { + return this.changesetHandler.UploadChangeset(layout, generateChangeXML); } public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource { diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index 8027a18d9..d95e0fa49 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -157,23 +157,6 @@ export abstract class OsmObject { const elements: any[] = data.elements; return OsmObject.ParseObjects(elements); } - - public static DownloadAll(neededIds, forceRefresh = true): UIEventSource { - // local function which downloads all the objects one by one - // this is one big loop, running one download, then rerunning the entire function - - const allSources: UIEventSource [] = neededIds.map(id => OsmObject.DownloadObject(id, forceRefresh)) - const allCompleted = new UIEventSource(undefined).map(_ => { - return !allSources.some(uiEventSource => uiEventSource.data === undefined) - }, allSources) - return allCompleted.map(completed => { - if (completed) { - return allSources.map(src => src.data) - } - return undefined - }); - } - protected static isPolygon(tags: any): boolean { for (const tagsKey in tags) { if (!tags.hasOwnProperty(tagsKey)) { diff --git a/State.ts b/State.ts index 105e7ef05..04b9728b3 100644 --- a/State.ts +++ b/State.ts @@ -32,11 +32,11 @@ export default class State { /** The mapping from id -> UIEventSource */ - public allElements: ElementStorage; + public allElements: ElementStorage = new ElementStorage(); /** THe change handler */ - public changes: Changes; + public changes: Changes = new Changes(); /** The leaflet instance of the big basemap */ @@ -155,7 +155,6 @@ export default class State { constructor(layoutToUse: LayoutConfig) { const self = this; - this.layoutToUse.setData(layoutToUse); // -- Location control initialization @@ -376,6 +375,8 @@ export default class State { this.osmConnection = new OsmConnection( this.featureSwitchIsTesting.data, this.featureSwitchFakeUser.data, + this.allElements, + this.changes, QueryParameters.GetQueryParameter( "oauth_token", undefined, @@ -387,9 +388,7 @@ export default class State { this.featureSwitchApiURL.data ); - this.allElements = new ElementStorage(); - this.changes = new Changes(); - + new ChangeToElementsActor(this.changes, this.allElements) new PendingChangesUploader(this.changes, this.selectedElement); diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 57f651e9a..fb2c93a0a 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -57,7 +57,6 @@ export default class SimpleAddUI extends Toggle { function createNewPoint(tags: any[], location: { lat: number, lon: number }, snapOntoWay?: OsmWay) { - console.trace("Creating a new point") const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {snapOnto: snapOntoWay}) State.state.changes.applyAction(newElementAction) selectedPreset.setData(undefined) diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 7de4c3a36..d4708deac 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -54,13 +54,16 @@ }, "tagRenderings": [ { + "id": "uk_addresses_explanation", "render": "There probably is an address here" }, { + "id": "uk_addresses_embedding_outline", "render": "An outline embedding this point with an address already exists in OpenStreetMap.
This
object has address {_embedding_object:addr:street} {_embedding_object:addr:housenumber}", "condition": "_embedding_object:id~*" }, { + "id": "uk_addresses_import_button", "render": "{import_button(ref:inspireid=$inspireid, Add this address, ./assets/themes/uk_addresses/housenumber_add.svg)}" }, "all_tags" @@ -109,11 +112,13 @@ }, "tagRenderings": [ { + "id": "uk_addresses_explanation_osm", "render": { "en": "This address is saved in OpenStreetMap" } }, { + "id": "uk_addresses_housenumber", "render": { "en": "The housenumber is {addr:housenumber}" }, @@ -137,6 +142,7 @@ ] }, { + "id": "uk_addresses_street", "render": { "en": "This address is in street {addr:street}" }, diff --git a/langs/en.json b/langs/en.json index fdfc46fe5..9687f11a9 100644 --- a/langs/en.json +++ b/langs/en.json @@ -13,7 +13,8 @@ "uploadDone": "Your picture has been added. Thanks for helping out!", "dontDelete": "Cancel", "doDelete": "Remove image", - "isDeleted": "Deleted" + "isDeleted": "Deleted", + "hasBeenImported": "This feature has been imported" }, "centerMessage": { "loadingData": "Loading data…", diff --git a/langs/themes/en.json b/langs/themes/en.json index 8fed7dd34..e2b1d6591 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1326,10 +1326,10 @@ "description": "Addresses", "name": "Known addresses in OSM", "tagRenderings": { - "0": { + "uk_addresses_explanation_osm": { "render": "This address is saved in OpenStreetMap" }, - "1": { + "uk_addresses_housenumber": { "mappings": { "0": { "then": "This building has no house number" @@ -1338,7 +1338,7 @@ "question": "What is the number of this house?", "render": "The housenumber is {addr:housenumber}" }, - "2": { + "uk_addresses_street": { "question": "What street is this address located in?", "render": "This address is in street {addr:street}" } From 215aebce1983ebd2f027327547678f9ba5fd68fc Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 27 Sep 2021 14:45:48 +0200 Subject: [PATCH 036/110] More work on clustering, more or less finished --- InitUiElements.ts | 74 ++++-- Logic/FeatureSource/FeaturePipeline.ts | 32 ++- Models/ThemeConfig/Json/LayoutConfigJson.ts | 4 +- Models/ThemeConfig/LayoutConfig.ts | 9 +- UI/ShowDataLayer/PerTileCountAggregator.ts | 258 ++++++++++++-------- UI/ShowDataLayer/ShowDataLayer.ts | 77 +++--- assets/layers/tree_node/tree_node.json | 2 +- assets/themes/trees/trees.json | 5 +- 8 files changed, 276 insertions(+), 185 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index 0bc6506d1..c3e57766a 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -39,7 +39,8 @@ import Combine from "./UI/Base/Combine"; import {SubtleButton} from "./UI/Base/SubtleButton"; import ShowTileInfo from "./UI/ShowDataLayer/ShowTileInfo"; import {Tiles} from "./Models/TileRange"; -import PerTileCountAggregator from "./UI/ShowDataLayer/PerTileCountAggregator"; +import {TileHierarchyAggregator} from "./UI/ShowDataLayer/PerTileCountAggregator"; +import {BBox} from "./Logic/GeoOperations"; export class InitUiElements { static InitAll( @@ -430,48 +431,67 @@ export class InitUiElements { return flayers; }); - const clusterCounter = new PerTileCountAggregator(State.state.locationControl.map(l => { - const z = l.zoom + 1 - if(z < 7){ - return 7 - } - return z - })) - const clusterShow = Math.min(...State.state.layoutToUse.data.layers.map(layer => layer.minzoomVisible ?? layer.minzoom)) + const layers = State.state.layoutToUse.data.layers + const clusterShow = Math.min(...layers.map(layer => layer.minzoom)) + + + const clusterCounter = TileHierarchyAggregator.createHierarchy() new ShowDataLayer({ - features: clusterCounter, + features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.data.clustering.minNeededElements), leafletMap: State.state.leafletMap, layerToShow: ShowTileInfo.styling, - doShowLayer: State.state.locationControl.map(l => l.zoom < clusterShow) + doShowLayer: layers.length === 1 ? undefined : State.state.locationControl.map(l => l.zoom < clusterShow) }) + State.state.featurePipeline = new FeaturePipeline( source => { + + clusterCounter.addTile(source) + const clustering = State.state.layoutToUse.data.clustering const doShowFeatures = source.features.map( f => { const z = State.state.locationControl.data.zoom - if(z >= clustering.maxZoom){ - return true - } - if(z < source.layer.layerDef.minzoom){ + + if (z < source.layer.layerDef.minzoom) { + // Layer is always hidden for this zoom level return false; } - if(f.length > clustering.minNeededElements){ - console.log("Activating clustering for tile ", Tiles.tile_from_index(source.tileIndex)," as it has ", f.length, "features (clustering starts at)", clustering.minNeededElements) + + if (z >= clustering.maxZoom) { + return true + } + + if (f.length > clustering.minNeededElements) { + // This tile alone has too much features return false } - + + let [tileZ, tileX, tileY] = Tiles.tile_from_index(source.tileIndex); + if (tileZ >= z) { + + while (tileZ > z) { + tileZ-- + tileX = Math.floor(tileX / 2) + tileY = Math.floor(tileY / 2) + } + + if (clusterCounter.getTile(Tiles.tile_index(tileZ, tileX, tileY))?.totalValue > clustering.minNeededElements) { + return false + } + } + + + const bounds = State.state.currentBounds.data + const tilebbox = BBox.fromTileIndex(source.tileIndex) + if (!tilebbox.overlapsWith(bounds)) { + return false + } + return true - }, [State.state.locationControl] + }, [State.state.locationControl, State.state.currentBounds] ) - clusterCounter.addTile(source, doShowFeatures.map(b => !b)) - - /* - new ShowTileInfo({source: source, - leafletMap: State.state.leafletMap, - layer: source.layer.layerDef, - doShowLayer: doShowFeatures.map(b => !b) - })*/ + new ShowDataLayer( { features: source, diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 1e033dad7..a24d6f516 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -134,6 +134,8 @@ export default class FeaturePipeline implements FeatureSourceState { layer: source.layer, minZoomLevel: 14, dontEnforceMinZoom: true, + maxFeatureCount: state.layoutToUse.data.clustering.minNeededElements, + maxZoomLevel: state.layoutToUse.data.clustering.maxZoom, registerTile: (tile) => { // We save the tile data for the given layer to local storage new SaveTileToLocalStorageActor(tile, tile.tileIndex) @@ -171,20 +173,26 @@ export default class FeaturePipeline implements FeatureSourceState { private applyMetaTags(src: FeatureSourceForLayer){ const self = this - console.log("Applying metatagging onto ", src.name) - MetaTagging.addMetatags( - src.features.data, - { - memberships: this.relationTracker, - getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox) + console.debug("Applying metatagging onto ", src.name) + window.setTimeout( + () => { + MetaTagging.addMetatags( + src.features.data, + { + memberships: this.relationTracker, + getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox) + }, + src.layer.layerDef, + { + includeDates: true, + // We assume that the non-dated metatags are already set by the cache generator + includeNonDates: !src.layer.layerDef.source.isOsmCacheLayer + } + ) }, - src.layer.layerDef, - { - includeDates: true, - // We assume that the non-dated metatags are already set by the cache generator - includeNonDates: !src.layer.layerDef.source.isOsmCacheLayer - } + 15 ) + } private updateAllMetaTagging() { diff --git a/Models/ThemeConfig/Json/LayoutConfigJson.ts b/Models/ThemeConfig/Json/LayoutConfigJson.ts index 856ab2736..887649fc4 100644 --- a/Models/ThemeConfig/Json/LayoutConfigJson.ts +++ b/Models/ThemeConfig/Json/LayoutConfigJson.ts @@ -228,8 +228,8 @@ export interface LayoutConfigJson { */ maxZoom?: number, /** - * The number of elements that should be showed (in total) before clustering starts to happen. - * If clustering is defined, defaults to 0 + * The number of elements per tile needed to start clustering + * If clustering is defined, defaults to 25 */ minNeededElements?: number }, diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index b632d8a29..9e6325039 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -129,17 +129,12 @@ export default class LayoutConfig { this.clustering = { maxZoom: 16, - minNeededElements: 500 + minNeededElements: 25 }; if (json.clustering) { this.clustering = { maxZoom: json.clustering.maxZoom ?? 18, - minNeededElements: json.clustering.minNeededElements ?? 1 - } - for (const layer of this.layers) { - if (layer.wayHandling !== LayerConfig.WAYHANDLING_CENTER_ONLY) { - console.debug("WARNING: In order to allow clustering, every layer must be set to CENTER_ONLY. Layer", layer.id, "does not respect this for layout", this.id); - } + minNeededElements: json.clustering.minNeededElements ?? 25 } } diff --git a/UI/ShowDataLayer/PerTileCountAggregator.ts b/UI/ShowDataLayer/PerTileCountAggregator.ts index 82e247f74..e4085f2cd 100644 --- a/UI/ShowDataLayer/PerTileCountAggregator.ts +++ b/UI/ShowDataLayer/PerTileCountAggregator.ts @@ -3,120 +3,178 @@ import {BBox} from "../../Logic/GeoOperations"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; import {UIEventSource} from "../../Logic/UIEventSource"; import {Tiles} from "../../Models/TileRange"; +import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +export class TileHierarchyAggregator implements FeatureSource { + private _parent: TileHierarchyAggregator; + private _root: TileHierarchyAggregator; + private _z: number; + private _x: number; + private _y: number; + private _tileIndex: number + private _counter: SingleTileCounter -/** - * A feature source containing meta features. - * It will contain exactly one point for every tile of the specified (dynamic) zoom level - */ -export default class PerTileCountAggregator implements FeatureSource { - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); - public readonly name: string = "PerTileCountAggregator" + private _subtiles: [TileHierarchyAggregator, TileHierarchyAggregator, TileHierarchyAggregator, TileHierarchyAggregator] = [undefined, undefined, undefined, undefined] + public totalValue: number = 0 - private readonly perTile: Map = new Map() - private readonly _requestedZoomLevel: UIEventSource; + private static readonly empty = [] + public readonly features = new UIEventSource<{ feature: any, freshness: Date }[]>(TileHierarchyAggregator.empty) + public readonly name; - constructor(requestedZoomLevel: UIEventSource) { - this._requestedZoomLevel = requestedZoomLevel; - const self = this; - this._requestedZoomLevel.addCallbackAndRun(_ => self.update()) + private readonly featuresStatic = [] + private readonly featureProperties: { count: number, tileId: number }; + + private constructor(parent: TileHierarchyAggregator, z: number, x: number, y: number) { + this._parent = parent; + this._root = parent?._root ?? this + this._z = z; + this._x = x; + this._y = y; + this._tileIndex = Tiles.tile_index(z, x, y) + this.name = "Count(" + this._tileIndex + ")" + + const totals = { + tileId: this._tileIndex, + count: 0 + } + this.featureProperties = totals + + const now = new Date() + const feature = { + "type": "Feature", + "properties": totals, + "geometry": { + "type": "Point", + "coordinates": Tiles.centerPointOf(z, x, y) + } + } + this.featuresStatic.push({feature: feature, freshness: now}) + + const bbox = BBox.fromTile(z, x, y) + const box = { + "type": "Feature", + "properties": totals, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [bbox.minLon, bbox.minLat], + [bbox.minLon, bbox.maxLat], + [bbox.maxLon, bbox.maxLat], + [bbox.maxLon, bbox.minLat], + [bbox.minLon, bbox.minLat] + ] + ] + } + } + this.featuresStatic.push({feature: box, freshness: now}) + } + + public getTile(tileIndex): TileHierarchyAggregator { + if (tileIndex === this._tileIndex) { + return this; + } + let [tileZ, tileX, tileY] = Tiles.tile_from_index(tileIndex) + while (tileZ - 1 > this._z) { + tileX = Math.floor(tileX / 2) + tileY = Math.floor(tileY / 2) + tileZ-- + } + const xDiff = tileX - (2 * this._x) + const yDiff = tileY - (2 * this._y) + const subtileIndex = yDiff * 2 + xDiff; + return this._subtiles[subtileIndex]?.getTile(tileIndex) } private update() { - const now = new Date() - const allCountsAsFeatures : {feature: any, freshness: Date}[] = [] - const aggregate = this.calculatePerTileCount() - aggregate.forEach((totalsPerLayer, tileIndex) => { - const totals = {} - let totalCount = 0 - totalsPerLayer.forEach((total, layerId) => { - totals[layerId] = total - totalCount += total - }) - totals["tileId"] = tileIndex - totals["count"] = totalCount - const feature = { - "type": "Feature", - "properties": totals, - "geometry": { - "type": "Point", - "coordinates": Tiles.centerPointOf(...Tiles.tile_from_index(tileIndex)) - } - } - allCountsAsFeatures.push({feature: feature, freshness: now}) - - const bbox= BBox.fromTileIndex(tileIndex) - const box = { - "type": "Feature", - "properties":totals, - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [bbox.minLon, bbox.minLat], - [bbox.minLon, bbox.maxLat], - [bbox.maxLon, bbox.maxLat], - [bbox.maxLon, bbox.minLat], - [bbox.minLon, bbox.minLat] - ] - ] - } - } - allCountsAsFeatures.push({feature:box, freshness: now}) + const newMap = new Map() + let total = 0 + this?._counter?.countsPerLayer?.data?.forEach((count, layerId) => { + newMap.set(layerId, count) + total += count }) - this.features.setData(allCountsAsFeatures) - } - /** - * Calculates an aggregate count per tile and per subtile - * @private - */ - private calculatePerTileCount() { - const perTileCount = new Map>() - const targetZoom = this._requestedZoomLevel.data; - // We only search for tiles of the same zoomlevel or a higher zoomlevel, which is embedded - for (const singleTileCounter of Array.from(this.perTile.values())) { - - let tileZ = singleTileCounter.z - let tileX = singleTileCounter.x - let tileY = singleTileCounter.y - if (tileZ < targetZoom) { + for (const tile of this._subtiles) { + if (tile === undefined) { continue; } + total += tile.totalValue + } + this.totalValue = total + this._parent?.update() + + if (total === 0) { + this.features.setData(TileHierarchyAggregator.empty) + } else { + this.featureProperties.count = total; + this.features.data = this.featuresStatic + this.features.ping() + } + } - while (tileZ > targetZoom) { + public addTile(source: FeatureSourceForLayer & Tiled) { + const self = this; + if (source.tileIndex === this._tileIndex) { + if (this._counter === undefined) { + this._counter = new SingleTileCounter(this._tileIndex) + this._counter.countsPerLayer.addCallbackAndRun(_ => self.update()) + } + this._counter.addTileCount(source) + } else { + + // We have to give it to one of the subtiles + let [tileZ, tileX, tileY] = Tiles.tile_from_index(source.tileIndex) + while (tileZ - 1 > this._z) { tileX = Math.floor(tileX / 2) tileY = Math.floor(tileY / 2) tileZ-- } - const tileI = Tiles.tile_index(tileZ, tileX, tileY) - let counts = perTileCount.get(tileI) - if (counts === undefined) { - counts = new Map() - perTileCount.set(tileI, counts) + const xDiff = tileX - (2 * this._x) + const yDiff = tileY - (2 * this._y) + + const subtileIndex = yDiff * 2 + xDiff; + if (this._subtiles[subtileIndex] === undefined) { + this._subtiles[subtileIndex] = new TileHierarchyAggregator(this, tileZ, tileX, tileY) } - singleTileCounter.countsPerLayer.data.forEach((count, layerId) => { - if (counts.has(layerId)) { - counts.set(layerId, count + counts.get(layerId)) - } else { - counts.set(layerId, count) - } + this._subtiles[subtileIndex].addTile(source) + } + + } + + public static createHierarchy() { + return new TileHierarchyAggregator(undefined, 0, 0, 0) + } + + + private visitSubTiles(f : (aggr: TileHierarchyAggregator) => boolean){ + const visitFurther = f(this) + if(visitFurther){ + this._subtiles.forEach(tile => tile?.visitSubTiles(f)) + } + } + + getCountsForZoom(locationControl: UIEventSource<{ zoom : number }>, cutoff: number) : FeatureSource{ + const self = this + return new StaticFeatureSource( + locationControl.map(loc => { + const features = [] + const targetZoom = loc.zoom + self.visitSubTiles(aggr => { + if(aggr.totalValue < cutoff) { + return false + } + if(aggr._z === targetZoom){ + features.push(...aggr.features.data) + return false + } + return aggr._z <= targetZoom; + + }) + + return features }) - } - return perTileCount; + , true); } - - public addTile(tile: FeatureSourceForLayer & Tiled, shouldBeCounted: UIEventSource) { - let counter = this.perTile.get(tile.tileIndex) - if (counter === undefined) { - counter = new SingleTileCounter(tile.tileIndex) - this.perTile.set(tile.tileIndex, counter) - // We do **NOT** add a callback on the perTile index, even though we could! It'll update just fine without it - } - counter.addTileCount(tile, shouldBeCounted) - } - - } /** @@ -131,6 +189,7 @@ class SingleTileCounter implements Tiled { public readonly x: number public readonly y: number + constructor(tileIndex: number) { this.tileIndex = tileIndex this.bbox = BBox.fromTileIndex(tileIndex) @@ -140,17 +199,16 @@ class SingleTileCounter implements Tiled { this.y = y } - public addTileCount(source: FeatureSourceForLayer, shouldBeCounted: UIEventSource) { + public addTileCount(source: FeatureSourceForLayer) { const layer = source.layer.layerDef this.registeredLayers.set(layer.id, layer) const self = this + source.features.map(f => { - /*if (!shouldBeCounted.data) { - return; - }*/ self.countsPerLayer.data.set(layer.id, f.length) self.countsPerLayer.ping() - }, [shouldBeCounted]) + }) + } } \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 278ea3597..0d02bcd4e 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -17,6 +17,7 @@ export default class ShowDataLayer { // Used to generate a fresh ID when needed private _cleanCount = 0; private geoLayer = undefined; + private isDirty = false; /** * If the selected element triggers, this is used to lookup the correct layer and to open the popup @@ -37,9 +38,30 @@ export default class ShowDataLayer { this._layerToShow = options.layerToShow; const self = this; + options.leafletMap.addCallbackAndRunD(_ => { + self.update(options) + } + ); + features.addCallback(_ => self.update(options)); - options.leafletMap.addCallback(_ => self.update(options)); - this.update(options); + options.doShowLayer?.addCallbackAndRun(doShow => { + const mp = options.leafletMap.data; + if (mp == undefined) { + return; + } + if (doShow) { + if (self.isDirty) { + self.update(options) + } else { + mp.addLayer(this.geoLayer) + } + } else { + if(this.geoLayer !== undefined){ + mp.removeLayer(this.geoLayer) + } + } + + }) State.state.selectedElement.addCallbackAndRunD(selected => { if (self._leafletMap.data === undefined) { @@ -68,26 +90,16 @@ export default class ShowDataLayer { } }) - options.doShowLayer?.addCallbackAndRun(doShow => { - const mp = options.leafletMap.data; - if (this.geoLayer == undefined || mp == undefined) { - return; - } - if (doShow) { - mp.addLayer(this.geoLayer) - } else { - mp.removeLayer(this.geoLayer) - } - - - }) - } - private update(options) { + private update(options: ShowDataLayerOptions) { if (this._features.data === undefined) { return; } + this.isDirty = true; + if (options?.doShowLayer?.data === false) { + return; + } const mp = options.leafletMap.data; if (mp === undefined) { @@ -99,7 +111,18 @@ export default class ShowDataLayer { mp.removeLayer(this.geoLayer); } - this.geoLayer = this.CreateGeojsonLayer() + const self = this; + const data = { + type: "FeatureCollection", + features: [] + } + // @ts-ignore + this.geoLayer = L.geoJSON(data, { + style: feature => self.createStyleFor(feature), + pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng), + onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer) + }); + const allFeats = this._features.data; for (const feat of allFeats) { if (feat === undefined) { @@ -123,6 +146,7 @@ export default class ShowDataLayer { if (options.doShowLayer?.data ?? true) { mp.addLayer(this.geoLayer) } + this.isDirty = false; } @@ -143,7 +167,7 @@ export default class ShowDataLayer { return; } - const tagSource = feature.properties.id === undefined ? new UIEventSource(feature.properties) : + const tagSource = feature.properties.id === undefined ? new UIEventSource(feature.properties) : State.state.allElements.getEventSourceById(feature.properties.id) const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) const style = layer.GenerateLeafletStyle(tagSource, clickable); @@ -218,19 +242,4 @@ export default class ShowDataLayer { } - private CreateGeojsonLayer(): L.Layer { - const self = this; - const data = { - type: "FeatureCollection", - features: [] - } - // @ts-ignore - return L.geoJSON(data, { - style: feature => self.createStyleFor(feature), - pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng), - onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer) - }); - - } - } \ No newline at end of file diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree_node/tree_node.json index bba09af12..59c1d03c1 100644 --- a/assets/layers/tree_node/tree_node.json +++ b/assets/layers/tree_node/tree_node.json @@ -7,7 +7,7 @@ "ru": "Дерево", "fr": "Arbre" }, - "minzoom": 14, + "minzoom": 16, "source": { "osmTags": { "and": [ diff --git a/assets/themes/trees/trees.json b/assets/themes/trees/trees.json index 52961293a..c609a43f1 100644 --- a/assets/themes/trees/trees.json +++ b/assets/themes/trees/trees.json @@ -45,10 +45,11 @@ "startLat": 50.642, "startLon": 4.482, "startZoom": 8, - "widenFactor": 1.5, + "widenFactor": 1.01, "socialImage": "./assets/themes/trees/logo.svg", "clustering": { - "maxZoom": 18 + "maxZoom": 19, + "minNeededElements": 25 }, "layers": [ "tree_node" From 38037014b08cc0675ffa491606bcbdafb641d891 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 27 Sep 2021 15:38:12 +0200 Subject: [PATCH 037/110] Fix bug in bounds calculation for negative lats/lons --- InitUiElements.ts | 7 ++--- Logic/GeoOperations.ts | 27 ++++++++++++++--- Logic/SimpleMetaTagger.ts | 3 ++ UI/Base/Minimap.ts | 4 ++- test.ts | 61 ++++++++++++++------------------------ test/GeoOperations.spec.ts | 14 +++++++-- 6 files changed, 65 insertions(+), 51 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index c3e57766a..19f1e3340 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -41,6 +41,7 @@ import ShowTileInfo from "./UI/ShowDataLayer/ShowTileInfo"; import {Tiles} from "./Models/TileRange"; import {TileHierarchyAggregator} from "./UI/ShowDataLayer/PerTileCountAggregator"; import {BBox} from "./Logic/GeoOperations"; +import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource"; export class InitUiElements { static InitAll( @@ -421,9 +422,6 @@ export class InitUiElements { const flayer = { isDisplayed: isDisplayed, layerDef: layer, - isSufficientlyZoomed: state.locationControl.map(l => { - return l.zoom >= (layer.minzoomVisible ?? layer.minzoom) - }), appliedFilters: new UIEventSource(undefined), }; flayers.push(flayer); @@ -491,7 +489,7 @@ export class InitUiElements { return true }, [State.state.locationControl, State.state.currentBounds] ) - + new ShowDataLayer( { features: source, @@ -502,7 +500,6 @@ export class InitUiElements { ); }, state ); - } private static setupAllLayerElements() { diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index dd806ded4..cfef09e5d 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -388,10 +388,10 @@ export class BBox { static global: BBox = new BBox([[-180, -90], [180, 90]]); constructor(coordinates) { - this.maxLat = Number.MIN_VALUE; - this.maxLon = Number.MIN_VALUE; - this.minLat = Number.MAX_VALUE; - this.minLon = Number.MAX_VALUE; + this.maxLat = -90; + this.maxLon = -180; + this.minLat = 90; + this.minLon = 180; for (const coordinate of coordinates) { @@ -491,4 +491,23 @@ export class BBox { toLeaflet() { return [[this.minLat, this.minLon], [this.maxLat, this.maxLon]] } + + asGeoJson(properties: any) : any{ + return { + type:"Feature", + properties: properties, + geometry:{ + type:"Polygon", + coordinates:[[ + + [this.minLon, this.minLat], + [this.maxLon, this.minLat], + [this.maxLon, this.maxLat], + [this.minLon, this.maxLat], + [this.minLon, this.minLat], + + ]] + } + } + } } \ No newline at end of file diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index b74f0f542..a938e9c72 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -97,6 +97,9 @@ export default class SimpleMetaTagger { continue; } for (const unit of units) { + if(unit === undefined){ + continue + } if (unit.appliesToKeys === undefined) { console.error("The unit ", unit, "has no appliesToKey defined") continue diff --git a/UI/Base/Minimap.ts b/UI/Base/Minimap.ts index 963d39db1..e276c8281 100644 --- a/UI/Base/Minimap.ts +++ b/UI/Base/Minimap.ts @@ -30,6 +30,8 @@ export default class Minimap { /** * Construct a minimap */ - public static createMiniMap: (options: MinimapOptions) => (BaseUIElement & MinimapObj) + public static createMiniMap: (options: MinimapOptions) => (BaseUIElement & MinimapObj) = (_) => { + throw "CreateMinimap hasn't been initialized yet. Please call MinimapImplementation.initialize()" + } } \ No newline at end of file diff --git a/test.ts b/test.ts index 86a1c8f6e..c70e45da5 100644 --- a/test.ts +++ b/test.ts @@ -5,46 +5,29 @@ import MinimapImplementation from "./UI/Base/MinimapImplementation"; import {UIEventSource} from "./Logic/UIEventSource"; import FilteredLayer from "./Models/FilteredLayer"; import {And} from "./Logic/Tags/And"; +import ShowDataLayer from "./UI/ShowDataLayer/ShowDataLayer"; +import ShowTileInfo from "./UI/ShowDataLayer/ShowTileInfo"; +import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource"; +import {BBox} from "./Logic/GeoOperations"; +import Minimap from "./UI/Base/Minimap"; -const layout = AllKnownLayouts.allKnownLayouts.get("cyclestreets") -State.state = new State(layout) +State.state = new State(undefined) + +const leafletMap = new UIEventSource(undefined) MinimapImplementation.initialize() -const feature = { - "type": "Feature", - "properties": { - id: "way/1234", - "highway":"residential", - "cyclestreet":"yes" - }, - "geometry": { - "type": "LineString", - "coordinates": [ - [ - 3.2207107543945312, - 51.21978729870313 - ], - [ - 3.2198524475097656, - 51.21899435057332 - ], - [ - 3.2155394554138184, - 51.21617188199714 - ] - ] - } -} +Minimap.createMiniMap({ + leafletMap: leafletMap, +}).SetStyle("height: 600px; width: 600px") + .AttachTo("maindiv") -State.state.allElements.addOrGetElement(feature) -State.state.filteredLayers = new UIEventSource( - layout.layers.map( l => ({ - layerDef :l, - appliedFilters: new UIEventSource(undefined), - isDisplayed: new UIEventSource(undefined) - })) -) +const bbox = BBox.fromTile(16,32754,21785).asGeoJson({ + count: 42, + tileId: 42 +}) -const splitroad = new SplitRoadWizard("way/1234") - splitroad.AttachTo("maindiv") - -splitroad.dialogIsOpened.setData(true) +console.log(bbox) +new ShowDataLayer({ + layerToShow: ShowTileInfo.styling, + leafletMap: leafletMap, + features: new StaticFeatureSource([ bbox], false) +}) \ No newline at end of file diff --git a/test/GeoOperations.spec.ts b/test/GeoOperations.spec.ts index 7a556b20d..49447afd5 100644 --- a/test/GeoOperations.spec.ts +++ b/test/GeoOperations.spec.ts @@ -1,7 +1,8 @@ import {Utils} from "../Utils"; import * as Assert from "assert"; import T from "./TestHelper"; -import {GeoOperations} from "../Logic/GeoOperations"; +import {BBox, GeoOperations} from "../Logic/GeoOperations"; +import {equal} from "assert"; Utils.runningFromConsole = true; @@ -176,7 +177,16 @@ export default class GeoOperationsSpec extends T { const overlap = GeoOperations.calculateOverlap(point, [GeoOperationsSpec.polygon]); Assert.equal(1, overlap.length) - }] + }], + ["bbox bounds test", + () => { + const bbox = BBox.fromTile(16, 32754, 21785) + equal(-0.0714111328125, bbox.minLon) + equal(-0.076904296875, bbox.maxLon) + equal(51.53266860674158, bbox.minLat) + equal(51.5292513551899, bbox.maxLat) + } + ] ] ) From 0a9e7c0b36ce7609a9fd7eec5ab64f24084e5b0c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 27 Sep 2021 18:35:32 +0200 Subject: [PATCH 038/110] Fix bbox bug, add ids to filters, add filter state to the URL --- InitUiElements.ts | 37 +++++++-- .../Sources/FilteringFeatureSource.ts | 18 ++-- Logic/MetaTagging.ts | 1 + Models/Constants.ts | 2 +- Models/FilteredLayer.ts | 3 +- Models/ThemeConfig/FilterConfig.ts | 12 ++- Models/ThemeConfig/Json/FilterConfigJson.ts | 4 + UI/BigComponents/FilterView.ts | 83 +++++++++++++------ UI/BigComponents/SimpleAddUI.ts | 4 +- assets/layers/birdhide/birdhide.json | 2 + assets/layers/cafe_pub/cafe_pub.json | 1 + .../charging_station/charging_station.json | 3 + .../charging_station.protojson | 2 + assets/layers/charging_station/csvToJson.ts | 1 + assets/layers/food/food.json | 4 + .../layers/nature_reserve/nature_reserve.json | 2 + .../public_bookcase/public_bookcase.json | 3 + assets/layers/toilet/toilet.json | 3 + .../themes/cycle_highways/cycle_highways.json | 6 ++ .../housenumber_unknown_small.svg | 60 ++++++++++++++ assets/themes/uk_addresses/license_info.json | 8 ++ assets/themes/uk_addresses/uk_addresses.json | 40 ++++++--- test/GeoOperations.spec.ts | 8 +- 23 files changed, 248 insertions(+), 59 deletions(-) create mode 100644 assets/themes/uk_addresses/housenumber_unknown_small.svg diff --git a/InitUiElements.ts b/InitUiElements.ts index 19f1e3340..f3593556f 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -42,6 +42,8 @@ import {Tiles} from "./Models/TileRange"; import {TileHierarchyAggregator} from "./UI/ShowDataLayer/PerTileCountAggregator"; import {BBox} from "./Logic/GeoOperations"; import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource"; +import FilterConfig from "./Models/ThemeConfig/FilterConfig"; +import FilteredLayer from "./Models/FilteredLayer"; export class InitUiElements { static InitAll( @@ -406,8 +408,10 @@ export class InitUiElements { private static InitLayers(): void { const state = State.state; + const empty = [] + state.filteredLayers = state.layoutToUse.map((layoutToUse) => { - const flayers = []; + const flayers: FilteredLayer[] = []; for (const layer of layoutToUse.layers) { const isDisplayed = QueryParameters.GetQueryParameter( @@ -422,30 +426,47 @@ export class InitUiElements { const flayer = { isDisplayed: isDisplayed, layerDef: layer, - appliedFilters: new UIEventSource(undefined), + appliedFilters: new UIEventSource<{ filter: FilterConfig, selected: number }[]>([]), }; + + if (layer.filters.length > 0) { + const filtersPerName = new Map() + layer.filters.forEach(f => filtersPerName.set(f.id, f)) + const qp = QueryParameters.GetQueryParameter("filter-" + layer.id, "","Filtering state for a layer") + flayer.appliedFilters.map(filters => { + filters = filters ?? [] + return filters.map(f => f.filter.id + "." + f.selected).join(",") + }, [], textual => { + if(textual.length === 0){ + return empty + } + return textual.split(",").map(part => { + const [filterId, selected] = part.split("."); + return {filter: filtersPerName.get(filterId), selected: Number(selected)} + }).filter(f => f.filter !== undefined && !isNaN(f.selected)) + }).syncWith(qp, true) + } + flayers.push(flayer); } return flayers; }); + const layers = State.state.layoutToUse.data.layers - const clusterShow = Math.min(...layers.map(layer => layer.minzoom)) - - + const clusterCounter = TileHierarchyAggregator.createHierarchy() new ShowDataLayer({ features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.data.clustering.minNeededElements), leafletMap: State.state.leafletMap, layerToShow: ShowTileInfo.styling, - doShowLayer: layers.length === 1 ? undefined : State.state.locationControl.map(l => l.zoom < clusterShow) }) State.state.featurePipeline = new FeaturePipeline( source => { clusterCounter.addTile(source) - + const clustering = State.state.layoutToUse.data.clustering const doShowFeatures = source.features.map( f => { @@ -489,7 +510,7 @@ export class InitUiElements { return true }, [State.state.locationControl, State.state.currentBounds] ) - + new ShowDataLayer( { features: source, diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index 70d5a566c..65c6df0ec 100644 --- a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -5,13 +5,14 @@ import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import Hash from "../../Web/Hash"; import {BBox} from "../../GeoOperations"; -export default class FilteringFeatureSource implements FeatureSourceForLayer , Tiled { +export default class FilteringFeatureSource implements FeatureSourceForLayer, Tiled { public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name; public readonly layer: FilteredLayer; -public readonly tileIndex : number - public readonly bbox : BBox + public readonly tileIndex: number + public readonly bbox: BBox + constructor( state: { locationControl: UIEventSource<{ zoom: number }>, @@ -21,7 +22,7 @@ public readonly tileIndex : number upstream: FeatureSourceForLayer ) { const self = this; - this.name = "FilteringFeatureSource("+upstream.name+")" + this.name = "FilteringFeatureSource(" + upstream.name + ")" this.tileIndex = tileIndex this.bbox = BBox.fromTileIndex(tileIndex) @@ -50,12 +51,15 @@ public readonly tileIndex : number } const tagsFilter = layer.appliedFilters.data; - if (tagsFilter) { - if (!tagsFilter.matchesProperties(f.feature.properties)) { + for (const filter of tagsFilter ?? []) { + const neededTags = filter.filter.options[filter.selected].osmTags + if (!neededTags.matchesProperties(f.feature.properties)) { // Hidden by the filter on the layer itself - we want to hide it no matter wat return false; } } + + if (!layer.isDisplayed) { // The layer itself is either disabled or hidden due to zoom constraints // We should return true, but it might still match some other layer @@ -80,7 +84,7 @@ public readonly tileIndex : number }); layer.appliedFilters.addCallback(_ => { - if(!layer.isDisplayed.data){ + if (!layer.isDisplayed.data) { // Currently not shown. // Note that a change in 'isSHown' will trigger an update as well, so we don't have to watch it another time return; diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index c02ed04e4..2a85e6292 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -150,6 +150,7 @@ export default class MetaTagging { for (const f of functions) { f(params, feature); } + State.state.allElements.getEventSourceById(feature.properties.id).ping(); } catch (e) { console.error("While calculating a tag value: ", e) } diff --git a/Models/Constants.ts b/Models/Constants.ts index 7f1b0c8de..91c9e0015 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.0-alpha-2"; + public static vNumber = "0.10.0-alpha-3"; public static ImgurApiKey = '7070e7167f0a25a' // The user journey states thresholds when a new feature gets unlocked diff --git a/Models/FilteredLayer.ts b/Models/FilteredLayer.ts index 6c387270f..68ffb4483 100644 --- a/Models/FilteredLayer.ts +++ b/Models/FilteredLayer.ts @@ -1,9 +1,10 @@ import {UIEventSource} from "../Logic/UIEventSource"; import LayerConfig from "./ThemeConfig/LayerConfig"; import {And} from "../Logic/Tags/And"; +import FilterConfig from "./ThemeConfig/FilterConfig"; export default interface FilteredLayer { readonly isDisplayed: UIEventSource; - readonly appliedFilters: UIEventSource; + readonly appliedFilters: UIEventSource<{filter: FilterConfig, selected: number}[]>; readonly layerDef: LayerConfig; } \ No newline at end of file diff --git a/Models/ThemeConfig/FilterConfig.ts b/Models/ThemeConfig/FilterConfig.ts index 464919d76..2dd9f69d6 100644 --- a/Models/ThemeConfig/FilterConfig.ts +++ b/Models/ThemeConfig/FilterConfig.ts @@ -5,7 +5,8 @@ import Translations from "../../UI/i18n/Translations"; import {TagUtils} from "../../Logic/Tags/TagUtils"; export default class FilterConfig { - readonly options: { + public readonly id: string + public readonly options: { question: Translation; osmTags: TagsFilter; }[]; @@ -14,11 +15,18 @@ export default class FilterConfig { if (json.options === undefined) { throw `A filter without options was given at ${context}` } + if (json.id === undefined) { + throw `A filter without id was found at ${context}` + } + if(json.id.match(/^[a-zA-Z0-9_-]*$/) === null){ + throw `A filter with invalid id was found at ${context}. Ids should only contain letters, numbers or - _` + + } if (json.options.map === undefined) { throw `A filter was given where the options aren't a list at ${context}` } - + this.id = json.id; this.options = json.options.map((option, i) => { const question = Translations.T( option.question, diff --git a/Models/ThemeConfig/Json/FilterConfigJson.ts b/Models/ThemeConfig/Json/FilterConfigJson.ts index c49f9f3eb..7151e3854 100644 --- a/Models/ThemeConfig/Json/FilterConfigJson.ts +++ b/Models/ThemeConfig/Json/FilterConfigJson.ts @@ -1,6 +1,10 @@ import {AndOrTagConfigJson} from "./TagConfigJson"; export default interface FilterConfigJson { + /** + * An id/name for this filter, used to set the URL parameters + */ + id: string, /** * The options for a filter * If there are multiple options these will be a list of radio buttons diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts index 6e3fae5f7..249b3dacb 100644 --- a/UI/BigComponents/FilterView.ts +++ b/UI/BigComponents/FilterView.ts @@ -7,8 +7,6 @@ import Combine from "../Base/Combine"; import Translations from "../i18n/Translations"; import {Translation} from "../i18n/Translation"; import Svg from "../../Svg"; -import {TagsFilter} from "../../Logic/Tags/TagsFilter"; -import {And} from "../../Logic/Tags/And"; import {UIEventSource} from "../../Logic/UIEventSource"; import BaseUIElement from "../BaseUIElement"; import State from "../../State"; @@ -16,11 +14,6 @@ import FilteredLayer from "../../Models/FilteredLayer"; import BackgroundSelector from "./BackgroundSelector"; import FilterConfig from "../../Models/ThemeConfig/FilterConfig"; - -/** - * Shows the filter - */ - export default class FilterView extends VariableUiElement { constructor(filteredLayer: UIEventSource) { const backgroundSelector = new Toggle( @@ -101,26 +94,52 @@ export default class FilterView extends VariableUiElement { return undefined; } - let listFilterElements: [BaseUIElement, UIEventSource][] = layer.filters.map( + const filterIndexes = new Map() + layer.filters.forEach((f, i) => filterIndexes.set(f.id, i)) + + let listFilterElements: [BaseUIElement, UIEventSource<{ filter: FilterConfig, selected: number }>][] = layer.filters.map( FilterView.createFilter ); - const update = () => { - let listTagsFilters = Utils.NoNull( - listFilterElements.map((input) => input[1].data) - ); - flayer.appliedFilters.setData(new And(listTagsFilters)); - }; + listFilterElements.forEach((inputElement, i) => + inputElement[1].addCallback((changed) => { + const oldValue = flayer.appliedFilters.data + + if(changed === undefined){ + // Lets figure out which filter should be removed + // We know this inputElement corresponds with layer.filters[i] + // SO, if there is a value in 'oldValue' with this filter, we have to recalculated + if(!oldValue.some(f => f.filter === layer.filters[i])){ + // The filter to remove is already gone, we can stop + return; + } + }else if(oldValue.some(f => f.filter === changed.filter && f.selected === changed.selected)){ + // The changed value is already there + return; + } + const listTagsFilters = Utils.NoNull( + listFilterElements.map((input) => input[1].data) + ); - listFilterElements.forEach((inputElement) => - inputElement[1].addCallback((_) => update()) + console.log(listTagsFilters, oldValue) + flayer.appliedFilters.setData(listTagsFilters); + }) ); flayer.appliedFilters.addCallbackAndRun(appliedFilters => { - if (appliedFilters === undefined || appliedFilters.and.length === 0) { - listFilterElements.forEach(filter => filter[1].setData(undefined)) - return + for (let i = 0; i < layer.filters.length; i++){ + const filter = layer.filters[i]; + let foundMatch = undefined + for (const appliedFilter of appliedFilters) { + if(appliedFilter.filter === filter){ + foundMatch = appliedFilter + break; + } + } + + listFilterElements[i][1].setData(foundMatch) } + }) return new Combine(listFilterElements.map(input => input[0].SetClass("mt-3"))) @@ -128,7 +147,7 @@ export default class FilterView extends VariableUiElement { } - private static createFilter(filterConfig: FilterConfig): [BaseUIElement, UIEventSource] { + private static createFilter(filterConfig: FilterConfig): [BaseUIElement, UIEventSource<{ filter: FilterConfig, selected: number }>] { if (filterConfig.options.length === 1) { let option = filterConfig.options[0]; @@ -142,20 +161,36 @@ export default class FilterView extends VariableUiElement { .ToggleOnClick() .SetClass("block m-1") - return [toggle, toggle.isEnabled.map(enabled => enabled ? option.osmTags : undefined, [], tags => tags !== undefined)] + const selected = { + filter: filterConfig, + selected: 0 + } + return [toggle, toggle.isEnabled.map(enabled => enabled ? selected : undefined, [], + f => f?.filter === filterConfig && f?.selected === 0) + ] } let options = filterConfig.options; + const values = options.map((f, i) => ({ + filter: filterConfig, selected: i + })) const radio = new RadioButton( options.map( - (option) => - new FixedInputElement(option.question.Clone(), option.osmTags) + (option, i) => + new FixedInputElement(option.question.Clone(), i) ), { dontStyle: true } ); - return [radio, radio.GetValue()] + return [radio, + radio.GetValue().map( + i => values[i], + [], + selected => { + return selected?.selected + } + )] } } diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index fb2c93a0a..5dca2cfc2 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -223,14 +223,14 @@ export default class SimpleAddUI extends Toggle { ] ).SetClass("flex flex-col") ).onClick(() => { - preset.layerToAddTo.appliedFilters.setData(new And([])) + preset.layerToAddTo.appliedFilters.setData([]) cancel() }) const disableFiltersOrConfirm = new Toggle( openLayerOrConfirm, disableFilter, - preset.layerToAddTo.appliedFilters.map(filters => filters === undefined || filters.normalize().and.length === 0) + preset.layerToAddTo.appliedFilters.map(filters => filters === undefined || filters.length === 0) ) diff --git a/assets/layers/birdhide/birdhide.json b/assets/layers/birdhide/birdhide.json index 01e2853f9..1a581b8a0 100644 --- a/assets/layers/birdhide/birdhide.json +++ b/assets/layers/birdhide/birdhide.json @@ -258,6 +258,7 @@ "wayHandling": 1, "filter": [ { + "id": "wheelchair", "options": [ { "question": { @@ -275,6 +276,7 @@ ] }, { + "id": "shelter", "options": [ { "question": { diff --git a/assets/layers/cafe_pub/cafe_pub.json b/assets/layers/cafe_pub/cafe_pub.json index 803d39ffa..e5977e8bb 100644 --- a/assets/layers/cafe_pub/cafe_pub.json +++ b/assets/layers/cafe_pub/cafe_pub.json @@ -170,6 +170,7 @@ ], "filter": [ { + "id": "opened-now", "options": [ { "question": { diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index ae3a12b1d..f3caa2321 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -2627,6 +2627,7 @@ "wayHandling": 1, "filter": [ { + "id": "vehicle-type", "options": [ { "question": { @@ -2656,6 +2657,7 @@ ] }, { + "id": "working", "options": [ { "question": { @@ -2671,6 +2673,7 @@ ] }, { + "id": "connection_type", "options": [ { "question": { diff --git a/assets/layers/charging_station/charging_station.protojson b/assets/layers/charging_station/charging_station.protojson index 111afe533..73aced6ad 100644 --- a/assets/layers/charging_station/charging_station.protojson +++ b/assets/layers/charging_station/charging_station.protojson @@ -648,6 +648,7 @@ "wayHandling": 1, "filter": [ { + "id": "vehicle-type", "options": [ { "question": { @@ -677,6 +678,7 @@ ] }, { + "id": "working", "options": [ { "question": { diff --git a/assets/layers/charging_station/csvToJson.ts b/assets/layers/charging_station/csvToJson.ts index 08e33a229..4a5b04f53 100644 --- a/assets/layers/charging_station/csvToJson.ts +++ b/assets/layers/charging_station/csvToJson.ts @@ -242,6 +242,7 @@ function run(file, protojson) { }) proto["filter"].push({ + id:"connection_type", options: filterOptions }) diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index c05bb6453..1bac9814d 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -560,6 +560,7 @@ ], "filter": [ { + "id": "opened-now", "options": [ { "question": { @@ -571,6 +572,7 @@ ] }, { + "id": "vegetarian", "options": [ { "question": { @@ -589,6 +591,7 @@ ] }, { + "id": "vegan", "options": [ { "question": { @@ -605,6 +608,7 @@ ] }, { + "id": "halal", "options": [ { "question": { diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index 5a3551d52..dab52299c 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -423,6 +423,7 @@ ], "filter": [ { + "id": "access", "options": [ { "question": { @@ -433,6 +434,7 @@ ] }, { + "id": "dogs", "options": [ { "question": { diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index d5576a1b8..cc21f23f8 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -444,6 +444,7 @@ }, "filter": [ { + "id": "kid-books", "options": [ { "question": "Kinderboeken aanwezig?", @@ -452,6 +453,7 @@ ] }, { + "id": "adult-books", "options": [ { "question": "Boeken voor volwassenen aanwezig?", @@ -460,6 +462,7 @@ ] }, { + "id": "inside", "options": [ { "question": "Binnen of buiten", diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index 9f7fedf32..c8ee81894 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -411,6 +411,7 @@ ], "filter": [ { + "id": "wheelchair", "options": [ { "question": { @@ -421,6 +422,7 @@ ] }, { + "id": "changing_table", "options": [ { "question": { @@ -431,6 +433,7 @@ ] }, { + "id": "free", "options": [ { "question": { diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 9cea92fde..53dea9cc8 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -143,6 +143,7 @@ }, "filter": [ { + "id": "name-alt", "options": [ { "question": "Name contains 'alt'", @@ -151,6 +152,7 @@ ] }, { + "id": "name-wenslijn", "options": [ { "question": "Name contains 'wenslijn'", @@ -159,6 +161,7 @@ ] }, { + "id": "name-omleiding", "options": [ { "question": "Name contains 'omleiding'", @@ -167,6 +170,7 @@ ] }, { + "id":"ref-alt", "options": [ { "question": "Reference contains 'alt'", @@ -175,6 +179,7 @@ ] }, { + "id": "missing_link", "options": [ { "question": "No filter" @@ -194,6 +199,7 @@ ] }, { + "id": "proposed", "options": [ { "question": "No filter" diff --git a/assets/themes/uk_addresses/housenumber_unknown_small.svg b/assets/themes/uk_addresses/housenumber_unknown_small.svg new file mode 100644 index 000000000..398ce8f72 --- /dev/null +++ b/assets/themes/uk_addresses/housenumber_unknown_small.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/assets/themes/uk_addresses/license_info.json b/assets/themes/uk_addresses/license_info.json index 0c6fb0715..7d805cee4 100644 --- a/assets/themes/uk_addresses/license_info.json +++ b/assets/themes/uk_addresses/license_info.json @@ -39,5 +39,13 @@ "https://github.com/streetcomplete/StreetComplete/tree/master/res/graphics", "https://f-droid.org/packages/de.westnordost.streetcomplete/" ] + }, + { + "path": "housenumber_unknown_small.svg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] } ] \ No newline at end of file diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index d4708deac..8dd07bf47 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -15,11 +15,15 @@ "maintainer": "Pieter Vander Vennet, Rob Nickerson, Russ Garrett", "icon": "./assets/themes/uk_addresses/housenumber_unknown.svg", "version": "2021-09-17", - "startLat": -0.08528530407, - "startLon": 51.52103754846, - "startZoom": 18, - "widenFactor": 1.5, + "startLat": -0.08706, + "startLon": 51.52224, + "startZoom": 17, + "widenFactor": 1.01, "socialImage": "", + "clustering": { + "minNeededFeatures": 25, + "maxZoom": 17 + }, "layers": [ { "id": "to_import", @@ -34,21 +38,21 @@ "minzoom": 12, "wayHandling": 1, "icon": { - "render": "./assets/themes/uk_addresses/housenumber_unknown.svg" - }, - "iconSize": { - "render": "40,40,center", + "render": "./assets/themes/uk_addresses/housenumber_unknown.svg", "mappings": [ { "if": "_embedding_object:id~*", - "then": "15,15,center" + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" }, { "if": "_imported=yes", - "then": "8,8,center" + "then": "./assets/themes/uk_addresses/housenumber_unknown_small.svg" } ] }, + "iconSize": { + "render": "40,40,center" + }, "title": { "render": "Address to be determined" }, @@ -73,6 +77,22 @@ "_embedding_object:addr:housenumber=JSON.parse(feat.properties._embedding_object)?.['addr:housenumber']", "_embedding_object:addr:street=JSON.parse(feat.properties._embedding_object)?.['addr:street']", "_embedding_object:id=JSON.parse(feat.properties._embedding_object)?.id" + ], + "filter": [ + { + "id": "to_handle", + "options": [ + { + "question": "Only show non-matched objects", + "osmTags": { + "and": [ + "_imported=", + "_embedding_object:id=" + ] + } + } + ] + } ] }, { diff --git a/test/GeoOperations.spec.ts b/test/GeoOperations.spec.ts index 49447afd5..372402648 100644 --- a/test/GeoOperations.spec.ts +++ b/test/GeoOperations.spec.ts @@ -181,10 +181,10 @@ export default class GeoOperationsSpec extends T { ["bbox bounds test", () => { const bbox = BBox.fromTile(16, 32754, 21785) - equal(-0.0714111328125, bbox.minLon) - equal(-0.076904296875, bbox.maxLon) - equal(51.53266860674158, bbox.minLat) - equal(51.5292513551899, bbox.maxLat) + equal(-0.076904296875, bbox.minLon) + equal(-0.0714111328125, bbox.maxLon) + equal(51.5292513551899, bbox.minLat) + equal(51.53266860674158, bbox.maxLat) } ] ] From 41a2a79fe9aafca32f03c0e6fd12ab7588dd269a Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 28 Sep 2021 17:30:48 +0200 Subject: [PATCH 039/110] More refactoring of the featurepipeline, introduction of fetching data from the OSM-API directly per tile, personal theme refactoring --- Customizations/AllKnownLayouts.ts | 21 +++ InitUiElements.ts | 112 ++++++------- Logic/Actors/OverpassFeatureSource.ts | 92 +++++----- Logic/BBox.ts | 158 ++++++++++++++++++ Logic/ContributorCount.ts | 2 +- Logic/ExtraFunction.ts | 3 +- Logic/FeatureSource/FeaturePipeline.ts | 146 +++++++++++++--- Logic/FeatureSource/FeatureSource.ts | 2 +- .../PerLayerFeatureSourceSplitter.ts | 3 +- .../Sources/FeatureSourceMerger.ts | 2 +- .../Sources/FilteringFeatureSource.ts | 2 +- Logic/FeatureSource/Sources/GeoJsonSource.ts | 2 +- .../Sources/RememberingSource.ts | 2 +- .../Sources/SimpleFeatureSource.ts | 8 +- .../TiledFeatureSource/OsmFeatureSource.ts | 112 +++++++++++++ .../TiledFeatureSource/TileHierarchy.ts | 2 +- .../TiledFeatureSource/TileHierarchyMerger.ts | 2 +- .../TiledFeatureSource/TiledFeatureSource.ts | 18 +- .../TiledFromLocalStorageSource.ts | 16 +- Logic/GeoOperations.ts | 135 +-------------- Logic/Osm/OsmConnection.ts | 32 ++-- Logic/Osm/OsmObject.ts | 2 +- Logic/Osm/Overpass.ts | 15 +- Models/TileRange.ts | 2 +- State.ts | 28 ++-- UI/Base/Minimap.ts | 2 +- UI/Base/MinimapImplementation.ts | 2 +- UI/BigComponents/Attribution.ts | 2 +- UI/BigComponents/DownloadPanel.ts | 3 +- UI/BigComponents/FullWelcomePaneWithTabs.ts | 6 +- UI/BigComponents/LeftControls.ts | 2 +- UI/BigComponents/PersonalLayersPanel.ts | 120 ------------- UI/BigComponents/SimpleAddUI.ts | 2 +- UI/ExportPDF.ts | 2 +- UI/Input/LocationInput.ts | 3 +- UI/Popup/SplitRoadWizard.ts | 3 +- UI/ShowDataLayer/PerTileCountAggregator.ts | 2 +- Utils.ts | 21 +-- assets/layers/bike_shop/bike_shop.json | 9 +- assets/layers/toilet/toilet.json | 2 +- assets/layers/toilet/wheelchair.svg | 123 +++++++------- assets/themes/bicyclelib/bicyclelib.json | 8 +- assets/themes/personal/personal.json | 7 +- .../toerisme_vlaanderen.json | 2 +- preferences.ts | 18 +- test.ts | 58 +++---- test/GeoOperations.spec.ts | 3 +- test/OsmConnection.spec.ts | 17 +- 48 files changed, 746 insertions(+), 590 deletions(-) create mode 100644 Logic/BBox.ts create mode 100644 Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts delete mode 100644 UI/BigComponents/PersonalLayersPanel.ts diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index 35cfb010c..57dbbf0ba 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -1,6 +1,7 @@ import AllKnownLayers from "./AllKnownLayers"; import * as known_themes from "../assets/generated/known_layers_and_themes.json" import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; +import LayerConfig from "../Models/ThemeConfig/LayerConfig"; export class AllKnownLayouts { @@ -8,6 +9,26 @@ export class AllKnownLayouts { public static allKnownLayouts: Map = AllKnownLayouts.AllLayouts(); public static layoutsList: LayoutConfig[] = AllKnownLayouts.GenerateOrderedList(AllKnownLayouts.allKnownLayouts); + public static AllPublicLayers(){ + const allLayers : LayerConfig[] = [] + const seendIds = new Set() + const publicLayouts = AllKnownLayouts.layoutsList.filter(l => !l.hideFromOverview) + for (const layout of publicLayouts) { + if(layout.hideFromOverview){ + continue + } + for (const layer of layout.layers) { + if(seendIds.has(layer.id)){ + continue + } + seendIds.add(layer.id) + allLayers.push(layer) + } + + } + return allLayers + } + private static GenerateOrderedList(allKnownLayouts: Map): LayoutConfig[] { const keys = ["personal", "cyclofix", "hailhydrant", "bookcases", "toilets", "aed"] const list = [] diff --git a/InitUiElements.ts b/InitUiElements.ts index f3593556f..202e64149 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -27,7 +27,6 @@ import MapControlButton from "./UI/MapControlButton"; import LZString from "lz-string"; import AllKnownLayers from "./Customizations/AllKnownLayers"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; -import {TagsFilter} from "./Logic/Tags/TagsFilter"; import LeftControls from "./UI/BigComponents/LeftControls"; import RightControls from "./UI/BigComponents/RightControls"; import {LayoutConfigJson} from "./Models/ThemeConfig/Json/LayoutConfigJson"; @@ -40,10 +39,10 @@ import {SubtleButton} from "./UI/Base/SubtleButton"; import ShowTileInfo from "./UI/ShowDataLayer/ShowTileInfo"; import {Tiles} from "./Models/TileRange"; import {TileHierarchyAggregator} from "./UI/ShowDataLayer/PerTileCountAggregator"; -import {BBox} from "./Logic/GeoOperations"; -import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource"; import FilterConfig from "./Models/ThemeConfig/FilterConfig"; import FilteredLayer from "./Models/FilteredLayer"; +import {BBox} from "./Logic/BBox"; +import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; export class InitUiElements { static InitAll( @@ -70,10 +69,24 @@ export class InitUiElements { "LayoutFromBase64 is ", layoutFromBase64 ); + + if(layoutToUse.id === personal.id){ + layoutToUse.layers = AllKnownLayouts.AllPublicLayers() + for (const layer of layoutToUse.layers) { + layer.minzoomVisible = Math.max(layer.minzoomVisible, layer.minzoom) + layer.minzoom = Math.max(16, layer.minzoom) + } + } State.state = new State(layoutToUse); - // This 'leaks' the global state via the window object, useful for debugging + if(layoutToUse.id === personal.id) { + // Disable overpass all together + State.state.overpassMaxZoom.setData(0) + + } + + // This 'leaks' the global state via the window object, useful for debugging // @ts-ignore window.mapcomplete_state = State.state; @@ -102,45 +115,6 @@ export class InitUiElements { } } - function updateFavs() { - // This is purely for the personal theme to load the layers there - const favs = State.state.favouriteLayers.data ?? []; - - const neededLayers = new Set(); - - console.log("Favourites are: ", favs); - layoutToUse.layers.splice(0, layoutToUse.layers.length); - let somethingChanged = false; - for (const fav of favs) { - if (AllKnownLayers.sharedLayers.has(fav)) { - const layer = AllKnownLayers.sharedLayers.get(fav); - if (!neededLayers.has(layer)) { - neededLayers.add(layer); - somethingChanged = true; - } - } - - for (const layouts of State.state.installedThemes.data) { - for (const layer of layouts.layout.layers) { - if (typeof layer === "string") { - continue; - } - if (layer.id === fav) { - if (!neededLayers.has(layer)) { - neededLayers.add(layer); - somethingChanged = true; - } - } - } - } - } - if (somethingChanged) { - State.state.layoutToUse.data.layers = Array.from(neededLayers); - State.state.layoutToUse.ping(); - State.state.featurePipeline?.ForceRefresh(); - } - } - if (layoutToUse.customCss !== undefined) { Utils.LoadCustomCss(layoutToUse.customCss); } @@ -206,18 +180,9 @@ export class InitUiElements { .addCallbackAndRunD(_ => addHomeMarker()); State.state.leafletMap.addCallbackAndRunD(_ => addHomeMarker()) - if (layoutToUse.id === personal.id) { - updateFavs(); - } InitUiElements.setupAllLayerElements(); - - if (layoutToUse.id === personal.id) { - State.state.favouriteLayers.addCallback(updateFavs); - State.state.installedThemes.addCallback(updateFavs); - } else { State.state.locationControl.ping(); - } new SelectedFeatureHandler(Hash.hash, State.state) @@ -414,15 +379,29 @@ export class InitUiElements { const flayers: FilteredLayer[] = []; for (const layer of layoutToUse.layers) { - const isDisplayed = QueryParameters.GetQueryParameter( - "layer-" + layer.id, - "true", - "Wether or not layer " + layer.id + " is shown" - ).map( - (str) => str !== "false", - [], - (b) => b.toString() - ); + let defaultShown = "true" + if(layoutToUse.id === personal.id){ + defaultShown = "false" + } + + let isDisplayed: UIEventSource + if(layoutToUse.id === personal.id){ + isDisplayed = State.state.osmConnection.GetPreference("personal-theme-layer-" + layer.id + "-enabled") + .map(value => value === "yes", [], enabled => { + return enabled ? "yes" : ""; + }) + isDisplayed.addCallbackAndRun(d =>console.log("IsDisplayed for layer", layer.id, "is currently", d) ) + }else{ + isDisplayed = QueryParameters.GetQueryParameter( + "layer-" + layer.id, + defaultShown, + "Wether or not layer " + layer.id + " is shown" + ).map( + (str) => str !== "false", + [], + (b) => b.toString() + ); + } const flayer = { isDisplayed: isDisplayed, layerDef: layer, @@ -453,8 +432,6 @@ export class InitUiElements { }); - const layers = State.state.layoutToUse.data.layers - const clusterCounter = TileHierarchyAggregator.createHierarchy() new ShowDataLayer({ features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.data.clustering.minNeededElements), @@ -471,6 +448,10 @@ export class InitUiElements { const doShowFeatures = source.features.map( f => { const z = State.state.locationControl.data.zoom + + if(!source.layer.isDisplayed.data){ + return false; + } if (z < source.layer.layerDef.minzoom) { // Layer is always hidden for this zoom level @@ -482,7 +463,7 @@ export class InitUiElements { } if (f.length > clustering.minNeededElements) { - // This tile alone has too much features + // This tile alone already has too much features return false } @@ -504,11 +485,12 @@ export class InitUiElements { const bounds = State.state.currentBounds.data const tilebbox = BBox.fromTileIndex(source.tileIndex) if (!tilebbox.overlapsWith(bounds)) { + // Not within range return false } return true - }, [State.state.locationControl, State.state.currentBounds] + }, [State.state.currentBounds] ) new ShowDataLayer( diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index 20181fc14..d77f326e5 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -9,9 +9,10 @@ import {TagsFilter} from "../Tags/TagsFilter"; import SimpleMetaTagger from "../SimpleMetaTagger"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import RelationsTracker from "../Osm/RelationsTracker"; +import {BBox} from "../BBox"; -export default class OverpassFeatureSource implements FeatureSource, FeatureSourceState { +export default class OverpassFeatureSource implements FeatureSource { public readonly name = "OverpassFeatureSource" @@ -21,7 +22,6 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour public readonly features: UIEventSource<{ feature: any, freshness: Date }[]> = new UIEventSource(undefined); - public readonly sufficientlyZoomed: UIEventSource; public readonly runningQuery: UIEventSource = new UIEventSource(false); public readonly timeout: UIEventSource = new UIEventSource(0); @@ -40,10 +40,12 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour private readonly state: { readonly locationControl: UIEventSource, readonly layoutToUse: UIEventSource, - readonly leafletMap: any, readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; + readonly currentBounds :UIEventSource } + private readonly _isActive: UIEventSource; + private _onUpdated?: (bbox: BBox, dataFreshness: Date) => void; /** * The most important layer should go first, as that one gets first pick for the questions */ @@ -51,33 +53,24 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour state: { readonly locationControl: UIEventSource, readonly layoutToUse: UIEventSource, - readonly leafletMap: any, readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; - readonly overpassMaxZoom: UIEventSource - }) { + readonly overpassMaxZoom: UIEventSource, + readonly currentBounds :UIEventSource + }, + + options?: { + isActive?: UIEventSource, + onUpdated?: (bbox: BBox, freshness: Date) => void, + relationTracker: RelationsTracker}) { this.state = state - this.relationsTracker = new RelationsTracker() + this._isActive = options.isActive; + this._onUpdated =options. onUpdated; + this.relationsTracker = options.relationTracker const location = state.locationControl const self = this; - this.sufficientlyZoomed = location.map(location => { - if (location?.zoom === undefined) { - return false; - } - let minzoom = Math.min(...state.layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); - if (location.zoom < minzoom) { - return false; - } - const maxZoom = state.overpassMaxZoom.data - if (maxZoom !== undefined && location.zoom > maxZoom) { - return false; - } - - return true; - }, [state.layoutToUse] - ); for (let i = 0; i < 25; i++) { // This update removes all data on all layers -> erase the map on lower levels too this._previousBounds.set(i, []); @@ -89,16 +82,11 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour location.addCallback(() => { self.update() }); - state.leafletMap.addCallbackAndRunD(_ => { - self.update(); + + state.currentBounds.addCallback(_ => { + self.update() }) - } - - public ForceRefresh() { - for (let i = 0; i < 25; i++) { - this._previousBounds.set(i, []); - } - this.update(); + } private GetFilter(): Overpass { @@ -152,24 +140,34 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour } private update() { - this.updateAsync().then(_ => { + if(!this._isActive.data){ + return; + } + const self = this + this.updateAsync().then(bboxAndDate => { + if(bboxAndDate === undefined || self._onUpdated === undefined){ + return; + } + const [bbox, date] = bboxAndDate + self._onUpdated(bbox, date); }) } - private async updateAsync(): Promise { + private async updateAsync(): Promise<[BBox, Date]> { if (this.runningQuery.data) { console.log("Still running a query, not updating"); - return; + return undefined; } if (this.timeout.data > 0) { console.log("Still in timeout - not updating") - return; + return undefined; } - const bounds = this.state.leafletMap.data?.getBounds()?.pad(this.state.layoutToUse.data.widenFactor); + const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.data.widenFactor)?.expandToTileBounds(14); + if (bounds === undefined) { - return; + return undefined; } const n = Math.min(90, bounds.getNorth()); @@ -178,13 +176,12 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour const w = Math.max(-180, bounds.getWest()); const queryBounds = {north: n, east: e, south: s, west: w}; - const z = Math.floor(this.state.locationControl.data.zoom ?? 0); const self = this; const overpass = this.GetFilter(); if (overpass === undefined) { - return; + return undefined; } this.runningQuery.setData(true); @@ -195,15 +192,14 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour try { [data, date] = await overpass.queryGeoJson(queryBounds) + console.log("Querying overpass is done", data) } catch (e) { - console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to`, e); - self.retries.data++; self.retries.ping(); + console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to`, e); self.timeout.setData(self.retries.data * 5); - self.runningQuery.setData(false); - + while (self.timeout.data > 0) { await Utils.waitFor(1000) self.timeout.data-- @@ -212,16 +208,20 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour } } while (data === undefined); + const z = Math.floor(this.state.locationControl.data.zoom ?? 0); self._previousBounds.get(z).push(queryBounds); self.retries.setData(0); try { data.features.forEach(feature => SimpleMetaTagger.objectMetaInfo.applyMetaTagsOnFeature(feature, date)); self.features.setData(data.features.map(f => ({feature: f, freshness: date}))); + return [bounds, date]; } catch (e) { console.error("Got the overpass response, but could not process it: ", e, e.stack) + }finally { + self.runningQuery.setData(false); } - self.runningQuery.setData(false); + } @@ -231,7 +231,7 @@ export default class OverpassFeatureSource implements FeatureSource, FeatureSour return false; } - const b = this.state.leafletMap.data.getBounds(); + const b = this.state.currentBounds.data; return b.getSouth() >= bounds.south && b.getNorth() <= bounds.north && b.getEast() <= bounds.east && diff --git a/Logic/BBox.ts b/Logic/BBox.ts new file mode 100644 index 000000000..e642c6ce3 --- /dev/null +++ b/Logic/BBox.ts @@ -0,0 +1,158 @@ +import * as turf from "@turf/turf"; +import {TileRange, Tiles} from "../Models/TileRange"; + +export class BBox { + + readonly maxLat: number; + readonly maxLon: number; + readonly minLat: number; + readonly minLon: number; + static global: BBox = new BBox([[-180, -90], [180, 90]]); + + constructor(coordinates) { + this.maxLat = -90; + this.maxLon = -180; + this.minLat = 90; + this.minLon = 180; + + + for (const coordinate of coordinates) { + this.maxLon = Math.max(this.maxLon, coordinate[0]); + this.maxLat = Math.max(this.maxLat, coordinate[1]); + this.minLon = Math.min(this.minLon, coordinate[0]); + this.minLat = Math.min(this.minLat, coordinate[1]); + } + this.check(); + } + + static fromLeafletBounds(bounds) { + return new BBox([[bounds.getWest(), bounds.getNorth()], [bounds.getEast(), bounds.getSouth()]]) + } + + static get(feature): BBox { + if (feature.bbox?.overlapsWith === undefined) { + const turfBbox: number[] = turf.bbox(feature) + feature.bbox = new BBox([[turfBbox[0], turfBbox[1]], [turfBbox[2], turfBbox[3]]]); + } + return feature.bbox; + } + + /** + * Constructs a tilerange which fully contains this bbox (thus might be a bit larger) + * @param zoomlevel + */ + public containingTileRange(zoomlevel): TileRange{ + return Tiles.TileRangeBetween(zoomlevel, this.minLat, this.minLon, this.maxLat, this.maxLon) + } + + public overlapsWith(other: BBox) { + if (this.maxLon < other.minLon) { + return false; + } + if (this.maxLat < other.minLat) { + return false; + } + if (this.minLon > other.maxLon) { + return false; + } + return this.minLat <= other.maxLat; + + } + + public isContainedIn(other: BBox) { + if (this.maxLon > other.maxLon) { + return false; + } + if (this.maxLat > other.maxLat) { + return false; + } + if (this.minLon < other.minLon) { + return false; + } + if (this.minLat < other.minLat) { + return false + } + return true; + } + + private check() { + if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) { + console.log(this); + throw "BBOX has NAN"; + } + } + + static fromTile(z: number, x: number, y: number): BBox { + return new BBox(Tiles.tile_bounds_lon_lat(z, x, y)) + } + + static fromTileIndex(i: number): BBox { + if (i === 0) { + return BBox.global + } + return BBox.fromTile(...Tiles.tile_from_index(i)) + } + + getEast() { + return this.maxLon + } + + getNorth() { + return this.maxLat + } + + getWest() { + return this.minLon + } + + getSouth() { + return this.minLat + } + + pad(factor: number): BBox { + const latDiff = this.maxLat - this.minLat + const lat = (this.maxLat + this.minLat) / 2 + const lonDiff = this.maxLon - this.minLon + const lon = (this.maxLon + this.minLon) / 2 + return new BBox([[ + lon - lonDiff * factor, + lat - latDiff * factor + ], [lon + lonDiff * factor, + lat + latDiff * factor]]) + } + + toLeaflet() { + return [[this.minLat, this.minLon], [this.maxLat, this.maxLon]] + } + + asGeoJson(properties: any): any { + return { + type: "Feature", + properties: properties, + geometry: { + type: "Polygon", + coordinates: [[ + + [this.minLon, this.minLat], + [this.maxLon, this.minLat], + [this.maxLon, this.maxLat], + [this.minLon, this.maxLat], + [this.minLon, this.minLat], + + ]] + } + } + } + + /** + * Expands the BBOx so that it contains complete tiles for the given zoomlevel + * @param zoomlevel + */ + expandToTileBounds(zoomlevel: number) : BBox{ + const ul = Tiles.embedded_tile(this.minLat, this.minLon, zoomlevel) + const lr = Tiles.embedded_tile(this.maxLat, this.maxLon, zoomlevel) + const boundsul = Tiles.tile_bounds_lon_lat(ul.z, ul.x, ul.y) + const boundslr = Tiles.tile_bounds_lon_lat(lr.z, lr.x, lr.y) + return new BBox([].concat(boundsul, boundslr)) + } +} \ No newline at end of file diff --git a/Logic/ContributorCount.ts b/Logic/ContributorCount.ts index 2dcd8450f..f39d1106f 100644 --- a/Logic/ContributorCount.ts +++ b/Logic/ContributorCount.ts @@ -2,7 +2,7 @@ import {UIEventSource} from "./UIEventSource"; import FeaturePipeline from "./FeatureSource/FeaturePipeline"; import Loc from "../Models/Loc"; -import {BBox} from "./GeoOperations"; +import {BBox} from "./BBox"; export default class ContributorCount { diff --git a/Logic/ExtraFunction.ts b/Logic/ExtraFunction.ts index 5839fca08..76c28c5a9 100644 --- a/Logic/ExtraFunction.ts +++ b/Logic/ExtraFunction.ts @@ -1,4 +1,4 @@ -import {BBox, GeoOperations} from "./GeoOperations"; +import {GeoOperations} from "./GeoOperations"; import Combine from "../UI/Base/Combine"; import RelationsTracker from "./Osm/RelationsTracker"; import State from "../State"; @@ -7,6 +7,7 @@ import List from "../UI/Base/List"; import Title from "../UI/Base/Title"; import {UIEventSourceTools} from "./UIEventSource"; import AspectedRouting from "./Osm/aspectedRouting"; +import {BBox} from "./BBox"; export interface ExtraFuncParams { /** diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index a24d6f516..fab051aa5 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -17,18 +17,23 @@ import RegisteringAllFromFeatureSourceActor from "./Actors/RegisteringAllFromFea import TiledFromLocalStorageSource from "./TiledFeatureSource/TiledFromLocalStorageSource"; import SaveTileToLocalStorageActor from "./Actors/SaveTileToLocalStorageActor"; import DynamicGeoJsonTileSource from "./TiledFeatureSource/DynamicGeoJsonTileSource"; -import {BBox} from "../GeoOperations"; import {TileHierarchyMerger} from "./TiledFeatureSource/TileHierarchyMerger"; import RelationsTracker from "../Osm/RelationsTracker"; import {NewGeometryFromChangesFeatureSource} from "./Sources/NewGeometryFromChangesFeatureSource"; import ChangeGeometryApplicator from "./Sources/ChangeGeometryApplicator"; +import {BBox} from "../BBox"; +import OsmFeatureSource from "./TiledFeatureSource/OsmFeatureSource"; +import {OsmConnection} from "../Osm/OsmConnection"; +import {Tiles} from "../../Models/TileRange"; -export default class FeaturePipeline implements FeatureSourceState { +export default class FeaturePipeline { public readonly sufficientlyZoomed: UIEventSource; + public readonly runningQuery: UIEventSource; public readonly timeout: UIEventSource; + public readonly somethingLoaded: UIEventSource = new UIEventSource(false) public readonly newDataLoadedSignal: UIEventSource = new UIEventSource(undefined) @@ -39,27 +44,59 @@ export default class FeaturePipeline implements FeatureSourceState { constructor( handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void, state: { - filteredLayers: UIEventSource, - locationControl: UIEventSource, - selectedElement: UIEventSource, - changes: Changes, - layoutToUse: UIEventSource, - leafletMap: any, + readonly filteredLayers: UIEventSource, + readonly locationControl: UIEventSource, + readonly selectedElement: UIEventSource, + readonly changes: Changes, + readonly layoutToUse: UIEventSource, + readonly leafletMap: any, readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; readonly overpassMaxZoom: UIEventSource; + readonly osmConnection: OsmConnection + readonly currentBounds: UIEventSource }) { const self = this - const updater = new OverpassFeatureSource(state); + + /** + * Maps tileid onto last download moment + */ + const tileFreshnesses = new Map() + const osmSourceZoomLevel = 14 + const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12)) + this.relationTracker = new RelationsTracker() + + const updater = new OverpassFeatureSource(state, + { + relationTracker: this.relationTracker, + isActive: useOsmApi.map(b => !b), + onUpdated: (bbox, freshness) => { + // This callback contains metadata of the overpass call + const range = bbox.containingTileRange(osmSourceZoomLevel) + Tiles.MapRange(range, (x, y) => { + tileFreshnesses.set(Tiles.tile_index(osmSourceZoomLevel, x, y), freshness) + }) + + } + }); + this.overpassUpdater = updater; - this.sufficientlyZoomed = updater.sufficientlyZoomed - this.runningQuery = updater.runningQuery + this.sufficientlyZoomed = state.locationControl.map(location => { + if (location?.zoom === undefined) { + return false; + } + let minzoom = Math.min(...state.layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); + return location.zoom >= minzoom; + } + ); + this.timeout = updater.timeout - this.relationTracker = updater.relationsTracker + + // Register everything in the state' 'AllElements' new RegisteringAllFromFeatureSourceActor(updater) - + const perLayerHierarchy = new Map() this.perLayerHierarchy = perLayerHierarchy @@ -72,7 +109,7 @@ export default class FeaturePipeline implements FeatureSourceState { new ChangeGeometryApplicator(src, state.changes) ) ) - + handleFeatureSource(srcFiltered) self.somethingLoaded.setData(true) }; @@ -81,6 +118,7 @@ export default class FeaturePipeline implements FeatureSourceState { perLayerHierarchy.get(layerId).registerTile(src) } + for (const filteredLayer of state.filteredLayers.data) { const hierarchy = new TileHierarchyMerger(filteredLayer, (tile, _) => patchedHandleFeatureSource(tile)) const id = filteredLayer.layerDef.id @@ -91,12 +129,25 @@ export default class FeaturePipeline implements FeatureSourceState { // This is an OSM layer // We load the cached values and register them // Getting data from upstream happens a bit lower - new TiledFromLocalStorageSource(filteredLayer, + const localStorage = new TiledFromLocalStorageSource(filteredLayer, (src) => { new RegisteringAllFromFeatureSourceActor(src) hierarchy.registerTile(src); src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src)) }, state) + + localStorage.tileFreshness.forEach((value, key) => { + if (tileFreshnesses.has(key)) { + const previous = tileFreshnesses.get(key) + if (value < previous) { + tileFreshnesses.set(key, value) + } + } else { + tileFreshnesses.set(key, value) + } + }) + + continue } @@ -106,7 +157,7 @@ export default class FeaturePipeline implements FeatureSourceState { const src = new GeoJsonSource(filteredLayer) TiledFeatureSource.createHierarchy(src, { layer: src.layer, - minZoomLevel:14, + minZoomLevel: 14, dontEnforceMinZoom: true, registerTile: (tile) => { new RegisteringAllFromFeatureSourceActor(tile) @@ -118,16 +169,54 @@ export default class FeaturePipeline implements FeatureSourceState { new DynamicGeoJsonTileSource( filteredLayer, tile => { - new RegisteringAllFromFeatureSourceActor(tile) - addToHierarchy(tile, id) - tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) - }, + new RegisteringAllFromFeatureSourceActor(tile) + addToHierarchy(tile, id) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + }, state ) } - } + console.log("Tilefreshnesses are", tileFreshnesses) + const oldestAllowedDate = new Date(new Date().getTime() - (60 * 60 * 24 * 30 * 1000)); + + const neededTilesFromOsm = state.currentBounds.map(bbox => { + if (bbox === undefined) { + return + } + const range = bbox.containingTileRange(osmSourceZoomLevel) + const tileIndexes = [] + if (range.total > 100) { + // Too much tiles! + return [] + } + Tiles.MapRange(range, (x, y) => { + const i = Tiles.tile_index(osmSourceZoomLevel, x, y); + if (tileFreshnesses.get(i) > oldestAllowedDate) { + console.debug("Skipping tile", osmSourceZoomLevel, x, y, "as a decently fresh one is available") + // The cached tiles contain decently fresh data + return; + } + tileIndexes.push(i) + }) + return tileIndexes + }) + + const osmFeatureSource = new OsmFeatureSource({ + isActive: useOsmApi, + neededTiles: neededTilesFromOsm, + handleTile: tile => { + new RegisteringAllFromFeatureSourceActor(tile) + new SaveTileToLocalStorageActor(tile, tile.tileIndex) + addToHierarchy(tile, tile.layer.layerDef.id), + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + + }, + state: state + }) + + // Actually load data from the overpass source new PerLayerFeatureSourceSplitter(state.filteredLayers, (source) => TiledFeatureSource.createHierarchy(source, { @@ -169,9 +258,15 @@ export default class FeaturePipeline implements FeatureSourceState { self.updateAllMetaTagging() }) + + this.runningQuery = updater.runningQuery.map( + overpass => overpass || osmFeatureSource.isRunning.data, [osmFeatureSource.isRunning] + ) + + } - - private applyMetaTags(src: FeatureSourceForLayer){ + + private applyMetaTags(src: FeatureSourceForLayer) { const self = this console.debug("Applying metatagging onto ", src.name) window.setTimeout( @@ -192,7 +287,7 @@ export default class FeaturePipeline implements FeatureSourceState { }, 15 ) - + } private updateAllMetaTagging() { @@ -231,7 +326,4 @@ export default class FeaturePipeline implements FeatureSourceState { }) } - public ForceRefresh() { - this.overpassUpdater.ForceRefresh() - } } \ No newline at end of file diff --git a/Logic/FeatureSource/FeatureSource.ts b/Logic/FeatureSource/FeatureSource.ts index 791882e11..7d603d1f6 100644 --- a/Logic/FeatureSource/FeatureSource.ts +++ b/Logic/FeatureSource/FeatureSource.ts @@ -1,7 +1,7 @@ import {UIEventSource} from "../UIEventSource"; import {Utils} from "../../Utils"; import FilteredLayer from "../../Models/FilteredLayer"; -import {BBox} from "../GeoOperations"; +import {BBox} from "../BBox"; export default interface FeatureSource { features: UIEventSource<{ feature: any, freshness: Date }[]>; diff --git a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts index d16e2c558..f9a0cbe1a 100644 --- a/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts +++ b/Logic/FeatureSource/PerLayerFeatureSourceSplitter.ts @@ -15,6 +15,7 @@ export default class PerLayerFeatureSourceSplitter { handleLayerData: (source: FeatureSourceForLayer & Tiled) => void, upstream: FeatureSource, options?:{ + tileIndex?: number, handleLeftovers?: (featuresWithoutLayer: any[]) => void }) { @@ -71,7 +72,7 @@ export default class PerLayerFeatureSourceSplitter { let featureSource = knownLayers.get(id) if (featureSource === undefined) { // Not yet initialized - now is a good time - featureSource = new SimpleFeatureSource(layer) + featureSource = new SimpleFeatureSource(layer, options?.tileIndex) featureSource.features.setData(features) knownLayers.set(id, featureSource) handleLayerData(featureSource) diff --git a/Logic/FeatureSource/Sources/FeatureSourceMerger.ts b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts index 44ae28543..b1797d0ae 100644 --- a/Logic/FeatureSource/Sources/FeatureSourceMerger.ts +++ b/Logic/FeatureSource/Sources/FeatureSourceMerger.ts @@ -5,9 +5,9 @@ import {UIEventSource} from "../../UIEventSource"; import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; import FilteredLayer from "../../../Models/FilteredLayer"; -import {BBox} from "../../GeoOperations"; import {Utils} from "../../../Utils"; import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled, IndexedFeatureSource { diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index 65c6df0ec..6b433f88d 100644 --- a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -3,7 +3,7 @@ import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; import FilteredLayer from "../../../Models/FilteredLayer"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import Hash from "../../Web/Hash"; -import {BBox} from "../../GeoOperations"; +import {BBox} from "../../BBox"; export default class FilteringFeatureSource implements FeatureSourceForLayer, Tiled { public features: UIEventSource<{ feature: any; freshness: Date }[]> = diff --git a/Logic/FeatureSource/Sources/GeoJsonSource.ts b/Logic/FeatureSource/Sources/GeoJsonSource.ts index 6222fc729..301f1bc55 100644 --- a/Logic/FeatureSource/Sources/GeoJsonSource.ts +++ b/Logic/FeatureSource/Sources/GeoJsonSource.ts @@ -5,8 +5,8 @@ import {UIEventSource} from "../../UIEventSource"; import FilteredLayer from "../../../Models/FilteredLayer"; import {Utils} from "../../../Utils"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; -import {BBox} from "../../GeoOperations"; import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { diff --git a/Logic/FeatureSource/Sources/RememberingSource.ts b/Logic/FeatureSource/Sources/RememberingSource.ts index b31097061..683576736 100644 --- a/Logic/FeatureSource/Sources/RememberingSource.ts +++ b/Logic/FeatureSource/Sources/RememberingSource.ts @@ -4,7 +4,7 @@ */ import FeatureSource, {Tiled} from "../FeatureSource"; import {UIEventSource} from "../../UIEventSource"; -import {BBox} from "../../GeoOperations"; +import {BBox} from "../../BBox"; export default class RememberingSource implements FeatureSource , Tiled{ diff --git a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts index b3a476f5f..b06aae6d2 100644 --- a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts +++ b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts @@ -1,20 +1,22 @@ import {UIEventSource} from "../../UIEventSource"; import FilteredLayer from "../../../Models/FilteredLayer"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; -import {BBox} from "../../GeoOperations"; import {Utils} from "../../../Utils"; import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled { public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public readonly name: string = "SimpleFeatureSource"; public readonly layer: FilteredLayer; public readonly bbox: BBox = BBox.global; - public readonly tileIndex: number = Tiles.tile_index(0, 0, 0); + public readonly tileIndex: number; - constructor(layer: FilteredLayer) { + constructor(layer: FilteredLayer, tileIndex: number) { this.name = "SimpleFeatureSource(" + layer.layerDef.id + ")" this.layer = layer + this.tileIndex = tileIndex ?? 0; + this.bbox = BBox.fromTileIndex(this.tileIndex) } } \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts new file mode 100644 index 000000000..2bed33298 --- /dev/null +++ b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts @@ -0,0 +1,112 @@ +import {Utils} from "../../../Utils"; +import * as OsmToGeoJson from "osmtogeojson"; +import StaticFeatureSource from "../Sources/StaticFeatureSource"; +import PerLayerFeatureSourceSplitter from "../PerLayerFeatureSourceSplitter"; +import {UIEventSource} from "../../UIEventSource"; +import FilteredLayer from "../../../Models/FilteredLayer"; +import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; +import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; +import {OsmConnection} from "../../Osm/OsmConnection"; + +export default class OsmFeatureSource { + private readonly _backend: string; + + public readonly isRunning: UIEventSource = new UIEventSource(false) + private readonly filteredLayers: UIEventSource; + private readonly handleTile: (fs: (FeatureSourceForLayer & Tiled)) => void; + private isActive: UIEventSource; + private options: { + handleTile: (tile: FeatureSourceForLayer & Tiled) => void; + isActive: UIEventSource, + neededTiles: UIEventSource, + state: { + readonly osmConnection: OsmConnection; + }; + }; + private readonly downloadedTiles = new Set() + + constructor(options: { + handleTile: (tile: FeatureSourceForLayer & Tiled) => void; + isActive: UIEventSource, + neededTiles: UIEventSource, + state: { + readonly filteredLayers: UIEventSource; + readonly osmConnection: OsmConnection; + }; + }) { + this.options = options; + this._backend = options.state.osmConnection._oauth_config.url; + this.filteredLayers = options.state.filteredLayers.map(layers => layers.filter(layer => layer.layerDef.source.geojsonSource === undefined)) + this.handleTile = options.handleTile + this.isActive = options.isActive + const self = this + options.neededTiles.addCallbackAndRunD(neededTiles => { + if (options.isActive?.data === false) { + return; + } + + self.isRunning.setData(true) + try { + + for (const neededTile of neededTiles) { + if (self.downloadedTiles.has(neededTile)) { + return; + } + self.downloadedTiles.add(neededTile) + Promise.resolve(self.LoadTile(...Tiles.tile_from_index(neededTile)).then(_ => { + })) + } + } catch (e) { + console.error(e) + } + self.isRunning.setData(false) + }) + } + + private async LoadTile(z, x, y): Promise { + if (z > 18) { + throw "This is an absurd high zoom level" + } + + const bbox = BBox.fromTile(z, x, y) + const url = `${this._backend}/api/0.6/map?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}` + try { + + console.log("Attempting to get tile", z, x, y, "from the osm api") + const osmXml = await Utils.download(url, {"accept": "application/xml"}) + try { + const parsed = new DOMParser().parseFromString(osmXml, "text/xml"); + console.log("Got tile", z, x, y, "from the osm api") + const geojson = OsmToGeoJson.default(parsed, + // @ts-ignore + { + flatProperties: true + }); + console.log("Tile geojson:", z, x, y, "is", geojson) + new PerLayerFeatureSourceSplitter(this.filteredLayers, + this.handleTile, + new StaticFeatureSource(geojson.features, false), + { + tileIndex: Tiles.tile_index(z, x, y) + } + ); + } catch (e) { + console.error("Weird error: ", e) + } + } catch (e) { + console.error("Could not download tile", z, x, y, "due to", e, "; retrying with smaller bounds") + if (e === "rate limited") { + return; + } + await this.LoadTile(z + 1, x * 2, y * 2) + await this.LoadTile(z + 1, 1 + x * 2, y * 2) + await this.LoadTile(z + 1, x * 2, 1 + y * 2) + await this.LoadTile(z + 1, 1 + x * 2, 1 + y * 2) + return; + } + + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts index d905d2f65..f2e43069d 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts @@ -1,5 +1,5 @@ import FeatureSource, {Tiled} from "../FeatureSource"; -import {BBox} from "../../GeoOperations"; +import {BBox} from "../../BBox"; export default interface TileHierarchy { diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts index d8ab294ce..844754854 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts @@ -3,9 +3,9 @@ import {UIEventSource} from "../../UIEventSource"; import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; import FilteredLayer from "../../../Models/FilteredLayer"; import {Utils} from "../../../Utils"; -import {BBox} from "../../GeoOperations"; import FeatureSourceMerger from "../Sources/FeatureSourceMerger"; import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; export class TileHierarchyMerger implements TileHierarchy { public readonly loadedTiles: Map = new Map(); diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts index 2dcf3e6d2..4950ea328 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts @@ -1,10 +1,10 @@ import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; import {UIEventSource} from "../../UIEventSource"; import {Utils} from "../../../Utils"; -import {BBox} from "../../GeoOperations"; import FilteredLayer from "../../../Models/FilteredLayer"; import TileHierarchy from "./TileHierarchy"; import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; /** * Contains all features in a tiled fashion. @@ -109,7 +109,6 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource, // To much features - we split return featureCount > this.maxFeatureCount - } /*** @@ -143,7 +142,20 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource, for (const feature of features) { const bbox = BBox.get(feature.feature) - if (this.options.dontEnforceMinZoom || this.options.minZoomLevel === undefined) { + + if (this.options.dontEnforceMinZoom) { + if (bbox.overlapsWith(this.upper_left.bbox)) { + ulf.push(feature) + } else if (bbox.overlapsWith(this.upper_right.bbox)) { + urf.push(feature) + } else if (bbox.overlapsWith(this.lower_left.bbox)) { + llf.push(feature) + } else if (bbox.overlapsWith(this.lower_right.bbox)) { + lrf.push(feature) + } else { + overlapsboundary.push(feature) + } + }else if (this.options.minZoomLevel === undefined) { if (bbox.isContainedIn(this.upper_left.bbox)) { ulf.push(feature) } else if (bbox.isContainedIn(this.upper_right.bbox)) { diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts index 3e879bd4a..4b578b7da 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -5,12 +5,13 @@ import Loc from "../../../Models/Loc"; import TileHierarchy from "./TileHierarchy"; import {Utils} from "../../../Utils"; import SaveTileToLocalStorageActor from "../Actors/SaveTileToLocalStorageActor"; -import {BBox} from "../../GeoOperations"; import {Tiles} from "../../../Models/TileRange"; +import {BBox} from "../../BBox"; export default class TiledFromLocalStorageSource implements TileHierarchy { public loadedTiles: Map = new Map(); - +public tileFreshness : Map = new Map() + constructor(layer: FilteredLayer, handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void, state: { @@ -29,7 +30,14 @@ export default class TiledFromLocalStorageSource implements TileHierarchy Tiles.tile_from_index(i).join("/")).join(", ")) + console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Tiles.tile_from_index(i).join("/")).join(", ")) + for (const index of indexes) { + const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" +index+"-time"; + const data = Number(localStorage.getItem(prefix)) + const freshness = new Date() + freshness.setTime(data) + this.tileFreshness.set(index, freshness) + } const zLevels = indexes.map(i => i % 100) const indexesSet = new Set(indexes) @@ -72,7 +80,7 @@ export default class TiledFromLocalStorageSource implements TileHierarchy console.log("Tiles to load from localstorage:", t)) + neededTiles.addCallbackAndRun(t => console.debug("Tiles to load from localstorage:", t)) neededTiles.addCallbackAndRunD(neededIndexes => { for (const neededIndex of neededIndexes) { diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index cfef09e5d..3fa10f7a2 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -1,6 +1,5 @@ import * as turf from '@turf/turf' -import {Utils} from "../Utils"; -import {Tiles} from "../Models/TileRange"; +import {BBox} from "./BBox"; export class GeoOperations { @@ -379,135 +378,3 @@ export class GeoOperations { } -export class BBox { - - readonly maxLat: number; - readonly maxLon: number; - readonly minLat: number; - readonly minLon: number; - static global: BBox = new BBox([[-180, -90], [180, 90]]); - - constructor(coordinates) { - this.maxLat = -90; - this.maxLon = -180; - this.minLat = 90; - this.minLon = 180; - - - for (const coordinate of coordinates) { - this.maxLon = Math.max(this.maxLon, coordinate[0]); - this.maxLat = Math.max(this.maxLat, coordinate[1]); - this.minLon = Math.min(this.minLon, coordinate[0]); - this.minLat = Math.min(this.minLat, coordinate[1]); - } - this.check(); - } - - static fromLeafletBounds(bounds) { - return new BBox([[bounds.getWest(), bounds.getNorth()], [bounds.getEast(), bounds.getSouth()]]) - } - - static get(feature): BBox { - if (feature.bbox?.overlapsWith === undefined) { - const turfBbox: number[] = turf.bbox(feature) - feature.bbox = new BBox([[turfBbox[0], turfBbox[1]], [turfBbox[2], turfBbox[3]]]); - } - return feature.bbox; - } - - public overlapsWith(other: BBox) { - if (this.maxLon < other.minLon) { - return false; - } - if (this.maxLat < other.minLat) { - return false; - } - if (this.minLon > other.maxLon) { - return false; - } - return this.minLat <= other.maxLat; - - } - - public isContainedIn(other: BBox) { - if (this.maxLon > other.maxLon) { - return false; - } - if (this.maxLat > other.maxLat) { - return false; - } - if (this.minLon < other.minLon) { - return false; - } - if (this.minLat < other.minLat) { - return false - } - return true; - } - - private check() { - if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) { - console.log(this); - throw "BBOX has NAN"; - } - } - - static fromTile(z: number, x: number, y: number): BBox { - return new BBox(Tiles.tile_bounds_lon_lat(z, x, y)) - } - - static fromTileIndex(i: number): BBox { - return BBox.fromTile(...Tiles.tile_from_index(i)) - } - - getEast() { - return this.maxLon - } - - getNorth() { - return this.maxLat - } - - getWest() { - return this.minLon - } - - getSouth() { - return this.minLat - } - - pad(factor: number) : BBox { - const latDiff = this.maxLat - this.minLat - const lat = (this.maxLat + this.minLat) / 2 - const lonDiff = this.maxLon - this.minLon - const lon = (this.maxLon + this.minLon) / 2 - return new BBox([[ - lon - lonDiff * factor, - lat - latDiff * factor - ], [lon + lonDiff * factor, - lat + latDiff * factor]]) - } - - toLeaflet() { - return [[this.minLat, this.minLon], [this.maxLat, this.maxLon]] - } - - asGeoJson(properties: any) : any{ - return { - type:"Feature", - properties: properties, - geometry:{ - type:"Polygon", - coordinates:[[ - - [this.minLon, this.minLat], - [this.maxLon, this.minLat], - [this.maxLon, this.maxLat], - [this.minLon, this.maxLat], - [this.minLon, this.minLat], - - ]] - } - } - } -} \ No newline at end of file diff --git a/Logic/Osm/OsmConnection.ts b/Logic/Osm/OsmConnection.ts index 5d955eb04..4e1a9297a 100644 --- a/Logic/Osm/OsmConnection.ts +++ b/Logic/Osm/OsmConnection.ts @@ -62,26 +62,26 @@ export class OsmConnection { }; private isChecking = false; - constructor(dryRun: boolean, - fakeUser: boolean, + constructor(options:{dryRun?: false | boolean, + fakeUser?: false | boolean, allElements: ElementStorage, changes: Changes, - oauth_token: UIEventSource, + oauth_token?: UIEventSource, // Used to keep multiple changesets open and to write to the correct changeset layoutName: string, - singlePage: boolean = true, - osmConfiguration: "osm" | "osm-test" = 'osm' + singlePage?: boolean, + osmConfiguration?: "osm" | "osm-test" } ) { - this.fakeUser = fakeUser; - this._singlePage = singlePage; - this._oauth_config = OsmConnection.oauth_configs[osmConfiguration] ?? OsmConnection.oauth_configs.osm; + this.fakeUser = options.fakeUser ?? false; + this._singlePage = options.singlePage ?? true; + this._oauth_config = OsmConnection.oauth_configs[options.osmConfiguration ?? 'osm'] ?? OsmConnection.oauth_configs.osm; console.debug("Using backend", this._oauth_config.url) OsmObject.SetBackendUrl(this._oauth_config.url + "/") this._iframeMode = Utils.runningFromConsole ? false : window !== window.top; this.userDetails = new UIEventSource(new UserDetails(this._oauth_config.url), "userDetails"); - this.userDetails.data.dryRun = dryRun || fakeUser; - if (fakeUser) { + this.userDetails.data.dryRun = (options.dryRun ?? false) || (options.fakeUser ?? false) ; + if (options.fakeUser) { const ud = this.userDetails.data; ud.csCount = 5678 ud.loggedIn = true; @@ -98,23 +98,23 @@ export class OsmConnection { } }); this.isLoggedIn.addCallbackAndRunD(li => console.log("User is logged in!", li)) - this._dryRun = dryRun; + this._dryRun = options.dryRun; this.updateAuthObject(); this.preferencesHandler = new OsmPreferences(this.auth, this); - this.changesetHandler = new ChangesetHandler(layoutName, dryRun, this, allElements, changes, this.auth); - if (oauth_token.data !== undefined) { - console.log(oauth_token.data) + this.changesetHandler = new ChangesetHandler(options.layoutName, options.dryRun, this, options.allElements, options.changes, this.auth); + if (options.oauth_token?.data !== undefined) { + console.log(options.oauth_token.data) const self = this; - this.auth.bootstrapToken(oauth_token.data, + this.auth.bootstrapToken(options.oauth_token.data, (x) => { console.log("Called back: ", x) self.AttemptLogin(); }, this.auth); - oauth_token.setData(undefined); + options. oauth_token.setData(undefined); } if (this.auth.authenticated()) { diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index d95e0fa49..4538622d1 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -1,7 +1,7 @@ import {Utils} from "../../Utils"; import * as polygon_features from "../../assets/polygon-features.json"; import {UIEventSource} from "../UIEventSource"; -import {BBox} from "../GeoOperations"; +import {BBox} from "../BBox"; export abstract class OsmObject { diff --git a/Logic/Osm/Overpass.ts b/Logic/Osm/Overpass.ts index de6b65287..2bf1b4e70 100644 --- a/Logic/Osm/Overpass.ts +++ b/Logic/Osm/Overpass.ts @@ -16,8 +16,8 @@ export class Overpass { private readonly _extraScripts: string[]; private _includeMeta: boolean; private _relationTracker: RelationsTracker; - - + + constructor(filter: TagsFilter, extraScripts: string[], interpreterUrl: UIEventSource, timeout: UIEventSource, @@ -41,10 +41,13 @@ export class Overpass { } const self = this; const json = await Utils.downloadJson(query) - - if (json.elements === [] && ((json.remarks ?? json.remark).indexOf("runtime error") >= 0)) { - console.log("Timeout or other runtime error"); - throw("Runtime error (timeout)") + console.log("Got json!", json) + if (json.elements.length === 0 && json.remark !== undefined) { + console.warn("Timeout or other runtime error while querying overpass", json.remark); + throw `Runtime error (timeout or similar)${json.remark}` + } + if(json.elements.length === 0){ + console.warn("No features for" ,json) } self._relationTracker.RegisterRelations(json) diff --git a/Models/TileRange.ts b/Models/TileRange.ts index 215d5a76f..b61f192a0 100644 --- a/Models/TileRange.ts +++ b/Models/TileRange.ts @@ -86,7 +86,7 @@ export class Tiles { static embedded_tile(lat: number, lon: number, z: number): { x: number, y: number, z: number } { return {x: Tiles.lon2tile(lon, z), y: Tiles.lat2tile(lat, z), z: z} } - + static TileRangeBetween(zoomlevel: number, lat0: number, lon0: number, lat1: number, lon1: number): TileRange { const t0 = Tiles.embedded_tile(lat0, lon0, zoomlevel) const t1 = Tiles.embedded_tile(lat1, lon1, zoomlevel) diff --git a/State.ts b/State.ts index 04b9728b3..85dfc9cfd 100644 --- a/State.ts +++ b/State.ts @@ -17,7 +17,7 @@ import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; import FilteredLayer from "./Models/FilteredLayer"; import ChangeToElementsActor from "./Logic/Actors/ChangeToElementsActor"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; -import {BBox} from "./Logic/GeoOperations"; +import {BBox} from "./Logic/BBox"; /** * Contains the global state: a bunch of UI-event sources @@ -83,7 +83,7 @@ export default class State { public readonly featureSwitchExportAsPdf: UIEventSource; public readonly overpassUrl: UIEventSource; public readonly overpassTimeout: UIEventSource; - public readonly overpassMaxZoom: UIEventSource = new UIEventSource(undefined); + public readonly overpassMaxZoom: UIEventSource = new UIEventSource(20); public featurePipeline: FeaturePipeline; @@ -97,7 +97,7 @@ export default class State { * The current visible extent of the screen */ public readonly currentBounds = new UIEventSource(undefined) - + public backgroundLayer; public readonly backgroundLayerId: UIEventSource; @@ -372,23 +372,21 @@ export default class State { return; } - this.osmConnection = new OsmConnection( - this.featureSwitchIsTesting.data, - this.featureSwitchFakeUser.data, - this.allElements, - this.changes, - QueryParameters.GetQueryParameter( + this.osmConnection = new OsmConnection({ + changes: this.changes, + dryRun: this.featureSwitchIsTesting.data, + fakeUser: this.featureSwitchFakeUser.data, + allElements: this.allElements, + oauth_token: QueryParameters.GetQueryParameter( "oauth_token", undefined, "Used to complete the login" ), - layoutToUse?.id, - true, - // @ts-ignore - this.featureSwitchApiURL.data - ); + layoutName: layoutToUse?.id, + osmConfiguration: <'osm' | 'osm-test'>this.featureSwitchApiURL.data + }) + - new ChangeToElementsActor(this.changes, this.allElements) new PendingChangesUploader(this.changes, this.selectedElement); diff --git a/UI/Base/Minimap.ts b/UI/Base/Minimap.ts index e276c8281..32cdadbd6 100644 --- a/UI/Base/Minimap.ts +++ b/UI/Base/Minimap.ts @@ -1,8 +1,8 @@ import BaseUIElement from "../BaseUIElement"; import Loc from "../../Models/Loc"; import BaseLayer from "../../Models/BaseLayer"; -import {BBox} from "../../Logic/GeoOperations"; import {UIEventSource} from "../../Logic/UIEventSource"; +import {BBox} from "../../Logic/BBox"; export interface MinimapOptions { background?: UIEventSource, diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts index 1761d1ada..110235437 100644 --- a/UI/Base/MinimapImplementation.ts +++ b/UI/Base/MinimapImplementation.ts @@ -4,10 +4,10 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import Loc from "../../Models/Loc"; import BaseLayer from "../../Models/BaseLayer"; import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; -import {BBox} from "../../Logic/GeoOperations"; import * as L from "leaflet"; import {Map} from "leaflet"; import Minimap, {MinimapObj, MinimapOptions} from "./Minimap"; +import {BBox} from "../../Logic/BBox"; export default class MinimapImplementation extends BaseUIElement implements MinimapObj { private static _nextId = 0; diff --git a/UI/BigComponents/Attribution.ts b/UI/BigComponents/Attribution.ts index 7e70fa367..9b1ddb013 100644 --- a/UI/BigComponents/Attribution.ts +++ b/UI/BigComponents/Attribution.ts @@ -7,7 +7,7 @@ import Constants from "../../Models/Constants"; import Loc from "../../Models/Loc"; import {VariableUiElement} from "../Base/VariableUIElement"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; -import {BBox} from "../../Logic/GeoOperations"; +import {BBox} from "../../Logic/BBox"; /** * The bottom right attribution panel in the leaflet map diff --git a/UI/BigComponents/DownloadPanel.ts b/UI/BigComponents/DownloadPanel.ts index 746715826..500d28e19 100644 --- a/UI/BigComponents/DownloadPanel.ts +++ b/UI/BigComponents/DownloadPanel.ts @@ -5,7 +5,7 @@ import State from "../../State"; import {Utils} from "../../Utils"; import Combine from "../Base/Combine"; import CheckBoxes from "../Input/Checkboxes"; -import {BBox, GeoOperations} from "../../Logic/GeoOperations"; +import {GeoOperations} from "../../Logic/GeoOperations"; import Toggle from "../Input/Toggle"; import Title from "../Base/Title"; import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; @@ -13,6 +13,7 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import SimpleMetaTagger from "../../Logic/SimpleMetaTagger"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import {meta} from "@turf/turf"; +import {BBox} from "../../Logic/BBox"; export class DownloadPanel extends Toggle { diff --git a/UI/BigComponents/FullWelcomePaneWithTabs.ts b/UI/BigComponents/FullWelcomePaneWithTabs.ts index a4a3e826a..f6574f04b 100644 --- a/UI/BigComponents/FullWelcomePaneWithTabs.ts +++ b/UI/BigComponents/FullWelcomePaneWithTabs.ts @@ -1,7 +1,5 @@ import State from "../../State"; import ThemeIntroductionPanel from "./ThemeIntroductionPanel"; -import * as personal from "../../assets/themes/personal/personal.json"; -import PersonalLayersPanel from "./PersonalLayersPanel"; import Svg from "../../Svg"; import Translations from "../i18n/Translations"; import ShareScreen from "./ShareScreen"; @@ -32,9 +30,7 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { private static ConstructBaseTabs(layoutToUse: LayoutConfig, isShown: UIEventSource): { header: string | BaseUIElement; content: BaseUIElement }[] { let welcome: BaseUIElement = new ThemeIntroductionPanel(isShown); - if (layoutToUse.id === personal.id) { - welcome = new PersonalLayersPanel(); - } + const tabs: { header: string | BaseUIElement, content: BaseUIElement }[] = [ {header: ``, content: welcome}, { diff --git a/UI/BigComponents/LeftControls.ts b/UI/BigComponents/LeftControls.ts index b3e848d4d..7ee2a74e6 100644 --- a/UI/BigComponents/LeftControls.ts +++ b/UI/BigComponents/LeftControls.ts @@ -11,8 +11,8 @@ import AllDownloads from "./AllDownloads"; import FilterView from "./FilterView"; import {UIEventSource} from "../../Logic/UIEventSource"; import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; -import {BBox} from "../../Logic/GeoOperations"; import Loc from "../../Models/Loc"; +import {BBox} from "../../Logic/BBox"; export default class LeftControls extends Combine { diff --git a/UI/BigComponents/PersonalLayersPanel.ts b/UI/BigComponents/PersonalLayersPanel.ts deleted file mode 100644 index e5a41b733..000000000 --- a/UI/BigComponents/PersonalLayersPanel.ts +++ /dev/null @@ -1,120 +0,0 @@ -import {AllKnownLayouts} from "../../Customizations/AllKnownLayouts"; -import Svg from "../../Svg"; -import State from "../../State"; -import Combine from "../Base/Combine"; -import Toggle from "../Input/Toggle"; -import {SubtleButton} from "../Base/SubtleButton"; -import Translations from "../i18n/Translations"; -import BaseUIElement from "../BaseUIElement"; -import {VariableUiElement} from "../Base/VariableUIElement"; -import {UIEventSource} from "../../Logic/UIEventSource"; -import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; - -export default class PersonalLayersPanel extends VariableUiElement { - - constructor() { - super( - State.state.installedThemes.map(installedThemes => { - const t = Translations.t.favourite; - - // Lets get all the layers - const allThemes = AllKnownLayouts.layoutsList.concat(installedThemes.map(layout => layout.layout)) - .filter(theme => !theme.hideFromOverview) - - const allLayers = [] - { - const seenLayers = new Set() - for (const layers of allThemes.map(theme => theme.layers)) { - for (const layer of layers) { - if (seenLayers.has(layer.id)) { - continue - } - seenLayers.add(layer.id) - allLayers.push(layer) - } - } - } - - // Time to create a panel based on them! - const panel: BaseUIElement = new Combine(allLayers.map(PersonalLayersPanel.CreateLayerToggle)); - - - return new Toggle( - new Combine([ - t.panelIntro.Clone(), - panel - ]).SetClass("flex flex-col"), - new SubtleButton( - Svg.osm_logo_ui(), - t.loginNeeded.Clone().SetClass("text-center") - ).onClick(() => State.state.osmConnection.AttemptLogin()), - State.state.osmConnection.isLoggedIn - ) - }) - ) - } - - /*** - * Creates a toggle for the given layer, which'll update State.state.favouriteLayers right away - * @param layer - * @constructor - * @private - */ - private static CreateLayerToggle(layer: LayerConfig): Toggle { - let icon: BaseUIElement = new Combine([layer.GenerateLeafletStyle( - new UIEventSource({id: "node/-1"}), - false - ).icon.html]).SetClass("relative") - let iconUnset = new Combine([layer.GenerateLeafletStyle( - new UIEventSource({id: "node/-1"}), - false - ).icon.html]).SetClass("relative") - - iconUnset.SetStyle("opacity:0.1") - - let name = layer.name; - if (name === undefined) { - return undefined; - } - const content = new Combine([ - Translations.WT(name).Clone().SetClass("font-bold"), - Translations.WT(layer.description)?.Clone() - ]).SetClass("flex flex-col") - - const contentUnselected = new Combine([ - Translations.WT(name).Clone().SetClass("font-bold"), - Translations.WT(layer.description)?.Clone() - ]).SetClass("flex flex-col line-through") - - return new Toggle( - new SubtleButton( - icon, - content), - new SubtleButton( - iconUnset, - contentUnselected - ), - State.state.favouriteLayers.map(favLayers => { - return favLayers.indexOf(layer.id) >= 0 - }, [], (selected, current) => { - if (!selected && current.indexOf(layer.id) <= 0) { - // Not selected and not contained: nothing to change: we return current as is - return current; - } - if (selected && current.indexOf(layer.id) >= 0) { - // Selected and contained: this is fine! - return current; - } - const clone = [...current] - if (selected) { - clone.push(layer.id) - } else { - clone.splice(clone.indexOf(layer.id), 1) - } - return clone - }) - ).ToggleOnClick(); - } - - -} \ No newline at end of file diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 5dca2cfc2..ca758ab80 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -20,7 +20,7 @@ import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; import PresetConfig from "../../Models/ThemeConfig/PresetConfig"; import FilteredLayer from "../../Models/FilteredLayer"; import {And} from "../../Logic/Tags/And"; -import {BBox} from "../../Logic/GeoOperations"; +import {BBox} from "../../Logic/BBox"; /* * The SimpleAddUI is a single panel, which can have multiple states: diff --git a/UI/ExportPDF.ts b/UI/ExportPDF.ts index f1a113daa..54879b9b2 100644 --- a/UI/ExportPDF.ts +++ b/UI/ExportPDF.ts @@ -5,7 +5,6 @@ import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter"; import {UIEventSource} from "../Logic/UIEventSource"; import Minimap from "./Base/Minimap"; import Loc from "../Models/Loc"; -import {BBox} from "../Logic/GeoOperations"; import BaseLayer from "../Models/BaseLayer"; import {FixedUiElement} from "./Base/FixedUiElement"; import Translations from "./i18n/Translations"; @@ -14,6 +13,7 @@ import Constants from "../Models/Constants"; import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline"; import ShowDataLayer from "./ShowDataLayer/ShowDataLayer"; +import {BBox} from "../Logic/BBox"; /** * Creates screenshoter to take png screenshot * Creates jspdf and downloads it diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index 0621eccc8..262d9a7db 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -7,11 +7,12 @@ import Combine from "../Base/Combine"; import Svg from "../../Svg"; import State from "../../State"; import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; -import {BBox, GeoOperations} from "../../Logic/GeoOperations"; +import {GeoOperations} from "../../Logic/GeoOperations"; import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import {BBox} from "../../Logic/BBox"; export default class LocationInput extends InputElement { diff --git a/UI/Popup/SplitRoadWizard.ts b/UI/Popup/SplitRoadWizard.ts index b1df0a5fa..17e1bd013 100644 --- a/UI/Popup/SplitRoadWizard.ts +++ b/UI/Popup/SplitRoadWizard.ts @@ -5,7 +5,7 @@ import {SubtleButton} from "../Base/SubtleButton"; import Minimap from "../Base/Minimap"; import State from "../../State"; import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; -import {BBox, GeoOperations} from "../../Logic/GeoOperations"; +import {GeoOperations} from "../../Logic/GeoOperations"; import {LeafletMouseEvent} from "leaflet"; import Combine from "../Base/Combine"; import {Button} from "../Base/Button"; @@ -15,6 +15,7 @@ import Title from "../Base/Title"; import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; +import {BBox} from "../../Logic/BBox"; export default class SplitRoadWizard extends Toggle { private static splitLayerStyling = new LayerConfig({ diff --git a/UI/ShowDataLayer/PerTileCountAggregator.ts b/UI/ShowDataLayer/PerTileCountAggregator.ts index e4085f2cd..f313e741f 100644 --- a/UI/ShowDataLayer/PerTileCountAggregator.ts +++ b/UI/ShowDataLayer/PerTileCountAggregator.ts @@ -1,9 +1,9 @@ import FeatureSource, {FeatureSourceForLayer, Tiled} from "../../Logic/FeatureSource/FeatureSource"; -import {BBox} from "../../Logic/GeoOperations"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; import {UIEventSource} from "../../Logic/UIEventSource"; import {Tiles} from "../../Models/TileRange"; import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; +import {BBox} from "../../Logic/BBox"; export class TileHierarchyAggregator implements FeatureSource { private _parent: TileHierarchyAggregator; diff --git a/Utils.ts b/Utils.ts index 477598f79..aced008ad 100644 --- a/Utils.ts +++ b/Utils.ts @@ -192,11 +192,12 @@ export class Utils { } /** - * Copies all key-value pairs of the source into the target. + * Copies all key-value pairs of the source into the target. This will change the target * If the key starts with a '+', the values of the list will be appended to the target instead of overwritten * @param source * @param target * @constructor + * @return the second parameter as is */ static Merge(source: any, target: any) { for (const key in source) { @@ -288,15 +289,13 @@ export class Utils { } - private static injectedDownloads = {} public static injectJsonDownloadForTests(url: string, data) { Utils.injectedDownloads[url] = data } - public static downloadJson(url: string, headers?: any): Promise { - + public static download(url: string, headers?: any): Promise { const injected = Utils.injectedDownloads[url] if (injected !== undefined) { console.log("Using injected resource for test for URL", url) @@ -311,17 +310,14 @@ export class Utils { const xhr = new XMLHttpRequest(); xhr.onload = () => { if (xhr.status == 200) { - try { - resolve(JSON.parse(xhr.response)) - } catch (e) { - reject("Not a valid json: " + xhr.response) - } + resolve(xhr.response) + } else if (xhr.status === 509 || xhr.status === 429){ + reject("rate limited") } else { reject(xhr.statusText) } }; xhr.open('GET', url); - xhr.setRequestHeader("accept", "application/json") if (headers !== undefined) { for (const key in headers) { @@ -334,6 +330,11 @@ export class Utils { ) } + public static async downloadJson(url: string, headers?: any): Promise { + const data = await Utils.download(url, Utils.Merge({"accept": "application/json"}, headers ?? {})) + return JSON.parse(data) + } + /** * Triggers a 'download file' popup which will download the contents */ diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index 571ff46e2..d7930f854 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -24,7 +24,7 @@ ] }, { - "#": "if sport is defined and is not bicycle, it is retrackted; if bicycle retail/repair is marked as 'no', it is retracted too.", + "#": "if sport is defined and is not bicycle, it is not matched; if bicycle retail/repair is marked as 'no', it is not shown to too.", "##": "There will be a few false-positives with this. They will get filtered out by people marking both 'not selling bikes' and 'not repairing bikes'. Furthermore, the OSMers will add a sports-subcategory on it", "and": [ "shop=sports", @@ -38,13 +38,6 @@ ] } ] - }, - { - "#": "Any shop with any bicycle service", - "and": [ - "shop~*", - "service:bicycle:.*~~.*" - ] } ] } diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index c8ee81894..da9b3ae90 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -27,7 +27,7 @@ "mappings": [ { "if": "wheelchair=yes", - "then": "./assets/layers/toilet/wheelchair.svg" + "then": "circle:white;./assets/layers/toilet/wheelchair.svg" }, { "if": { diff --git a/assets/layers/toilet/wheelchair.svg b/assets/layers/toilet/wheelchair.svg index 929ea6c40..d113492ad 100644 --- a/assets/layers/toilet/wheelchair.svg +++ b/assets/layers/toilet/wheelchair.svg @@ -2,76 +2,71 @@ image/svg+xml + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + id="defs9" /> + 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="namedview7" + showgrid="false" + inkscape:zoom="0.8559553" + inkscape:cx="-16.568588" + inkscape:cy="292.29436" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer2" /> - + inkscape:groupmode="layer" + id="layer2" + inkscape:label="background"> + + inkscape:groupmode="layer" + id="layer1" + inkscape:label="wheelchair"> \ No newline at end of file + inkscape:connector-curvature="0" + style="clip-rule:evenodd;fill:#000000;fill-rule:evenodd;stroke-width:0.66635805" + d="m 310.84431,395.24795 c -20.28873,40.10642 -62.75413,66.52908 -108.0502,66.52908 -66.52908,0 -120.790412,-54.26133 -120.790412,-120.79041 0,-46.71209 28.310452,-90.1207 70.555212,-109.36341 l 2.73376,35.67521 c -24.98647,15.74498 -40.3895,44.15435 -40.3895,73.93288 0,48.26215 39.36263,87.62413 87.62413,87.62413 44.15407,0 81.80523,-33.88535 86.93958,-77.35545 z" + id="path4" /> \ No newline at end of file diff --git a/assets/themes/bicyclelib/bicyclelib.json b/assets/themes/bicyclelib/bicyclelib.json index 3a8fb3e8e..c0c85b13b 100644 --- a/assets/themes/bicyclelib/bicyclelib.json +++ b/assets/themes/bicyclelib/bicyclelib.json @@ -43,6 +43,12 @@ "widenFactor": 1.5, "roamingRenderings": [], "layers": [ - "bicycle_library" + { + "builtin": "bicycle_library", + "override": { + "minZoom": 0 + } + }, + ] } \ No newline at end of file diff --git a/assets/themes/personal/personal.json b/assets/themes/personal/personal.json index d9df14a7b..88296088d 100644 --- a/assets/themes/personal/personal.json +++ b/assets/themes/personal/personal.json @@ -12,7 +12,7 @@ "zh_Hant": "個人化主題" }, "description": { - "en": "Create a personal theme based on all the available layers of all themes", + "en": "Create a personal theme based on all the available layers of all themes. Open the layer selection to select one or more layers.", "nl": "Stel je eigen thema samen door lagen te combineren van alle andere themas", "es": "Crea una interficie basada en todas las capas disponibles de todas las interficies", "ca": "Crea una interfície basada en totes les capes disponibles de totes les interfícies", @@ -37,11 +37,14 @@ ], "maintainer": "MapComplete", "icon": "./assets/svg/addSmall.svg", + "clustering": { + "maxZoom": 19 + }, "version": "0", "startLat": 0, "startLon": 0, "startZoom": 16, - "widenFactor": 3, + "widenFactor": 1.2, "layers": [], "roamingRenderings": [] } \ No newline at end of file diff --git a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json index c09b548e8..7bef2f129 100644 --- a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json +++ b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json @@ -20,7 +20,7 @@ "startZoom": 8, "startLat": 50.8536, "startLon": 4.433, - "widenFactor": 2, + "widenFactor": 1.5, "layers": [ { "builtin": [ diff --git a/preferences.ts b/preferences.ts index 310892003..c96912b5d 100644 --- a/preferences.ts +++ b/preferences.ts @@ -10,9 +10,17 @@ import LZString from "lz-string"; import BaseUIElement from "./UI/BaseUIElement"; import Table from "./UI/Base/Table"; import {LayoutConfigJson} from "./Models/ThemeConfig/Json/LayoutConfigJson"; +import {Changes} from "./Logic/Osm/Changes"; +import {ElementStorage} from "./Logic/ElementStorage"; -const connection = new OsmConnection(false, false, new UIEventSource(undefined), ""); +const connection = new OsmConnection({ + osmConfiguration: 'osm', + changes: new Changes(), + layoutName: '', + allElements: new ElementStorage() +}); + let rendered = false; @@ -20,6 +28,7 @@ function salvageThemes(preferences: any) { const knownThemeNames = new Set(); const correctThemeNames = [] for (const key in preferences) { + try{ if (!(typeof key === "string")) { continue; } @@ -36,7 +45,7 @@ function salvageThemes(preferences: any) { } else { knownThemeNames.add(theme); } - } + }catch(e){console.error(e)}} for (const correctThemeName of correctThemeNames) { knownThemeNames.delete(correctThemeName); @@ -65,8 +74,13 @@ function salvageThemes(preferences: any) { try { jsonObject = JSON.parse(atob(combined)); } catch (e) { + try{ + // We try to decode with lz-string jsonObject = JSON.parse(Utils.UnMinify(LZString.decompressFromBase64(combined))) as LayoutConfigJson; + }catch(e0){ + console.log("Could not salvage theme. Initial parsing failed due to:", e,"\nWith LZ failed due ", e0) + } } diff --git a/test.ts b/test.ts index c70e45da5..e758de8cd 100644 --- a/test.ts +++ b/test.ts @@ -1,33 +1,35 @@ -import SplitRoadWizard from "./UI/Popup/SplitRoadWizard"; -import State from "./State"; +import {Tiles} from "./Models/TileRange"; +import OsmFeatureSource from "./Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource"; +import {Utils} from "./Utils"; import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; -import MinimapImplementation from "./UI/Base/MinimapImplementation"; -import {UIEventSource} from "./Logic/UIEventSource"; -import FilteredLayer from "./Models/FilteredLayer"; -import {And} from "./Logic/Tags/And"; -import ShowDataLayer from "./UI/ShowDataLayer/ShowDataLayer"; -import ShowTileInfo from "./UI/ShowDataLayer/ShowTileInfo"; -import StaticFeatureSource from "./Logic/FeatureSource/Sources/StaticFeatureSource"; -import {BBox} from "./Logic/GeoOperations"; -import Minimap from "./UI/Base/Minimap"; +import LayerConfig from "./Models/ThemeConfig/LayerConfig"; -State.state = new State(undefined) +const allLayers: LayerConfig[] = [] +const seenIds = new Set() +for (const layoutConfig of AllKnownLayouts.layoutsList) { + if (layoutConfig.hideFromOverview) { + continue + } + for (const layer of layoutConfig.layers) { + if (seenIds.has(layer.id)) { + continue + } + seenIds.add(layer.id) + allLayers.push(layer) + } +} -const leafletMap = new UIEventSource(undefined) -MinimapImplementation.initialize() -Minimap.createMiniMap({ - leafletMap: leafletMap, -}).SetStyle("height: 600px; width: 600px") - .AttachTo("maindiv") +console.log("All layer ids", allLayers.map(l => l.id)) -const bbox = BBox.fromTile(16,32754,21785).asGeoJson({ - count: 42, - tileId: 42 +const src = new OsmFeatureSource({ + backend: "https://www.openstreetmap.org", + handleTile: tile => console.log("Got tile", tile), + allLayers: allLayers }) - -console.log(bbox) -new ShowDataLayer({ - layerToShow: ShowTileInfo.styling, - leafletMap: leafletMap, - features: new StaticFeatureSource([ bbox], false) -}) \ No newline at end of file +src.LoadTile(16, 33354, 21875).then(geojson => { + console.log("Got geojson", geojson); + Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson), "test.geojson", { + mimetype: "application/vnd.geo+json" + }) +}) +//*/ \ No newline at end of file diff --git a/test/GeoOperations.spec.ts b/test/GeoOperations.spec.ts index 372402648..56e07acaf 100644 --- a/test/GeoOperations.spec.ts +++ b/test/GeoOperations.spec.ts @@ -1,8 +1,9 @@ import {Utils} from "../Utils"; import * as Assert from "assert"; import T from "./TestHelper"; -import {BBox, GeoOperations} from "../Logic/GeoOperations"; +import {GeoOperations} from "../Logic/GeoOperations"; import {equal} from "assert"; +import {BBox} from "../Logic/BBox"; Utils.runningFromConsole = true; diff --git a/test/OsmConnection.spec.ts b/test/OsmConnection.spec.ts index 0b6cf3b47..9d7b114f5 100644 --- a/test/OsmConnection.spec.ts +++ b/test/OsmConnection.spec.ts @@ -2,6 +2,9 @@ import T from "./TestHelper"; import UserDetails, {OsmConnection} from "../Logic/Osm/OsmConnection"; import {UIEventSource} from "../Logic/UIEventSource"; import ScriptUtils from "../scripts/ScriptUtils"; +import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; +import {ElementStorage} from "../Logic/ElementStorage"; +import {Changes} from "../Logic/Osm/Changes"; export default class OsmConnectionSpec extends T { @@ -15,12 +18,14 @@ export default class OsmConnectionSpec extends T { super("osmconnection", [ ["login on dev", () => { - const osmConn = new OsmConnection(false, false, - new UIEventSource(undefined), - "Unit test", - true, - "osm-test" - ) + const osmConn = new OsmConnection({ + osmConfiguration: "osm-test", + layoutName: "Unit test", + allElements: new ElementStorage(), + changes: new Changes(), + oauth_token: new UIEventSource(OsmConnectionSpec._osm_token) + } + ); osmConn.userDetails.map((userdetails: UserDetails) => { if (userdetails.loggedIn) { From a78d33112bfb7d801f3c561943a376b18fe350e6 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 28 Sep 2021 18:00:44 +0200 Subject: [PATCH 040/110] Refactoring: LayoutToUse is a simple value now --- InitUiElements.ts | 116 +++++++++--------- Logic/Actors/BackgroundLayerResetter.ts | 6 +- Logic/Actors/OverpassFeatureSource.ts | 13 +- Logic/Actors/TitleHandler.ts | 12 +- Logic/FeatureSource/FeaturePipeline.ts | 10 +- .../TiledFeatureSource/OsmFeatureSource.ts | 2 +- Logic/Osm/Actions/DeleteAction.ts | 2 +- Logic/Osm/Changes.ts | 2 +- Logic/SimpleMetaTagger.ts | 2 +- State.ts | 46 +++---- UI/BigComponents/Attribution.ts | 4 +- UI/BigComponents/AttributionPanel.ts | 8 +- UI/BigComponents/DownloadPanel.ts | 2 +- UI/BigComponents/FullWelcomePaneWithTabs.ts | 2 +- UI/BigComponents/MoreScreen.ts | 2 +- UI/BigComponents/ShareScreen.ts | 2 +- UI/BigComponents/ThemeIntroductionPanel.ts | 15 +-- UI/BigComponents/UserBadge.ts | 2 +- UI/Image/ImageUploadFlow.ts | 2 +- UI/Input/DropDown.ts | 2 +- UI/Input/LocationInput.ts | 21 ++-- UI/SpecialVisualizations.ts | 13 +- 22 files changed, 133 insertions(+), 153 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index 202e64149..40932dfd2 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -323,9 +323,7 @@ export class InitUiElements { State.state.backgroundLayer, State.state.locationControl, State.state.availableBackgroundLayers, - State.state.layoutToUse.map( - (layout: LayoutConfig) => layout.defaultBackgroundId - ) + State.state.layoutToUse.defaultBackgroundId ); const attr = new Attribution( @@ -345,7 +343,7 @@ export class InitUiElements { }).SetClass("w-full h-full") .AttachTo("leafletDiv") - const layout = State.state.layoutToUse.data; + const layout = State.state.layoutToUse; if (layout.lockLocation) { if (layout.lockLocation === true) { const tile = Tiles.embedded_tile( @@ -375,66 +373,66 @@ export class InitUiElements { const state = State.state; const empty = [] - state.filteredLayers = state.layoutToUse.map((layoutToUse) => { - const flayers: FilteredLayer[] = []; + const flayers: FilteredLayer[] = []; - for (const layer of layoutToUse.layers) { - let defaultShown = "true" - if(layoutToUse.id === personal.id){ - defaultShown = "false" - } - - let isDisplayed: UIEventSource - if(layoutToUse.id === personal.id){ - isDisplayed = State.state.osmConnection.GetPreference("personal-theme-layer-" + layer.id + "-enabled") - .map(value => value === "yes", [], enabled => { - return enabled ? "yes" : ""; - }) - isDisplayed.addCallbackAndRun(d =>console.log("IsDisplayed for layer", layer.id, "is currently", d) ) - }else{ - isDisplayed = QueryParameters.GetQueryParameter( - "layer-" + layer.id, - defaultShown, - "Wether or not layer " + layer.id + " is shown" - ).map( - (str) => str !== "false", - [], - (b) => b.toString() - ); - } - const flayer = { - isDisplayed: isDisplayed, - layerDef: layer, - appliedFilters: new UIEventSource<{ filter: FilterConfig, selected: number }[]>([]), - }; - - if (layer.filters.length > 0) { - const filtersPerName = new Map() - layer.filters.forEach(f => filtersPerName.set(f.id, f)) - const qp = QueryParameters.GetQueryParameter("filter-" + layer.id, "","Filtering state for a layer") - flayer.appliedFilters.map(filters => { - filters = filters ?? [] - return filters.map(f => f.filter.id + "." + f.selected).join(",") - }, [], textual => { - if(textual.length === 0){ - return empty - } - return textual.split(",").map(part => { - const [filterId, selected] = part.split("."); - return {filter: filtersPerName.get(filterId), selected: Number(selected)} - }).filter(f => f.filter !== undefined && !isNaN(f.selected)) - }).syncWith(qp, true) - } - - flayers.push(flayer); + for (const layer of state.layoutToUse.layers) { + let defaultShown = "true" + if(state.layoutToUse.id === personal.id){ + defaultShown = "false" } - return flayers; - }); + + let isDisplayed: UIEventSource + if(state.layoutToUse.id === personal.id){ + isDisplayed = State.state.osmConnection.GetPreference("personal-theme-layer-" + layer.id + "-enabled") + .map(value => value === "yes", [], enabled => { + return enabled ? "yes" : ""; + }) + isDisplayed.addCallbackAndRun(d =>console.log("IsDisplayed for layer", layer.id, "is currently", d) ) + }else{ + isDisplayed = QueryParameters.GetQueryParameter( + "layer-" + layer.id, + defaultShown, + "Wether or not layer " + layer.id + " is shown" + ).map( + (str) => str !== "false", + [], + (b) => b.toString() + ); + } + const flayer = { + isDisplayed: isDisplayed, + layerDef: layer, + appliedFilters: new UIEventSource<{ filter: FilterConfig, selected: number }[]>([]), + }; + + if (layer.filters.length > 0) { + const filtersPerName = new Map() + layer.filters.forEach(f => filtersPerName.set(f.id, f)) + const qp = QueryParameters.GetQueryParameter("filter-" + layer.id, "","Filtering state for a layer") + flayer.appliedFilters.map(filters => { + filters = filters ?? [] + return filters.map(f => f.filter.id + "." + f.selected).join(",") + }, [], textual => { + if(textual.length === 0){ + return empty + } + return textual.split(",").map(part => { + const [filterId, selected] = part.split("."); + return {filter: filtersPerName.get(filterId), selected: Number(selected)} + }).filter(f => f.filter !== undefined && !isNaN(f.selected)) + }).syncWith(qp, true) + } + + flayers.push(flayer); + } + state.filteredLayers = new UIEventSource(flayers); + + const clusterCounter = TileHierarchyAggregator.createHierarchy() new ShowDataLayer({ - features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.data.clustering.minNeededElements), + features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.clustering.minNeededElements), leafletMap: State.state.leafletMap, layerToShow: ShowTileInfo.styling, }) @@ -444,7 +442,7 @@ export class InitUiElements { clusterCounter.addTile(source) - const clustering = State.state.layoutToUse.data.clustering + const clustering = State.state.layoutToUse.clustering const doShowFeatures = source.features.map( f => { const z = State.state.locationControl.data.zoom diff --git a/Logic/Actors/BackgroundLayerResetter.ts b/Logic/Actors/BackgroundLayerResetter.ts index 96c43bda3..d193d8132 100644 --- a/Logic/Actors/BackgroundLayerResetter.ts +++ b/Logic/Actors/BackgroundLayerResetter.ts @@ -11,8 +11,8 @@ export default class BackgroundLayerResetter { constructor(currentBackgroundLayer: UIEventSource, location: UIEventSource, availableLayers: UIEventSource, - defaultLayerId: UIEventSource = undefined) { - defaultLayerId = defaultLayerId ?? new UIEventSource(AvailableBaseLayers.osmCarto.id); + defaultLayerId: string = undefined) { + defaultLayerId = defaultLayerId ?? AvailableBaseLayers.osmCarto.id; // Change the baselayer back to OSM if we go out of the current range of the layer availableLayers.addCallbackAndRun(availableLayers => { @@ -28,7 +28,7 @@ export default class BackgroundLayerResetter { if (availableLayer.min_zoom > location.data.zoom) { break; } - if (availableLayer.id === defaultLayerId.data) { + if (availableLayer.id === defaultLayerId) { defaultLayer = availableLayer; } return; // All good - the current layer still works! diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index d77f326e5..248703954 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -3,7 +3,7 @@ import Loc from "../../Models/Loc"; import {Or} from "../Tags/Or"; import {Overpass} from "../Osm/Overpass"; import Bounds from "../../Models/Bounds"; -import FeatureSource, {FeatureSourceState} from "../FeatureSource/FeatureSource"; +import FeatureSource from "../FeatureSource/FeatureSource"; import {Utils} from "../../Utils"; import {TagsFilter} from "../Tags/TagsFilter"; import SimpleMetaTagger from "../SimpleMetaTagger"; @@ -39,7 +39,7 @@ export default class OverpassFeatureSource implements FeatureSource { private readonly _previousBounds: Map = new Map(); private readonly state: { readonly locationControl: UIEventSource, - readonly layoutToUse: UIEventSource, + readonly layoutToUse: LayoutConfig, readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; readonly currentBounds :UIEventSource @@ -52,7 +52,7 @@ export default class OverpassFeatureSource implements FeatureSource { constructor( state: { readonly locationControl: UIEventSource, - readonly layoutToUse: UIEventSource, + readonly layoutToUse: LayoutConfig, readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; readonly overpassMaxZoom: UIEventSource, @@ -76,9 +76,6 @@ export default class OverpassFeatureSource implements FeatureSource { this._previousBounds.set(i, []); } - state.layoutToUse.addCallback(() => { - self.update() - }); location.addCallback(() => { self.update() }); @@ -92,7 +89,7 @@ export default class OverpassFeatureSource implements FeatureSource { private GetFilter(): Overpass { let filters: TagsFilter[] = []; let extraScripts: string[] = []; - for (const layer of this.state.layoutToUse.data.layers) { + for (const layer of this.state.layoutToUse.layers) { if (typeof (layer) === "string") { throw "A layer was not expanded!" } @@ -164,7 +161,7 @@ export default class OverpassFeatureSource implements FeatureSource { return undefined; } - const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.data.widenFactor)?.expandToTileBounds(14); + const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.widenFactor)?.expandToTileBounds(14); if (bounds === undefined) { return undefined; diff --git a/Logic/Actors/TitleHandler.ts b/Logic/Actors/TitleHandler.ts index 49661f289..699a33aa8 100644 --- a/Logic/Actors/TitleHandler.ts +++ b/Logic/Actors/TitleHandler.ts @@ -3,12 +3,18 @@ import Translations from "../../UI/i18n/Translations"; import Locale from "../../UI/i18n/Locale"; import TagRenderingAnswer from "../../UI/Popup/TagRenderingAnswer"; import Combine from "../../UI/Base/Combine"; +import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; +import {ElementStorage} from "../ElementStorage"; export default class TitleHandler { - constructor(state) { + constructor(state : { + selectedElement: UIEventSource, + layoutToUse: LayoutConfig, + allElements: ElementStorage + }) { const currentTitle: UIEventSource = state.selectedElement.map( selected => { - const layout = state.layoutToUse.data + const layout = state.layoutToUse const defaultTitle = Translations.WT(layout?.title)?.txt ?? "MapComplete" if (selected === undefined) { @@ -27,7 +33,7 @@ export default class TitleHandler { } } return defaultTitle - }, [Locale.language, state.layoutToUse] + }, [Locale.language] ) diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index fab051aa5..d65771da2 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -1,7 +1,7 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import FilteringFeatureSource from "./Sources/FilteringFeatureSource"; import PerLayerFeatureSourceSplitter from "./PerLayerFeatureSourceSplitter"; -import FeatureSource, {FeatureSourceForLayer, FeatureSourceState, IndexedFeatureSource, Tiled} from "./FeatureSource"; +import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "./FeatureSource"; import TiledFeatureSource from "./TiledFeatureSource/TiledFeatureSource"; import {UIEventSource} from "../UIEventSource"; import {TileHierarchyTools} from "./TiledFeatureSource/TileHierarchy"; @@ -48,7 +48,7 @@ export default class FeaturePipeline { readonly locationControl: UIEventSource, readonly selectedElement: UIEventSource, readonly changes: Changes, - readonly layoutToUse: UIEventSource, + readonly layoutToUse: LayoutConfig, readonly leafletMap: any, readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; @@ -86,7 +86,7 @@ export default class FeaturePipeline { if (location?.zoom === undefined) { return false; } - let minzoom = Math.min(...state.layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); + let minzoom = Math.min(...state.layoutToUse.layers.map(layer => layer.minzoom ?? 18)); return location.zoom >= minzoom; } ); @@ -223,8 +223,8 @@ export default class FeaturePipeline { layer: source.layer, minZoomLevel: 14, dontEnforceMinZoom: true, - maxFeatureCount: state.layoutToUse.data.clustering.minNeededElements, - maxZoomLevel: state.layoutToUse.data.clustering.maxZoom, + maxFeatureCount: state.layoutToUse.clustering.minNeededElements, + maxZoomLevel: state.layoutToUse.clustering.maxZoom, registerTile: (tile) => { // We save the tile data for the given layer to local storage new SaveTileToLocalStorageActor(tile, tile.tileIndex) diff --git a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts index 2bed33298..a7485facf 100644 --- a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts @@ -65,7 +65,7 @@ export default class OsmFeatureSource { } private async LoadTile(z, x, y): Promise { - if (z > 18) { + if (z > 20) { throw "This is an absurd high zoom level" } diff --git a/Logic/Osm/Actions/DeleteAction.ts b/Logic/Osm/Actions/DeleteAction.ts index 25542bf18..68919bd12 100644 --- a/Logic/Osm/Actions/DeleteAction.ts +++ b/Logic/Osm/Actions/DeleteAction.ts @@ -62,7 +62,7 @@ export default class DeleteAction { } State.state.osmConnection.changesetHandler.DeleteElement( obj, - State.state.layoutToUse.data, + State.state.layoutToUse, reason, State.state.allElements, () => { diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index 828ba7d78..b4bf3b4ec 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -131,7 +131,7 @@ export class Changes { } await State.state.osmConnection.UploadChangeset( - State.state.layoutToUse.data, + State.state.layoutToUse, State.state.allElements, (csId) => Changes.createChangesetFor(csId, changes), ) diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index a938e9c72..4b047fc7d 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -87,7 +87,7 @@ export default class SimpleMetaTagger { }, (feature => { - const units = Utils.NoNull([].concat(...State.state?.layoutToUse?.data?.layers?.map(layer => layer.units ?? []))); + const units = Utils.NoNull([].concat(...State.state?.layoutToUse?.layers?.map(layer => layer.units ?? []))); if (units.length == 0) { return; } diff --git a/State.ts b/State.ts index 85dfc9cfd..0af8e5a64 100644 --- a/State.ts +++ b/State.ts @@ -27,7 +27,7 @@ export default class State { // The singleton of the global state public static state: State; - public readonly layoutToUse = new UIEventSource(undefined, "layoutToUse"); + public readonly layoutToUse : LayoutConfig; /** The mapping from id -> UIEventSource @@ -83,7 +83,9 @@ export default class State { public readonly featureSwitchExportAsPdf: UIEventSource; public readonly overpassUrl: UIEventSource; public readonly overpassTimeout: UIEventSource; - public readonly overpassMaxZoom: UIEventSource = new UIEventSource(20); + + + public readonly overpassMaxZoom: UIEventSource = new UIEventSource(17, "overpass-max-zoom: point to switch between OSM-api and overpass"); public featurePipeline: FeaturePipeline; @@ -155,7 +157,7 @@ export default class State { constructor(layoutToUse: LayoutConfig) { const self = this; - this.layoutToUse.setData(layoutToUse); + this.layoutToUse = layoutToUse; // -- Location control initialization { @@ -192,14 +194,7 @@ export default class State { lat.setData(latlonz.lat); lon.setData(latlonz.lon); }); - - this.layoutToUse.addCallback((layoutToUse) => { - const lcd = self.locationControl.data; - lcd.zoom = lcd.zoom ?? layoutToUse?.startZoom; - lcd.lat = lcd.lat ?? layoutToUse?.startLat; - lcd.lon = lcd.lon ?? layoutToUse?.startLon; - self.locationControl.ping(); - }); + } // Helper function to initialize feature switches @@ -208,28 +203,19 @@ export default class State { deflt: (layout: LayoutConfig) => boolean, documentation: string ): UIEventSource { - const queryParameterSource = QueryParameters.GetQueryParameter( + + const defaultValue = deflt(self.layoutToUse); + const queryParam = QueryParameters.GetQueryParameter( key, - undefined, + "" + defaultValue, documentation ); - // I'm so sorry about someone trying to decipher this // It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened - return UIEventSource.flatten( - self.layoutToUse.map((layout) => { - const defaultValue = deflt(layout); - const queryParam = QueryParameters.GetQueryParameter( - key, - "" + defaultValue, - documentation - ); - return queryParam.map((str) => - str === undefined ? defaultValue : str !== "false" - ); - }), - [queryParameterSource] - ); + return queryParam.map((str) => + str === undefined ? defaultValue : str !== "false" + ) + } // Feature switch initialization - not as a function as the UIEventSources are readonly @@ -412,11 +398,11 @@ export default class State { Locale.language .addCallback((currentLanguage) => { - const layoutToUse = self.layoutToUse.data; + const layoutToUse = self.layoutToUse; if (layoutToUse === undefined) { return; } - if (this.layoutToUse.data.language.indexOf(currentLanguage) < 0) { + if (this.layoutToUse.language.indexOf(currentLanguage) < 0) { console.log( "Resetting language to", layoutToUse.language[0], diff --git a/UI/BigComponents/Attribution.ts b/UI/BigComponents/Attribution.ts index 9b1ddb013..471556798 100644 --- a/UI/BigComponents/Attribution.ts +++ b/UI/BigComponents/Attribution.ts @@ -16,13 +16,13 @@ export default class Attribution extends Combine { constructor(location: UIEventSource, userDetails: UIEventSource, - layoutToUse: UIEventSource, + layoutToUse: LayoutConfig, currentBounds: UIEventSource) { const mapComplete = new Link(`Mapcomplete ${Constants.vNumber}`, 'https://github.com/pietervdvn/MapComplete', true); const reportBug = new Link(Svg.bug_ui().SetClass("small-image"), "https://github.com/pietervdvn/MapComplete/issues", true); - const layoutId = layoutToUse?.data?.id; + const layoutId = layoutToUse?.id; const now = new Date() // Note: getMonth is zero-index, we want 1-index but with one substracted, so it checks out! const startDate = now.getFullYear() + "-" + now.getMonth() + "-" + now.getDate() diff --git a/UI/BigComponents/AttributionPanel.ts b/UI/BigComponents/AttributionPanel.ts index 542326314..c18c8d287 100644 --- a/UI/BigComponents/AttributionPanel.ts +++ b/UI/BigComponents/AttributionPanel.ts @@ -20,11 +20,11 @@ export default class AttributionPanel extends Combine { private static LicenseObject = AttributionPanel.GenerateLicenses(); - constructor(layoutToUse: UIEventSource, contributions: UIEventSource>) { + constructor(layoutToUse: LayoutConfig, contributions: UIEventSource>) { super([ Translations.t.general.attribution.attributionContent, - ((layoutToUse.data.maintainer ?? "") == "") ? "" : Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.data.maintainer}), - layoutToUse.data.credits, + ((layoutToUse.maintainer ?? "") == "") ? "" : Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.maintainer}), + layoutToUse.credits, "
", new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.currentBounds), "
", @@ -65,7 +65,7 @@ export default class AttributionPanel extends Combine { "
", AttributionPanel.CodeContributors(), "

", Translations.t.general.attribution.iconAttribution.title.Clone().SetClass("pt-6 pb-3"), "

", - ...Utils.NoNull(Array.from(layoutToUse.data.ExtractImages())) + ...Utils.NoNull(Array.from(layoutToUse.ExtractImages())) .map(AttributionPanel.IconAttribution) ]); this.SetClass("flex flex-col link-underline overflow-hidden") diff --git a/UI/BigComponents/DownloadPanel.ts b/UI/BigComponents/DownloadPanel.ts index 500d28e19..f23d98747 100644 --- a/UI/BigComponents/DownloadPanel.ts +++ b/UI/BigComponents/DownloadPanel.ts @@ -26,7 +26,7 @@ export class DownloadPanel extends Toggle { const t = Translations.t.general.download - const name = State.state.layoutToUse.data.id; + const name = State.state.layoutToUse.id; const includeMetaToggle = new CheckBoxes([t.includeMetaData.Clone()]) const metaisIncluded = includeMetaToggle.GetValue().map(selected => selected.length > 0) diff --git a/UI/BigComponents/FullWelcomePaneWithTabs.ts b/UI/BigComponents/FullWelcomePaneWithTabs.ts index f6574f04b..8ee36ecec 100644 --- a/UI/BigComponents/FullWelcomePaneWithTabs.ts +++ b/UI/BigComponents/FullWelcomePaneWithTabs.ts @@ -19,7 +19,7 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { constructor(isShown: UIEventSource) { - const layoutToUse = State.state.layoutToUse.data; + const layoutToUse = State.state.layoutToUse; super( () => layoutToUse.title.Clone(), () => FullWelcomePaneWithTabs.GenerateContents(layoutToUse, State.state.osmConnection.userDetails, isShown), diff --git a/UI/BigComponents/MoreScreen.ts b/UI/BigComponents/MoreScreen.ts index 9d8fa44f9..01e4b9f95 100644 --- a/UI/BigComponents/MoreScreen.ts +++ b/UI/BigComponents/MoreScreen.ts @@ -126,7 +126,7 @@ export default class MoreScreen extends Combine { if (layout.hideFromOverview) { return undefined; } - if (layout.id === State.state.layoutToUse.data?.id) { + if (layout.id === State.state.layoutToUse?.id) { return undefined; } diff --git a/UI/BigComponents/ShareScreen.ts b/UI/BigComponents/ShareScreen.ts index 9b97ef441..32e1b0ce0 100644 --- a/UI/BigComponents/ShareScreen.ts +++ b/UI/BigComponents/ShareScreen.ts @@ -17,7 +17,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; export default class ShareScreen extends Combine { constructor(layout: LayoutConfig = undefined, layoutDefinition: string = undefined) { - layout = layout ?? State.state?.layoutToUse?.data; + layout = layout ?? State.state?.layoutToUse; layoutDefinition = layoutDefinition ?? State.state?.layoutDefinition; const tr = Translations.t.general.sharescreen; diff --git a/UI/BigComponents/ThemeIntroductionPanel.ts b/UI/BigComponents/ThemeIntroductionPanel.ts index 8c4add4d9..75c5b6f55 100644 --- a/UI/BigComponents/ThemeIntroductionPanel.ts +++ b/UI/BigComponents/ThemeIntroductionPanel.ts @@ -2,21 +2,17 @@ import State from "../../State"; import Combine from "../Base/Combine"; import LanguagePicker from "../LanguagePicker"; import Translations from "../i18n/Translations"; -import {VariableUiElement} from "../Base/VariableUIElement"; import Toggle from "../Input/Toggle"; import {SubtleButton} from "../Base/SubtleButton"; import Svg from "../../Svg"; import {UIEventSource} from "../../Logic/UIEventSource"; -export default class ThemeIntroductionPanel extends VariableUiElement { +export default class ThemeIntroductionPanel extends Combine { constructor(isShown: UIEventSource) { + const layout = State.state.layoutToUse - const languagePicker = - new VariableUiElement( - State.state.layoutToUse.map(layout => LanguagePicker.CreateLanguagePicker(layout.language, Translations.t.general.pickLanguage.Clone())) - ) - ; + const languagePicker = LanguagePicker.CreateLanguagePicker(layout.language, Translations.t.general.pickLanguage.Clone()) const toTheMap = new SubtleButton( undefined, @@ -53,8 +49,7 @@ export default class ThemeIntroductionPanel extends VariableUiElement { State.state.featureSwitchUserbadge ) - - super(State.state.layoutToUse.map(layout => new Combine([ + super([ layout.description.Clone(), "

", toTheMap, @@ -63,7 +58,7 @@ export default class ThemeIntroductionPanel extends VariableUiElement { "
", languagePicker, ...layout.CustomCodeSnippets() - ]))) + ]) this.SetClass("link-underline") } diff --git a/UI/BigComponents/UserBadge.ts b/UI/BigComponents/UserBadge.ts index 62f74e0ae..006b1823a 100644 --- a/UI/BigComponents/UserBadge.ts +++ b/UI/BigComponents/UserBadge.ts @@ -47,7 +47,7 @@ export default class UserBadge extends Toggle { }); const linkStyle = "flex items-baseline" - const languagePicker = (LanguagePicker.CreateLanguagePicker(State.state.layoutToUse.data.language) ?? new FixedUiElement("")) + const languagePicker = (LanguagePicker.CreateLanguagePicker(State.state.layoutToUse.language) ?? new FixedUiElement("")) .SetStyle("width:min-content;"); let messageSpan = diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 2a8053523..432541395 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -55,7 +55,7 @@ export class ImageUploadFlow extends Toggle { const tags = tagsSource.data; - const layout = State.state?.layoutToUse?.data + const layout = State.state?.layoutToUse let matchingLayer: LayerConfig = undefined for (const layer of layout?.layers ?? []) { if (layer.source.osmTags.matchesProperties(tags)) { diff --git a/UI/Input/DropDown.ts b/UI/Input/DropDown.ts index fe8f8bc98..147ad0f50 100644 --- a/UI/Input/DropDown.ts +++ b/UI/Input/DropDown.ts @@ -47,7 +47,7 @@ export class DropDown extends InputElement { } options = options ?? {} - options.select_class = options.select_class ?? 'bg-indigo-100 p-1 rounded hover:bg-indigo-200' + options.select_class = options.select_class ?? 'bg-indigo-100 p-1 rounded hover:bg-indigo-200 w-full' { diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index 262d9a7db..e826773ab 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -39,7 +39,7 @@ export default class LocationInput extends InputElement { private readonly _maxSnapDistance: number private readonly _snappedPointTags: any; private readonly _bounds: UIEventSource; - public readonly _matching_layer: UIEventSource; + public readonly _matching_layer: LayerConfig; constructor(options: { mapBackground?: UIEventSource, @@ -63,18 +63,17 @@ export default class LocationInput extends InputElement { if (self._snappedPointTags !== undefined) { - this._matching_layer = State.state.layoutToUse.map(layout => { + const layout = State.state.layoutToUse - for (const layer of layout.layers) { - if (layer.source.osmTags.matchesProperties(self._snappedPointTags)) { - return layer - } + let matchingLayer = LocationInput.matchLayer + for (const layer of layout.layers) { + if (layer.source.osmTags.matchesProperties(self._snappedPointTags)) { + matchingLayer = layer } - console.error("No matching layer found for tags ", self._snappedPointTags) - return LocationInput.matchLayer - }) + } + this._matching_layer = matchingLayer; } else { - this._matching_layer = new UIEventSource(LocationInput.matchLayer) + this._matching_layer = LocationInput.matchLayer } this._snappedPoint = options.centerLocation.map(loc => { @@ -176,7 +175,7 @@ export default class LocationInput extends InputElement { enablePopups: false, zoomToFeatures: false, leafletMap: map.leafletMap, - layerToShow: this._matching_layer.data + layerToShow: this._matching_layer }) } diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 7814cbbdc..5b15cfdaa 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -316,10 +316,10 @@ export default class SpecialVisualizations { const generateShareData = () => { - const title = state?.layoutToUse?.data?.title?.txt ?? "MapComplete"; + const title = state?.layoutToUse?.title?.txt ?? "MapComplete"; let matchingLayer: LayerConfig = undefined; - for (const layer of (state?.layoutToUse?.data?.layers ?? [])) { + for (const layer of (state?.layoutToUse?.layers ?? [])) { if (layer.source.osmTags.matchesProperties(tagSource?.data)) { matchingLayer = layer } @@ -337,7 +337,7 @@ export default class SpecialVisualizations { return { title: name, url: url, - text: state?.layoutToUse?.data?.shortDescription?.txt ?? "MapComplete" + text: state?.layoutToUse?.shortDescription?.txt ?? "MapComplete" } } @@ -363,15 +363,14 @@ export default class SpecialVisualizations { if (value === undefined) { return undefined } - const allUnits = [].concat(...state.layoutToUse.data.layers.map(lyr => lyr.units)) + const allUnits = [].concat(...state.layoutToUse.layers.map(lyr => lyr.units)) const unit = allUnits.filter(unit => unit.isApplicableToKey(key))[0] if (unit === undefined) { return value; } return unit.asHumanLongValue(value); - }, - [state.layoutToUse]) + }) ) } }, @@ -410,7 +409,7 @@ There are also some technicalities in your theme to keep in mind: A reference number to the original dataset is an excellen way to do this `, constr: (state, tagSource, args) => { - if (!state.layoutToUse.data.official && !state.featureSwitchIsTesting.data) { + if (!state.layoutToUse.official && !state.featureSwitchIsTesting.data) { return new Combine([new FixedUiElement("The import button is disabled for unofficial themes to prevent accidents.").SetClass("alert"), new FixedUiElement("To test, add 'test=true' to the URL. The changeset will be printed in the console. Please open a PR to officialize this theme to actually enable the import button.")]) } From c34f558c67582bb3f2aea88d4f5b7996ddcf6013 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 28 Sep 2021 18:40:45 +0200 Subject: [PATCH 041/110] Move json data injection to approppriate place, fixes tests --- Utils.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Utils.ts b/Utils.ts index aced008ad..424784205 100644 --- a/Utils.ts +++ b/Utils.ts @@ -296,12 +296,6 @@ export class Utils { } public static download(url: string, headers?: any): Promise { - const injected = Utils.injectedDownloads[url] - if (injected !== undefined) { - console.log("Using injected resource for test for URL", url) - return new Promise((resolve, _) => resolve(injected)) - } - if (this.externalDownloadFunction !== undefined) { return this.externalDownloadFunction(url, headers) } @@ -311,8 +305,8 @@ export class Utils { xhr.onload = () => { if (xhr.status == 200) { resolve(xhr.response) - } else if (xhr.status === 509 || xhr.status === 429){ - reject("rate limited") + } else if (xhr.status === 509 || xhr.status === 429) { + reject("rate limited") } else { reject(xhr.statusText) } @@ -331,8 +325,18 @@ export class Utils { } public static async downloadJson(url: string, headers?: any): Promise { + const injected = Utils.injectedDownloads[url] + if (injected !== undefined) { + console.log("Using injected resource for test for URL", url) + return new Promise((resolve, _) => resolve(injected)) + } const data = await Utils.download(url, Utils.Merge({"accept": "application/json"}, headers ?? {})) - return JSON.parse(data) + try { + return JSON.parse(data) + } catch (e) { + console.error("Could not parse ", data, "due to", e, "\n", e.stack) + throw e; + } } /** From e9aa8d347f250c0110011781b8479c90f1c54f30 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 28 Sep 2021 18:41:08 +0200 Subject: [PATCH 042/110] Small, experimental fixes to the themes --- assets/themes/bicyclelib/bicyclelib.json | 3 +-- assets/themes/cycle_highways/cycle_highways.json | 2 +- assets/themes/personal/personal.json | 2 +- assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json | 4 ++++ langs/themes/en.json | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/assets/themes/bicyclelib/bicyclelib.json b/assets/themes/bicyclelib/bicyclelib.json index c0c85b13b..bc251907b 100644 --- a/assets/themes/bicyclelib/bicyclelib.json +++ b/assets/themes/bicyclelib/bicyclelib.json @@ -48,7 +48,6 @@ "override": { "minZoom": 0 } - }, - + } ] } \ No newline at end of file diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 53dea9cc8..08b7c8556 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -170,7 +170,7 @@ ] }, { - "id":"ref-alt", + "id": "ref-alt", "options": [ { "question": "Reference contains 'alt'", diff --git a/assets/themes/personal/personal.json b/assets/themes/personal/personal.json index 88296088d..2896e5fbc 100644 --- a/assets/themes/personal/personal.json +++ b/assets/themes/personal/personal.json @@ -12,7 +12,7 @@ "zh_Hant": "個人化主題" }, "description": { - "en": "Create a personal theme based on all the available layers of all themes. Open the layer selection to select one or more layers.", + "en": "Create a personal theme based on all the available layers of all themes. In order to show some data, open layer selection", "nl": "Stel je eigen thema samen door lagen te combineren van alle andere themas", "es": "Crea una interficie basada en todas las capas disponibles de todas las interficies", "ca": "Crea una interfície basada en totes les capes disponibles de totes les interfícies", diff --git a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json index 7bef2f129..aeea7f806 100644 --- a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json +++ b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json @@ -39,5 +39,9 @@ "binocular", "observation_tower" ], + "overiddeAll": { + "minZoomVisible": 0 + }, + "hideFromOverview": true } \ No newline at end of file diff --git a/langs/themes/en.json b/langs/themes/en.json index e2b1d6591..b62768bef 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1216,7 +1216,7 @@ "title": "Parking" }, "personal": { - "description": "Create a personal theme based on all the available layers of all themes", + "description": "Create a personal theme based on all the available layers of all themes. In order to show some data, open layer selection", "title": "Personal theme" }, "playgrounds": { From e723427b99b77744af49c831c916bf098a812f94 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 28 Sep 2021 22:33:09 +0200 Subject: [PATCH 043/110] Fix #477 --- scripts/generateWikiPage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/generateWikiPage.ts b/scripts/generateWikiPage.ts index ca03e3fb5..688dfd2fe 100644 --- a/scripts/generateWikiPage.ts +++ b/scripts/generateWikiPage.ts @@ -20,8 +20,8 @@ function generateWikiEntry(layout: LayoutConfig) { |name= [https://mapcomplete.osm.be/${layout.id} ${layout.id}] |region= Worldwide |lang= ${languages} -|descr= A MapComplete theme: ${Translations.W(layout.description) - .InnerRenderAsString() +|descr= A MapComplete theme: ${Translations.WT(layout.description) + .textFor("en") .replace(".*<\/a>/, "]]") } From 231f4f2c971f9713781a22f4b9c51884d09d50d6 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 01:10:25 +0200 Subject: [PATCH 044/110] New wikipage --- Docs/wikiIndex.txt | 280 +++++++++++++++++++++++++++++++-------------- 1 file changed, 196 insertions(+), 84 deletions(-) diff --git a/Docs/wikiIndex.txt b/Docs/wikiIndex.txt index f9d8ab6da..4bd9edfad 100644 --- a/Docs/wikiIndex.txt +++ b/Docs/wikiIndex.txt @@ -4,8 +4,8 @@ {{service_item |name= [https://mapcomplete.osm.be/personal personal] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:gl|en}}, {{#language:de|en}} -|descr= A MapComplete theme: Create a personal theme based on all the available layers of all themes +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:gl|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: Create a personal theme based on all the available layers of all themes. In order to show some data, open [[#filter]] |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, personal @@ -13,25 +13,29 @@ {{service_item |name= [https://mapcomplete.osm.be/cyclofix cyclofix] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:gl|en}}, {{#language:de|en}} +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:gl|en}}, {{#language:de|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}} |descr= A MapComplete theme: The goal of this map is to present cyclists with an easy-to-use solution to find the appropriate infrastructure for their needs.

You can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide more data by answering the questions.

All changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others.

For more information about the cyclofix project, go to [[https://cyclofix.osm.be/]]. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, cyclofix }} {{service_item -|name= [https://mapcomplete.osm.be/aed aed] +|name= [https://mapcomplete.osm.be/hailhydrant hailhydrant] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:de|en}} -|descr= A MapComplete theme: On this map, one can find and mark nearby defibrillators -|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|lang= {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:fr|en}}, {{#language:nb_NO|en}}, {{#language:it|en}}, {{#language:id|en}} +|descr= A MapComplete theme: On this map you can find and update hydrants, fire stations, ambulance stations, and extinguishers in your favorite neighborhoods. + +You can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide additional details by answering available questions. + +All changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Erwin Olario;]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, aed +|genre= POI, editor, hailhydrant }} {{service_item |name= [https://mapcomplete.osm.be/bookcases bookcases] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}} +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:pt_BR|en}} |descr= A MapComplete theme: A public bookcase is a small streetside cabinet, box, old phone boot or some other objects where books are stored. Everyone can place or take a book. This map aims to collect all these bookcases. You can discover new bookcases nearby and, with a free OpenStreetMap account, quickly add your favourite bookcases. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png @@ -40,47 +44,173 @@ {{service_item |name= [https://mapcomplete.osm.be/toilets toilets] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}} +|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}} |descr= A MapComplete theme: A map of public toilets |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, toilets }} {{service_item -|name= [https://mapcomplete.osm.be/artworks artworks] +|name= [https://mapcomplete.osm.be/aed aed] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:de|en}} -|descr= A MapComplete theme: Welcome to Open Artwork Map, a map of statues, busts, grafittis, ... all over the world +|lang= {{#language:en|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:id|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:sv|en}}, {{#language:pl|en}}, {{#language:pt_BR|en}} +|descr= A MapComplete theme: On this map, one can find and mark nearby defibrillators |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, artworks +|genre= POI, editor, aed +}} +{{service_item +|name= [https://mapcomplete.osm.be/artwork artwork] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:id|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:sv|en}}, {{#language:pl|en}}, {{#language:es|en}}, {{#language:nb_NO|en}} +|descr= A MapComplete theme: Welcome to Open Artwork Map, a map of statues, busts, grafittis and other artwork all over the world +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, artwork +}} +{{service_item +|name= [https://mapcomplete.osm.be/benches benches] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:pt_BR|en}} +|descr= A MapComplete theme: This map shows all benches that are recorded in OpenStreetMap: Individual benches, and benches belonging to public transport stops or shelters. With an OpenStreetMap account, you can map new benches or edit details of existing benches. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Florian Edelmann;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, benches +}} +{{service_item +|name= [https://mapcomplete.osm.be/bicyclelib bicyclelib] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:fr|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:de|en}}, {{#language:pt_BR|en}} +|descr= A MapComplete theme: A bicycle library is a place where bicycles can be lent, often for a small yearly fee. A notable use case are bicycle libraries for kids, which allows them to change for a bigger bike when they've outgrown their current bike +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, bicyclelib +}} +{{service_item +|name= [https://mapcomplete.osm.be/binoculars binoculars] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}} +|descr= A MapComplete theme: A map with binoculars fixed in place with a pole. It can typically be found on touristic locations, viewpoints, on top of panoramic towers or occasionally on a nature reserve. +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, binoculars +}} +{{service_item +|name= [https://mapcomplete.osm.be/cafes_and_pubs cafes_and_pubs] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}} +|descr= A MapComplete theme: Cafés, kroegen en drinkgelegenheden +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, cafes_and_pubs +}} +{{service_item +|name= [https://mapcomplete.osm.be/campersite campersite] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:fr|en}}, {{#language:zh_Hant|en}}, {{#language:pt_BR|en}}, {{#language:id|en}}, {{#language:nb_NO|en}} +|descr= A MapComplete theme: This site collects all official camper stopover places and places where you can dump grey and black water. You can add details about the services provided and the cost. Add pictures and reviews. This is a website and a webapp. The data is stored in OpenStreetMap, so it will be free forever and can be re-used by any app. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by joost schouppe;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, campersite +}} +{{service_item +|name= [https://mapcomplete.osm.be/charging_stations charging_stations] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:id|en}}, {{#language:it|en}}, {{#language:ja|en}}, {{#language:ru|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:nl|en}}, {{#language:nb_NO|en}} +|descr= A MapComplete theme: On this open map, one can find and mark information about charging stations +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, charging_stations +}} +{{service_item +|name= [https://mapcomplete.osm.be/climbing climbing] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:de|en}}, {{#language:en|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:it|en}}, {{#language:ca|en}}, {{#language:fr|en}}, {{#language:id|en}} +|descr= A MapComplete theme: On this map you will find various climbing opportunities such as climbing gyms, bouldering halls and rocks in nature. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Christian Neumann ;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, climbing +}} +{{service_item +|name= [https://mapcomplete.osm.be/cycle_infra cycle_infra] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}} +|descr= A MapComplete theme: A map where you can view and edit things related to the bicycle infrastructure. Made during #osoc21. +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, cycle_infra +}} +{{service_item +|name= [https://mapcomplete.osm.be/cyclestreets cyclestreets] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:it|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: A cyclestreet is is a street where motorized traffic is not allowed to overtake cyclists. They are signposted by a special traffic sign. Cyclestreets can be found in the Netherlands and Belgium, but also in Germany and France. +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, cyclestreets +}} +{{service_item +|name= [https://mapcomplete.osm.be/drinking_water drinking_water] +|region= Worldwide +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}} +|descr= A MapComplete theme: On this map, publicly accessible drinking water spots are shown and can be easily added +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, drinking_water +}} +{{service_item +|name= [https://mapcomplete.osm.be/facadegardens facadegardens] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:fr|en}}, {{#language:nb_NO|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: [[https://nl.wikipedia.org/wiki/Geveltuin' target=_blank>Facade gardens
, green facades and trees in the city not only bring peace and quiet, but also a more beautiful city, greater biodiversity, a cooling effect and better air quality.
Klimaan VZW and Mechelen Klimaatneutraal want to map existing and new facade gardens as an example for people who want to build their own garden or for city walkers who love nature.
More info about the project at klimaan.be. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by joost schouppe; stla;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, facadegardens +}} +{{service_item +|name= [https://mapcomplete.osm.be/food food] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}} +|descr= A MapComplete theme: Restaurants en fast food +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, food +}} +{{service_item +|name= [https://mapcomplete.osm.be/fritures fritures] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:ca|en}}, {{#language:id|en}}, {{#language:ru|en}}, {{#language:it|en}}, {{#language:nb_NO|en}} +|descr= A MapComplete theme: Op deze kaart vind je je favoriete frituur! +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, fritures }} {{service_item |name= [https://mapcomplete.osm.be/ghostbikes ghostbikes] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}} +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:nb_NO|en}}, {{#language:zh_Hant|en}}, {{#language:fr|en}}, {{#language:eo|en}}, {{#language:es|en}}, {{#language:fi|en}}, {{#language:gl|en}}, {{#language:hu|en}}, {{#language:it|en}}, {{#language:pl|en}}, {{#language:pt_BR|en}}, {{#language:ru|en}}, {{#language:sv|en}} |descr= A MapComplete theme: A ghost bike is a memorial for a cyclist who died in a traffic accident, in the form of a white bicycle placed permanently near the accident location.

On this map, one can see all the ghost bikes which are known by OpenStreetMap. Is a ghost bike missing? Everyone can add or update information here - you only need to have a (free) OpenStreetMap account. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, ghostbikes }} {{service_item -|name= [https://mapcomplete.osm.be/shops shops] +|name= [https://mapcomplete.osm.be/hackerspaces hackerspaces] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:fr|en}} -|descr= A MapComplete theme: On this map, one can mark basic information about shops, add opening hours and phone numbers +|lang= {{#language:en|en}} +|descr= A MapComplete theme: On this map you can see hackerspaces, add a new hackerspace or update data directly |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, shops +|genre= POI, editor, hackerspaces }} {{service_item -|name= [https://mapcomplete.osm.be/drinking_water drinking_water] +|name= [https://mapcomplete.osm.be/maps maps] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}} -|descr= A MapComplete theme: On this map, publicly accessible drinkging water spots are shown and can be easily added +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: On this map you can find all maps OpenStreetMap knows - typically a big map on an information board showing the area, city or region, e.g. a tourist map on the back of a billboard, a map of a nature reserve, a map of cycling networks in the region, ...)

If a map is missing, you can easily map this map on OpenStreetMap. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, drinking_water +|genre= POI, editor, maps }} {{service_item |name= [https://mapcomplete.osm.be/nature nature] @@ -92,111 +222,93 @@ |genre= POI, editor, nature }} {{service_item -|name= [https://mapcomplete.osm.be/fietsstraten fietsstraten] -|region= Worldwide -|lang= {{#language:nl|en}} -|descr= A MapComplete theme: Een fietsstraat is een straat waar
  • automobilisten geen fietsers mogen inhalen
  • Er een maximumsnelheid van 30km/u geldt
  • Fietsers gemotoriseerde voortuigen links mogen inhalen
  • Fietsers nog steeds voorrang aan rechts moeten verlenen - ook aan auto's en voetgangers op het zebrapad


Op deze open kaart kan je alle gekende fietsstraten zien en kan je ontbrekende fietsstraten aanduiden. Om de kaart aan te passen, moet je je aanmelden met OpenStreetMap en helemaal inzoomen tot straatniveau. -|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} -|image= MapComplete_Screenshot.png -|genre= POI, editor, fietsstraten -}} -{{service_item -|name= [https://mapcomplete.osm.be/bicyclelib bicyclelib] +|name= [https://mapcomplete.osm.be/observation_towers observation_towers] |region= Worldwide |lang= {{#language:en|en}}, {{#language:nl|en}} -|descr= A MapComplete theme: A bicycle library is a place where bicycles can be lent, often for a small yearly fee. A notable use case are bicycle libraries for kids, which allows them to change for a bigger bike when they've outgrown their current bike +|descr= A MapComplete theme: Publicly accessible towers to enjoy the view |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, bicyclelib +|genre= POI, editor, observation_towers }} {{service_item -|name= [https://mapcomplete.osm.be/maps maps] +|name= [https://mapcomplete.osm.be/openwindpowermap openwindpowermap] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}} -|descr= A MapComplete theme: On this map you can find all maps OpenStreetMap knows - typically a big map on an information board showing the area, city or region, e.g. a tourist map on the back of a billboard, a map of a nature reserve, a map of cycling networks in the region, ...)

If a map is missing, you can easily map this map on OpenStreetMap. +|lang= {{#language:en|en}}, {{#language:fr|en}}, {{#language:nl|en}} +|descr= A MapComplete theme: A map for showing and editing wind turbines. +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Seppe Santens;]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, openwindpowermap +}} +{{service_item +|name= [https://mapcomplete.osm.be/parkings parkings] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:en|en}} +|descr= A MapComplete theme: This map shows different parking spots |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, maps +|genre= POI, editor, parkings }} {{service_item -|name= [https://mapcomplete.osm.be/fritures fritures] +|name= [https://mapcomplete.osm.be/playgrounds playgrounds] |region= Worldwide -|lang= {{#language:nl|en}}, {{#language:fr|en}} -|descr= A MapComplete theme: Op deze kaart vind je je favoriete frituur! +|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: On this map, you find playgrounds and can add more information |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, fritures +|genre= POI, editor, playgrounds }} {{service_item -|name= [https://mapcomplete.osm.be/benches benches] +|name= [https://mapcomplete.osm.be/shops shops] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}} -|descr= A MapComplete theme: This map shows all benches that are recorded in OpenStreetMap: Individual benches, and benches belonging to public transport stops or shelters. With an OpenStreetMap account, you can map new benches or edit details of existing benches. -|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Florian Edelmann;]}} -|image= MapComplete_Screenshot.png -|genre= POI, editor, benches -}} -{{service_item -|name= [https://mapcomplete.osm.be/charging_stations charging_stations] -|region= Worldwide -|lang= {{#language:en|en}} -|descr= A MapComplete theme: On this open map, one can find and mark information about charging stations +|lang= {{#language:en|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:nl|en}}, {{#language:ca|en}}, {{#language:id|en}} +|descr= A MapComplete theme: On this map, one can mark basic information about shops, add opening hours and phone numbers |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, charging_stations +|genre= POI, editor, shops +}} +{{service_item +|name= [https://mapcomplete.osm.be/sport_pitches sport_pitches] +|region= Worldwide +|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}} +|descr= A MapComplete theme: A sport pitch is an area where sports are played +|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} +|image= MapComplete_Screenshot.png +|genre= POI, editor, sport_pitches }} {{service_item |name= [https://mapcomplete.osm.be/surveillance surveillance] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}} +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:fr|en}}, {{#language:pl|en}} |descr= A MapComplete theme: On this open map, you can find surveillance cameras. |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png |genre= POI, editor, surveillance }} {{service_item -|name= [https://mapcomplete.osm.be/climbing climbing] -|region= Worldwide -|lang= {{#language:de|en}}, {{#language:en|en}}, {{#language:nl|en}} -|descr= A MapComplete theme: On this map you will find various climbing opportunities such as climbing gyms, bouldering halls and rocks in nature. -|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Christian Neumann ;]}} -|image= MapComplete_Screenshot.png -|genre= POI, editor, climbing -}} -{{service_item -|name= [https://mapcomplete.osm.be/playgrounds playgrounds] -|region= Worldwide -|lang= {{#language:nl|en}} -|descr= A MapComplete theme: Op deze kaart vind je speelplekken zoals speeltuinen, speelbossen en sportterreinen -|material= {{yes|[https://mapcomplete.osm.be/ Yes]}} -|image= MapComplete_Screenshot.png -|genre= POI, editor, playgrounds -}} -{{service_item |name= [https://mapcomplete.osm.be/trees trees] |region= Worldwide -|lang= {{#language:nl|en}}, {{#language:en|en}} +|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:fr|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}} |descr= A MapComplete theme: Map all the trees! |material= {{yes|[https://mapcomplete.osm.be/ Yes, by Midgard;]}} |image= MapComplete_Screenshot.png |genre= POI, editor, trees }} {{service_item -|name= [https://mapcomplete.osm.be/campersite campersite] +|name= [https://mapcomplete.osm.be/uk_addresses uk_addresses] |region= Worldwide |lang= {{#language:en|en}} -|descr= A MapComplete theme: This site collects all official camper stopover places and places where you can dump grey and black water. You can add details about the services provided and the cost. Add pictures and reviews. This is a website and a webapp. The data is stored in OpenStreetMap, so it will be free forever and can be re-used by any app. -|material= {{yes|[https://mapcomplete.osm.be/ Yes, by joost schouppe;]}} +|descr= A MapComplete theme: Contribute to OpenStreetMap by filling out address information +|material= {{yes|[https://mapcomplete.osm.be/ Yes, by Pieter Vander Vennet, Rob Nickerson, Russ Garrett;]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, campersite +|genre= POI, editor, uk_addresses }} {{service_item -|name= [https://mapcomplete.osm.be/sport_pitches sport_pitches] +|name= [https://mapcomplete.osm.be/waste_basket waste_basket] |region= Worldwide -|lang= {{#language:nl|en}} -|descr= A MapComplete theme: Een sportveld is een ingerichte plaats met infrastructuur om een sport te beoefenen +|lang= {{#language:en|en}}, {{#language:nl|en}} +|descr= A MapComplete theme: On this map, you'll find waste baskets near you. If a waste basket is missing on this map, you can add it yourself |material= {{yes|[https://mapcomplete.osm.be/ Yes]}} |image= MapComplete_Screenshot.png -|genre= POI, editor, sport_pitches +|genre= POI, editor, waste_basket }} |} \ No newline at end of file From 6890c5189e79d5a4f5d044ba09d3aa69c1c1551d Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 01:12:29 +0200 Subject: [PATCH 045/110] More refactoring --- UI/BigComponents/BackgroundSelector.ts | 4 ++- UI/BigComponents/DownloadPanel.ts | 2 +- UI/BigComponents/ImportButton.ts | 4 +-- UI/BigComponents/SimpleAddUI.ts | 4 +-- UI/Image/DeleteImage.ts | 8 ++--- UI/Image/ImageUploadFlow.ts | 4 +-- UI/Input/DropDown.ts | 2 +- UI/Popup/DeleteWizard.ts | 4 +-- UI/Popup/TagRenderingQuestion.ts | 4 +-- UI/ShowDataLayer/ShowDataLayer.ts | 32 +++++++++++-------- UI/ShowDataLayer/ShowTileInfo.ts | 24 ++------------ ...gregator.ts => TileHierarchyAggregator.ts} | 19 ++++++----- 12 files changed, 52 insertions(+), 59 deletions(-) rename UI/ShowDataLayer/{PerTileCountAggregator.ts => TileHierarchyAggregator.ts} (93%) diff --git a/UI/BigComponents/BackgroundSelector.ts b/UI/BigComponents/BackgroundSelector.ts index d13b4e9c0..f5d1a1415 100644 --- a/UI/BigComponents/BackgroundSelector.ts +++ b/UI/BigComponents/BackgroundSelector.ts @@ -25,7 +25,9 @@ export default class BackgroundSelector extends VariableUiElement { if (baseLayers.length <= 1) { return undefined; } - return new DropDown(Translations.t.general.backgroundMap.Clone(), baseLayers, State.state.backgroundLayer) + return new DropDown(Translations.t.general.backgroundMap.Clone(), baseLayers, State.state.backgroundLayer, { + select_class: 'bg-indigo-100 p-1 rounded hover:bg-indigo-200 w-full' + }) } ) ) diff --git a/UI/BigComponents/DownloadPanel.ts b/UI/BigComponents/DownloadPanel.ts index f23d98747..552f6f357 100644 --- a/UI/BigComponents/DownloadPanel.ts +++ b/UI/BigComponents/DownloadPanel.ts @@ -20,7 +20,7 @@ export class DownloadPanel extends Toggle { constructor() { const state: { featurePipeline: FeaturePipeline, - layoutToUse: UIEventSource, + layoutToUse: LayoutConfig, currentBounds: UIEventSource } = State.state diff --git a/UI/BigComponents/ImportButton.ts b/UI/BigComponents/ImportButton.ts index f62b912fd..160405264 100644 --- a/UI/BigComponents/ImportButton.ts +++ b/UI/BigComponents/ImportButton.ts @@ -31,14 +31,14 @@ export default class ImportButton extends Toggle { const button = new SubtleButton(imageUrl, message) - button.onClick(() => { + button.onClick(async () => { if (isImported.data) { return } originalTags.data["_imported"] = "yes" originalTags.ping() // will set isImported as per its definition const newElementAction = new CreateNewNodeAction(newTags.data, lat, lon) - State.state.changes.applyAction(newElementAction) + await State.state.changes.applyAction(newElementAction) State.state.selectedElement.setData(State.state.allElements.ContainingFeatures.get( newElementAction.newElementId )) diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index ca758ab80..5e8a7b46d 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -56,9 +56,9 @@ export default class SimpleAddUI extends Toggle { const presetsOverview = SimpleAddUI.CreateAllPresetsPanel(selectedPreset) - function createNewPoint(tags: any[], location: { lat: number, lon: number }, snapOntoWay?: OsmWay) { + async function createNewPoint(tags: any[], location: { lat: number, lon: number }, snapOntoWay?: OsmWay) { const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {snapOnto: snapOntoWay}) - State.state.changes.applyAction(newElementAction) + await State.state.changes.applyAction(newElementAction) selectedPreset.setData(undefined) isShown.setData(false) State.state.selectedElement.setData(State.state.allElements.ContainingFeatures.get( diff --git a/UI/Image/DeleteImage.ts b/UI/Image/DeleteImage.ts index fe3f2cf09..800eec53c 100644 --- a/UI/Image/DeleteImage.ts +++ b/UI/Image/DeleteImage.ts @@ -15,15 +15,15 @@ export default class DeleteImage extends Toggle { const isDeletedBadge = Translations.t.image.isDeleted.Clone() .SetClass("rounded-full p-1") .SetStyle("color:white;background:#ff8c8c") - .onClick(() => { - State.state?.changes?.applyAction(new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data)) + .onClick(async() => { + await State.state?.changes?.applyAction(new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data)) }); const deleteButton = Translations.t.image.doDelete.Clone() .SetClass("block w-full pl-4 pr-4") .SetStyle("color:white;background:#ff8c8c; border-top-left-radius:30rem; border-top-right-radius: 30rem;") - .onClick(() => { - State.state?.changes?.applyAction( + .onClick( async() => { + await State.state?.changes?.applyAction( new ChangeTagAction(tags.data.id, new Tag(key, ""), tags.data) ) }); diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 432541395..4eec0b85c 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -29,10 +29,10 @@ export class ImageUploadFlow extends Toggle { key = imagePrefix + ":" + freeIndex; } console.log("Adding image:" + key, url); - State.state.changes + Promise.resolve(State.state.changes .applyAction(new ChangeTagAction( tags.id, new Tag(key, url), tagsSource.data - )) + ))) }) diff --git a/UI/Input/DropDown.ts b/UI/Input/DropDown.ts index 147ad0f50..fe8f8bc98 100644 --- a/UI/Input/DropDown.ts +++ b/UI/Input/DropDown.ts @@ -47,7 +47,7 @@ export class DropDown extends InputElement { } options = options ?? {} - options.select_class = options.select_class ?? 'bg-indigo-100 p-1 rounded hover:bg-indigo-200 w-full' + options.select_class = options.select_class ?? 'bg-indigo-100 p-1 rounded hover:bg-indigo-200' { diff --git a/UI/Popup/DeleteWizard.ts b/UI/Popup/DeleteWizard.ts index b81e82f47..6025ff890 100644 --- a/UI/Popup/DeleteWizard.ts +++ b/UI/Popup/DeleteWizard.ts @@ -51,14 +51,14 @@ export default class DeleteWizard extends Toggle { const confirm = new UIEventSource(false) - function softDelete(reason: string, tagsToApply: { k: string, v: string }[]) { + async function softDelete(reason: string, tagsToApply: { k: string, v: string }[]) { if (reason !== undefined) { tagsToApply.splice(0, 0, { k: "fixme", v: `A mapcomplete user marked this feature to be deleted (${reason})` }) } - (State.state?.changes ?? new Changes()) + await (State.state?.changes ?? new Changes()) .applyAction(new ChangeTagAction( id, new And(tagsToApply.map(kv => new Tag(kv.k, kv.v))), tagsSource.data )) diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 5f0881d29..ea6839e14 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -58,10 +58,10 @@ export default class TagRenderingQuestion extends Combine { console.error("MultiAnswer failed - probably not a single option was possible", configuration) throw "MultiAnswer failed - probably not a single option was possible" } - const save = () => { + const save = async () => { const selection = inputElement.GetValue().data; if (selection) { - (State.state?.changes ?? new Changes()) + await (State.state?.changes ?? new Changes()) .applyAction(new ChangeTagAction( tags.data.id, selection, tags.data )) diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 0d02bcd4e..ade1860ae 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -63,6 +63,7 @@ export default class ShowDataLayer { }) + State.state.selectedElement.addCallbackAndRunD(selected => { if (self._leafletMap.data === undefined) { return; @@ -76,17 +77,20 @@ export default class ShowDataLayer { if (leafletLayer.getPopup().isOpen()) { return; } - if (selected.properties.id === feature.properties.id) { - // A small sanity check to prevent infinite loops: - if (selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again - && feature.id === feature.properties.id // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too - ) { - leafletLayer.openPopup() - } - if (feature.id !== feature.properties.id) { - console.trace("Not opening the popup for", feature) - } - + if (selected.properties.id !== feature.properties.id) { + return; + } + + if (feature.id !== feature.properties.id) { + // Probably a feature which has renamed + console.trace("Not opening the popup for", feature) + return; + } + if (selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again + && feature.id === feature.properties.id // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too + ) { + console.log("Opening popup of feature", feature) + leafletLayer.openPopup() } }) @@ -167,8 +171,10 @@ export default class ShowDataLayer { return; } - const tagSource = feature.properties.id === undefined ? new UIEventSource(feature.properties) : - State.state.allElements.getEventSourceById(feature.properties.id) + let tagSource = State.state.allElements.getEventSourceById(feature.properties.id) + if(tagSource === undefined){ + tagSource = new UIEventSource(feature.properties) + } const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) const style = layer.GenerateLeafletStyle(tagSource, clickable); const baseElement = style.icon.html; diff --git a/UI/ShowDataLayer/ShowTileInfo.ts b/UI/ShowDataLayer/ShowTileInfo.ts index a2fa322b6..1bb636727 100644 --- a/UI/ShowDataLayer/ShowTileInfo.ts +++ b/UI/ShowDataLayer/ShowTileInfo.ts @@ -1,32 +1,14 @@ import FeatureSource, {Tiled} from "../../Logic/FeatureSource/FeatureSource"; import {UIEventSource} from "../../Logic/UIEventSource"; -import {Utils} from "../../Utils"; import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; import ShowDataLayer from "./ShowDataLayer"; import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; import {GeoOperations} from "../../Logic/GeoOperations"; import {Tiles} from "../../Models/TileRange"; - +import * as clusterstyle from "../../assets/layers/cluster_style/cluster_style.json" export default class ShowTileInfo { - public static readonly styling = new LayerConfig({ - id: "tileinfo_styling", - title: { - render: "Tile {z}/{x}/{y}" - }, - tagRenderings: [ - "all_tags" - ], - source: { - osmTags: "tileId~*" - }, - color: {"render": "#3c3"}, - width: { - "render": "1" - }, - label: { - render: "
{count}
" - } - }, "tileinfo", true) + public static readonly styling = new LayerConfig( + clusterstyle, "tileinfo", true) constructor(options: { source: FeatureSource & Tiled, leafletMap: UIEventSource, layer?: LayerConfig, diff --git a/UI/ShowDataLayer/PerTileCountAggregator.ts b/UI/ShowDataLayer/TileHierarchyAggregator.ts similarity index 93% rename from UI/ShowDataLayer/PerTileCountAggregator.ts rename to UI/ShowDataLayer/TileHierarchyAggregator.ts index f313e741f..4b1685981 100644 --- a/UI/ShowDataLayer/PerTileCountAggregator.ts +++ b/UI/ShowDataLayer/TileHierarchyAggregator.ts @@ -22,7 +22,7 @@ export class TileHierarchyAggregator implements FeatureSource { public readonly name; private readonly featuresStatic = [] - private readonly featureProperties: { count: number, tileId: number }; + private readonly featureProperties: { count: string, tileId: string, id: string }; private constructor(parent: TileHierarchyAggregator, z: number, x: number, y: number) { this._parent = parent; @@ -34,8 +34,9 @@ export class TileHierarchyAggregator implements FeatureSource { this.name = "Count(" + this._tileIndex + ")" const totals = { - tileId: this._tileIndex, - count: 0 + id: ""+this._tileIndex, + tileId: ""+this._tileIndex, + count: ""+0 } this.featureProperties = totals @@ -106,7 +107,7 @@ export class TileHierarchyAggregator implements FeatureSource { if (total === 0) { this.features.setData(TileHierarchyAggregator.empty) } else { - this.featureProperties.count = total; + this.featureProperties.count = "" + total; this.features.data = this.featuresStatic this.features.ping() } @@ -145,7 +146,6 @@ export class TileHierarchyAggregator implements FeatureSource { return new TileHierarchyAggregator(undefined, 0, 0, 0) } - private visitSubTiles(f : (aggr: TileHierarchyAggregator) => boolean){ const visitFurther = f(this) if(visitFurther){ @@ -153,8 +153,9 @@ export class TileHierarchyAggregator implements FeatureSource { } } - getCountsForZoom(locationControl: UIEventSource<{ zoom : number }>, cutoff: number) : FeatureSource{ + getCountsForZoom(locationControl: UIEventSource<{ zoom : number }>, cutoff: number = 0) : FeatureSource{ const self = this + return new StaticFeatureSource( locationControl.map(loc => { const features = [] @@ -205,9 +206,11 @@ class SingleTileCounter implements Tiled { const self = this source.features.map(f => { - self.countsPerLayer.data.set(layer.id, f.length) + const isDisplayed = source.layer.isDisplayed.data + self.countsPerLayer.data.set(layer.id, isDisplayed ? f.length : 0) self.countsPerLayer.ping() - }) + }, [source.layer.isDisplayed]) + } From c2d477c97af1d6083a8dfb29fb7c2a40c2082b5c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 01:12:38 +0200 Subject: [PATCH 046/110] More refactoring --- InitUiElements.ts | 5 +- Logic/Actors/TitleHandler.ts | 2 +- Logic/ElementStorage.ts | 7 +- Logic/FeatureSource/FeaturePipeline.ts | 64 ++++++++++--------- .../Sources/FilteringFeatureSource.ts | 20 +----- Logic/MetaTagging.ts | 2 +- Logic/Osm/Changes.ts | 15 ++--- Logic/Tags/RegexTag.ts | 3 + Models/Constants.ts | 2 +- .../layers/cluster_style/cluster_style.json | 36 +++++++++++ .../toerisme_vlaanderen.json | 6 +- langs/themes/nl.json | 2 +- 12 files changed, 91 insertions(+), 73 deletions(-) create mode 100644 assets/layers/cluster_style/cluster_style.json diff --git a/InitUiElements.ts b/InitUiElements.ts index 40932dfd2..d0eb8b0d4 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -25,20 +25,18 @@ import ScrollableFullScreen from "./UI/Base/ScrollableFullScreen"; import Translations from "./UI/i18n/Translations"; import MapControlButton from "./UI/MapControlButton"; import LZString from "lz-string"; -import AllKnownLayers from "./Customizations/AllKnownLayers"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; import LeftControls from "./UI/BigComponents/LeftControls"; import RightControls from "./UI/BigComponents/RightControls"; import {LayoutConfigJson} from "./Models/ThemeConfig/Json/LayoutConfigJson"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; -import LayerConfig from "./Models/ThemeConfig/LayerConfig"; import Minimap from "./UI/Base/Minimap"; import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler"; import Combine from "./UI/Base/Combine"; import {SubtleButton} from "./UI/Base/SubtleButton"; import ShowTileInfo from "./UI/ShowDataLayer/ShowTileInfo"; import {Tiles} from "./Models/TileRange"; -import {TileHierarchyAggregator} from "./UI/ShowDataLayer/PerTileCountAggregator"; +import {TileHierarchyAggregator} from "./UI/ShowDataLayer/TileHierarchyAggregator"; import FilterConfig from "./Models/ThemeConfig/FilterConfig"; import FilteredLayer from "./Models/FilteredLayer"; import {BBox} from "./Logic/BBox"; @@ -435,6 +433,7 @@ export class InitUiElements { features: clusterCounter.getCountsForZoom(State.state.locationControl, State.state.layoutToUse.clustering.minNeededElements), leafletMap: State.state.leafletMap, layerToShow: ShowTileInfo.styling, + enablePopups: false }) State.state.featurePipeline = new FeaturePipeline( diff --git a/Logic/Actors/TitleHandler.ts b/Logic/Actors/TitleHandler.ts index 699a33aa8..91ec89b0c 100644 --- a/Logic/Actors/TitleHandler.ts +++ b/Logic/Actors/TitleHandler.ts @@ -29,7 +29,7 @@ export default class TitleHandler { if (layer.source.osmTags.matchesProperties(tags)) { const tagsSource = state.allElements.getEventSourceById(tags.id) const title = new TagRenderingAnswer(tagsSource, layer.title) - return new Combine([defaultTitle, " | ", title]).ConstructElement().innerText; + return new Combine([defaultTitle, " | ", title]).ConstructElement()?.innerText ?? defaultTitle; } } return defaultTitle diff --git a/Logic/ElementStorage.ts b/Logic/ElementStorage.ts index 91193ea6d..ae51094ae 100644 --- a/Logic/ElementStorage.ts +++ b/Logic/ElementStorage.ts @@ -39,11 +39,10 @@ export class ElementStorage { } getEventSourceById(elementId): UIEventSource { - if (this._elements.has(elementId)) { - return this._elements.get(elementId); + if(elementId === undefined){ + return undefined; } - console.error("Can not find eventsource with id ", elementId); - return undefined; + return this._elements.get(elementId); } has(id) { diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index d65771da2..e8d270f0b 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -62,11 +62,35 @@ export default class FeaturePipeline { /** * Maps tileid onto last download moment */ - const tileFreshnesses = new Map() + const tileFreshnesses = new UIEventSource>(new Map()) const osmSourceZoomLevel = 14 const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12)) this.relationTracker = new RelationsTracker() + console.log("Tilefreshnesses are", tileFreshnesses.data) + const oldestAllowedDate = new Date(new Date().getTime() - (60 * 60 * 24 * 30 * 1000)); + const neededTilesFromOsm = state.currentBounds.map(bbox => { + if (bbox === undefined) { + return + } + const range = bbox.containingTileRange(osmSourceZoomLevel) + const tileIndexes = [] + if (range.total > 100) { + // Too much tiles! + return [] + } + Tiles.MapRange(range, (x, y) => { + const i = Tiles.tile_index(osmSourceZoomLevel, x, y); + if (tileFreshnesses.data.get(i) > oldestAllowedDate) { + console.debug("Skipping tile", osmSourceZoomLevel, x, y, "as a decently fresh one is available") + // The cached tiles contain decently fresh data + return; + } + tileIndexes.push(i) + }) + return tileIndexes + }, [tileFreshnesses]) + const updater = new OverpassFeatureSource(state, { relationTracker: this.relationTracker, @@ -75,8 +99,9 @@ export default class FeaturePipeline { // This callback contains metadata of the overpass call const range = bbox.containingTileRange(osmSourceZoomLevel) Tiles.MapRange(range, (x, y) => { - tileFreshnesses.set(Tiles.tile_index(osmSourceZoomLevel, x, y), freshness) + tileFreshnesses.data.set(Tiles.tile_index(osmSourceZoomLevel, x, y), freshness) }) + tileFreshnesses.ping(); } }); @@ -137,17 +162,17 @@ export default class FeaturePipeline { }, state) localStorage.tileFreshness.forEach((value, key) => { - if (tileFreshnesses.has(key)) { - const previous = tileFreshnesses.get(key) + if (tileFreshnesses.data.has(key)) { + const previous = tileFreshnesses.data.get(key) if (value < previous) { - tileFreshnesses.set(key, value) + tileFreshnesses.data.set(key, value) } } else { - tileFreshnesses.set(key, value) + tileFreshnesses.data.set(key, value) } + tileFreshnesses.ping() }) - continue } @@ -178,30 +203,7 @@ export default class FeaturePipeline { } } - console.log("Tilefreshnesses are", tileFreshnesses) - const oldestAllowedDate = new Date(new Date().getTime() - (60 * 60 * 24 * 30 * 1000)); - - const neededTilesFromOsm = state.currentBounds.map(bbox => { - if (bbox === undefined) { - return - } - const range = bbox.containingTileRange(osmSourceZoomLevel) - const tileIndexes = [] - if (range.total > 100) { - // Too much tiles! - return [] - } - Tiles.MapRange(range, (x, y) => { - const i = Tiles.tile_index(osmSourceZoomLevel, x, y); - if (tileFreshnesses.get(i) > oldestAllowedDate) { - console.debug("Skipping tile", osmSourceZoomLevel, x, y, "as a decently fresh one is available") - // The cached tiles contain decently fresh data - return; - } - tileIndexes.push(i) - }) - return tileIndexes - }) + const osmFeatureSource = new OsmFeatureSource({ isActive: useOsmApi, diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index 6b433f88d..0c2c9d92a 100644 --- a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -28,8 +28,9 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti this.layer = upstream.layer; const layer = upstream.layer; - + function update() { + const features: { feature: any; freshness: Date }[] = upstream.features.data; const newFeatures = features.filter((f) => { if ( @@ -60,11 +61,6 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti } - if (!layer.isDisplayed) { - // The layer itself is either disabled or hidden due to zoom constraints - // We should return true, but it might still match some other layer - return false; - } return true; }); @@ -75,20 +71,8 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti update(); }); - layer.isDisplayed.addCallback(isShown => { - if (isShown) { - update(); - } else { - self.features.setData([]) - } - }); layer.appliedFilters.addCallback(_ => { - if (!layer.isDisplayed.data) { - // Currently not shown. - // Note that a change in 'isSHown' will trigger an update as well, so we don't have to watch it another time - return; - } update() }) diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index 2a85e6292..ee4209f4a 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -63,7 +63,7 @@ export default class MetaTagging { } somethingChanged = somethingChanged || metatag.applyMetaTagsOnFeature(feature, freshness) } catch (e) { - console.error("Could not calculate metatag for ", metatag.keys.join(","), ":", e) + console.error("Could not calculate metatag for ", metatag.keys.join(","), ":", e, e.stack) } } diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index b4bf3b4ec..570bf553b 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -149,14 +149,13 @@ export class Changes { } - public applyAction(action: OsmChangeAction) { - action.Perform(this).then(changes => { - console.log("Received changes:", changes) - this.pendingChanges.data.push(...changes); - this.pendingChanges.ping(); - this.allChanges.data.push(...changes) - this.allChanges.ping() - }) + public async applyAction(action: OsmChangeAction): Promise { + const changes = await action.Perform(this) + console.log("Received changes:", changes) + this.pendingChanges.data.push(...changes); + this.pendingChanges.ping(); + this.allChanges.data.push(...changes) + this.allChanges.ping() } private CreateChangesetObjects(changes: ChangeDescription[], downloadedOsmObjects: OsmObject[]): { diff --git a/Logic/Tags/RegexTag.ts b/Logic/Tags/RegexTag.ts index 20fbbbcbf..fae2fd70b 100644 --- a/Logic/Tags/RegexTag.ts +++ b/Logic/Tags/RegexTag.ts @@ -19,6 +19,9 @@ export class RegexTag extends TagsFilter { if (fromTag === undefined) { return; } + if(typeof fromTag === "number"){ + fromTag = "" + fromTag; + } if (typeof possibleRegex === "string") { return fromTag === possibleRegex; } diff --git a/Models/Constants.ts b/Models/Constants.ts index 91c9e0015..a7f79bf39 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.0-alpha-3"; + public static vNumber = "0.10.0-alpha-4"; public static ImgurApiKey = '7070e7167f0a25a' // The user journey states thresholds when a new feature gets unlocked diff --git a/assets/layers/cluster_style/cluster_style.json b/assets/layers/cluster_style/cluster_style.json new file mode 100644 index 000000000..ea6760612 --- /dev/null +++ b/assets/layers/cluster_style/cluster_style.json @@ -0,0 +1,36 @@ +{ + "id": "cluster_style", + "description": "The style for the clustering in all themes.", + "source": { + "osmTags": "tileId~*" + }, + "color": { + "render": "#3c3", + "mappings": [ + { + "if": "count>200", + "then": "#f33" + }, + { + "if": "count>100", + "then": "#c93" + }, + { + "if": "count>50", + "then": "#cc3" + } + ] + }, + "width": { + "render": "1" + }, + "label": { + "render": "
{count}
", + "mappings": [ + { + "if": "count>99", + "then": "
>99
" + } + ] + } +} \ No newline at end of file diff --git a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json index aeea7f806..2b303ea14 100644 --- a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json +++ b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json @@ -14,7 +14,7 @@ "nl": "Een kaart om toeristisch relevante info op aan te duiden" }, "description": { - "nl": "Op deze kaart kan je info zien voor toeristen en zelf aanpasingen maken, zichtbaar voor iedereen" + "nl": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:
  • Eetgelegenheden
  • Cafés en bars
  • (Fiets)oplaadpunten
  • Fietspompen, fietserverhuur en fietswinkels
  • Uitkijktorens
  • ...
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.

Met de steun van Toerisme Vlaanderen" }, "icon": "./assets/svg/star.svg", "startZoom": 8, @@ -39,9 +39,5 @@ "binocular", "observation_tower" ], - "overiddeAll": { - "minZoomVisible": 0 - }, - "hideFromOverview": true } \ No newline at end of file diff --git a/langs/themes/nl.json b/langs/themes/nl.json index a1e3dee82..e3220df4d 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -1008,7 +1008,7 @@ "title": "Surveillance under Surveillance" }, "toerisme_vlaanderen": { - "description": "Op deze kaart kan je info zien voor toeristen en zelf aanpasingen maken, zichtbaar voor iedereen", + "description": "Op deze kaart kan je info zien die relevant is voor toerisme, zoals:
  • Eetgelegenheden
  • Cafés en bars
  • (Fiets)oplaadpunten
  • Fietspompen, fietserverhuur en fietswinkels
  • Uitkijktorens
  • ...
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.

Met de steun van Toerisme Vlaanderen", "shortDescription": "Een kaart om toeristisch relevante info op aan te duiden", "title": "Toeristisch relevante info" }, From 09897b47e04e50ab748b88d46df38597d1108cba Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 16:55:05 +0200 Subject: [PATCH 047/110] Add mssing assets --- Logic/Actors/OverpassFeatureSource.ts | 108 ++++++++---------- Logic/FeatureSource/FeaturePipeline.ts | 22 ++-- Logic/Osm/Overpass.ts | 12 +- Models/Bounds.ts | 6 - Models/Constants.ts | 10 ++ Models/ThemeConfig/Json/LayoutConfigJson.ts | 4 +- Models/ThemeConfig/LayoutConfig.ts | 12 +- State.ts | 6 +- Utils.ts | 1 + .../toerisme_vlaanderen/license_info.json | 12 ++ assets/themes/toerisme_vlaanderen/logo.png | Bin 0 -> 11674 bytes 11 files changed, 104 insertions(+), 89 deletions(-) delete mode 100644 Models/Bounds.ts create mode 100644 assets/themes/toerisme_vlaanderen/license_info.json create mode 100644 assets/themes/toerisme_vlaanderen/logo.png diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index 248703954..fa5de3eed 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -2,7 +2,6 @@ import {UIEventSource} from "../UIEventSource"; import Loc from "../../Models/Loc"; import {Or} from "../Tags/Or"; import {Overpass} from "../Osm/Overpass"; -import Bounds from "../../Models/Bounds"; import FeatureSource from "../FeatureSource/FeatureSource"; import {Utils} from "../../Utils"; import {TagsFilter} from "../Tags/TagsFilter"; @@ -36,16 +35,17 @@ export default class OverpassFeatureSource implements FeatureSource { * If the map location changes, we check for each layer if it is loaded: * we start checking the bounds at the first zoom level the layer might operate. If in bounds - no reload needed, otherwise we continue walking down */ - private readonly _previousBounds: Map = new Map(); + private readonly _previousBounds: Map = new Map(); private readonly state: { readonly locationControl: UIEventSource, readonly layoutToUse: LayoutConfig, - readonly overpassUrl: UIEventSource; + readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; - readonly currentBounds :UIEventSource + readonly currentBounds: UIEventSource } private readonly _isActive: UIEventSource; private _onUpdated?: (bbox: BBox, dataFreshness: Date) => void; + /** * The most important layer should go first, as that one gets first pick for the questions */ @@ -53,20 +53,20 @@ export default class OverpassFeatureSource implements FeatureSource { state: { readonly locationControl: UIEventSource, readonly layoutToUse: LayoutConfig, - readonly overpassUrl: UIEventSource; + readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; readonly overpassMaxZoom: UIEventSource, - readonly currentBounds :UIEventSource - }, - - options?: { + readonly currentBounds: UIEventSource + }, + options?: { isActive?: UIEventSource, - onUpdated?: (bbox: BBox, freshness: Date) => void, - relationTracker: RelationsTracker}) { + onUpdated?: (bbox: BBox, freshness: Date) => void, + relationTracker: RelationsTracker + }) { this.state = state this._isActive = options.isActive; - this._onUpdated =options. onUpdated; + this._onUpdated = options.onUpdated; this.relationsTracker = options.relationTracker const location = state.locationControl const self = this; @@ -79,14 +79,14 @@ export default class OverpassFeatureSource implements FeatureSource { location.addCallback(() => { self.update() }); - + state.currentBounds.addCallback(_ => { self.update() }) - + } - private GetFilter(): Overpass { + private GetFilter(interpreterUrl: string): Overpass { let filters: TagsFilter[] = []; let extraScripts: string[] = []; for (const layer of this.state.layoutToUse.layers) { @@ -113,7 +113,7 @@ export default class OverpassFeatureSource implements FeatureSource { continue; } for (const previousLoadedBound of previousLoadedBounds) { - previouslyLoaded = previouslyLoaded || this.IsInBounds(previousLoadedBound); + previouslyLoaded = previouslyLoaded || this.state.currentBounds.data.isContainedIn(previousLoadedBound); if (previouslyLoaded) { break; } @@ -133,16 +133,16 @@ export default class OverpassFeatureSource implements FeatureSource { if (filters.length + extraScripts.length === 0) { return undefined; } - return new Overpass(new Or(filters), extraScripts, this.state.overpassUrl, this.state.overpassTimeout, this.relationsTracker); + return new Overpass(new Or(filters), extraScripts, interpreterUrl, this.state.overpassTimeout, this.relationsTracker); } private update() { - if(!this._isActive.data){ + if (!this._isActive.data) { return; } const self = this this.updateAsync().then(bboxAndDate => { - if(bboxAndDate === undefined || self._onUpdated === undefined){ + if (bboxAndDate === undefined || self._onUpdated === undefined) { return; } const [bbox, date] = bboxAndDate @@ -162,51 +162,54 @@ export default class OverpassFeatureSource implements FeatureSource { } const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.widenFactor)?.expandToTileBounds(14); - + if (bounds === undefined) { return undefined; } - - const n = Math.min(90, bounds.getNorth()); - const e = Math.min(180, bounds.getEast()); - const s = Math.max(-90, bounds.getSouth()); - const w = Math.max(-180, bounds.getWest()); - const queryBounds = {north: n, east: e, south: s, west: w}; - - const self = this; - const overpass = this.GetFilter(); - - if (overpass === undefined) { - return undefined; - } - this.runningQuery.setData(true); let data: any = undefined let date: Date = undefined + const overpassUrls = self.state.overpassUrl.data + let lastUsed = 0; do { - try { - [data, date] = await overpass.queryGeoJson(queryBounds) + const overpass = this.GetFilter(overpassUrls[lastUsed]); + + if (overpass === undefined) { + return undefined; + } + this.runningQuery.setData(true); + + [data, date] = await overpass.queryGeoJson(bounds) console.log("Querying overpass is done", data) } catch (e) { self.retries.data++; self.retries.ping(); - console.error(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to`, e); + console.error(`QUERY FAILED due to`, e); - self.timeout.setData(self.retries.data * 5); - - while (self.timeout.data > 0) { - await Utils.waitFor(1000) - self.timeout.data-- - self.timeout.ping(); + await Utils.waitFor(1000) + + if (lastUsed + 1 < overpassUrls.length) { + lastUsed++ + console.log("Trying next time with", overpassUrls[lastUsed]) + } else { + lastUsed = 0 + self.timeout.setData(self.retries.data * 5); + + while (self.timeout.data > 0) { + await Utils.waitFor(1000) + console.log(self.timeout.data) + self.timeout.data-- + self.timeout.ping(); + } } } } while (data === undefined); const z = Math.floor(this.state.locationControl.data.zoom ?? 0); - self._previousBounds.get(z).push(queryBounds); + self._previousBounds.get(z).push(bounds); self.retries.setData(0); try { @@ -215,25 +218,10 @@ export default class OverpassFeatureSource implements FeatureSource { return [bounds, date]; } catch (e) { console.error("Got the overpass response, but could not process it: ", e, e.stack) - }finally { + } finally { self.runningQuery.setData(false); } - } - - private IsInBounds(bounds: Bounds): boolean { - if (this._previousBounds === undefined) { - return false; - } - - const b = this.state.currentBounds.data; - return b.getSouth() >= bounds.south && - b.getNorth() <= bounds.north && - b.getEast() <= bounds.east && - b.getWest() >= bounds.west; - } - - } \ No newline at end of file diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index e8d270f0b..3db3a039d 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -30,10 +30,10 @@ import {Tiles} from "../../Models/TileRange"; export default class FeaturePipeline { public readonly sufficientlyZoomed: UIEventSource; - + public readonly runningQuery: UIEventSource; public readonly timeout: UIEventSource; - + public readonly somethingLoaded: UIEventSource = new UIEventSource(false) public readonly newDataLoadedSignal: UIEventSource = new UIEventSource(undefined) @@ -50,7 +50,7 @@ export default class FeaturePipeline { readonly changes: Changes, readonly layoutToUse: LayoutConfig, readonly leafletMap: any, - readonly overpassUrl: UIEventSource; + readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; readonly overpassMaxZoom: UIEventSource; readonly osmConnection: OsmConnection @@ -67,6 +67,7 @@ export default class FeaturePipeline { const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12)) this.relationTracker = new RelationsTracker() + console.log("Tilefreshnesses are", tileFreshnesses.data) const oldestAllowedDate = new Date(new Date().getTime() - (60 * 60 * 24 * 30 * 1000)); const neededTilesFromOsm = state.currentBounds.map(bbox => { @@ -90,7 +91,7 @@ export default class FeaturePipeline { }) return tileIndexes }, [tileFreshnesses]) - + const updater = new OverpassFeatureSource(state, { relationTracker: this.relationTracker, @@ -105,8 +106,10 @@ export default class FeaturePipeline { } }); - + this.overpassUpdater = updater; + this.timeout = updater.timeout + this.sufficientlyZoomed = state.locationControl.map(location => { if (location?.zoom === undefined) { return false; @@ -115,10 +118,10 @@ export default class FeaturePipeline { return location.zoom >= minzoom; } ); - + this.timeout = updater.timeout - - + + // Register everything in the state' 'AllElements' new RegisteringAllFromFeatureSourceActor(updater) @@ -203,9 +206,8 @@ export default class FeaturePipeline { } } - - const osmFeatureSource = new OsmFeatureSource({ + const osmFeatureSource = new OsmFeatureSource({ isActive: useOsmApi, neededTiles: neededTilesFromOsm, handleTile: tile => { diff --git a/Logic/Osm/Overpass.ts b/Logic/Osm/Overpass.ts index 2bf1b4e70..47b92e278 100644 --- a/Logic/Osm/Overpass.ts +++ b/Logic/Osm/Overpass.ts @@ -1,9 +1,9 @@ import * as OsmToGeoJson from "osmtogeojson"; -import Bounds from "../../Models/Bounds"; import {TagsFilter} from "../Tags/TagsFilter"; import RelationsTracker from "./RelationsTracker"; import {Utils} from "../../Utils"; import {UIEventSource} from "../UIEventSource"; +import {BBox} from "../BBox"; /** * Interfaces overpass to get all the latest data @@ -11,7 +11,7 @@ import {UIEventSource} from "../UIEventSource"; export class Overpass { public static testUrl: string = null private _filter: TagsFilter - private readonly _interpreterUrl: UIEventSource; + private readonly _interpreterUrl: string; private readonly _timeout: UIEventSource; private readonly _extraScripts: string[]; private _includeMeta: boolean; @@ -19,7 +19,7 @@ export class Overpass { constructor(filter: TagsFilter, extraScripts: string[], - interpreterUrl: UIEventSource, + interpreterUrl: string, timeout: UIEventSource, relationTracker: RelationsTracker, includeMeta = true) { @@ -31,9 +31,9 @@ export class Overpass { this._relationTracker = relationTracker } - public async queryGeoJson(bounds: Bounds): Promise<[any, Date]> { + public async queryGeoJson(bounds: BBox): Promise<[any, Date]> { - let query = this.buildQuery("[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]") + let query = this.buildQuery("[bbox:" + bounds.getSouth() + "," + bounds.getWest() + "," + bounds.getNorth() + "," + bounds.getEast() + "]") if (Overpass.testUrl !== null) { console.log("Using testing URL") @@ -68,6 +68,6 @@ export class Overpass { } const query = `[out:json][timeout:${this._timeout.data}]${bbox};(${filter});out body;${this._includeMeta ? 'out meta;' : ''}>;out skel qt;` - return `${this._interpreterUrl.data}?data=${encodeURIComponent(query)}` + return `${this._interpreterUrl}?data=${encodeURIComponent(query)}` } } diff --git a/Models/Bounds.ts b/Models/Bounds.ts deleted file mode 100644 index 3a993c8e0..000000000 --- a/Models/Bounds.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default interface Bounds { - north: number, - east: number, - south: number, - west: number -} \ No newline at end of file diff --git a/Models/Constants.ts b/Models/Constants.ts index a7f79bf39..b9855a293 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -4,6 +4,16 @@ export default class Constants { public static vNumber = "0.10.0-alpha-4"; public static ImgurApiKey = '7070e7167f0a25a' + public static defaultOverpassUrls = [ + // The official instance, 10000 queries per day per project allowed + "https://overpass-api.de/api/interpreter", + // 'Fair usage' + "https://overpass.kumi.systems/api/interpreter", + // "https://overpass.nchc.org.tw/api/interpreter", + "https://overpass.openstreetmap.ru/cgi/interpreter", + // The french api, only 1000 per day per project allowed, so we put it as last resort + "https://overpass.openstreetmap.fr/api/interpreter" + ] // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/Models/ThemeConfig/Json/LayoutConfigJson.ts b/Models/ThemeConfig/Json/LayoutConfigJson.ts index 887649fc4..73caee792 100644 --- a/Models/ThemeConfig/Json/LayoutConfigJson.ts +++ b/Models/ThemeConfig/Json/LayoutConfigJson.ts @@ -263,9 +263,9 @@ export interface LayoutConfigJson { enablePdfDownload?: boolean; /** - * Set a different overpass URL. Default: https://overpass-api.de/api/interpreter + * Set one or more overpass URLs to use for this theme.. */ - overpassUrl?: string; + overpassUrl?: string | string[]; /** * Set a different timeout for overpass queries - in seconds. Default: 30s */ diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index 9e6325039..4f8551632 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -6,6 +6,7 @@ import AllKnownLayers from "../../Customizations/AllKnownLayers"; import {Utils} from "../../Utils"; import LayerConfig from "./LayerConfig"; import {LayerConfigJson} from "./Json/LayerConfigJson"; +import Constants from "../Constants"; export default class LayoutConfig { public readonly id: string; @@ -50,7 +51,7 @@ export default class LayoutConfig { How long is the cache valid, in seconds? */ public readonly cacheTimeout?: number; - public readonly overpassUrl: string; + public readonly overpassUrl: string[]; public readonly overpassTimeout: number; public readonly official: boolean; @@ -157,7 +158,14 @@ export default class LayoutConfig { this.enablePdfDownload = json.enablePdfDownload ?? false; this.customCss = json.customCss; this.cacheTimeout = json.cacheTimout ?? (60 * 24 * 60 * 60) - this.overpassUrl = json.overpassUrl ?? "https://overpass-api.de/api/interpreter" + this.overpassUrl = Constants.defaultOverpassUrls + if(json.overpassUrl !== undefined){ + if(typeof json.overpassUrl === "string"){ + this.overpassUrl = [json.overpassUrl] + }else{ + this.overpassUrl = json.overpassUrl + } + } this.overpassTimeout = json.overpassTimeout ?? 30 } diff --git a/State.ts b/State.ts index 0af8e5a64..00dac3bb5 100644 --- a/State.ts +++ b/State.ts @@ -81,7 +81,7 @@ export default class State { public readonly featureSwitchEnableExport: UIEventSource; public readonly featureSwitchFakeUser: UIEventSource; public readonly featureSwitchExportAsPdf: UIEventSource; - public readonly overpassUrl: UIEventSource; + public readonly overpassUrl: UIEventSource; public readonly overpassTimeout: UIEventSource; @@ -321,9 +321,9 @@ export default class State { ); this.overpassUrl = QueryParameters.GetQueryParameter("overpassUrl", - layoutToUse?.overpassUrl, + layoutToUse?.overpassUrl.join(","), "Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter" - ) + ).map(param => param.split(","), [], urls => urls.join(",")) this.overpassTimeout = QueryParameters.GetQueryParameter("overpassTimeout", "" + layoutToUse?.overpassTimeout, diff --git a/Utils.ts b/Utils.ts index 424784205..bd5cd62a5 100644 --- a/Utils.ts +++ b/Utils.ts @@ -320,6 +320,7 @@ export class Utils { } xhr.send(); + xhr.onerror = reject } ) } diff --git a/assets/themes/toerisme_vlaanderen/license_info.json b/assets/themes/toerisme_vlaanderen/license_info.json new file mode 100644 index 000000000..374abea1e --- /dev/null +++ b/assets/themes/toerisme_vlaanderen/license_info.json @@ -0,0 +1,12 @@ +[ + { + "path": "logo.png", + "license": "Logo (all rights reserved)", + "authors": [ + "Toerisme Vlaanderen" + ], + "sources": [ + "https://www.toerismevlaanderen.be/" + ] + } +] \ No newline at end of file diff --git a/assets/themes/toerisme_vlaanderen/logo.png b/assets/themes/toerisme_vlaanderen/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6d0d16dd8458cd28b8130b2250618188a2eb8601 GIT binary patch literal 11674 zcmaKSWmuG5*EZeV&Ct>vLk}R0(um{?j5Gs7N_Q&KqKJTi63S509nv8VAt@!@-QVbQ z-|zeU_>TAbF|)6|*LkkB_S*ZJ+1GK#>p#*UCSV{yLqj9h(o{1?20)+=PVNfq`%P`^04I9|b~8ym5j_u8sH2l+032!* z@W|LMz{L(|&#t5hkoT8G5x7CU!2o|ZS9dR2e+Blxyt1hFpKf7xz+V$@7X|kJ1ocEu zAD{|@LjjUPQi66OQqllvppdAfw6uh@06DtT6=jLLjO06|F5-|@pBKTup!h7<^#7w)#Dk*zhKnd{ojuMHMF<;A3F~pxa;3M z?d^o2u246qySEoADAB(Wp(2-k40l2m5bUZ3v-9~gs->pD?&ITRFDt1gCL#kAlK_gV zsH>@niULI?Kr)g*5jAm9Nf8-UnLp0{5d1d~)gxq&!m6o=t4OJ+ONawSQP)FB88HcU zkcy0ygouioxHw4dU#yn9mp9nm4*JLGAFR{=V#WVYtgI>=3igJb^0&Q|4A1Jg^EGI5J5=^dx#(uEFmK( zBPH`pPy#F>Eg@kqBl=8Qg8d(`{eR8XzbUJOq}4@5r6ok9#YCiJL`7vlQffeul%$xd zn2egLh%~z}DjIt`S#KwASLi>B6XfdsPs`Qm&jOKk1-m<-Wdmfb>Iw$C z+e6_{cYu=@z#E3j!^;h7;|g8{2@2 zYWAy8Ra`)blfVWh?8DEN*2v!fw7ST?th4BSo!$z8h91!-`i>qFc{MvNq4r1O2&U4deT5f!G=P z2*8Wze$T3VG60am1d(x`-Pop^Jq;`p&W-@!Q3NWA4D#m&Kyjt8|Bn4~LJ--DlVO1% zqonEy`^-OL|6#kB;NO1^CH>b-6oIUy$BO_|uHzQ8jCb(=8%}tG_2o4GbrtV_#?JL2 zJb&B^%Wq|5TK|mthb(J7W7GQQKY=RMkMc8C{X?d?*3`#uQTZP@CctN>yoMPSXk0Vp zwezwA$W_2lj<^^LwEA{hE^v+F5YwN5uo5#87=#zm$6uMtb0?&l!)Vad!l%2YL#Fm9 zJ~+yZdYqbfdVUq5)w;*!KlDG-Zx9K zm8Il`M~-DM*u4PaGNB`Fq8kaF@0%g%&{k*}f7cZUh17#X9H;cxr$fRh#SsURwWRb+VA*V8~l*_D6=HJgBGAQ zaGsyTYdR(^I>0vq;2bvnD)R#EU2IF0-s%(FjG{K7(cwU6-{GY-J^x({RhjyTLTM;0IjD%$zl(n{5K2nI69K5w-U&U@h7F!C z9t~_+H%!A>>=!2abGpy{VAQhb)n?!Pi{6+$8ZehrEa0a`l|`4LNswz-&)R&7{|D35 ztkTK-Q|rsNzDwHE{Ysqp)=c!TRED&|gKQ7U)M(;^1-mN6Ti}jEUjuYhRkxD+*72`s z_riNui}Ga5RIdLB@v~yiLKi0zm@_^=4f14mo(JGsTq5L&9 z#$Ci(w{#7nF!#G3$4~h;*jLVOhi+PARgDSzx}MPlu4*yy!I^goVjcVEG)hOvC|I-D zjK@v%*u6^twD@8{j9Zs_ady>8^g4cVD+5zCH}`m;-1_Ht{;tA>o+`uk>RlDcD{ND# z0)chSou57N*1!pm8+wYsTHEnT4z)Y@x7#U1<0OxU z6nt$yGCZlpp^tDy=roQpPhHn({{j{AYhD}PvVNPNKe_Mc>**4i9p$gvq_{65_{zim zS=eX1i97O^SPq-0St&NDL;dgz5;kr{Bz2~3vukHtdGWo^sQJi#BQWuDi(CyFuP}0f zhkxzcd->e3jkGlJ0@EqLiECGeJxb`_=Qd<^%#%)c(Et*l_lo#O8?(=*za{ppcWSBw zO+0hV`nJHjR2tWWee^Hc9Q^`1ayHc77>KK}SYc*W-9l@AvXFQ=;*v7jMS;A{N8&0I zlicE{UDb_Arl{A2ZuoT-RWZ7$YRp-gv6*9zv&ugM)*0a!`cRf|>~^;VP2jO-cZZ}* zPw=DLS~e(ke6^lh3k?%YPhJsu8qW~MjK(6ltGZ5Z>0@Kh6>*HoH~mq+C+IV!B@K8x z7qUy<^fc-Iap`)5HW{Zc?%vUWljtH)9Q_bCY<(Cx=71&TmT^!wmW?PV!_ehaNM0{vKR#3TA>D!UG%Z-hK&+t_9c z1Z90F34cD~Q=vv%*`Copa0p!EJ$#8+Pseul`&vO zxfi6dYtg*!5gH;Kp8T4MjHUK;0ARH#$oRZG{_0Lh*W*i(&&`rG?S{?8#4G$;meCkT zzhe8QQe`=dYa)3pxv_2ht*t^8KG%r8M~QgOocF3*=~L;1i@MV|^Cy>>Q??$0>>If+ zYkshPJu&F`TBNvc#>e!ETy@AGt+Dathu*6VTOY1z6`^!DPQ^H<;NV~QkOjrPg1APE zYzV8V3PxD$fb~_Us~PwR>!)&CP!!4JXLMi6v;;vnRw5d&sC~c2fU5-$63X|yM<)SJ zSzACfl_^r>M8{j+J<`I!8UcE$b&Q(>a$Hg4@uJK-#D!F@Ieu%?dvFhf{E9@&V!^2w z)4^?5&%0I*d}Tw2t${|Z@Tsu6nO*$0#LwjpTu&aV%0wod?`7@#OtijqW)(TJ?QK$D z?5KCRPP0qx?)J)Wr5%QpW4>N05V&Q;`EXW%{(hhD9#SQ%(R+P_KXby%jA`UrO1HY> zFA#%V92;1Cq`%aEHRbCi%!m`jTbXosTSd?fG;)~8;mod}myFeHbh~qP&N`$q6vpC- z+uysIN-q@R8y-%!e2i%3`C;m@f>Q#NS+vDimrQh%vP{W2{^(SlxU3a?6+@v5SN)eoUPT!I1f0K}RMl z!uDOiA+v7s&c~ykmS8)X)>M+;g*wi&-KVzr`#$dY<%f=rAL^Ex*tZ=Kr=N|ZR+~b? z+Z1$CahFd`+3B9+e}s=vJ?5=HX;wjH|UY z!a_D#n?P~B2w_;pR%io&xo96gGoiGu8}cvel)Mr%Vy2Az7LieR8`CF z;kM07bf%(p)X(R23Xj}J&7$I&Sww`CPD}WD1a6Z-B2_Q@bP7~vVePeaxTY()l2(?#Szs}m9qw#`?;Iw`iU)0>yLEhVikD^T^ z^6#F>n?yGAJ~LZG`;-W)oj;tD*Rd{a#?vndc(_mD&En~#Nl)4RwgUN)If@ec2Uz2BdV4#Ju2Tlnzmn2wjcJ{ zZuu;IG$Qcltmj?O7ep@2P3YIPCK1~`lV8`;63EQk8fM%F<|-m_rN7kU=D%U|lDm@C zB-H8%x0DOgQhd{SddjdB#waasVftgS;IXZrxzQFSU`E8Bx1~XW$tL1;=QTbI5A)76 zv3ox?wX+b;b4x|eB_ zv09r6QT+1{GSgPw^P(GJ?sh=Mf$vjG)UW&5@;`Bo4rXwRav0lj=L8yAcK;rTj2kWl zF+W*mUQS-NA_zs26K_pxA4SI+k59`)p4aN{s3XBi(32_}rYocrdvh@`RYu=ZTuoq+f=6+|K=e+BDd0P!_tYaa` z|7GBb3;GajVKMSm$S$y))0Y)qZ_!CJ(Rmo?fAIyg(k%@hkaR2f3d)Lb*+b~1=doM5d$K&o|GpE9S3(#V|S5{ z6Z$={Q<&Z2^A^`C|M3OqE!T~;pH3k7tAUW2ZMOZqg--qhho~KRlBda#b6^QounC34 zM3Jh*J1MP&_{=Nz*%t;Y6q?&$NIb--pO$+gk16^4bu~8C!cP@CDaZmesLayrK@3@NBj$|qG^>>uuR#$ZE8A- zyt!aanb5P$LpuqOvpdWek6n1IH?&{Dt#qt%^RfjenWXV69NJeCg;f9dCS z5&?4aF;(B+H4$WF;KWDa_-hypdSe`yC`wiLM7 z5f|RH40tewQ_h-)$XOJfxs$7yOFMKcTJtFAet2bnaJNV6Wu48Yy(86TuNK*A_%V=S z1AyM_s`>i*Jrb6J*8$GF+xtd!T3_YCj<-o-<(dD!{V^*RcI3rFdR9NF)aWJp@}9?; z`6I7--mze9HGcl)jg^+EGY{Xp8hrQ6U&~?E3CKQi zR+$@|E5Z53gru#_qADaZuO{+&zf<4lW#b&{I zGJgj^S<( zZ1w$ib!yPt5K3Fo`KoKMQC52C7e*g5We``hDZu}yJj|WEsSNR@Mb@~W`bQLkU34TN z=~(~!kmdu>8+q$r(yV%7QI20E;Sav5j%KYHw>mXr?(rYrP7__u_P&s`z~Fn)!WOe4 zh55ndRm1{haR6q9yJ?15KFo)+p*@^gz+5Yg#m16dHImJ=K4My;xOcnv>1~)Fr{d^i zUbLp+c~Sn9K?oiri->+5PVJinj!hyW{A2yz=f&pb2@UE`W*F@^PFTGmaoQUqd5XN3 zodvQL*;-I`$se|FYtQqd7Fo@HUsrN{lGPf0n1%$qG&Vt^$dc!r_)11dCaTMpn%1`y zI7^=@U(YdcFoAu1KeW!ZnKangj5$nCR(9gUbK1$0HXd(uN>AdKCZjRp1>yp^>)p~d zZA`)II&M68-YqA79a}8@jla!`zl^Pi`W~zt@@*)7SykZY)c;)MMzh+CNDyS>t+Z=z zs{O2b6<*&gR%WiL_@a(TXQPoxd*|_%p*Jos;@(em+=>E%>ltkPJ21So_jiPEEX55W zAD7#Mew7=LA*~B~gRb$o+~|Q7QqFq2U8lJQxvudF6OW+0TY4kv_*usFsjeq15YMbN zr{}%c2QsEo%IHD83Zafbl8h%sMB@ilvQOvu_+CWrcDM3?!muYcHVR9#FzrToJozm+ zRF1d{(s*BK3-|Nnh0bvloJ(;pX+w0e_pobU&)Q{f^(}c;6pqdw%BTkOP+Tw=+^+>ie^X1 zlcxxwOH3s8Jf~IZq&E6sZ@q%fx>?-x!wLF*+Jh#CZufRfbv0wo>D8rdjIHpR`|y>U ze)_Fh{+_*5rdU=2<5*khlv%1nqGW!wvCLw^h11}9+A^}pM9U~SYPu5XQR15F+8zGc zX`Z8Z#+;MUsBr?4iUY;~DR=YHbBvl9_f)jlQ`%89f3`{YpZf`~R&HyIE`_Epvo14g z3;65f)}Oc7Ui9jyrZK)4IlxtVIH59cGA{nGD5Pu`7eXTCu_v(U^;7AK zy_~q(MzU*~D_GhZ-=&^F@s_=8qi2R@I%IcdY&8zhB_;b)XmY~O&~FNx58EbxB}C&( zU0FEkGw1Bq4{mOWufjb}!WN8@J89gG;P{Kt!WnWE$^IL4amZEd|{F}Ld3ede7=awlUe@{R!*L-jdLU~uJ_ zI5mi^+^)rYEE;zhIxcc9BTI0p|7$NAZ~8t?0bZVt=G0yv6E^}(RX8#2%%M$5e!G>Z z*pzTS`3O7Amu>l8ZEk@_FC$2lWkLJNXw?-<4SvcIkLcj>?-}1jL+$$swLrJgTEpDT zB}!f!F@%SO&xBS{^x2{-S6{C9oV8m~h*H?ctE8LHY3(=8imD3F#dinfxo!u-lj8Cvmy>Y|VBtyL& zY>8cA%FN2;Dz+mOT`#N@tR~}gH~hXMOziVi%fmdJFVcn|iCf_PbK!j#>Xo)O*|m0( zm)88K?l-fIeM<}YPl0_6>Vp>|#mqTNivDJ14+7yb6j%oM5I;AWYmOv&cAJ47qqcD^|4 zlF7l6^U_3dx}!Lmu7e}CNvoN1Gk?|9qR|)I1yEfydlW@IKK7#D#x1t9#hresnhJ=6 zQ#K9O$&Q#&-i=}kuBR3P=p0JgGU|^zd(rQv@VvuWZ@Y<3u`xikOUaygALhF63RWyF zC&hMTAc;@kz5TU7_T6E2cvZw{gEp5YEnSoR@=P1&3L_;huFx{N>kmK0^$YfLHyzT1 z8!9c;h1f7sewuePn$L7r-( z#$Jq%_B2gw+&+W3o-J*5j*I|)!~>qnrlX?zaRc^;4$^}SQ$};FAB?Qz#F;HSYn7G9 zCG$rU%f?tWN5cxW=YbL^d)i7>C}hxKe)3SxEG}p!NO($yJ956T<}#`4C-&=7mC$x# z@0hGQ$1={R-6z5wQXI}%)94nBLr|u+%@vt*PxSNWjcYPz8iGYU_}o>9)BE2hTa>y& zn58n@$=WpNMw+`7tTrj#Zc+xbJ7`k4287*;s9p5) z0l>rEWxmsX&BGs`%1w)|zWaqD?@8DAQ}NpK7c5c>Ln$xDk@O_2Qh)40dzckayn#wuZ&rU~l4-xOV* zJHE!Q-M_g5as<$vUV^*7WF8)I?!tcH@FuUmGM(t65)^i7D>Sg%-OHHmIp;3%;Y?-5-$Seg#emi>W0W>;YGzU(5<8)M#QEkRk#wJi2U zJ~O*p%*GNi@l^Qwq`}NH)~J~sZKsWiXipij6P8`BR!mH!7s5%QBe1E|>~1rof4PZU z)h!^QzPbuS^V~D(BHMK(B+t2r#M&i|;y7GFDA*tUjwwZ7cjI^cHyBs_I%E2iNj&CA zcc#EtcdK8LqP#EQx~3Convi?G3F4#eBL1%xWt&ynINS&xwfzOY{__hpB9-X^Cq&`&SLBGkIX*x@S@ zF)D#<_*x52>^oGHU5MEbCwr+3!M)?rasTBqn^9b_!jP!#F*@e(HX;+}`Pu?T`P5^Z z?sEF=Y)`)7FE-JQZoz@?DcXrL;7!}>R?YCY$o=~FSjh+X)K@`YA5E`6hz}vVyc6R~ zDgVUZ$5FE{rO$h+!8O3s}Nt zs{!Sbm4xO~%FJK|a?2wY8y=PJD=Qo8L+({x2ezD63$iv15?7hW?_Iaa#ueDVpzcXr zUbl$Xu^7{}X%rWmkyhGDY$_1?S{;VY_El8|G~0x2>GC19KVMs7swAKr2dngSOsl?4 zpk(y@=QLR)qf03j(}LvoMsfm{ot?_YT=+8MP~DIFTnVIt9#67zhKyv77mj3aHD1EIH1z}wO&WhB_5FOaWj@$)JverUkPO~cHj%{IZdj>27Q?I2a~ zr?b<6mxaCG*eW$}Ps*@$X^Lj1C=G|LGQAkf)SgFxPsc264nj8wJ=DKlEd&?=P1=A<{! ze)~?aRS=VcIcRk0p?6xTfg-P6OE?z&6i%WK`@Ya=C9#zXmS0M(lmDD5&%BV@$6@A` zRx;(=%9o0m@~%nWrMkBAlTR4Ha<7MWFERv>jeP<`0I=@KKyIMc`k&wG`F@Ci^VUe{i0aoZLiarMl$kXY)6or0Yy zp&AL-+1b@N>YS>~F$E+lRWuCBGYpfUAmvOgkv+WmI4=Jl|Jw(WnEkpTO;%8_M?`Co zdGgHnq)*$cBrVcJC4DZKW(^AubyX7Wwy;v|AG3t3!X(=L zP!t}E{zL2vBFq-5oMr26LnUNy3rX|p8_?E7TyMLDbmrePduG4!7r>A79ZD0JcZ72aCEbx-NzTDK>B z=i=&|wKLPIWrqCv#8MoiI2E6takj~5;#v*Nmugbx(WEeSGwgk}@q(ySu;Pt3_;Ua!&+SQt_Pbm0fY2P3U|EHp{ScDi#f*^W z&8gzwwl9ShAu96FkhGZHAy6r3;>j{jiyX?Izl7~>4@`(Ck*TaF!hP{idfezDoLACim= z%+DJp+r*~^TeKi}_>i&d&etb~_#xYPw93r!zm$G2Aw1JvuB9~xgd)jaxYuRmy*H1s z`xKqI@Ejw;aim1wN>I>ljh(B&re;@8C%Cf&R#Y)3^?Jj9L*P{Cb22x0ET(Mz%awvI z3!Z$V_08B_PLX0=DBx1a)aNBHg|C^FUgs^j);FOai^2u`Nw!cb^}KUf$}b=WBJFpI z6l4DRd0DLiPl+_fJ=2?JP6_mBjM~ogaruY$==+XM;D?xBsh8zKkgYpZsmq@TLGklp z2BPbAsWH!i+MpfQOwiT@tFs1_^yA_SHM*wdo z9)~QLx>}5g!=svLHaKK_BKpA_L}{qQri|5u2!1fFYPpxUN+%~+hmz^$%&ZHjYdu!2 zs(_ur+iVxWSF-ZfTyGfkS@DPiwMTgx0lM7*_~^JU+j8t*rMu^HG8u$v9@^u>}4&JRPv>1A}7nbH9?Q z$kqkTDkm4x7uk<-<25Mlrce(K8hvEAx$Y)P-S{0#4U4Ty6_ z{X@;_#^vgWmD)`L>94deUa>jJGhzYSu@uL2yznTo4D=wlF^O${MTFBa0(wc#7s4Ek zq#Zm5T`ej{xwx1t$|6j#Q8rzarX-$O=4s}fqYOd4l6CIU+v$gj2XA^?Uyn6$*f;AW zU6U+`HvJ(?!G_Rg%%mJ_S`0of9GMqilT7)TB_jX~yQHQ^CoF{wF^ z3mYrpQ3gj*=K|9JN->i%jcFTcv$I37`c}xhuEAwcb5?rAdaBVh+Kr zNKvVH?zPBX9_iLwf!_TCxTlKc*kM{BA1e+bGw1e2Yq}s3KIr|>@~$Vy6JXZ7+d@gI#F zB@q2+1)yKTgHow!?x-oE6$qbf2Ueg2Y~7Dmai_JM#Zjue& z>kMAB&GCo&9a`Ww_8eiaUQ}W^edON)*Iv2e{DpS}FPGWX+M|Sor~zD>z%@O@P9I7T zh>GBkATZ!xGfZoz?F{sPe0JD_`E35c8`5S1t5LeVKZ(3@vVjf%gKXZ<2%aWFNd$S0 zBJclb1mB`$eVo4Rtp3(FRG8b Date: Wed, 29 Sep 2021 17:47:30 +0200 Subject: [PATCH 048/110] Small fixes to various layers, mostly presets related --- assets/layers/bench/bench.json | 36 ++--- assets/layers/birdhide/birdhide.json | 4 +- .../layers/drinking_water/drinking_water.json | 14 +- .../information_board/information_board.json | 10 +- .../layers/nature_reserve/nature_reserve.json | 2 +- assets/layers/parking/parking.json | 137 ++---------------- assets/layers/picnic_table/picnic_table.json | 10 +- assets/layers/toilet/toilet.json | 24 +-- assets/themes/natuurpunt/natuurpunt.json | 7 +- 9 files changed, 63 insertions(+), 181 deletions(-) diff --git a/assets/layers/bench/bench.json b/assets/layers/bench/bench.json index 5a94c40ad..af5bbd10b 100644 --- a/assets/layers/bench/bench.json +++ b/assets/layers/bench/bench.json @@ -581,37 +581,21 @@ "amenity=bench" ], "title": { - "en": "Bench", - "de": "Sitzbank", - "fr": "Banc", - "nl": "Zitbank", - "es": "Banco", - "it": "Panchina", + "en": "bench", + "de": "sitzbank", + "fr": "banc", + "nl": "zitbank", + "es": "banco", + "it": "panchina", "ru": "Скамейка", - "id": "Bangku", + "id": "bangku", "zh_Hans": "长椅", - "nb_NO": "Benk", + "nb_NO": "benk", "zh_Hant": "長椅", - "pt_BR": "Banco", - "fi": "Penkki", + "pt_BR": "banco", + "fi": "penkki", "pl": "Ławka" }, - "description": { - "en": "Add a new bench", - "de": "Neue Sitzbank eintragen", - "fr": "Ajouter un nouveau banc", - "nl": "Voeg een nieuwe zitbank toe", - "es": "Añadir un nuevo banco", - "hu": "Pad hozzáadása", - "it": "Aggiungi una nuova panchina", - "ru": "Добавить новую скамейку", - "zh_Hans": "增加一个新的长椅", - "nb_NO": "Legg til en ny benk", - "zh_Hant": "新增長椅", - "pt_BR": "Adicionar um novo banco", - "fi": "Lisää uusi penkki", - "pl": "Dodaj nową ławkę" - }, "presiceInput": { "preferredBackground": "photo" } diff --git a/assets/layers/birdhide/birdhide.json b/assets/layers/birdhide/birdhide.json index 1a581b8a0..38f8e1970 100644 --- a/assets/layers/birdhide/birdhide.json +++ b/assets/layers/birdhide/birdhide.json @@ -235,7 +235,7 @@ "amenity=shelter" ], "title": { - "nl": "Vogelkijkhut" + "nl": "vogelkijkhut" }, "description": { "nl": "Een overdekte hut waarbinnen er warm en droog naar vogels gekeken kan worden" @@ -248,7 +248,7 @@ "shelter=no" ], "title": { - "nl": "Vogelkijkwand" + "nl": "vogelkijkwand" }, "description": { "nl": "Een vogelkijkwand waarachter men kan staan om vogels te kijken" diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 40cce1af9..43af3910c 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -57,14 +57,14 @@ "presets": [ { "title": { - "en": "Drinking water", - "nl": "Drinkbaar water", - "fr": "Eau potable", - "gl": "Auga potábel", - "de": "Trinkwasser", - "it": "Acqua potabile", + "en": "drinking water", + "nl": "drinkbaar water", + "fr": "eau potable", + "gl": "auga potábel", + "de": "trinkwasser", + "it": "acqua potabile", "ru": "Питьевая вода", - "id": "Air minum" + "id": "air minum" }, "tags": [ "amenity=drinking_water" diff --git a/assets/layers/information_board/information_board.json b/assets/layers/information_board/information_board.json index f25c71245..6f240fa4f 100644 --- a/assets/layers/information_board/information_board.json +++ b/assets/layers/information_board/information_board.json @@ -45,11 +45,11 @@ "information=board" ], "title": { - "nl": "Informatiebord", - "en": "Information board", - "it": "Pannello informativo", - "fr": "Panneau d'informations", - "de": "Informationstafel", + "nl": "informatiebord", + "en": "information board", + "it": "pannello informativo", + "fr": "panneau d'informations", + "de": "informationstafel", "ru": "Информационный щит" } } diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index dab52299c..c6fb976af 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -414,7 +414,7 @@ "fixme=Toegevoegd met MapComplete, geometry nog uit te tekenen" ], "title": { - "nl": "Natuurreservaat" + "nl": "natuurreservaat" }, "description": { "nl": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt" diff --git a/assets/layers/parking/parking.json b/assets/layers/parking/parking.json index 1646d143e..5459e47cb 100644 --- a/assets/layers/parking/parking.json +++ b/assets/layers/parking/parking.json @@ -49,122 +49,7 @@ "nl": "Parking" }, "tagRenderings": [ - "images", - { - "render": { - "nl": "De toegankelijkheid van dit gebied is: {access:description}" - }, - "question": { - "nl": "Is dit gebied toegankelijk?" - }, - "freeform": { - "key": "access:description" - }, - "mappings": [ - { - "if": { - "and": [ - "access=yes", - "fee=" - ] - }, - "then": { - "nl": "Vrij toegankelijk" - } - }, - { - "if": { - "and": [ - "access=no", - "fee=" - ] - }, - "then": { - "nl": "Niet toegankelijk" - } - }, - { - "if": { - "and": [ - "access=private", - "fee=" - ] - }, - "then": { - "nl": "Niet toegankelijk, want privégebied" - } - }, - { - "if": { - "and": [ - "access=permissive", - "fee=" - ] - }, - "then": { - "nl": "Toegankelijk, ondanks dat het privegebied is" - } - }, - { - "if": { - "and": [ - "access=guided", - "fee=" - ] - }, - "then": { - "nl": "Enkel toegankelijk met een gids of tijdens een activiteit" - } - }, - { - "if": { - "and": [ - "access=yes", - "fee=yes" - ] - }, - "then": { - "nl": "Toegankelijk mits betaling" - } - } - ], - "id": "Access tag" - }, - { - "render": { - "nl": "Beheer door {operator}" - }, - "question": { - "nl": "Wie beheert dit pad?" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "operator=Natuurpunt" - ] - }, - "then": { - "nl": "Dit gebied wordt beheerd door Natuurpunt" - } - }, - { - "if": { - "and": [ - "operator~(n|N)atuurpunt.*" - ] - }, - "then": { - "nl": "Dit gebied wordt beheerd door {operator}" - }, - "hideInAnswer": true - } - ], - "id": "Operator tag" - } + "images" ], "wayHandling": 1, "iconSize": { @@ -176,16 +61,24 @@ "presets": [ { "tags": [ - "amenity=parking", - "amenity=motorcycle_parking", - "amenity=bicycle_parking", - "fixme=Toegevoegd met MapComplete, geometry nog uit te tekenen" + "amenity=bicycle_parking" ], "title": { - "nl": "Paden" + "nl": "fietsparking" }, "description": { - "nl": "Voeg een ontbrekend, erkend pad toe." + "nl": "Voeg hier een fietsenstalling toe" + } + }, + { + "tags": [ + "amenity=parking" + ], + "title": { + "nl": "parking" + }, + "description": { + "nl": "Voeg hier een parking voor auto's toe" } } ] diff --git a/assets/layers/picnic_table/picnic_table.json b/assets/layers/picnic_table/picnic_table.json index dbe67a9af..c414410fe 100644 --- a/assets/layers/picnic_table/picnic_table.json +++ b/assets/layers/picnic_table/picnic_table.json @@ -92,12 +92,12 @@ "leisure=picnic_table" ], "title": { - "en": "Picnic table", - "nl": "Picnic-tafel", - "it": "Tavolo da picnic", + "en": "picnic table", + "nl": "picnic-tafel", + "it": "tavolo da picnic", "ru": "Стол для пикника", - "de": "Picknicktisch", - "fr": "Table de pique-nique" + "de": "picknicktisch", + "fr": "table de pique-nique" } } ], diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index da9b3ae90..68bc21fe6 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -47,12 +47,12 @@ "presets": [ { "title": { - "en": "Toilet", - "de": "Toilette", - "fr": "Toilettes", - "nl": "Toilet", - "ru": "Туалет", - "it": "Servizi igienici" + "en": "toilet", + "de": "toilette", + "fr": "toilettes", + "nl": "toilet", + "ru": "tуалет", + "it": "servizi igienici" }, "tags": [ "amenity=toilets" @@ -68,12 +68,12 @@ }, { "title": { - "en": "Toilets with wheelchair accessible toilet", - "de": "Toiletten mit rollstuhlgerechter Toilette", - "fr": "Toilettes accessible aux personnes à mobilité réduite", - "nl": "Een rolstoeltoegankelijke toilet", - "it": "Servizi igienici accessibili per persone in sedia a rotelle", - "ru": "Туалет с доступом для пользователей кресел-колясок" + "en": "toilets with wheelchair accessible toilet", + "de": "toiletten mit rollstuhlgerechter Toilette", + "fr": "toilettes accessible aux personnes à mobilité réduite", + "nl": "een rolstoeltoegankelijke toilet", + "it": "servizi igienici accessibili per persone in sedia a rotelle", + "ru": "tуалет с доступом для пользователей кресел-колясок" }, "tags": [ "amenity=toilets", diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index f0e921d38..1ed0b7e88 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -29,6 +29,10 @@ "enablePdfDownload": true, "enableDownload": true, "hideFromOverview": true, + "clustering": { + "#": "Disable clustering for this theme", + "maxZoom": 0 + }, "layers": [ { "#": "Nature reserve with geometry, z>=13", @@ -70,7 +74,8 @@ "minzoom": 1, "icon": { "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" - } + }, + "presets": null } }, { From 36d62f9923d6cd9bb3dbbd866850e02e277dd7e5 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 17:48:15 +0200 Subject: [PATCH 049/110] Add blacklist of ids to geojson source in order to avoid duplicate features to be loaded multiple times --- Logic/FeatureSource/Sources/GeoJsonSource.ts | 19 ++++++++++++++++++- .../DynamicGeoJsonTileSource.ts | 11 ++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Logic/FeatureSource/Sources/GeoJsonSource.ts b/Logic/FeatureSource/Sources/GeoJsonSource.ts index 301f1bc55..b341a0dd3 100644 --- a/Logic/FeatureSource/Sources/GeoJsonSource.ts +++ b/Logic/FeatureSource/Sources/GeoJsonSource.ts @@ -21,14 +21,26 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { public readonly tileIndex public readonly bbox; + /** + * Only used if the actual source is a tiled geojson. + * A big feature might be contained in multiple tiles. + * However, we only want to load them once. The blacklist thus contains all ids of all features previously seen + * @private + */ + private readonly featureIdBlacklist?: UIEventSource> + public constructor(flayer: FilteredLayer, - zxy?: [number, number, number]) { + zxy?: [number, number, number], + options?: { + featureIdBlacklist?: UIEventSource> + }) { if (flayer.layerDef.source.geojsonZoomLevel !== undefined && zxy === undefined) { throw "Dynamic layers are not supported. Use 'DynamicGeoJsonTileSource instead" } this.layer = flayer; + this.featureIdBlacklist = options?.featureIdBlacklist let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id); if (zxy !== undefined) { const [z, x, y] = zxy; @@ -68,6 +80,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { const props = feature.properties for (const key in props) { if (typeof props[key] !== "string") { + // Make sure all the values are string, it crashes stuff otherwise props[key] = "" + props[key] } } @@ -82,6 +95,10 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { continue; } self.seenids.add(props.id) + + if(self.featureIdBlacklist?.data?.has(props.id)){ + continue; + } let freshness: Date = time; if (feature.properties["_last_edit:timestamp"] !== undefined) { diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts index 8843b182a..fcfdbea5a 100644 --- a/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource.ts @@ -37,6 +37,8 @@ export default class DynamicGeoJsonTileSource extends DynamicTileSource { console.warn("No whitelist found for ", layer.layerDef.id, err) }) + const seenIds = new Set(); + const blackList = new UIEventSource(seenIds) super( layer, source.geojsonZoomLevel, @@ -50,8 +52,15 @@ export default class DynamicGeoJsonTileSource extends DynamicTileSource { const src = new GeoJsonSource( layer, - zxy + zxy, + { + featureIdBlacklist: blackList + } ) + src.features.addCallbackAndRunD(feats => { + feats.forEach(feat => seenIds.add(feat.feature.properties.id)) + blackList.ping(); + }) registerLayer(src) return src }, From a4aeed092b398fbcb9b359845d6fd71ac8319c66 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 17:55:55 +0200 Subject: [PATCH 050/110] Version bump --- Models/Constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Models/Constants.ts b/Models/Constants.ts index b9855a293..d524c6689 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.0-alpha-4"; + public static vNumber = "0.10.0-rc0"; public static ImgurApiKey = '7070e7167f0a25a' public static defaultOverpassUrls = [ // The official instance, 10000 queries per day per project allowed From 3d95324038f04acf1bfdeca3e7ce54dae01d7233 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 17:56:38 +0200 Subject: [PATCH 051/110] Small translation fixes --- langs/layers/de.json | 13 ++++---- langs/layers/en.json | 13 ++++---- langs/layers/es.json | 3 +- langs/layers/fi.json | 3 +- langs/layers/fr.json | 13 ++++---- langs/layers/gl.json | 2 +- langs/layers/hu.json | 5 ---- langs/layers/id.json | 4 +-- langs/layers/it.json | 13 ++++---- langs/layers/nb_NO.json | 3 +- langs/layers/nl.json | 63 +++++++++------------------------------ langs/layers/pl.json | 1 - langs/layers/pt_BR.json | 3 +- langs/layers/ru.json | 5 ++-- langs/layers/zh_Hans.json | 1 - langs/layers/zh_Hant.json | 1 - 16 files changed, 47 insertions(+), 99 deletions(-) diff --git a/langs/layers/de.json b/langs/layers/de.json index d98acb582..b95c28ac5 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -3,8 +3,7 @@ "name": "Sitzbänke", "presets": { "0": { - "description": "Neue Sitzbank eintragen", - "title": "Sitzbank" + "title": "sitzbank" } }, "tagRenderings": { @@ -754,7 +753,7 @@ "name": "Trinkwasser", "presets": { "0": { - "title": "Trinkwasser" + "title": "trinkwasser" } }, "tagRenderings": { @@ -823,7 +822,7 @@ "name": "Informationstafeln", "presets": { "0": { - "title": "Informationstafel" + "title": "informationstafel" } }, "title": { @@ -904,7 +903,7 @@ "name": "Picknick-Tische", "presets": { "0": { - "title": "Picknicktisch" + "title": "picknicktisch" } }, "tagRenderings": { @@ -1134,11 +1133,11 @@ "presets": { "0": { "description": "Eine öffentlich zugängliche Toilette", - "title": "Toilette" + "title": "toilette" }, "1": { "description": "Eine Toilettenanlage mit mindestens einer rollstuhlgerechten Toilette", - "title": "Toiletten mit rollstuhlgerechter Toilette" + "title": "toiletten mit rollstuhlgerechter Toilette" } }, "tagRenderings": { diff --git a/langs/layers/en.json b/langs/layers/en.json index 7ab575873..a3a428462 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -94,8 +94,7 @@ "name": "Benches", "presets": { "0": { - "description": "Add a new bench", - "title": "Bench" + "title": "bench" } }, "tagRenderings": { @@ -2375,7 +2374,7 @@ "name": "Drinking water", "presets": { "0": { - "title": "Drinking water" + "title": "drinking water" } }, "tagRenderings": { @@ -2587,7 +2586,7 @@ "name": "Information boards", "presets": { "0": { - "title": "Information board" + "title": "information board" } }, "title": { @@ -2733,7 +2732,7 @@ "name": "Picnic tables", "presets": { "0": { - "title": "Picnic table" + "title": "picnic table" } }, "tagRenderings": { @@ -3263,11 +3262,11 @@ "presets": { "0": { "description": "A publicly accessible toilet or restroom", - "title": "Toilet" + "title": "toilet" }, "1": { "description": "A restroom which has at least one wheelchair-accessible toilet", - "title": "Toilets with wheelchair accessible toilet" + "title": "toilets with wheelchair accessible toilet" } }, "tagRenderings": { diff --git a/langs/layers/es.json b/langs/layers/es.json index 9746ea958..44e99ca4c 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -3,8 +3,7 @@ "name": "Bancos", "presets": { "0": { - "description": "Añadir un nuevo banco", - "title": "Banco" + "title": "banco" } }, "tagRenderings": { diff --git a/langs/layers/fi.json b/langs/layers/fi.json index abe9d2b7f..a13231cd6 100644 --- a/langs/layers/fi.json +++ b/langs/layers/fi.json @@ -3,8 +3,7 @@ "name": "Penkit", "presets": { "0": { - "description": "Lisää uusi penkki", - "title": "Penkki" + "title": "penkki" } }, "tagRenderings": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 009a50fc0..52c61d899 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -3,8 +3,7 @@ "name": "Bancs", "presets": { "0": { - "description": "Ajouter un nouveau banc", - "title": "Banc" + "title": "banc" } }, "tagRenderings": { @@ -810,7 +809,7 @@ "name": "Eau potable", "presets": { "0": { - "title": "Eau potable" + "title": "eau potable" } }, "tagRenderings": { @@ -951,7 +950,7 @@ "name": "Panneaux d'informations", "presets": { "0": { - "title": "Panneau d'informations" + "title": "panneau d'informations" } }, "title": { @@ -1043,7 +1042,7 @@ "name": "Tables de pique-nique", "presets": { "0": { - "title": "Table de pique-nique" + "title": "table de pique-nique" } }, "tagRenderings": { @@ -1550,11 +1549,11 @@ "presets": { "0": { "description": "Des toilettes", - "title": "Toilettes" + "title": "toilettes" }, "1": { "description": "Toilettes avec au moins un WC accessible aux personnes à mobilité réduite", - "title": "Toilettes accessible aux personnes à mobilité réduite" + "title": "toilettes accessible aux personnes à mobilité réduite" } }, "tagRenderings": { diff --git a/langs/layers/gl.json b/langs/layers/gl.json index 44f1a21ba..aa261176f 100644 --- a/langs/layers/gl.json +++ b/langs/layers/gl.json @@ -372,7 +372,7 @@ "name": "Auga potábel", "presets": { "0": { - "title": "Auga potábel" + "title": "auga potábel" } }, "title": { diff --git a/langs/layers/hu.json b/langs/layers/hu.json index 2842a2d33..5fb6aa09b 100644 --- a/langs/layers/hu.json +++ b/langs/layers/hu.json @@ -1,11 +1,6 @@ { "bench": { "name": "Padok", - "presets": { - "0": { - "description": "Pad hozzáadása" - } - }, "tagRenderings": { "bench-backrest": { "mappings": { diff --git a/langs/layers/id.json b/langs/layers/id.json index 7aacdc7aa..54f3e27c2 100644 --- a/langs/layers/id.json +++ b/langs/layers/id.json @@ -3,7 +3,7 @@ "name": "Bangku", "presets": { "0": { - "title": "Bangku" + "title": "bangku" } }, "tagRenderings": { @@ -65,7 +65,7 @@ "name": "Air minum", "presets": { "0": { - "title": "Air minum" + "title": "air minum" } }, "title": { diff --git a/langs/layers/it.json b/langs/layers/it.json index d5e8c4050..b8585841f 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -3,8 +3,7 @@ "name": "Panchine", "presets": { "0": { - "description": "Aggiungi una nuova panchina", - "title": "Panchina" + "title": "panchina" } }, "tagRenderings": { @@ -826,7 +825,7 @@ "name": "Acqua potabile", "presets": { "0": { - "title": "Acqua potabile" + "title": "acqua potabile" } }, "tagRenderings": { @@ -910,7 +909,7 @@ "name": "Pannelli informativi", "presets": { "0": { - "title": "Pannello informativo" + "title": "pannello informativo" } }, "title": { @@ -1002,7 +1001,7 @@ "name": "Tavoli da picnic", "presets": { "0": { - "title": "Tavolo da picnic" + "title": "tavolo da picnic" } }, "tagRenderings": { @@ -1509,11 +1508,11 @@ "presets": { "0": { "description": "Servizi igienici aperti al pubblico", - "title": "Servizi igienici" + "title": "servizi igienici" }, "1": { "description": "Servizi igienici che hanno almeno una toilette accessibile a persone in sedia a rotelle", - "title": "Servizi igienici accessibili per persone in sedia a rotelle" + "title": "servizi igienici accessibili per persone in sedia a rotelle" } }, "tagRenderings": { diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json index e22f10944..c515216b9 100644 --- a/langs/layers/nb_NO.json +++ b/langs/layers/nb_NO.json @@ -3,8 +3,7 @@ "name": "Benker", "presets": { "0": { - "description": "Legg til en ny benk", - "title": "Benk" + "title": "benk" } }, "tagRenderings": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 8b133247b..dec4d1973 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -93,8 +93,7 @@ "name": "Zitbanken", "presets": { "0": { - "description": "Voeg een nieuwe zitbank toe", - "title": "Zitbank" + "title": "zitbank" } }, "tagRenderings": { @@ -828,11 +827,11 @@ "presets": { "0": { "description": "Een overdekte hut waarbinnen er warm en droog naar vogels gekeken kan worden", - "title": "Vogelkijkhut" + "title": "vogelkijkhut" }, "1": { "description": "Een vogelkijkwand waarachter men kan staan om vogels te kijken", - "title": "Vogelkijkwand" + "title": "vogelkijkwand" } }, "size": { @@ -2321,7 +2320,7 @@ "name": "Drinkbaar water", "presets": { "0": { - "title": "Drinkbaar water" + "title": "drinkbaar water" } }, "tagRenderings": { @@ -2660,7 +2659,7 @@ "name": "Informatieborden", "presets": { "0": { - "title": "Informatiebord" + "title": "informatiebord" } }, "title": { @@ -2739,7 +2738,7 @@ "presets": { "0": { "description": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt", - "title": "Natuurreservaat" + "title": "natuurreservaat" } }, "tagRenderings": { @@ -2906,46 +2905,12 @@ "name": "parking", "presets": { "0": { - "description": "Voeg een ontbrekend, erkend pad toe.", - "title": "Paden" - } - }, - "tagRenderings": { - "Access tag": { - "mappings": { - "0": { - "then": "Vrij toegankelijk" - }, - "1": { - "then": "Niet toegankelijk" - }, - "2": { - "then": "Niet toegankelijk, want privégebied" - }, - "3": { - "then": "Toegankelijk, ondanks dat het privegebied is" - }, - "4": { - "then": "Enkel toegankelijk met een gids of tijdens een activiteit" - }, - "5": { - "then": "Toegankelijk mits betaling" - } - }, - "question": "Is dit gebied toegankelijk?", - "render": "De toegankelijkheid van dit gebied is: {access:description}" + "description": "Voeg hier een fietsenstalling toe", + "title": "fietsparking" }, - "Operator tag": { - "mappings": { - "0": { - "then": "Dit gebied wordt beheerd door Natuurpunt" - }, - "1": { - "then": "Dit gebied wordt beheerd door {operator}" - } - }, - "question": "Wie beheert dit pad?", - "render": "Beheer door {operator}" + "1": { + "description": "Voeg hier een parking voor auto's toe", + "title": "parking" } }, "title": { @@ -2968,7 +2933,7 @@ "name": "Picnictafels", "presets": { "0": { - "title": "Picnic-tafel" + "title": "picnic-tafel" } }, "tagRenderings": { @@ -3531,11 +3496,11 @@ "presets": { "0": { "description": "Een publieke toilet", - "title": "Toilet" + "title": "toilet" }, "1": { "description": "Deze toiletten hebben op zijn minst één rolstoeltoegankelijke WC", - "title": "Een rolstoeltoegankelijke toilet" + "title": "een rolstoeltoegankelijke toilet" } }, "tagRenderings": { diff --git a/langs/layers/pl.json b/langs/layers/pl.json index 7c91508bb..64b52a16b 100644 --- a/langs/layers/pl.json +++ b/langs/layers/pl.json @@ -3,7 +3,6 @@ "name": "Ławki", "presets": { "0": { - "description": "Dodaj nową ławkę", "title": "Ławka" } }, diff --git a/langs/layers/pt_BR.json b/langs/layers/pt_BR.json index 602d1ea85..923a5d76d 100644 --- a/langs/layers/pt_BR.json +++ b/langs/layers/pt_BR.json @@ -3,8 +3,7 @@ "name": "Bancos", "presets": { "0": { - "description": "Adicionar um novo banco", - "title": "Banco" + "title": "banco" } }, "tagRenderings": { diff --git a/langs/layers/ru.json b/langs/layers/ru.json index 2c6485f51..4e776b452 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -3,7 +3,6 @@ "name": "Скамейки", "presets": { "0": { - "description": "Добавить новую скамейку", "title": "Скамейка" } }, @@ -1023,10 +1022,10 @@ "presets": { "0": { "description": "Туалет или комната отдыха со свободным доступом", - "title": "Туалет" + "title": "tуалет" }, "1": { - "title": "Туалет с доступом для пользователей кресел-колясок" + "title": "tуалет с доступом для пользователей кресел-колясок" } }, "tagRenderings": { diff --git a/langs/layers/zh_Hans.json b/langs/layers/zh_Hans.json index 90b74fe66..360a52c61 100644 --- a/langs/layers/zh_Hans.json +++ b/langs/layers/zh_Hans.json @@ -3,7 +3,6 @@ "name": "长椅", "presets": { "0": { - "description": "增加一个新的长椅", "title": "长椅" } }, diff --git a/langs/layers/zh_Hant.json b/langs/layers/zh_Hant.json index 994ecb486..ccacafa7f 100644 --- a/langs/layers/zh_Hant.json +++ b/langs/layers/zh_Hant.json @@ -3,7 +3,6 @@ "name": "長椅", "presets": { "0": { - "description": "新增長椅", "title": "長椅" } }, From 018431401679c03c8e9c18ab041f6bb24eae509f Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 18:21:06 +0200 Subject: [PATCH 052/110] Add release notes --- Docs/Release_Notes.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Docs/Release_Notes.md b/Docs/Release_Notes.md index ea1b1ded2..c6f0ac35d 100644 --- a/Docs/Release_Notes.md +++ b/Docs/Release_Notes.md @@ -3,6 +3,41 @@ Release Notes Some highlights of new releases. +0.10 +---- + +The 0.10 version contains a lot of refactorings on various core of the application, namely in the rendering stack, the fetching of data and uploading. + +Some highlights are: + +1. The addition of fallback overpass servers +2. Fetching data from OSM directly (especially useful in the personal theme) +3. Splitting all the features per tile (with a maximum amount of features per tile, splitting further if needed), making everything a ton faster +4. If a tile has too much features, the featuers are not shown. Instead, a rectangle with the feature amount is shown. + +Furthermore, it contains a few new themes and theme updates: + +- Restaurants and fast food +- Pubs and cafés +- Charging stations got a major overhaul - thanks for all the input on the available plugs +- Observation towers and binoculars +- The addition of a hackerspace theme (as made on SOTM) + +Other various small improvements: + +- The filter state is now exposed in the URL, so can be shared +- Lots of other fixes, as usual + +0.8 and 0.9 +----------- + +Addition of filters per layer +Addition of a download-as-pdf for select themes +Addition of a download-as-geojson and download-as-csv for select themes + +... + + 0.7.0 ----- From 6dcc710cc2728773e22ee164b6c53a6d6875145b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 19:48:36 +0200 Subject: [PATCH 053/110] Add a little hand, inviting the user to drag location inputs --- UI/Input/LocationInput.ts | 23 +++++++++--- assets/svg/hand.svg | 77 ++++++++++++++------------------------- index.css | 46 +++++++++++++++++++++++ test.ts | 26 ++++++------- 4 files changed, 101 insertions(+), 71 deletions(-) diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index 875fce9cb..adc441472 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -11,6 +11,7 @@ import {GeoOperations} from "../../Logic/GeoOperations"; import ShowDataLayer from "../ShowDataLayer"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import * as L from "leaflet"; +import {FixedUiElement} from "../Base/FixedUiElement"; export default class LocationInput extends InputElement { @@ -128,7 +129,7 @@ export default class LocationInput extends InputElement { }) } - this.mapBackground = options.mapBackground ?? State.state.backgroundLayer ?? new UIEventSource(AvailableBaseLayers.osmCarto) + this.mapBackground = options.mapBackground ?? State.state?.backgroundLayer ?? new UIEventSource(AvailableBaseLayers.osmCarto) this.SetClass("block h-full") } @@ -147,7 +148,7 @@ export default class LocationInput extends InputElement { { location: this._centerLocation, background: this.mapBackground, - attribution: this.mapBackground !== State.state.backgroundLayer, + attribution: this.mapBackground !== State.state?.backgroundLayer, lastClickLocation: clickLocation } ) @@ -219,7 +220,6 @@ export default class LocationInput extends InputElement { ) } } - this.mapBackground.map(layer => { const leaflet = map.leafletMap.data if (leaflet === undefined || layer === undefined) { @@ -231,20 +231,31 @@ export default class LocationInput extends InputElement { leaflet.setZoom(layer.max_zoom - 1) }, [map.leafletMap]) + + const animatedHand = Svg.hand_ui() + .SetStyle("width: 2rem; height: unset;") + .SetClass("hand-drag-animation block pointer-events-none") + return new Combine([ new Combine([ Svg.move_arrows_ui() .SetClass("block relative pointer-events-none") .SetStyle("left: -2.5rem; top: -2.5rem; width: 5rem; height: 5rem") - ]).SetClass("block w-0 h-0 z-10 relative") - .SetStyle("background: rgba(255, 128, 128, 0.21); left: 50%; top: 50%"), + ]).SetClass("block w-0 h-0 z-10 relative") + .SetStyle("background: rgba(255, 128, 128, 0.21); left: 50%; top: 50%; opacity: 0.5"), + + new Combine([ + animatedHand]) + .SetClass("block w-0 h-0 z-10 relative") + .SetStyle("left: calc(50% + 3rem); top: calc(50% + 2rem); opacity: 0.7"), + map .SetClass("z-0 relative block w-full h-full bg-gray-100") ]).ConstructElement(); } catch (e) { console.error("Could not generate LocationInputElement:", e) - return undefined; + return new FixedUiElement("Constructing a locationInput failed due to" + e).SetClass("alert").ConstructElement(); } } diff --git a/assets/svg/hand.svg b/assets/svg/hand.svg index 75118da95..bbef4187a 100644 --- a/assets/svg/hand.svg +++ b/assets/svg/hand.svg @@ -1,53 +1,30 @@ - - - - image/svg+xml - - - - - - - + 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="svg6" + height="39.557907" + width="28.561806" + version="1.1"> + + + + image/svg+xml + + + + + + + diff --git a/index.css b/index.css index bf49576ee..bfdfad61a 100644 --- a/index.css +++ b/index.css @@ -307,6 +307,7 @@ li::marker { @keyframes slide { + /* This is the animation on the marker to add a new point - it slides through all the possible presets */ from { transform: translateX(0%); } @@ -316,6 +317,51 @@ li::marker { } } +.hand-drag-animation { + animation: hand-drag-animation 6s ease-in-out infinite; + transform-origin: 50% 125%; +} + +@keyframes hand-drag-animation { + /* This is the animation on the little extra hand on the location input. If fades in, invites the user to interact/drag the map */ + 0% { + opacity: 0; + transform: rotate(-30deg); + } + + 6% { + opacity: 1; + transform: rotate(-30deg); + } + + 12% { + opacity: 1; + transform: rotate(-45deg); + } + + 24% { + opacity: 1; + transform: rotate(-00deg); + } + + 30% { + opacity: 1; + transform: rotate(-30deg); + } + + + 36% { + opacity: 0; + transform: rotate(-30deg); + } + + 100% { + opacity: 0; + transform: rotate(-30deg); + } + +} + /**************************************/ diff --git a/test.ts b/test.ts index c34a6c303..51c8177a1 100644 --- a/test.ts +++ b/test.ts @@ -1,16 +1,12 @@ -const client_token = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" +import LocationInput from "./UI/Input/LocationInput"; +import Loc from "./Models/Loc"; +import {UIEventSource} from "./Logic/UIEventSource"; -const image_id = '196804715753265'; -const api_url = 'https://graph.mapillary.com/' + image_id + '?fields=thumb_1024_url&&access_token=' + client_token; -fetch(api_url, - { - headers: {'Authorization': 'OAuth ' + client_token} - } -).then(response => { - return response.json() -}).then( - json => { - const thumbnail_url = json["thumb_1024"] - console.log(thumbnail_url) - } -) \ No newline at end of file +new LocationInput({ + centerLocation: new UIEventSource({ + lat: 51.1110, + lon: 3.3701, + zoom : 14 + }) +}).SetStyle("height: 500px") + .AttachTo("maindiv"); \ No newline at end of file From 4da6070b2820ba3d66aa539832f141268ab5fd1b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 19:56:59 +0200 Subject: [PATCH 054/110] Small fixes after feedback --- .../Actors/SaveTileToLocalStorageActor.ts | 4 +++- .../TiledFromLocalStorageSource.ts | 14 ++++++++++++-- assets/themes/cyclestreets/cyclestreets.json | 4 ++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts b/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts index 331168bb8..22e6a7ba8 100644 --- a/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts +++ b/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts @@ -7,7 +7,8 @@ import {FeatureSourceForLayer} from "../FeatureSource"; export default class SaveTileToLocalStorageActor { public static readonly storageKey: string = "cached-features"; - + public static readonly formatVersion : string = "1" + constructor(source: FeatureSourceForLayer, tileIndex: number) { source.features.addCallbackAndRunD(features => { const key = `${SaveTileToLocalStorageActor.storageKey}-${source.layer.layerDef.id}-${tileIndex}` @@ -20,6 +21,7 @@ export default class SaveTileToLocalStorageActor { try { localStorage.setItem(key, JSON.stringify(features)); localStorage.setItem(key + "-time", JSON.stringify(now)) + localStorage.setItem(key+"-format", SaveTileToLocalStorageActor.formatVersion) } catch (e) { console.warn("Could not save the features to local storage:", e) } diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts index 4b578b7da..3ad933d7e 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -32,8 +32,18 @@ public tileFreshness : Map = new Map() console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Tiles.tile_from_index(i).join("/")).join(", ")) for (const index of indexes) { - const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" +index+"-time"; - const data = Number(localStorage.getItem(prefix)) + + const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" +index; + const version = localStorage.getItem(prefix+"-format") + if(version === undefined || version !== SaveTileToLocalStorageActor.formatVersion){ + // Invalid version! Remove this tile from local storage + localStorage.removeItem(prefix) + undefinedTiles.add(index) + console.log("Dropped old format tile", prefix) + continue + } + + const data = Number(localStorage.getItem(prefix+"-time")) const freshness = new Date() freshness.setTime(data) this.tileFreshness.set(index, freshness) diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index 45ad1a90c..39f75ac78 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -35,6 +35,10 @@ "startLon": 3.2228, "maintainer": "MapComplete", "widenfactor": 2, + "clustering": { + "maxZoom": 12, + "minNeededElements": 200 + }, "roamingRenderings": [ { "question": { From a6e8714ae07fe3f63f55172938de1f0503e5f543 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 23:56:59 +0200 Subject: [PATCH 055/110] Refactoring of image detection, fix loading wikimedia images --- Logic/Actors/ImageSearcher.ts | 173 ---------------- .../TiledFromLocalStorageSource.ts | 3 +- Logic/ImageProviders/AllImageProviders.ts | 54 ++++- Logic/ImageProviders/GenericImageProvider.ts | 36 ++++ .../ImageProviders/ImageAttributionSource.ts | 33 ---- Logic/ImageProviders/ImageProvider.ts | 64 ++++++ Logic/ImageProviders/Imgur.ts | 23 ++- Logic/ImageProviders/LicenseInfo.ts | 10 + Logic/ImageProviders/Mapillary.ts | 59 +++--- Logic/ImageProviders/WikidataImageProvider.ts | 51 +++++ Logic/ImageProviders/Wikimedia.ts | 185 ------------------ .../ImageProviders/WikimediaImageProvider.ts | 163 +++++++++++++++ Logic/UIEventSource.ts | 2 +- Models/Constants.ts | 7 +- UI/Image/AttributedImage.ts | 23 +-- UI/Image/Attribution.ts | 2 +- UI/Image/ImageCarousel.ts | 67 +++---- UI/SpecialVisualizations.ts | 7 +- assets/themes/natuurpunt/natuurpunt.json | 4 +- test/ImageSearcher.spec.ts | 28 --- test/TestAll.ts | 2 - 21 files changed, 468 insertions(+), 528 deletions(-) delete mode 100644 Logic/Actors/ImageSearcher.ts create mode 100644 Logic/ImageProviders/GenericImageProvider.ts delete mode 100644 Logic/ImageProviders/ImageAttributionSource.ts create mode 100644 Logic/ImageProviders/ImageProvider.ts create mode 100644 Logic/ImageProviders/LicenseInfo.ts create mode 100644 Logic/ImageProviders/WikidataImageProvider.ts delete mode 100644 Logic/ImageProviders/Wikimedia.ts create mode 100644 Logic/ImageProviders/WikimediaImageProvider.ts delete mode 100644 test/ImageSearcher.spec.ts diff --git a/Logic/Actors/ImageSearcher.ts b/Logic/Actors/ImageSearcher.ts deleted file mode 100644 index c2d089e51..000000000 --- a/Logic/Actors/ImageSearcher.ts +++ /dev/null @@ -1,173 +0,0 @@ -import {ImagesInCategory, Wikidata, Wikimedia} from "../ImageProviders/Wikimedia"; -import {UIEventSource} from "../UIEventSource"; - -/** - * There are multiple way to fetch images for an object - * 1) There is an image tag - * 2) There is an image tag, the image tag contains multiple ';'-separated URLS - * 3) there are multiple image tags, e.g. 'image', 'image:0', 'image:1', and 'image_0', 'image_1' - however, these are pretty rare so we are gonna ignore them - * 4) There is a wikimedia_commons-tag, which either has a 'File': or a 'category:' containing images - * 5) There is a wikidata-tag, and the wikidata item either has an 'image' attribute or has 'a link to a wikimedia commons category' - * 6) There is a wikipedia article, from which we can deduct the wikidata item - * - * For some images, author and license should be shown - */ -/** - * Class which search for all the possible locations for images and which builds a list of UI-elements for it. - * Note that this list is embedded into an UIEVentSource, ready to put it into a carousel. - * - */ -export class ImageSearcher extends UIEventSource<{ key: string, url: string }[]> { - - private static _cache = new Map(); - private readonly _wdItem = new UIEventSource(""); - private readonly _commons = new UIEventSource(""); - - private constructor(tags: UIEventSource, imagePrefix = "image", loadSpecial = true) { - super([]) - const self = this; - - function AddImages(images: { key: string, url: string }[]) { - const oldUrls = self.data.map(kurl => kurl.url); - let somethingChanged = false; - for (const image of images) { - const url = image.url; - - if (url === undefined || url === null || url === "") { - continue; - } - if (oldUrls.indexOf(url) >= 0) { - // Already exists - continue; - } - - self.data.push(image); - somethingChanged = true; - } - if (somethingChanged) { - self.ping(); - } - } - - function addImage(image: string) { - AddImages([{url: image, key: undefined}]); - } - - - // By wrapping this in a UIEventSource, we prevent multiple queries of loadWikiData - this._wdItem.addCallback(wdItemContents => { - ImageSearcher.loadWikidata(wdItemContents, addImage); - }); - this._commons.addCallback(commonsData => { - ImageSearcher.LoadCommons(commonsData, addImage) - }); - tags.addCallbackAndRun(tags => { - AddImages(ImageSearcher.LoadImages(tags, imagePrefix)); - }); - - if (loadSpecial) { - tags.addCallbackAndRunD(tags => { - - const wdItem = tags.wikidata; - if (wdItem !== undefined) { - self._wdItem.setData(wdItem); - } - const commons = tags.wikimedia_commons; - if (commons !== undefined) { - self._commons.setData(commons); - } - - if (tags.mapillary) { - let mapillary = tags.mapillary; - const prefix = "https://www.mapillary.com/map/im/"; - - let regex = /https?:\/\/www.mapillary.com\/app\/.*pKey=([^&]*).*/ - let match = mapillary.match(regex); - if (match) { - mapillary = match[1]; - } - - if (mapillary.indexOf(prefix) < 0) { - mapillary = prefix + mapillary; - } - - - AddImages([{url: mapillary, key: undefined}]); - } - }) - } - } - - public static construct(tags: UIEventSource, imagePrefix = "image", loadSpecial = true): ImageSearcher { - const key = tags.data["id"] + " " + imagePrefix + loadSpecial; - if (tags.data["id"] !== undefined && ImageSearcher._cache.has(key)) { - return ImageSearcher._cache.get(key) - } - - const searcher = new ImageSearcher(tags, imagePrefix, loadSpecial); - ImageSearcher._cache.set(key, searcher) - return searcher; - } - - private static loadWikidata(wikidataItem, addImage: ((url: string) => void)): void { - // Load the wikidata item, then detect usage on 'commons' - let allWikidataId = wikidataItem.split(";"); - for (let wikidataId of allWikidataId) { - // @ts-ignore - if (wikidataId.startsWith("Q")) { - wikidataId = wikidataId.substr(1); - } - Wikimedia.GetWikiData(parseInt(wikidataId), (wd: Wikidata) => { - addImage(wd.image); - Wikimedia.GetCategoryFiles(wd.commonsWiki, (images: ImagesInCategory) => { - for (const image of images.images) { - if (image.startsWith("File:")) { - addImage(image); - } - } - }) - }) - } - } - - private static LoadCommons(commonsData: string, addImage: ((url: string) => void)): void { - const allCommons: string[] = commonsData.split(";"); - for (const commons of allCommons) { - if (commons.startsWith("Category:")) { - Wikimedia.GetCategoryFiles(commons, (images: ImagesInCategory) => { - for (const image of images.images) { - if (image.startsWith("File:")) { - addImage(image) - } - } - }) - } else { - if (commons.startsWith("File:")) { - addImage(commons) - } - } - } - } - - private static LoadImages(tags: any, imagePrefix: string): { key: string, url: string }[] { - const imageTag = tags[imagePrefix]; - const images: { key: string, url: string }[] = []; - if (imageTag !== undefined) { - const bareImages = imageTag.split(";"); - for (const bareImage of bareImages) { - images.push({key: imagePrefix, url: bareImage}) - } - } - - for (const key in tags) { - if (key.startsWith(imagePrefix + ":")) { - const url = tags[key] - images.push({key: key, url: url}) - } - } - - - return images; - } - -} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts index 3ad933d7e..016537968 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -24,11 +24,12 @@ public tileFreshness : Map = new Map() // @ts-ignore const indexes: number[] = Object.keys(localStorage) .filter(key => { - return key.startsWith(prefix) && !key.endsWith("-time"); + return key.startsWith(prefix) && !key.endsWith("-time") && !key.endsWith("-format"); }) .map(key => { return Number(key.substring(prefix.length)); }) + .filter(i => !isNaN(i)) console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Tiles.tile_from_index(i).join("/")).join(", ")) for (const index of indexes) { diff --git a/Logic/ImageProviders/AllImageProviders.ts b/Logic/ImageProviders/AllImageProviders.ts index e616e59e7..c1fe3bb45 100644 --- a/Logic/ImageProviders/AllImageProviders.ts +++ b/Logic/ImageProviders/AllImageProviders.ts @@ -1,9 +1,59 @@ import {Mapillary} from "./Mapillary"; -import {Wikimedia} from "./Wikimedia"; +import {WikimediaImageProvider} from "./WikimediaImageProvider"; import {Imgur} from "./Imgur"; +import GenericImageProvider from "./GenericImageProvider"; +import {UIEventSource} from "../UIEventSource"; +import ImageProvider, {ProvidedImage} from "./ImageProvider"; +import {WikidataImageProvider} from "./WikidataImageProvider"; +import {Utils} from "../../Utils"; +/** + * A generic 'from the interwebz' image picker, without attribution + */ export default class AllImageProviders { - public static ImageAttributionSource = [Imgur.singleton, Mapillary.singleton, Wikimedia.singleton] + public static ImageAttributionSource: ImageProvider[] = [ + Imgur.singleton, + Mapillary.singleton, + WikidataImageProvider.singleton, + WikimediaImageProvider.singleton, + new GenericImageProvider(Imgur.defaultValuePrefix)] + + + private static _cache: Map> = new Map>() + + public static LoadImagesFor(tags: UIEventSource, imagePrefix: string, loadSpecialSource: boolean): UIEventSource { + const id = tags.data.id + if (id === undefined) { + return undefined; + } + + const cached = this._cache.get(tags.data.id) + if (cached !== undefined) { + return cached + } + + const source = new UIEventSource([]) + this._cache.set(id, source) + const allSources = [] + for (const imageProvider of AllImageProviders.ImageAttributionSource) { + const singleSource = imageProvider.GetRelevantUrls(tags) + allSources.push(singleSource) + singleSource.addCallbackAndRunD(_ => { + const all : ProvidedImage[] = [].concat(...allSources.map(source => source.data)) + const uniq = [] + const seen = new Set() + for (const img of all) { + if(seen.has(img.url)){ + continue + } + seen.add(img.url) + uniq.push(img) + } + source.setData(uniq) + }) + } + return source; + } } \ No newline at end of file diff --git a/Logic/ImageProviders/GenericImageProvider.ts b/Logic/ImageProviders/GenericImageProvider.ts new file mode 100644 index 000000000..170829fe0 --- /dev/null +++ b/Logic/ImageProviders/GenericImageProvider.ts @@ -0,0 +1,36 @@ +import ImageProvider, {ProvidedImage} from "./ImageProvider"; + +export default class GenericImageProvider extends ImageProvider { + public defaultKeyPrefixes: string[] = ["image"]; + + private readonly _valuePrefixBlacklist: string[]; + + public constructor(valuePrefixBlacklist: string[]) { + super(); + this._valuePrefixBlacklist = valuePrefixBlacklist; + } + + + protected DownloadAttribution(url: string) { + return undefined + } + + async ExtractUrls(key: string, value: string): Promise[]> { + + if (this._valuePrefixBlacklist.some(prefix => value.startsWith(prefix))) { + return [] + } + + return [Promise.resolve({ + key: key, + url: value, + provider: this + })] + } + + SourceIcon(backlinkSource?: string) { + return undefined; + } + + +} \ No newline at end of file diff --git a/Logic/ImageProviders/ImageAttributionSource.ts b/Logic/ImageProviders/ImageAttributionSource.ts deleted file mode 100644 index 1f0f097b7..000000000 --- a/Logic/ImageProviders/ImageAttributionSource.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {UIEventSource} from "../UIEventSource"; -import {LicenseInfo} from "./Wikimedia"; -import BaseUIElement from "../../UI/BaseUIElement"; - - -export default abstract class ImageAttributionSource { - - private _cache = new Map>() - - GetAttributionFor(url: string): UIEventSource { - const cached = this._cache.get(url); - if (cached !== undefined) { - return cached; - } - const src = new UIEventSource(undefined) - this._cache.set(url, src) - this.DownloadAttribution(url).then(license => - src.setData(license)) - .catch(e => console.error("Could not download license information for ", url, " due to", e)) - return src; - } - - - public abstract SourceIcon(backlinkSource?: string): BaseUIElement; - - /*Converts a value to a URL. Can return null if not applicable*/ - public PrepareUrl(value: string): string | UIEventSource { - return value; - } - - protected abstract DownloadAttribution(url: string): Promise; - -} \ No newline at end of file diff --git a/Logic/ImageProviders/ImageProvider.ts b/Logic/ImageProviders/ImageProvider.ts new file mode 100644 index 000000000..67154d945 --- /dev/null +++ b/Logic/ImageProviders/ImageProvider.ts @@ -0,0 +1,64 @@ +import {UIEventSource} from "../UIEventSource"; +import BaseUIElement from "../../UI/BaseUIElement"; +import {LicenseInfo} from "./LicenseInfo"; + +export interface ProvidedImage { + url: string, key: string, provider: ImageProvider +} + +export default abstract class ImageProvider { + + protected abstract readonly defaultKeyPrefixes : string[] + + private _cache = new Map>() + + GetAttributionFor(url: string): UIEventSource { + const cached = this._cache.get(url); + if (cached !== undefined) { + return cached; + } + const src =UIEventSource.FromPromise(this.DownloadAttribution(url)) + this._cache.set(url, src) + return src; + } + + public abstract SourceIcon(backlinkSource?: string): BaseUIElement; + + protected abstract DownloadAttribution(url: string): Promise; + + /** + * Given a properies object, maps it onto _all_ the available pictures for this imageProvider + */ + public GetRelevantUrls(allTags: UIEventSource, options?: { + prefixes?: string[] + }):UIEventSource { + const prefixes = options?.prefixes ?? this.defaultKeyPrefixes + const relevantUrls = new UIEventSource<{ url: string; key: string; provider: ImageProvider }[]>([]) + const seenValues = new Set() + allTags.addCallbackAndRunD(tags => { + for (const key in tags) { + if(!prefixes.some(prefix => key.startsWith(prefix))){ + continue + } + const value = tags[key] + if(seenValues.has(value)){ + continue + } + seenValues.add(value) + + this.ExtractUrls(key, value).then(promises => { + for (const promise of promises) { + promise.then(providedImage => { + relevantUrls.data.push(providedImage) + relevantUrls.ping() + }) + } + }) + } + }) + return relevantUrls + } + + public abstract ExtractUrls(key: string, value: string) : Promise[]>; + +} \ No newline at end of file diff --git a/Logic/ImageProviders/Imgur.ts b/Logic/ImageProviders/Imgur.ts index f85f3228a..289dad1f8 100644 --- a/Logic/ImageProviders/Imgur.ts +++ b/Logic/ImageProviders/Imgur.ts @@ -1,12 +1,14 @@ -// @ts-ignore import $ from "jquery" -import {LicenseInfo} from "./Wikimedia"; -import ImageAttributionSource from "./ImageAttributionSource"; +import ImageProvider, {ProvidedImage} from "./ImageProvider"; import BaseUIElement from "../../UI/BaseUIElement"; import {Utils} from "../../Utils"; import Constants from "../../Models/Constants"; +import {LicenseInfo} from "./LicenseInfo"; -export class Imgur extends ImageAttributionSource { +export class Imgur extends ImageProvider { + + public static readonly defaultValuePrefix = ["https://i.imgur.com"] + public readonly defaultKeyPrefixes: string[] = ["image"]; public static readonly singleton = new Imgur(); @@ -87,7 +89,7 @@ export class Imgur extends ImageAttributionSource { return undefined; } - protected async DownloadAttribution(url: string): Promise { + protected DownloadAttribution: (url: string) => Promise = async (url: string) => { const hash = url.substr("https://i.imgur.com/".length).split(".jpg")[0]; const apiUrl = 'https://api.imgur.com/3/image/' + hash; @@ -110,5 +112,16 @@ export class Imgur extends ImageAttributionSource { return licenseInfo } + public async ExtractUrls(key: string, value: string): Promise[]> { + if (Imgur.defaultValuePrefix.some(prefix => value.startsWith(prefix))) { + return [Promise.resolve({ + url: value, + key: key, + provider: this + })] + } + return [] + } + } \ No newline at end of file diff --git a/Logic/ImageProviders/LicenseInfo.ts b/Logic/ImageProviders/LicenseInfo.ts new file mode 100644 index 000000000..b5954693c --- /dev/null +++ b/Logic/ImageProviders/LicenseInfo.ts @@ -0,0 +1,10 @@ +export class LicenseInfo { + artist: string = ""; + license: string = ""; + licenseShortName: string = ""; + usageTerms: string = ""; + attributionRequired: boolean = false; + copyrighted: boolean = false; + credit: string = ""; + description: string = ""; +} \ No newline at end of file diff --git a/Logic/ImageProviders/Mapillary.ts b/Logic/ImageProviders/Mapillary.ts index ae5808a01..45342bc14 100644 --- a/Logic/ImageProviders/Mapillary.ts +++ b/Logic/ImageProviders/Mapillary.ts @@ -1,19 +1,19 @@ -import {LicenseInfo} from "./Wikimedia"; -import ImageAttributionSource from "./ImageAttributionSource"; +import ImageProvider, {ProvidedImage} from "./ImageProvider"; import BaseUIElement from "../../UI/BaseUIElement"; import {UIEventSource} from "../UIEventSource"; import Svg from "../../Svg"; import {Utils} from "../../Utils"; +import {LicenseInfo} from "./LicenseInfo"; +import Constants from "../../Models/Constants"; -export class Mapillary extends ImageAttributionSource { +export class Mapillary extends ImageProvider { + defaultKeyPrefixes = ["mapillary"] + public static readonly singleton = new Mapillary(); private static readonly v4_cached_urls = new Map>(); - private static readonly client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' - private static readonly client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" - private constructor() { super(); } @@ -56,29 +56,34 @@ export class Mapillary extends ImageAttributionSource { return Svg.mapillary_svg(); } - PrepareUrl(value: string): string | UIEventSource { - const keyV = Mapillary.ExtractKeyFromURL(value) - if (!keyV.isApiv4) { - return `https://images.mapillary.com/${keyV.key}/thumb-640.jpg?client_id=${Mapillary.client_token_v3}` - } else { - const key = keyV.key; - if (Mapillary.v4_cached_urls.has(key)) { - return Mapillary.v4_cached_urls.get(key) - } - - const metadataUrl = 'https://graph.mapillary.com/' + key + '?fields=thumb_1024_url&&access_token=' + Mapillary.client_token_v4; - const source = new UIEventSource(undefined) - Mapillary.v4_cached_urls.set(key, source) - Utils.downloadJson(metadataUrl).then( - json => { - console.warn("Got response on mapillary image", json, json["thumb_1024_url"]) - return source.setData(json["thumb_1024_url"]); - } - ) - return source - } + async ExtractUrls(key: string, value: string): Promise[]> { + return [this.PrepareUrlAsync(key, value)] } + private async PrepareUrlAsync(key: string, value: string): Promise { + const keyV = Mapillary.ExtractKeyFromURL(value) + if (!keyV.isApiv4) { + const url = `https://images.mapillary.com/${keyV.key}/thumb-640.jpg?client_id=${Constants.mapillary_client_token_v3}` + return { + url: url, + provider: this, + key: key + } + } else { + const key = keyV.key; + const metadataUrl = 'https://graph.mapillary.com/' + key + '?fields=thumb_1024_url&&access_token=' + Constants.mapillary_client_token_v4; + const source = new UIEventSource(undefined) + Mapillary.v4_cached_urls.set(key, source) + const response = await Utils.downloadJson(metadataUrl) + const url = response["thumb_1024_url"]; + return { + url: url, + provider: this, + key: key + } + } + } + protected async DownloadAttribution(url: string): Promise { const keyV = Mapillary.ExtractKeyFromURL(url) diff --git a/Logic/ImageProviders/WikidataImageProvider.ts b/Logic/ImageProviders/WikidataImageProvider.ts new file mode 100644 index 000000000..3bb7d3c91 --- /dev/null +++ b/Logic/ImageProviders/WikidataImageProvider.ts @@ -0,0 +1,51 @@ +import {Utils} from "../../Utils"; +import ImageProvider, {ProvidedImage} from "./ImageProvider"; +import BaseUIElement from "../../UI/BaseUIElement"; +import Svg from "../../Svg"; +import {WikimediaImageProvider} from "./WikimediaImageProvider"; + +export class WikidataImageProvider extends ImageProvider { + + public SourceIcon(backlinkSource?: string): BaseUIElement { + throw Svg.wikidata_svg(); + } + + public static readonly singleton = new WikidataImageProvider() + public readonly defaultKeyPrefixes = ["wikidata"] + + private constructor() { + super() + } + + protected DownloadAttribution(url: string): Promise { + throw new Error("Method not implemented; shouldn't be needed!"); + } + + public async ExtractUrls(key: string, value: string): Promise[]> { + const wikidataUrl = "https://www.wikidata.org/wiki/" + if (value.startsWith(wikidataUrl)) { + value = value.substring(wikidataUrl.length) + } + if (!value.startsWith("Q")) { + value = "Q" + value + } + const url = "https://www.wikidata.org/wiki/Special:EntityData/" + value + ".json"; + const response = await Utils.downloadJson(url) + const entity = response.entities[value]; + const commons = entity.sitelinks.commonswiki; + // P18 is the claim 'depicted in this image' + const image = entity.claims.P18?.[0]?.mainsnak?.datavalue?.value; + const allImages = [] + if (image !== undefined) { + // We found a 'File://' + const promises = await WikimediaImageProvider.singleton.ExtractUrls(key, image) + allImages.push(...promises) + } + if (commons !== undefined) { + const promises = await WikimediaImageProvider.singleton.ExtractUrls(commons, image) + allImages.push(...promises) + } + return allImages + } + +} \ No newline at end of file diff --git a/Logic/ImageProviders/Wikimedia.ts b/Logic/ImageProviders/Wikimedia.ts deleted file mode 100644 index cd5aea5d5..000000000 --- a/Logic/ImageProviders/Wikimedia.ts +++ /dev/null @@ -1,185 +0,0 @@ -import ImageAttributionSource from "./ImageAttributionSource"; -import BaseUIElement from "../../UI/BaseUIElement"; -import Svg from "../../Svg"; -import Link from "../../UI/Base/Link"; -import {Utils} from "../../Utils"; - -/** - * This module provides endpoints for wikipedia/wikimedia and others - */ -export class Wikimedia extends ImageAttributionSource { - - - public static readonly singleton = new Wikimedia(); - - private constructor() { - super(); - } - - - static ImageNameToUrl(filename: string, width: number = 500, height: number = 200): string { - filename = encodeURIComponent(filename); - return "https://commons.wikimedia.org/wiki/Special:FilePath/" + filename + "?width=" + width + "&height=" + height; - } - - static GetCategoryFiles(categoryName: string, handleCategory: ((ImagesInCategory: ImagesInCategory) => void), - alreadyLoaded = 0, - continueParameter: { k: string, param: string } = undefined) { - if (categoryName === undefined || categoryName === null || categoryName === "") { - return; - } - // @ts-ignore - if (!categoryName.startsWith("Category:")) { - categoryName = "Category:" + categoryName; - } - let url = "https://commons.wikimedia.org/w/api.php?" + - "action=query&list=categorymembers&format=json&" + - "&origin=*" + - "&cmtitle=" + encodeURIComponent(categoryName); - if (continueParameter !== undefined) { - url = url + "&" + continueParameter.k + "=" + continueParameter.param; - } - const self = this; - console.log("Loading a wikimedia category: ", url) - Utils.downloadJson(url).then((response) => { - let imageOverview = new ImagesInCategory(); - let members = response.query?.categorymembers; - if (members === undefined) { - members = []; - } - - for (const member of members) { - imageOverview.images.push(member.title); - } - console.log("Got images! ", imageOverview) - if (response.continue === undefined) { - handleCategory(imageOverview); - return; - } - - if (alreadyLoaded > 10) { - console.log(`Recursive wikimedia category load stopped for ${categoryName} - got already enough images now (${alreadyLoaded})`) - handleCategory(imageOverview) - return; - } - - self.GetCategoryFiles(categoryName, - (recursiveImages) => { - recursiveImages.images.push(...imageOverview.images); - handleCategory(recursiveImages); - }, - alreadyLoaded + 10, - {k: "cmcontinue", param: response.continue.cmcontinue}) - - }); - } - - static GetWikiData(id: number, handleWikidata: ((Wikidata) => void)) { - const url = "https://www.wikidata.org/wiki/Special:EntityData/Q" + id + ".json"; - Utils.downloadJson(url).then(response => { - const entity = response.entities["Q" + id]; - const commons = entity.sitelinks.commonswiki; - const wd = new Wikidata(); - wd.commonsWiki = commons?.title; - - // P18 is the claim 'depicted in this image' - const image = entity.claims.P18?.[0]?.mainsnak?.datavalue?.value; - if (image) { - wd.image = "File:" + image; - } - handleWikidata(wd); - }); - } - - private static ExtractFileName(url: string) { - if (!url.startsWith("http")) { - return url; - } - const path = new URL(url).pathname - return path.substring(path.lastIndexOf("/") + 1); - - } - - SourceIcon(backlink: string): BaseUIElement { - const img = Svg.wikimedia_commons_white_svg() - .SetStyle("width:2em;height: 2em"); - if (backlink === undefined) { - return img - } - - - return new Link(Svg.wikimedia_commons_white_img, - `https://commons.wikimedia.org/wiki/${backlink}`, true) - - - } - - PrepareUrl(value: string): string { - - if (value.toLowerCase().startsWith("https://commons.wikimedia.org/wiki/")) { - return value; - } - return Wikimedia.ImageNameToUrl(value, 500, 400) - .replace(/'/g, '%27'); - } - - protected async DownloadAttribution(filename: string): Promise { - filename = Wikimedia.ExtractFileName(filename) - - if (filename === "") { - return undefined; - } - - const url = "https://en.wikipedia.org/w/" + - "api.php?action=query&prop=imageinfo&iiprop=extmetadata&" + - "titles=" + filename + - "&format=json&origin=*"; - const data = await Utils.downloadJson(url) - const licenseInfo = new LicenseInfo(); - const license = (data.query.pages[-1].imageinfo ?? [])[0]?.extmetadata; - if (license === undefined) { - console.error("This file has no usable metedata or license attached... Please fix the license info file yourself!") - return undefined; - } - - licenseInfo.artist = license.Artist?.value; - licenseInfo.license = license.License?.value; - licenseInfo.copyrighted = license.Copyrighted?.value; - licenseInfo.attributionRequired = license.AttributionRequired?.value; - licenseInfo.usageTerms = license.UsageTerms?.value; - licenseInfo.licenseShortName = license.LicenseShortName?.value; - licenseInfo.credit = license.Credit?.value; - licenseInfo.description = license.ImageDescription?.value; - return licenseInfo; - - } - - -} - -export class Wikidata { - - commonsWiki: string; - image: string; - -} - -export class ImagesInCategory { - // Filenames of relevant images - images: string[] = []; -} - -export class LicenseInfo { - - - artist: string = ""; - license: string = ""; - licenseShortName: string = ""; - usageTerms: string = ""; - attributionRequired: boolean = false; - copyrighted: boolean = false; - credit: string = ""; - description: string = ""; - - -} \ No newline at end of file diff --git a/Logic/ImageProviders/WikimediaImageProvider.ts b/Logic/ImageProviders/WikimediaImageProvider.ts new file mode 100644 index 000000000..cf4c1837b --- /dev/null +++ b/Logic/ImageProviders/WikimediaImageProvider.ts @@ -0,0 +1,163 @@ +import ImageProvider, {ProvidedImage} from "./ImageProvider"; +import BaseUIElement from "../../UI/BaseUIElement"; +import Svg from "../../Svg"; +import Link from "../../UI/Base/Link"; +import {Utils} from "../../Utils"; +import {LicenseInfo} from "./LicenseInfo"; + +/** + * This module provides endpoints for wikimedia and others + */ +export class WikimediaImageProvider extends ImageProvider { + + + public readonly defaultKeyPrefixes = ["wikimedia_commons"] + public static readonly singleton = new WikimediaImageProvider(); + + private constructor() { + super(); + } + /** + * Recursively walks a wikimedia commons category in order to search for (image) files + * Returns (a promise of) a list of URLS + * @param categoryName The name of the wikimedia category + * @param maxLoad: the maximum amount of images to return + * @param continueParameter: if the page indicates that more pages should be loaded, this uses a token to continue. Provided by wikimedia + */ + private static async GetImagesInCategory(categoryName: string, + maxLoad = 10, + continueParameter: string = undefined): Promise { + if (categoryName === undefined || categoryName === null || categoryName === "") { + return []; + } + if (!categoryName.startsWith("Category:")) { + categoryName = "Category:" + categoryName; + } + + let url = "https://commons.wikimedia.org/w/api.php?" + + "action=query&list=categorymembers&format=json&" + + "&origin=*" + + "&cmtitle=" + encodeURIComponent(categoryName); + if (continueParameter !== undefined) { + url = `${url}&cmcontinue=${continueParameter}`; + } + console.log("Loading a wikimedia category: ", url) + const response = await Utils.downloadJson(url) + const members = response.query?.categorymembers ?? []; + const imageOverview: string[] = members.map(member => member.title); + + if (response.continue === undefined) { + // We are done crawling through the category - no continuation in sight + return imageOverview; + } + + if (maxLoad - imageOverview.length <= 0) { + console.log(`Recursive wikimedia category load stopped for ${categoryName}`) + return imageOverview; + } + + // We do have a continue token - let's load the next page + const recursive = await this.GetImagesInCategory(categoryName, maxLoad - imageOverview.length, response.continue.cmcontinue) + imageOverview.push(...recursive) + return imageOverview + } + + private static ExtractFileName(url: string) { + if (!url.startsWith("http")) { + return url; + } + const path = new URL(url).pathname + return path.substring(path.lastIndexOf("/") + 1); + + } + + SourceIcon(backlink: string): BaseUIElement { + const img = Svg.wikimedia_commons_white_svg() + .SetStyle("width:2em;height: 2em"); + if (backlink === undefined) { + return img + } + + + return new Link(Svg.wikimedia_commons_white_img, + `https://commons.wikimedia.org/wiki/${backlink}`, true) + + + } + + private PrepareUrl(value: string): string { + + if (value.toLowerCase().startsWith("https://commons.wikimedia.org/wiki/")) { + return value; + } + return (`https://commons.wikimedia.org/wiki/Special:FilePath/${encodeURIComponent(value)}?width=500&height=400`) + } + + protected async DownloadAttribution(filename: string): Promise { + filename = WikimediaImageProvider.ExtractFileName(filename) + + if (filename === "") { + return undefined; + } + + const url = "https://en.wikipedia.org/w/" + + "api.php?action=query&prop=imageinfo&iiprop=extmetadata&" + + "titles=" + filename + + "&format=json&origin=*"; + const data = await Utils.downloadJson(url) + const licenseInfo = new LicenseInfo(); + const license = (data.query.pages[-1].imageinfo ?? [])[0]?.extmetadata; + if (license === undefined) { + console.error("This file has no usable metedata or license attached... Please fix the license info file yourself!") + return undefined; + } + + licenseInfo.artist = license.Artist?.value; + licenseInfo.license = license.License?.value; + licenseInfo.copyrighted = license.Copyrighted?.value; + licenseInfo.attributionRequired = license.AttributionRequired?.value; + licenseInfo.usageTerms = license.UsageTerms?.value; + licenseInfo.licenseShortName = license.LicenseShortName?.value; + licenseInfo.credit = license.Credit?.value; + licenseInfo.description = license.ImageDescription?.value; + return licenseInfo; + + } + + private async UrlForImage(image: string): Promise{ + if(!image.startsWith("File:")){ + image = "File:"+image + } + return {url: this.PrepareUrl(image), key: undefined, provider: this} + } + + public async ExtractUrls(key: string, value: string): Promise[]> { + const commonsPrefix = "https://commons.wikimedia.org/wiki/" + if(value.startsWith(commonsPrefix)){ + value = value.substring(commonsPrefix.length) + } else if(value.startsWith("https://upload.wikimedia.org")){ + const result : ProvidedImage = { + key: undefined, + url: value, + provider: this + } + return [Promise.resolve(result)] + } + if(value.startsWith("Category:")){ + const urls = await WikimediaImageProvider.GetImagesInCategory(value) + return urls.map(image => this.UrlForImage(image)) + } + if(value.startsWith("File:")){ + return [this.UrlForImage(value)] + } + if(value.startsWith("http")){ + // PRobably an error + return [] + } + // We do a last effort and assume this is a file + return [this.UrlForImage("File:"+value)] + } + + +} + diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index 42282ea2a..5f51dc004 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -64,7 +64,7 @@ export class UIEventSource { public static FromPromise(promise : Promise): UIEventSource{ const src = new UIEventSource(undefined) - promise.then(d => src.setData(d)) + promise?.then(d => src.setData(d)) return src } diff --git a/Models/Constants.ts b/Models/Constants.ts index d524c6689..4e588a057 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,8 +2,11 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.0-rc0"; + public static vNumber = "0.10.0-rc1"; public static ImgurApiKey = '7070e7167f0a25a' + public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' + public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" + public static defaultOverpassUrls = [ // The official instance, 10000 queries per day per project allowed "https://overpass-api.de/api/interpreter", @@ -15,6 +18,8 @@ export default class Constants { "https://overpass.openstreetmap.fr/api/interpreter" ] + + // The user journey states thresholds when a new feature gets unlocked public static userJourney = { moreScreenUnlock: 1, diff --git a/UI/Image/AttributedImage.ts b/UI/Image/AttributedImage.ts index 7614077c5..919a9ac17 100644 --- a/UI/Image/AttributedImage.ts +++ b/UI/Image/AttributedImage.ts @@ -1,30 +1,19 @@ import Combine from "../Base/Combine"; import Attribution from "./Attribution"; import Img from "../Base/Img"; -import ImageAttributionSource from "../../Logic/ImageProviders/ImageAttributionSource"; +import {ProvidedImage} from "../../Logic/ImageProviders/ImageProvider"; import BaseUIElement from "../BaseUIElement"; -import {VariableUiElement} from "../Base/VariableUIElement"; -import Loading from "../Base/Loading"; export class AttributedImage extends Combine { - constructor(urlSource: string, imgSource: ImageAttributionSource) { - const preparedUrl = imgSource.PrepareUrl(urlSource) + constructor(imageInfo: ProvidedImage) { let img: BaseUIElement; let attr: BaseUIElement - if (typeof preparedUrl === "string") { - img = new Img(urlSource); - attr = new Attribution(imgSource.GetAttributionFor(urlSource), imgSource.SourceIcon()) - } else { - img = new VariableUiElement(preparedUrl.map(url => { - if(url === undefined){ - return new Loading() - } - return new Img(url, false, {fallbackImage: './assets/svg/blocked.svg'}); - })) - attr = new VariableUiElement(preparedUrl.map(_ => new Attribution(imgSource.GetAttributionFor(urlSource), imgSource.SourceIcon()))) - } + img = new Img(imageInfo.url); + attr = new Attribution(imageInfo.provider.GetAttributionFor(imageInfo.url), + imageInfo.provider.SourceIcon(), + ) super([img, attr]); diff --git a/UI/Image/Attribution.ts b/UI/Image/Attribution.ts index 9afd9b815..f57911e93 100644 --- a/UI/Image/Attribution.ts +++ b/UI/Image/Attribution.ts @@ -3,7 +3,7 @@ import Translations from "../i18n/Translations"; import BaseUIElement from "../BaseUIElement"; import {VariableUiElement} from "../Base/VariableUIElement"; import {UIEventSource} from "../../Logic/UIEventSource"; -import {LicenseInfo} from "../../Logic/ImageProviders/Wikimedia"; +import {LicenseInfo} from "../../Logic/ImageProviders/LicenseInfo"; export default class Attribution extends VariableUiElement { diff --git a/UI/Image/ImageCarousel.ts b/UI/Image/ImageCarousel.ts index 4e7d032d2..c20f52080 100644 --- a/UI/Image/ImageCarousel.ts +++ b/UI/Image/ImageCarousel.ts @@ -4,29 +4,35 @@ import Combine from "../Base/Combine"; import DeleteImage from "./DeleteImage"; import {AttributedImage} from "./AttributedImage"; import BaseUIElement from "../BaseUIElement"; -import Img from "../Base/Img"; import Toggle from "../Input/Toggle"; -import {Wikimedia} from "../../Logic/ImageProviders/Wikimedia"; -import {Imgur} from "../../Logic/ImageProviders/Imgur"; -import {Mapillary} from "../../Logic/ImageProviders/Mapillary"; +import ImageProvider from "../../Logic/ImageProviders/ImageProvider"; export class ImageCarousel extends Toggle { - constructor(images: UIEventSource<{ key: string, url: string }[]>, tags: UIEventSource) { - const uiElements = images.map((imageURLS: { key: string, url: string }[]) => { + constructor(images: UIEventSource<{ key: string, url: string, provider: ImageProvider }[]>, tags: UIEventSource) { + const uiElements = images.map((imageURLS: { key: string, url: string, provider: ImageProvider }[]) => { const uiElements: BaseUIElement[] = []; for (const url of imageURLS) { - let image = ImageCarousel.CreateImageElement(url.url) - if (url.key !== undefined) { - image = new Combine([ - image, - new DeleteImage(url.key, tags).SetClass("delete-image-marker absolute top-0 left-0 pl-3") - ]).SetClass("relative"); + + try { + + let image = new AttributedImage(url) + + if (url.key !== undefined) { + image = new Combine([ + image, + new DeleteImage(url.key, tags).SetClass("delete-image-marker absolute top-0 left-0 pl-3") + ]).SetClass("relative"); + } + image + .SetClass("w-full block") + .SetStyle("min-width: 50px; background: grey;") + uiElements.push(image); + } catch (e) { + console.error("Could not generate image element for", url.url, "due to", e) } - image - .SetClass("w-full block") - .SetStyle("min-width: 50px; background: grey;") - uiElements.push(image); + + } return uiElements; }); @@ -38,33 +44,4 @@ export class ImageCarousel extends Toggle { ) this.SetClass("block w-full"); } - - /*** - * Creates either a 'simpleimage' or a 'wikimediaimage' based on the string - * @param url - * @constructor - */ - private static CreateImageElement(url: string): BaseUIElement { - // @ts-ignore - let attrSource: ImageAttributionSource = undefined; - if (url.startsWith("File:")) { - attrSource = Wikimedia.singleton - } else if (url.toLowerCase().startsWith("https://commons.wikimedia.org/wiki/")) { - attrSource = Wikimedia.singleton; - } else if (url.toLowerCase().startsWith("https://i.imgur.com/")) { - attrSource = Imgur.singleton - } else if (url.toLowerCase().startsWith("https://www.mapillary.com/map/im/")) { - attrSource = Mapillary.singleton - } else { - return new Img(url); - } - - try { - return new AttributedImage(url, attrSource) - } catch (e) { - console.error("Could not create an image: ", e) - return undefined; - } - - } } \ No newline at end of file diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 5b15cfdaa..4aba67e7d 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -13,20 +13,19 @@ import Translations from "./i18n/Translations"; import ReviewForm from "./Reviews/ReviewForm"; import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"; import State from "../State"; -import {ImageSearcher} from "../Logic/Actors/ImageSearcher"; import BaseUIElement from "./BaseUIElement"; import Title from "./Base/Title"; import Table from "./Base/Table"; import Histogram from "./BigComponents/Histogram"; import Loc from "../Models/Loc"; import {Utils} from "../Utils"; -import BaseLayer from "../Models/BaseLayer"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; import ImportButton from "./BigComponents/ImportButton"; import {Tag} from "../Logic/Tags/Tag"; import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer"; import Minimap from "./Base/Minimap"; +import AllImageProviders from "../Logic/ImageProviders/AllImageProviders"; export interface SpecialVisualization { funcName: string, @@ -76,9 +75,7 @@ export default class SpecialVisualizations { constr: (state: State, tags, args) => { const imagePrefix = args[0]; const loadSpecial = args[1].toLowerCase() === "true"; - const searcher: UIEventSource<{ key: string, url: string }[]> = ImageSearcher.construct(tags, imagePrefix, loadSpecial); - - return new ImageCarousel(searcher, tags); + return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefix, loadSpecial), tags); } }, { diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index 1ed0b7e88..c5345a55f 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -75,7 +75,7 @@ "icon": { "render": "circle:#FE6F32;./assets/themes/natuurpunt/nature_reserve.svg" }, - "presets": null + "presets": [] } }, { @@ -262,4 +262,4 @@ } } ] -} \ No newline at end of file +} diff --git a/test/ImageSearcher.spec.ts b/test/ImageSearcher.spec.ts deleted file mode 100644 index 9b3d713ac..000000000 --- a/test/ImageSearcher.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {Utils} from "../Utils"; -import {equal} from "assert"; -import T from "./TestHelper"; -import {UIEventSource} from "../Logic/UIEventSource"; -import {ImageSearcher} from "../Logic/Actors/ImageSearcher"; - -Utils.runningFromConsole = true; -export default class ImageSearcherSpec extends T { - - constructor() { - super("imagesearcher", [ - [ - "Should find images", - () => { - const tags = new UIEventSource({ - "mapillary": "https://www.mapillary.com/app/?pKey=bYH6FFl8LXAPapz4PNSh3Q" - }); - const searcher = ImageSearcher.construct(tags) - const result = searcher.data[0]; - equal(result.url, "https://www.mapillary.com/map/im/bYH6FFl8LXAPapz4PNSh3Q"); - } - ], - ]); - - } - - -} diff --git a/test/TestAll.ts b/test/TestAll.ts index 1a25a0afc..0efa98dd4 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -1,7 +1,6 @@ import TagSpec from "./Tag.spec"; import ImageAttributionSpec from "./ImageAttribution.spec"; import GeoOperationsSpec from "./GeoOperations.spec"; -import ImageSearcherSpec from "./ImageSearcher.spec"; import ThemeSpec from "./Theme.spec"; import UtilsSpec from "./Utils.spec"; import OsmObjectSpec from "./OsmObject.spec"; @@ -18,7 +17,6 @@ const allTests = [ new TagSpec(), new ImageAttributionSpec(), new GeoOperationsSpec(), - new ImageSearcherSpec(), new ThemeSpec(), new UtilsSpec(), new UnitsSpec(), From 0326668e622ce71e2e1a15f7e0dd7bdf5dc6fedb Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 23:58:17 +0200 Subject: [PATCH 056/110] Fix index page --- State.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/State.ts b/State.ts index 00dac3bb5..7279136b3 100644 --- a/State.ts +++ b/State.ts @@ -321,7 +321,7 @@ export default class State { ); this.overpassUrl = QueryParameters.GetQueryParameter("overpassUrl", - layoutToUse?.overpassUrl.join(","), + (layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(",") , "Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter" ).map(param => param.split(","), [], urls => urls.join(",")) From 8cbb693c98e1fe619affb631b45dc5774ca3baef Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 29 Sep 2021 23:59:56 +0200 Subject: [PATCH 057/110] Fix geolocation --- Logic/Actors/GeoLocationHandler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index 01e09a717..3abf8e8e2 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -60,12 +60,12 @@ export default class GeoLocationHandler extends VariableUiElement { * @private */ private readonly _previousLocationGrant: UIEventSource; - private readonly _layoutToUse: UIEventSource; + private readonly _layoutToUse: LayoutConfig; constructor( currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>, leafletMap: UIEventSource, - layoutToUse: UIEventSource + layoutToUse: LayoutConfig ) { const hasLocation = currentGPSLocation.map( (location) => location !== undefined @@ -264,7 +264,7 @@ export default class GeoLocationHandler extends VariableUiElement { } // We check that the GPS location is not out of bounds - const b = this._layoutToUse.data.lockLocation; + const b = this._layoutToUse.lockLocation; let inRange = true; if (b) { if (b !== true) { From 32cbd6e2c139abbdf8cdbafcadbced1b2fb20780 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 30 Sep 2021 00:26:21 +0200 Subject: [PATCH 058/110] Also pickup wikimedia categories in the image tags, fix #433 --- Logic/ImageProviders/AllImageProviders.ts | 10 +++--- .../ImageProviders/WikimediaImageProvider.ts | 35 +++++++++++-------- Models/ThemeConfig/FilterConfig.ts | 4 +++ UI/BigComponents/SimpleAddUI.ts | 21 +++++++++-- UI/SpecialVisualizations.ts | 10 ++---- .../public_bookcase/public_bookcase.json | 6 ++-- 6 files changed, 55 insertions(+), 31 deletions(-) diff --git a/Logic/ImageProviders/AllImageProviders.ts b/Logic/ImageProviders/AllImageProviders.ts index c1fe3bb45..18589f635 100644 --- a/Logic/ImageProviders/AllImageProviders.ts +++ b/Logic/ImageProviders/AllImageProviders.ts @@ -5,7 +5,6 @@ import GenericImageProvider from "./GenericImageProvider"; import {UIEventSource} from "../UIEventSource"; import ImageProvider, {ProvidedImage} from "./ImageProvider"; import {WikidataImageProvider} from "./WikidataImageProvider"; -import {Utils} from "../../Utils"; /** * A generic 'from the interwebz' image picker, without attribution @@ -17,12 +16,12 @@ export default class AllImageProviders { Mapillary.singleton, WikidataImageProvider.singleton, WikimediaImageProvider.singleton, - new GenericImageProvider(Imgur.defaultValuePrefix)] + new GenericImageProvider([].concat(...Imgur.defaultValuePrefix, WikimediaImageProvider.commonsPrefix))] private static _cache: Map> = new Map>() - public static LoadImagesFor(tags: UIEventSource, imagePrefix: string, loadSpecialSource: boolean): UIEventSource { + public static LoadImagesFor(tags: UIEventSource, imagePrefix?: string): UIEventSource { const id = tags.data.id if (id === undefined) { return undefined; @@ -33,11 +32,14 @@ export default class AllImageProviders { return cached } + const source = new UIEventSource([]) this._cache.set(id, source) const allSources = [] for (const imageProvider of AllImageProviders.ImageAttributionSource) { - const singleSource = imageProvider.GetRelevantUrls(tags) + const singleSource = imageProvider.GetRelevantUrls(tags, { + prefixes: imagePrefix !== undefined ? [imagePrefix] : undefined + }) allSources.push(singleSource) singleSource.addCallbackAndRunD(_ => { const all : ProvidedImage[] = [].concat(...allSources.map(source => source.data)) diff --git a/Logic/ImageProviders/WikimediaImageProvider.ts b/Logic/ImageProviders/WikimediaImageProvider.ts index cf4c1837b..b8940a520 100644 --- a/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/Logic/ImageProviders/WikimediaImageProvider.ts @@ -11,12 +11,15 @@ import {LicenseInfo} from "./LicenseInfo"; export class WikimediaImageProvider extends ImageProvider { - public readonly defaultKeyPrefixes = ["wikimedia_commons"] + private readonly commons_key = "wikimedia_commons" + public readonly defaultKeyPrefixes = [this.commons_key,"image"] public static readonly singleton = new WikimediaImageProvider(); + public static readonly commonsPrefix = "https://commons.wikimedia.org/wiki/" private constructor() { super(); } + /** * Recursively walks a wikimedia commons category in order to search for (image) files * Returns (a promise of) a list of URLS @@ -124,38 +127,42 @@ export class WikimediaImageProvider extends ImageProvider { } - private async UrlForImage(image: string): Promise{ - if(!image.startsWith("File:")){ - image = "File:"+image + private async UrlForImage(image: string): Promise { + if (!image.startsWith("File:")) { + image = "File:" + image } return {url: this.PrepareUrl(image), key: undefined, provider: this} } - + public async ExtractUrls(key: string, value: string): Promise[]> { - const commonsPrefix = "https://commons.wikimedia.org/wiki/" - if(value.startsWith(commonsPrefix)){ - value = value.substring(commonsPrefix.length) - } else if(value.startsWith("https://upload.wikimedia.org")){ - const result : ProvidedImage = { + + if(key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){ + return [] + } + + if (value.startsWith(WikimediaImageProvider.commonsPrefix)) { + value = value.substring(WikimediaImageProvider.commonsPrefix.length) + } else if (value.startsWith("https://upload.wikimedia.org")) { + const result: ProvidedImage = { key: undefined, url: value, provider: this } return [Promise.resolve(result)] } - if(value.startsWith("Category:")){ + if (value.startsWith("Category:")) { const urls = await WikimediaImageProvider.GetImagesInCategory(value) return urls.map(image => this.UrlForImage(image)) } - if(value.startsWith("File:")){ + if (value.startsWith("File:")) { return [this.UrlForImage(value)] } - if(value.startsWith("http")){ + if (value.startsWith("http")) { // PRobably an error return [] } // We do a last effort and assume this is a file - return [this.UrlForImage("File:"+value)] + return [this.UrlForImage("File:" + value)] } diff --git a/Models/ThemeConfig/FilterConfig.ts b/Models/ThemeConfig/FilterConfig.ts index 2dd9f69d6..7a032c6de 100644 --- a/Models/ThemeConfig/FilterConfig.ts +++ b/Models/ThemeConfig/FilterConfig.ts @@ -42,5 +42,9 @@ export default class FilterConfig { return {question: question, osmTags: osmTags}; }); + + if(this.options.length > 1 && this.options[0].osmTags["and"]?.length !== 0){ + throw "Error in "+context+"."+this.id+": the first option of a multi-filter should always be the 'reset' option and not have any filters" + } } } \ No newline at end of file diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 5e8a7b46d..ad6a73ec5 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -19,7 +19,6 @@ import CreateNewNodeAction from "../../Logic/Osm/Actions/CreateNewNodeAction"; import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject"; import PresetConfig from "../../Models/ThemeConfig/PresetConfig"; import FilteredLayer from "../../Models/FilteredLayer"; -import {And} from "../../Logic/Tags/And"; import {BBox} from "../../Logic/BBox"; /* @@ -230,7 +229,25 @@ export default class SimpleAddUI extends Toggle { const disableFiltersOrConfirm = new Toggle( openLayerOrConfirm, disableFilter, - preset.layerToAddTo.appliedFilters.map(filters => filters === undefined || filters.length === 0) + preset.layerToAddTo.appliedFilters.map(filters => { + if(filters === undefined || filters.length === 0){ + return true; + } + for (const filter of filters) { + if(filter.selected === 0 && filter.filter.options.length === 1){ + return false; + } + if(filter.selected !== undefined){ + const tags = filter.filter.options[filter.selected].osmTags + if(tags !== undefined && tags["and"]?.length !== 0){ + // This actually doesn't filter anything at all + return false; + } + } + } + return true + + }) ) diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 4aba67e7d..5801e1d88 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -66,16 +66,10 @@ export default class SpecialVisualizations { name: "image key/prefix", defaultValue: "image", doc: "The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... " - }, - { - name: "smart search", - defaultValue: "true", - doc: "Also include images given via 'Wikidata', 'wikimedia_commons' and 'mapillary" - }], + }], constr: (state: State, tags, args) => { const imagePrefix = args[0]; - const loadSpecial = args[1].toLowerCase() === "true"; - return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefix, loadSpecial), tags); + return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefix), tags); } }, { diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index cc21f23f8..64b6230c1 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -465,9 +465,9 @@ "id": "inside", "options": [ { - "question": "Binnen of buiten", - "osmTags": { - "and": [] + "question": { + "nl": "Binnen of buiten", + "en": "Indoor or outdoor" } }, { From 4f456e8a7f68ac1ffd8241636733409c82671fab Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 30 Sep 2021 04:13:23 +0200 Subject: [PATCH 059/110] Better tracking of cached data, only load data if needed --- Logic/Actors/GeoLocationHandler.ts | 3 + Logic/Actors/OverpassFeatureSource.ts | 110 +++------ .../Actors/SaveTileToLocalStorageActor.ts | 26 +- Logic/FeatureSource/FeaturePipeline.ts | 225 ++++++++++++------ .../FeatureSource/TileFreshnessCalculator.ts | 72 ++++++ .../TiledFeatureSource/OsmFeatureSource.ts | 12 +- .../TiledFeatureSource/TileHierarchyMerger.ts | 1 - .../TiledFromLocalStorageSource.ts | 38 +-- Logic/SimpleMetaTagger.ts | 9 +- Models/Constants.ts | 2 +- Models/TileRange.ts | 1 + test/TestAll.ts | 4 +- test/TileFreshnessCalculator.spec.ts | 31 +++ 13 files changed, 349 insertions(+), 185 deletions(-) create mode 100644 Logic/FeatureSource/TileFreshnessCalculator.ts create mode 100644 test/TileFreshnessCalculator.spec.ts diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index 3abf8e8e2..43ce441d7 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -207,6 +207,9 @@ export default class GeoLocationHandler extends VariableUiElement { }); const map = self._leafletMap.data; + if(map === undefined){ + return; + } const newMarker = L.marker(location.latlng, {icon: icon}); newMarker.addTo(map); diff --git a/Logic/Actors/OverpassFeatureSource.ts b/Logic/Actors/OverpassFeatureSource.ts index fa5de3eed..4f4f7a77f 100644 --- a/Logic/Actors/OverpassFeatureSource.ts +++ b/Logic/Actors/OverpassFeatureSource.ts @@ -1,5 +1,4 @@ import {UIEventSource} from "../UIEventSource"; -import Loc from "../../Models/Loc"; import {Or} from "../Tags/Or"; import {Overpass} from "../Osm/Overpass"; import FeatureSource from "../FeatureSource/FeatureSource"; @@ -9,6 +8,8 @@ import SimpleMetaTagger from "../SimpleMetaTagger"; import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import RelationsTracker from "../Osm/RelationsTracker"; import {BBox} from "../BBox"; +import Loc from "../../Models/Loc"; +import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; export default class OverpassFeatureSource implements FeatureSource { @@ -28,14 +29,7 @@ export default class OverpassFeatureSource implements FeatureSource { private readonly retries: UIEventSource = new UIEventSource(0); - /** - * The previous bounds for which the query has been run at the given zoom level - * - * Note that some layers only activate on a certain zoom level. - * If the map location changes, we check for each layer if it is loaded: - * we start checking the bounds at the first zoom level the layer might operate. If in bounds - no reload needed, otherwise we continue walking down - */ - private readonly _previousBounds: Map = new Map(); + private readonly state: { readonly locationControl: UIEventSource, readonly layoutToUse: LayoutConfig, @@ -44,11 +38,8 @@ export default class OverpassFeatureSource implements FeatureSource { readonly currentBounds: UIEventSource } private readonly _isActive: UIEventSource; - private _onUpdated?: (bbox: BBox, dataFreshness: Date) => void; + private readonly onBboxLoaded: (bbox: BBox, date: Date, layers: LayerConfig[]) => void; - /** - * The most important layer should go first, as that one gets first pick for the questions - */ constructor( state: { readonly locationControl: UIEventSource, @@ -60,68 +51,25 @@ export default class OverpassFeatureSource implements FeatureSource { }, options?: { isActive?: UIEventSource, - onUpdated?: (bbox: BBox, freshness: Date) => void, - relationTracker: RelationsTracker + relationTracker: RelationsTracker, + onBboxLoaded?: (bbox: BBox, date: Date, layers: LayerConfig[]) => void }) { this.state = state this._isActive = options.isActive; - this._onUpdated = options.onUpdated; + this.onBboxLoaded = options.onBboxLoaded this.relationsTracker = options.relationTracker - const location = state.locationControl const self = this; - - for (let i = 0; i < 25; i++) { - // This update removes all data on all layers -> erase the map on lower levels too - this._previousBounds.set(i, []); - } - - location.addCallback(() => { - self.update() - }); - state.currentBounds.addCallback(_ => { self.update() }) } - private GetFilter(interpreterUrl: string): Overpass { + private GetFilter(interpreterUrl: string, layersToDownload: LayerConfig[]): Overpass { let filters: TagsFilter[] = []; let extraScripts: string[] = []; - for (const layer of this.state.layoutToUse.layers) { - if (typeof (layer) === "string") { - throw "A layer was not expanded!" - } - if (this.state.locationControl.data.zoom < layer.minzoom) { - continue; - } - if (layer.doNotDownload) { - continue; - } - if (layer.source.geojsonSource !== undefined) { - // Not our responsibility to download this layer! - continue; - } - - - // Check if data for this layer has already been loaded - let previouslyLoaded = false; - for (let z = layer.minzoom; z < 25 && !previouslyLoaded; z++) { - const previousLoadedBounds = this._previousBounds.get(z); - if (previousLoadedBounds === undefined) { - continue; - } - for (const previousLoadedBound of previousLoadedBounds) { - previouslyLoaded = previouslyLoaded || this.state.currentBounds.data.isContainedIn(previousLoadedBound); - if (previouslyLoaded) { - break; - } - } - } - if (previouslyLoaded) { - continue; - } + for (const layer of layersToDownload) { if (layer.source.overpassScript !== undefined) { extraScripts.push(layer.source.overpassScript) } else { @@ -140,17 +88,17 @@ export default class OverpassFeatureSource implements FeatureSource { if (!this._isActive.data) { return; } - const self = this - this.updateAsync().then(bboxAndDate => { - if (bboxAndDate === undefined || self._onUpdated === undefined) { + const self = this; + this.updateAsync().then(bboxDate => { + if(bboxDate === undefined || self.onBboxLoaded === undefined){ return; } - const [bbox, date] = bboxAndDate - self._onUpdated(bbox, date); + const [bbox, date, layers] = bboxDate + self.onBboxLoaded(bbox, date, layers) }) } - private async updateAsync(): Promise<[BBox, Date]> { + private async updateAsync(): Promise<[BBox, Date, LayerConfig[]]> { if (this.runningQuery.data) { console.log("Still running a query, not updating"); return undefined; @@ -167,6 +115,26 @@ export default class OverpassFeatureSource implements FeatureSource { return undefined; } const self = this; + + + const layersToDownload = [] + for (const layer of this.state.layoutToUse.layers) { + + if (typeof (layer) === "string") { + throw "A layer was not expanded!" + } + if(this.state.locationControl.data.zoom < layer.minzoom){ + continue; + } + if (layer.doNotDownload) { + continue; + } + if (layer.source.geojsonSource !== undefined) { + // Not our responsibility to download this layer! + continue; + } + layersToDownload.push(layer) + } let data: any = undefined let date: Date = undefined @@ -175,7 +143,8 @@ export default class OverpassFeatureSource implements FeatureSource { do { try { - const overpass = this.GetFilter(overpassUrls[lastUsed]); + + const overpass = this.GetFilter(overpassUrls[lastUsed], layersToDownload); if (overpass === undefined) { return undefined; @@ -208,14 +177,11 @@ export default class OverpassFeatureSource implements FeatureSource { } } while (data === undefined); - const z = Math.floor(this.state.locationControl.data.zoom ?? 0); - self._previousBounds.get(z).push(bounds); self.retries.setData(0); - try { data.features.forEach(feature => SimpleMetaTagger.objectMetaInfo.applyMetaTagsOnFeature(feature, date)); self.features.setData(data.features.map(f => ({feature: f, freshness: date}))); - return [bounds, date]; + return [bounds, date, layersToDownload]; } catch (e) { console.error("Got the overpass response, but could not process it: ", e, e.stack) } finally { diff --git a/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts b/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts index 22e6a7ba8..2894d56b8 100644 --- a/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts +++ b/Logic/FeatureSource/Actors/SaveTileToLocalStorageActor.ts @@ -7,28 +7,30 @@ import {FeatureSourceForLayer} from "../FeatureSource"; export default class SaveTileToLocalStorageActor { public static readonly storageKey: string = "cached-features"; - public static readonly formatVersion : string = "1" - + public static readonly formatVersion: string = "1" + constructor(source: FeatureSourceForLayer, tileIndex: number) { source.features.addCallbackAndRunD(features => { const key = `${SaveTileToLocalStorageActor.storageKey}-${source.layer.layerDef.id}-${tileIndex}` - const now = new Date().getTime() - - if (features.length == 0) { - return; - } + const now = new Date() try { - localStorage.setItem(key, JSON.stringify(features)); - localStorage.setItem(key + "-time", JSON.stringify(now)) - localStorage.setItem(key+"-format", SaveTileToLocalStorageActor.formatVersion) + if (features.length > 0) { + localStorage.setItem(key, JSON.stringify(features)); + } + // We _still_ write the time to know that this tile is empty! + SaveTileToLocalStorageActor.MarkVisited(source.layer.layerDef.id, tileIndex, now) } catch (e) { console.warn("Could not save the features to local storage:", e) } }) - - } + public static MarkVisited(layerId: string, tileId: number, freshness: Date){ + const key = `${SaveTileToLocalStorageActor.storageKey}-${layerId}-${tileId}` + localStorage.setItem(key + "-time", JSON.stringify(freshness.getTime())) + localStorage.setItem(key + "-format", SaveTileToLocalStorageActor.formatVersion) + + } } \ No newline at end of file diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 3db3a039d..5d5b3e166 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -25,6 +25,7 @@ import {BBox} from "../BBox"; import OsmFeatureSource from "./TiledFeatureSource/OsmFeatureSource"; import {OsmConnection} from "../Osm/OsmConnection"; import {Tiles} from "../../Models/TileRange"; +import TileFreshnessCalculator from "./TileFreshnessCalculator"; export default class FeaturePipeline { @@ -38,9 +39,27 @@ export default class FeaturePipeline { public readonly newDataLoadedSignal: UIEventSource = new UIEventSource(undefined) private readonly overpassUpdater: OverpassFeatureSource + private state: { + readonly filteredLayers: UIEventSource, + readonly locationControl: UIEventSource, + readonly selectedElement: UIEventSource, + readonly changes: Changes, + readonly layoutToUse: LayoutConfig, + readonly leafletMap: any, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + readonly overpassMaxZoom: UIEventSource; + readonly osmConnection: OsmConnection + readonly currentBounds: UIEventSource + }; private readonly relationTracker: RelationsTracker private readonly perLayerHierarchy: Map; + private readonly freshnesses = new Map(); + + private readonly oldestAllowedDate: Date = new Date(new Date().getTime() - 60 * 60 * 24 * 30 * 1000); + private readonly osmSourceZoomLevel = 14 + constructor( handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void, state: { @@ -48,7 +67,7 @@ export default class FeaturePipeline { readonly locationControl: UIEventSource, readonly selectedElement: UIEventSource, readonly changes: Changes, - readonly layoutToUse: LayoutConfig, + readonly layoutToUse: LayoutConfig, readonly leafletMap: any, readonly overpassUrl: UIEventSource; readonly overpassTimeout: UIEventSource; @@ -56,59 +75,15 @@ export default class FeaturePipeline { readonly osmConnection: OsmConnection readonly currentBounds: UIEventSource }) { + this.state = state; const self = this - - /** - * Maps tileid onto last download moment - */ - const tileFreshnesses = new UIEventSource>(new Map()) - const osmSourceZoomLevel = 14 + // milliseconds const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12)) this.relationTracker = new RelationsTracker() + const neededTilesFromOsm = this.getNeededTilesFromOsm() - console.log("Tilefreshnesses are", tileFreshnesses.data) - const oldestAllowedDate = new Date(new Date().getTime() - (60 * 60 * 24 * 30 * 1000)); - const neededTilesFromOsm = state.currentBounds.map(bbox => { - if (bbox === undefined) { - return - } - const range = bbox.containingTileRange(osmSourceZoomLevel) - const tileIndexes = [] - if (range.total > 100) { - // Too much tiles! - return [] - } - Tiles.MapRange(range, (x, y) => { - const i = Tiles.tile_index(osmSourceZoomLevel, x, y); - if (tileFreshnesses.data.get(i) > oldestAllowedDate) { - console.debug("Skipping tile", osmSourceZoomLevel, x, y, "as a decently fresh one is available") - // The cached tiles contain decently fresh data - return; - } - tileIndexes.push(i) - }) - return tileIndexes - }, [tileFreshnesses]) - - const updater = new OverpassFeatureSource(state, - { - relationTracker: this.relationTracker, - isActive: useOsmApi.map(b => !b), - onUpdated: (bbox, freshness) => { - // This callback contains metadata of the overpass call - const range = bbox.containingTileRange(osmSourceZoomLevel) - Tiles.MapRange(range, (x, y) => { - tileFreshnesses.data.set(Tiles.tile_index(osmSourceZoomLevel, x, y), freshness) - }) - tileFreshnesses.ping(); - - } - }); - - this.overpassUpdater = updater; - this.timeout = updater.timeout this.sufficientlyZoomed = state.locationControl.map(location => { if (location?.zoom === undefined) { @@ -119,12 +94,6 @@ export default class FeaturePipeline { } ); - this.timeout = updater.timeout - - - // Register everything in the state' 'AllElements' - new RegisteringAllFromFeatureSourceActor(updater) - const perLayerHierarchy = new Map() this.perLayerHierarchy = perLayerHierarchy @@ -140,40 +109,32 @@ export default class FeaturePipeline { handleFeatureSource(srcFiltered) self.somethingLoaded.setData(true) + self.freshnesses.get(src.layer.layerDef.id).addTileLoad(src.tileIndex, new Date()) }; - function addToHierarchy(src: FeatureSource & Tiled, layerId: string) { - perLayerHierarchy.get(layerId).registerTile(src) - } - for (const filteredLayer of state.filteredLayers.data) { - const hierarchy = new TileHierarchyMerger(filteredLayer, (tile, _) => patchedHandleFeatureSource(tile)) const id = filteredLayer.layerDef.id - perLayerHierarchy.set(id, hierarchy) const source = filteredLayer.layerDef.source + const hierarchy = new TileHierarchyMerger(filteredLayer, (tile, _) => patchedHandleFeatureSource(tile)) + perLayerHierarchy.set(id, hierarchy) + + this.freshnesses.set(id, new TileFreshnessCalculator()) + if (source.geojsonSource === undefined) { // This is an OSM layer // We load the cached values and register them // Getting data from upstream happens a bit lower - const localStorage = new TiledFromLocalStorageSource(filteredLayer, + new TiledFromLocalStorageSource(filteredLayer, (src) => { new RegisteringAllFromFeatureSourceActor(src) hierarchy.registerTile(src); src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src)) }, state) - localStorage.tileFreshness.forEach((value, key) => { - if (tileFreshnesses.data.has(key)) { - const previous = tileFreshnesses.data.get(key) - if (value < previous) { - tileFreshnesses.data.set(key, value) - } - } else { - tileFreshnesses.data.set(key, value) - } - tileFreshnesses.ping() + TiledFromLocalStorageSource.GetFreshnesses(id).forEach((value, key) => { + self.freshnesses.get(id).addTileLoad(key, value) }) continue @@ -189,7 +150,7 @@ export default class FeaturePipeline { dontEnforceMinZoom: true, registerTile: (tile) => { new RegisteringAllFromFeatureSourceActor(tile) - addToHierarchy(tile, id) + perLayerHierarchy.get(id).registerTile(tile) tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) } }) @@ -198,7 +159,7 @@ export default class FeaturePipeline { filteredLayer, tile => { new RegisteringAllFromFeatureSourceActor(tile) - addToHierarchy(tile, id) + perLayerHierarchy.get(id).registerTile(tile) tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) }, state @@ -213,14 +174,22 @@ export default class FeaturePipeline { handleTile: tile => { new RegisteringAllFromFeatureSourceActor(tile) new SaveTileToLocalStorageActor(tile, tile.tileIndex) - addToHierarchy(tile, tile.layer.layerDef.id), - tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) }, - state: state + state: state, + markTileVisited: (tileId) => + state.filteredLayers.data.forEach(flayer => { + SaveTileToLocalStorageActor.MarkVisited(flayer.layerDef.id, tileId, new Date()) + }) }) + const updater = this.initOverpassUpdater(state, useOsmApi) + this.overpassUpdater = updater; + this.timeout = updater.timeout + // Actually load data from the overpass source new PerLayerFeatureSourceSplitter(state.filteredLayers, (source) => TiledFeatureSource.createHierarchy(source, { @@ -232,7 +201,7 @@ export default class FeaturePipeline { registerTile: (tile) => { // We save the tile data for the given layer to local storage new SaveTileToLocalStorageActor(tile, tile.tileIndex) - addToHierarchy(new RememberingSource(tile), source.layer.layerDef.id); + perLayerHierarchy.get(source.layer.layerDef.id).registerTile(new RememberingSource(tile)) tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) } @@ -247,7 +216,7 @@ export default class FeaturePipeline { new PerLayerFeatureSourceSplitter(state.filteredLayers, (perLayer) => { // We don't bother to split them over tiles as it'll contain little features by default, so we simply add them like this - addToHierarchy(perLayer, perLayer.layer.layerDef.id) + perLayerHierarchy.get(perLayer.layer.layerDef.id).registerTile(perLayer) // AT last, we always apply the metatags whenever possible perLayer.features.addCallbackAndRunD(_ => self.applyMetaTags(perLayer)) perLayer.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(perLayer)) @@ -270,6 +239,106 @@ export default class FeaturePipeline { } + private freshnessForVisibleLayers(z: number, x: number, y: number): Date { + let oldestDate = undefined; + for (const flayer of this.state.filteredLayers.data) { + if (!flayer.isDisplayed.data) { + continue + } + if (this.state.locationControl.data.zoom < flayer.layerDef.minzoom) { + continue; + } + const freshness = this.freshnesses.get(flayer.layerDef.id).freshnessFor(z, x, y) + if (freshness === undefined) { + // SOmething is undefined --> we return undefined as we have to download + return undefined + } + if (oldestDate === undefined || oldestDate > freshness) { + oldestDate = freshness + } + } + return oldestDate + } + + private getNeededTilesFromOsm(): UIEventSource { + const self = this + return this.state.currentBounds.map(bbox => { + if (bbox === undefined) { + return + } + const osmSourceZoomLevel = self.osmSourceZoomLevel + const range = bbox.containingTileRange(osmSourceZoomLevel) + const tileIndexes = [] + if (range.total > 100) { + // Too much tiles! + return [] + } + Tiles.MapRange(range, (x, y) => { + const i = Tiles.tile_index(osmSourceZoomLevel, x, y); + const oldestDate = self.freshnessForVisibleLayers(osmSourceZoomLevel, x, y) + if (oldestDate !== undefined && oldestDate > this.oldestAllowedDate) { + console.debug("Skipping tile", osmSourceZoomLevel, x, y, "as a decently fresh one is available") + // The cached tiles contain decently fresh data + return; + } + tileIndexes.push(i) + }) + return tileIndexes + }) + } + + private initOverpassUpdater(state: { + layoutToUse: LayoutConfig, + currentBounds: UIEventSource, + locationControl: UIEventSource, + readonly overpassUrl: UIEventSource; + readonly overpassTimeout: UIEventSource; + readonly overpassMaxZoom: UIEventSource, + }, useOsmApi: UIEventSource): OverpassFeatureSource { + const minzoom = Math.min(...state.layoutToUse.layers.map(layer => layer.minzoom)) + const allUpToDateAndZoomSufficient = state.currentBounds.map(bbox => { + if (bbox === undefined) { + return true + } + let zoom = state.locationControl.data.zoom + if (zoom < minzoom) { + return true; + } + if (zoom > 16) { + zoom = 16 + } + if (zoom < 8) { + zoom = zoom + 2 + } + const range = bbox.containingTileRange(zoom) + const self = this; + const allFreshnesses = Tiles.MapRange(range, (x, y) => self.freshnessForVisibleLayers(zoom, x, y)) + return !allFreshnesses.some(freshness => freshness === undefined || freshness < this.oldestAllowedDate) + + }, [state.locationControl]) + + allUpToDateAndZoomSufficient.addCallbackAndRunD(allUpToDate => console.log("All up to data is: ", allUpToDate)) + const self = this; + const updater = new OverpassFeatureSource(state, + { + relationTracker: this.relationTracker, + isActive: useOsmApi.map(b => !b && !allUpToDateAndZoomSufficient.data, [allUpToDateAndZoomSufficient]), + onBboxLoaded: ((bbox, date, downloadedLayers) => { + Tiles.MapRange(bbox.containingTileRange(self.osmSourceZoomLevel), (x, y) => { + downloadedLayers.forEach(layer => { + SaveTileToLocalStorageActor.MarkVisited(layer.id, Tiles.tile_index(this.osmSourceZoomLevel, x, y), date) + }) + }) + + }) + }); + + + // Register everything in the state' 'AllElements' + new RegisteringAllFromFeatureSourceActor(updater) + return updater; + } + private applyMetaTags(src: FeatureSourceForLayer) { const self = this console.debug("Applying metatagging onto ", src.name) diff --git a/Logic/FeatureSource/TileFreshnessCalculator.ts b/Logic/FeatureSource/TileFreshnessCalculator.ts new file mode 100644 index 000000000..6b397f540 --- /dev/null +++ b/Logic/FeatureSource/TileFreshnessCalculator.ts @@ -0,0 +1,72 @@ +import {Tiles} from "../../Models/TileRange"; + +export default class TileFreshnessCalculator { + + /** + * All the freshnesses per tile index + * @private + */ + private readonly freshnesses = new Map(); + + /** + * Marks that some data got loaded for this layer + * @param tileId + * @param freshness + */ + public addTileLoad(tileId: number, freshness: Date){ + const existingFreshness = this.freshnessFor(...Tiles.tile_from_index(tileId)) + if(existingFreshness >= freshness){ + return; + } + this.freshnesses.set(tileId, freshness) + + + // Do we have freshness for the neighbouring tiles? If so, we can mark the tile above as loaded too! + let [z, x, y] = Tiles.tile_from_index(tileId) + if(z === 0){ + return; + } + x = x - (x % 2) // Make the tiles always even + y = y - (y % 2) + + const ul = this.freshnessFor(z, x, y)?.getTime() + if(ul === undefined){ + return + } + const ur = this.freshnessFor(z, x + 1, y)?.getTime() + if(ur === undefined){ + return + } + const ll = this.freshnessFor(z, x, y + 1)?.getTime() + if(ll === undefined){ + return + } + const lr = this.freshnessFor(z, x + 1, y + 1)?.getTime() + if(lr === undefined){ + return + } + + const leastFresh = Math.min(ul, ur, ll, lr) + const date = new Date() + date.setTime(leastFresh) + this.addTileLoad( + Tiles.tile_index(z - 1, Math.floor(x / 2), Math.floor(y / 2)), + date + ) + + } + + public freshnessFor(z: number, x: number, y:number): Date { + if(z < 0){ + return undefined + } + const tileId = Tiles.tile_index(z, x, y) + if(this.freshnesses.has(tileId)) { + return this.freshnesses.get(tileId) + } + // recurse up + return this.freshnessFor(z - 1, Math.floor(x /2), Math.floor(y / 2)) + + } + +} \ No newline at end of file diff --git a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts index a7485facf..5641d5b98 100644 --- a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts @@ -22,7 +22,8 @@ export default class OsmFeatureSource { neededTiles: UIEventSource, state: { readonly osmConnection: OsmConnection; - }; + }, + markTileVisited?: (tileId: number) => void }; private readonly downloadedTiles = new Set() @@ -33,7 +34,8 @@ export default class OsmFeatureSource { state: { readonly filteredLayers: UIEventSource; readonly osmConnection: OsmConnection; - }; + }, + markTileVisited?: (tileId: number) => void }) { this.options = options; this._backend = options.state.osmConnection._oauth_config.url; @@ -84,13 +86,17 @@ export default class OsmFeatureSource { flatProperties: true }); console.log("Tile geojson:", z, x, y, "is", geojson) + const index = Tiles.tile_index(z, x, y); new PerLayerFeatureSourceSplitter(this.filteredLayers, this.handleTile, new StaticFeatureSource(geojson.features, false), { - tileIndex: Tiles.tile_index(z, x, y) + tileIndex:index } ); + if(this.options.markTileVisited){ + this.options.markTileVisited(index) + } } catch (e) { console.error("Weird error: ", e) } diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts index 844754854..6fd3dae65 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TileHierarchyMerger.ts @@ -2,7 +2,6 @@ import TileHierarchy from "./TileHierarchy"; import {UIEventSource} from "../../UIEventSource"; import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource"; import FilteredLayer from "../../../Models/FilteredLayer"; -import {Utils} from "../../../Utils"; import FeatureSourceMerger from "../Sources/FeatureSourceMerger"; import {Tiles} from "../../../Models/TileRange"; import {BBox} from "../../BBox"; diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts index 016537968..6f815f48d 100644 --- a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts @@ -3,15 +3,29 @@ import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; import {UIEventSource} from "../../UIEventSource"; import Loc from "../../../Models/Loc"; import TileHierarchy from "./TileHierarchy"; -import {Utils} from "../../../Utils"; import SaveTileToLocalStorageActor from "../Actors/SaveTileToLocalStorageActor"; import {Tiles} from "../../../Models/TileRange"; import {BBox} from "../../BBox"; export default class TiledFromLocalStorageSource implements TileHierarchy { public loadedTiles: Map = new Map(); -public tileFreshness : Map = new Map() - + + public static GetFreshnesses(layerId: string): Map { + const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layerId + "-" + const freshnesses = new Map() + for (const key of Object.keys(localStorage)) { + if(!(key.startsWith(prefix) && key.endsWith("-time"))){ + continue + } + const index = Number(key.substring(prefix.length, key.length - "-time".length)) + const time = Number(localStorage.getItem(key)) + const freshness = new Date() + freshness.setTime(time) + freshnesses.set(index, freshness) + } + return freshnesses + } + constructor(layer: FilteredLayer, handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void, state: { @@ -33,21 +47,17 @@ public tileFreshness : Map = new Map() console.debug("Layer", layer.layerDef.id, "has following tiles in available in localstorage", indexes.map(i => Tiles.tile_from_index(i).join("/")).join(", ")) for (const index of indexes) { - - const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" +index; - const version = localStorage.getItem(prefix+"-format") - if(version === undefined || version !== SaveTileToLocalStorageActor.formatVersion){ + + const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.layerDef.id + "-" + index; + const version = localStorage.getItem(prefix + "-format") + if (version === undefined || version !== SaveTileToLocalStorageActor.formatVersion) { // Invalid version! Remove this tile from local storage localStorage.removeItem(prefix) + localStorage.removeItem(prefix+"-time") + localStorage.removeItem(prefix+"-format") undefinedTiles.add(index) console.log("Dropped old format tile", prefix) - continue } - - const data = Number(localStorage.getItem(prefix+"-time")) - const freshness = new Date() - freshness.setTime(data) - this.tileFreshness.set(index, freshness) } const zLevels = indexes.map(i => i % 100) @@ -91,8 +101,6 @@ public tileFreshness : Map = new Map() } , [layer.isDisplayed, state.leafletMap]).stabilized(50); - neededTiles.addCallbackAndRun(t => console.debug("Tiles to load from localstorage:", t)) - neededTiles.addCallbackAndRunD(neededIndexes => { for (const neededIndex of neededIndexes) { // We load the features from localStorage diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 4b047fc7d..360bb54e5 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -97,7 +97,7 @@ export default class SimpleMetaTagger { continue; } for (const unit of units) { - if(unit === undefined){ + if (unit === undefined) { continue } if (unit.appliesToKeys === undefined) { @@ -108,7 +108,12 @@ export default class SimpleMetaTagger { continue; } const value = feature.properties[key] - const [, denomination] = unit.findDenomination(value) + const denom = unit.findDenomination(value) + if (denom === undefined) { + // no valid value found + break; + } + const [, denomination] = denom; let canonical = denomination?.canonicalValue(value) ?? undefined; if (canonical === value) { break; diff --git a/Models/Constants.ts b/Models/Constants.ts index 4e588a057..855f9dd2b 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.0-rc1"; + public static vNumber = "0.10.0-rc2"; public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" diff --git a/Models/TileRange.ts b/Models/TileRange.ts index b61f192a0..da30e498c 100644 --- a/Models/TileRange.ts +++ b/Models/TileRange.ts @@ -107,4 +107,5 @@ export class Tiles { } } + } \ No newline at end of file diff --git a/test/TestAll.ts b/test/TestAll.ts index 0efa98dd4..6123681fb 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -9,6 +9,7 @@ import UnitsSpec from "./Units.spec"; import RelationSplitHandlerSpec from "./RelationSplitHandler.spec"; import SplitActionSpec from "./SplitAction.spec"; import {Utils} from "../Utils"; +import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec"; ScriptUtils.fixUtils() @@ -21,7 +22,8 @@ const allTests = [ new UtilsSpec(), new UnitsSpec(), new RelationSplitHandlerSpec(), - new SplitActionSpec() + new SplitActionSpec(), + new TileFreshnessCalculatorSpec() ] Utils.externalDownloadFunction = async (url) => { diff --git a/test/TileFreshnessCalculator.spec.ts b/test/TileFreshnessCalculator.spec.ts new file mode 100644 index 000000000..6305b9a5d --- /dev/null +++ b/test/TileFreshnessCalculator.spec.ts @@ -0,0 +1,31 @@ +import T from "./TestHelper"; +import TileFreshnessCalculator from "../Logic/FeatureSource/TileFreshnessCalculator"; +import {Tiles} from "../Models/TileRange"; +import {equal} from "assert"; + +export default class TileFreshnessCalculatorSpec extends T { + + constructor() { + super("TileFreshnessCalculatorSpec", [ + [ + "TileFresnessTests", + () => { + const calc = new TileFreshnessCalculator(); + // 19/266407/175535 + const date = new Date() + date.setTime(42) + calc.addTileLoad(Tiles.tile_index(19, 266406, 175534), date) + equal(42, calc.freshnessFor(19, 266406, 175534).getTime()) + equal(42, calc.freshnessFor(20, 266406 * 2, 175534 * 2 + 1).getTime()) + equal(undefined, calc.freshnessFor(19, 266406, 175535)) + equal(undefined, calc.freshnessFor(18, 266406 / 2, 175534 / 2)) + calc.addTileLoad(Tiles.tile_index(19, 266406, 175534+1), date) + calc.addTileLoad(Tiles.tile_index(19, 266406+1, 175534), date) + calc.addTileLoad(Tiles.tile_index(19, 266406+1, 175534+1), date) + equal(42, calc.freshnessFor(18, 266406 / 2, 175534 / 2).getTime()) + } + ] + ]) + } + +} \ No newline at end of file From 9ba27778717268e29f30d1c63e9c1092cf000522 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 30 Sep 2021 04:25:13 +0200 Subject: [PATCH 060/110] Add a few more questions to the toilet theme --- .../layers/cluster_style/cluster_style.json | 68 +++++++++---------- assets/layers/parking/parking.json | 2 +- assets/layers/toilet/toilet.json | 46 +++++++++++++ assets/layers/watermill/watermill.json | 2 +- assets/tagRenderings/questions.json | 21 ++++-- assets/themes/natuurpunt/natuurpunt.json | 2 +- langs/layers/en.json | 31 +++++++++ langs/layers/nl.json | 31 +++++++++ langs/shared-questions/pt.json | 38 +++++------ 9 files changed, 178 insertions(+), 63 deletions(-) diff --git a/assets/layers/cluster_style/cluster_style.json b/assets/layers/cluster_style/cluster_style.json index ea6760612..d6b68d113 100644 --- a/assets/layers/cluster_style/cluster_style.json +++ b/assets/layers/cluster_style/cluster_style.json @@ -1,36 +1,36 @@ { - "id": "cluster_style", - "description": "The style for the clustering in all themes.", - "source": { - "osmTags": "tileId~*" - }, - "color": { - "render": "#3c3", - "mappings": [ - { - "if": "count>200", - "then": "#f33" - }, - { - "if": "count>100", - "then": "#c93" - }, - { - "if": "count>50", - "then": "#cc3" - } - ] - }, - "width": { - "render": "1" - }, - "label": { - "render": "
{count}
", - "mappings": [ - { - "if": "count>99", - "then": "
>99
" - } - ] - } + "id": "cluster_style", + "description": "The style for the clustering in all themes.", + "source": { + "osmTags": "tileId~*" + }, + "color": { + "render": "#3c3", + "mappings": [ + { + "if": "count>200", + "then": "#f33" + }, + { + "if": "count>100", + "then": "#c93" + }, + { + "if": "count>50", + "then": "#cc3" + } + ] + }, + "width": { + "render": "1" + }, + "label": { + "render": "
{count}
", + "mappings": [ + { + "if": "count>99", + "then": "
>99
" + } + ] + } } \ No newline at end of file diff --git a/assets/layers/parking/parking.json b/assets/layers/parking/parking.json index 888ee00a9..42f09f53e 100644 --- a/assets/layers/parking/parking.json +++ b/assets/layers/parking/parking.json @@ -82,4 +82,4 @@ } } ] -} +} \ No newline at end of file diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index 68bc21fe6..8b10b35ee 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -407,6 +407,52 @@ } ], "id": "toilet-changing_table:location" + }, + { + "id": "toilet-handwashing", + "question": { + "en": "Do these toilets have a sink to wash your hands?", + "nl": "Hebben deze toiletten een lavabo om de handen te wassen?" + }, + "mappings": [ + { + "if": "toilets:handwashing=yes", + "then": { + "en": "This toilets have a sink to wash your hands", + "nl": "Deze toiletten hebben een lavabo waar men de handen kan wassen" + } + }, + { + "if": "toilets:handwashing=no", + "then": { + "en": "This toilets don't have a sink to wash your hands", + "nl": "Deze toiletten hebben geen lavabo waar men de handen kan wassen" + } + } + ] + }, + { + "id": "toilet-has-paper", + "question": { + "en": "Does one have to bring their own toilet paper to this toilet?", + "nl": "Moet je je eigen toiletpappier meenemen naar deze toilet?" + }, + "mappings": [ + { + "if": "toilets:paper_supplied=yes", + "then": { + "en": "Toilet paper is equipped with toilet paper", + "nl": "Deze toilet is voorzien van toiletpapier" + } + }, + { + "if": "toilets:paper_supplied=no", + "then": { + "en": "You have to bring your own toilet paper to this toilet", + "nl": "Je moet je eigen toiletpapier meebrengen naar deze toilet" + } + } + ] } ], "filter": [ diff --git a/assets/layers/watermill/watermill.json b/assets/layers/watermill/watermill.json index 54b5f056c..6f8dcb65f 100644 --- a/assets/layers/watermill/watermill.json +++ b/assets/layers/watermill/watermill.json @@ -170,4 +170,4 @@ "color": { "render": "#FFC0CB" } -} +} \ No newline at end of file diff --git a/assets/tagRenderings/questions.json b/assets/tagRenderings/questions.json index 30aab5b27..fb2e939bd 100644 --- a/assets/tagRenderings/questions.json +++ b/assets/tagRenderings/questions.json @@ -56,7 +56,8 @@ "de": "Was ist die Mail-Adresse von {name}?", "pt_BR": "Qual o endereço de e-mail de {name}?", "pl": "Jaki jest adres e-mail do {name}?", - "sv": "Vad är e-postadressen till {name}?" + "sv": "Vad är e-postadressen till {name}?", + "pt": "Qual é o endereço de e-mail de {name}?" }, "freeform": { "key": "email", @@ -184,7 +185,8 @@ "it": "C'è ancora qualche informazione importante che non è stato possibile fornire nelle domande precedenti? Aggiungila qui.
Non ripetere informazioni già fornite", "de": "Gibt es noch etwas, das die vorhergehenden Fragen nicht abgedeckt haben? Hier wäre Platz dafür.
Bitte keine bereits erhobenen Informationen.", "pl": "Czy jest jeszcze coś istotnego, czego nie mogłeś podać w poprzednich pytaniach? Dodaj to tutaj.
Nie powtarzaj już podanych faktów", - "pt_BR": "Ainda há algo de relevante que não pôde dar nas perguntas anteriores? Adicione aqui.
Não repita fatos já declarados" + "pt_BR": "Ainda há algo de relevante que não pôde dar nas perguntas anteriores? Adicione aqui.
Não repita fatos já declarados", + "pt": "Ainda há algo de relevante que não tenha podido dar nas perguntas anteriores? Adicione-o aqui.
Não repita factos já declarados" }, "render": "{description}", "freeform": { @@ -266,7 +268,8 @@ "fr": "À quel étage se situe l’élément ?", "pl": "Na jakim poziomie znajduje się ta funkcja?", "pt_BR": "Em que nível esse recurso está localizado?", - "ru": "На каком этаже находится этот объект?" + "ru": "На каком этаже находится этот объект?", + "pt": "Em que nível se encontra este elemento?" }, "render": { "en": "Located on the {level}th floor", @@ -294,7 +297,8 @@ "zh_Hant": "位於地下", "fr": "En sous-sol", "pl": "Znajduje się pod ziemią", - "sv": "Ligger under jorden" + "sv": "Ligger under jorden", + "pt": "Está no subsolo" }, "hideInAnswer": true }, @@ -309,7 +313,8 @@ "zh_Hant": "位於 1 樓", "fr": "Rez-de-chaussée", "pl": "Znajduje się na parterze", - "sv": "Ligger på bottenvåningen" + "sv": "Ligger på bottenvåningen", + "pt": "Está ao nível do rés-do-chão" } }, { @@ -324,7 +329,8 @@ "zh_Hant": "位於 1 樓", "fr": "Rez-de-chaussée", "pl": "Znajduje się na parterze", - "sv": "Ligger på bottenvåningen" + "sv": "Ligger på bottenvåningen", + "pt": "Está ao nível do rés-do-chão" } }, { @@ -338,7 +344,8 @@ "zh_Hant": "位於 2 樓", "fr": "Premier étage", "pl": "Znajduje się na pierwszym piętrze", - "sv": "Ligger på första våningen" + "sv": "Ligger på första våningen", + "pt": "Está no primeiro andar" } } ] diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index c5345a55f..7ebcab3b5 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -262,4 +262,4 @@ } } ] -} +} \ No newline at end of file diff --git a/langs/layers/en.json b/langs/layers/en.json index a3a428462..c107bdc35 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -2883,6 +2883,15 @@ }, "public_bookcase": { "description": "A streetside cabinet with books, accessible to anyone", + "filter": { + "2": { + "options": { + "0": { + "question": "Indoor or outdoor" + } + } + } + }, "name": "Bookcases", "presets": { "0": { @@ -3313,6 +3322,28 @@ "question": "How much does one have to pay for these toilets?", "render": "The fee is {charge}" }, + "toilet-handwashing": { + "mappings": { + "0": { + "then": "This toilets have a sink to wash your hands" + }, + "1": { + "then": "This toilets don't have a sink to wash your hands" + } + }, + "question": "Do these toilets have a sink to wash your hands?" + }, + "toilet-has-paper": { + "mappings": { + "0": { + "then": "Toilet paper is equipped with toilet paper" + }, + "1": { + "then": "You have to bring your own toilet paper to this toilet" + } + }, + "question": "Does one have to bring their own toilet paper to this toilet?" + }, "toilets-changing-table": { "mappings": { "0": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index d253c3161..b7a918c9c 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3099,6 +3099,15 @@ }, "public_bookcase": { "description": "Een straatkastje met boeken voor iedereen", + "filter": { + "2": { + "options": { + "0": { + "question": "Binnen of buiten" + } + } + } + }, "name": "Boekenruilkastjes", "presets": { "0": { @@ -3547,6 +3556,28 @@ "question": "Hoeveel moet men betalen om deze toiletten te gebruiken?", "render": "De toiletten gebruiken kost {charge}" }, + "toilet-handwashing": { + "mappings": { + "0": { + "then": "Deze toiletten hebben een lavabo waar men de handen kan wassen" + }, + "1": { + "then": "Deze toiletten hebben geen lavabo waar men de handen kan wassen" + } + }, + "question": "Hebben deze toiletten een lavabo om de handen te wassen?" + }, + "toilet-has-paper": { + "mappings": { + "0": { + "then": "Deze toilet is voorzien van toiletpapier" + }, + "1": { + "then": "Je moet je eigen toiletpapier meebrengen naar deze toilet" + } + }, + "question": "Moet je je eigen toiletpappier meenemen naar deze toilet?" + }, "toilets-changing-table": { "mappings": { "0": { diff --git a/langs/shared-questions/pt.json b/langs/shared-questions/pt.json index 60330bb63..4ba738f83 100644 --- a/langs/shared-questions/pt.json +++ b/langs/shared-questions/pt.json @@ -1,27 +1,27 @@ { "undefined": { - "level": { - "question": "Em que nível se encontra este elemento?", - "mappings": { - "3": { - "then": "Está no primeiro andar" - }, - "2": { - "then": "Está ao nível do rés-do-chão" - }, - "1": { - "then": "Está ao nível do rés-do-chão" - }, - "0": { - "then": "Está no subsolo" - } - } + "description": { + "question": "Ainda há algo de relevante que não tenha podido dar nas perguntas anteriores? Adicione-o aqui.
Não repita factos já declarados" }, "email": { "question": "Qual é o endereço de e-mail de {name}?" }, - "description": { - "question": "Ainda há algo de relevante que não tenha podido dar nas perguntas anteriores? Adicione-o aqui.
Não repita factos já declarados" + "level": { + "mappings": { + "0": { + "then": "Está no subsolo" + }, + "1": { + "then": "Está ao nível do rés-do-chão" + }, + "2": { + "then": "Está ao nível do rés-do-chão" + }, + "3": { + "then": "Está no primeiro andar" + } + }, + "question": "Em que nível se encontra este elemento?" } } -} +} \ No newline at end of file From 8cdc0dba61be5695bd2f383f612db3af907dd150 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 30 Sep 2021 18:50:08 +0200 Subject: [PATCH 061/110] Add USB as socket to charging stations, some styling tweaks --- UI/BigComponents/FilterView.ts | 6 +- assets/layers/charging_station/README.md | 19 + .../charging_station/charging_station.json | 669 +++++++++++++----- assets/layers/charging_station/csvToJson.ts | 25 +- .../layers/charging_station/license_info.json | 10 + assets/layers/charging_station/types.csv | 29 +- assets/layers/charging_station/usb_port.svg | 74 ++ 7 files changed, 616 insertions(+), 216 deletions(-) create mode 100644 assets/layers/charging_station/README.md create mode 100644 assets/layers/charging_station/usb_port.svg diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts index 249b3dacb..ffcdbc3c9 100644 --- a/UI/BigComponents/FilterView.ts +++ b/UI/BigComponents/FilterView.ts @@ -155,8 +155,8 @@ export default class FilterView extends VariableUiElement { const iconUnselected = Svg.checkbox_empty_svg().SetClass("block mr-2"); const toggle = new Toggle( - new Combine([icon, option.question.Clone()]).SetClass("flex"), - new Combine([iconUnselected, option.question.Clone()]).SetClass("flex") + new Combine([icon, option.question.Clone().SetClass("block")]).SetClass("flex"), + new Combine([iconUnselected, option.question.Clone().SetClass("block")]).SetClass("flex") ) .ToggleOnClick() .SetClass("block m-1") @@ -178,7 +178,7 @@ export default class FilterView extends VariableUiElement { const radio = new RadioButton( options.map( (option, i) => - new FixedInputElement(option.question.Clone(), i) + new FixedInputElement(option.question.Clone().SetClass("block"), i) ), { dontStyle: true diff --git a/assets/layers/charging_station/README.md b/assets/layers/charging_station/README.md new file mode 100644 index 000000000..445b81c1e --- /dev/null +++ b/assets/layers/charging_station/README.md @@ -0,0 +1,19 @@ +The charging station theme +========================== + +As you might have noticed, the charging station theme is complicated and large. + +There are a ton of repititive questions. Luckily, we can generate those. + +If you want to add a missing socket type, then: + +- Add all the properties in 'types.csv' +- Add an icon. (Note: icons are way better as pictures as they are perceived more abstractly) +- Update license_info.json with the copyright info of the new icon. Note that we strive to have Creative-commons icons only (though there are exceptions) + +AT this point, most of the work should be done; feel free to send a PR. If you would like to test it locally first (which is recommended) and have a working dev environment, then run: + +- Run 'ts-node csvToJson.ts' which will generate a new charging_station.json based on the protojson +- Run`npm run query:licenses` to get an interactive program to add the license of your artwork, followed by `npm run generate:licenses` +- Run `npm run generate:layeroverview` to generate the layer files +- Run `npm run start` to run the instance \ No newline at end of file diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index f3caa2321..5a8385357 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -157,8 +157,8 @@ "if": "socket:schuko=1", "ifnot": "socket:schuko=", "then": { - "en": " Schuko wall plug without ground pin (CEE7/4 type F)", - "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F) Schuko wall plug without ground pin (CEE7/4 type F)", - "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F) European wall plug with ground pin (CEE7/4 type E)", - "nl": " Europese stekker met aardingspin (CEE7/4 type E)" + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E) European wall plug with ground pin (CEE7/4 type E)", - "nl": " Europese stekker met aardingspin (CEE7/4 type E)" + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E) Chademo", - "nl": " " + "en": "
Chademo
", + "nl": "
Chademo Chademo", - "nl": " " + "en": "
Chademo
", + "nl": "
Chademo Type 1 with cable (J1772)", - "nl": " Type 1 met kabel (J1772)" + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772) Type 1 with cable (J1772)", - "nl": " Type 1 met kabel (J1772)" + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772) Type 1 without cable (J1772)", - "nl": " Type 1 zonder kabel (J1772)" + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772) Type 1 without cable (J1772)", - "nl": " Type 1 zonder kabel (J1772)" + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772) Type 1 CCS (aka Type 1 Combo)", - "nl": " " + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo) Type 1 CCS (aka Type 1 Combo)", - "nl": " " + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo) Tesla Supercharger", - "nl": " " + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger Tesla Supercharger", - "nl": " " + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger Type 2 (mennekes)", - "nl": " " + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes) Type 2 (mennekes)", - "nl": " " + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes) Type 2 CCS (mennekes)", - "nl": " " + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes) Type 2 CCS (mennekes)", - "nl": " " + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes) Type 2 with cable (mennekes)", - "nl": " Type 2 met kabel (J1772)" + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772) Type 2 with cable (mennekes)", - "nl": " Type 2 met kabel (J1772)" + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772) Tesla Supercharger CCS (a branded type2_css)", - "nl": " " + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo) Tesla Supercharger CCS (a branded type2_css)", - "nl": " " + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo) Tesla Supercharger (destination)", - "nl": " " + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination) Tesla Supercharger (destination)", - "nl": " " + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination) Tesla supercharger (destination (A Type 2 with cable branded as tesla)", - "nl": " " + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) Tesla supercharger (destination (A Type 2 with cable branded as tesla)", - "nl": " " + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te ladenChademo are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" + "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" }, "render": { "en": "There are Chademo plugs of type Chademo available here", - "nl": "Hier zijn stekkers van het type " + "nl": "Hier zijn Chademo stekkers van het type Chademo" }, "freeform": { "key": "socket:chademo", @@ -704,11 +865,11 @@ "id": "voltage-2", "question": { "en": "What voltage do the plugs with Chademo offer?", - "nl": "Welke spanning levert de stekker van type " + "nl": "Welke spanning levert de stekker van type Chademo " }, "render": { "en": "Chademo outputs {socket:chademo:voltage} volt", - "nl": " heeft een spanning van {socket:chademo:voltage} volt" + "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" }, "freeform": { "key": "socket:chademo:voltage", @@ -719,7 +880,7 @@ "if": "socket:socket:chademo:voltage=500 V", "then": { "en": "Chademo outputs 500 volt", - "nl": " heeft een spanning van 500 volt" + "nl": "Chademo heeft een spanning van 500 volt" } } ], @@ -734,11 +895,11 @@ "id": "current-2", "question": { "en": "What current do the plugs with Chademo offer?", - "nl": "Welke stroom levert de stekker van type ?" + "nl": "Welke stroom levert de stekker van type Chademo ?" }, "render": { "en": "Chademo outputs at most {socket:chademo:current}A", - "nl": " levert een stroom van maximaal {socket:chademo:current}A" + "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" }, "freeform": { "key": "socket:chademo:current", @@ -749,7 +910,7 @@ "if": "socket:socket:chademo:current=120 A", "then": { "en": "Chademo outputs at most 120 A", - "nl": " levert een stroom van maximaal 120 A" + "nl": "Chademo levert een stroom van maximaal 120 A" } } ], @@ -764,11 +925,11 @@ "id": "power-output-2", "question": { "en": "What power output does a single plug of type Chademo offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" + "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" }, "render": { "en": "Chademo outputs at most {socket:chademo:output}", - "nl": " levert een vermogen van maximaal {socket:chademo:output}" + "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" }, "freeform": { "key": "socket:chademo:output", @@ -779,7 +940,7 @@ "if": "socket:socket:chademo:output=50 kw", "then": { "en": "Chademo outputs at most 50 kw", - "nl": " levert een vermogen van maximaal 50 kw" + "nl": "Chademo levert een vermogen van maximaal 50 kw" } } ], @@ -1058,11 +1219,11 @@ "id": "plugs-5", "question": { "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" + "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" }, "render": { "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", - "nl": "Hier zijn stekkers van het type " + "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" }, "freeform": { "key": "socket:type1_combo", @@ -1079,11 +1240,11 @@ "id": "voltage-5", "question": { "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke spanning levert de stekker van type " + "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " }, "render": { "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", - "nl": " heeft een spanning van {socket:type1_combo:voltage} volt" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" }, "freeform": { "key": "socket:type1_combo:voltage", @@ -1094,14 +1255,14 @@ "if": "socket:socket:type1_combo:voltage=400 V", "then": { "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", - "nl": " heeft een spanning van 400 volt" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" } }, { "if": "socket:socket:type1_combo:voltage=1000 V", "then": { "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", - "nl": " heeft een spanning van 1000 volt" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" } } ], @@ -1116,11 +1277,11 @@ "id": "current-5", "question": { "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke stroom levert de stekker van type ?" + "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" }, "render": { "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", - "nl": " levert een stroom van maximaal {socket:type1_combo:current}A" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" }, "freeform": { "key": "socket:type1_combo:current", @@ -1131,14 +1292,14 @@ "if": "socket:socket:type1_combo:current=50 A", "then": { "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", - "nl": " levert een stroom van maximaal 50 A" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" } }, { "if": "socket:socket:type1_combo:current=125 A", "then": { "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" } } ], @@ -1153,11 +1314,11 @@ "id": "power-output-5", "question": { "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" + "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" }, "render": { "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", - "nl": " levert een vermogen van maximaal {socket:type1_combo:output}" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" }, "freeform": { "key": "socket:type1_combo:output", @@ -1168,28 +1329,28 @@ "if": "socket:socket:type1_combo:output=50 kw", "then": { "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", - "nl": " levert een vermogen van maximaal 50 kw" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" } }, { "if": "socket:socket:type1_combo:output=62.5 kw", "then": { "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", - "nl": " levert een vermogen van maximaal 62.5 kw" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" } }, { "if": "socket:socket:type1_combo:output=150 kw", "then": { "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", - "nl": " levert een vermogen van maximaal 150 kw" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" } }, { "if": "socket:socket:type1_combo:output=350 kw", "then": { "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", - "nl": " levert een vermogen van maximaal 350 kw" + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" } } ], @@ -1204,11 +1365,11 @@ "id": "plugs-6", "question": { "en": "How much plugs of type Tesla Supercharger are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" + "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" }, "render": { "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", - "nl": "Hier zijn stekkers van het type " + "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" }, "freeform": { "key": "socket:tesla_supercharger", @@ -1225,11 +1386,11 @@ "id": "voltage-6", "question": { "en": "What voltage do the plugs with Tesla Supercharger offer?", - "nl": "Welke spanning levert de stekker van type " + "nl": "Welke spanning levert de stekker van type Tesla Supercharger " }, "render": { "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", - "nl": " heeft een spanning van {socket:tesla_supercharger:voltage} volt" + "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" }, "freeform": { "key": "socket:tesla_supercharger:voltage", @@ -1240,7 +1401,7 @@ "if": "socket:socket:tesla_supercharger:voltage=480 V", "then": { "en": "Tesla Supercharger outputs 480 volt", - "nl": " heeft een spanning van 480 volt" + "nl": "Tesla Supercharger heeft een spanning van 480 volt" } } ], @@ -1255,11 +1416,11 @@ "id": "current-6", "question": { "en": "What current do the plugs with Tesla Supercharger offer?", - "nl": "Welke stroom levert de stekker van type ?" + "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" }, "render": { "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", - "nl": " levert een stroom van maximaal {socket:tesla_supercharger:current}A" + "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" }, "freeform": { "key": "socket:tesla_supercharger:current", @@ -1270,14 +1431,14 @@ "if": "socket:socket:tesla_supercharger:current=125 A", "then": { "en": "Tesla Supercharger outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" + "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:tesla_supercharger:current=350 A", "then": { "en": "Tesla Supercharger outputs at most 350 A", - "nl": " levert een stroom van maximaal 350 A" + "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" } } ], @@ -1292,11 +1453,11 @@ "id": "power-output-6", "question": { "en": "What power output does a single plug of type Tesla Supercharger offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" }, "render": { "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", - "nl": " levert een vermogen van maximaal {socket:tesla_supercharger:output}" + "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" }, "freeform": { "key": "socket:tesla_supercharger:output", @@ -1307,21 +1468,21 @@ "if": "socket:socket:tesla_supercharger:output=120 kw", "then": { "en": "Tesla Supercharger outputs at most 120 kw", - "nl": " levert een vermogen van maximaal 120 kw" + "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" } }, { "if": "socket:socket:tesla_supercharger:output=150 kw", "then": { "en": "Tesla Supercharger outputs at most 150 kw", - "nl": " levert een vermogen van maximaal 150 kw" + "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" } }, { "if": "socket:socket:tesla_supercharger:output=250 kw", "then": { "en": "Tesla Supercharger outputs at most 250 kw", - "nl": " levert een vermogen van maximaal 250 kw" + "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" } } ], @@ -1336,11 +1497,11 @@ "id": "plugs-7", "question": { "en": "How much plugs of type Type 2 (mennekes) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" + "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" }, "render": { "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", - "nl": "Hier zijn stekkers van het type " + "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" }, "freeform": { "key": "socket:type2", @@ -1357,11 +1518,11 @@ "id": "voltage-7", "question": { "en": "What voltage do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type " + "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " }, "render": { "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", - "nl": " heeft een spanning van {socket:type2:voltage} volt" + "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" }, "freeform": { "key": "socket:type2:voltage", @@ -1372,14 +1533,14 @@ "if": "socket:socket:type2:voltage=230 V", "then": { "en": "Type 2 (mennekes) outputs 230 volt", - "nl": " heeft een spanning van 230 volt" + "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" } }, { "if": "socket:socket:type2:voltage=400 V", "then": { "en": "Type 2 (mennekes) outputs 400 volt", - "nl": " heeft een spanning van 400 volt" + "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" } } ], @@ -1394,11 +1555,11 @@ "id": "current-7", "question": { "en": "What current do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type ?" + "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" }, "render": { "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", - "nl": " levert een stroom van maximaal {socket:type2:current}A" + "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" }, "freeform": { "key": "socket:type2:current", @@ -1409,14 +1570,14 @@ "if": "socket:socket:type2:current=16 A", "then": { "en": "Type 2 (mennekes) outputs at most 16 A", - "nl": " levert een stroom van maximaal 16 A" + "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" } }, { "if": "socket:socket:type2:current=32 A", "then": { "en": "Type 2 (mennekes) outputs at most 32 A", - "nl": " levert een stroom van maximaal 32 A" + "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" } } ], @@ -1431,11 +1592,11 @@ "id": "power-output-7", "question": { "en": "What power output does a single plug of type Type 2 (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" + "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" }, "render": { "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", - "nl": " levert een vermogen van maximaal {socket:type2:output}" + "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" }, "freeform": { "key": "socket:type2:output", @@ -1446,14 +1607,14 @@ "if": "socket:socket:type2:output=11 kw", "then": { "en": "Type 2 (mennekes) outputs at most 11 kw", - "nl": " levert een vermogen van maximaal 11 kw" + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" } }, { "if": "socket:socket:type2:output=22 kw", "then": { "en": "Type 2 (mennekes) outputs at most 22 kw", - "nl": " levert een vermogen van maximaal 22 kw" + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" } } ], @@ -1468,11 +1629,11 @@ "id": "plugs-8", "question": { "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" + "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" }, "render": { "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", - "nl": "Hier zijn stekkers van het type " + "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" }, "freeform": { "key": "socket:type2_combo", @@ -1489,11 +1650,11 @@ "id": "voltage-8", "question": { "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type " + "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " }, "render": { "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", - "nl": " heeft een spanning van {socket:type2_combo:voltage} volt" + "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" }, "freeform": { "key": "socket:type2_combo:voltage", @@ -1504,14 +1665,14 @@ "if": "socket:socket:type2_combo:voltage=500 V", "then": { "en": "Type 2 CCS (mennekes) outputs 500 volt", - "nl": " heeft een spanning van 500 volt" + "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" } }, { "if": "socket:socket:type2_combo:voltage=920 V", "then": { "en": "Type 2 CCS (mennekes) outputs 920 volt", - "nl": " heeft een spanning van 920 volt" + "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" } } ], @@ -1526,11 +1687,11 @@ "id": "current-8", "question": { "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type ?" + "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" }, "render": { "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", - "nl": " levert een stroom van maximaal {socket:type2_combo:current}A" + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" }, "freeform": { "key": "socket:type2_combo:current", @@ -1541,14 +1702,14 @@ "if": "socket:socket:type2_combo:current=125 A", "then": { "en": "Type 2 CCS (mennekes) outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:type2_combo:current=350 A", "then": { "en": "Type 2 CCS (mennekes) outputs at most 350 A", - "nl": " levert een stroom van maximaal 350 A" + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" } } ], @@ -1563,11 +1724,11 @@ "id": "power-output-8", "question": { "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" + "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" }, "render": { "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", - "nl": " levert een vermogen van maximaal {socket:type2_combo:output}" + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" }, "freeform": { "key": "socket:type2_combo:output", @@ -1578,7 +1739,7 @@ "if": "socket:socket:type2_combo:output=50 kw", "then": { "en": "Type 2 CCS (mennekes) outputs at most 50 kw", - "nl": " levert een vermogen van maximaal 50 kw" + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" } } ], @@ -1725,11 +1886,11 @@ "id": "plugs-10", "question": { "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" + "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" }, "render": { "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", - "nl": "Hier zijn stekkers van het type " + "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" }, "freeform": { "key": "socket:tesla_supercharger_ccs", @@ -1746,11 +1907,11 @@ "id": "voltage-10", "question": { "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke spanning levert de stekker van type " + "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " }, "render": { "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": " heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" }, "freeform": { "key": "socket:tesla_supercharger_ccs:voltage", @@ -1761,14 +1922,14 @@ "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", "then": { "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", - "nl": " heeft een spanning van 500 volt" + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" } }, { "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", "then": { "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", - "nl": " heeft een spanning van 920 volt" + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" } } ], @@ -1783,11 +1944,11 @@ "id": "current-10", "question": { "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke stroom levert de stekker van type ?" + "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" }, "render": { "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": " levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" }, "freeform": { "key": "socket:tesla_supercharger_ccs:current", @@ -1798,14 +1959,14 @@ "if": "socket:socket:tesla_supercharger_ccs:current=125 A", "then": { "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:tesla_supercharger_ccs:current=350 A", "then": { "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", - "nl": " levert een stroom van maximaal 350 A" + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" } } ], @@ -1820,11 +1981,11 @@ "id": "power-output-10", "question": { "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" }, "render": { "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": " levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" }, "freeform": { "key": "socket:tesla_supercharger_ccs:output", @@ -1835,7 +1996,7 @@ "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", "then": { "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", - "nl": " levert een vermogen van maximaal 50 kw" + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" } } ], @@ -1850,11 +2011,11 @@ "id": "plugs-11", "question": { "en": "How much plugs of type Tesla Supercharger (destination) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" + "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" }, "render": { "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", - "nl": "Hier zijn stekkers van het type " + "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" }, "freeform": { "key": "socket:tesla_destination", @@ -1871,11 +2032,11 @@ "id": "voltage-11", "question": { "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke spanning levert de stekker van type " + "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " }, "render": { "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", - "nl": " heeft een spanning van {socket:tesla_destination:voltage} volt" + "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" }, "freeform": { "key": "socket:tesla_destination:voltage", @@ -1886,7 +2047,7 @@ "if": "socket:socket:tesla_destination:voltage=480 V", "then": { "en": "Tesla Supercharger (destination) outputs 480 volt", - "nl": " heeft een spanning van 480 volt" + "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" } } ], @@ -1901,11 +2062,11 @@ "id": "current-11", "question": { "en": "What current do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke stroom levert de stekker van type ?" + "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" }, "render": { "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", - "nl": " levert een stroom van maximaal {socket:tesla_destination:current}A" + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" }, "freeform": { "key": "socket:tesla_destination:current", @@ -1916,14 +2077,14 @@ "if": "socket:socket:tesla_destination:current=125 A", "then": { "en": "Tesla Supercharger (destination) outputs at most 125 A", - "nl": " levert een stroom van maximaal 125 A" + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:tesla_destination:current=350 A", "then": { "en": "Tesla Supercharger (destination) outputs at most 350 A", - "nl": " levert een stroom van maximaal 350 A" + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" } } ], @@ -1938,11 +2099,11 @@ "id": "power-output-11", "question": { "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" }, "render": { "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", - "nl": " levert een vermogen van maximaal {socket:tesla_destination:output}" + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" }, "freeform": { "key": "socket:tesla_destination:output", @@ -1953,21 +2114,21 @@ "if": "socket:socket:tesla_destination:output=120 kw", "then": { "en": "Tesla Supercharger (destination) outputs at most 120 kw", - "nl": " levert een vermogen van maximaal 120 kw" + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" } }, { "if": "socket:socket:tesla_destination:output=150 kw", "then": { "en": "Tesla Supercharger (destination) outputs at most 150 kw", - "nl": " levert een vermogen van maximaal 150 kw" + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" } }, { "if": "socket:socket:tesla_destination:output=250 kw", "then": { "en": "Tesla Supercharger (destination) outputs at most 250 kw", - "nl": " levert een vermogen van maximaal 250 kw" + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" } } ], @@ -1982,11 +2143,11 @@ "id": "plugs-12", "question": { "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", - "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" + "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" }, "render": { "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", - "nl": "Hier zijn stekkers van het type " + "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" }, "freeform": { "key": "socket:tesla_destination", @@ -2003,11 +2164,11 @@ "id": "voltage-12", "question": { "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke spanning levert de stekker van type " + "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " }, "render": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", - "nl": " heeft een spanning van {socket:tesla_destination:voltage} volt" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" }, "freeform": { "key": "socket:tesla_destination:voltage", @@ -2018,14 +2179,14 @@ "if": "socket:socket:tesla_destination:voltage=230 V", "then": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", - "nl": " heeft een spanning van 230 volt" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" } }, { "if": "socket:socket:tesla_destination:voltage=400 V", "then": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", - "nl": " heeft een spanning van 400 volt" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" } } ], @@ -2040,11 +2201,11 @@ "id": "current-12", "question": { "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke stroom levert de stekker van type ?" + "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" }, "render": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", - "nl": " levert een stroom van maximaal {socket:tesla_destination:current}A" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" }, "freeform": { "key": "socket:tesla_destination:current", @@ -2055,14 +2216,14 @@ "if": "socket:socket:tesla_destination:current=16 A", "then": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", - "nl": " levert een stroom van maximaal 16 A" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" } }, { "if": "socket:socket:tesla_destination:current=32 A", "then": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", - "nl": " levert een stroom van maximaal 32 A" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" } } ], @@ -2077,11 +2238,11 @@ "id": "power-output-12", "question": { "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welk vermogen levert een enkele stekker van type ?" + "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" }, "render": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", - "nl": " levert een vermogen van maximaal {socket:tesla_destination:output}" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" }, "freeform": { "key": "socket:tesla_destination:output", @@ -2092,14 +2253,14 @@ "if": "socket:socket:tesla_destination:output=11 kw", "then": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", - "nl": " levert een vermogen van maximaal 11 kw" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" } }, { "if": "socket:socket:tesla_destination:output=22 kw", "then": { "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", - "nl": " levert een vermogen van maximaal 22 kw" + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" } } ], @@ -2110,6 +2271,131 @@ ] } }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type USB to charge phones and small electronics are available here?", + "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" + }, + "render": { + "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", + "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "voltage-13", + "question": { + "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " + }, + "render": { + "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" + }, + "freeform": { + "key": "socket:USB-A:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:voltage=5 V", + "then": { + "en": "USB to charge phones and small electronics outputs 5 volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "current-13", + "question": { + "en": "What current do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" + }, + "freeform": { + "key": "socket:USB-A:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:current=1 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 1 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" + } + }, + { + "if": "socket:socket:USB-A:current=2 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 2 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "power-output-13", + "question": { + "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", + "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" + }, + "freeform": { + "key": "socket:USB-A:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:output=5w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 5w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" + } + }, + { + "if": "socket:socket:USB-A:output=10w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 10w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, { "id": "Authentication", "question": { @@ -2305,24 +2591,6 @@ "nl": "Betalen via een lidkaart van het netwerk" } } - ], - "mappings": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" - } - } ] } }, @@ -2519,7 +2787,12 @@ } }, { - "if": "amenity=charging_station", + "if": { + "and": [ + "amenity=charging_station", + "operational_status=" + ] + }, "then": { "en": "This charging station works", "nl": "Dit oplaadpunt werkt" @@ -2698,7 +2971,7 @@ { "question": { "en": "Has a Chademo connector", - "nl": "Heeft een " + "nl": "Heeft een Chademo " }, "osmTags": "socket:chademo~*" }, @@ -2719,28 +2992,28 @@ { "question": { "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", - "nl": "Heeft een " + "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " }, "osmTags": "socket:type1_combo~*" }, { "question": { "en": "Has a Tesla Supercharger connector", - "nl": "Heeft een " + "nl": "Heeft een Tesla Supercharger " }, "osmTags": "socket:tesla_supercharger~*" }, { "question": { "en": "Has a Type 2 (mennekes) connector", - "nl": "Heeft een " + "nl": "Heeft een Type 2 (mennekes) " }, "osmTags": "socket:type2~*" }, { "question": { "en": "Has a Type 2 CCS (mennekes) connector", - "nl": "Heeft een " + "nl": "Heeft een Type 2 CCS (mennekes) " }, "osmTags": "socket:type2_combo~*" }, @@ -2754,23 +3027,30 @@ { "question": { "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", - "nl": "Heeft een " + "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " }, "osmTags": "socket:tesla_supercharger_ccs~*" }, { "question": { "en": "Has a Tesla Supercharger (destination) connector", - "nl": "Heeft een " + "nl": "Heeft een Tesla Supercharger (destination) " }, "osmTags": "socket:tesla_destination~*" }, { "question": { "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", - "nl": "Heeft een " + "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " }, "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a USB to charge phones and small electronics connector", + "nl": "Heeft een USB om GSMs en kleine electronica op te laden " + }, + "osmTags": "socket:USB-A~*" } ] } @@ -2853,7 +3133,8 @@ "socket:type2_cable:voltage", "socket:tesla_supercharger_ccs:voltage", "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage" + "socket:tesla_destination:voltage", + "socket:USB-A:voltage" ], "applicableUnits": [ { @@ -2887,7 +3168,8 @@ "socket:type2_cable:current", "socket:tesla_supercharger_ccs:current", "socket:tesla_destination:current", - "socket:tesla_destination:current" + "socket:tesla_destination:current", + "socket:USB-A:current" ], "applicableUnits": [ { @@ -2920,7 +3202,8 @@ "socket:type2_cable:output", "socket:tesla_supercharger_ccs:output", "socket:tesla_destination:output", - "socket:tesla_destination:output" + "socket:tesla_destination:output", + "socket:USB-A:output" ], "applicableUnits": [ { diff --git a/assets/layers/charging_station/csvToJson.ts b/assets/layers/charging_station/csvToJson.ts index 4a5b04f53..1d0bab2f7 100644 --- a/assets/layers/charging_station/csvToJson.ts +++ b/assets/layers/charging_station/csvToJson.ts @@ -17,7 +17,8 @@ function loadCsv(file): { countryBlackList?: string[], commonVoltages?: number[], commonCurrents?: number[], - commonOutputs?: string[] + commonOutputs?: string[], + associatedVehicleTypes?:string[] }[] { const entries: string[] = Utils.NoNull(readFileSync(file, "utf8").split("\n").map(str => str.trim())) const header = entries.shift().split(",") @@ -29,7 +30,7 @@ function loadCsv(file): { } const v = {} - const colonSeperated = ["commonVoltages", "commonOutputs", "commonCurrents", "countryWhiteList","countryBlackList"] + const colonSeperated = ["commonVoltages", "commonOutputs", "commonCurrents", "countryWhiteList","countryBlackList","associatedVehicleTypes"] const descriptionTranslations = new Map() for (let j = 0; j < header.length; j++) { const key = header[j]; @@ -67,8 +68,8 @@ function run(file, protojson) { for (let i = 0; i < entries.length; i++){ const e = entries[i]; const txt = { - en: ` ${e.description.get("en")}`, - nl: ` ${e.description.get("nl")}` + en: `
${e.description.get("en")}
`, + nl: `
${e.description.get("nl")} 0) { + // This is a 'hideInAnswer', thus _reverse_ logic! const countries = e.countryWhiteList.map(country => "_country!=" + country) //HideInAnswer if it is in the wrong country json["hideInAnswer"] = {or: countries} }else if (e.countryBlackList .length > 0) { const countries = e.countryBlackList.map(country => "_country=" + country) //HideInAnswer if it is in the wrong country json["hideInAnswer"] = {or: countries} } + + if(e.associatedVehicleTypes?.length > 0 && e.associatedVehicleTypes.indexOf("*") < 0){ + // This plug only occurs if some vehicle specific vehicle type is present. + // IF all of the needed vehicle types are explicitly NO, then we hide this type as well + let hideInAnswer : any = {and: [].concat(...e.associatedVehicleTypes.map(neededVehicle => [neededVehicle+"~*", neededVehicle+"!=yes"]))} + if(json["hideInAnswer"] !== undefined){ + hideInAnswer = {or: [json["hideInAnswer"], hideInAnswer]} + } + json["hideInAnswer"] = hideInAnswer + } overview_question_answers.push(json) @@ -300,7 +312,8 @@ function run(file, protojson) { } proto["units"].push(...extraUnits) - mergeTranslations("charging_station.json",proto) + // mergeTranslations("charging_station.json",proto) + writeFileSync("charging_station.json", JSON.stringify(proto, undefined, " ")) } @@ -330,7 +343,6 @@ async function queryTagInfo(file, type, clean: ((s: string) => string)) { const countsArray = Array.from(counts.keys()) countsArray.sort() console.log(`${e.key}:${type} = ${countsArray.join(";")}`) - // console.log(`${countsArray.join(";")}`) } } @@ -362,6 +374,7 @@ function mergeTranslations(origPath, newConfig: LayerConfigJson){ } try { + console.log("Generating the charging_station.json file") run("types.csv", "charging_station.protojson") /*/ queryTagInfo("types.csv","voltage", s => s.trim()) diff --git a/assets/layers/charging_station/license_info.json b/assets/layers/charging_station/license_info.json index 96d5a1ef7..0445cb394 100644 --- a/assets/layers/charging_station/license_info.json +++ b/assets/layers/charging_station/license_info.json @@ -108,5 +108,15 @@ "sources": [ "https://upload.wikimedia.org/wikipedia/commons/2/20/UnderCon_icon.svg" ] + }, + { + "path": "usb_port.svg", + "license": "CC-BY", + "authors": [ + "Ryan Dardis" + ], + "sources": [ + "https://thenounproject.com/term/usb-port/94768/" + ] } ] \ No newline at end of file diff --git a/assets/layers/charging_station/types.csv b/assets/layers/charging_station/types.csv index 4b76be6a7..ad7f9cc17 100644 --- a/assets/layers/charging_station/types.csv +++ b/assets/layers/charging_station/types.csv @@ -1,14 +1,15 @@ -key,image,description:en,countryWhiteList,countryBlackList,commonVoltages,commonCurrents,commonOutputs,description:nl -socket:schuko,CEE7_4F.svg,Schuko wall plug without ground pin (CEE7/4 type F),be;fr;ma;tn;pl;cs;sk;mo,,230,16,3.6 kW,Schuko stekker zonder aardingspin (CEE7/4 type F) -socket:typee,TypeE.svg,European wall plug with ground pin (CEE7/4 type E),,,230,16,3 kW;22 kW;,Europese stekker met aardingspin (CEE7/4 type E) -socket:chademo,Chademo_type4.svg,Chademo,,,500,120,50 kW, -socket:type1_cable,Type1_J1772.svg,Type 1 with cable (J1772),,,200;240,32,3.7 kW;7 kW,Type 1 met kabel (J1772) -socket:type1,Type1_J1772.svg,Type 1 without cable (J1772),,,200;240,32,3.7 kW;6.6 kW;7 kW;7.2 kW,Type 1 zonder kabel (J1772) -socket:type1_combo,Type1-ccs.svg,Type 1 CCS (aka Type 1 Combo),,,400;1000,50;125,50 kW;62.5 kW;150 kW;350 kW;, -socket:tesla_supercharger,Tesla-hpwc-model-s.svg,Tesla Supercharger,,,480,125;350,120 kW;150 kW;250 kW, -socket:type2,Type2_socket.svg,Type 2 (mennekes),,,230;400,16;32,11 kW;22 kW, -socket:type2_combo,Type2_CCS.svg,Type 2 CCS (mennekes),,,500;920,125;350,50 kW, -socket:type2_cable,Type2_tethered.svg,Type 2 with cable (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 met kabel (J1772) -socket:tesla_supercharger_ccs,Type2_CCS.svg,Tesla Supercharger CCS (a branded type2_css),,,500;920,125;350,50 kW, -socket:tesla_destination,Tesla-hpwc-model-s.svg,Tesla Supercharger (destination),us,,480,125;350,120 kW;150 kW;250 kW, -socket:tesla_destination,Type2_tethered.svg,Tesla supercharger (destination (A Type 2 with cable branded as tesla),,us,230;400,16;32,11 kW;22 kW, +key,image,description:en,countryWhiteList,countryBlackList,commonVoltages,commonCurrents,commonOutputs,description:nl,associatedVehicleTypes +socket:schuko,CEE7_4F.svg,Schuko wall plug without ground pin (CEE7/4 type F),be;fr;ma;tn;pl;cs;sk;mo,,230,16,3.6 kW,Schuko stekker zonder aardingspin (CEE7/4 type F),* +socket:typee,TypeE.svg,European wall plug with ground pin (CEE7/4 type E),,,230,16,3 kW;22 kW;,Europese stekker met aardingspin (CEE7/4 type E),* +socket:chademo,Chademo_type4.svg,Chademo,,,500,120,50 kW,Chademo,car;motorcar;hgv;bus +socket:type1_cable,Type1_J1772.svg,Type 1 with cable (J1772),,,200;240,32,3.7 kW;7 kW,Type 1 met kabel (J1772),car;motorcar;hgv;bus +socket:type1,Type1_J1772.svg,Type 1 without cable (J1772),,,200;240,32,3.7 kW;6.6 kW;7 kW;7.2 kW,Type 1 zonder kabel (J1772),car;motorcar;hgv;bus +socket:type1_combo,Type1-ccs.svg,Type 1 CCS (aka Type 1 Combo),,,400;1000,50;125,50 kW;62.5 kW;150 kW;350 kW;,Type 1 CCS (ook gekend als Type 1 Combo),car;motorcar;hgv;bus +socket:tesla_supercharger,Tesla-hpwc-model-s.svg,Tesla Supercharger,,,480,125;350,120 kW;150 kW;250 kW,Tesla Supercharger,car;motorcar;hgv;bus +socket:type2,Type2_socket.svg,Type 2 (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 (mennekes),car;motorcar;hgv;bus +socket:type2_combo,Type2_CCS.svg,Type 2 CCS (mennekes),,,500;920,125;350,50 kW,Type 2 CCS (mennekes),car;motorcar;hgv;bus +socket:type2_cable,Type2_tethered.svg,Type 2 with cable (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 met kabel (J1772),car;motorcar;hgv;bus +socket:tesla_supercharger_ccs,Type2_CCS.svg,Tesla Supercharger CCS (a branded type2_css),,,500;920,125;350,50 kW,Tesla Supercharger CCS (een type2 CCS met Tesla-logo),car;motorcar;hgv;bus +socket:tesla_destination,Tesla-hpwc-model-s.svg,Tesla Supercharger (destination),us,,480,125;350,120 kW;150 kW;250 kW,Tesla Supercharger (destination),car;motorcar;hgv;bus +socket:tesla_destination,Type2_tethered.svg,Tesla supercharger (destination (A Type 2 with cable branded as tesla),,us,230;400,16;32,11 kW;22 kW,Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo),car;motorcar;hgv;bus +socket:USB-A,usb_port.svg,USB to charge phones and small electronics,,,5,1;2,5W;10W,USB om GSMs en kleine electronica op te laden,* diff --git a/assets/layers/charging_station/usb_port.svg b/assets/layers/charging_station/usb_port.svg new file mode 100644 index 000000000..f813f20f0 --- /dev/null +++ b/assets/layers/charging_station/usb_port.svg @@ -0,0 +1,74 @@ + +image/svg+xml \ No newline at end of file From 29e7fea95ae17bf96677658a418d29055e98584c Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Thu, 30 Sep 2021 20:09:09 +0200 Subject: [PATCH 062/110] Replace inline style with TW classes --- .../mapcomplete_charging_stations.json | 38 +++++----- .../charging_station/charging_station.json | 74 +++++++++---------- assets/layers/charging_station/csvToJson.ts | 10 +-- css/index-tailwind-output.css | 5 ++ langs/layers/en.json | 38 +++++----- langs/layers/nl.json | 38 +++++----- 6 files changed, 104 insertions(+), 99 deletions(-) diff --git a/Docs/TagInfo/mapcomplete_charging_stations.json b/Docs/TagInfo/mapcomplete_charging_stations.json index 0306d7284..22ca1d0a4 100644 --- a/Docs/TagInfo/mapcomplete_charging_stations.json +++ b/Docs/TagInfo/mapcomplete_charging_stations.json @@ -106,84 +106,84 @@ }, { "key": "socket:schuko", - "description": "Layer 'Charging stations' shows socket:schuko=1 with a fixed text, namely ' Schuko wall plug without ground pin (CEE7/4 type F)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:schuko=1 with a fixed text, namely ' Schuko wall plug without ground pin (CEE7/4 type F)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:schuko", - "description": "Layer 'Charging stations' shows socket:schuko~^..*$&socket:schuko!~^1$ with a fixed text, namely ' Schuko wall plug without ground pin (CEE7/4 type F)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:schuko~^..*$&socket:schuko!~^1$ with a fixed text, namely ' Schuko wall plug without ground pin (CEE7/4 type F)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:typee", - "description": "Layer 'Charging stations' shows socket:typee=1 with a fixed text, namely ' European wall plug with ground pin (CEE7/4 type E)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:typee=1 with a fixed text, namely ' European wall plug with ground pin (CEE7/4 type E)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:typee", - "description": "Layer 'Charging stations' shows socket:typee~^..*$&socket:typee!~^1$ with a fixed text, namely ' European wall plug with ground pin (CEE7/4 type E)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:typee~^..*$&socket:typee!~^1$ with a fixed text, namely ' European wall plug with ground pin (CEE7/4 type E)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:chademo", - "description": "Layer 'Charging stations' shows socket:chademo=1 with a fixed text, namely ' Chademo' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:chademo=1 with a fixed text, namely ' Chademo' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:chademo", - "description": "Layer 'Charging stations' shows socket:chademo~^..*$&socket:chademo!~^1$ with a fixed text, namely ' Chademo' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:chademo~^..*$&socket:chademo!~^1$ with a fixed text, namely ' Chademo' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type1_cable", - "description": "Layer 'Charging stations' shows socket:type1_cable=1 with a fixed text, namely ' Type 1 with cable (J1772)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type1_cable=1 with a fixed text, namely ' Type 1 with cable (J1772)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type1_cable", - "description": "Layer 'Charging stations' shows socket:type1_cable~^..*$&socket:type1_cable!~^1$ with a fixed text, namely ' Type 1 with cable (J1772)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type1_cable~^..*$&socket:type1_cable!~^1$ with a fixed text, namely ' Type 1 with cable (J1772)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type1", - "description": "Layer 'Charging stations' shows socket:type1=1 with a fixed text, namely ' Type 1 without cable (J1772)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type1=1 with a fixed text, namely ' Type 1 without cable (J1772)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type1", - "description": "Layer 'Charging stations' shows socket:type1~^..*$&socket:type1!~^1$ with a fixed text, namely ' Type 1 without cable (J1772)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type1~^..*$&socket:type1!~^1$ with a fixed text, namely ' Type 1 without cable (J1772)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type1_combo", - "description": "Layer 'Charging stations' shows socket:type1_combo=1 with a fixed text, namely ' Type 1 CCS (aka Type 1 Combo)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type1_combo=1 with a fixed text, namely ' Type 1 CCS (aka Type 1 Combo)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type1_combo", - "description": "Layer 'Charging stations' shows socket:type1_combo~^..*$&socket:type1_combo!~^1$ with a fixed text, namely ' Type 1 CCS (aka Type 1 Combo)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type1_combo~^..*$&socket:type1_combo!~^1$ with a fixed text, namely ' Type 1 CCS (aka Type 1 Combo)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:tesla_supercharger", - "description": "Layer 'Charging stations' shows socket:tesla_supercharger=1 with a fixed text, namely ' Tesla Supercharger' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:tesla_supercharger=1 with a fixed text, namely ' Tesla Supercharger' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:tesla_supercharger", - "description": "Layer 'Charging stations' shows socket:tesla_supercharger~^..*$&socket:tesla_supercharger!~^1$ with a fixed text, namely ' Tesla Supercharger' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:tesla_supercharger~^..*$&socket:tesla_supercharger!~^1$ with a fixed text, namely ' Tesla Supercharger' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type2", - "description": "Layer 'Charging stations' shows socket:type2=1 with a fixed text, namely ' Type 2 (mennekes)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type2=1 with a fixed text, namely ' Type 2 (mennekes)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type2", - "description": "Layer 'Charging stations' shows socket:type2~^..*$&socket:type2!~^1$ with a fixed text, namely ' Type 2 (mennekes)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type2~^..*$&socket:type2!~^1$ with a fixed text, namely ' Type 2 (mennekes)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:type2_combo", - "description": "Layer 'Charging stations' shows socket:type2_combo=1 with a fixed text, namely ' Type 2 CCS (mennekes)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", + "description": "Layer 'Charging stations' shows socket:type2_combo=1 with a fixed text, namely ' Type 2 CCS (mennekes)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')", "value": "1" }, { "key": "socket:type2_combo", - "description": "Layer 'Charging stations' shows socket:type2_combo~^..*$&socket:type2_combo!~^1$ with a fixed text, namely ' Type 2 CCS (mennekes)' (in the MapComplete.osm.be theme 'Charging stations')" + "description": "Layer 'Charging stations' shows socket:type2_combo~^..*$&socket:type2_combo!~^1$ with a fixed text, namely ' Type 2 CCS (mennekes)' (in the MapComplete.osm.be theme 'Charging stations')" }, { "key": "socket:schuko", @@ -793,4 +793,4 @@ "value": "yes" } ] -} \ No newline at end of file +} diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 3935c515f..e20cde958 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -151,8 +151,8 @@ "if": "socket:schuko=1", "ifnot": "socket:schuko=", "then": { - "en": " Schuko wall plug without ground pin (CEE7/4 type F)", - "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "en": " Schuko wall plug without ground pin (CEE7/4 type F)", + "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" } }, { @@ -163,8 +163,8 @@ ] }, "then": { - "en": " Schuko wall plug without ground pin (CEE7/4 type F)", - "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "en": " Schuko wall plug without ground pin (CEE7/4 type F)", + "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" }, "hideInAnswer": true }, @@ -172,8 +172,8 @@ "if": "socket:typee=1", "ifnot": "socket:typee=", "then": { - "en": " European wall plug with ground pin (CEE7/4 type E)", - "nl": " Europese stekker met aardingspin (CEE7/4 type E)" + "en": " European wall plug with ground pin (CEE7/4 type E)", + "nl": " Europese stekker met aardingspin (CEE7/4 type E)" } }, { @@ -184,8 +184,8 @@ ] }, "then": { - "en": " European wall plug with ground pin (CEE7/4 type E)", - "nl": " Europese stekker met aardingspin (CEE7/4 type E)" + "en": " European wall plug with ground pin (CEE7/4 type E)", + "nl": " Europese stekker met aardingspin (CEE7/4 type E)" }, "hideInAnswer": true }, @@ -193,8 +193,8 @@ "if": "socket:chademo=1", "ifnot": "socket:chademo=", "then": { - "en": " Chademo", - "nl": " " + "en": " Chademo", + "nl": " " } }, { @@ -205,8 +205,8 @@ ] }, "then": { - "en": " Chademo", - "nl": " " + "en": " Chademo", + "nl": " " }, "hideInAnswer": true }, @@ -214,8 +214,8 @@ "if": "socket:type1_cable=1", "ifnot": "socket:type1_cable=", "then": { - "en": " Type 1 with cable (J1772)", - "nl": " Type 1 met kabel (J1772)" + "en": " Type 1 with cable (J1772)", + "nl": " Type 1 met kabel (J1772)" } }, { @@ -226,8 +226,8 @@ ] }, "then": { - "en": " Type 1 with cable (J1772)", - "nl": " Type 1 met kabel (J1772)" + "en": " Type 1 with cable (J1772)", + "nl": " Type 1 met kabel (J1772)" }, "hideInAnswer": true }, @@ -235,8 +235,8 @@ "if": "socket:type1=1", "ifnot": "socket:type1=", "then": { - "en": " Type 1 without cable (J1772)", - "nl": " Type 1 zonder kabel (J1772)" + "en": " Type 1 without cable (J1772)", + "nl": " Type 1 zonder kabel (J1772)" } }, { @@ -247,8 +247,8 @@ ] }, "then": { - "en": " Type 1 without cable (J1772)", - "nl": " Type 1 zonder kabel (J1772)" + "en": " Type 1 without cable (J1772)", + "nl": " Type 1 zonder kabel (J1772)" }, "hideInAnswer": true }, @@ -256,8 +256,8 @@ "if": "socket:type1_combo=1", "ifnot": "socket:type1_combo=", "then": { - "en": " Type 1 CCS (aka Type 1 Combo)", - "nl": " " + "en": " Type 1 CCS (aka Type 1 Combo)", + "nl": " " } }, { @@ -268,8 +268,8 @@ ] }, "then": { - "en": " Type 1 CCS (aka Type 1 Combo)", - "nl": " " + "en": " Type 1 CCS (aka Type 1 Combo)", + "nl": " " }, "hideInAnswer": true }, @@ -277,8 +277,8 @@ "if": "socket:tesla_supercharger=1", "ifnot": "socket:tesla_supercharger=", "then": { - "en": " Tesla Supercharger", - "nl": " " + "en": " Tesla Supercharger", + "nl": " " } }, { @@ -289,8 +289,8 @@ ] }, "then": { - "en": " Tesla Supercharger", - "nl": " " + "en": " Tesla Supercharger", + "nl": " " }, "hideInAnswer": true }, @@ -298,8 +298,8 @@ "if": "socket:type2=1", "ifnot": "socket:type2=", "then": { - "en": " Type 2 (mennekes)", - "nl": " " + "en": " Type 2 (mennekes)", + "nl": " " } }, { @@ -310,8 +310,8 @@ ] }, "then": { - "en": " Type 2 (mennekes)", - "nl": " " + "en": " Type 2 (mennekes)", + "nl": " " }, "hideInAnswer": true }, @@ -319,8 +319,8 @@ "if": "socket:type2_combo=1", "ifnot": "socket:type2_combo=", "then": { - "en": " Type 2 CCS (mennekes)", - "nl": " " + "en": " Type 2 CCS (mennekes)", + "nl": " " } }, { @@ -331,8 +331,8 @@ ] }, "then": { - "en": " Type 2 CCS (mennekes)", - "nl": " " + "en": " Type 2 CCS (mennekes)", + "nl": " " }, "hideInAnswer": true } @@ -2180,4 +2180,4 @@ "eraseInvalidValues": true } ] -} \ No newline at end of file +} diff --git a/assets/layers/charging_station/csvToJson.ts b/assets/layers/charging_station/csvToJson.ts index 7ea31a10f..80c3e557e 100644 --- a/assets/layers/charging_station/csvToJson.ts +++ b/assets/layers/charging_station/csvToJson.ts @@ -64,8 +64,8 @@ function run(file, protojson) { const entries = loadCsv(file) for (const e of entries) { const txt = { - en: ` ${e.description.get("en")}`, - nl: ` ${e.description.get("nl")}` + en: ` ${e.description.get("en")}`, + nl: ` ${e.description.get("nl")}` } const json = { if: `${e.key}=1`, @@ -220,8 +220,8 @@ function run(file, protojson) { options: filterOptions }) - - + + const extraUnits = [ { appliesToKey: entries.map(e => e.key + ":voltage"), @@ -273,7 +273,7 @@ function run(file, protojson) { proto["units"] = [] } proto["units"].push(...extraUnits) - + writeFileSync("charging_station.json", JSON.stringify(proto, undefined, " ")) } diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index e3d669e7c..1f7b87e18 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -839,6 +839,11 @@ video { margin-bottom: 0.75rem; } +.mx-4 { + margin-left: 1rem; + margin-right: 1rem; +} + .-ml-1 { margin-left: -0.25rem; } diff --git a/langs/layers/en.json b/langs/layers/en.json index 9149f1966..a6472bf0f 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -960,58 +960,58 @@ "4": { "mappings": { "0": { - "then": " Schuko wall plug without ground pin (CEE7/4 type F)" + "then": " Schuko wall plug without ground pin (CEE7/4 type F)" }, "1": { - "then": " Schuko wall plug without ground pin (CEE7/4 type F)" + "then": " Schuko wall plug without ground pin (CEE7/4 type F)" }, "2": { - "then": " European wall plug with ground pin (CEE7/4 type E)" + "then": " European wall plug with ground pin (CEE7/4 type E)" }, "3": { - "then": " European wall plug with ground pin (CEE7/4 type E)" + "then": " European wall plug with ground pin (CEE7/4 type E)" }, "4": { - "then": " Chademo" + "then": " Chademo" }, "5": { - "then": " Chademo" + "then": " Chademo" }, "6": { - "then": " Type 1 with cable (J1772)" + "then": " Type 1 with cable (J1772)" }, "7": { - "then": " Type 1 with cable (J1772)" + "then": " Type 1 with cable (J1772)" }, "8": { - "then": " Type 1 without cable (J1772)" + "then": " Type 1 without cable (J1772)" }, "9": { - "then": " Type 1 without cable (J1772)" + "then": " Type 1 without cable (J1772)" }, "10": { - "then": " Type 1 CCS (aka Type 1 Combo)" + "then": " Type 1 CCS (aka Type 1 Combo)" }, "11": { - "then": " Type 1 CCS (aka Type 1 Combo)" + "then": " Type 1 CCS (aka Type 1 Combo)" }, "12": { - "then": " Tesla Supercharger" + "then": " Tesla Supercharger" }, "13": { - "then": " Tesla Supercharger" + "then": " Tesla Supercharger" }, "14": { - "then": " Type 2 (mennekes)" + "then": " Type 2 (mennekes)" }, "15": { - "then": " Type 2 (mennekes)" + "then": " Type 2 (mennekes)" }, "16": { - "then": " Type 2 CCS (mennekes)" + "then": " Type 2 CCS (mennekes)" }, "17": { - "then": " Type 2 CCS (mennekes)" + "then": " Type 2 CCS (mennekes)" } }, "question": "Which charging stations are available here?" @@ -3383,4 +3383,4 @@ "watermill": { "name": "Watermill" } -} \ No newline at end of file +} diff --git a/langs/layers/nl.json b/langs/layers/nl.json index bddc9c255..a4be84c4f 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -1036,58 +1036,58 @@ "4": { "mappings": { "0": { - "then": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "then": " Schuko stekker zonder aardingspin (CEE7/4 type F)" }, "1": { - "then": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "then": " Schuko stekker zonder aardingspin (CEE7/4 type F)" }, "2": { - "then": " Europese stekker met aardingspin (CEE7/4 type E)" + "then": " Europese stekker met aardingspin (CEE7/4 type E)" }, "3": { - "then": " Europese stekker met aardingspin (CEE7/4 type E)" + "then": " Europese stekker met aardingspin (CEE7/4 type E)" }, "4": { - "then": " " + "then": " " }, "5": { - "then": " " + "then": " " }, "6": { - "then": " Type 1 met kabel (J1772)" + "then": " Type 1 met kabel (J1772)" }, "7": { - "then": " Type 1 met kabel (J1772)" + "then": " Type 1 met kabel (J1772)" }, "8": { - "then": " Type 1 zonder kabel (J1772)" + "then": " Type 1 zonder kabel (J1772)" }, "9": { - "then": " Type 1 zonder kabel (J1772)" + "then": " Type 1 zonder kabel (J1772)" }, "10": { - "then": " " + "then": " " }, "11": { - "then": " " + "then": " " }, "12": { - "then": " " + "then": " " }, "13": { - "then": " " + "then": " " }, "14": { - "then": " " + "then": " " }, "15": { - "then": " " + "then": " " }, "16": { - "then": " " + "then": " " }, "17": { - "then": " " + "then": " " } } }, @@ -3781,4 +3781,4 @@ "render": "Watermolens" } } -} \ No newline at end of file +} From 43618fe3f1210696ac6dcca552234ba9d4f5bbab Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Thu, 30 Sep 2021 21:41:21 +0200 Subject: [PATCH 063/110] Refactoring: Small html/variables changes --- UI/Input/Checkboxes.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/UI/Input/Checkboxes.ts b/UI/Input/Checkboxes.ts index 15aa75832..32386eea3 100644 --- a/UI/Input/Checkboxes.ts +++ b/UI/Input/Checkboxes.ts @@ -31,7 +31,7 @@ export default class CheckBoxes extends InputElement { } protected InnerConstructElement(): HTMLElement { - const el = document.createElement("form"); + const formTag = document.createElement("form"); const value = this.value; const elements = this._elements; @@ -57,7 +57,7 @@ export default class CheckBoxes extends InputElement { "bg-red" ); - const wrapper = document.createElement("span"); + const wrapper = document.createElement("div"); wrapper.classList.add( "wrapper", "flex", @@ -68,7 +68,7 @@ export default class CheckBoxes extends InputElement { ); wrapper.appendChild(input); wrapper.appendChild(label); - el.appendChild(wrapper); + formTag.appendChild(wrapper); value.addCallbackAndRunD((selectedValues) => { if (selectedValues.indexOf(i) >= 0) { @@ -97,6 +97,6 @@ export default class CheckBoxes extends InputElement { }; } - return el; + return formTag; } } From b8dc1063d0aa8c30b346fdd8d7006c6a9e432098 Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Thu, 30 Sep 2021 21:41:51 +0200 Subject: [PATCH 064/110] Refactoring: Random spaces / newline changes --- UI/BaseUIElement.ts | 5 ++--- UI/Input/FixedInputElement.ts | 4 +--- UI/Input/InputElement.ts | 1 - 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/UI/BaseUIElement.ts b/UI/BaseUIElement.ts index 99462acbd..ff2d75b83 100644 --- a/UI/BaseUIElement.ts +++ b/UI/BaseUIElement.ts @@ -99,9 +99,8 @@ export default abstract class BaseUIElement { if (this.InnerConstructElement === undefined) { throw "ERROR! This is not a correct baseUIElement: " + this.constructor.name } + try { - - const el = this.InnerConstructElement(); if (el === undefined) { @@ -163,4 +162,4 @@ export default abstract class BaseUIElement { } protected abstract InnerConstructElement(): HTMLElement; -} \ No newline at end of file +} diff --git a/UI/Input/FixedInputElement.ts b/UI/Input/FixedInputElement.ts index d82f795d4..ee2e8d950 100644 --- a/UI/Input/FixedInputElement.ts +++ b/UI/Input/FixedInputElement.ts @@ -41,6 +41,4 @@ export class FixedInputElement extends InputElement { protected InnerConstructElement(): HTMLElement { return this._el; } - - -} \ No newline at end of file +} diff --git a/UI/Input/InputElement.ts b/UI/Input/InputElement.ts index aadc6d9ee..f9920b1d6 100644 --- a/UI/Input/InputElement.ts +++ b/UI/Input/InputElement.ts @@ -10,4 +10,3 @@ export abstract class InputElement extends BaseUIElement { abstract IsValid(t: T): boolean; } - From 9889b0d464dcb0e9a785f7041cc2d36dcfcb9fbe Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Thu, 30 Sep 2021 21:42:31 +0200 Subject: [PATCH 065/110] Checkboxes: Margin only bottom We only need it to the bottom and left/right mess with w-full in this css-setup. --- UI/Input/Checkboxes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/Input/Checkboxes.ts b/UI/Input/Checkboxes.ts index 32386eea3..affad2192 100644 --- a/UI/Input/Checkboxes.ts +++ b/UI/Input/Checkboxes.ts @@ -64,7 +64,7 @@ export default class CheckBoxes extends InputElement { "w-full", "border", "border-gray-400", - "m-1" + "mb-1" ); wrapper.appendChild(input); wrapper.appendChild(label); From 821c97662bc4d98c89f022268beee01340be0a16 Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Thu, 30 Sep 2021 21:44:55 +0200 Subject: [PATCH 066/110] TW Classes instead of inline; no pointer event "all" The way I read https://developer.mozilla.org/de/docs/Web/CSS/pointer-events, `pointer-events: all` is a special SVG thing and what we want here, is what TW provides with `pointer-events-none` https://tailwindcss.com/docs/pointer-events so that event pass through. --- UI/BaseUIElement.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/UI/BaseUIElement.ts b/UI/BaseUIElement.ts index ff2d75b83..d0152bbd5 100644 --- a/UI/BaseUIElement.ts +++ b/UI/BaseUIElement.ts @@ -131,8 +131,7 @@ export default abstract class BaseUIElement { // @ts-ignore e.consumed = true; } - el.style.pointerEvents = "all"; - el.style.cursor = "pointer"; + el.classList.add("pointer-events-none", "cursor-pointer"); } if (this._onHover !== undefined) { From 8e040a8c4dc8ea580a434516743ee1f5e676bd54 Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Thu, 30 Sep 2021 21:46:20 +0200 Subject: [PATCH 067/110] Fix #491 with custom HTML/Classes inside translations This is not the fixed I hoped for, but the JS generates so many empty `` tags that I cannot get rid of, that it is not possible to style this "from the outside", which is unfortunate. --- .../charging_station/charging_station.json | 128 +++++++++--------- css/index-tailwind-output.css | 12 +- langs/layers/en.json | 92 ++++++------- langs/layers/nl.json | 38 +++--- 4 files changed, 137 insertions(+), 133 deletions(-) diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index e20cde958..9c9aa4471 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -151,8 +151,8 @@ "if": "socket:schuko=1", "ifnot": "socket:schuko=", "then": { - "en": " Schuko wall plug without ground pin (CEE7/4 type F)", - "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "en": "

Schuko wall plug without ground pin (CEE7/4 type F)

", + "nl": "

Schuko stekker zonder aardingspin (CEE7/4 type F)

" } }, { @@ -163,8 +163,8 @@ ] }, "then": { - "en": " Schuko wall plug without ground pin (CEE7/4 type F)", - "nl": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "en": "

Schuko wall plug without ground pin (CEE7/4 type F)

", + "nl": "

Schuko stekker zonder aardingspin (CEE7/4 type F)

" }, "hideInAnswer": true }, @@ -172,8 +172,8 @@ "if": "socket:typee=1", "ifnot": "socket:typee=", "then": { - "en": " European wall plug with ground pin (CEE7/4 type E)", - "nl": " Europese stekker met aardingspin (CEE7/4 type E)" + "en": "

European wall plug with ground pin (CEE7/4 type E)

", + "nl": "

Europese stekker met aardingspin (CEE7/4 type E)

" } }, { @@ -184,8 +184,8 @@ ] }, "then": { - "en": " European wall plug with ground pin (CEE7/4 type E)", - "nl": " Europese stekker met aardingspin (CEE7/4 type E)" + "en": "

European wall plug with ground pin (CEE7/4 type E)

", + "nl": "

Europese stekker met aardingspin (CEE7/4 type E)

" }, "hideInAnswer": true }, @@ -193,8 +193,8 @@ "if": "socket:chademo=1", "ifnot": "socket:chademo=", "then": { - "en": " Chademo", - "nl": " " + "en": "

Chademo

", + "nl": "

" } }, { @@ -205,8 +205,8 @@ ] }, "then": { - "en": " Chademo", - "nl": " " + "en": "

Chademo

", + "nl": "

" }, "hideInAnswer": true }, @@ -214,8 +214,8 @@ "if": "socket:type1_cable=1", "ifnot": "socket:type1_cable=", "then": { - "en": " Type 1 with cable (J1772)", - "nl": " Type 1 met kabel (J1772)" + "en": "

Type 1 with cable (J1772)

", + "nl": "

Type 1 met kabel (J1772)

" } }, { @@ -226,8 +226,8 @@ ] }, "then": { - "en": " Type 1 with cable (J1772)", - "nl": " Type 1 met kabel (J1772)" + "en": "

Type 1 with cable (J1772)

", + "nl": "

Type 1 met kabel (J1772)

" }, "hideInAnswer": true }, @@ -235,8 +235,8 @@ "if": "socket:type1=1", "ifnot": "socket:type1=", "then": { - "en": " Type 1 without cable (J1772)", - "nl": " Type 1 zonder kabel (J1772)" + "en": "

Type 1 without cable (J1772)

", + "nl": "

Type 1 zonder kabel (J1772)

" } }, { @@ -247,8 +247,8 @@ ] }, "then": { - "en": " Type 1 without cable (J1772)", - "nl": " Type 1 zonder kabel (J1772)" + "en": "

Type 1 without cable (J1772)

", + "nl": "

Type 1 zonder kabel (J1772)

" }, "hideInAnswer": true }, @@ -256,8 +256,8 @@ "if": "socket:type1_combo=1", "ifnot": "socket:type1_combo=", "then": { - "en": " Type 1 CCS (aka Type 1 Combo)", - "nl": " " + "en": "

Type 1 CCS (aka Type 1 Combo)

", + "nl": "

" } }, { @@ -268,8 +268,8 @@ ] }, "then": { - "en": " Type 1 CCS (aka Type 1 Combo)", - "nl": " " + "en": "

Type 1 CCS (aka Type 1 Combo)

", + "nl": "

" }, "hideInAnswer": true }, @@ -277,8 +277,8 @@ "if": "socket:tesla_supercharger=1", "ifnot": "socket:tesla_supercharger=", "then": { - "en": " Tesla Supercharger", - "nl": " " + "en": "

Tesla Supercharger

", + "nl": "

" } }, { @@ -289,8 +289,8 @@ ] }, "then": { - "en": " Tesla Supercharger", - "nl": " " + "en": "

Tesla Supercharger

", + "nl": "

" }, "hideInAnswer": true }, @@ -298,8 +298,8 @@ "if": "socket:type2=1", "ifnot": "socket:type2=", "then": { - "en": " Type 2 (mennekes)", - "nl": " " + "en": "

Type 2 (mennekes)

", + "nl": "

" } }, { @@ -310,8 +310,8 @@ ] }, "then": { - "en": " Type 2 (mennekes)", - "nl": " " + "en": "

Type 2 (mennekes)

", + "nl": "

" }, "hideInAnswer": true }, @@ -319,8 +319,8 @@ "if": "socket:type2_combo=1", "ifnot": "socket:type2_combo=", "then": { - "en": " Type 2 CCS (mennekes)", - "nl": " " + "en": "

Type 2 CCS (mennekes)

", + "nl": "

" } }, { @@ -331,8 +331,8 @@ ] }, "then": { - "en": " Type 2 CCS (mennekes)", - "nl": " " + "en": "

Type 2 CCS (mennekes)

", + "nl": "

" }, "hideInAnswer": true } @@ -561,11 +561,11 @@ }, { "question": { - "en": "How much plugs of type Chademo are available here?", + "en": "How much plugs of type Chademo are available here?", "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" }, "render": { - "en": "There are Chademo plugs of type Chademo available here", + "en": "There are Chademo plugs of type Chademo available here", "nl": "Hier zijn stekkers van het type " }, "freeform": { @@ -581,11 +581,11 @@ }, { "question": { - "en": "What voltage do the plugs with Chademo offer?", + "en": "What voltage do the plugs with Chademo offer?", "nl": "Welke spanning levert de stekker van type " }, "render": { - "en": "Chademo outputs {socket:chademo:voltage} volt", + "en": "Chademo outputs {socket:chademo:voltage} volt", "nl": " heeft een spanning van {socket:chademo:voltage} volt" }, "freeform": { @@ -596,7 +596,7 @@ { "if": "socket:socket:chademo:voltage=500 V", "then": { - "en": "Chademo outputs 500 volt", + "en": "Chademo outputs 500 volt", "nl": " heeft een spanning van 500 volt" } } @@ -610,11 +610,11 @@ }, { "question": { - "en": "What current do the plugs with Chademo offer?", + "en": "What current do the plugs with Chademo offer?", "nl": "Welke stroom levert de stekker van type ?" }, "render": { - "en": "Chademo outputs at most {socket:chademo:current}A", + "en": "Chademo outputs at most {socket:chademo:current}A", "nl": " levert een stroom van maximaal {socket:chademo:current}A" }, "freeform": { @@ -625,7 +625,7 @@ { "if": "socket:socket:chademo:current=120 A", "then": { - "en": "Chademo outputs at most 120 A", + "en": "Chademo outputs at most 120 A", "nl": " levert een stroom van maximaal 120 A" } } @@ -639,11 +639,11 @@ }, { "question": { - "en": "What power output does a single plug of type Chademo offer?", + "en": "What power output does a single plug of type Chademo offer?", "nl": "Welk vermogen levert een enkele stekker van type ?" }, "render": { - "en": "Chademo outputs at most {socket:chademo:output}", + "en": "Chademo outputs at most {socket:chademo:output}", "nl": " levert een vermogen van maximaal {socket:chademo:output}" }, "freeform": { @@ -654,7 +654,7 @@ { "if": "socket:socket:chademo:output=50 kw", "then": { - "en": "Chademo outputs at most 50 kw", + "en": "Chademo outputs at most 50 kw", "nl": " levert een vermogen van maximaal 50 kw" } } @@ -1066,11 +1066,11 @@ }, { "question": { - "en": "How much plugs of type Tesla Supercharger are available here?", + "en": "How much plugs of type Tesla Supercharger are available here?", "nl": "Hoeveel stekkers van type heeft dit oplaadpunt?" }, "render": { - "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", + "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", "nl": "Hier zijn stekkers van het type " }, "freeform": { @@ -1086,11 +1086,11 @@ }, { "question": { - "en": "What voltage do the plugs with Tesla Supercharger offer?", + "en": "What voltage do the plugs with Tesla Supercharger offer?", "nl": "Welke spanning levert de stekker van type " }, "render": { - "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", + "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", "nl": " heeft een spanning van {socket:tesla_supercharger:voltage} volt" }, "freeform": { @@ -1101,7 +1101,7 @@ { "if": "socket:socket:tesla_supercharger:voltage=480 V", "then": { - "en": "Tesla Supercharger outputs 480 volt", + "en": "Tesla Supercharger outputs 480 volt", "nl": " heeft een spanning van 480 volt" } } @@ -1115,11 +1115,11 @@ }, { "question": { - "en": "What current do the plugs with Tesla Supercharger offer?", + "en": "What current do the plugs with Tesla Supercharger offer?", "nl": "Welke stroom levert de stekker van type ?" }, "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", "nl": " levert een stroom van maximaal {socket:tesla_supercharger:current}A" }, "freeform": { @@ -1130,14 +1130,14 @@ { "if": "socket:socket:tesla_supercharger:current=125 A", "then": { - "en": "Tesla Supercharger outputs at most 125 A", + "en": "Tesla Supercharger outputs at most 125 A", "nl": " levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:tesla_supercharger:current=350 A", "then": { - "en": "Tesla Supercharger outputs at most 350 A", + "en": "Tesla Supercharger outputs at most 350 A", "nl": " levert een stroom van maximaal 350 A" } } @@ -1151,11 +1151,11 @@ }, { "question": { - "en": "What power output does a single plug of type Tesla Supercharger offer?", + "en": "What power output does a single plug of type Tesla Supercharger offer?", "nl": "Welk vermogen levert een enkele stekker van type ?" }, "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", "nl": " levert een vermogen van maximaal {socket:tesla_supercharger:output}" }, "freeform": { @@ -1166,21 +1166,21 @@ { "if": "socket:socket:tesla_supercharger:output=120 kw", "then": { - "en": "Tesla Supercharger outputs at most 120 kw", + "en": "Tesla Supercharger outputs at most 120 kw", "nl": " levert een vermogen van maximaal 120 kw" } }, { "if": "socket:socket:tesla_supercharger:output=150 kw", "then": { - "en": "Tesla Supercharger outputs at most 150 kw", + "en": "Tesla Supercharger outputs at most 150 kw", "nl": " levert een vermogen van maximaal 150 kw" } }, { "if": "socket:socket:tesla_supercharger:output=250 kw", "then": { - "en": "Tesla Supercharger outputs at most 250 kw", + "en": "Tesla Supercharger outputs at most 250 kw", "nl": " levert een vermogen van maximaal 250 kw" } } @@ -1970,7 +1970,7 @@ }, { "question": { - "en": "Has a Chademo connector", + "en": "Has a Chademo connector", "nl": "Heeft een " }, "osmTags": "socket:chademo~*" @@ -1998,7 +1998,7 @@ }, { "question": { - "en": "Has a Tesla Supercharger connector", + "en": "Has a Tesla Supercharger connector", "nl": "Heeft een " }, "osmTags": "socket:tesla_supercharger~*" @@ -2180,4 +2180,4 @@ "eraseInvalidValues": true } ] -} +} \ No newline at end of file diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 1f7b87e18..a4f297283 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -912,6 +912,10 @@ video { margin-right: 0px; } +.mb-1 { + margin-bottom: 0.25rem; +} + .mb-4 { margin-bottom: 1rem; } @@ -1152,14 +1156,14 @@ video { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } -.cursor-wait { - cursor: wait; -} - .cursor-pointer { cursor: pointer; } +.cursor-wait { + cursor: wait; +} + .resize { resize: both; } diff --git a/langs/layers/en.json b/langs/layers/en.json index a6472bf0f..3a55d9b00 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -903,7 +903,7 @@ "question": "Has a European wall plug with ground pin (CEE7/4 type E) connector" }, "3": { - "question": "Has a Chademo connector" + "question": "Has a Chademo connector" }, "4": { "question": "Has a Type 1 with cable (J1772) connector" @@ -915,7 +915,7 @@ "question": "Has a Type 1 CCS (aka Type 1 Combo) connector" }, "7": { - "question": "Has a Tesla Supercharger connector" + "question": "Has a Tesla Supercharger connector" }, "8": { "question": "Has a Type 2 (mennekes) connector" @@ -960,58 +960,58 @@ "4": { "mappings": { "0": { - "then": " Schuko wall plug without ground pin (CEE7/4 type F)" + "then": "

Schuko wall plug without ground pin (CEE7/4 type F)

" }, "1": { - "then": " Schuko wall plug without ground pin (CEE7/4 type F)" + "then": "

Schuko wall plug without ground pin (CEE7/4 type F)

" }, "2": { - "then": " European wall plug with ground pin (CEE7/4 type E)" + "then": "

European wall plug with ground pin (CEE7/4 type E)

" }, "3": { - "then": " European wall plug with ground pin (CEE7/4 type E)" + "then": "

European wall plug with ground pin (CEE7/4 type E)

" }, "4": { - "then": " Chademo" + "then": "

Chademo

" }, "5": { - "then": " Chademo" + "then": "

Chademo

" }, "6": { - "then": " Type 1 with cable (J1772)" + "then": "

Type 1 with cable (J1772)

" }, "7": { - "then": " Type 1 with cable (J1772)" + "then": "

Type 1 with cable (J1772)

" }, "8": { - "then": " Type 1 without cable (J1772)" + "then": "

Type 1 without cable (J1772)

" }, "9": { - "then": " Type 1 without cable (J1772)" + "then": "

Type 1 without cable (J1772)

" }, "10": { - "then": " Type 1 CCS (aka Type 1 Combo)" + "then": "

Type 1 CCS (aka Type 1 Combo)

" }, "11": { - "then": " Type 1 CCS (aka Type 1 Combo)" + "then": "

Type 1 CCS (aka Type 1 Combo)

" }, "12": { - "then": " Tesla Supercharger" + "then": "

Tesla Supercharger

" }, "13": { - "then": " Tesla Supercharger" + "then": "

Tesla Supercharger

" }, "14": { - "then": " Type 2 (mennekes)" + "then": "

Type 2 (mennekes)

" }, "15": { - "then": " Type 2 (mennekes)" + "then": "

Type 2 (mennekes)

" }, "16": { - "then": " Type 2 CCS (mennekes)" + "then": "

Type 2 CCS (mennekes)

" }, "17": { - "then": " Type 2 CCS (mennekes)" + "then": "

Type 2 CCS (mennekes)

" } }, "question": "Which charging stations are available here?" @@ -1082,35 +1082,35 @@ "render": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}" }, "13": { - "question": "How much plugs of type Chademo are available here?", - "render": "There are Chademo plugs of type Chademo available here" + "question": "How much plugs of type Chademo are available here?", + "render": "There are Chademo plugs of type Chademo available here" }, "14": { "mappings": { "0": { - "then": "Chademo outputs 500 volt" + "then": "Chademo outputs 500 volt" } }, - "question": "What voltage do the plugs with Chademo offer?", - "render": "Chademo outputs {socket:chademo:voltage} volt" + "question": "What voltage do the plugs with Chademo offer?", + "render": "Chademo outputs {socket:chademo:voltage} volt" }, "15": { "mappings": { "0": { - "then": "Chademo outputs at most 120 A" + "then": "Chademo outputs at most 120 A" } }, - "question": "What current do the plugs with Chademo offer?", - "render": "Chademo outputs at most {socket:chademo:current}A" + "question": "What current do the plugs with Chademo offer?", + "render": "Chademo outputs at most {socket:chademo:current}A" }, "16": { "mappings": { "0": { - "then": "Chademo outputs at most 50 kw" + "then": "Chademo outputs at most 50 kw" } }, - "question": "What power output does a single plug of type Chademo offer?", - "render": "Chademo outputs at most {socket:chademo:output}" + "question": "What power output does a single plug of type Chademo offer?", + "render": "Chademo outputs at most {socket:chademo:output}" }, "17": { "question": "How much plugs of type Type 1 with cable (J1772) are available here?", @@ -1239,44 +1239,44 @@ "render": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}" }, "29": { - "question": "How much plugs of type Tesla Supercharger are available here?", - "render": "There are Tesla Supercharger plugs of type Tesla Supercharger available here" + "question": "How much plugs of type Tesla Supercharger are available here?", + "render": "There are Tesla Supercharger plugs of type Tesla Supercharger available here" }, "30": { "mappings": { "0": { - "then": "Tesla Supercharger outputs 480 volt" + "then": "Tesla Supercharger outputs 480 volt" } }, - "question": "What voltage do the plugs with Tesla Supercharger offer?", - "render": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt" + "question": "What voltage do the plugs with Tesla Supercharger offer?", + "render": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt" }, "31": { "mappings": { "0": { - "then": "Tesla Supercharger outputs at most 125 A" + "then": "Tesla Supercharger outputs at most 125 A" }, "1": { - "then": "Tesla Supercharger outputs at most 350 A" + "then": "Tesla Supercharger outputs at most 350 A" } }, - "question": "What current do the plugs with Tesla Supercharger offer?", - "render": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A" + "question": "What current do the plugs with Tesla Supercharger offer?", + "render": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A" }, "32": { "mappings": { "0": { - "then": "Tesla Supercharger outputs at most 120 kw" + "then": "Tesla Supercharger outputs at most 120 kw" }, "1": { - "then": "Tesla Supercharger outputs at most 150 kw" + "then": "Tesla Supercharger outputs at most 150 kw" }, "2": { - "then": "Tesla Supercharger outputs at most 250 kw" + "then": "Tesla Supercharger outputs at most 250 kw" } }, - "question": "What power output does a single plug of type Tesla Supercharger offer?", - "render": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}" + "question": "What power output does a single plug of type Tesla Supercharger offer?", + "render": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}" }, "33": { "question": "How much plugs of type Type 2 (mennekes) are available here?", @@ -3383,4 +3383,4 @@ "watermill": { "name": "Watermill" } -} +} \ No newline at end of file diff --git a/langs/layers/nl.json b/langs/layers/nl.json index a4be84c4f..97668f8ab 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -1036,58 +1036,58 @@ "4": { "mappings": { "0": { - "then": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "then": "

Schuko stekker zonder aardingspin (CEE7/4 type F)

" }, "1": { - "then": " Schuko stekker zonder aardingspin (CEE7/4 type F)" + "then": "

Schuko stekker zonder aardingspin (CEE7/4 type F)

" }, "2": { - "then": " Europese stekker met aardingspin (CEE7/4 type E)" + "then": "

Europese stekker met aardingspin (CEE7/4 type E)

" }, "3": { - "then": " Europese stekker met aardingspin (CEE7/4 type E)" + "then": "

Europese stekker met aardingspin (CEE7/4 type E)

" }, "4": { - "then": " " + "then": "

" }, "5": { - "then": " " + "then": "

" }, "6": { - "then": " Type 1 met kabel (J1772)" + "then": "

Type 1 met kabel (J1772)

" }, "7": { - "then": " Type 1 met kabel (J1772)" + "then": "

Type 1 met kabel (J1772)

" }, "8": { - "then": " Type 1 zonder kabel (J1772)" + "then": "

Type 1 zonder kabel (J1772)

" }, "9": { - "then": " Type 1 zonder kabel (J1772)" + "then": "

Type 1 zonder kabel (J1772)

" }, "10": { - "then": " " + "then": "

" }, "11": { - "then": " " + "then": "

" }, "12": { - "then": " " + "then": "

" }, "13": { - "then": " " + "then": "

" }, "14": { - "then": " " + "then": "

" }, "15": { - "then": " " + "then": "

" }, "16": { - "then": " " + "then": "

" }, "17": { - "then": " " + "then": "

" } } }, @@ -3781,4 +3781,4 @@ "render": "Watermolens" } } -} +} \ No newline at end of file From 79031e89924fa052dcdb712e2a9c3f617ad57716 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 00:06:34 +0200 Subject: [PATCH 068/110] Use node 16 in github actions, remove broken 'pull_request_check' github action --- .github/workflows/deploy_pietervdvn.yml | 2 +- .github/workflows/pull_request_check.yml | 71 ------------------- .../workflows/theme_validation_and_deploy.yml | 2 +- 3 files changed, 2 insertions(+), 73 deletions(-) delete mode 100644 .github/workflows/pull_request_check.yml diff --git a/.github/workflows/deploy_pietervdvn.yml b/.github/workflows/deploy_pietervdvn.yml index 54b89561a..dfa564468 100644 --- a/.github/workflows/deploy_pietervdvn.yml +++ b/.github/workflows/deploy_pietervdvn.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v1.2.0 with: - node-version: '15' + node-version: '16' env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' diff --git a/.github/workflows/pull_request_check.yml b/.github/workflows/pull_request_check.yml deleted file mode 100644 index 27165f776..000000000 --- a/.github/workflows/pull_request_check.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Pull request check -on: - pull_request_target: - types: [ opened, edited, synchronize, ready_for_review, review_requested ] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - uses: actions/checkout@v2 - - name: Set up Node.js - uses: actions/setup-node@v1.2.0 - env: - ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' - - - name: install deps - run: npm ci - - - name: create generated dir - run: mkdir ./assets/generated - - - name: create stub themes - run: "echo '{\"layers\": [], \"themes\": []}' > ./assets/generated/known_layers_and_themes.json" - - - name: generate assets - run: npm run generate:images - - - name: generate translations - run: npm run generate:translations - - - name: Compile license info - run: npm run generate:licenses - - - name: Compile and validate themes and layers - run: npm run validate:layeroverview - - - name: Validate license info - run: npm run validate:licenses - - - name: Set failure key - run: | - ls - if [[ -f "layer_report.txt" || -f "missing_licenses.txt" ]]; then - echo "Found a report..." - echo "VALIDATION_FAILED=true" >> $GITHUB_ENV - else - echo "VALIDATION_FAILED=false" >> $GITHUB_ENV - fi - - - name: Test variable - run: echo "${{ env.VALIDATION_FAILED }}" - - - name: Archive reports - uses: actions/upload-artifact@v2 - if: >- - env.VALIDATION_FAILED == 'true' - with: - name: reports - path: | - layer_report.txt - missing_licenses.txt - - - name: Comment PR - uses: allthatjazzleo/actions-pull-request-add-comment@master - if: >- - env.VALIDATION_FAILED == 'true' - with: - message: "cat layer_report.txt missing_licenses.txt" - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/theme_validation_and_deploy.yml b/.github/workflows/theme_validation_and_deploy.yml index 06c0b7fdf..fd7efe0d8 100644 --- a/.github/workflows/theme_validation_and_deploy.yml +++ b/.github/workflows/theme_validation_and_deploy.yml @@ -13,7 +13,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v1.2.0 with: - node-version: '15' + node-version: '16' env: ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' From 04a2f91b36e81a26e361a2a26c3026fd9bdae4ea Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 00:23:44 +0200 Subject: [PATCH 069/110] Fix small styling issue with tabbed component --- UI/Base/TabbedComponent.ts | 2 +- css/index-tailwind-output.css | 102 +++++++++++++++++++++++++++++++--- 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/UI/Base/TabbedComponent.ts b/UI/Base/TabbedComponent.ts index b5a33871a..8f71a462a 100644 --- a/UI/Base/TabbedComponent.ts +++ b/UI/Base/TabbedComponent.ts @@ -31,7 +31,7 @@ export class TabbedComponent extends Combine { tabs.push(tab) } - const header = new Combine(tabs).SetClass("block tabs-header-bar") + const header = new Combine(tabs).SetClass("tabs-header-bar") const actualContent = new VariableUiElement( openedTabSrc.map(i => contentElements[i]) ) diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index e3d669e7c..568e2072f 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -1322,6 +1322,11 @@ video { background-color: rgba(156, 163, 175, var(--tw-bg-opacity)); } +.bg-indigo-100 { + --tw-bg-opacity: 1; + background-color: rgba(224, 231, 255, var(--tw-bg-opacity)); +} + .bg-gray-300 { --tw-bg-opacity: 1; background-color: rgba(209, 213, 219, var(--tw-bg-opacity)); @@ -1337,11 +1342,6 @@ video { background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); } -.bg-indigo-100 { - --tw-bg-opacity: 1; - background-color: rgba(224, 231, 255, var(--tw-bg-opacity)); -} - .bg-gray-100 { --tw-bg-opacity: 1; background-color: rgba(243, 244, 246, var(--tw-bg-opacity)); @@ -1573,10 +1573,6 @@ video { text-decoration: underline; } -.line-through { - text-decoration: line-through; -} - .opacity-0 { opacity: 0; } @@ -1992,6 +1988,8 @@ li::marker { } @-webkit-keyframes slide { + /* This is the animation on the marker to add a new point - it slides through all the possible presets */ + from { transform: translateX(0%); } @@ -2002,6 +2000,8 @@ li::marker { } @keyframes slide { + /* This is the animation on the marker to add a new point - it slides through all the possible presets */ + from { transform: translateX(0%); } @@ -2011,6 +2011,90 @@ li::marker { } } +.hand-drag-animation { + -webkit-animation: hand-drag-animation 6s ease-in-out infinite; + animation: hand-drag-animation 6s ease-in-out infinite; + transform-origin: 50% 125%; +} + +@-webkit-keyframes hand-drag-animation { + /* This is the animation on the little extra hand on the location input. If fades in, invites the user to interact/drag the map */ + + 0% { + opacity: 0; + transform: rotate(-30deg); + } + + 6% { + opacity: 1; + transform: rotate(-30deg); + } + + 12% { + opacity: 1; + transform: rotate(-45deg); + } + + 24% { + opacity: 1; + transform: rotate(-00deg); + } + + 30% { + opacity: 1; + transform: rotate(-30deg); + } + + 36% { + opacity: 0; + transform: rotate(-30deg); + } + + 100% { + opacity: 0; + transform: rotate(-30deg); + } +} + +@keyframes hand-drag-animation { + /* This is the animation on the little extra hand on the location input. If fades in, invites the user to interact/drag the map */ + + 0% { + opacity: 0; + transform: rotate(-30deg); + } + + 6% { + opacity: 1; + transform: rotate(-30deg); + } + + 12% { + opacity: 1; + transform: rotate(-45deg); + } + + 24% { + opacity: 1; + transform: rotate(-00deg); + } + + 30% { + opacity: 1; + transform: rotate(-30deg); + } + + 36% { + opacity: 0; + transform: rotate(-30deg); + } + + 100% { + opacity: 0; + transform: rotate(-30deg); + } +} + /**************************************/ #topleft-tools { From 90a52fa47a531f6009e3d73f9a29fcea734103ef Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 00:37:44 +0200 Subject: [PATCH 070/110] Reset translations and charging station layer after merge --- .../charging_station/charging_station.json | 6412 ++++++++--------- css/index-tailwind-output.css | 16 +- langs/layers/en.json | 128 +- langs/layers/nl.json | 340 +- 4 files changed, 3487 insertions(+), 3409 deletions(-) diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 5a8385357..a643a8025 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,3233 +1,3233 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "it": "Stazioni di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjoner", - "ru": "Зарядные станции", - "zh_Hant": "充電站" - }, - "minzoom": 10, - "source": { - "osmTags": { - "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" + "id": "charging_station", + "name": { + "en": "Charging stations", + "it": "Stazioni di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjoner", + "ru": "Зарядные станции", + "zh_Hant": "充電站" + }, + "minzoom": 10, + "source": { + "osmTags": { + "or": [ + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" + ] + } + }, + "title": { + "render": { + "en": "Charging station", + "it": "Stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + } + }, + "description": { + "en": "A charging station", + "it": "Una stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "En ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + }, + "calculatedTags": [ + "motorcar=feat.properties.motorcar ?? feat.properties.car" + ], + "tagRenderings": [ + "images", + { + "id": "Type", + "question": { + "en": "Which vehicles are allowed to charge here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "bicycles can be charged here" + } + }, + { + "if": "motorcar=yes", + "extraTags": "car=", + "ifnot": { + "and": [ + "car=", + "motorcar=no" ] + }, + "then": { + "en": "Cars can be charged here" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here" + } } + ] }, - "title": { - "render": { - "en": "Charging station", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } - }, - "description": { - "en": "A charging station", - "it": "Una stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "En ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - }, - "calculatedTags": [ - "motorcar=feat.properties.motorcar ?? feat.properties.car" - ], - "tagRenderings": [ - "images", - { - "id": "Type", - "question": { - "en": "Which vehicles are allowed to charge here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "bicycle=yes", - "ifnot": "bicycle=no", - "then": { - "en": "bicycles can be charged here" - } - }, - { - "if": "motorcar=yes", - "extraTags": "car=", - "ifnot": { - "and": [ - "car=", - "motorcar=no" - ] - }, - "then": { - "en": "Cars can be charged here" - } - }, - { - "if": "scooter=yes", - "ifnot": "scooter=no", - "then": { - "en": "Scooters can be charged here" - } - }, - { - "if": "hgv=yes", - "ifnot": "hgv=no", - "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here" - } - }, - { - "if": "bus=yes", - "ifnot": "bus=no", - "then": { - "en": "Buses can be charged here" - } - } - ] - }, - { - "id": "access", - "question": { - "en": "Who is allowed to use this charging station?" - }, - "render": { - "en": "Access is {access}" - }, - "freeform": { - "key": "access", - "addExtraTags": [ - "fixme=Freeform field used for access - doublecheck the value" - ] - }, - "mappings": [ - { - "if": "access=yes", - "then": "Anyone can use this charging station (payment might be needed)" - }, - { - "if": { - "or": [ - "access=permissive", - "access=public" - ] - }, - "then": "Anyone can use this charging station (payment might be needed)", - "hideInAnswer": true - }, - { - "if": "access=customers", - "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " - }, - { - "if": "access=private", - "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" - } - ] - }, - { - "id": "capacity", - "render": { - "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" - }, - "question": { - "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" - }, - "freeform": { - "key": "capacity", - "type": "pnat" - } - }, - { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F) Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F) European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E) European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E) Chademo
", - "nl": "
Chademo Chademo
", - "nl": "
Chademo Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772) Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772) Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772) Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772) Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo) Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo) Tesla Supercharger
", - "nl": "
Tesla Supercharger Tesla Supercharger
", - "nl": "
Tesla Supercharger Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes) Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes) Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes) Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes) Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772) Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772) Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo) Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo) Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination) Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination) Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te ladenSchuko wall plug without ground pin (CEE7/4 type F) are available here?", - "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", - "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "voltage-0", - "question": { - "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" - }, - "freeform": { - "key": "socket:schuko:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:voltage=230 V", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "current-0", - "question": { - "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" - }, - "freeform": { - "key": "socket:schuko:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:current=16 A", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "power-output-0", - "question": { - "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" - }, - "freeform": { - "key": "socket:schuko:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:output=3.6 kw", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", - "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", - "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "voltage-1", - "question": { - "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" - }, - "freeform": { - "key": "socket:typee:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:voltage=230 V", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "current-1", - "question": { - "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" - }, - "freeform": { - "key": "socket:typee:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:current=16 A", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "power-output-1", - "question": { - "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" - }, - "freeform": { - "key": "socket:typee:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:output=3 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" - } - }, - { - "if": "socket:socket:typee:output=22 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type Chademo are available here?", - "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Chademo plugs of type Chademo available here", - "nl": "Hier zijn Chademo stekkers van het type Chademo" - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "voltage-2", - "question": { - "en": "What voltage do the plugs with Chademo offer?", - "nl": "Welke spanning levert de stekker van type Chademo " - }, - "render": { - "en": "Chademo outputs {socket:chademo:voltage} volt", - "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" - }, - "freeform": { - "key": "socket:chademo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:voltage=500 V", - "then": { - "en": "Chademo outputs 500 volt", - "nl": "Chademo heeft een spanning van 500 volt" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "current-2", - "question": { - "en": "What current do the plugs with Chademo offer?", - "nl": "Welke stroom levert de stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:current}A", - "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" - }, - "freeform": { - "key": "socket:chademo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:current=120 A", - "then": { - "en": "Chademo outputs at most 120 A", - "nl": "Chademo levert een stroom van maximaal 120 A" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "power-output-2", - "question": { - "en": "What power output does a single plug of type Chademo offer?", - "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:output}", - "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" - }, - "freeform": { - "key": "socket:chademo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:output=50 kw", - "then": { - "en": "Chademo outputs at most 50 kw", - "nl": "Chademo levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type Type 1 with cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", - "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "voltage-3", - "question": { - "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " - }, - "render": { - "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type1_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:voltage=200 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 200 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1_cable:voltage=240 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 240 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "current-3", - "question": { - "en": "What current do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "freeform": { - "key": "socket:type1_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:current=32 A", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 32 A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "power-output-3", - "question": { - "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "freeform": { - "key": "socket:type1_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:output=3.7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1_cable:output=7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type Type 1 without cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", - "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "voltage-4", - "question": { - "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " - }, - "render": { - "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" - }, - "freeform": { - "key": "socket:type1:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:voltage=200 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 200 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1:voltage=240 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 240 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "current-4", - "question": { - "en": "What current do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" - }, - "freeform": { - "key": "socket:type1:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:current=32 A", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 32 A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "power-output-4", - "question": { - "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" - }, - "freeform": { - "key": "socket:type1:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:output=3.7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1:output=6.6 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" - } - }, - { - "if": "socket:socket:type1:output=7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" - } - }, - { - "if": "socket:socket:type1:output=7.2 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", - "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", - "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "voltage-5", - "question": { - "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type1_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:voltage=400 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" - } - }, - { - "if": "socket:socket:type1_combo:voltage=1000 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "current-5", - "question": { - "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "freeform": { - "key": "socket:type1_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:current=50 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" - } - }, - { - "if": "socket:socket:type1_combo:current=125 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "power-output-5", - "question": { - "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "freeform": { - "key": "socket:type1_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:output=50 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=62.5 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=150 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=350 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type Tesla Supercharger are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", - "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "voltage-6", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger " - }, - "render": { - "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", - "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:voltage=480 V", - "then": { - "en": "Tesla Supercharger outputs 480 volt", - "nl": "Tesla Supercharger heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "current-6", - "question": { - "en": "What current do the plugs with Tesla Supercharger offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", - "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:current=125 A", - "then": { - "en": "Tesla Supercharger outputs at most 125 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger:current=350 A", - "then": { - "en": "Tesla Supercharger outputs at most 350 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "power-output-6", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", - "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:output=120 kw", - "then": { - "en": "Tesla Supercharger outputs at most 120 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=150 kw", - "then": { - "en": "Tesla Supercharger outputs at most 150 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=250 kw", - "then": { - "en": "Tesla Supercharger outputs at most 250 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type Type 2 (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", - "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "voltage-7", - "question": { - "en": "What voltage do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " - }, - "render": { - "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", - "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" - }, - "freeform": { - "key": "socket:type2:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:voltage=230 V", - "then": { - "en": "Type 2 (mennekes) outputs 230 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2:voltage=400 V", - "then": { - "en": "Type 2 (mennekes) outputs 400 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "current-7", - "question": { - "en": "What current do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" - }, - "freeform": { - "key": "socket:type2:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:current=16 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 16 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2:current=32 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 32 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "power-output-7", - "question": { - "en": "What power output does a single plug of type Type 2 (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" - }, - "freeform": { - "key": "socket:type2:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:output=11 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 11 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2:output=22 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 22 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", - "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "voltage-8", - "question": { - "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type2_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:voltage=500 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 500 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:type2_combo:voltage=920 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 920 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "current-8", - "question": { - "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "freeform": { - "key": "socket:type2_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:current=125 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 125 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:type2_combo:current=350 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 350 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "power-output-8", - "question": { - "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "freeform": { - "key": "socket:type2_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:output=50 kw", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 50 kw", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", - "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "voltage-9", - "question": { - "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type2_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:voltage=230 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 230 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2_cable:voltage=400 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 400 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "current-9", - "question": { - "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" - }, - "freeform": { - "key": "socket:type2_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:current=16 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 16 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2_cable:current=32 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 32 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "power-output-9", - "question": { - "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "freeform": { - "key": "socket:type2_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:output=11 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 11 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2_cable:output=22 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 22 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", - "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "voltage-10", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "current-10", - "question": { - "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:current=125 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:current=350 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "power-output-10", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type Tesla Supercharger (destination) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", - "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-11", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " - }, - "render": { - "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=480 V", - "then": { - "en": "Tesla Supercharger (destination) outputs 480 volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-11", - "question": { - "en": "What current do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=125 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 125 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=350 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 350 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-11", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=120 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 120 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=150 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 150 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=250 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 250 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", - "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", - "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-12", - "question": { - "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=230 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:tesla_destination:voltage=400 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-12", - "question": { - "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=16 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=32 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-12", - "question": { - "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=11 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=22 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-13", - "question": { - "en": "How much plugs of type USB to charge phones and small electronics are available here?", - "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" - }, - "render": { - "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", - "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "voltage-13", - "question": { - "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " - }, - "render": { - "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" - }, - "freeform": { - "key": "socket:USB-A:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:voltage=5 V", - "then": { - "en": "USB to charge phones and small electronics outputs 5 volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "current-13", - "question": { - "en": "What current do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" - }, - "freeform": { - "key": "socket:USB-A:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:current=1 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 1 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" - } - }, - { - "if": "socket:socket:USB-A:current=2 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 2 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "power-output-13", - "question": { - "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", - "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" - }, - "freeform": { - "key": "socket:USB-A:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:output=5w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 5w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" - } - }, - { - "if": "socket:socket:USB-A:output=10w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 10w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "Authentication", - "question": { - "en": "What kind of authentication is available at the charging station?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "No authentication is needed" - } - } - ] - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - }, - "it": { - "0": { - "then": "Non appartiene a una rete" - } - }, - "ja": { - "0": { - "then": "大規模な運営チェーンの一部ではない" - } - }, - "ru": { - "0": { - "then": "Не является частью более крупной сети" - } - }, - "zh_Hant": { - "0": { - "then": "不屬於大型網路" - } - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?" - }, - "mappings": [ - { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)" - } - } - ] - }, - { - "id": "fee/charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" - }, - "freeform": { - "key": "charge", - "addExtraTags": [ - "fee=yes" - ] - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no", - "charge=" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - } - } - ] - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { - "or": [ - "fee=yes", - "charge~*" - ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" - } - } - ] - } - }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" - }, - "mappings": [ - { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd" - } - } - ] - }, - { - "id": "Network", - "render": { - "en": "Part of the network {network}" - }, - "question": { - "en": "Is this charging station part of a network?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ - { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" - } - ] - }, - { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?" - }, - "render": { - "en": "This charging station is operated by {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "network:={operator}" - ] - }, - "then": { - "en": "Actually, {operator} is the network" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" - } - ] - }, - { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?" - }, - "render": { - "en": "In case of problems, call {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?" - }, - "render": { - "en": "In case of problems, send an email to {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?" - }, - "render": { - "en": "More info on {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?" - }, - "render": { - "en": "Reference number is {ref}" - }, - "freeform": { - "key": "ref" - } - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?" - }, - "mappings": [ - { - "if": "operational_status=broken", - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot" - } - }, - { - "if": { - "and": [ - "planned:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" - } - }, - { - "if": { - "and": [ - "construction:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" - } - }, - { - "if": { - "and": [ - "disused:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" - } - }, - { - "if": { - "and": [ - "amenity=charging_station", - "operational_status=" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" - } - } - ] - }, - { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging" - } - } - ] - } - ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } + { + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?" + }, + "render": { + "en": "Access is {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" ] + }, + "mappings": [ + { + "if": "access=yes", + "then": "Anyone can use this charging station (payment might be needed)" + }, + { + "if": { + "or": [ + "access=permissive", + "access=public" + ] + }, + "then": "Anyone can use this charging station (payment might be needed)", + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " + }, + { + "if": "access=private", + "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" + } + ] }, - "iconOverlays": [ + { + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" + } + }, + { + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging stations are available here?" + }, + "multiAnswer": true, + "mappings": [ { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross_bottom_right:#c22;" + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" + ] + } }, { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true + "if": { + "and": [ + "socket:schuko~*", + "socket:schuko!=1" + ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true }, { - "if": { + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } + }, + { + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" + ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" + ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" + ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "or": [ + "_country!=us" + ] + }, + { "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true - } - ], - "width": { - "render": "8" - }, - "iconSize": { - "render": "50,50,bottom" - }, - "color": { - "render": "#00f" - }, - "presets": [ + } + ] + } + }, { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "Charging station" - }, - "preciseInput": { - "preferredBackground": "map" + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "or": [ + "_country=us" + ] + }, + { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + }, + "hideInAnswer": true + } + ] + }, + { + "id": "plugs-0", + "question": { + "en": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", + "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", + "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "voltage-0", + "question": { + "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" + }, + "freeform": { + "key": "socket:schuko:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:voltage=230 V", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "current-0", + "question": { + "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" + }, + "freeform": { + "key": "socket:schuko:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:current=16 A", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "power-output-0", + "question": { + "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" + }, + "freeform": { + "key": "socket:schuko:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:output=3.6 kw", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", + "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", + "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "voltage-1", + "question": { + "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" + }, + "freeform": { + "key": "socket:typee:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:voltage=230 V", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "current-1", + "question": { + "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" + }, + "freeform": { + "key": "socket:typee:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:current=16 A", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "power-output-1", + "question": { + "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" + }, + "freeform": { + "key": "socket:typee:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:output=3 kw", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" + } + }, + { + "if": "socket:socket:typee:output=22 kw", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type Chademo are available here?", + "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Chademo plugs of type Chademo available here", + "nl": "Hier zijn Chademo stekkers van het type Chademo" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "voltage-2", + "question": { + "en": "What voltage do the plugs with Chademo offer?", + "nl": "Welke spanning levert de stekker van type Chademo " + }, + "render": { + "en": "Chademo outputs {socket:chademo:voltage} volt", + "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" + }, + "freeform": { + "key": "socket:chademo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:voltage=500 V", + "then": { + "en": "Chademo outputs 500 volt", + "nl": "Chademo heeft een spanning van 500 volt" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "current-2", + "question": { + "en": "What current do the plugs with Chademo offer?", + "nl": "Welke stroom levert de stekker van type Chademo ?" + }, + "render": { + "en": "Chademo outputs at most {socket:chademo:current}A", + "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" + }, + "freeform": { + "key": "socket:chademo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:current=120 A", + "then": { + "en": "Chademo outputs at most 120 A", + "nl": "Chademo levert een stroom van maximaal 120 A" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "power-output-2", + "question": { + "en": "What power output does a single plug of type Chademo offer?", + "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" + }, + "render": { + "en": "Chademo outputs at most {socket:chademo:output}", + "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" + }, + "freeform": { + "key": "socket:chademo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:output=50 kw", + "then": { + "en": "Chademo outputs at most 50 kw", + "nl": "Chademo levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type Type 1 with cable (J1772) are available here?", + "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", + "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "voltage-3", + "question": { + "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " + }, + "render": { + "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type1_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:voltage=200 V", + "then": { + "en": "Type 1 with cable (J1772) outputs 200 volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1_cable:voltage=240 V", + "then": { + "en": "Type 1 with cable (J1772) outputs 240 volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "current-3", + "question": { + "en": "What current do the plugs with Type 1 with cable (J1772) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" + }, + "render": { + "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", + "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" + }, + "freeform": { + "key": "socket:type1_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:current=32 A", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 32 A", + "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "power-output-3", + "question": { + "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" + }, + "render": { + "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" + }, + "freeform": { + "key": "socket:type1_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:output=3.7 kw", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1_cable:output=7 kw", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 7 kw", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type Type 1 without cable (J1772) are available here?", + "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", + "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "voltage-4", + "question": { + "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " + }, + "render": { + "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" + }, + "freeform": { + "key": "socket:type1:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:voltage=200 V", + "then": { + "en": "Type 1 without cable (J1772) outputs 200 volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1:voltage=240 V", + "then": { + "en": "Type 1 without cable (J1772) outputs 240 volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "current-4", + "question": { + "en": "What current do the plugs with Type 1 without cable (J1772) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" + }, + "render": { + "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", + "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" + }, + "freeform": { + "key": "socket:type1:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:current=32 A", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 32 A", + "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "power-output-4", + "question": { + "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" + }, + "render": { + "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" + }, + "freeform": { + "key": "socket:type1:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:output=3.7 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1:output=6.6 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" + } + }, + { + "if": "socket:socket:type1:output=7 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 7 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" + } + }, + { + "if": "socket:socket:type1:output=7.2 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", + "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", + "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "voltage-5", + "question": { + "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type1_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:voltage=400 V", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" + } + }, + { + "if": "socket:socket:type1_combo:voltage=1000 V", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "current-5", + "question": { + "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" + }, + "freeform": { + "key": "socket:type1_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:current=50 A", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" + } + }, + { + "if": "socket:socket:type1_combo:current=125 A", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "power-output-5", + "question": { + "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" + }, + "freeform": { + "key": "socket:type1_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:output=50 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=62.5 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=150 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=350 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type Tesla Supercharger are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", + "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "voltage-6", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger " + }, + "render": { + "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", + "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:voltage=480 V", + "then": { + "en": "Tesla Supercharger outputs 480 volt", + "nl": "Tesla Supercharger heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "current-6", + "question": { + "en": "What current do the plugs with Tesla Supercharger offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" + }, + "render": { + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", + "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:current=125 A", + "then": { + "en": "Tesla Supercharger outputs at most 125 A", + "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger:current=350 A", + "then": { + "en": "Tesla Supercharger outputs at most 350 A", + "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "power-output-6", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" + }, + "render": { + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", + "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:output=120 kw", + "then": { + "en": "Tesla Supercharger outputs at most 120 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=150 kw", + "then": { + "en": "Tesla Supercharger outputs at most 150 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=250 kw", + "then": { + "en": "Tesla Supercharger outputs at most 250 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type Type 2 (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", + "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "voltage-7", + "question": { + "en": "What voltage do the plugs with Type 2 (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " + }, + "render": { + "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", + "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" + }, + "freeform": { + "key": "socket:type2:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:voltage=230 V", + "then": { + "en": "Type 2 (mennekes) outputs 230 volt", + "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2:voltage=400 V", + "then": { + "en": "Type 2 (mennekes) outputs 400 volt", + "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "current-7", + "question": { + "en": "What current do the plugs with Type 2 (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" + }, + "render": { + "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" + }, + "freeform": { + "key": "socket:type2:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:current=16 A", + "then": { + "en": "Type 2 (mennekes) outputs at most 16 A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2:current=32 A", + "then": { + "en": "Type 2 (mennekes) outputs at most 32 A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "power-output-7", + "question": { + "en": "What power output does a single plug of type Type 2 (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" + }, + "render": { + "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" + }, + "freeform": { + "key": "socket:type2:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:output=11 kw", + "then": { + "en": "Type 2 (mennekes) outputs at most 11 kw", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2:output=22 kw", + "then": { + "en": "Type 2 (mennekes) outputs at most 22 kw", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", + "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "voltage-8", + "question": { + "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type2_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:voltage=500 V", + "then": { + "en": "Type 2 CCS (mennekes) outputs 500 volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:type2_combo:voltage=920 V", + "then": { + "en": "Type 2 CCS (mennekes) outputs 920 volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "current-8", + "question": { + "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" + }, + "freeform": { + "key": "socket:type2_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:current=125 A", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 125 A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:type2_combo:current=350 A", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 350 A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "power-output-8", + "question": { + "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" + }, + "freeform": { + "key": "socket:type2_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:output=50 kw", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 50 kw", + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", + "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "voltage-9", + "question": { + "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type2_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:voltage=230 V", + "then": { + "en": "Type 2 with cable (mennekes) outputs 230 volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2_cable:voltage=400 V", + "then": { + "en": "Type 2 with cable (mennekes) outputs 400 volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "current-9", + "question": { + "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" + }, + "freeform": { + "key": "socket:type2_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:current=16 A", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 16 A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2_cable:current=32 A", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 32 A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "power-output-9", + "question": { + "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" + }, + "freeform": { + "key": "socket:type2_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:output=11 kw", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 11 kw", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2_cable:output=22 kw", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 22 kw", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", + "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "voltage-10", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "current-10", + "question": { + "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:current=125 A", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:current=350 A", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "power-output-10", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type Tesla Supercharger (destination) are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", + "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-11", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " + }, + "render": { + "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", + "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=480 V", + "then": { + "en": "Tesla Supercharger (destination) outputs 480 volt", + "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-11", + "question": { + "en": "What current do the plugs with Tesla Supercharger (destination) offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" + }, + "render": { + "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=125 A", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 125 A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=350 A", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 350 A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-11", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" + }, + "render": { + "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=120 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 120 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=150 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 150 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=250 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 250 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", + "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", + "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-12", + "question": { + "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=230 V", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:tesla_destination:voltage=400 V", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-12", + "question": { + "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=16 A", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=32 A", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-12", + "question": { + "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=11 kw", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=22 kw", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type USB to charge phones and small electronics are available here?", + "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" + }, + "render": { + "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", + "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "voltage-13", + "question": { + "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " + }, + "render": { + "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" + }, + "freeform": { + "key": "socket:USB-A:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:voltage=5 V", + "then": { + "en": "USB to charge phones and small electronics outputs 5 volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "current-13", + "question": { + "en": "What current do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" + }, + "freeform": { + "key": "socket:USB-A:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:current=1 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 1 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" + } + }, + { + "if": "socket:socket:USB-A:current=2 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 2 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "power-output-13", + "question": { + "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", + "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" + }, + "freeform": { + "key": "socket:USB-A:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:output=5w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 5w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" + } + }, + { + "if": "socket:socket:USB-A:output=10w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 10w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "Authentication", + "question": { + "en": "What kind of authentication is available at the charging station?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "No authentication is needed" + } + } + ] + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + }, + "it": { + "0": { + "then": "Non appartiene a una rete" + } + }, + "ja": { + "0": { + "then": "大規模な運営チェーンの一部ではない" + } + }, + "ru": { + "0": { + "then": "Не является частью более крупной сети" + } + }, + "zh_Hant": { + "0": { + "then": "不屬於大型網路" + } + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)" + } + } + ] + }, + { + "id": "fee/charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" + }, + "freeform": { + "key": "charge", + "addExtraTags": [ + "fee=yes" + ] + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no", + "charge=" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + } + } + ] + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk" } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } + } + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" + } } - ], - "wayHandling": 1, - "filter": [ + ] + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}" + }, + "question": { + "en": "Is this charging station part of a network?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ { - "id": "vehicle-type", - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen" - } - }, - { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen" - }, - "osmTags": "bicycle=yes" - }, - { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" - ] - } - } - ] + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network" + } }, { - "id": "working", - "options": [ - { - "question": { - "en": "Only working charging stations" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" - ] - } - } - ] + "if": "network=none", + "then": { + "en": "Not part of a bigger network" + }, + "hideInAnswer": true }, { - "id": "connection_type", - "options": [ - { - "question": { - "en": "All connectors", - "nl": "Alle types" - } - }, - { - "question": { - "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", - "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", - "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a Chademo connector", - "nl": "Heeft een Chademo " - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a Type 1 with cable (J1772) connector", - "nl": "Heeft een Type 1 met kabel (J1772) " - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a Type 1 without cable (J1772) connector", - "nl": "Heeft een Type 1 zonder kabel (J1772) " - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", - "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger connector", - "nl": "Heeft een Tesla Supercharger " - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a Type 2 (mennekes) connector", - "nl": "Heeft een Type 2 (mennekes) " - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a Type 2 CCS (mennekes) connector", - "nl": "Heeft een Type 2 CCS (mennekes) " - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a Type 2 with cable (mennekes) connector", - "nl": "Heeft een Type 2 met kabel (J1772) " - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", - "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger (destination) connector", - "nl": "Heeft een Tesla Supercharger (destination) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", - "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a USB to charge phones and small electronics connector", - "nl": "Heeft een USB om GSMs en kleine electronica op te laden " - }, - "osmTags": "socket:USB-A~*" - } - ] + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" } - ], - "units": [ + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?" + }, + "render": { + "en": "This charging station is operated by {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ - { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut" - } - }, - { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren" - }, - "humanSingular": { - "en": " hour", - "nl": " uur" - } - }, - { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day" - }, - "humanSingular": { - "en": " day", - "nl": " dag" - } - } + "if": { + "and": [ + "network:={operator}" ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage", - "socket:USB-A:voltage" - ], - "applicableUnits": [ - { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current", - "socket:USB-A:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output", - "socket:USB-A:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt" - } - }, - { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt" - } - } - ], - "eraseInvalidValues": true + }, + "then": { + "en": "Actually, {operator} is the network" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?" + }, + "render": { + "en": "In case of problems, call {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?" + }, + "render": { + "en": "In case of problems, send an email to {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website of the operator?" + }, + "render": { + "en": "More info on {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?" + }, + "render": { + "en": "Reference number is {ref}" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" + }, + "mappings": [ + { + "if": "operational_status=broken", + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + } + }, + { + "if": { + "and": [ + "construction:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + } + }, + { + "if": { + "and": [ + "disused:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + } + }, + { + "if": { + "and": [ + "amenity=charging_station", + "operational_status=" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging" + } + } + ] + } + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } ] + }, + "iconOverlays": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross_bottom_right:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg", + "badge": true + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg", + "badge": true + } + ], + "width": { + "render": "8" + }, + "iconSize": { + "render": "50,50,bottom" + }, + "color": { + "render": "#00f" + }, + "presets": [ + { + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "Charging station" + }, + "preciseInput": { + "preferredBackground": "map" + } + } + ], + "wayHandling": 1, + "filter": [ + { + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] + }, + { + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] + }, + { + "id": "connection_type", + "options": [ + { + "question": { + "en": "All connectors", + "nl": "Alle types" + } + }, + { + "question": { + "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", + "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", + "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a Chademo connector", + "nl": "Heeft een Chademo " + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a Type 1 with cable (J1772) connector", + "nl": "Heeft een Type 1 met kabel (J1772) " + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a Type 1 without cable (J1772) connector", + "nl": "Heeft een Type 1 zonder kabel (J1772) " + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", + "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger connector", + "nl": "Heeft een Tesla Supercharger " + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a Type 2 (mennekes) connector", + "nl": "Heeft een Type 2 (mennekes) " + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a Type 2 CCS (mennekes) connector", + "nl": "Heeft een Type 2 CCS (mennekes) " + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a Type 2 with cable (mennekes) connector", + "nl": "Heeft een Type 2 met kabel (J1772) " + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", + "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger (destination) connector", + "nl": "Heeft een Tesla Supercharger (destination) " + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", + "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a USB to charge phones and small electronics connector", + "nl": "Heeft een USB om GSMs en kleine electronica op te laden " + }, + "osmTags": "socket:USB-A~*" + } + ] + } + ], + "units": [ + { + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ + { + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut" + } + }, + { + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren" + }, + "humanSingular": { + "en": " hour", + "nl": " uur" + } + }, + { + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day" + }, + "humanSingular": { + "en": " day", + "nl": " dag" + } + } + ] + }, + { + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage" + ], + "applicableUnits": [ + { + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current" + ], + "applicableUnits": [ + { + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt" + } + } + ], + "eraseInvalidValues": true + } + ] } \ No newline at end of file diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 4cbe196ae..821461d81 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -912,14 +912,14 @@ video { margin-right: 0px; } -.mb-1 { - margin-bottom: 0.25rem; -} - .mb-4 { margin-bottom: 1rem; } +.mb-1 { + margin-bottom: 0.25rem; +} + .box-border { box-sizing: border-box; } @@ -1156,14 +1156,14 @@ video { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } -.cursor-pointer { - cursor: pointer; -} - .cursor-wait { cursor: wait; } +.cursor-pointer { + cursor: pointer; +} + .resize { resize: both; } diff --git a/langs/layers/en.json b/langs/layers/en.json index 8900f0068..bb4b25dce 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -888,7 +888,7 @@ "question": "Has a European wall plug with ground pin (CEE7/4 type E) connector" }, "3": { - "question": "Has a Chademo connector" + "question": "Has a Chademo connector" }, "4": { "question": "Has a Type 1 with cable (J1772) connector" @@ -900,7 +900,7 @@ "question": "Has a Type 1 CCS (aka Type 1 Combo) connector" }, "7": { - "question": "Has a Tesla Supercharger connector" + "question": "Has a Tesla Supercharger connector" }, "8": { "question": "Has a Type 2 (mennekes) connector" @@ -919,6 +919,9 @@ }, "13": { "question": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector" + }, + "14": { + "question": "Has a USB to charge phones and small electronics connector" } } } @@ -966,83 +969,88 @@ "Available_charging_stations (generated)": { "mappings": { "0": { - "then": "

Schuko wall plug without ground pin (CEE7/4 type F)

" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
" }, "1": { - "then": "

Schuko wall plug without ground pin (CEE7/4 type F)

" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
" }, "2": { - "then": "

European wall plug with ground pin (CEE7/4 type E)

" + "then": "
European wall plug with ground pin (CEE7/4 type E)
" }, "3": { - "then": "

European wall plug with ground pin (CEE7/4 type E)

" + "then": "
European wall plug with ground pin (CEE7/4 type E)
" }, "4": { - "then": "

Chademo

" + "then": "
Chademo
" }, "5": { - "then": "

Chademo

" + "then": "
Chademo
" }, "6": { - "then": "

Type 1 with cable (J1772)

" + "then": "
Type 1 with cable (J1772)
" }, "7": { - "then": "

Type 1 with cable (J1772)

" + "then": "
Type 1 with cable (J1772)
" }, "8": { - "then": "

Type 1 without cable (J1772)

" + "then": "
Type 1 without cable (J1772)
" }, "9": { - "then": "

Type 1 without cable (J1772)

" + "then": "
Type 1 without cable (J1772)
" }, "10": { - "then": "

Type 1 CCS (aka Type 1 Combo)

" + "then": "
Type 1 CCS (aka Type 1 Combo)
" }, "11": { - "then": "

Type 1 CCS (aka Type 1 Combo)

" + "then": "
Type 1 CCS (aka Type 1 Combo)
" }, "12": { - "then": "

Tesla Supercharger

" + "then": "
Tesla Supercharger
" }, "13": { - "then": "

Tesla Supercharger

" + "then": "
Tesla Supercharger
" }, "14": { - "then": "

Type 2 (mennekes)

" + "then": "
Type 2 (mennekes)
" }, "15": { - "then": "

Type 2 (mennekes)

" + "then": "
Type 2 (mennekes)
" }, "16": { - "then": "

Type 2 CCS (mennekes)

" + "then": "
Type 2 CCS (mennekes)
" }, "17": { - "then": " Type 2 CCS (mennekes)" + "then": "
Type 2 CCS (mennekes)
" }, "18": { - "then": " Type 2 with cable (mennekes)" + "then": "
Type 2 with cable (mennekes)
" }, "19": { - "then": " Type 2 with cable (mennekes)" + "then": "
Type 2 with cable (mennekes)
" }, "20": { - "then": " Tesla Supercharger CCS (a branded type2_css)" + "then": "
Tesla Supercharger CCS (a branded type2_css)
" }, "21": { - "then": " Tesla Supercharger CCS (a branded type2_css)" + "then": "
Tesla Supercharger CCS (a branded type2_css)
" }, "22": { - "then": " Tesla Supercharger (destination)" + "then": "
Tesla Supercharger (destination)
" }, "23": { - "then": " Tesla Supercharger (destination)" + "then": "
Tesla Supercharger (destination)
" }, "24": { - "then": " Tesla supercharger (destination (A Type 2 with cable branded as tesla)" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
" }, "25": { - "then": " Tesla supercharger (destination (A Type 2 with cable branded as tesla)" - + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
" + }, + "26": { + "then": "
USB to charge phones and small electronics
" + }, + "27": { + "then": "
USB to charge phones and small electronics
" } }, "question": "Which charging stations are available here?" @@ -1127,18 +1135,22 @@ }, "question": "Which vehicles are allowed to charge here?" }, - "13": { - "question": "How much plugs of type Chademo are available here?", - "render": "There are Chademo plugs of type Chademo available here" + "access": { + "question": "Who is allowed to use this charging station?", + "render": "Access is {access}" + }, + "capacity": { + "question": "How much vehicles can be charged here at the same time?", + "render": "{capacity} vehicles can be charged here at the same time" }, "current-0": { "mappings": { "0": { - "then": "Chademo outputs 500 volt" + "then": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A" } }, - "question": "What voltage do the plugs with Chademo offer?", - "render": "Chademo outputs {socket:chademo:voltage} volt" + "question": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "render": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A" }, "current-1": { "mappings": { @@ -1152,7 +1164,6 @@ "current-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A" }, "1": { @@ -1186,6 +1197,18 @@ "question": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", "render": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A" }, + "current-13": { + "mappings": { + "0": { + "then": "USB to charge phones and small electronics outputs at most 1 A" + }, + "1": { + "then": "USB to charge phones and small electronics outputs at most 2 A" + } + }, + "question": "What current do the plugs with USB to charge phones and small electronics offer?", + "render": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A" + }, "current-2": { "mappings": { "0": { @@ -1297,14 +1320,6 @@ }, "payment-options": { "override": { - "mappings": { - "0": { - "then": "Payment is done using a dedicated app" - }, - "1": { - "then": "Payment is done using a membership card" - } - }, "mappings+": { "0": { "then": "Payment is done using a dedicated app" @@ -1339,6 +1354,10 @@ "question": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", "render": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here" }, + "plugs-13": { + "question": "How much plugs of type USB to charge phones and small electronics are available here?", + "render": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here" + }, "plugs-2": { "question": "How much plugs of type Chademo are available here?", "render": "There are Chademo plugs of type Chademo available here" @@ -1428,6 +1447,18 @@ "question": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", "render": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}" }, + "power-output-13": { + "mappings": { + "0": { + "then": "USB to charge phones and small electronics outputs at most 5w" + }, + "1": { + "then": "USB to charge phones and small electronics outputs at most 10w" + } + }, + "question": "What power output does a single plug of type USB to charge phones and small electronics offer?", + "render": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}" + }, "power-output-2": { "mappings": { "0": { @@ -1588,6 +1619,15 @@ "question": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", "render": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt" }, + "voltage-13": { + "mappings": { + "0": { + "then": "USB to charge phones and small electronics outputs 5 volt" + } + }, + "question": "What voltage do the plugs with USB to charge phones and small electronics offer?", + "render": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt" + }, "voltage-2": { "mappings": { "0": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 0ba838cc7..ae21e436e 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -990,7 +990,7 @@ "question": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " }, "3": { - "question": "Heeft een " + "question": "Heeft een Chademo " }, "4": { "question": "Heeft een Type 1 met kabel (J1772) " @@ -999,28 +999,31 @@ "question": "Heeft een Type 1 zonder kabel (J1772) " }, "6": { - "question": "Heeft een " + "question": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " }, "7": { - "question": "Heeft een " + "question": "Heeft een Tesla Supercharger " }, "8": { - "question": "Heeft een " + "question": "Heeft een Type 2 (mennekes) " }, "9": { - "question": "Heeft een " + "question": "Heeft een Type 2 CCS (mennekes) " }, "10": { "question": "Heeft een Type 2 met kabel (J1772) " }, "11": { - "question": "Heeft een " + "question": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " }, "12": { - "question": "Heeft een " + "question": "Heeft een Tesla Supercharger (destination) " }, "13": { - "question": "Heeft een " + "question": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + }, + "14": { + "question": "Heeft een USB om GSMs en kleine electronica op te laden " } } } @@ -1029,82 +1032,88 @@ "Available_charging_stations (generated)": { "mappings": { "0": { - "then": "

Schuko stekker zonder aardingspin (CEE7/4 type F)

" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "1": { - "then": "

Schuko stekker zonder aardingspin (CEE7/4 type F)

" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "2": { - "then": "

Europese stekker met aardingspin (CEE7/4 type E)

" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
" }, "3": { - "then": "

Europese stekker met aardingspin (CEE7/4 type E)

" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
" }, "4": { - "then": "

" + "then": "
Chademo
" }, "5": { - "then": "

" + "then": "
Chademo
" }, "6": { - "then": "

Type 1 met kabel (J1772)

" + "then": "
Type 1 met kabel (J1772)
" }, "7": { - "then": "

Type 1 met kabel (J1772)

" + "then": "
Type 1 met kabel (J1772)
" }, "8": { - "then": "

Type 1 zonder kabel (J1772)

" + "then": "
Type 1 zonder kabel (J1772)
" }, "9": { - "then": "

Type 1 zonder kabel (J1772)

" + "then": "
Type 1 zonder kabel (J1772)
" }, "10": { - "then": "

" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "11": { - "then": "

" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "12": { - "then": "

" + "then": "
Tesla Supercharger
" }, "13": { - "then": "

" + "then": "
Tesla Supercharger
" }, "14": { - "then": "

" + "then": "
Type 2 (mennekes)
" }, "15": { - "then": "

" + "then": "
Type 2 (mennekes)
" }, "16": { - "then": "

" + "then": "
Type 2 CCS (mennekes)
" }, "17": { - "then": " " + "then": "
Type 2 CCS (mennekes)
" }, "18": { - "then": " Type 2 met kabel (J1772)" + "then": "
Type 2 met kabel (J1772)
" }, "19": { - "then": " Type 2 met kabel (J1772)" + "then": "
Type 2 met kabel (J1772)
" }, "20": { - "then": " " + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "21": { - "then": " " + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "22": { - "then": " " + "then": "
Tesla Supercharger (destination)
" }, "23": { - "then": " " + "then": "
Tesla Supercharger (destination)
" }, "24": { - "then": " " + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" }, "25": { - "then": " " + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "26": { + "then": "
USB om GSMs en kleine electronica op te laden
" + }, + "27": { + "then": "
USB om GSMs en kleine electronica op te laden
" } } }, @@ -1153,47 +1162,59 @@ "current-10": { "mappings": { "0": { - "then": " levert een stroom van maximaal 125 A" + "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" }, "1": { - "then": " levert een stroom van maximaal 350 A" + "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + "question": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?", + "render": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" }, "current-11": { "mappings": { "0": { - "then": " levert een stroom van maximaal 125 A" + "then": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" }, "1": { - "then": " levert een stroom van maximaal 350 A" + "then": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:tesla_destination:current}A" + "question": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?", + "render": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" }, "current-12": { "mappings": { "0": { - "then": " levert een stroom van maximaal 16 A" + "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" }, "1": { - "then": " levert een stroom van maximaal 32 A" + "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:tesla_destination:current}A" + "question": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?", + "render": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "current-13": { + "mappings": { + "0": { + "then": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" + }, + "1": { + "then": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" + } + }, + "question": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?", + "render": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" }, "current-2": { "mappings": { "0": { - "then": " levert een stroom van maximaal 120 A" + "then": "Chademo levert een stroom van maximaal 120 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:chademo:current}A" + "question": "Welke stroom levert de stekker van type Chademo ?", + "render": "Chademo levert een stroom van maximaal {socket:chademo:current}A" }, "current-3": { "mappings": { @@ -1216,50 +1237,50 @@ "current-5": { "mappings": { "0": { - "then": " levert een stroom van maximaal 50 A" + "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" }, "1": { - "then": " levert een stroom van maximaal 125 A" + "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:type1_combo:current}A" + "question": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?", + "render": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" }, "current-6": { "mappings": { "0": { - "then": " levert een stroom van maximaal 125 A" + "then": "Tesla Supercharger levert een stroom van maximaal 125 A" }, "1": { - "then": " levert een stroom van maximaal 350 A" + "then": "Tesla Supercharger levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:tesla_supercharger:current}A" + "question": "Welke stroom levert de stekker van type Tesla Supercharger ?", + "render": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" }, "current-7": { "mappings": { "0": { - "then": " levert een stroom van maximaal 16 A" + "then": "Type 2 (mennekes) levert een stroom van maximaal 16 A" }, "1": { - "then": " levert een stroom van maximaal 32 A" + "then": "Type 2 (mennekes) levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:type2:current}A" + "question": "Welke stroom levert de stekker van type Type 2 (mennekes) ?", + "render": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" }, "current-8": { "mappings": { "0": { - "then": " levert een stroom van maximaal 125 A" + "then": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" }, "1": { - "then": " levert een stroom van maximaal 350 A" + "then": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type ?", - "render": " levert een stroom van maximaal {socket:type2_combo:current}A" + "question": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?", + "render": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" }, "current-9": { "mappings": { @@ -1293,14 +1314,6 @@ }, "payment-options": { "override": { - "mappings": { - "0": { - "then": "Betalen via een app van het netwerk" - }, - "1": { - "then": "Betalen via een lidkaart van het netwerk" - } - }, "mappings+": { "0": { "then": "Betalen via een app van het netwerk" @@ -1320,20 +1333,24 @@ "render": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" }, "plugs-10": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?", + "render": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" }, "plugs-11": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?", + "render": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" }, "plugs-12": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?", + "render": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + }, + "plugs-13": { + "question": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?", + "render": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" }, "plugs-2": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?", + "render": "Hier zijn Chademo stekkers van het type Chademo" }, "plugs-3": { "question": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?", @@ -1344,20 +1361,20 @@ "render": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" }, "plugs-5": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?", + "render": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" }, "plugs-6": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?", + "render": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" }, "plugs-7": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?", + "render": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" }, "plugs-8": { - "question": "Hoeveel stekkers van type heeft dit oplaadpunt?", - "render": "Hier zijn stekkers van het type " + "question": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?", + "render": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" }, "plugs-9": { "question": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?", @@ -1387,47 +1404,59 @@ "power-output-10": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 50 kw" + "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + "question": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?", + "render": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" }, "power-output-11": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 120 kw" + "then": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" }, "1": { - "then": " levert een vermogen van maximaal 150 kw" + "then": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" }, "2": { - "then": " levert een vermogen van maximaal 250 kw" + "then": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:tesla_destination:output}" + "question": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?", + "render": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" }, "power-output-12": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 11 kw" + "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" }, "1": { - "then": " levert een vermogen van maximaal 22 kw" + "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:tesla_destination:output}" + "question": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?", + "render": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "power-output-13": { + "mappings": { + "0": { + "then": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" + }, + "1": { + "then": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" + } + }, + "question": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?", + "render": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" }, "power-output-2": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 50 kw" + "then": "Chademo levert een vermogen van maximaal 50 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:chademo:output}" + "question": "Welk vermogen levert een enkele stekker van type Chademo ?", + "render": "Chademo levert een vermogen van maximaal {socket:chademo:output}" }, "power-output-3": { "mappings": { @@ -1462,56 +1491,56 @@ "power-output-5": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 50 kw" + "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" }, "1": { - "then": " levert een vermogen van maximaal 62.5 kw" + "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" }, "2": { - "then": " levert een vermogen van maximaal 150 kw" + "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" }, "3": { - "then": " levert een vermogen van maximaal 350 kw" + "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:type1_combo:output}" + "question": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?", + "render": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" }, "power-output-6": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 120 kw" + "then": "Tesla Supercharger levert een vermogen van maximaal 120 kw" }, "1": { - "then": " levert een vermogen van maximaal 150 kw" + "then": "Tesla Supercharger levert een vermogen van maximaal 150 kw" }, "2": { - "then": " levert een vermogen van maximaal 250 kw" + "then": "Tesla Supercharger levert een vermogen van maximaal 250 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:tesla_supercharger:output}" + "question": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?", + "render": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" }, "power-output-7": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 11 kw" + "then": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" }, "1": { - "then": " levert een vermogen van maximaal 22 kw" + "then": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:type2:output}" + "question": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?", + "render": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" }, "power-output-8": { "mappings": { "0": { - "then": " levert een vermogen van maximaal 50 kw" + "then": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type ?", - "render": " levert een vermogen van maximaal {socket:type2_combo:output}" + "question": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?", + "render": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" }, "power-output-9": { "mappings": { @@ -1546,44 +1575,53 @@ "voltage-10": { "mappings": { "0": { - "then": " heeft een spanning van 500 volt" + "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" }, "1": { - "then": " heeft een spanning van 920 volt" + "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + "question": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ", + "render": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" }, "voltage-11": { "mappings": { "0": { - "then": " heeft een spanning van 480 volt" + "then": "Tesla Supercharger (destination) heeft een spanning van 480 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:tesla_destination:voltage} volt" + "question": "Welke spanning levert de stekker van type Tesla Supercharger (destination) ", + "render": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" }, "voltage-12": { "mappings": { "0": { - "then": " heeft een spanning van 230 volt" + "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" }, "1": { - "then": " heeft een spanning van 400 volt" + "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:tesla_destination:voltage} volt" + "question": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ", + "render": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "voltage-13": { + "mappings": { + "0": { + "then": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" + } + }, + "question": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden ", + "render": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" }, "voltage-2": { "mappings": { "0": { - "then": " heeft een spanning van 500 volt" + "then": "Chademo heeft een spanning van 500 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:chademo:voltage} volt" + "question": "Welke spanning levert de stekker van type Chademo ", + "render": "Chademo heeft een spanning van {socket:chademo:voltage} volt" }, "voltage-3": { "mappings": { @@ -1612,47 +1650,47 @@ "voltage-5": { "mappings": { "0": { - "then": " heeft een spanning van 400 volt" + "then": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" }, "1": { - "then": " heeft een spanning van 1000 volt" + "then": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:type1_combo:voltage} volt" + "question": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ", + "render": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" }, "voltage-6": { "mappings": { "0": { - "then": " heeft een spanning van 480 volt" + "then": "Tesla Supercharger heeft een spanning van 480 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:tesla_supercharger:voltage} volt" + "question": "Welke spanning levert de stekker van type Tesla Supercharger ", + "render": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" }, "voltage-7": { "mappings": { "0": { - "then": " heeft een spanning van 230 volt" + "then": "Type 2 (mennekes) heeft een spanning van 230 volt" }, "1": { - "then": " heeft een spanning van 400 volt" + "then": "Type 2 (mennekes) heeft een spanning van 400 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:type2:voltage} volt" + "question": "Welke spanning levert de stekker van type Type 2 (mennekes) ", + "render": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" }, "voltage-8": { "mappings": { "0": { - "then": " heeft een spanning van 500 volt" + "then": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" }, "1": { - "then": " heeft een spanning van 920 volt" + "then": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" } }, - "question": "Welke spanning levert de stekker van type ", - "render": " heeft een spanning van {socket:type2_combo:voltage} volt" + "question": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) ", + "render": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" }, "voltage-9": { "mappings": { From 24f0089e5756aabccefd3da3e633d14498f10e89 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 00:38:52 +0200 Subject: [PATCH 071/110] Version number bump --- Models/Constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Models/Constants.ts b/Models/Constants.ts index 855f9dd2b..52ed3c81b 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.0-rc2"; + public static vNumber = "0.10.1-rc0"; public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" From c31a50b13985cbcedc50ec2f53f2f081708fb57e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 01:33:07 +0200 Subject: [PATCH 072/110] BBox bounds can not be bigger then the entire world anymore, fixes performance issues --- Logic/BBox.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Logic/BBox.ts b/Logic/BBox.ts index e642c6ce3..a6f350cf8 100644 --- a/Logic/BBox.ts +++ b/Logic/BBox.ts @@ -22,6 +22,13 @@ export class BBox { this.minLon = Math.min(this.minLon, coordinate[0]); this.minLat = Math.min(this.minLat, coordinate[1]); } + + this.maxLon = Math.min(this.maxLon, 180) + this.maxLat = Math.min(this.maxLat, 90) + this.minLon = Math.max(this.minLon, -180) + this.minLat = Math.max(this.minLat, -90) + + this.check(); } @@ -41,10 +48,10 @@ export class BBox { * Constructs a tilerange which fully contains this bbox (thus might be a bit larger) * @param zoomlevel */ - public containingTileRange(zoomlevel): TileRange{ - return Tiles.TileRangeBetween(zoomlevel, this.minLat, this.minLon, this.maxLat, this.maxLon) + public containingTileRange(zoomlevel): TileRange { + return Tiles.TileRangeBetween(zoomlevel, this.minLat, this.minLon, this.maxLat, this.maxLon) } - + public overlapsWith(other: BBox) { if (this.maxLon < other.minLon) { return false; @@ -148,7 +155,7 @@ export class BBox { * Expands the BBOx so that it contains complete tiles for the given zoomlevel * @param zoomlevel */ - expandToTileBounds(zoomlevel: number) : BBox{ + expandToTileBounds(zoomlevel: number): BBox { const ul = Tiles.embedded_tile(this.minLat, this.minLon, zoomlevel) const lr = Tiles.embedded_tile(this.maxLat, this.maxLon, zoomlevel) const boundsul = Tiles.tile_bounds_lon_lat(ul.z, ul.x, ul.y) From 725a3c37c7039f64a22216c572e819a8b14a0ccc Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 01:34:59 +0200 Subject: [PATCH 073/110] Don't calculate tile bounds if not sufficiently zoomed --- Logic/FeatureSource/FeaturePipeline.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 5d5b3e166..b0931766a 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -82,8 +82,6 @@ export default class FeaturePipeline { const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12)) this.relationTracker = new RelationsTracker() - const neededTilesFromOsm = this.getNeededTilesFromOsm() - this.sufficientlyZoomed = state.locationControl.map(location => { if (location?.zoom === undefined) { @@ -94,6 +92,7 @@ export default class FeaturePipeline { } ); + const neededTilesFromOsm = this.getNeededTilesFromOsm(this.sufficientlyZoomed) const perLayerHierarchy = new Map() this.perLayerHierarchy = perLayerHierarchy @@ -260,12 +259,15 @@ export default class FeaturePipeline { return oldestDate } - private getNeededTilesFromOsm(): UIEventSource { + private getNeededTilesFromOsm(isSufficientlyZoomed: UIEventSource): UIEventSource { const self = this return this.state.currentBounds.map(bbox => { if (bbox === undefined) { return } + if (!isSufficientlyZoomed.data) { + return; + } const osmSourceZoomLevel = self.osmSourceZoomLevel const range = bbox.containingTileRange(osmSourceZoomLevel) const tileIndexes = [] @@ -300,6 +302,9 @@ export default class FeaturePipeline { if (bbox === undefined) { return true } + if (!this.sufficientlyZoomed?.data) { + return true; + } let zoom = state.locationControl.data.zoom if (zoom < minzoom) { return true; From 32689f5179d9c9b1b3f8dc7b54f4ba1b1bea176b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 02:55:44 +0200 Subject: [PATCH 074/110] Bump maximum allowed memory of node --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f7fd84e3d..5c2e83ab6 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "homepage": "https://mapcomplete.osm.be", "main": "index.js", "scripts": { - "increase-memory": "export NODE_OPTIONS=--max_old_space_size=4096", + "increase-memory": "export NODE_OPTIONS=--max_old_space_size=6182", "start": "npm run start:prepare && npm-run-all --parallel start:parallel:*", "start:prepare": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory", "start:parallel:parcel": "parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.png vendor/* vendor/*/*", From 80d4beb38e49782dcff24bb7aae8f2175dfb840c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 02:56:21 +0200 Subject: [PATCH 075/110] Throw error if tilerange to big, prevent performance problem --- Models/TileRange.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Models/TileRange.ts b/Models/TileRange.ts index da30e498c..06ce4b765 100644 --- a/Models/TileRange.ts +++ b/Models/TileRange.ts @@ -1,3 +1,6 @@ +import {control} from "leaflet"; +import zoom = control.zoom; + export interface TileRange { xstart: number, ystart: number, @@ -96,6 +99,12 @@ export class Tiles { const ystart = Math.min(t0.y, t1.y) const yend = Math.max(t0.y, t1.y) const total = (1 + xend - xstart) * (1 + yend - ystart) + if(total > 1000){ + console.trace("Detected a big tilerange which'll be iterated over: zoomlevel", zoomlevel, "bounds:", [[lon0, lat0], [lon1, lat1]]) + } + if(total > 10000){ + throw "Tilerange too big" + } return { xstart: xstart, From 634f4e316ac240038c8d0948854bdc2a3da7c83b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 02:57:26 +0200 Subject: [PATCH 076/110] Prevent loops when two features with the same id are shown at the same time --- UI/ShowDataLayer/ShowDataLayer.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index ade1860ae..e180cdf5d 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -223,7 +223,9 @@ export default class ShowDataLayer { popup.setContent(`
Rendering
`) leafletLayer.on("popupopen", () => { - State.state.selectedElement.setData(feature) + if(State.state.selectedElement.data?.properties?.id !== feature.properties.id){ + State.state.selectedElement.setData(feature) + } if (infobox === undefined) { const tags = State.state.allElements.getEventSourceById(feature.properties.id); From 8b870474d7b150abb2a0c8d4a3400c0225e8f4a3 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 02:57:41 +0200 Subject: [PATCH 077/110] Improvements in loading images --- Logic/Actors/SelectedFeatureHandler.ts | 2 +- Logic/ImageProviders/AllImageProviders.ts | 14 +++++++-- Logic/ImageProviders/ImageProvider.ts | 14 +++++++-- Logic/ImageProviders/Mapillary.ts | 31 ++++++++++++------- Logic/ImageProviders/WikidataImageProvider.ts | 4 +++ .../ImageProviders/WikimediaImageProvider.ts | 4 +-- UI/Image/AttributedImage.ts | 5 ++- UI/Image/ImageCarousel.ts | 2 -- css/index-tailwind-output.css | 16 +++++----- 9 files changed, 61 insertions(+), 31 deletions(-) diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts index c76febd41..440115984 100644 --- a/Logic/Actors/SelectedFeatureHandler.ts +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -46,7 +46,7 @@ export default class SelectedFeatureHandler { // IF the selected element changes, set the hash correctly state.selectedElement.addCallback(feature => { if (feature === undefined) { - if (SelectedFeatureHandler._no_trigger_on.has(hash.data)) { + if (!SelectedFeatureHandler._no_trigger_on.has(hash.data)) { hash.setData("") } } diff --git a/Logic/ImageProviders/AllImageProviders.ts b/Logic/ImageProviders/AllImageProviders.ts index 18589f635..92818379f 100644 --- a/Logic/ImageProviders/AllImageProviders.ts +++ b/Logic/ImageProviders/AllImageProviders.ts @@ -16,7 +16,7 @@ export default class AllImageProviders { Mapillary.singleton, WikidataImageProvider.singleton, WikimediaImageProvider.singleton, - new GenericImageProvider([].concat(...Imgur.defaultValuePrefix, WikimediaImageProvider.commonsPrefix))] + new GenericImageProvider([].concat(...Imgur.defaultValuePrefix, WikimediaImageProvider.commonsPrefix, ...Mapillary.valuePrefixes))] private static _cache: Map> = new Map>() @@ -37,8 +37,18 @@ export default class AllImageProviders { this._cache.set(id, source) const allSources = [] for (const imageProvider of AllImageProviders.ImageAttributionSource) { + + let prefixes = imageProvider.defaultKeyPrefixes + if(imagePrefix !== undefined){ + prefixes = [...prefixes] + if(prefixes.indexOf("image") >= 0){ + prefixes.splice(prefixes.indexOf("image"), 1) + } + prefixes.push(imagePrefix) + } + const singleSource = imageProvider.GetRelevantUrls(tags, { - prefixes: imagePrefix !== undefined ? [imagePrefix] : undefined + prefixes: prefixes }) allSources.push(singleSource) singleSource.addCallbackAndRunD(_ => { diff --git a/Logic/ImageProviders/ImageProvider.ts b/Logic/ImageProviders/ImageProvider.ts index 67154d945..efce6aa92 100644 --- a/Logic/ImageProviders/ImageProvider.ts +++ b/Logic/ImageProviders/ImageProvider.ts @@ -8,7 +8,7 @@ export interface ProvidedImage { export default abstract class ImageProvider { - protected abstract readonly defaultKeyPrefixes : string[] + public abstract readonly defaultKeyPrefixes : string[] = ["mapillary", "image"] private _cache = new Map>() @@ -33,6 +33,9 @@ export default abstract class ImageProvider { prefixes?: string[] }):UIEventSource { const prefixes = options?.prefixes ?? this.defaultKeyPrefixes + if(prefixes === undefined){ + throw "The image provider"+this.constructor.name+" doesn't define `defaultKeyPrefixes`" + } const relevantUrls = new UIEventSource<{ url: string; key: string; provider: ImageProvider }[]>([]) const seenValues = new Set() allTags.addCallbackAndRunD(tags => { @@ -45,10 +48,15 @@ export default abstract class ImageProvider { continue } seenValues.add(value) - this.ExtractUrls(key, value).then(promises => { - for (const promise of promises) { + for (const promise of promises ?? []) { + if(promise === undefined){ + continue + } promise.then(providedImage => { + if(providedImage === undefined){ + return + } relevantUrls.data.push(providedImage) relevantUrls.ping() }) diff --git a/Logic/ImageProviders/Mapillary.ts b/Logic/ImageProviders/Mapillary.ts index 45342bc14..3fa25a534 100644 --- a/Logic/ImageProviders/Mapillary.ts +++ b/Logic/ImageProviders/Mapillary.ts @@ -5,24 +5,25 @@ import Svg from "../../Svg"; import {Utils} from "../../Utils"; import {LicenseInfo} from "./LicenseInfo"; import Constants from "../../Models/Constants"; +import {fail} from "assert"; export class Mapillary extends ImageProvider { - defaultKeyPrefixes = ["mapillary"] + defaultKeyPrefixes = ["mapillary","image"] public static readonly singleton = new Mapillary(); - - private static readonly v4_cached_urls = new Map>(); + private static readonly valuePrefix = "https://a.mapillary.com" + public static readonly valuePrefixes = [Mapillary.valuePrefix, "http://mapillary.com","https://mapillary.com"] private constructor() { super(); } - private static ExtractKeyFromURL(value: string): { + private static ExtractKeyFromURL(value: string, failIfNoMath = false): { key: string, isApiv4?: boolean } { - if (value.startsWith("https://a.mapillary.com")) { + if (value.startsWith(Mapillary.valuePrefix)) { const key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1) return {key: key, isApiv4: !isNaN(Number(key))}; } @@ -47,8 +48,11 @@ export class Mapillary extends ImageProvider { if (matchApi !== null) { return {key: matchApi[1]}; } - - + + if(failIfNoMath){ + return undefined; + } + return {key: value, isApiv4: !isNaN(Number(value))}; } @@ -61,7 +65,12 @@ export class Mapillary extends ImageProvider { } private async PrepareUrlAsync(key: string, value: string): Promise { - const keyV = Mapillary.ExtractKeyFromURL(value) + const failIfNoMatch = key.indexOf("mapillary") < 0 + const keyV = Mapillary.ExtractKeyFromURL(value, failIfNoMatch) + if(keyV === undefined){ + return undefined; + } + if (!keyV.isApiv4) { const url = `https://images.mapillary.com/${keyV.key}/thumb-640.jpg?client_id=${Constants.mapillary_client_token_v3}` return { @@ -70,10 +79,8 @@ export class Mapillary extends ImageProvider { key: key } } else { - const key = keyV.key; - const metadataUrl = 'https://graph.mapillary.com/' + key + '?fields=thumb_1024_url&&access_token=' + Constants.mapillary_client_token_v4; - const source = new UIEventSource(undefined) - Mapillary.v4_cached_urls.set(key, source) + const mapillaryId = keyV.key; + const metadataUrl = 'https://graph.mapillary.com/' + mapillaryId + '?fields=thumb_1024_url&&access_token=' + Constants.mapillary_client_token_v4; const response = await Utils.downloadJson(metadataUrl) const url = response["thumb_1024_url"]; return { diff --git a/Logic/ImageProviders/WikidataImageProvider.ts b/Logic/ImageProviders/WikidataImageProvider.ts index 3bb7d3c91..8576de5d9 100644 --- a/Logic/ImageProviders/WikidataImageProvider.ts +++ b/Logic/ImageProviders/WikidataImageProvider.ts @@ -26,6 +26,10 @@ export class WikidataImageProvider extends ImageProvider { if (value.startsWith(wikidataUrl)) { value = value.substring(wikidataUrl.length) } + if(value.startsWith("http")){ + // Probably some random link in the image field - we skip it + return undefined + } if (!value.startsWith("Q")) { value = "Q" + value } diff --git a/Logic/ImageProviders/WikimediaImageProvider.ts b/Logic/ImageProviders/WikimediaImageProvider.ts index b8940a520..20f69a7e4 100644 --- a/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/Logic/ImageProviders/WikimediaImageProvider.ts @@ -44,7 +44,7 @@ export class WikimediaImageProvider extends ImageProvider { if (continueParameter !== undefined) { url = `${url}&cmcontinue=${continueParameter}`; } - console.log("Loading a wikimedia category: ", url) + console.debug("Loading a wikimedia category: ", url) const response = await Utils.downloadJson(url) const members = response.query?.categorymembers ?? []; const imageOverview: string[] = members.map(member => member.title); @@ -55,7 +55,7 @@ export class WikimediaImageProvider extends ImageProvider { } if (maxLoad - imageOverview.length <= 0) { - console.log(`Recursive wikimedia category load stopped for ${categoryName}`) + console.debug(`Recursive wikimedia category load stopped for ${categoryName}`) return imageOverview; } diff --git a/UI/Image/AttributedImage.ts b/UI/Image/AttributedImage.ts index 919a9ac17..b0bb60d80 100644 --- a/UI/Image/AttributedImage.ts +++ b/UI/Image/AttributedImage.ts @@ -3,6 +3,7 @@ import Attribution from "./Attribution"; import Img from "../Base/Img"; import {ProvidedImage} from "../../Logic/ImageProviders/ImageProvider"; import BaseUIElement from "../BaseUIElement"; +import {Mapillary} from "../../Logic/ImageProviders/Mapillary"; export class AttributedImage extends Combine { @@ -10,7 +11,9 @@ export class AttributedImage extends Combine { constructor(imageInfo: ProvidedImage) { let img: BaseUIElement; let attr: BaseUIElement - img = new Img(imageInfo.url); + img = new Img(imageInfo.url, false, { + fallbackImage: imageInfo.provider === Mapillary.singleton ? "./assets/svg/blocked.svg" : undefined + }); attr = new Attribution(imageInfo.provider.GetAttributionFor(imageInfo.url), imageInfo.provider.SourceIcon(), ) diff --git a/UI/Image/ImageCarousel.ts b/UI/Image/ImageCarousel.ts index c20f52080..9932da616 100644 --- a/UI/Image/ImageCarousel.ts +++ b/UI/Image/ImageCarousel.ts @@ -13,9 +13,7 @@ export class ImageCarousel extends Toggle { const uiElements = images.map((imageURLS: { key: string, url: string, provider: ImageProvider }[]) => { const uiElements: BaseUIElement[] = []; for (const url of imageURLS) { - try { - let image = new AttributedImage(url) if (url.key !== undefined) { diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 821461d81..4cbe196ae 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -912,14 +912,14 @@ video { margin-right: 0px; } -.mb-4 { - margin-bottom: 1rem; -} - .mb-1 { margin-bottom: 0.25rem; } +.mb-4 { + margin-bottom: 1rem; +} + .box-border { box-sizing: border-box; } @@ -1156,14 +1156,14 @@ video { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } -.cursor-wait { - cursor: wait; -} - .cursor-pointer { cursor: pointer; } +.cursor-wait { + cursor: wait; +} + .resize { resize: both; } From 4c2beb5334773fcc3ec9dfb425e9f863462eae48 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 03:14:36 +0200 Subject: [PATCH 078/110] Make question generation laze, huge performance boost --- UI/Base/Lazy.ts | 16 ++++++++++++++++ UI/Popup/EditableTagRendering.ts | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 UI/Base/Lazy.ts diff --git a/UI/Base/Lazy.ts b/UI/Base/Lazy.ts new file mode 100644 index 000000000..e2b846cd8 --- /dev/null +++ b/UI/Base/Lazy.ts @@ -0,0 +1,16 @@ +import BaseUIElement from "../BaseUIElement"; + +export default class Lazy extends BaseUIElement{ + private readonly _f: () => BaseUIElement; + + constructor(f: () => BaseUIElement) { + super(); + this._f = f; + } + + protected InnerConstructElement(): HTMLElement { + // The caching of the BaseUIElement will guarantee that _f will only be called once + return this._f().ConstructElement(); + } + +} \ No newline at end of file diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 4c5a13ea5..f40118a05 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -9,6 +9,7 @@ import Toggle from "../Input/Toggle"; import BaseUIElement from "../BaseUIElement"; import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; import {Unit} from "../../Models/Unit"; +import Lazy from "../Base/Lazy"; export default class EditableTagRendering extends Toggle { @@ -44,14 +45,14 @@ export default class EditableTagRendering extends Toggle { editMode.setData(false) }); - const question = new TagRenderingQuestion(tags, configuration, + const question = new Lazy(() => new TagRenderingQuestion(tags, configuration, { units: units, cancelButton: cancelbutton, afterSave: () => { editMode.setData(false) } - }) + })) rendering = new Toggle( From 4fcd3523b77043160159316406018e2bc5815aa9 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 04:49:19 +0200 Subject: [PATCH 079/110] Add check that translation for a certain theme is complete, add a few missing dutch translations --- Models/ThemeConfig/Json/LayoutConfigJson.ts | 5 ++++ Models/ThemeConfig/LayerConfig.ts | 4 ++++ Models/ThemeConfig/LayoutConfig.ts | 2 +- UI/i18n/Translation.ts | 18 ++++++++++++++ assets/layers/toilet/toilet.json | 9 ++++--- assets/themes/natuurpunt/natuurpunt.json | 3 +++ scripts/generateLayerOverview.ts | 26 +++++++++++++++++++++ 7 files changed, 63 insertions(+), 4 deletions(-) diff --git a/Models/ThemeConfig/Json/LayoutConfigJson.ts b/Models/ThemeConfig/Json/LayoutConfigJson.ts index 73caee792..89439f529 100644 --- a/Models/ThemeConfig/Json/LayoutConfigJson.ts +++ b/Models/ThemeConfig/Json/LayoutConfigJson.ts @@ -52,6 +52,11 @@ export interface LayoutConfigJson { */ language: string | string[]; + /** + * Only used in 'generateLayerOverview': if present, every translation will be checked to make sure it is fully translated + */ + mustHaveLanguage?: string[] + /** * The title, as shown in the welcome message and the more-screen */ diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 1e8fae148..a2e55d71c 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -302,6 +302,10 @@ export default class LayerConfig { this.filters = (json.filter ?? []).map((option, i) => { return new FilterConfig(option, `${context}.filter-[${i}]`) }); + + if(json["filters"] !== undefined){ + throw "Error in "+context+": use 'filter' instead of 'filters'" + } const titleIcons = []; const defaultIcons = [ diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index 4f8551632..8b10f6c54 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -81,7 +81,7 @@ export default class LayoutConfig { this.title = new Translation(json.title, context + ".title"); this.description = new Translation(json.description, context + ".description"); this.shortDescription = json.shortDescription === undefined ? this.description.FirstSentence() : new Translation(json.shortDescription, context + ".shortdescription"); - this.descriptionTail = json.descriptionTail === undefined ? new Translation({"*": ""}, context + ".descriptionTail") : new Translation(json.descriptionTail, context + ".descriptionTail"); + this.descriptionTail = json.descriptionTail === undefined ? undefined : new Translation(json.descriptionTail, context + ".descriptionTail"); this.icon = json.icon; this.socialImage = json.socialImage; this.startZoom = json.startZoom; diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 796c52e8d..15bff437a 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -196,4 +196,22 @@ export class Translation extends BaseUIElement { return allIcons.filter(icon => icon != undefined) } + static ExtractAllTranslationsFrom(object: any, context = ""): { context: string, tr: Translation }[] { + const allTranslations: { context: string, tr: Translation }[] = [] + for (const key in object) { + const v = object[key] + if (v === undefined || v === null) { + continue + } + if (v instanceof Translation) { + allTranslations.push({context: context +"." + key, tr: v}) + continue + } + if (typeof v === "object") { + allTranslations.push(...Translation.ExtractAllTranslationsFrom(v, context + "." + key)) + continue + } + } + return allTranslations + } } \ No newline at end of file diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index 8b10b35ee..d7592efc7 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -461,7 +461,8 @@ "options": [ { "question": { - "en": "Wheelchair accessible" + "en": "Wheelchair accessible", + "nl": "Rolstoel toegankelijk" }, "osmTags": "wheelchair=yes" } @@ -472,7 +473,8 @@ "options": [ { "question": { - "en": "Has a changing table" + "en": "Has a changing table", + "nl": "Heeft een luiertafel" }, "osmTags": "changing_table=yes" } @@ -483,7 +485,8 @@ "options": [ { "question": { - "en": "Free to use" + "en": "Free to use", + "nl": "Gratis toegankelijk" }, "osmTags": { "or": [ diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index 7ebcab3b5..4b49e7465 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -17,6 +17,9 @@ "nl", "en" ], + "mustHaveLanguage": [ + "nl" + ], "maintainer": "", "icon": "./assets/themes/natuurpunt/natuurpunt.png", "version": "0", diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index 969f9e9b7..d97c235cb 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -5,6 +5,8 @@ import {LayoutConfigJson} from "../Models/ThemeConfig/Json/LayoutConfigJson"; import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; import LayerConfig from "../Models/ThemeConfig/LayerConfig"; +import {Translation} from "../UI/i18n/Translation"; +import {Utils} from "../Utils"; // This scripts scans 'assets/layers/*.json' for layer definition files and 'assets/themes/*.json' for theme definition files. // It spits out an overview of those to be used to load them @@ -139,6 +141,16 @@ class LayerOverviewUtils { } } } + + const referencedLayers = Utils.NoNull(themeFile.layers.map(layer => { + if(typeof layer === "string"){ + return layer + } + if(layer["builtin"] !== undefined){ + return layer["builtin"] + } + return undefined + })) themeFile.layers = themeFile.layers .filter(l => typeof l != "string") // We remove all the builtin layer references as they don't work with ts-node for some weird reason @@ -154,6 +166,20 @@ class LayerOverviewUtils { if (theme.id !== filename) { themeErrorCount.push("Theme ids should be the same as the name.json, but we got id: " + theme.id + " and filename " + filename + " (" + themePath + ")") } + const neededLanguages = themeFile["mustHaveLanguage"] + if (neededLanguages !== undefined) { + console.log("Checking language requerements for ", theme.id, "as it must have", neededLanguages.join(", ")) + const allTranslations = [].concat(Translation.ExtractAllTranslationsFrom(theme, theme.id), ...referencedLayers.map(layerId => Translation.ExtractAllTranslationsFrom(knownLayerIds.get(layerId), theme.id+"->"+layerId))) + for (const neededLanguage of neededLanguages) { + allTranslations + .filter(t => t.tr.translations[neededLanguage] === undefined && t.tr.translations["*"] === undefined) + .forEach(missing => { + themeErrorCount.push("The theme " + theme.id + " should be translation-complete for " + neededLanguage + ", but it lacks a translation for " + missing.context) + }) + } + + + } } catch (e) { themeErrorCount.push("Could not parse theme " + themeFile["id"] + "due to", e) From e904043069b951ebb847d48009910ef15ec33982 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 04:49:40 +0200 Subject: [PATCH 080/110] Small tweaks --- Logic/FeatureSource/FeaturePipeline.ts | 18 ++++---- Models/TileRange.ts | 10 ++--- UI/BigComponents/ThemeIntroductionPanel.ts | 4 +- UI/ShowDataLayer/ShowDataLayer.ts | 17 +++++--- Utils.ts | 3 +- test.ts | 50 +--------------------- 6 files changed, 27 insertions(+), 75 deletions(-) diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index b0931766a..e34d882ad 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -298,16 +298,13 @@ export default class FeaturePipeline { readonly overpassMaxZoom: UIEventSource, }, useOsmApi: UIEventSource): OverpassFeatureSource { const minzoom = Math.min(...state.layoutToUse.layers.map(layer => layer.minzoom)) - const allUpToDateAndZoomSufficient = state.currentBounds.map(bbox => { + const overpassIsActive = state.currentBounds.map(bbox => { if (bbox === undefined) { - return true - } - if (!this.sufficientlyZoomed?.data) { - return true; + return false } let zoom = state.locationControl.data.zoom if (zoom < minzoom) { - return true; + return false; } if (zoom > 16) { zoom = 16 @@ -315,19 +312,22 @@ export default class FeaturePipeline { if (zoom < 8) { zoom = zoom + 2 } + const range = bbox.containingTileRange(zoom) + if(range.total > 100){ + return false + } const self = this; const allFreshnesses = Tiles.MapRange(range, (x, y) => self.freshnessForVisibleLayers(zoom, x, y)) - return !allFreshnesses.some(freshness => freshness === undefined || freshness < this.oldestAllowedDate) + return allFreshnesses.some(freshness => freshness === undefined || freshness < this.oldestAllowedDate) }, [state.locationControl]) - allUpToDateAndZoomSufficient.addCallbackAndRunD(allUpToDate => console.log("All up to data is: ", allUpToDate)) const self = this; const updater = new OverpassFeatureSource(state, { relationTracker: this.relationTracker, - isActive: useOsmApi.map(b => !b && !allUpToDateAndZoomSufficient.data, [allUpToDateAndZoomSufficient]), + isActive: useOsmApi.map(b => !b && overpassIsActive.data, [overpassIsActive]), onBboxLoaded: ((bbox, date, downloadedLayers) => { Tiles.MapRange(bbox.containingTileRange(self.osmSourceZoomLevel), (x, y) => { downloadedLayers.forEach(layer => { diff --git a/Models/TileRange.ts b/Models/TileRange.ts index 06ce4b765..7a542a213 100644 --- a/Models/TileRange.ts +++ b/Models/TileRange.ts @@ -14,6 +14,10 @@ export class Tiles { public static MapRange(tileRange: TileRange, f: (x: number, y: number) => T): T[] { const result: T[] = [] + const total = tileRange.total + if(total > 5000){ + throw "Tilerange too big" + } for (let x = tileRange.xstart; x <= tileRange.xend; x++) { for (let y = tileRange.ystart; y <= tileRange.yend; y++) { const t = f(x, y); @@ -99,12 +103,6 @@ export class Tiles { const ystart = Math.min(t0.y, t1.y) const yend = Math.max(t0.y, t1.y) const total = (1 + xend - xstart) * (1 + yend - ystart) - if(total > 1000){ - console.trace("Detected a big tilerange which'll be iterated over: zoomlevel", zoomlevel, "bounds:", [[lon0, lat0], [lon1, lat1]]) - } - if(total > 10000){ - throw "Tilerange too big" - } return { xstart: xstart, diff --git a/UI/BigComponents/ThemeIntroductionPanel.ts b/UI/BigComponents/ThemeIntroductionPanel.ts index 75c5b6f55..fec58bb29 100644 --- a/UI/BigComponents/ThemeIntroductionPanel.ts +++ b/UI/BigComponents/ThemeIntroductionPanel.ts @@ -12,7 +12,7 @@ export default class ThemeIntroductionPanel extends Combine { constructor(isShown: UIEventSource) { const layout = State.state.layoutToUse - const languagePicker = LanguagePicker.CreateLanguagePicker(layout.language, Translations.t.general.pickLanguage.Clone()) + const languagePicker = LanguagePicker.CreateLanguagePicker(layout.language, Translations.t.general.pickLanguage.Clone()) const toTheMap = new SubtleButton( undefined, @@ -54,7 +54,7 @@ export default class ThemeIntroductionPanel extends Combine { "

", toTheMap, loginStatus, - layout.descriptionTail.Clone(), + layout.descriptionTail?.Clone(), "
", languagePicker, ...layout.CustomCodeSnippets() diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index e180cdf5d..73c78f945 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -22,6 +22,8 @@ export default class ShowDataLayer { /** * If the selected element triggers, this is used to lookup the correct layer and to open the popup * Used to avoid a lot of callbacks on the selected element + * + * Note: the key of this dictionary is 'feature.properties.id+features.geometry.type' as one feature might have multiple presentations * @private */ private readonly leafletLayersPerId = new Map() @@ -68,7 +70,7 @@ export default class ShowDataLayer { if (self._leafletMap.data === undefined) { return; } - const v = self.leafletLayersPerId.get(selected.properties.id) + const v = self.leafletLayersPerId.get(selected.properties.id+selected.geometry.type) if (v === undefined) { return; } @@ -83,7 +85,7 @@ export default class ShowDataLayer { if (feature.id !== feature.properties.id) { // Probably a feature which has renamed - console.trace("Not opening the popup for", feature) + console.log("Not opening the popup for", feature, "as probably renamed") return; } if (selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again @@ -223,10 +225,6 @@ export default class ShowDataLayer { popup.setContent(`
Rendering
`) leafletLayer.on("popupopen", () => { - if(State.state.selectedElement.data?.properties?.id !== feature.properties.id){ - State.state.selectedElement.setData(feature) - } - if (infobox === undefined) { const tags = State.state.allElements.getEventSourceById(feature.properties.id); infobox = new FeatureInfoBox(tags, layer); @@ -242,11 +240,16 @@ export default class ShowDataLayer { infobox.AttachTo(id) infobox.Activate(); + + + if(State.state.selectedElement.data?.properties?.id !== feature.properties.id){ + // x State.state.selectedElement.setData(feature) + } }); // Add the feature to the index to open the popup when needed - this.leafletLayersPerId.set(feature.properties.id, {feature: feature, leafletlayer: leafletLayer}) + this.leafletLayersPerId.set(feature.properties.id+feature.geometry.type, {feature: feature, leafletlayer: leafletLayer}) } diff --git a/Utils.ts b/Utils.ts index bd5cd62a5..5c04c9547 100644 --- a/Utils.ts +++ b/Utils.ts @@ -1,5 +1,4 @@ import * as colors from "./assets/colors.json" -import {TileRange} from "./Models/TileRange"; export class Utils { @@ -238,7 +237,7 @@ export class Utils { } return target; } - + static getOrSetDefault(dict: Map, k: K, v: () => V) { let found = dict.get(k); if (found !== undefined) { diff --git a/test.ts b/test.ts index 7dd34cd93..e20e313f0 100644 --- a/test.ts +++ b/test.ts @@ -1,50 +1,2 @@ -<<<<<<< HEAD -import {Tiles} from "./Models/TileRange"; -import OsmFeatureSource from "./Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource"; -import {Utils} from "./Utils"; import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; -import LayerConfig from "./Models/ThemeConfig/LayerConfig"; - -const allLayers: LayerConfig[] = [] -const seenIds = new Set() -for (const layoutConfig of AllKnownLayouts.layoutsList) { - if (layoutConfig.hideFromOverview) { - continue - } - for (const layer of layoutConfig.layers) { - if (seenIds.has(layer.id)) { - continue - } - seenIds.add(layer.id) - allLayers.push(layer) - } -} - -console.log("All layer ids", allLayers.map(l => l.id)) - -const src = new OsmFeatureSource({ - backend: "https://www.openstreetmap.org", - handleTile: tile => console.log("Got tile", tile), - allLayers: allLayers -}) -src.LoadTile(16, 33354, 21875).then(geojson => { - console.log("Got geojson", geojson); - Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson), "test.geojson", { - mimetype: "application/vnd.geo+json" - }) -}) -//*/ -======= -import LocationInput from "./UI/Input/LocationInput"; -import Loc from "./Models/Loc"; -import {UIEventSource} from "./Logic/UIEventSource"; - -new LocationInput({ - centerLocation: new UIEventSource({ - lat: 51.1110, - lon: 3.3701, - zoom : 14 - }) -}).SetStyle("height: 500px") - .AttachTo("maindiv"); ->>>>>>> feature/animated-precise-input +import {Translation} from "./UI/i18n/Translation"; From 4480f2f5e39157c8a515a89780954b9551dc428c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 04:59:22 +0200 Subject: [PATCH 081/110] Experimental json merge driver --- .gitattributes | 1 + Docs/Development_deployment.md | 5 + css/index-tailwind-output.css | 398 ++++++++++++++++----------------- package-lock.json | 75 +++++++ package.json | 1 + 5 files changed, 281 insertions(+), 199 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..8e0cc6929 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.json merge=json diff --git a/Docs/Development_deployment.md b/Docs/Development_deployment.md index aceae49e7..659ecc5d0 100644 --- a/Docs/Development_deployment.md +++ b/Docs/Development_deployment.md @@ -102,6 +102,11 @@ Weird errors Try removing `node_modules`, `package-lock.json` and `.cache` +Misc setup +---------- + +The json-git-merger is used to quickly merge translation files, [documentation here](https://github.com/jonatanpedersen/git-json-merge#single-project--directory) + Overview of package.json-scripts -------------------------------- diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 4cbe196ae..4ed8a97b9 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -716,30 +716,6 @@ video { left: 0px; } -.bottom-3 { - bottom: 0.75rem; -} - -.left-3 { - left: 0.75rem; -} - -.right-2 { - right: 0.5rem; -} - -.left-24 { - left: 6rem; -} - -.right-24 { - right: 6rem; -} - -.top-56 { - top: 14rem; -} - .top-0 { top: 0px; } @@ -764,6 +740,30 @@ video { bottom: 0px; } +.bottom-3 { + bottom: 0.75rem; +} + +.left-3 { + left: 0.75rem; +} + +.right-2 { + right: 0.5rem; +} + +.left-24 { + left: 6rem; +} + +.right-24 { + right: 6rem; +} + +.top-56 { + top: 14rem; +} + .isolate { isolation: isolate; } @@ -788,6 +788,22 @@ video { float: none; } +.m-2 { + margin: 0.5rem; +} + +.m-1 { + margin: 0.25rem; +} + +.m-0 { + margin: 0px; +} + +.m-3 { + margin: 0.75rem; +} + .m-8 { margin: 2rem; } @@ -796,10 +812,6 @@ video { margin: 2.75rem; } -.m-1 { - margin: 0.25rem; -} - .m-5 { margin: 1.25rem; } @@ -808,27 +820,10 @@ video { margin: 0.125rem; } -.m-0 { - margin: 0px; -} - -.m-2 { - margin: 0.5rem; -} - -.m-3 { - margin: 0.75rem; -} - .m-4 { margin: 1rem; } -.my-2 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} - .mx-10 { margin-left: 2.5rem; margin-right: 2.5rem; @@ -844,26 +839,15 @@ video { margin-right: 1rem; } -.-ml-1 { - margin-left: -0.25rem; -} - -.mr-3 { - margin-right: 0.75rem; -} - -.ml-3 { - margin-left: 0.75rem; +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; } .mb-2 { margin-bottom: 0.5rem; } -.mr-4 { - margin-right: 1rem; -} - .mt-3 { margin-top: 0.75rem; } @@ -876,10 +860,6 @@ video { margin-right: 0.5rem; } -.mt-2 { - margin-top: 0.5rem; -} - .mb-10 { margin-bottom: 2.5rem; } @@ -888,12 +868,8 @@ video { margin-top: 0.25rem; } -.mt-0 { - margin-top: 0px; -} - -.ml-2 { - margin-left: 0.5rem; +.ml-3 { + margin-left: 0.75rem; } .mt-4 { @@ -904,10 +880,6 @@ video { margin-bottom: 2rem; } -.ml-1 { - margin-left: 0.25rem; -} - .mr-0 { margin-right: 0px; } @@ -916,6 +888,34 @@ video { margin-bottom: 0.25rem; } +.mr-3 { + margin-right: 0.75rem; +} + +.-ml-1 { + margin-left: -0.25rem; +} + +.mr-4 { + margin-right: 1rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-0 { + margin-top: 0px; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + .mb-4 { margin-bottom: 1rem; } @@ -968,32 +968,16 @@ video { height: 100%; } -.h-5 { - height: 1.25rem; -} - -.h-24 { - height: 6rem; -} - -.h-10 { - height: 2.5rem; -} - -.h-8 { - height: 2rem; -} - .h-12 { height: 3rem; } -.h-screen { - height: 100vh; +.h-5 { + height: 1.25rem; } -.h-11 { - height: 2.75rem; +.h-screen { + height: 100vh; } .h-32 { @@ -1012,6 +996,22 @@ video { height: 1.5rem; } +.h-10 { + height: 2.5rem; +} + +.h-8 { + height: 2rem; +} + +.h-24 { + height: 6rem; +} + +.h-11 { + height: 2.75rem; +} + .h-3 { height: 0.75rem; } @@ -1032,18 +1032,6 @@ video { width: 100%; } -.w-5 { - width: 1.25rem; -} - -.w-10 { - width: 2.5rem; -} - -.w-8 { - width: 2rem; -} - .w-12 { width: 3rem; } @@ -1052,10 +1040,6 @@ video { width: 100vw; } -.w-11 { - width: 2.75rem; -} - .w-0 { width: 0px; } @@ -1084,16 +1068,32 @@ video { width: 50%; } -.min-w-min { - min-width: -webkit-min-content; - min-width: -moz-min-content; - min-width: min-content; +.w-10 { + width: 2.5rem; +} + +.w-8 { + width: 2rem; +} + +.w-5 { + width: 1.25rem; +} + +.w-11 { + width: 2.75rem; } .min-w-\[20em\] { min-width: 20em; } +.min-w-min { + min-width: -webkit-min-content; + min-width: -moz-min-content; + min-width: min-content; +} + .max-w-full { max-width: 100%; } @@ -1156,14 +1156,14 @@ video { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } -.cursor-pointer { - cursor: pointer; -} - .cursor-wait { cursor: wait; } +.cursor-pointer { + cursor: pointer; +} + .resize { resize: both; } @@ -1247,6 +1247,10 @@ video { word-break: break-all; } +.rounded { + border-radius: 0.25rem; +} + .rounded-full { border-radius: 9999px; } @@ -1255,26 +1259,22 @@ video { border-radius: 1.5rem; } -.rounded { - border-radius: 0.25rem; +.rounded-xl { + border-radius: 0.75rem; } .rounded-lg { border-radius: 0.5rem; } -.rounded-xl { - border-radius: 0.75rem; +.border-4 { + border-width: 4px; } .border { border-width: 1px; } -.border-4 { - border-width: 4px; -} - .border-2 { border-width: 2px; } @@ -1311,24 +1311,14 @@ video { --tw-border-opacity: 0.5; } -.bg-white { - --tw-bg-opacity: 1; - background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); -} - .bg-blue-50 { --tw-bg-opacity: 1; background-color: rgba(239, 246, 255, var(--tw-bg-opacity)); } -.bg-blue-100 { +.bg-white { --tw-bg-opacity: 1; - background-color: rgba(219, 234, 254, var(--tw-bg-opacity)); -} - -.bg-gray-400 { - --tw-bg-opacity: 1; - background-color: rgba(156, 163, 175, var(--tw-bg-opacity)); + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); } .bg-indigo-100 { @@ -1346,11 +1336,6 @@ video { background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); } -.bg-gray-200 { - --tw-bg-opacity: 1; - background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); -} - .bg-gray-100 { --tw-bg-opacity: 1; background-color: rgba(243, 244, 246, var(--tw-bg-opacity)); @@ -1366,6 +1351,21 @@ video { background-color: rgba(254, 202, 202, var(--tw-bg-opacity)); } +.bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgba(219, 234, 254, var(--tw-bg-opacity)); +} + +.bg-gray-400 { + --tw-bg-opacity: 1; + background-color: rgba(156, 163, 175, var(--tw-bg-opacity)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); +} + .p-1\.5 { padding: 0.375rem; } @@ -1374,18 +1374,14 @@ video { padding: 0.25rem; } -.p-3 { - padding: 0.75rem; +.p-2 { + padding: 0.5rem; } .p-4 { padding: 1rem; } -.p-2 { - padding: 0.5rem; -} - .p-0\.5 { padding: 0.125rem; } @@ -1394,12 +1390,8 @@ video { padding: 0px; } -.pt-3 { - padding-top: 0.75rem; -} - -.pb-3 { - padding-bottom: 0.75rem; +.p-3 { + padding: 0.75rem; } .pl-4 { @@ -1410,18 +1402,14 @@ video { padding-bottom: 0px; } -.pl-1 { - padding-left: 0.25rem; -} - -.pr-1 { - padding-right: 0.25rem; -} - .pt-6 { padding-top: 1.5rem; } +.pb-3 { + padding-bottom: 0.75rem; +} + .pl-2 { padding-left: 0.5rem; } @@ -1442,10 +1430,6 @@ video { padding-left: 0.75rem; } -.pr-0 { - padding-right: 0px; -} - .pb-2 { padding-bottom: 0.5rem; } @@ -1458,6 +1442,22 @@ video { padding-top: 0px; } +.pt-3 { + padding-top: 0.75rem; +} + +.pl-1 { + padding-left: 0.25rem; +} + +.pr-1 { + padding-right: 0.25rem; +} + +.pr-0 { + padding-right: 0px; +} + .pr-2 { padding-right: 0.5rem; } @@ -1474,16 +1474,6 @@ video { vertical-align: middle; } -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} - .text-2xl { font-size: 1.5rem; line-height: 2rem; @@ -1499,6 +1489,16 @@ video { line-height: 1.75rem; } +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + .text-4xl { font-size: 2.25rem; line-height: 2.5rem; @@ -1508,6 +1508,10 @@ video { font-weight: 700; } +.font-medium { + font-weight: 500; +} + .font-extrabold { font-weight: 800; } @@ -1516,10 +1520,6 @@ video { font-weight: 600; } -.font-medium { - font-weight: 500; -} - .uppercase { text-transform: uppercase; } @@ -1553,26 +1553,26 @@ video { letter-spacing: -0.025em; } -.text-white { - --tw-text-opacity: 1; - color: rgba(255, 255, 255, var(--tw-text-opacity)); -} - .text-gray-900 { --tw-text-opacity: 1; color: rgba(17, 24, 39, var(--tw-text-opacity)); } -.text-gray-800 { - --tw-text-opacity: 1; - color: rgba(31, 41, 55, var(--tw-text-opacity)); -} - .text-gray-500 { --tw-text-opacity: 1; color: rgba(107, 114, 128, var(--tw-text-opacity)); } +.text-white { + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgba(31, 41, 55, var(--tw-text-opacity)); +} + .text-green-600 { --tw-text-opacity: 1; color: rgba(5, 150, 105, var(--tw-text-opacity)); @@ -2170,16 +2170,16 @@ li::marker { width: unset; } -.hover\:bg-blue-200:hover { - --tw-bg-opacity: 1; - background-color: rgba(191, 219, 254, var(--tw-bg-opacity)); -} - .hover\:bg-indigo-200:hover { --tw-bg-opacity: 1; background-color: rgba(199, 210, 254, var(--tw-bg-opacity)); } +.hover\:bg-blue-200:hover { + --tw-bg-opacity: 1; + background-color: rgba(191, 219, 254, var(--tw-bg-opacity)); +} + .hover\:text-blue-800:hover { --tw-text-opacity: 1; color: rgba(30, 64, 175, var(--tw-text-opacity)); @@ -2214,14 +2214,14 @@ li::marker { height: 6rem; } - .sm\:w-24 { - width: 6rem; - } - .sm\:w-auto { width: auto; } + .sm\:w-24 { + width: 6rem; + } + .sm\:max-w-sm { max-width: 24rem; } diff --git a/package-lock.json b/package-lock.json index 8981f9c64..d0501ac77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ "@types/node": "^7.0.5", "assert": "^2.0.0", "fs": "0.0.1-security", + "git-json-merge": "^0.4.5", "marked": "^2.0.0", "read-file": "^0.2.0", "sharp": "^0.28.3", @@ -3078,6 +3079,12 @@ "node": ">=0.4.0" } }, + "node_modules/adiff": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/adiff/-/adiff-0.2.13.tgz", + "integrity": "sha1-3D3TL5RNl/J366WM5SmXrf8fdyg=", + "dev": true + }, "node_modules/affine-hull": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz", @@ -5255,6 +5262,15 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -6260,6 +6276,22 @@ "assert-plus": "^1.0.0" } }, + "node_modules/git-json-merge": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/git-json-merge/-/git-json-merge-0.4.5.tgz", + "integrity": "sha512-akVUhyzRtkXGe5uAcw3AijF/253RA7tAPdfHtKLawYAhDjuyP+Ebr1YvZUv+7Jyr41g+IVRpKPBd2h+m6AHNqQ==", + "dev": true, + "dependencies": { + "detect-indent": "^6.0.0", + "xdiff": "^0.2.11" + }, + "bin": { + "git-json-merge": "bin/git-json-merge" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -16338,6 +16370,18 @@ "async-limiter": "~1.0.0" } }, + "node_modules/xdiff": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/xdiff/-/xdiff-0.2.11.tgz", + "integrity": "sha1-cj1SPhtvJlojPK/HeGBiLqXS2Mg=", + "dev": true, + "dependencies": { + "adiff": "~0.2.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -19104,6 +19148,12 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" }, + "adiff": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/adiff/-/adiff-0.2.13.tgz", + "integrity": "sha1-3D3TL5RNl/J366WM5SmXrf8fdyg=", + "dev": true + }, "affine-hull": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz", @@ -20861,6 +20911,12 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true + }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -21666,6 +21722,16 @@ "assert-plus": "^1.0.0" } }, + "git-json-merge": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/git-json-merge/-/git-json-merge-0.4.5.tgz", + "integrity": "sha512-akVUhyzRtkXGe5uAcw3AijF/253RA7tAPdfHtKLawYAhDjuyP+Ebr1YvZUv+7Jyr41g+IVRpKPBd2h+m6AHNqQ==", + "dev": true, + "requires": { + "detect-indent": "^6.0.0", + "xdiff": "^0.2.11" + } + }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -29892,6 +29958,15 @@ "async-limiter": "~1.0.0" } }, + "xdiff": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/xdiff/-/xdiff-0.2.11.tgz", + "integrity": "sha1-cj1SPhtvJlojPK/HeGBiLqXS2Mg=", + "dev": true, + "requires": { + "adiff": "~0.2.4" + } + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", diff --git a/package.json b/package.json index 5c2e83ab6..6081d4af3 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "@types/node": "^7.0.5", "assert": "^2.0.0", "fs": "0.0.1-security", + "git-json-merge": "^0.4.5", "marked": "^2.0.0", "read-file": "^0.2.0", "sharp": "^0.28.3", From 6b19a618e1f92c6a4fbacd265aed1fa26690671e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 05:01:10 +0200 Subject: [PATCH 082/110] Remove accidental duplicate --- langs/nl.json | 125 -------------------------------------------------- 1 file changed, 125 deletions(-) diff --git a/langs/nl.json b/langs/nl.json index d1b6d3129..702ae077c 100644 --- a/langs/nl.json +++ b/langs/nl.json @@ -108,131 +108,6 @@ "intro": "MapComplete is een OpenStreetMap applicatie waar informatie over een specifiek thema bekeken en aangepast kan worden.", "pickTheme": "Kies hieronder een thema om te beginnen." }, - "general": { - "loginWithOpenStreetMap": "Aanmelden met OpenStreetMap", - "welcomeBack": "Je bent aangemeld. Welkom terug!", - "loginToStart": "Meld je aan om deze vraag te beantwoorden", - "search": { - "search": "Zoek naar een locatie", - "searching": "Aan het zoeken…", - "nothing": "Niets gevonden…", - "error": "Iets ging mis…" - }, - "returnToTheMap": "Naar de kaart", - "save": "Opslaan", - "cancel": "Annuleren", - "skip": "Vraag overslaan", - "oneSkippedQuestion": "Een vraag is overgeslaan", - "skippedQuestions": "Sommige vragen zijn overgeslaan", - "number": "getal", - "osmLinkTooltip": "Bekijk dit object op OpenStreetMap waar geschiedenis en meer aanpasopties zijn", - "add": { - "addNew": "Voeg hier een {category} toe", - "title": "Nieuw punt toevoegen?", - "intro": "Je klikte ergens waar er nog geen data is. Kies hieronder welk punt je wilt toevoegen
", - "pleaseLogin": "Gelieve je aan te melden om een punt to te voegen", - "zoomInFurther": "Gelieve verder in te zoomen om een punt toe te voegen.", - "stillLoading": "De data wordt nog geladen. Nog even geduld en dan kan je een punt toevoegen.", - "confirmIntro": "

Voeg hier een {title} toe?

Het punt dat je hier toevoegt, is zichtbaar voor iedereen. Veel applicaties gebruiken deze data, voeg dus enkel punten toe die echt bestaan.", - "confirmButton": "Voeg hier een {category} toe
Je toevoeging is voor iedereen zichtbaar
", - "openLayerControl": "Open de laag-instellingen", - "layerNotEnabled": "De laag {layer} is gedeactiveerd. Activeer deze om een punt toe te voegen" - }, - "pickLanguage": "Kies je taal: ", - "about": "Bewerk en voeg data toe aan OpenStreetMap over een specifiek onderwerp op een gemakkelijke manier", - "nameInlineQuestion": "De naam van dit {category} is $$$", - "noNameCategory": "{category} zonder naam", - "questions": { - "phoneNumberOf": "Wat is het telefoonnummer van {category}?", - "phoneNumberIs": "Het telefoonnummer van {category} is {phone}", - "websiteOf": "Wat is de website van {category}?", - "websiteIs": "Website: {website}", - "emailOf": "Wat is het email-adres van {category}?", - "emailIs": "Het email-adres van {category} is {email}" - }, - "openStreetMapIntro": "

Een open kaart

Zou het niet fantastisch zijn als er een open kaart zou zijn die door iedereen aangepast én gebruikt kan worden? Een kaart waar iedereen zijn interesses aan zou kunnen toevoegen? Dan zouden er geen duizend-en-één verschillende kleine kaartjes, websites, ... meer nodig zijn

OpenStreetMap is deze open kaart. Je mag de kaartdata gratis gebruiken (mits bronvermelding en herpublicatie van aanpassingen). Daarenboven mag je de kaart ook gratis aanpassen als je een account maakt. Ook deze website is gebaseerd op OpenStreetMap. Als je hier een vraag beantwoord, gaat het antwoord daar ook naartoe

Tenslotte zijn er reeds vele gebruikers van OpenStreetMap. Denk maar Organic Maps, OsmAnd, verschillende gespecialiseerde routeplanners, de achtergrondkaarten op Facebook, Instagram,...
Zelfs Apple Maps en Bing-Maps gebruiken OpenStreetMap in hun kaarten!

Kortom, als je hier een punt toevoegd of een vraag beantwoord, zal dat na een tijdje ook in al dié applicaties te zien zijn.

", - "attribution": { - "attributionTitle": "Met dank aan", - "attributionContent": "

Alle data is voorzien door OpenStreetMap, gratis en vrij te hergebruiken onder de Open DataBase Licentie.

", - "themeBy": "Thema gemaakt door {author}", - "iconAttribution": { - "title": "Iconen en afbeeldingen" - }, - "mapContributionsByAndHidden": "De zichtbare data heeft bijdragen van {contributors} en {hiddenCount} andere bijdragers", - "mapContributionsBy": "De huidige data is bijgedragen door {contributors}", - "codeContributionsBy": "MapComplete is gebouwd door {contributors} en {hiddenCount} andere bijdragers" - }, - "sharescreen": { - "intro": "

Deel deze kaart

Kopieer onderstaande link om deze kaart naar vrienden en familie door te sturen:", - "addToHomeScreen": "

Voeg toe aan je thuis-scherm

Je kan deze website gemakkelijk aan het thuisscherm van je smartphone toevoegen voor een native feel. Klik op de 'voeg toe aan thuis-scherm' knop in de URL-balk om dit te doen.", - "embedIntro": "

Plaats dit op je website

Voeg dit kaartje toe op je eigen website.
We moedigen dit zelfs aan - je hoeft geen toestemming te vragen.
Het is gratis en zal dat altijd blijven. Hoe meer het gebruikt wordt, hoe waardevoller.", - "copiedToClipboard": "Link gekopieerd naar klembord", - "thanksForSharing": "Bedankt om te delen!", - "editThisTheme": "Pas dit thema aan", - "editThemeDescription": "Pas vragen aan of voeg vragen toe aan dit kaartthema", - "fsUserbadge": "Activeer de login-knop", - "fsSearch": "Activeer de zoekbalk", - "fsWelcomeMessage": "Toon het welkomstbericht en de bijhorende tabbladen", - "fsLayers": "Toon de knop voor laagbediening", - "fsLayerControlToggle": "Toon de laagbediening meteen volledig", - "fsAddNew": "Activeer het toevoegen van nieuwe POI", - "fsGeolocation": "Toon het knopje voor geolocalisatie (enkel op mobiel)", - "fsIncludeCurrentBackgroundMap": "Gebruik de huidige achtergrond {name}", - "fsIncludeCurrentLayers": "Toon enkel de huidig getoonde lagen", - "fsIncludeCurrentLocation": "Start op de huidige locatie" - }, - "morescreen": { - "intro": "

Meer thematische kaarten

Vind je het leuk om geodata te verzamelen?
Hier vind je meer kaartthemas.", - "requestATheme": "Wil je een eigen kaartthema, vraag dit in de issue tracker.", - "streetcomplete": "Een andere, gelijkaardige Android-applicatie is StreetComplete.", - "createYourOwnTheme": "Maak je eigen MapComplete-kaart" - }, - "readYourMessages": "Gelieve eerst je berichten op OpenStreetMap te lezen alvorens nieuwe punten toe te voegen.", - "fewChangesBefore": "Gelieve eerst enkele vragen van bestaande punten te beantwoorden vooraleer zelf punten toe te voegen.", - "goToInbox": "Ga naar de berichten", - "getStartedLogin": "Login met OpenStreetMap om te beginnen", - "getStartedNewAccount": " of maak een nieuwe account aan", - "noTagsSelected": "Geen tags geselecteerd", - "customThemeIntro": "

Onofficiële thema's

De onderstaande thema's heb je eerder bezocht en zijn gemaakt door andere OpenStreetMappers.", - "aboutMapcomplete": "

Over MapComplete

Met MapComplete kun je OpenStreetMap verrijken met informatie over een bepaald thema. Beantwoord enkele vragen, en binnen een paar minuten is jouw bijdrage wereldwijd beschikbaar! De maker van het thema bepaalt de elementen, vragen en taalversies voor het thema.

Ontdek meer

MapComplete biedt altijd de volgende stap naar meer OpenStreetMap:

  • Indien ingebed in een website linkt het iframe naar de volledige MapComplete
  • De volledige versie heeft uitleg over OpenStreetMap
  • Bekijken kan altijd, maar wijzigen vereist een OSM-account
  • Als je niet aangemeld bent, wordt je gevraagd dit te doen
  • Als je minstens één vraag hebt beantwoord, kan je ook elementen toevoegen
  • Heb je genoeg changesets, dan verschijnen de OSM-tags, nog later links naar de wiki

Merk je een bug of wil je een extra feature? Wil je helpen vertalen? Bezoek dan de broncode en issue tracker.

Wil je je vorderingen zien? Volg de edits op OsmCha.

", - "backgroundMap": "Achtergrondkaart", - "layerSelection": { - "zoomInToSeeThisLayer": "Vergroot de kaart om deze laag te zien", - "title": "Selecteer lagen" - }, - "weekdays": { - "abbreviations": { - "monday": "Maan", - "tuesday": "Din", - "wednesday": "Woe", - "thursday": "Don", - "friday": "Vrij", - "saturday": "Zat", - "sunday": "Zon" - }, - "monday": "Maandag", - "tuesday": "Dinsdag", - "wednesday": "Woensdag", - "thursday": "Donderdag", - "friday": "Vrijdag", - "saturday": "Zaterdag", - "sunday": "Zondag" - }, - "opening_hours": { - "error_loading": "Fout: kon deze openingstijden niet weergeven.", - "open_during_ph": "Op een feestdag is deze zaak", - "opensAt": "vanaf", - "openTill": "tot", - "closed_until": "Gesloten - open op {date}", - "closed_permanently": "Gesloten voor onbepaalde tijd", - "open_24_7": "Dag en nacht open", - "ph_not_known": " ", - "ph_closed": "gesloten", - "ph_open": "open", - "ph_open_as_usual": "geopend zoals gewoonlijk", - "not_all_rules_parsed": "De openingsuren van deze zaak zijn ingewikkeld. De volgende regels worden niet getoond bij het ingeven:" - } - }, "reviews": { "title": "{count} beoordelingen", "title_singular": "Eén beoordeling", From 6ea1bd2012a2d46bf55799646bb696d04bad0473 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 05:01:37 +0200 Subject: [PATCH 083/110] Translation regeneration --- .../charging_station/charging_station.json | 6392 ++++++++--------- assets/tagRenderings/questions.json | 47 +- langs/layers/nl.json | 23 + langs/shared-questions/pt.json | 56 +- langs/shared-questions/pt_BR.json | 40 +- 5 files changed, 3301 insertions(+), 3257 deletions(-) diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index a643a8025..3441ee183 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,3233 +1,3233 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "it": "Stazioni di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjoner", - "ru": "Зарядные станции", - "zh_Hant": "充電站" - }, - "minzoom": 10, - "source": { - "osmTags": { - "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" - ] - } - }, - "title": { - "render": { - "en": "Charging station", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } - }, - "description": { - "en": "A charging station", - "it": "Una stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "En ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - }, - "calculatedTags": [ - "motorcar=feat.properties.motorcar ?? feat.properties.car" - ], - "tagRenderings": [ - "images", - { - "id": "Type", - "question": { - "en": "Which vehicles are allowed to charge here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "bicycle=yes", - "ifnot": "bicycle=no", - "then": { - "en": "bicycles can be charged here" - } - }, - { - "if": "motorcar=yes", - "extraTags": "car=", - "ifnot": { - "and": [ - "car=", - "motorcar=no" - ] - }, - "then": { - "en": "Cars can be charged here" - } - }, - { - "if": "scooter=yes", - "ifnot": "scooter=no", - "then": { - "en": "Scooters can be charged here" - } - }, - { - "if": "hgv=yes", - "ifnot": "hgv=no", - "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here" - } - }, - { - "if": "bus=yes", - "ifnot": "bus=no", - "then": { - "en": "Buses can be charged here" - } - } - ] + "id": "charging_station", + "name": { + "en": "Charging stations", + "it": "Stazioni di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjoner", + "ru": "Зарядные станции", + "zh_Hant": "充電站" }, - { - "id": "access", - "question": { - "en": "Who is allowed to use this charging station?" - }, - "render": { - "en": "Access is {access}" - }, - "freeform": { - "key": "access", - "addExtraTags": [ - "fixme=Freeform field used for access - doublecheck the value" - ] - }, - "mappings": [ - { - "if": "access=yes", - "then": "Anyone can use this charging station (payment might be needed)" - }, - { - "if": { + "minzoom": 10, + "source": { + "osmTags": { "or": [ - "access=permissive", - "access=public" + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" ] - }, - "then": "Anyone can use this charging station (payment might be needed)", - "hideInAnswer": true - }, - { - "if": "access=customers", - "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " - }, - { - "if": "access=private", - "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" } - ] }, - { - "id": "capacity", - "render": { - "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" - }, - "question": { - "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" - }, - "freeform": { - "key": "capacity", - "type": "pnat" - } + "title": { + "render": { + "en": "Charging station", + "it": "Stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + } }, - { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?" - }, - "multiAnswer": true, - "mappings": [ + "description": { + "en": "A charging station", + "it": "Una stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "En ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + }, + "calculatedTags": [ + "motorcar=feat.properties.motorcar ?? feat.properties.car" + ], + "tagRenderings": [ + "images", { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": { - "or": [ - "_country!=be", - "_country!=fr", - "_country!=ma", - "_country!=tn", - "_country!=pl", - "_country!=cs", - "_country!=sk", - "_country!=mo" + "id": "Type", + "question": { + "en": "Which vehicles are allowed to charge here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "bicycles can be charged here" + } + }, + { + "if": "motorcar=yes", + "extraTags": "car=", + "ifnot": { + "and": [ + "car=", + "motorcar=no" + ] + }, + "then": { + "en": "Cars can be charged here" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here" + } + } ] - } }, { - "if": { - "and": [ - "socket:schuko~*", - "socket:schuko!=1" - ] - }, - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:typee=1", - "ifnot": "socket:typee=", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - } - }, - { - "if": { - "and": [ - "socket:typee~*", - "socket:typee!=1" - ] - }, - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:chademo=1", - "ifnot": "socket:chademo=", - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:chademo~*", - "socket:chademo!=1" - ] - }, - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_cable=1", - "ifnot": "socket:type1_cable=", - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=1" - ] - }, - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1=1", - "ifnot": "socket:type1=", - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type1~*", - "socket:type1!=1" - ] - }, - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_combo=1", - "ifnot": "socket:type1_combo=", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=1" - ] - }, - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger=1", - "ifnot": "socket:tesla_supercharger=", - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2=1", - "ifnot": "socket:type2=", - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type2~*", - "socket:type2!=1" - ] - }, - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_combo=1", - "ifnot": "socket:type2_combo=", - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=1" - ] - }, - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_cable=1", - "ifnot": "socket:type2_cable=", - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=1" - ] - }, - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger_ccs=1", - "ifnot": "socket:tesla_supercharger_ccs=", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": { - "or": [ - { - "or": [ - "_country!=us" + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?" + }, + "render": { + "en": "Access is {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" ] - }, - { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } + }, + "mappings": [ + { + "if": "access=yes", + "then": "Anyone can use this charging station (payment might be needed)" + }, + { + "if": { + "or": [ + "access=permissive", + "access=public" + ] + }, + "then": "Anyone can use this charging station (payment might be needed)", + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " + }, + { + "if": "access=private", + "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" + } ] - } }, { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "or": [ - "_country=us" - ] - }, - { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:USB-A=1", - "ifnot": "socket:USB-A=", - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - } - }, - { - "if": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=1" - ] - }, - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - }, - "hideInAnswer": true - } - ] - }, - { - "id": "plugs-0", - "question": { - "en": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", - "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", - "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "voltage-0", - "question": { - "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" - }, - "freeform": { - "key": "socket:schuko:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:voltage=230 V", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "current-0", - "question": { - "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" - }, - "freeform": { - "key": "socket:schuko:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:current=16 A", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "power-output-0", - "question": { - "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" - }, - "freeform": { - "key": "socket:schuko:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:output=3.6 kw", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", - "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", - "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "voltage-1", - "question": { - "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" - }, - "freeform": { - "key": "socket:typee:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:voltage=230 V", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "current-1", - "question": { - "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" - }, - "freeform": { - "key": "socket:typee:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:current=16 A", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "power-output-1", - "question": { - "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" - }, - "freeform": { - "key": "socket:typee:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:output=3 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" - } - }, - { - "if": "socket:socket:typee:output=22 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type Chademo are available here?", - "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Chademo plugs of type Chademo available here", - "nl": "Hier zijn Chademo stekkers van het type Chademo" - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "voltage-2", - "question": { - "en": "What voltage do the plugs with Chademo offer?", - "nl": "Welke spanning levert de stekker van type Chademo " - }, - "render": { - "en": "Chademo outputs {socket:chademo:voltage} volt", - "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" - }, - "freeform": { - "key": "socket:chademo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:voltage=500 V", - "then": { - "en": "Chademo outputs 500 volt", - "nl": "Chademo heeft een spanning van 500 volt" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "current-2", - "question": { - "en": "What current do the plugs with Chademo offer?", - "nl": "Welke stroom levert de stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:current}A", - "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" - }, - "freeform": { - "key": "socket:chademo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:current=120 A", - "then": { - "en": "Chademo outputs at most 120 A", - "nl": "Chademo levert een stroom van maximaal 120 A" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "power-output-2", - "question": { - "en": "What power output does a single plug of type Chademo offer?", - "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:output}", - "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" - }, - "freeform": { - "key": "socket:chademo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:output=50 kw", - "then": { - "en": "Chademo outputs at most 50 kw", - "nl": "Chademo levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type Type 1 with cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", - "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "voltage-3", - "question": { - "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " - }, - "render": { - "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type1_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:voltage=200 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 200 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1_cable:voltage=240 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 240 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "current-3", - "question": { - "en": "What current do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "freeform": { - "key": "socket:type1_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:current=32 A", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 32 A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "power-output-3", - "question": { - "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "freeform": { - "key": "socket:type1_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:output=3.7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1_cable:output=7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type Type 1 without cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", - "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "voltage-4", - "question": { - "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " - }, - "render": { - "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" - }, - "freeform": { - "key": "socket:type1:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:voltage=200 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 200 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1:voltage=240 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 240 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "current-4", - "question": { - "en": "What current do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" - }, - "freeform": { - "key": "socket:type1:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:current=32 A", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 32 A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "power-output-4", - "question": { - "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" - }, - "freeform": { - "key": "socket:type1:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:output=3.7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1:output=6.6 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" - } - }, - { - "if": "socket:socket:type1:output=7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" - } - }, - { - "if": "socket:socket:type1:output=7.2 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", - "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", - "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "voltage-5", - "question": { - "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type1_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:voltage=400 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" - } - }, - { - "if": "socket:socket:type1_combo:voltage=1000 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "current-5", - "question": { - "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "freeform": { - "key": "socket:type1_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:current=50 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" - } - }, - { - "if": "socket:socket:type1_combo:current=125 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "power-output-5", - "question": { - "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "freeform": { - "key": "socket:type1_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:output=50 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=62.5 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=150 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=350 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type Tesla Supercharger are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", - "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "voltage-6", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger " - }, - "render": { - "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", - "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:voltage=480 V", - "then": { - "en": "Tesla Supercharger outputs 480 volt", - "nl": "Tesla Supercharger heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "current-6", - "question": { - "en": "What current do the plugs with Tesla Supercharger offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", - "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:current=125 A", - "then": { - "en": "Tesla Supercharger outputs at most 125 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger:current=350 A", - "then": { - "en": "Tesla Supercharger outputs at most 350 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "power-output-6", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", - "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:output=120 kw", - "then": { - "en": "Tesla Supercharger outputs at most 120 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=150 kw", - "then": { - "en": "Tesla Supercharger outputs at most 150 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=250 kw", - "then": { - "en": "Tesla Supercharger outputs at most 250 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type Type 2 (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", - "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "voltage-7", - "question": { - "en": "What voltage do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " - }, - "render": { - "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", - "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" - }, - "freeform": { - "key": "socket:type2:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:voltage=230 V", - "then": { - "en": "Type 2 (mennekes) outputs 230 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2:voltage=400 V", - "then": { - "en": "Type 2 (mennekes) outputs 400 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "current-7", - "question": { - "en": "What current do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" - }, - "freeform": { - "key": "socket:type2:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:current=16 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 16 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2:current=32 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 32 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "power-output-7", - "question": { - "en": "What power output does a single plug of type Type 2 (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" - }, - "freeform": { - "key": "socket:type2:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:output=11 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 11 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2:output=22 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 22 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", - "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "voltage-8", - "question": { - "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type2_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:voltage=500 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 500 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:type2_combo:voltage=920 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 920 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "current-8", - "question": { - "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "freeform": { - "key": "socket:type2_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:current=125 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 125 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:type2_combo:current=350 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 350 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "power-output-8", - "question": { - "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "freeform": { - "key": "socket:type2_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:output=50 kw", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 50 kw", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", - "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "voltage-9", - "question": { - "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type2_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:voltage=230 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 230 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2_cable:voltage=400 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 400 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "current-9", - "question": { - "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" - }, - "freeform": { - "key": "socket:type2_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:current=16 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 16 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2_cable:current=32 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 32 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "power-output-9", - "question": { - "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "freeform": { - "key": "socket:type2_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:output=11 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 11 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2_cable:output=22 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 22 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", - "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "voltage-10", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "current-10", - "question": { - "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:current=125 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:current=350 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "power-output-10", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type Tesla Supercharger (destination) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", - "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-11", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " - }, - "render": { - "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=480 V", - "then": { - "en": "Tesla Supercharger (destination) outputs 480 volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-11", - "question": { - "en": "What current do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=125 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 125 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=350 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 350 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-11", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=120 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 120 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=150 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 150 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=250 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 250 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", - "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", - "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-12", - "question": { - "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=230 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:tesla_destination:voltage=400 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-12", - "question": { - "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=16 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=32 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-12", - "question": { - "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=11 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=22 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-13", - "question": { - "en": "How much plugs of type USB to charge phones and small electronics are available here?", - "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" - }, - "render": { - "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", - "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "voltage-13", - "question": { - "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " - }, - "render": { - "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" - }, - "freeform": { - "key": "socket:USB-A:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:voltage=5 V", - "then": { - "en": "USB to charge phones and small electronics outputs 5 volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "current-13", - "question": { - "en": "What current do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" - }, - "freeform": { - "key": "socket:USB-A:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:current=1 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 1 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" - } - }, - { - "if": "socket:socket:USB-A:current=2 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 2 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "power-output-13", - "question": { - "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", - "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" - }, - "freeform": { - "key": "socket:USB-A:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:output=5w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 5w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" - } - }, - { - "if": "socket:socket:USB-A:output=10w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 10w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "Authentication", - "question": { - "en": "What kind of authentication is available at the charging station?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "No authentication is needed" - } - } - ] - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - }, - "it": { - "0": { - "then": "Non appartiene a una rete" - } - }, - "ja": { - "0": { - "then": "大規模な運営チェーンの一部ではない" - } - }, - "ru": { - "0": { - "then": "Не является частью более крупной сети" - } - }, - "zh_Hant": { - "0": { - "then": "不屬於大型網路" - } - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?" - }, - "mappings": [ - { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)" - } - } - ] - }, - { - "id": "fee/charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" - }, - "freeform": { - "key": "charge", - "addExtraTags": [ - "fee=yes" - ] - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no", - "charge=" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - } - } - ] - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { - "or": [ - "fee=yes", - "charge~*" - ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" + }, + { + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging stations are available here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" + ] + } + }, + { + "if": { + "and": [ + "socket:schuko~*", + "socket:schuko!=1" + ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } + }, + { + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" + ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" + ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" + ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "or": [ + "_country!=us" + ] + }, + { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "or": [ + "_country=us" + ] + }, + { + "and": [ + "car~*", + "car!=yes", + "motorcar~*", + "motorcar!=yes", + "hgv~*", + "hgv!=yes", + "bus~*", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + }, + "hideInAnswer": true + } + ] + }, + { + "id": "plugs-0", + "question": { + "en": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", + "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", + "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "voltage-0", + "question": { + "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" + }, + "freeform": { + "key": "socket:schuko:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:voltage=230 V", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "current-0", + "question": { + "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" + }, + "freeform": { + "key": "socket:schuko:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:current=16 A", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "power-output-0", + "question": { + "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" + }, + "freeform": { + "key": "socket:schuko:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:output=3.6 kw", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", + "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", + "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "voltage-1", + "question": { + "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" + }, + "freeform": { + "key": "socket:typee:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:voltage=230 V", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "current-1", + "question": { + "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" + }, + "freeform": { + "key": "socket:typee:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:current=16 A", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "power-output-1", + "question": { + "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" + }, + "freeform": { + "key": "socket:typee:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:output=3 kw", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" + } + }, + { + "if": "socket:socket:typee:output=22 kw", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type Chademo are available here?", + "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Chademo plugs of type Chademo available here", + "nl": "Hier zijn Chademo stekkers van het type Chademo" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "voltage-2", + "question": { + "en": "What voltage do the plugs with Chademo offer?", + "nl": "Welke spanning levert de stekker van type Chademo " + }, + "render": { + "en": "Chademo outputs {socket:chademo:voltage} volt", + "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" + }, + "freeform": { + "key": "socket:chademo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:voltage=500 V", + "then": { + "en": "Chademo outputs 500 volt", + "nl": "Chademo heeft een spanning van 500 volt" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "current-2", + "question": { + "en": "What current do the plugs with Chademo offer?", + "nl": "Welke stroom levert de stekker van type Chademo ?" + }, + "render": { + "en": "Chademo outputs at most {socket:chademo:current}A", + "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" + }, + "freeform": { + "key": "socket:chademo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:current=120 A", + "then": { + "en": "Chademo outputs at most 120 A", + "nl": "Chademo levert een stroom van maximaal 120 A" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "power-output-2", + "question": { + "en": "What power output does a single plug of type Chademo offer?", + "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" + }, + "render": { + "en": "Chademo outputs at most {socket:chademo:output}", + "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" + }, + "freeform": { + "key": "socket:chademo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:output=50 kw", + "then": { + "en": "Chademo outputs at most 50 kw", + "nl": "Chademo levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type Type 1 with cable (J1772) are available here?", + "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", + "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "voltage-3", + "question": { + "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " + }, + "render": { + "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type1_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:voltage=200 V", + "then": { + "en": "Type 1 with cable (J1772) outputs 200 volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1_cable:voltage=240 V", + "then": { + "en": "Type 1 with cable (J1772) outputs 240 volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "current-3", + "question": { + "en": "What current do the plugs with Type 1 with cable (J1772) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" + }, + "render": { + "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", + "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" + }, + "freeform": { + "key": "socket:type1_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:current=32 A", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 32 A", + "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "power-output-3", + "question": { + "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" + }, + "render": { + "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" + }, + "freeform": { + "key": "socket:type1_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:output=3.7 kw", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1_cable:output=7 kw", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 7 kw", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type Type 1 without cable (J1772) are available here?", + "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", + "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "voltage-4", + "question": { + "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " + }, + "render": { + "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" + }, + "freeform": { + "key": "socket:type1:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:voltage=200 V", + "then": { + "en": "Type 1 without cable (J1772) outputs 200 volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1:voltage=240 V", + "then": { + "en": "Type 1 without cable (J1772) outputs 240 volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "current-4", + "question": { + "en": "What current do the plugs with Type 1 without cable (J1772) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" + }, + "render": { + "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", + "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" + }, + "freeform": { + "key": "socket:type1:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:current=32 A", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 32 A", + "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "power-output-4", + "question": { + "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" + }, + "render": { + "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" + }, + "freeform": { + "key": "socket:type1:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:output=3.7 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1:output=6.6 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" + } + }, + { + "if": "socket:socket:type1:output=7 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 7 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" + } + }, + { + "if": "socket:socket:type1:output=7.2 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", + "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", + "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "voltage-5", + "question": { + "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type1_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:voltage=400 V", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" + } + }, + { + "if": "socket:socket:type1_combo:voltage=1000 V", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "current-5", + "question": { + "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" + }, + "freeform": { + "key": "socket:type1_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:current=50 A", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" + } + }, + { + "if": "socket:socket:type1_combo:current=125 A", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "power-output-5", + "question": { + "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" + }, + "freeform": { + "key": "socket:type1_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:output=50 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=62.5 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=150 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=350 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type Tesla Supercharger are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", + "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "voltage-6", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger " + }, + "render": { + "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", + "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:voltage=480 V", + "then": { + "en": "Tesla Supercharger outputs 480 volt", + "nl": "Tesla Supercharger heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "current-6", + "question": { + "en": "What current do the plugs with Tesla Supercharger offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" + }, + "render": { + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", + "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:current=125 A", + "then": { + "en": "Tesla Supercharger outputs at most 125 A", + "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger:current=350 A", + "then": { + "en": "Tesla Supercharger outputs at most 350 A", + "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "power-output-6", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" + }, + "render": { + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", + "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:output=120 kw", + "then": { + "en": "Tesla Supercharger outputs at most 120 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=150 kw", + "then": { + "en": "Tesla Supercharger outputs at most 150 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=250 kw", + "then": { + "en": "Tesla Supercharger outputs at most 250 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type Type 2 (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", + "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "voltage-7", + "question": { + "en": "What voltage do the plugs with Type 2 (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " + }, + "render": { + "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", + "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" + }, + "freeform": { + "key": "socket:type2:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:voltage=230 V", + "then": { + "en": "Type 2 (mennekes) outputs 230 volt", + "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2:voltage=400 V", + "then": { + "en": "Type 2 (mennekes) outputs 400 volt", + "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "current-7", + "question": { + "en": "What current do the plugs with Type 2 (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" + }, + "render": { + "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" + }, + "freeform": { + "key": "socket:type2:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:current=16 A", + "then": { + "en": "Type 2 (mennekes) outputs at most 16 A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2:current=32 A", + "then": { + "en": "Type 2 (mennekes) outputs at most 32 A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "power-output-7", + "question": { + "en": "What power output does a single plug of type Type 2 (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" + }, + "render": { + "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" + }, + "freeform": { + "key": "socket:type2:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:output=11 kw", + "then": { + "en": "Type 2 (mennekes) outputs at most 11 kw", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2:output=22 kw", + "then": { + "en": "Type 2 (mennekes) outputs at most 22 kw", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", + "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "voltage-8", + "question": { + "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type2_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:voltage=500 V", + "then": { + "en": "Type 2 CCS (mennekes) outputs 500 volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:type2_combo:voltage=920 V", + "then": { + "en": "Type 2 CCS (mennekes) outputs 920 volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "current-8", + "question": { + "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" + }, + "freeform": { + "key": "socket:type2_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:current=125 A", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 125 A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:type2_combo:current=350 A", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 350 A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "power-output-8", + "question": { + "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" + }, + "freeform": { + "key": "socket:type2_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:output=50 kw", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 50 kw", + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", + "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "voltage-9", + "question": { + "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type2_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:voltage=230 V", + "then": { + "en": "Type 2 with cable (mennekes) outputs 230 volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2_cable:voltage=400 V", + "then": { + "en": "Type 2 with cable (mennekes) outputs 400 volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "current-9", + "question": { + "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" + }, + "freeform": { + "key": "socket:type2_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:current=16 A", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 16 A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2_cable:current=32 A", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 32 A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "power-output-9", + "question": { + "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" + }, + "freeform": { + "key": "socket:type2_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:output=11 kw", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 11 kw", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2_cable:output=22 kw", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 22 kw", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", + "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "voltage-10", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "current-10", + "question": { + "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:current=125 A", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:current=350 A", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "power-output-10", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type Tesla Supercharger (destination) are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", + "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-11", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " + }, + "render": { + "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", + "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=480 V", + "then": { + "en": "Tesla Supercharger (destination) outputs 480 volt", + "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-11", + "question": { + "en": "What current do the plugs with Tesla Supercharger (destination) offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" + }, + "render": { + "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=125 A", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 125 A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=350 A", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 350 A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-11", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" + }, + "render": { + "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=120 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 120 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=150 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 150 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=250 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 250 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", + "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", + "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-12", + "question": { + "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=230 V", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:tesla_destination:voltage=400 V", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-12", + "question": { + "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=16 A", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=32 A", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-12", + "question": { + "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=11 kw", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=22 kw", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type USB to charge phones and small electronics are available here?", + "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" + }, + "render": { + "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", + "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "voltage-13", + "question": { + "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " + }, + "render": { + "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" + }, + "freeform": { + "key": "socket:USB-A:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:voltage=5 V", + "then": { + "en": "USB to charge phones and small electronics outputs 5 volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "current-13", + "question": { + "en": "What current do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" + }, + "freeform": { + "key": "socket:USB-A:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:current=1 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 1 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" + } + }, + { + "if": "socket:socket:USB-A:current=2 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 2 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "power-output-13", + "question": { + "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", + "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" + }, + "freeform": { + "key": "socket:USB-A:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:output=5w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 5w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" + } + }, + { + "if": "socket:socket:USB-A:output=10w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 10w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "Authentication", + "question": { + "en": "What kind of authentication is available at the charging station?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "No authentication is needed" + } + } + ] + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + }, + "it": { + "0": { + "then": "Non appartiene a una rete" + } + }, + "ja": { + "0": { + "then": "大規模な運営チェーンの一部ではない" + } + }, + "ru": { + "0": { + "then": "Не является частью более крупной сети" + } + }, + "zh_Hant": { + "0": { + "then": "不屬於大型網路" + } + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)" + } + } + ] + }, + { + "id": "fee/charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" + }, + "freeform": { + "key": "charge", + "addExtraTags": [ + "fee=yes" + ] + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no", + "charge=" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + } + } + ] + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk" + } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } + } + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" + } + } + ] + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}" + }, + "question": { + "en": "Is this charging station part of a network?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ + { + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network" + } + }, + { + "if": "network=none", + "then": { + "en": "Not part of a bigger network" + }, + "hideInAnswer": true + }, + { + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" + } + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?" + }, + "render": { + "en": "This charging station is operated by {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ + { + "if": { + "and": [ + "network:={operator}" + ] + }, + "then": { + "en": "Actually, {operator} is the network" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" + } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?" + }, + "render": { + "en": "In case of problems, call {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?" + }, + "render": { + "en": "In case of problems, send an email to {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website of the operator?" + }, + "render": { + "en": "More info on {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?" + }, + "render": { + "en": "Reference number is {ref}" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" + }, + "mappings": [ + { + "if": "operational_status=broken", + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + } + }, + { + "if": { + "and": [ + "construction:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + } + }, + { + "if": { + "and": [ + "disused:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + } + }, + { + "if": { + "and": [ + "amenity=charging_station", + "operational_status=" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging" + } + } + ] + } + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" } - } ] - } }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" - }, - "mappings": [ + "iconOverlays": [ { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd" - } + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross_bottom_right:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg", + "badge": true + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg", + "badge": true } - ] + ], + "width": { + "render": "8" }, - { - "id": "Network", - "render": { - "en": "Part of the network {network}" - }, - "question": { - "en": "Is this charging station part of a network?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ + "iconSize": { + "render": "50,50,bottom" + }, + "color": { + "render": "#00f" + }, + "presets": [ { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "Charging station" + }, + "preciseInput": { + "preferredBackground": "map" + } } - ] - }, - { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?" - }, - "render": { - "en": "This charging station is operated by {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ + ], + "wayHandling": 1, + "filter": [ { - "if": { - "and": [ - "network:={operator}" + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] + }, + { + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] + }, + { + "id": "connection_type", + "options": [ + { + "question": { + "en": "All connectors", + "nl": "Alle types" + } + }, + { + "question": { + "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", + "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", + "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a Chademo connector", + "nl": "Heeft een Chademo " + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a Type 1 with cable (J1772) connector", + "nl": "Heeft een Type 1 met kabel (J1772) " + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a Type 1 without cable (J1772) connector", + "nl": "Heeft een Type 1 zonder kabel (J1772) " + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", + "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger connector", + "nl": "Heeft een Tesla Supercharger " + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a Type 2 (mennekes) connector", + "nl": "Heeft een Type 2 (mennekes) " + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a Type 2 CCS (mennekes) connector", + "nl": "Heeft een Type 2 CCS (mennekes) " + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a Type 2 with cable (mennekes) connector", + "nl": "Heeft een Type 2 met kabel (J1772) " + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", + "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger (destination) connector", + "nl": "Heeft een Tesla Supercharger (destination) " + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", + "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a USB to charge phones and small electronics connector", + "nl": "Heeft een USB om GSMs en kleine electronica op te laden " + }, + "osmTags": "socket:USB-A~*" + } ] - }, - "then": { - "en": "Actually, {operator} is the network" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" } - ] - }, - { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?" - }, - "render": { - "en": "In case of problems, call {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?" - }, - "render": { - "en": "In case of problems, send an email to {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?" - }, - "render": { - "en": "More info on {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?" - }, - "render": { - "en": "Reference number is {ref}" - }, - "freeform": { - "key": "ref" - } - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?" - }, - "mappings": [ + ], + "units": [ { - "if": "operational_status=broken", - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot" - } + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ + { + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut" + } + }, + { + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren" + }, + "humanSingular": { + "en": " hour", + "nl": " uur" + } + }, + { + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day" + }, + "humanSingular": { + "en": " day", + "nl": " dag" + } + } + ] }, { - "if": { - "and": [ - "planned:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" - } + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage" + ], + "applicableUnits": [ + { + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt" + } + } + ], + "eraseInvalidValues": true }, { - "if": { - "and": [ - "construction:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" - } + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current" + ], + "applicableUnits": [ + { + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } + } + ], + "eraseInvalidValues": true }, { - "if": { - "and": [ - "disused:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" - } - }, - { - "if": { - "and": [ - "amenity=charging_station", - "operational_status=" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" - } + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt" + } + } + ], + "eraseInvalidValues": true } - ] - }, - { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging" - } - } - ] - } - ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } ] - }, - "iconOverlays": [ - { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross_bottom_right:#c22;" - }, - { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true - }, - { - "if": { - "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } - ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true - } - ], - "width": { - "render": "8" - }, - "iconSize": { - "render": "50,50,bottom" - }, - "color": { - "render": "#00f" - }, - "presets": [ - { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "Charging station" - }, - "preciseInput": { - "preferredBackground": "map" - } - } - ], - "wayHandling": 1, - "filter": [ - { - "id": "vehicle-type", - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen" - } - }, - { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen" - }, - "osmTags": "bicycle=yes" - }, - { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" - ] - } - } - ] - }, - { - "id": "working", - "options": [ - { - "question": { - "en": "Only working charging stations" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" - ] - } - } - ] - }, - { - "id": "connection_type", - "options": [ - { - "question": { - "en": "All connectors", - "nl": "Alle types" - } - }, - { - "question": { - "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", - "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", - "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a Chademo connector", - "nl": "Heeft een Chademo " - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a Type 1 with cable (J1772) connector", - "nl": "Heeft een Type 1 met kabel (J1772) " - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a Type 1 without cable (J1772) connector", - "nl": "Heeft een Type 1 zonder kabel (J1772) " - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", - "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger connector", - "nl": "Heeft een Tesla Supercharger " - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a Type 2 (mennekes) connector", - "nl": "Heeft een Type 2 (mennekes) " - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a Type 2 CCS (mennekes) connector", - "nl": "Heeft een Type 2 CCS (mennekes) " - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a Type 2 with cable (mennekes) connector", - "nl": "Heeft een Type 2 met kabel (J1772) " - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", - "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger (destination) connector", - "nl": "Heeft een Tesla Supercharger (destination) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", - "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a USB to charge phones and small electronics connector", - "nl": "Heeft een USB om GSMs en kleine electronica op te laden " - }, - "osmTags": "socket:USB-A~*" - } - ] - } - ], - "units": [ - { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ - { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut" - } - }, - { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren" - }, - "humanSingular": { - "en": " hour", - "nl": " uur" - } - }, - { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day" - }, - "humanSingular": { - "en": " day", - "nl": " dag" - } - } - ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage", - "socket:USB-A:voltage" - ], - "applicableUnits": [ - { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current", - "socket:USB-A:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output", - "socket:USB-A:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt" - } - }, - { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt" - } - } - ], - "eraseInvalidValues": true - } - ] } \ No newline at end of file diff --git a/assets/tagRenderings/questions.json b/assets/tagRenderings/questions.json index fb2e939bd..19a2babaa 100644 --- a/assets/tagRenderings/questions.json +++ b/assets/tagRenderings/questions.json @@ -21,7 +21,8 @@ "it": "Qual è il numero di telefono di {name}?", "pt_BR": "Qual o número de telefone de {name}?", "id": "Nomor telepon dari {name|?", - "pl": "Jaki jest numer telefonu do {name}?" + "pl": "Jaki jest numer telefonu do {name}?", + "pt": "Qual é o número de telefone de {name}?" }, "render": "{phone}", "freeform": { @@ -78,7 +79,8 @@ "de": "Was ist die Website von {name}?", "pt_BR": "Qual o site de {name}?", "pl": "Jaka jest strona internetowa {name}?", - "sv": "Vad är webbplatsen för {name}?" + "sv": "Vad är webbplatsen för {name}?", + "pt": "Qual é o sítio web de {name}?" }, "render": "{website}", "freeform": { @@ -89,7 +91,9 @@ "wheelchair-access": { "question": { "nl": "Is deze plaats rolstoeltoegankelijk?", - "en": "Is this place accessible with a wheelchair?" + "en": "Is this place accessible with a wheelchair?", + "pt": "Este lugar é acessível a utilizadores de cadeiras de rodas?", + "pt_BR": "Este lugar é acessível com uma cadeira de rodas?" }, "mappings": [ { @@ -100,7 +104,9 @@ }, "then": { "nl": "Deze plaats is speciaal aangepast voor gebruikers van een rolstoel", - "en": "This place is specially adapated for wheelchair users" + "en": "This place is specially adapated for wheelchair users", + "pt": "Este lugar está especialmente adaptado para utilizadores de cadeira de rodas", + "pt_BR": "Este lugar é especialmente adaptado para usuários de cadeira de rodas" } }, { @@ -111,7 +117,9 @@ }, "then": { "nl": "Deze plaats is vlot bereikbaar met een rolstoel", - "en": "This place is easily reachable with a wheelchair" + "en": "This place is easily reachable with a wheelchair", + "pt": "Este lugar é de fácil acesso com uma cadeira de rodas", + "pt_BR": "Este lugar é facilmente acessível com uma cadeira de rodas" } }, { @@ -122,7 +130,9 @@ }, "then": { "nl": "Deze plaats is bereikbaar met een rolstoel, maar het is niet makkelijk", - "en": "It is possible to reach this place in a wheelchair, but it is not easy" + "en": "It is possible to reach this place in a wheelchair, but it is not easy", + "pt": "É possível chegar a este local em cadeira de rodas, mas não é fácil", + "pt_BR": "É possível chegar a esse local em uma cadeira de rodas, mas não é fácil" } }, { @@ -133,7 +143,9 @@ }, "then": { "nl": "Niet rolstoeltoegankelijk", - "en": "This place is not reachable with a wheelchair" + "en": "This place is not reachable with a wheelchair", + "pt": "Este lugar não é acessível com uma cadeira de rodas", + "pt_BR": "Este lugar não é alcançável com uma cadeira de rodas" } } ] @@ -205,7 +217,8 @@ "it": "Quali sono gli orari di apertura di {name}?", "pt_BR": "Qual o horário de funcionamento de {name}?", "pl": "Jakie są godziny otwarcia {name}?", - "sv": "Vilka är öppettiderna för {name}?" + "sv": "Vilka är öppettiderna för {name}?", + "pt": "Qual é o horário de funcionamento de {name}?" }, "render": { "de": "

Öffnungszeiten

{opening_hours_table(opening_hours)}", @@ -218,7 +231,8 @@ "it": "

Orari di apertura

{opening_hours_table(opening_hours)}", "pl": "

Godziny otwarcia

{opening_hours_table(opening_hours)}", "pt_BR": "

Horário de funcionamento

{opening_hours_table(opening_hours)}", - "sv": "

Öppettider

{opening_hours_table(opening_hours)}" + "sv": "

Öppettider

{opening_hours_table(opening_hours)}", + "pt": "

Horário de funcionamento

{opening_hours_table(opening_hours)}" }, "freeform": { "key": "opening_hours", @@ -228,7 +242,9 @@ "payment-options": { "question": { "en": "Which methods of payment are accepted here?", - "nl": "Welke betaalmiddelen worden hier geaccepteerd?" + "nl": "Welke betaalmiddelen worden hier geaccepteerd?", + "pt": "Que métodos de pagamento são aceites aqui?", + "pt_BR": "Quais métodos de pagamento são aceitos aqui?" }, "multiAnswer": true, "mappings": [ @@ -237,7 +253,9 @@ "ifnot": "payment:cash=no", "then": { "en": "Cash is accepted here", - "nl": "Cash geld wordt hier aanvaard" + "nl": "Cash geld wordt hier aanvaard", + "pt": "Aceitam pagamento com dinheiro aqui", + "pt_BR": "Dinheiro é aceito aqui" } }, { @@ -245,7 +263,9 @@ "ifnot": "payment:cards=no", "then": { "en": "Payment cards are accepted here", - "nl": "Betalen met bankkaarten kan hier" + "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" } } ] @@ -280,7 +300,8 @@ "zh_Hant": "位於 {level} 樓", "fr": "Étage {level}", "pl": "Znajduje się na {level} piętrze", - "sv": "Ligger på {level}:e våningen" + "sv": "Ligger på {level}:e våningen", + "pt": "Está no {nível}º andar" }, "freeform": { "key": "level", diff --git a/langs/layers/nl.json b/langs/layers/nl.json index ae21e436e..003476b3c 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3539,6 +3539,29 @@ } }, "toilet": { + "filter": { + "0": { + "options": { + "0": { + "question": "Rolstoel toegankelijk" + } + } + }, + "1": { + "options": { + "0": { + "question": "Heeft een luiertafel" + } + } + }, + "2": { + "options": { + "0": { + "question": "Gratis toegankelijk" + } + } + } + }, "name": "Toiletten", "presets": { "0": { diff --git a/langs/shared-questions/pt.json b/langs/shared-questions/pt.json index a015f4301..9524e139d 100644 --- a/langs/shared-questions/pt.json +++ b/langs/shared-questions/pt.json @@ -1,7 +1,12 @@ { "undefined": { + "description": { + "question": "Ainda há algo de relevante que não tenha podido dar nas perguntas anteriores? Adicione-o aqui.
Não repita factos já declarados" + }, + "email": { + "question": "Qual é o endereço de e-mail de {name}?" + }, "level": { - "question": "Em que nível se encontra este elemento?", "mappings": { "0": { "then": "Está no subsolo" @@ -16,16 +21,31 @@ "then": "Está no primeiro andar" } }, + "question": "Em que nível se encontra este elemento?", "render": "Está no {nível}º andar" }, - "email": { - "question": "Qual é o endereço de e-mail de {name}?" + "opening_hours": { + "question": "Qual é o horário de funcionamento de {name}?", + "render": "

Horário de funcionamento

{opening_hours_table(opening_hours)}" }, - "description": { - "question": "Ainda há algo de relevante que não tenha podido dar nas perguntas anteriores? Adicione-o aqui.
Não repita factos já declarados" + "payment-options": { + "mappings": { + "0": { + "then": "Aceitam pagamento com dinheiro aqui" + }, + "1": { + "then": "Aceitam pagamento com cartões bancários aqui" + } + }, + "question": "Que métodos de pagamento são aceites aqui?" + }, + "phone": { + "question": "Qual é o número de telefone de {name}?" + }, + "website": { + "question": "Qual é o sítio web de {name}?" }, "wheelchair-access": { - "question": "Este lugar é acessível a utilizadores de cadeiras de rodas?", "mappings": { "0": { "then": "Este lugar está especialmente adaptado para utilizadores de cadeira de rodas" @@ -39,28 +59,8 @@ "3": { "then": "Este lugar não é acessível com uma cadeira de rodas" } - } - }, - "website": { - "question": "Qual é o sítio web de {name}?" - }, - "phone": { - "question": "Qual é o número de telefone de {name}?" - }, - "payment-options": { - "question": "Que métodos de pagamento são aceites aqui?", - "mappings": { - "0": { - "then": "Aceitam pagamento com dinheiro aqui" - }, - "1": { - "then": "Aceitam pagamento com cartões bancários aqui" - } - } - }, - "opening_hours": { - "render": "

Horário de funcionamento

{opening_hours_table(opening_hours)}", - "question": "Qual é o horário de funcionamento de {name}?" + }, + "question": "Este lugar é acessível a utilizadores de cadeiras de rodas?" } } } \ No newline at end of file diff --git a/langs/shared-questions/pt_BR.json b/langs/shared-questions/pt_BR.json index 38a5868bc..6599af377 100644 --- a/langs/shared-questions/pt_BR.json +++ b/langs/shared-questions/pt_BR.json @@ -28,6 +28,17 @@ "question": "Qual o horário de funcionamento de {name}?", "render": "

Horário de funcionamento

{opening_hours_table(opening_hours)}" }, + "payment-options": { + "mappings": { + "0": { + "then": "Dinheiro é aceito aqui" + }, + "1": { + "then": "Cartões de pagamento são aceitos aqui" + } + }, + "question": "Quais métodos de pagamento são aceitos aqui?" + }, "phone": { "question": "Qual o número de telefone de {name}?" }, @@ -35,32 +46,21 @@ "question": "Qual o site de {name}?" }, "wheelchair-access": { - "question": "Este lugar é acessível com uma cadeira de rodas?", "mappings": { - "3": { - "then": "Este lugar não é alcançável com uma cadeira de rodas" - }, - "2": { - "then": "É possível chegar a esse local em uma cadeira de rodas, mas não é fácil" + "0": { + "then": "Este lugar é especialmente adaptado para usuários de cadeira de rodas" }, "1": { "then": "Este lugar é facilmente acessível com uma cadeira de rodas" }, - "0": { - "then": "Este lugar é especialmente adaptado para usuários de cadeira de rodas" - } - } - }, - "payment-options": { - "question": "Quais métodos de pagamento são aceitos aqui?", - "mappings": { - "1": { - "then": "Cartões de pagamento são aceitos aqui" + "2": { + "then": "É possível chegar a esse local em uma cadeira de rodas, mas não é fácil" }, - "0": { - "then": "Dinheiro é aceito aqui" + "3": { + "then": "Este lugar não é alcançável com uma cadeira de rodas" } - } + }, + "question": "Este lugar é acessível com uma cadeira de rodas?" } } -} +} \ No newline at end of file From 087d5cdbef6fd02d757c0b49608e0aff40188217 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 05:23:50 +0200 Subject: [PATCH 084/110] Add a strt to package.json for when tailwind JIT isn't needed --- css/index-tailwind-output.css | 410 +++++++++++++++++----------------- package.json | 1 + 2 files changed, 206 insertions(+), 205 deletions(-) diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 4ed8a97b9..4cbe196ae 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -716,30 +716,6 @@ video { left: 0px; } -.top-0 { - top: 0px; -} - -.left-0 { - left: 0px; -} - -.right-0 { - right: 0px; -} - -.top-2 { - top: 0.5rem; -} - -.right-3 { - right: 0.75rem; -} - -.bottom-0 { - bottom: 0px; -} - .bottom-3 { bottom: 0.75rem; } @@ -764,6 +740,30 @@ video { top: 14rem; } +.top-0 { + top: 0px; +} + +.left-0 { + left: 0px; +} + +.right-0 { + right: 0px; +} + +.top-2 { + top: 0.5rem; +} + +.right-3 { + right: 0.75rem; +} + +.bottom-0 { + bottom: 0px; +} + .isolate { isolation: isolate; } @@ -788,22 +788,6 @@ video { float: none; } -.m-2 { - margin: 0.5rem; -} - -.m-1 { - margin: 0.25rem; -} - -.m-0 { - margin: 0px; -} - -.m-3 { - margin: 0.75rem; -} - .m-8 { margin: 2rem; } @@ -812,6 +796,10 @@ video { margin: 2.75rem; } +.m-1 { + margin: 0.25rem; +} + .m-5 { margin: 1.25rem; } @@ -820,10 +808,27 @@ video { margin: 0.125rem; } +.m-0 { + margin: 0px; +} + +.m-2 { + margin: 0.5rem; +} + +.m-3 { + margin: 0.75rem; +} + .m-4 { margin: 1rem; } +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + .mx-10 { margin-left: 2.5rem; margin-right: 2.5rem; @@ -839,15 +844,26 @@ video { margin-right: 1rem; } -.my-2 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; +.-ml-1 { + margin-left: -0.25rem; +} + +.mr-3 { + margin-right: 0.75rem; +} + +.ml-3 { + margin-left: 0.75rem; } .mb-2 { margin-bottom: 0.5rem; } +.mr-4 { + margin-right: 1rem; +} + .mt-3 { margin-top: 0.75rem; } @@ -860,6 +876,10 @@ video { margin-right: 0.5rem; } +.mt-2 { + margin-top: 0.5rem; +} + .mb-10 { margin-bottom: 2.5rem; } @@ -868,42 +888,6 @@ video { margin-top: 0.25rem; } -.ml-3 { - margin-left: 0.75rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.mb-8 { - margin-bottom: 2rem; -} - -.mr-0 { - margin-right: 0px; -} - -.mb-1 { - margin-bottom: 0.25rem; -} - -.mr-3 { - margin-right: 0.75rem; -} - -.-ml-1 { - margin-left: -0.25rem; -} - -.mr-4 { - margin-right: 1rem; -} - -.mt-2 { - margin-top: 0.5rem; -} - .mt-0 { margin-top: 0px; } @@ -912,10 +896,26 @@ video { margin-left: 0.5rem; } +.mt-4 { + margin-top: 1rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + .ml-1 { margin-left: 0.25rem; } +.mr-0 { + margin-right: 0px; +} + +.mb-1 { + margin-bottom: 0.25rem; +} + .mb-4 { margin-bottom: 1rem; } @@ -968,18 +968,34 @@ video { height: 100%; } -.h-12 { - height: 3rem; -} - .h-5 { height: 1.25rem; } +.h-24 { + height: 6rem; +} + +.h-10 { + height: 2.5rem; +} + +.h-8 { + height: 2rem; +} + +.h-12 { + height: 3rem; +} + .h-screen { height: 100vh; } +.h-11 { + height: 2.75rem; +} + .h-32 { height: 8rem; } @@ -996,22 +1012,6 @@ video { height: 1.5rem; } -.h-10 { - height: 2.5rem; -} - -.h-8 { - height: 2rem; -} - -.h-24 { - height: 6rem; -} - -.h-11 { - height: 2.75rem; -} - .h-3 { height: 0.75rem; } @@ -1032,6 +1032,18 @@ video { width: 100%; } +.w-5 { + width: 1.25rem; +} + +.w-10 { + width: 2.5rem; +} + +.w-8 { + width: 2rem; +} + .w-12 { width: 3rem; } @@ -1040,6 +1052,10 @@ video { width: 100vw; } +.w-11 { + width: 2.75rem; +} + .w-0 { width: 0px; } @@ -1068,32 +1084,16 @@ video { width: 50%; } -.w-10 { - width: 2.5rem; -} - -.w-8 { - width: 2rem; -} - -.w-5 { - width: 1.25rem; -} - -.w-11 { - width: 2.75rem; -} - -.min-w-\[20em\] { - min-width: 20em; -} - .min-w-min { min-width: -webkit-min-content; min-width: -moz-min-content; min-width: min-content; } +.min-w-\[20em\] { + min-width: 20em; +} + .max-w-full { max-width: 100%; } @@ -1156,14 +1156,14 @@ video { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } -.cursor-wait { - cursor: wait; -} - .cursor-pointer { cursor: pointer; } +.cursor-wait { + cursor: wait; +} + .resize { resize: both; } @@ -1247,10 +1247,6 @@ video { word-break: break-all; } -.rounded { - border-radius: 0.25rem; -} - .rounded-full { border-radius: 9999px; } @@ -1259,22 +1255,26 @@ video { border-radius: 1.5rem; } -.rounded-xl { - border-radius: 0.75rem; +.rounded { + border-radius: 0.25rem; } .rounded-lg { border-radius: 0.5rem; } -.border-4 { - border-width: 4px; +.rounded-xl { + border-radius: 0.75rem; } .border { border-width: 1px; } +.border-4 { + border-width: 4px; +} + .border-2 { border-width: 2px; } @@ -1311,14 +1311,24 @@ video { --tw-border-opacity: 0.5; } +.bg-white { + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); +} + .bg-blue-50 { --tw-bg-opacity: 1; background-color: rgba(239, 246, 255, var(--tw-bg-opacity)); } -.bg-white { +.bg-blue-100 { --tw-bg-opacity: 1; - background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); + background-color: rgba(219, 234, 254, var(--tw-bg-opacity)); +} + +.bg-gray-400 { + --tw-bg-opacity: 1; + background-color: rgba(156, 163, 175, var(--tw-bg-opacity)); } .bg-indigo-100 { @@ -1336,6 +1346,11 @@ video { background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); } +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); +} + .bg-gray-100 { --tw-bg-opacity: 1; background-color: rgba(243, 244, 246, var(--tw-bg-opacity)); @@ -1351,21 +1366,6 @@ video { background-color: rgba(254, 202, 202, var(--tw-bg-opacity)); } -.bg-blue-100 { - --tw-bg-opacity: 1; - background-color: rgba(219, 234, 254, var(--tw-bg-opacity)); -} - -.bg-gray-400 { - --tw-bg-opacity: 1; - background-color: rgba(156, 163, 175, var(--tw-bg-opacity)); -} - -.bg-gray-200 { - --tw-bg-opacity: 1; - background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); -} - .p-1\.5 { padding: 0.375rem; } @@ -1374,14 +1374,18 @@ video { padding: 0.25rem; } -.p-2 { - padding: 0.5rem; +.p-3 { + padding: 0.75rem; } .p-4 { padding: 1rem; } +.p-2 { + padding: 0.5rem; +} + .p-0\.5 { padding: 0.125rem; } @@ -1390,8 +1394,12 @@ video { padding: 0px; } -.p-3 { - padding: 0.75rem; +.pt-3 { + padding-top: 0.75rem; +} + +.pb-3 { + padding-bottom: 0.75rem; } .pl-4 { @@ -1402,12 +1410,16 @@ video { padding-bottom: 0px; } -.pt-6 { - padding-top: 1.5rem; +.pl-1 { + padding-left: 0.25rem; } -.pb-3 { - padding-bottom: 0.75rem; +.pr-1 { + padding-right: 0.25rem; +} + +.pt-6 { + padding-top: 1.5rem; } .pl-2 { @@ -1430,6 +1442,10 @@ video { padding-left: 0.75rem; } +.pr-0 { + padding-right: 0px; +} + .pb-2 { padding-bottom: 0.5rem; } @@ -1442,22 +1458,6 @@ video { padding-top: 0px; } -.pt-3 { - padding-top: 0.75rem; -} - -.pl-1 { - padding-left: 0.25rem; -} - -.pr-1 { - padding-right: 0.25rem; -} - -.pr-0 { - padding-right: 0px; -} - .pr-2 { padding-right: 0.5rem; } @@ -1474,6 +1474,16 @@ video { vertical-align: middle; } +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + .text-2xl { font-size: 1.5rem; line-height: 2rem; @@ -1489,16 +1499,6 @@ video { line-height: 1.75rem; } -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} - .text-4xl { font-size: 2.25rem; line-height: 2.5rem; @@ -1508,10 +1508,6 @@ video { font-weight: 700; } -.font-medium { - font-weight: 500; -} - .font-extrabold { font-weight: 800; } @@ -1520,6 +1516,10 @@ video { font-weight: 600; } +.font-medium { + font-weight: 500; +} + .uppercase { text-transform: uppercase; } @@ -1553,26 +1553,26 @@ video { letter-spacing: -0.025em; } -.text-gray-900 { - --tw-text-opacity: 1; - color: rgba(17, 24, 39, var(--tw-text-opacity)); -} - -.text-gray-500 { - --tw-text-opacity: 1; - color: rgba(107, 114, 128, var(--tw-text-opacity)); -} - .text-white { --tw-text-opacity: 1; color: rgba(255, 255, 255, var(--tw-text-opacity)); } +.text-gray-900 { + --tw-text-opacity: 1; + color: rgba(17, 24, 39, var(--tw-text-opacity)); +} + .text-gray-800 { --tw-text-opacity: 1; color: rgba(31, 41, 55, var(--tw-text-opacity)); } +.text-gray-500 { + --tw-text-opacity: 1; + color: rgba(107, 114, 128, var(--tw-text-opacity)); +} + .text-green-600 { --tw-text-opacity: 1; color: rgba(5, 150, 105, var(--tw-text-opacity)); @@ -2170,16 +2170,16 @@ li::marker { width: unset; } -.hover\:bg-indigo-200:hover { - --tw-bg-opacity: 1; - background-color: rgba(199, 210, 254, var(--tw-bg-opacity)); -} - .hover\:bg-blue-200:hover { --tw-bg-opacity: 1; background-color: rgba(191, 219, 254, var(--tw-bg-opacity)); } +.hover\:bg-indigo-200:hover { + --tw-bg-opacity: 1; + background-color: rgba(199, 210, 254, var(--tw-bg-opacity)); +} + .hover\:text-blue-800:hover { --tw-text-opacity: 1; color: rgba(30, 64, 175, var(--tw-text-opacity)); @@ -2214,14 +2214,14 @@ li::marker { height: 6rem; } - .sm\:w-auto { - width: auto; - } - .sm\:w-24 { width: 6rem; } + .sm\:w-auto { + width: auto; + } + .sm\:max-w-sm { max-width: 24rem; } diff --git a/package.json b/package.json index 6081d4af3..2505a2740 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "scripts": { "increase-memory": "export NODE_OPTIONS=--max_old_space_size=6182", "start": "npm run start:prepare && npm-run-all --parallel start:parallel:*", + "strt": "npm run start:prepare && npm run start:parallel:parcel", "start:prepare": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory", "start:parallel:parcel": "parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.png vendor/* vendor/*/*", "start:parallel:tailwindcli": "tailwindcss -i index.css -o css/index-tailwind-output.css --watch", From bedc57631366bf58d91a8be76b913f88f95c8c00 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 1 Oct 2021 05:24:10 +0200 Subject: [PATCH 085/110] Stabilize popups in ShowDataLayer --- Logic/Actors/SelectedFeatureHandler.ts | 14 +++++++++-- Models/Constants.ts | 2 +- UI/ShowDataLayer/ShowDataLayer.ts | 35 +++++++++++++------------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts index 440115984..73e69d72c 100644 --- a/Logic/Actors/SelectedFeatureHandler.ts +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -34,9 +34,19 @@ export default class SelectedFeatureHandler { }else{ // we search the element to select const feature = state.allElements.ContainingFeatures.get(h) - if(feature !== undefined){ - state.selectedElement.setData(feature) + if(feature === undefined){ + return; } + const currentlySeleced = state.selectedElement.data + if(currentlySeleced === undefined){ + state.selectedElement.setData(feature) + return; + } + if(currentlySeleced.properties?.id === feature.properties.id){ + // We already have the right feature + return; + } + state.selectedElement.setData(feature) } } diff --git a/Models/Constants.ts b/Models/Constants.ts index 52ed3c81b..50cfcf750 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.1-rc0"; + public static vNumber = "0.10.1-rc1"; public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 73c78f945..b8771aa7a 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -22,7 +22,7 @@ export default class ShowDataLayer { /** * If the selected element triggers, this is used to lookup the correct layer and to open the popup * Used to avoid a lot of callbacks on the selected element - * + * * Note: the key of this dictionary is 'feature.properties.id+features.geometry.type' as one feature might have multiple presentations * @private */ @@ -58,19 +58,19 @@ export default class ShowDataLayer { mp.addLayer(this.geoLayer) } } else { - if(this.geoLayer !== undefined){ + if (this.geoLayer !== undefined) { mp.removeLayer(this.geoLayer) } } }) - + State.state.selectedElement.addCallbackAndRunD(selected => { if (self._leafletMap.data === undefined) { return; } - const v = self.leafletLayersPerId.get(selected.properties.id+selected.geometry.type) + const v = self.leafletLayersPerId.get(selected.properties.id + selected.geometry.type) if (v === undefined) { return; } @@ -82,14 +82,14 @@ export default class ShowDataLayer { if (selected.properties.id !== feature.properties.id) { return; } - + if (feature.id !== feature.properties.id) { // Probably a feature which has renamed + // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too console.log("Not opening the popup for", feature, "as probably renamed") return; } if (selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again - && feature.id === feature.properties.id // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too ) { console.log("Opening popup of feature", feature) leafletLayer.openPopup() @@ -174,8 +174,8 @@ export default class ShowDataLayer { } let tagSource = State.state.allElements.getEventSourceById(feature.properties.id) - if(tagSource === undefined){ - tagSource = new UIEventSource(feature.properties) + if (tagSource === undefined) { + tagSource = new UIEventSource(feature.properties) } const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0) const style = layer.GenerateLeafletStyle(tagSource, clickable); @@ -221,10 +221,11 @@ export default class ShowDataLayer { let infobox: FeatureInfoBox = undefined; - const id = `popup-${feature.properties.id}-${this._cleanCount}` - popup.setContent(`
Rendering
`) + const id = `popup-${feature.properties.id}-${feature.geometry.type}-${this._cleanCount}` + popup.setContent(`
Popup for ${feature.properties.id} ${feature.geometry.type}
`) leafletLayer.on("popupopen", () => { + console.trace(`Opening the popup for ${feature.properties.id} ${feature.geometry.type}`) if (infobox === undefined) { const tags = State.state.allElements.getEventSourceById(feature.properties.id); infobox = new FeatureInfoBox(tags, layer); @@ -236,20 +237,18 @@ export default class ShowDataLayer { } }); } - - infobox.AttachTo(id) infobox.Activate(); - - - if(State.state.selectedElement.data?.properties?.id !== feature.properties.id){ - // x State.state.selectedElement.setData(feature) - } + State.state.selectedElement.setData(feature) + }); // Add the feature to the index to open the popup when needed - this.leafletLayersPerId.set(feature.properties.id+feature.geometry.type, {feature: feature, leafletlayer: leafletLayer}) + this.leafletLayersPerId.set(feature.properties.id + feature.geometry.type, { + feature: feature, + leafletlayer: leafletLayer + }) } From df34239256daf8af1a0da9f7397c998056e0ebca Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 2 Oct 2021 15:16:41 +0200 Subject: [PATCH 086/110] Fix questions: applicable mappings are now calculated dynamically; charging station theme now only shows applicable plugs based on the allowed vehicle types --- Docs/Tags_format.md | 2 + InitUiElements.ts | 7 +- Logic/FeatureSource/FeaturePipeline.ts | 33 +- Logic/UIEventSource.ts | 46 +- Logic/Web/Wikipedia.ts | 0 UI/Input/FixedInputElement.ts | 8 +- UI/Input/RadioButton.ts | 22 - UI/Popup/EditableTagRendering.ts | 68 +- UI/Popup/TagRenderingQuestion.ts | 132 +- assets/layers/charging_station/README.md | 18 +- .../charging_station/charging_station.json | 6558 +++++++++-------- assets/layers/charging_station/csvToJson.ts | 81 +- assets/layers/charging_station/types.csv | 30 +- test.ts | 31 +- 14 files changed, 3677 insertions(+), 3359 deletions(-) create mode 100644 Logic/Web/Wikipedia.ts diff --git a/Docs/Tags_format.md b/Docs/Tags_format.md index 4b42959ac..25db913cd 100644 --- a/Docs/Tags_format.md +++ b/Docs/Tags_format.md @@ -31,6 +31,8 @@ Strict not equals To check if a key does _not_ equal a certain value, use `key!=value`. This is converted behind the scenes to `key!~^value$` +If `key` is not present or empty, this will match too. + ### If key is present This implies that, to check if a key is present, `key!=` can be used. This will only match if the key is present and not diff --git a/InitUiElements.ts b/InitUiElements.ts index d0eb8b0d4..94500ec34 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -480,8 +480,11 @@ export class InitUiElements { const bounds = State.state.currentBounds.data - const tilebbox = BBox.fromTileIndex(source.tileIndex) - if (!tilebbox.overlapsWith(bounds)) { + if(bounds === undefined){ + // Map is not yet displayed + return false; + } + if (!source.bbox.overlapsWith(bounds)) { // Not within range return false } diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index e34d882ad..0dff97544 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -141,18 +141,25 @@ export default class FeaturePipeline { if (source.geojsonZoomLevel === undefined) { // This is a 'load everything at once' geojson layer - // We split them up into tiles anyway const src = new GeoJsonSource(filteredLayer) - TiledFeatureSource.createHierarchy(src, { - layer: src.layer, - minZoomLevel: 14, - dontEnforceMinZoom: true, - registerTile: (tile) => { - new RegisteringAllFromFeatureSourceActor(tile) - perLayerHierarchy.get(id).registerTile(tile) - tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) - } - }) + + if (source.isOsmCacheLayer) { + // We split them up into tiles anyway as it is an OSM source + TiledFeatureSource.createHierarchy(src, { + layer: src.layer, + minZoomLevel: 14, + dontEnforceMinZoom: true, + registerTile: (tile) => { + new RegisteringAllFromFeatureSourceActor(tile) + perLayerHierarchy.get(id).registerTile(tile) + tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) + } + }) + }else{ + new RegisteringAllFromFeatureSourceActor(src) + perLayerHierarchy.get(id).registerTile(src) + src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src)) + } } else { new DynamicGeoJsonTileSource( filteredLayer, @@ -312,9 +319,9 @@ export default class FeaturePipeline { if (zoom < 8) { zoom = zoom + 2 } - + const range = bbox.containingTileRange(zoom) - if(range.total > 100){ + if (range.total > 100) { return false } const self = this; diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index 5f51dc004..8401445d1 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -68,6 +68,50 @@ export class UIEventSource { return src } + /** + * Given a UIEVentSource with a list, returns a new UIEventSource which is only updated if the _contents_ of the list are different. + * E.g. + * const src = new UIEventSource([1,2,3]) + * const stable = UIEventSource.ListStabilized(src) + * src.addCallback(_ => console.log("src pinged")) + * stable.addCallback(_ => console.log("stable pinged)) + * src.setDate([...src.data]) + * + * This will only trigger 'src pinged' + * + * @param src + * @constructor + */ + public static ListStabilized(src: UIEventSource) : UIEventSource{ + + const stable = new UIEventSource(src.data) + src.addCallback(list => { + if(list === undefined){ + stable.setData(undefined) + return; + } + const oldList = stable.data + if(oldList === list){ + return; + } + if(oldList.length !== list.length){ + stable.setData(list); + return; + } + + for (let i = 0; i < list.length; i++) { + if(oldList[i] !== list[i]){ + stable.setData(list); + return; + } + } + + // No actual changes, so we don't do anything + return; + }) + return stable + } + /** * Adds a callback * @@ -88,7 +132,7 @@ export class UIEventSource { public addCallbackAndRun(callback: ((latestData: T) => (boolean | void | any))): UIEventSource { const doDeleteCallback = callback(this.data); - if (!doDeleteCallback) { + if (doDeleteCallback !== true) { this.addCallback(callback); } return this; diff --git a/Logic/Web/Wikipedia.ts b/Logic/Web/Wikipedia.ts new file mode 100644 index 000000000..e69de29bb diff --git a/UI/Input/FixedInputElement.ts b/UI/Input/FixedInputElement.ts index ee2e8d950..479aba16c 100644 --- a/UI/Input/FixedInputElement.ts +++ b/UI/Input/FixedInputElement.ts @@ -11,11 +11,15 @@ export class FixedInputElement extends InputElement { private readonly _el: HTMLElement; constructor(rendering: BaseUIElement | string, - value: T, + value: T | UIEventSource, comparator: ((t0: T, t1: T) => boolean) = undefined) { super(); this._comparator = comparator ?? ((t0, t1) => t0 == t1); - this.value = new UIEventSource(value); + if(value instanceof UIEventSource){ + this.value = value + }else{ + this.value = new UIEventSource(value); + } const selected = this.IsSelected; this._el = document.createElement("span") diff --git a/UI/Input/RadioButton.ts b/UI/Input/RadioButton.ts index 7653cc62f..73310559b 100644 --- a/UI/Input/RadioButton.ts +++ b/UI/Input/RadioButton.ts @@ -179,26 +179,4 @@ export class RadioButton extends InputElement { return form; } - /* - public ShowValue(t: T): boolean { - if (t === undefined) { - return false; - } - if (!this.IsValid(t)) { - return false; - } - // We check that what is selected matches the previous rendering - for (let i = 0; i < this._elements.length; i++) { - const e = this._elements[i]; - if (e.IsValid(t)) { - this._selectedElementIndex.setData(i); - e.GetValue().setData(t); - const radio = document.getElementById(this.IdFor(i)); - // @ts-ignore - radio?.checked = true; - return; - } - - } - }*/ } diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index f40118a05..5d7657fa5 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -18,41 +18,53 @@ export default class EditableTagRendering extends Toggle { units: Unit [], editMode = new UIEventSource(false) ) { + + // The tagrendering is hidden if: + // The answer is unknown. The questionbox will then show the question + // There is a condition hiding the answer + const renderingIsShown = tags.map(tags => + configuration.IsKnown(tags) && + (configuration?.condition?.matchesProperties(tags) ?? true)) + super( + new Lazy(() => EditableTagRendering.CreateRendering(tags, configuration, units, editMode)), + undefined, + renderingIsShown + ) + } + + private static CreateRendering(tags: UIEventSource, configuration: TagRenderingConfig, units: Unit[], editMode: UIEventSource) : BaseUIElement{ const answer: BaseUIElement = new TagRenderingAnswer(tags, configuration) answer.SetClass("w-full") let rendering = answer; if (configuration.question !== undefined && State.state?.featureSwitchUserbadge?.data) { // We have a question and editing is enabled - const editButton = - new Combine([Svg.pencil_ui()]).SetClass("block relative h-10 w-10 p-2 float-right").SetStyle("border: 1px solid black; border-radius: 0.7em") - .onClick(() => { - editMode.setData(true); - }); - - const answerWithEditButton = new Combine([answer, - new Toggle(editButton, + new Toggle(new Combine([Svg.pencil_ui()]).SetClass("block relative h-10 w-10 p-2 float-right").SetStyle("border: 1px solid black; border-radius: 0.7em") + .onClick(() => { + editMode.setData(true); + }), undefined, State.state.osmConnection.isLoggedIn) ]).SetClass("flex justify-between w-full") - const cancelbutton = - Translations.t.general.cancel.Clone() - .SetClass("btn btn-secondary mr-3") - .onClick(() => { - editMode.setData(false) - }); + const question = new Lazy(() => { + return new TagRenderingQuestion(tags, configuration, + { + units: units, + cancelButton: Translations.t.general.cancel.Clone() + .SetClass("btn btn-secondary mr-3") + .onClick(() => { + editMode.setData(false) + }), + afterSave: () => { + editMode.setData(false) + } + }) - const question = new Lazy(() => new TagRenderingQuestion(tags, configuration, - { - units: units, - cancelButton: cancelbutton, - afterSave: () => { - editMode.setData(false) - } - })) + + }) rendering = new Toggle( @@ -62,17 +74,7 @@ export default class EditableTagRendering extends Toggle { ) } rendering.SetClass("block w-full break-word text-default m-1 p-1 border-b border-gray-200 mb-2 pb-2") - // The tagrendering is hidden if: - // The answer is unknown. The questionbox will then show the question - // There is a condition hiding the answer - const renderingIsShown = tags.map(tags => - configuration.IsKnown(tags) && - (configuration?.condition?.matchesProperties(tags) ?? true)) - super( - rendering, - undefined, - renderingIsShown - ) + return rendering; } } \ No newline at end of file diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index ea6839e14..06c5b3dc3 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -31,7 +31,7 @@ import {Unit} from "../../Models/Unit"; * Shows the question element. * Note that the value _migh_ already be known, e.g. when selected or when changing the value */ -export default class TagRenderingQuestion extends Combine { +export default class TagRenderingQuestion extends VariableUiElement { constructor(tags: UIEventSource, configuration: TagRenderingConfig, @@ -43,6 +43,46 @@ export default class TagRenderingQuestion extends Combine { bottomText?: (src: UIEventSource) => BaseUIElement } ) { + + + const applicableMappings = + UIEventSource.ListStabilized(tags.map(tags => { + const applicableMappings : {if: TagsFilter, then: any, ifnot?: TagsFilter}[] = [] + for (const mapping of configuration.mappings) { + if (mapping.hideInAnswer === true) { + continue + } + if (mapping.hideInAnswer === false || mapping.hideInAnswer === undefined) { + applicableMappings.push(mapping) + continue + } + const condition = mapping.hideInAnswer; + const isShown = !condition.matchesProperties(tags) + if(isShown){ + applicableMappings.push(mapping) + } + } + return applicableMappings + })); + + super( + applicableMappings.map(applicableMappings => { + return TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options) + }) + ) + } + + private static GenerateFullQuestion(tags: UIEventSource, + applicableMappings: {if: TagsFilter, then: any, ifnot?: TagsFilter}[], + configuration: TagRenderingConfig, + options?: { + units?: Unit[], + afterSave?: () => void, + cancelButton?: BaseUIElement, + saveButtonConstr?: (src: UIEventSource) => BaseUIElement, + bottomText?: (src: UIEventSource) => BaseUIElement + } + ) { if (configuration === undefined) { throw "A question is needed for a question visualization" } @@ -52,7 +92,7 @@ export default class TagRenderingQuestion extends Combine { .SetClass("question-text"); - const inputElement: InputElement = TagRenderingQuestion.GenerateInputElement(configuration, applicableUnit, tags) + const inputElement: InputElement = TagRenderingQuestion.GenerateInputElement(configuration, applicableMappings, applicableUnit, tags) if (inputElement === undefined) { console.error("MultiAnswer failed - probably not a single option was possible", configuration) @@ -61,7 +101,7 @@ export default class TagRenderingQuestion extends Combine { const save = async () => { const selection = inputElement.GetValue().data; if (selection) { - await (State.state?.changes ?? new Changes()) + await (State.state?.changes ?? new Changes()) .applyAction(new ChangeTagAction( tags.data.id, selection, tags.data )) @@ -103,52 +143,77 @@ export default class TagRenderingQuestion extends Combine { ) ).SetClass("block break-all") } - super([ + return new Combine([ question, inputElement, options.cancelButton, saveButton, bottomTags] - ) - this.SetClass("question") + ).SetClass("question") } - private static GenerateInputElement(configuration: TagRenderingConfig, applicableUnit: Unit, tagsSource: UIEventSource): InputElement { + private static GenerateInputElement(configuration: TagRenderingConfig, + applicableMappings: {if: TagsFilter, then: any, ifnot?: TagsFilter}[], + applicableUnit: Unit, tagsSource: UIEventSource): InputElement { let inputEls: InputElement[]; - const mappings = (configuration.mappings ?? []) - .filter(mapping => { - if (mapping.hideInAnswer === true) { - return false; - } - return !(typeof (mapping.hideInAnswer) !== "boolean" && mapping.hideInAnswer.matchesProperties(tagsSource.data)); - }) + const ifNotsPresent = applicableMappings.some(mapping => mapping.ifnot !== undefined) - - function allIfNotsExcept(excludeIndex: number): TagsFilter[] { - if (configuration.mappings === undefined) { - return [] + function allIfNotsExcept(excludeIndex: number): UIEventSource { + if (configuration.mappings === undefined || configuration.mappings.length === 0) { + return undefined + } + if (!ifNotsPresent) { + return undefined } if (configuration.multiAnswer) { // The multianswer will do the ifnot configuration themself - return [] + return undefined } - return Utils.NoNull(configuration.mappings?.map((m, i) => excludeIndex === i ? undefined : m.ifnot)) + return tagsSource.map(currentTags => { + const negativeMappings = [] + + for (let i = 0; i < configuration.mappings.length; i++) { + const mapping = configuration.mappings[i]; + if (i === excludeIndex || mapping.ifnot === undefined) { + continue + } + + const hidden = mapping.hideInAnswer + if (hidden === undefined) { + negativeMappings.push(mapping.ifnot) + continue + } + if (hidden === true) { + continue + } + + if ((hidden).matchesProperties(currentTags)) { + // This option is currently hidden + continue + } + negativeMappings.push(mapping.ifnot) + + } + + return Utils.NoNull(negativeMappings) + }) + } const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); - const hasImages = mappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 + const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 - if (mappings.length < 8 || configuration.multiAnswer || hasImages) { - inputEls = (mappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i))); + if (applicableMappings.length < 8 || configuration.multiAnswer || hasImages || ifNotsPresent) { + inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i))); inputEls = Utils.NoNull(inputEls); } else { const dropdown: InputElement = new DropDown("", - mappings.map((mapping, i) => { + applicableMappings.map((mapping, i) => { return { - value: new And([mapping.if, ...allIfNotsExcept(i)]), + value: new And([mapping.if, ...allIfNotsExcept(i).data]), shown: Translations.WT(mapping.then).Clone() } }) @@ -178,6 +243,7 @@ export default class TagRenderingQuestion extends Combine { } + private static GenerateMultiAnswer( configuration: TagRenderingConfig, elements: InputElement[], freeformField: InputElement, ifNotSelected: TagsFilter[]): InputElement { @@ -278,17 +344,23 @@ export default class TagRenderingQuestion extends Combine { return inputEl; } + + /** + * Generates a (Fixed) input element for this mapping. + * Note that the mapping might hide itself if the condition is not met anymore. + * + * Returns: [the element itself, the value to select if not selected. The contents of this UIEventSource might swap to undefined if the conditions to show the answer are unmet] + */ private static GenerateMappingElement( tagsSource: UIEventSource, mapping: { if: TagsFilter, then: Translation, - hideInAnswer: boolean | TagsFilter - }, ifNot?: TagsFilter[]): InputElement { + }, ifNot?: UIEventSource): InputElement { - let tagging = mapping.if; - if (ifNot.length > 0) { - tagging = new And([tagging, ...ifNot]) + let tagging: TagsFilter | UIEventSource = mapping.if; + if (ifNot !== undefined) { + tagging = ifNot.map(ifNots => new And([mapping.if, ...ifNots])) } return new FixedInputElement( diff --git a/assets/layers/charging_station/README.md b/assets/layers/charging_station/README.md index 445b81c1e..bc88af2d7 100644 --- a/assets/layers/charging_station/README.md +++ b/assets/layers/charging_station/README.md @@ -16,4 +16,20 @@ AT this point, most of the work should be done; feel free to send a PR. If you w - Run 'ts-node csvToJson.ts' which will generate a new charging_station.json based on the protojson - Run`npm run query:licenses` to get an interactive program to add the license of your artwork, followed by `npm run generate:licenses` - Run `npm run generate:layeroverview` to generate the layer files -- Run `npm run start` to run the instance \ No newline at end of file +- Run `npm run start` to run the instance + +The CSV File +------------ + +The columns in the CSV file are: + +- key: the key as described on the wiki, starts with `socket:` +- image: The associated image (a .svg) +- description:en A description in english +- description:nl A description in english +- countryWhiteList: Only show this plug type in these countries +- countryBlackList: Don't show this plug type in these countries. NOt compatibel with the whiteList +- commonVoltages, commonCurrents, commonOutputs: common values for these tags +- associatedVehicleTypes and neverAssociatedWith: these work in tandem to hide options. + If every associated vehicle type is `no`, then the option is hidden + If at least one `neverAssociatedVehicleType` is `yes` and none of the associated types is yes, then the option is hidden too diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 3441ee183..850420aad 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,3233 +1,3379 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "it": "Stazioni di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjoner", - "ru": "Зарядные станции", - "zh_Hant": "充電站" - }, - "minzoom": 10, - "source": { - "osmTags": { - "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" + "id": "charging_station", + "name": { + "en": "Charging stations", + "it": "Stazioni di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjoner", + "ru": "Зарядные станции", + "zh_Hant": "充電站" + }, + "minzoom": 10, + "source": { + "osmTags": { + "or": [ + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" + ] + } + }, + "title": { + "render": { + "en": "Charging station", + "it": "Stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + } + }, + "description": { + "en": "A charging station", + "it": "Una stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "En ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + }, + "calculatedTags": [ + "motorcar=feat.properties.motorcar ?? feat.properties.car" + ], + "tagRenderings": [ + "images", + { + "id": "Type", + "question": { + "en": "Which vehicles are allowed to charge here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "bicycles can be charged here" + } + }, + { + "if": "motorcar=yes", + "extraTags": "car=", + "ifnot": { + "and": [ + "car=", + "motorcar=no" ] + }, + "then": { + "en": "Cars can be charged here" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here" + } } + ] }, - "title": { - "render": { - "en": "Charging station", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } - }, - "description": { - "en": "A charging station", - "it": "Una stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "En ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - }, - "calculatedTags": [ - "motorcar=feat.properties.motorcar ?? feat.properties.car" - ], - "tagRenderings": [ - "images", - { - "id": "Type", - "question": { - "en": "Which vehicles are allowed to charge here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "bicycle=yes", - "ifnot": "bicycle=no", - "then": { - "en": "bicycles can be charged here" - } - }, - { - "if": "motorcar=yes", - "extraTags": "car=", - "ifnot": { - "and": [ - "car=", - "motorcar=no" - ] - }, - "then": { - "en": "Cars can be charged here" - } - }, - { - "if": "scooter=yes", - "ifnot": "scooter=no", - "then": { - "en": "Scooters can be charged here" - } - }, - { - "if": "hgv=yes", - "ifnot": "hgv=no", - "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here" - } - }, - { - "if": "bus=yes", - "ifnot": "bus=no", - "then": { - "en": "Buses can be charged here" - } - } - ] - }, - { - "id": "access", - "question": { - "en": "Who is allowed to use this charging station?" - }, - "render": { - "en": "Access is {access}" - }, - "freeform": { - "key": "access", - "addExtraTags": [ - "fixme=Freeform field used for access - doublecheck the value" - ] - }, - "mappings": [ - { - "if": "access=yes", - "then": "Anyone can use this charging station (payment might be needed)" - }, - { - "if": { - "or": [ - "access=permissive", - "access=public" - ] - }, - "then": "Anyone can use this charging station (payment might be needed)", - "hideInAnswer": true - }, - { - "if": "access=customers", - "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " - }, - { - "if": "access=private", - "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" - } - ] - }, - { - "id": "capacity", - "render": { - "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" - }, - "question": { - "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" - }, - "freeform": { - "key": "capacity", - "type": "pnat" - } - }, - { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": { - "or": [ - "_country!=be", - "_country!=fr", - "_country!=ma", - "_country!=tn", - "_country!=pl", - "_country!=cs", - "_country!=sk", - "_country!=mo" - ] - } - }, - { - "if": { - "and": [ - "socket:schuko~*", - "socket:schuko!=1" - ] - }, - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:typee=1", - "ifnot": "socket:typee=", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - } - }, - { - "if": { - "and": [ - "socket:typee~*", - "socket:typee!=1" - ] - }, - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:chademo=1", - "ifnot": "socket:chademo=", - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:chademo~*", - "socket:chademo!=1" - ] - }, - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_cable=1", - "ifnot": "socket:type1_cable=", - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=1" - ] - }, - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1=1", - "ifnot": "socket:type1=", - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type1~*", - "socket:type1!=1" - ] - }, - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_combo=1", - "ifnot": "socket:type1_combo=", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=1" - ] - }, - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger=1", - "ifnot": "socket:tesla_supercharger=", - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2=1", - "ifnot": "socket:type2=", - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type2~*", - "socket:type2!=1" - ] - }, - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_combo=1", - "ifnot": "socket:type2_combo=", - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=1" - ] - }, - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_cable=1", - "ifnot": "socket:type2_cable=", - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=1" - ] - }, - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger_ccs=1", - "ifnot": "socket:tesla_supercharger_ccs=", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": { - "or": [ - { - "or": [ - "_country!=us" - ] - }, - { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "or": [ - "_country=us" - ] - }, - { - "and": [ - "car~*", - "car!=yes", - "motorcar~*", - "motorcar!=yes", - "hgv~*", - "hgv!=yes", - "bus~*", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:USB-A=1", - "ifnot": "socket:USB-A=", - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - } - }, - { - "if": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=1" - ] - }, - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - }, - "hideInAnswer": true - } - ] - }, - { - "id": "plugs-0", - "question": { - "en": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", - "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", - "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "voltage-0", - "question": { - "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" - }, - "freeform": { - "key": "socket:schuko:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:voltage=230 V", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "current-0", - "question": { - "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" - }, - "freeform": { - "key": "socket:schuko:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:current=16 A", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "power-output-0", - "question": { - "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" - }, - "freeform": { - "key": "socket:schuko:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:output=3.6 kw", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", - "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", - "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "voltage-1", - "question": { - "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" - }, - "freeform": { - "key": "socket:typee:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:voltage=230 V", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "current-1", - "question": { - "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" - }, - "freeform": { - "key": "socket:typee:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:current=16 A", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "power-output-1", - "question": { - "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" - }, - "freeform": { - "key": "socket:typee:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:output=3 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" - } - }, - { - "if": "socket:socket:typee:output=22 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type Chademo are available here?", - "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Chademo plugs of type Chademo available here", - "nl": "Hier zijn Chademo stekkers van het type Chademo" - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "voltage-2", - "question": { - "en": "What voltage do the plugs with Chademo offer?", - "nl": "Welke spanning levert de stekker van type Chademo " - }, - "render": { - "en": "Chademo outputs {socket:chademo:voltage} volt", - "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" - }, - "freeform": { - "key": "socket:chademo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:voltage=500 V", - "then": { - "en": "Chademo outputs 500 volt", - "nl": "Chademo heeft een spanning van 500 volt" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "current-2", - "question": { - "en": "What current do the plugs with Chademo offer?", - "nl": "Welke stroom levert de stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:current}A", - "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" - }, - "freeform": { - "key": "socket:chademo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:current=120 A", - "then": { - "en": "Chademo outputs at most 120 A", - "nl": "Chademo levert een stroom van maximaal 120 A" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "power-output-2", - "question": { - "en": "What power output does a single plug of type Chademo offer?", - "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:output}", - "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" - }, - "freeform": { - "key": "socket:chademo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:output=50 kw", - "then": { - "en": "Chademo outputs at most 50 kw", - "nl": "Chademo levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type Type 1 with cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", - "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "voltage-3", - "question": { - "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " - }, - "render": { - "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type1_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:voltage=200 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 200 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1_cable:voltage=240 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 240 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "current-3", - "question": { - "en": "What current do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "freeform": { - "key": "socket:type1_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:current=32 A", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 32 A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "power-output-3", - "question": { - "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "freeform": { - "key": "socket:type1_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:output=3.7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1_cable:output=7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type Type 1 without cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", - "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "voltage-4", - "question": { - "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " - }, - "render": { - "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" - }, - "freeform": { - "key": "socket:type1:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:voltage=200 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 200 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1:voltage=240 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 240 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "current-4", - "question": { - "en": "What current do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" - }, - "freeform": { - "key": "socket:type1:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:current=32 A", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 32 A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "power-output-4", - "question": { - "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" - }, - "freeform": { - "key": "socket:type1:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:output=3.7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1:output=6.6 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" - } - }, - { - "if": "socket:socket:type1:output=7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" - } - }, - { - "if": "socket:socket:type1:output=7.2 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", - "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", - "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "voltage-5", - "question": { - "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type1_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:voltage=400 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" - } - }, - { - "if": "socket:socket:type1_combo:voltage=1000 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "current-5", - "question": { - "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "freeform": { - "key": "socket:type1_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:current=50 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" - } - }, - { - "if": "socket:socket:type1_combo:current=125 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "power-output-5", - "question": { - "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "freeform": { - "key": "socket:type1_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:output=50 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=62.5 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=150 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=350 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type Tesla Supercharger are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", - "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "voltage-6", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger " - }, - "render": { - "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", - "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:voltage=480 V", - "then": { - "en": "Tesla Supercharger outputs 480 volt", - "nl": "Tesla Supercharger heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "current-6", - "question": { - "en": "What current do the plugs with Tesla Supercharger offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", - "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:current=125 A", - "then": { - "en": "Tesla Supercharger outputs at most 125 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger:current=350 A", - "then": { - "en": "Tesla Supercharger outputs at most 350 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "power-output-6", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", - "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:output=120 kw", - "then": { - "en": "Tesla Supercharger outputs at most 120 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=150 kw", - "then": { - "en": "Tesla Supercharger outputs at most 150 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=250 kw", - "then": { - "en": "Tesla Supercharger outputs at most 250 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type Type 2 (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", - "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "voltage-7", - "question": { - "en": "What voltage do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " - }, - "render": { - "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", - "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" - }, - "freeform": { - "key": "socket:type2:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:voltage=230 V", - "then": { - "en": "Type 2 (mennekes) outputs 230 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2:voltage=400 V", - "then": { - "en": "Type 2 (mennekes) outputs 400 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "current-7", - "question": { - "en": "What current do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" - }, - "freeform": { - "key": "socket:type2:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:current=16 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 16 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2:current=32 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 32 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "power-output-7", - "question": { - "en": "What power output does a single plug of type Type 2 (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" - }, - "freeform": { - "key": "socket:type2:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:output=11 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 11 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2:output=22 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 22 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", - "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "voltage-8", - "question": { - "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type2_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:voltage=500 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 500 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:type2_combo:voltage=920 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 920 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "current-8", - "question": { - "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "freeform": { - "key": "socket:type2_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:current=125 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 125 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:type2_combo:current=350 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 350 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "power-output-8", - "question": { - "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "freeform": { - "key": "socket:type2_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:output=50 kw", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 50 kw", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", - "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "voltage-9", - "question": { - "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type2_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:voltage=230 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 230 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2_cable:voltage=400 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 400 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "current-9", - "question": { - "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" - }, - "freeform": { - "key": "socket:type2_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:current=16 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 16 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2_cable:current=32 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 32 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "power-output-9", - "question": { - "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "freeform": { - "key": "socket:type2_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:output=11 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 11 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2_cable:output=22 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 22 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", - "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "voltage-10", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "current-10", - "question": { - "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:current=125 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:current=350 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "power-output-10", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type Tesla Supercharger (destination) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", - "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-11", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " - }, - "render": { - "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=480 V", - "then": { - "en": "Tesla Supercharger (destination) outputs 480 volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-11", - "question": { - "en": "What current do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=125 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 125 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=350 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 350 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-11", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=120 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 120 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=150 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 150 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=250 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 250 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", - "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", - "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-12", - "question": { - "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=230 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:tesla_destination:voltage=400 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-12", - "question": { - "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=16 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=32 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-12", - "question": { - "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=11 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=22 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-13", - "question": { - "en": "How much plugs of type USB to charge phones and small electronics are available here?", - "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" - }, - "render": { - "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", - "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "voltage-13", - "question": { - "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " - }, - "render": { - "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" - }, - "freeform": { - "key": "socket:USB-A:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:voltage=5 V", - "then": { - "en": "USB to charge phones and small electronics outputs 5 volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "current-13", - "question": { - "en": "What current do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" - }, - "freeform": { - "key": "socket:USB-A:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:current=1 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 1 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" - } - }, - { - "if": "socket:socket:USB-A:current=2 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 2 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "power-output-13", - "question": { - "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", - "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" - }, - "freeform": { - "key": "socket:USB-A:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:output=5w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 5w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" - } - }, - { - "if": "socket:socket:USB-A:output=10w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 10w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "Authentication", - "question": { - "en": "What kind of authentication is available at the charging station?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "No authentication is needed" - } - } - ] - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - }, - "it": { - "0": { - "then": "Non appartiene a una rete" - } - }, - "ja": { - "0": { - "then": "大規模な運営チェーンの一部ではない" - } - }, - "ru": { - "0": { - "then": "Не является частью более крупной сети" - } - }, - "zh_Hant": { - "0": { - "then": "不屬於大型網路" - } - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?" - }, - "mappings": [ - { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)" - } - } - ] - }, - { - "id": "fee/charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" - }, - "freeform": { - "key": "charge", - "addExtraTags": [ - "fee=yes" - ] - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no", - "charge=" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - } - } - ] - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { - "or": [ - "fee=yes", - "charge~*" - ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" - } - } - ] - } - }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" - }, - "mappings": [ - { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd" - } - } - ] - }, - { - "id": "Network", - "render": { - "en": "Part of the network {network}" - }, - "question": { - "en": "Is this charging station part of a network?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ - { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" - } - ] - }, - { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?" - }, - "render": { - "en": "This charging station is operated by {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "network:={operator}" - ] - }, - "then": { - "en": "Actually, {operator} is the network" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" - } - ] - }, - { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?" - }, - "render": { - "en": "In case of problems, call {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?" - }, - "render": { - "en": "In case of problems, send an email to {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?" - }, - "render": { - "en": "More info on {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?" - }, - "render": { - "en": "Reference number is {ref}" - }, - "freeform": { - "key": "ref" - } - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?" - }, - "mappings": [ - { - "if": "operational_status=broken", - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot" - } - }, - { - "if": { - "and": [ - "planned:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" - } - }, - { - "if": { - "and": [ - "construction:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" - } - }, - { - "if": { - "and": [ - "disused:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" - } - }, - { - "if": { - "and": [ - "amenity=charging_station", - "operational_status=" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" - } - } - ] - }, - { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging" - } - } - ] - } - ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } + { + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?" + }, + "render": { + "en": "Access is {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" ] + }, + "mappings": [ + { + "if": "access=yes", + "then": "Anyone can use this charging station (payment might be needed)" + }, + { + "if": { + "or": [ + "access=permissive", + "access=public" + ] + }, + "then": "Anyone can use this charging station (payment might be needed)", + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " + }, + { + "if": "access=private", + "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" + } + ] }, - "iconOverlays": [ + { + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" + } + }, + { + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging stations are available here?" + }, + "multiAnswer": true, + "mappings": [ { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross_bottom_right:#c22;" + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" + ] + } }, { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true + "if": { + "and": [ + "socket:schuko~*", + "socket:schuko!=1" + ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true }, { - "if": { + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } + }, + { + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" + ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "or": [ + { "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true - } - ], - "width": { - "render": "8" - }, - "iconSize": { - "render": "50,50,bottom" - }, - "color": { - "render": "#00f" - }, - "presets": [ + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "Charging station" - }, - "preciseInput": { - "preferredBackground": "map" + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" + ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" + ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country!=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + }, + "hideInAnswer": true + } + ] + }, + { + "id": "plugs-0", + "question": { + "en": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", + "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", + "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "voltage-0", + "question": { + "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" + }, + "freeform": { + "key": "socket:schuko:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:voltage=230 V", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "current-0", + "question": { + "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" + }, + "freeform": { + "key": "socket:schuko:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:current=16 A", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "power-output-0", + "question": { + "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" + }, + "freeform": { + "key": "socket:schuko:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:output=3.6 kw", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", + "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", + "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "voltage-1", + "question": { + "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" + }, + "freeform": { + "key": "socket:typee:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:voltage=230 V", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "current-1", + "question": { + "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" + }, + "freeform": { + "key": "socket:typee:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:current=16 A", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "power-output-1", + "question": { + "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" + }, + "freeform": { + "key": "socket:typee:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:output=3 kw", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" + } + }, + { + "if": "socket:socket:typee:output=22 kw", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type Chademo are available here?", + "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Chademo plugs of type Chademo available here", + "nl": "Hier zijn Chademo stekkers van het type Chademo" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "voltage-2", + "question": { + "en": "What voltage do the plugs with Chademo offer?", + "nl": "Welke spanning levert de stekker van type Chademo " + }, + "render": { + "en": "Chademo outputs {socket:chademo:voltage} volt", + "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" + }, + "freeform": { + "key": "socket:chademo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:voltage=500 V", + "then": { + "en": "Chademo outputs 500 volt", + "nl": "Chademo heeft een spanning van 500 volt" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "current-2", + "question": { + "en": "What current do the plugs with Chademo offer?", + "nl": "Welke stroom levert de stekker van type Chademo ?" + }, + "render": { + "en": "Chademo outputs at most {socket:chademo:current}A", + "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" + }, + "freeform": { + "key": "socket:chademo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:current=120 A", + "then": { + "en": "Chademo outputs at most 120 A", + "nl": "Chademo levert een stroom van maximaal 120 A" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "power-output-2", + "question": { + "en": "What power output does a single plug of type Chademo offer?", + "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" + }, + "render": { + "en": "Chademo outputs at most {socket:chademo:output}", + "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" + }, + "freeform": { + "key": "socket:chademo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:output=50 kw", + "then": { + "en": "Chademo outputs at most 50 kw", + "nl": "Chademo levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type Type 1 with cable (J1772) are available here?", + "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", + "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "voltage-3", + "question": { + "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " + }, + "render": { + "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type1_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:voltage=200 V", + "then": { + "en": "Type 1 with cable (J1772) outputs 200 volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1_cable:voltage=240 V", + "then": { + "en": "Type 1 with cable (J1772) outputs 240 volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "current-3", + "question": { + "en": "What current do the plugs with Type 1 with cable (J1772) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" + }, + "render": { + "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", + "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" + }, + "freeform": { + "key": "socket:type1_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:current=32 A", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 32 A", + "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "power-output-3", + "question": { + "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" + }, + "render": { + "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" + }, + "freeform": { + "key": "socket:type1_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:output=3.7 kw", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1_cable:output=7 kw", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 7 kw", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type Type 1 without cable (J1772) are available here?", + "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", + "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "voltage-4", + "question": { + "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " + }, + "render": { + "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" + }, + "freeform": { + "key": "socket:type1:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:voltage=200 V", + "then": { + "en": "Type 1 without cable (J1772) outputs 200 volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1:voltage=240 V", + "then": { + "en": "Type 1 without cable (J1772) outputs 240 volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "current-4", + "question": { + "en": "What current do the plugs with Type 1 without cable (J1772) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" + }, + "render": { + "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", + "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" + }, + "freeform": { + "key": "socket:type1:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:current=32 A", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 32 A", + "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "power-output-4", + "question": { + "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" + }, + "render": { + "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" + }, + "freeform": { + "key": "socket:type1:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:output=3.7 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1:output=6.6 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" + } + }, + { + "if": "socket:socket:type1:output=7 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 7 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" + } + }, + { + "if": "socket:socket:type1:output=7.2 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", + "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", + "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "voltage-5", + "question": { + "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type1_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:voltage=400 V", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" + } + }, + { + "if": "socket:socket:type1_combo:voltage=1000 V", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "current-5", + "question": { + "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" + }, + "freeform": { + "key": "socket:type1_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:current=50 A", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" + } + }, + { + "if": "socket:socket:type1_combo:current=125 A", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "power-output-5", + "question": { + "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" + }, + "freeform": { + "key": "socket:type1_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:output=50 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=62.5 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=150 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=350 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type Tesla Supercharger are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", + "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "voltage-6", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger " + }, + "render": { + "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", + "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:voltage=480 V", + "then": { + "en": "Tesla Supercharger outputs 480 volt", + "nl": "Tesla Supercharger heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "current-6", + "question": { + "en": "What current do the plugs with Tesla Supercharger offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" + }, + "render": { + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", + "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:current=125 A", + "then": { + "en": "Tesla Supercharger outputs at most 125 A", + "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger:current=350 A", + "then": { + "en": "Tesla Supercharger outputs at most 350 A", + "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "power-output-6", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" + }, + "render": { + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", + "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:output=120 kw", + "then": { + "en": "Tesla Supercharger outputs at most 120 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=150 kw", + "then": { + "en": "Tesla Supercharger outputs at most 150 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=250 kw", + "then": { + "en": "Tesla Supercharger outputs at most 250 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type Type 2 (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", + "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "voltage-7", + "question": { + "en": "What voltage do the plugs with Type 2 (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " + }, + "render": { + "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", + "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" + }, + "freeform": { + "key": "socket:type2:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:voltage=230 V", + "then": { + "en": "Type 2 (mennekes) outputs 230 volt", + "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2:voltage=400 V", + "then": { + "en": "Type 2 (mennekes) outputs 400 volt", + "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "current-7", + "question": { + "en": "What current do the plugs with Type 2 (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" + }, + "render": { + "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" + }, + "freeform": { + "key": "socket:type2:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:current=16 A", + "then": { + "en": "Type 2 (mennekes) outputs at most 16 A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2:current=32 A", + "then": { + "en": "Type 2 (mennekes) outputs at most 32 A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "power-output-7", + "question": { + "en": "What power output does a single plug of type Type 2 (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" + }, + "render": { + "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" + }, + "freeform": { + "key": "socket:type2:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:output=11 kw", + "then": { + "en": "Type 2 (mennekes) outputs at most 11 kw", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2:output=22 kw", + "then": { + "en": "Type 2 (mennekes) outputs at most 22 kw", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", + "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "voltage-8", + "question": { + "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type2_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:voltage=500 V", + "then": { + "en": "Type 2 CCS (mennekes) outputs 500 volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:type2_combo:voltage=920 V", + "then": { + "en": "Type 2 CCS (mennekes) outputs 920 volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "current-8", + "question": { + "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" + }, + "freeform": { + "key": "socket:type2_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:current=125 A", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 125 A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:type2_combo:current=350 A", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 350 A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "power-output-8", + "question": { + "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" + }, + "freeform": { + "key": "socket:type2_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:output=50 kw", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 50 kw", + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", + "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "voltage-9", + "question": { + "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type2_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:voltage=230 V", + "then": { + "en": "Type 2 with cable (mennekes) outputs 230 volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2_cable:voltage=400 V", + "then": { + "en": "Type 2 with cable (mennekes) outputs 400 volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "current-9", + "question": { + "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" + }, + "freeform": { + "key": "socket:type2_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:current=16 A", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 16 A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2_cable:current=32 A", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 32 A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "power-output-9", + "question": { + "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" + }, + "freeform": { + "key": "socket:type2_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:output=11 kw", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 11 kw", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2_cable:output=22 kw", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 22 kw", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", + "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "voltage-10", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "current-10", + "question": { + "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:current=125 A", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:current=350 A", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "power-output-10", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type Tesla Supercharger (destination) are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", + "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-11", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " + }, + "render": { + "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", + "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=480 V", + "then": { + "en": "Tesla Supercharger (destination) outputs 480 volt", + "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-11", + "question": { + "en": "What current do the plugs with Tesla Supercharger (destination) offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" + }, + "render": { + "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=125 A", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 125 A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=350 A", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 350 A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-11", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" + }, + "render": { + "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=120 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 120 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=150 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 150 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=250 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 250 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", + "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", + "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-12", + "question": { + "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=230 V", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:tesla_destination:voltage=400 V", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-12", + "question": { + "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=16 A", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=32 A", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-12", + "question": { + "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=11 kw", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=22 kw", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type USB to charge phones and small electronics are available here?", + "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" + }, + "render": { + "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", + "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "voltage-13", + "question": { + "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " + }, + "render": { + "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" + }, + "freeform": { + "key": "socket:USB-A:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:voltage=5 V", + "then": { + "en": "USB to charge phones and small electronics outputs 5 volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "current-13", + "question": { + "en": "What current do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" + }, + "freeform": { + "key": "socket:USB-A:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:current=1 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 1 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" + } + }, + { + "if": "socket:socket:USB-A:current=2 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 2 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "power-output-13", + "question": { + "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", + "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" + }, + "freeform": { + "key": "socket:USB-A:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:output=5w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 5w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" + } + }, + { + "if": "socket:socket:USB-A:output=10w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 10w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "Authentication", + "question": { + "en": "What kind of authentication is available at the charging station?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "No authentication is needed" + } + } + ] + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + }, + "it": { + "0": { + "then": "Non appartiene a una rete" + } + }, + "ja": { + "0": { + "then": "大規模な運営チェーンの一部ではない" + } + }, + "ru": { + "0": { + "then": "Не является частью более крупной сети" + } + }, + "zh_Hant": { + "0": { + "then": "不屬於大型網路" + } + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)" + } + } + ] + }, + { + "id": "fee/charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" + }, + "freeform": { + "key": "charge", + "addExtraTags": [ + "fee=yes" + ] + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no", + "charge=" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + } + } + ] + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk" } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } + } + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" + } } - ], - "wayHandling": 1, - "filter": [ + ] + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}" + }, + "question": { + "en": "Is this charging station part of a network?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ { - "id": "vehicle-type", - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen" - } - }, - { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen" - }, - "osmTags": "bicycle=yes" - }, - { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" - ] - } - } - ] + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network" + } }, { - "id": "working", - "options": [ - { - "question": { - "en": "Only working charging stations" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" - ] - } - } - ] + "if": "network=none", + "then": { + "en": "Not part of a bigger network" + }, + "hideInAnswer": true }, { - "id": "connection_type", - "options": [ - { - "question": { - "en": "All connectors", - "nl": "Alle types" - } - }, - { - "question": { - "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", - "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", - "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a Chademo connector", - "nl": "Heeft een Chademo " - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a Type 1 with cable (J1772) connector", - "nl": "Heeft een Type 1 met kabel (J1772) " - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a Type 1 without cable (J1772) connector", - "nl": "Heeft een Type 1 zonder kabel (J1772) " - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", - "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger connector", - "nl": "Heeft een Tesla Supercharger " - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a Type 2 (mennekes) connector", - "nl": "Heeft een Type 2 (mennekes) " - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a Type 2 CCS (mennekes) connector", - "nl": "Heeft een Type 2 CCS (mennekes) " - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a Type 2 with cable (mennekes) connector", - "nl": "Heeft een Type 2 met kabel (J1772) " - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", - "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger (destination) connector", - "nl": "Heeft een Tesla Supercharger (destination) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", - "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a USB to charge phones and small electronics connector", - "nl": "Heeft een USB om GSMs en kleine electronica op te laden " - }, - "osmTags": "socket:USB-A~*" - } - ] + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" } - ], - "units": [ + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?" + }, + "render": { + "en": "This charging station is operated by {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ - { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut" - } - }, - { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren" - }, - "humanSingular": { - "en": " hour", - "nl": " uur" - } - }, - { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day" - }, - "humanSingular": { - "en": " day", - "nl": " dag" - } - } + "if": { + "and": [ + "network:={operator}" ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage", - "socket:USB-A:voltage" - ], - "applicableUnits": [ - { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current", - "socket:USB-A:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output", - "socket:USB-A:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt" - } - }, - { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt" - } - } - ], - "eraseInvalidValues": true + }, + "then": { + "en": "Actually, {operator} is the network" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?" + }, + "render": { + "en": "In case of problems, call {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?" + }, + "render": { + "en": "In case of problems, send an email to {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website of the operator?" + }, + "render": { + "en": "More info on {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?" + }, + "render": { + "en": "Reference number is {ref}" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" + }, + "mappings": [ + { + "if": "operational_status=broken", + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + } + }, + { + "if": { + "and": [ + "construction:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + } + }, + { + "if": { + "and": [ + "disused:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + } + }, + { + "if": { + "and": [ + "amenity=charging_station", + "operational_status=" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging" + } + } + ] + } + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } ] + }, + "iconOverlays": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross_bottom_right:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg", + "badge": true + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg", + "badge": true + } + ], + "width": { + "render": "8" + }, + "iconSize": { + "render": "50,50,bottom" + }, + "color": { + "render": "#00f" + }, + "presets": [ + { + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "Charging station" + }, + "preciseInput": { + "preferredBackground": "map" + } + } + ], + "wayHandling": 1, + "filter": [ + { + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] + }, + { + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] + }, + { + "id": "connection_type", + "options": [ + { + "question": { + "en": "All connectors", + "nl": "Alle types" + } + }, + { + "question": { + "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", + "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", + "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a Chademo connector", + "nl": "Heeft een Chademo " + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a Type 1 with cable (J1772) connector", + "nl": "Heeft een Type 1 met kabel (J1772) " + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a Type 1 without cable (J1772) connector", + "nl": "Heeft een Type 1 zonder kabel (J1772) " + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", + "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger connector", + "nl": "Heeft een Tesla Supercharger " + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a Type 2 (mennekes) connector", + "nl": "Heeft een Type 2 (mennekes) " + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a Type 2 CCS (mennekes) connector", + "nl": "Heeft een Type 2 CCS (mennekes) " + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a Type 2 with cable (mennekes) connector", + "nl": "Heeft een Type 2 met kabel (J1772) " + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", + "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger (destination) connector", + "nl": "Heeft een Tesla Supercharger (destination) " + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", + "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a USB to charge phones and small electronics connector", + "nl": "Heeft een USB om GSMs en kleine electronica op te laden " + }, + "osmTags": "socket:USB-A~*" + } + ] + } + ], + "units": [ + { + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ + { + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut" + } + }, + { + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren" + }, + "humanSingular": { + "en": " hour", + "nl": " uur" + } + }, + { + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day" + }, + "humanSingular": { + "en": " day", + "nl": " dag" + } + } + ] + }, + { + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage" + ], + "applicableUnits": [ + { + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current" + ], + "applicableUnits": [ + { + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt" + } + } + ], + "eraseInvalidValues": true + } + ] } \ No newline at end of file diff --git a/assets/layers/charging_station/csvToJson.ts b/assets/layers/charging_station/csvToJson.ts index 2bd8d787a..14567f525 100644 --- a/assets/layers/charging_station/csvToJson.ts +++ b/assets/layers/charging_station/csvToJson.ts @@ -18,7 +18,8 @@ function loadCsv(file): { commonVoltages?: number[], commonCurrents?: number[], commonOutputs?: string[], - associatedVehicleTypes?:string[] + associatedVehicleTypes?: string[], + neverAssociatedWith?: string[] }[] { const entries: string[] = Utils.NoNull(readFileSync(file, "utf8").split("\n").map(str => str.trim())) const header = entries.shift().split(",") @@ -30,7 +31,7 @@ function loadCsv(file): { } const v = {} - const colonSeperated = ["commonVoltages", "commonOutputs", "commonCurrents", "countryWhiteList","countryBlackList","associatedVehicleTypes"] + const colonSeperated = ["commonVoltages", "commonOutputs", "commonCurrents", "countryWhiteList", "countryBlackList", "associatedVehicleTypes", "neverAssociatedWith"] const descriptionTranslations = new Map() for (let j = 0; j < header.length; j++) { const key = header[j]; @@ -54,7 +55,7 @@ function loadCsv(file): { function run(file, protojson) { const overview_question_answers = [] - const questions: (TagRenderingConfigJson & {"id": string})[] = [] + const questions: (TagRenderingConfigJson & { "id": string })[] = [] const filterOptions: { question: any, osmTags?: string } [] = [ { question: { @@ -65,7 +66,7 @@ function run(file, protojson) { ] const entries = loadCsv(file) - for (let i = 0; i < entries.length; i++){ + for (let i = 0; i < entries.length; i++) { const e = entries[i]; const txt = { en: `
${e.description.get("en")}
`, @@ -77,28 +78,43 @@ function run(file, protojson) { then: txt, } - if(e.countryWhiteList.length > 0 && e.countryBlackList.length > 0){ - throw "Error for type "+e.key+": don't defined both a whitelist and a blacklist" + if (e.countryWhiteList.length > 0 && e.countryBlackList.length > 0) { + throw "Error for type " + e.key + ": don't defined both a whitelist and a blacklist" } if (e.countryWhiteList.length > 0) { // This is a 'hideInAnswer', thus _reverse_ logic! const countries = e.countryWhiteList.map(country => "_country!=" + country) //HideInAnswer if it is in the wrong country json["hideInAnswer"] = {or: countries} - }else if (e.countryBlackList .length > 0) { + } else if (e.countryBlackList.length > 0) { const countries = e.countryBlackList.map(country => "_country=" + country) //HideInAnswer if it is in the wrong country json["hideInAnswer"] = {or: countries} } - - if(e.associatedVehicleTypes?.length > 0 && e.associatedVehicleTypes.indexOf("*") < 0){ + + if (e.associatedVehicleTypes?.length > 0 && e.associatedVehicleTypes.indexOf("*") < 0 && e.neverAssociatedWith?.length > 0) { // This plug only occurs if some vehicle specific vehicle type is present. // IF all of the needed vehicle types are explicitly NO, then we hide this type as well - let hideInAnswer : any = {and: [].concat(...e.associatedVehicleTypes.map(neededVehicle => [neededVehicle+"~*", neededVehicle+"!=yes"]))} - if(json["hideInAnswer"] !== undefined){ - hideInAnswer = {or: [json["hideInAnswer"], hideInAnswer]} + let associatedWith = {and: [].concat(...e.associatedVehicleTypes.map(neededVehicle => [neededVehicle + "=no"]))} + + // We also hide if: + // - One of the neverAssociatedVehiclesTYpes is set to 'yes' AND none of the associated types are set/yes + let neverAssociatedIsSet = { + and: [{ + or: e.neverAssociatedWith.map(vehicleType => vehicleType + "=yes") + }, + ...e.associatedVehicleTypes.map(associated => associated + "!=yes") + ] } - json["hideInAnswer"] = hideInAnswer + + let conditions = [associatedWith, neverAssociatedIsSet] + if (json["hideInAnswer"] !== undefined) { + conditions.push(json["hideInAnswer"]) + } + json["hideInAnswer"] = {or: conditions} + } + + overview_question_answers.push(json) // We add a second time for any amount to trigger a visualisation; but this is not an answer option @@ -115,7 +131,7 @@ function run(file, protojson) { const descrWithImage_nl = `${e.description.get("nl")} ` questions.push({ - "id":"plugs-"+i, + "id": "plugs-" + i, question: { en: `How much plugs of type ${descrWithImage_en} are available here?`, nl: `Hoeveel stekkers van type ${descrWithImage_nl} heeft dit oplaadpunt?`, @@ -134,7 +150,7 @@ function run(file, protojson) { }) questions.push({ - "id":"voltage-"+i, + "id": "voltage-" + i, question: { en: `What voltage do the plugs with ${descrWithImage_en} offer?`, nl: `Welke spanning levert de stekker van type ${descrWithImage_nl}` @@ -163,7 +179,7 @@ function run(file, protojson) { questions.push({ - "id":"current-"+i, + "id": "current-" + i, question: { en: `What current do the plugs with ${descrWithImage_en} offer?`, nl: `Welke stroom levert de stekker van type ${descrWithImage_nl}?`, @@ -192,7 +208,7 @@ function run(file, protojson) { questions.push({ - "id":"power-output-"+i, + "id": "power-output-" + i, question: { en: `What power output does a single plug of type ${descrWithImage_en} offer?`, nl: `Welk vermogen levert een enkele stekker van type ${descrWithImage_nl}?`, @@ -229,7 +245,7 @@ function run(file, protojson) { } const toggles = { - "id":"Available_charging_stations (generated)", + "id": "Available_charging_stations (generated)", "question": { "en": "Which charging stations are available here?" }, @@ -242,30 +258,29 @@ function run(file, protojson) { let protoString = readFileSync(protojson, "utf8") protoString = protoString.replace("{\"id\": \"$$$\"}", stringified.join(",\n")) - const proto = JSON.parse(protoString) + const proto = JSON.parse(protoString) proto.tagRenderings.forEach(tr => { - if(typeof tr === "string"){ + if (typeof tr === "string") { return; } - if(tr["id"] === undefined || typeof tr["id"] !== "string"){ + if (tr["id"] === undefined || typeof tr["id"] !== "string") { console.error(tr) throw "Every tagrendering should have an id, acting as comment" } }) - + proto["filter"].push({ - id:"connection_type", + id: "connection_type", options: filterOptions }) - const extraUnits = [ { appliesToKey: entries.map(e => e.key + ":voltage"), applicableUnits: [{ canonicalDenomination: 'V', - alternativeDenomination: ["v", "volt", "voltage",'V','Volt'], + alternativeDenomination: ["v", "volt", "voltage", 'V', 'Volt'], human: { en: "Volts", nl: "volt" @@ -277,7 +292,7 @@ function run(file, protojson) { appliesToKey: entries.map(e => e.key + ":current"), applicableUnits: [{ canonicalDenomination: 'A', - alternativeDenomination: ["a", "amp", "amperage",'A'], + alternativeDenomination: ["a", "amp", "amperage", 'A'], human: { en: "A", nl: "A" @@ -307,7 +322,7 @@ function run(file, protojson) { }, ]; - if(proto["units"] == undefined){ + if (proto["units"] == undefined) { proto["units"] = [] } proto["units"].push(...extraUnits) @@ -348,22 +363,22 @@ async function queryTagInfo(file, type, clean: ((s: string) => string)) { * @param origPath * @param newConfig */ -function mergeTranslations(origPath, newConfig: LayerConfigJson){ - const oldFile = JSON.parse(readFileSync(origPath, "utf-8")) - const newFile = newConfig +function mergeTranslations(origPath, newConfig: LayerConfigJson) { + const oldFile = JSON.parse(readFileSync(origPath, "utf-8")) + const newFile = newConfig const renderingsOld = oldFile.tagRenderings delete oldFile.tagRenderings const newRenderings = newFile.tagRenderings Utils.Merge(oldFile, newFile) for (const oldRendering of renderingsOld) { - + const oldRenderingName = oldRendering["id"] - if(oldRenderingName === undefined){ + if (oldRenderingName === undefined) { continue } const applicable = newRenderings.filter(r => r["id"] === oldRenderingName)[0] - if(applicable === undefined){ + if (applicable === undefined) { continue; } Utils.Merge(oldRendering, applicable) diff --git a/assets/layers/charging_station/types.csv b/assets/layers/charging_station/types.csv index ad7f9cc17..98d93f585 100644 --- a/assets/layers/charging_station/types.csv +++ b/assets/layers/charging_station/types.csv @@ -1,15 +1,15 @@ -key,image,description:en,countryWhiteList,countryBlackList,commonVoltages,commonCurrents,commonOutputs,description:nl,associatedVehicleTypes -socket:schuko,CEE7_4F.svg,Schuko wall plug without ground pin (CEE7/4 type F),be;fr;ma;tn;pl;cs;sk;mo,,230,16,3.6 kW,Schuko stekker zonder aardingspin (CEE7/4 type F),* -socket:typee,TypeE.svg,European wall plug with ground pin (CEE7/4 type E),,,230,16,3 kW;22 kW;,Europese stekker met aardingspin (CEE7/4 type E),* -socket:chademo,Chademo_type4.svg,Chademo,,,500,120,50 kW,Chademo,car;motorcar;hgv;bus -socket:type1_cable,Type1_J1772.svg,Type 1 with cable (J1772),,,200;240,32,3.7 kW;7 kW,Type 1 met kabel (J1772),car;motorcar;hgv;bus -socket:type1,Type1_J1772.svg,Type 1 without cable (J1772),,,200;240,32,3.7 kW;6.6 kW;7 kW;7.2 kW,Type 1 zonder kabel (J1772),car;motorcar;hgv;bus -socket:type1_combo,Type1-ccs.svg,Type 1 CCS (aka Type 1 Combo),,,400;1000,50;125,50 kW;62.5 kW;150 kW;350 kW;,Type 1 CCS (ook gekend als Type 1 Combo),car;motorcar;hgv;bus -socket:tesla_supercharger,Tesla-hpwc-model-s.svg,Tesla Supercharger,,,480,125;350,120 kW;150 kW;250 kW,Tesla Supercharger,car;motorcar;hgv;bus -socket:type2,Type2_socket.svg,Type 2 (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 (mennekes),car;motorcar;hgv;bus -socket:type2_combo,Type2_CCS.svg,Type 2 CCS (mennekes),,,500;920,125;350,50 kW,Type 2 CCS (mennekes),car;motorcar;hgv;bus -socket:type2_cable,Type2_tethered.svg,Type 2 with cable (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 met kabel (J1772),car;motorcar;hgv;bus -socket:tesla_supercharger_ccs,Type2_CCS.svg,Tesla Supercharger CCS (a branded type2_css),,,500;920,125;350,50 kW,Tesla Supercharger CCS (een type2 CCS met Tesla-logo),car;motorcar;hgv;bus -socket:tesla_destination,Tesla-hpwc-model-s.svg,Tesla Supercharger (destination),us,,480,125;350,120 kW;150 kW;250 kW,Tesla Supercharger (destination),car;motorcar;hgv;bus -socket:tesla_destination,Type2_tethered.svg,Tesla supercharger (destination (A Type 2 with cable branded as tesla),,us,230;400,16;32,11 kW;22 kW,Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo),car;motorcar;hgv;bus -socket:USB-A,usb_port.svg,USB to charge phones and small electronics,,,5,1;2,5W;10W,USB om GSMs en kleine electronica op te laden,* +key,image,description:en,countryWhiteList,countryBlackList,commonVoltages,commonCurrents,commonOutputs,description:nl,associatedVehicleTypes,neverAssociatedWith +socket:schuko,CEE7_4F.svg,Schuko wall plug without ground pin (CEE7/4 type F),be;fr;ma;tn;pl;cs;sk;mo,,230,16,3.6 kW,Schuko stekker zonder aardingspin (CEE7/4 type F),*, +socket:typee,TypeE.svg,European wall plug with ground pin (CEE7/4 type E),,,230,16,3 kW;22 kW;,Europese stekker met aardingspin (CEE7/4 type E),*, +socket:chademo,Chademo_type4.svg,Chademo,,,500,120,50 kW,Chademo,car;motorcar;hgv;bus,bicycle;scooter +socket:type1_cable,Type1_J1772.svg,Type 1 with cable (J1772),,,200;240,32,3.7 kW;7 kW,Type 1 met kabel (J1772),car;motorcar;hgv;bus,bicycle;scooter +socket:type1,Type1_J1772.svg,Type 1 without cable (J1772),,,200;240,32,3.7 kW;6.6 kW;7 kW;7.2 kW,Type 1 zonder kabel (J1772),car;motorcar;hgv;bus,bicycle;scooter +socket:type1_combo,Type1-ccs.svg,Type 1 CCS (aka Type 1 Combo),,,400;1000,50;125,50 kW;62.5 kW;150 kW;350 kW;,Type 1 CCS (ook gekend als Type 1 Combo),car;motorcar;hgv;bus,bicycle;scooter +socket:tesla_supercharger,Tesla-hpwc-model-s.svg,Tesla Supercharger,,,480,125;350,120 kW;150 kW;250 kW,Tesla Supercharger,car;motorcar;hgv;bus,bicycle;scooter +socket:type2,Type2_socket.svg,Type 2 (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 (mennekes),car;motorcar;hgv;bus,bicycle;scooter +socket:type2_combo,Type2_CCS.svg,Type 2 CCS (mennekes),,,500;920,125;350,50 kW,Type 2 CCS (mennekes),car;motorcar;hgv;bus,bicycle;scooter +socket:type2_cable,Type2_tethered.svg,Type 2 with cable (mennekes),,,230;400,16;32,11 kW;22 kW,Type 2 met kabel (J1772),car;motorcar;hgv;bus,bicycle;scooter +socket:tesla_supercharger_ccs,Type2_CCS.svg,Tesla Supercharger CCS (a branded type2_css),,,500;920,125;350,50 kW,Tesla Supercharger CCS (een type2 CCS met Tesla-logo),car;motorcar;hgv;bus,bicycle;scooter +socket:tesla_destination,Tesla-hpwc-model-s.svg,Tesla Supercharger (destination),us,,480,125;350,120 kW;150 kW;250 kW,Tesla Supercharger (destination),car;motorcar;hgv;bus,bicycle;scooter +socket:tesla_destination,Type2_tethered.svg,Tesla supercharger (destination (A Type 2 with cable branded as tesla),,us,230;400,16;32,11 kW;22 kW,Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo),car;motorcar;hgv;bus,bicycle;scooter +socket:USB-A,usb_port.svg,USB to charge phones and small electronics,,,5,1;2,5W;10W,USB om GSMs en kleine electronica op te laden,*, diff --git a/test.ts b/test.ts index e20e313f0..5dcdc0ce0 100644 --- a/test.ts +++ b/test.ts @@ -1,2 +1,31 @@ import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; -import {Translation} from "./UI/i18n/Translation"; +import TagRenderingQuestion from "./UI/Popup/TagRenderingQuestion"; +import {UIEventSource} from "./Logic/UIEventSource"; +import {VariableUiElement} from "./UI/Base/VariableUIElement"; + +const theme = AllKnownLayouts.allKnownLayouts.get("charging_stations") + +const tagRendering = theme.layers[0].tagRenderings.filter(tr => tr.id === "Available_charging_stations (generated)")[0] +const tag = new UIEventSource({ + id: "node/42", + amenity:"charging_station", + bicycle:"yes", + car:"no", + "motorcar":"no", + "hgv":"no", + bus:"no" +}) +window.tags = tag + +//const q = +new VariableUiElement(tag.map(_ => new TagRenderingQuestion(tag, tagRendering) )) + .SetStyle("width: 100px") + .AttachTo("maindiv") + + +window.setTimeout(_ => { + tag.data.bicycle="no" + tag.data.car = "yes" + tag.ping() + console.log("Pinged") +}, 2500) \ No newline at end of file From 9df263c362f821919899d1f7be4779a868df6b25 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 2 Oct 2021 17:57:54 +0200 Subject: [PATCH 087/110] Add Wikipedia page box --- Logic/UIEventSource.ts | 22 +- Logic/Web/Wikipedia.ts | 49 + UI/Base/Loading.ts | 16 +- UI/WikipediaBox.ts | 53 + .../charging_station/charging_station.json | 6684 ++++++++--------- assets/svg/loading.svg | 95 +- css/index-tailwind-output.css | 66 +- css/wikipedia.css | 32 + index.css | 6 + langs/en.json | 4 + test.html | 2 + test.ts | 33 +- 12 files changed, 3605 insertions(+), 3457 deletions(-) create mode 100644 UI/WikipediaBox.ts create mode 100644 css/wikipedia.css diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index 8401445d1..c30363f97 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -61,10 +61,30 @@ export class UIEventSource { run(); return source; } - + + /** + * Converts a promise into a UIVentsource, sets the UIEVentSource when the result is calculated. + * If the promise fails, the value will stay undefined + * @param promise + * @constructor + */ public static FromPromise(promise : Promise): UIEventSource{ const src = new UIEventSource(undefined) promise?.then(d => src.setData(d)) + promise?.catch(err => console.warn("Promise failed:", err)) + return src + } + + /** + * Converts a promise into a UIVentsource, sets the UIEVentSource when the result is calculated. + * If the promise fails, the value will stay undefined + * @param promise + * @constructor + */ + public static FromPromiseWithErr(promise : Promise): UIEventSource<{success: T} | {error: any}>{ + const src = new UIEventSource<{success: T}|{error: any}>(undefined) + promise?.then(d => src.setData({success:d})) + promise?.catch(err => src.setData({error: err})) return src } diff --git a/Logic/Web/Wikipedia.ts b/Logic/Web/Wikipedia.ts index e69de29bb..7c1260a28 100644 --- a/Logic/Web/Wikipedia.ts +++ b/Logic/Web/Wikipedia.ts @@ -0,0 +1,49 @@ +/** + * Some usefull utility functions around the wikipedia API + */ +import {Utils} from "../../Utils"; +import WikipediaBox from "../../UI/WikipediaBox"; + +export default class Wikipedia { + + /** + * When getting a wikipedia page data result, some elements (e.g. navigation, infoboxes, ...) should be removed if 'removeInfoBoxes' is set. + * We do this based on the classes. This set contains a blacklist of the classes to remove + * @private + */ + private static readonly classesToRemove = [ + "shortdescription", + "sidebar", + "infobox", + "mw-editsection", + "hatnote" // Often redirects + ] + + public static async GetArticle(options: { + pageName: string, + language?: "en" | string, + section?: number, + }): Promise { + + let section = "" + if (options.section !== undefined) { + section = "§ion=" + options.section + } + const url = `https://${options.language ?? "en"}.wikipedia.org/w/api.php?action=parse${section}&format=json&origin=*&prop=text&page=` + options.pageName + const response = await Utils.downloadJson(url) + const html = response["parse"]["text"]["*"]; + + const div = document.createElement("div") + div.innerHTML = html + const content = Array.from(div.children)[0] + + for (const forbiddenClass of Wikipedia.classesToRemove) { + const toRemove = content.getElementsByClassName(forbiddenClass) + for (const toRemoveElement of Array.from(toRemove)) { + toRemoveElement.parentElement?.removeChild(toRemoveElement) + } + } + return content.innerHTML; + } + +} \ No newline at end of file diff --git a/UI/Base/Loading.ts b/UI/Base/Loading.ts index 8711dec0d..03d0e3fdc 100644 --- a/UI/Base/Loading.ts +++ b/UI/Base/Loading.ts @@ -1,7 +1,17 @@ import {FixedUiElement} from "./FixedUiElement"; +import {Translation} from "../i18n/Translation"; +import Combine from "./Combine"; +import Svg from "../../Svg"; +import Translations from "../i18n/Translations"; -export default class Loading extends FixedUiElement { - constructor() { - super("Loading..."); // TODO to be improved +export default class Loading extends Combine { + constructor(msg?: Translation | string) { + const t = Translations.T(msg ) ?? Translations.t.general.loading.Clone(); + t.SetClass("pl-2") + super([ + Svg.loading_svg().SetClass("animate-spin").SetStyle("width: 1.5rem; height: 1.5rem; margin-bottom: 4px;"), + t + ]) + this.SetClass("flex m-1") } } \ No newline at end of file diff --git a/UI/WikipediaBox.ts b/UI/WikipediaBox.ts new file mode 100644 index 000000000..c3ab0fab7 --- /dev/null +++ b/UI/WikipediaBox.ts @@ -0,0 +1,53 @@ +import {UIEventSource} from "../Logic/UIEventSource"; +import {VariableUiElement} from "./Base/VariableUIElement"; +import Wikipedia from "../Logic/Web/Wikipedia"; +import Loading from "./Base/Loading"; +import {FixedUiElement} from "./Base/FixedUiElement"; +import Combine from "./Base/Combine"; +import BaseUIElement from "./BaseUIElement"; +import Title from "./Base/Title"; +import Translations from "./i18n/Translations"; +import Svg from "../Svg"; + +export default class WikipediaBox extends Combine{ + + constructor(options: { + pagename: string, + language: string + }) { + + const htmlContent = UIEventSource.FromPromiseWithErr(Wikipedia.GetArticle({ + pageName: options.pagename, + language: options.language, + removeInfoBoxes: true + })) + + const contents : UIEventSource = htmlContent.map(htmlContent => { + if(htmlContent === undefined){ + // Still loading + return new Loading("Loading wikipedia page").SetClass("p-4") + } + if(htmlContent["success"] !== undefined){ + return new FixedUiElement(htmlContent["success"]).SetClass("wikipedia-article") + } + if(htmlContent["error"]){ + return new FixedUiElement(htmlContent["error"]).SetClass("alert p-4") + } + + return undefined + + }) + + const scrollable = new Combine([new VariableUiElement(contents).SetClass("block pl-6 pt-2")]) + .SetClass("block overflow-auto normal-background rounded-lg") + super([ + new Combine([Svg.wikipedia_svg().SetStyle("width: 1.5rem").SetClass("mr-3"), + new Title(Translations.t.general.wikipedia.wikipediaboxTitle, 2)]).SetClass("flex"), + scrollable]) + + this + .SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col") + } + + +} \ No newline at end of file diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 850420aad..03be186b6 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,3379 +1,3379 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "it": "Stazioni di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjoner", - "ru": "Зарядные станции", - "zh_Hant": "充電站" - }, - "minzoom": 10, - "source": { - "osmTags": { - "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" - ] - } - }, - "title": { - "render": { - "en": "Charging station", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } - }, - "description": { - "en": "A charging station", - "it": "Una stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "En ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - }, - "calculatedTags": [ - "motorcar=feat.properties.motorcar ?? feat.properties.car" - ], - "tagRenderings": [ - "images", - { - "id": "Type", - "question": { - "en": "Which vehicles are allowed to charge here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "bicycle=yes", - "ifnot": "bicycle=no", - "then": { - "en": "bicycles can be charged here" - } - }, - { - "if": "motorcar=yes", - "extraTags": "car=", - "ifnot": { - "and": [ - "car=", - "motorcar=no" - ] - }, - "then": { - "en": "Cars can be charged here" - } - }, - { - "if": "scooter=yes", - "ifnot": "scooter=no", - "then": { - "en": "Scooters can be charged here" - } - }, - { - "if": "hgv=yes", - "ifnot": "hgv=no", - "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here" - } - }, - { - "if": "bus=yes", - "ifnot": "bus=no", - "then": { - "en": "Buses can be charged here" - } - } - ] + "id": "charging_station", + "name": { + "en": "Charging stations", + "it": "Stazioni di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjoner", + "ru": "Зарядные станции", + "zh_Hant": "充電站" }, - { - "id": "access", - "question": { - "en": "Who is allowed to use this charging station?" - }, - "render": { - "en": "Access is {access}" - }, - "freeform": { - "key": "access", - "addExtraTags": [ - "fixme=Freeform field used for access - doublecheck the value" - ] - }, - "mappings": [ - { - "if": "access=yes", - "then": "Anyone can use this charging station (payment might be needed)" - }, - { - "if": { + "minzoom": 10, + "source": { + "osmTags": { "or": [ - "access=permissive", - "access=public" + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" ] - }, - "then": "Anyone can use this charging station (payment might be needed)", - "hideInAnswer": true - }, - { - "if": "access=customers", - "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " - }, - { - "if": "access=private", - "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" } - ] }, - { - "id": "capacity", - "render": { - "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" - }, - "question": { - "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" - }, - "freeform": { - "key": "capacity", - "type": "pnat" - } + "title": { + "render": { + "en": "Charging station", + "it": "Stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + } }, - { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?" - }, - "multiAnswer": true, - "mappings": [ + "description": { + "en": "A charging station", + "it": "Una stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "En ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + }, + "calculatedTags": [ + "motorcar=feat.properties.motorcar ?? feat.properties.car" + ], + "tagRenderings": [ + "images", { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": { - "or": [ - "_country!=be", - "_country!=fr", - "_country!=ma", - "_country!=tn", - "_country!=pl", - "_country!=cs", - "_country!=sk", - "_country!=mo" + "id": "Type", + "question": { + "en": "Which vehicles are allowed to charge here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "bicycles can be charged here" + } + }, + { + "if": "motorcar=yes", + "extraTags": "car=", + "ifnot": { + "and": [ + "car=", + "motorcar=no" + ] + }, + "then": { + "en": "Cars can be charged here" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here" + } + } ] - } }, { - "if": { - "and": [ - "socket:schuko~*", - "socket:schuko!=1" - ] - }, - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:typee=1", - "ifnot": "socket:typee=", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - } - }, - { - "if": { - "and": [ - "socket:typee~*", - "socket:typee!=1" - ] - }, - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:chademo=1", - "ifnot": "socket:chademo=", - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?" + }, + "render": { + "en": "Access is {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } + }, + "mappings": [ + { + "if": "access=yes", + "then": "Anyone can use this charging station (payment might be needed)" + }, + { + "if": { + "or": [ + "access=permissive", + "access=public" + ] + }, + "then": "Anyone can use this charging station (payment might be needed)", + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " + }, + { + "if": "access=private", + "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" + } ] - } }, { - "if": { - "and": [ - "socket:chademo~*", - "socket:chademo!=1" - ] - }, - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_cable=1", - "ifnot": "socket:type1_cable=", - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=1" - ] - }, - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1=1", - "ifnot": "socket:type1=", - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1~*", - "socket:type1!=1" - ] - }, - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_combo=1", - "ifnot": "socket:type1_combo=", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=1" - ] - }, - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger=1", - "ifnot": "socket:tesla_supercharger=", - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2=1", - "ifnot": "socket:type2=", - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2~*", - "socket:type2!=1" - ] - }, - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_combo=1", - "ifnot": "socket:type2_combo=", - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=1" - ] - }, - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_cable=1", - "ifnot": "socket:type2_cable=", - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=1" - ] - }, - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger_ccs=1", - "ifnot": "socket:tesla_supercharger_ccs=", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country!=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:USB-A=1", - "ifnot": "socket:USB-A=", - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - } - }, - { - "if": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=1" - ] - }, - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - }, - "hideInAnswer": true - } - ] - }, - { - "id": "plugs-0", - "question": { - "en": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", - "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", - "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "voltage-0", - "question": { - "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" - }, - "freeform": { - "key": "socket:schuko:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:voltage=230 V", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "current-0", - "question": { - "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" - }, - "freeform": { - "key": "socket:schuko:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:current=16 A", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "power-output-0", - "question": { - "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" - }, - "freeform": { - "key": "socket:schuko:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:output=3.6 kw", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", - "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", - "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "voltage-1", - "question": { - "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" - }, - "freeform": { - "key": "socket:typee:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:voltage=230 V", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "current-1", - "question": { - "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" - }, - "freeform": { - "key": "socket:typee:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:current=16 A", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "power-output-1", - "question": { - "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" - }, - "freeform": { - "key": "socket:typee:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:output=3 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" - } - }, - { - "if": "socket:socket:typee:output=22 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type Chademo are available here?", - "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Chademo plugs of type Chademo available here", - "nl": "Hier zijn Chademo stekkers van het type Chademo" - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "voltage-2", - "question": { - "en": "What voltage do the plugs with Chademo offer?", - "nl": "Welke spanning levert de stekker van type Chademo " - }, - "render": { - "en": "Chademo outputs {socket:chademo:voltage} volt", - "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" - }, - "freeform": { - "key": "socket:chademo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:voltage=500 V", - "then": { - "en": "Chademo outputs 500 volt", - "nl": "Chademo heeft een spanning van 500 volt" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "current-2", - "question": { - "en": "What current do the plugs with Chademo offer?", - "nl": "Welke stroom levert de stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:current}A", - "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" - }, - "freeform": { - "key": "socket:chademo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:current=120 A", - "then": { - "en": "Chademo outputs at most 120 A", - "nl": "Chademo levert een stroom van maximaal 120 A" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "power-output-2", - "question": { - "en": "What power output does a single plug of type Chademo offer?", - "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:output}", - "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" - }, - "freeform": { - "key": "socket:chademo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:output=50 kw", - "then": { - "en": "Chademo outputs at most 50 kw", - "nl": "Chademo levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type Type 1 with cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", - "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "voltage-3", - "question": { - "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " - }, - "render": { - "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type1_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:voltage=200 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 200 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1_cable:voltage=240 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 240 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "current-3", - "question": { - "en": "What current do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "freeform": { - "key": "socket:type1_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:current=32 A", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 32 A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "power-output-3", - "question": { - "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "freeform": { - "key": "socket:type1_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:output=3.7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1_cable:output=7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type Type 1 without cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", - "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "voltage-4", - "question": { - "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " - }, - "render": { - "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" - }, - "freeform": { - "key": "socket:type1:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:voltage=200 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 200 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1:voltage=240 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 240 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "current-4", - "question": { - "en": "What current do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" - }, - "freeform": { - "key": "socket:type1:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:current=32 A", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 32 A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "power-output-4", - "question": { - "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" - }, - "freeform": { - "key": "socket:type1:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:output=3.7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1:output=6.6 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" - } - }, - { - "if": "socket:socket:type1:output=7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" - } - }, - { - "if": "socket:socket:type1:output=7.2 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", - "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", - "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "voltage-5", - "question": { - "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type1_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:voltage=400 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" - } - }, - { - "if": "socket:socket:type1_combo:voltage=1000 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "current-5", - "question": { - "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "freeform": { - "key": "socket:type1_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:current=50 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" - } - }, - { - "if": "socket:socket:type1_combo:current=125 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "power-output-5", - "question": { - "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "freeform": { - "key": "socket:type1_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:output=50 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=62.5 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=150 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=350 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type Tesla Supercharger are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", - "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "voltage-6", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger " - }, - "render": { - "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", - "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:voltage=480 V", - "then": { - "en": "Tesla Supercharger outputs 480 volt", - "nl": "Tesla Supercharger heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "current-6", - "question": { - "en": "What current do the plugs with Tesla Supercharger offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", - "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:current=125 A", - "then": { - "en": "Tesla Supercharger outputs at most 125 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger:current=350 A", - "then": { - "en": "Tesla Supercharger outputs at most 350 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "power-output-6", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", - "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:output=120 kw", - "then": { - "en": "Tesla Supercharger outputs at most 120 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=150 kw", - "then": { - "en": "Tesla Supercharger outputs at most 150 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=250 kw", - "then": { - "en": "Tesla Supercharger outputs at most 250 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type Type 2 (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", - "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "voltage-7", - "question": { - "en": "What voltage do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " - }, - "render": { - "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", - "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" - }, - "freeform": { - "key": "socket:type2:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:voltage=230 V", - "then": { - "en": "Type 2 (mennekes) outputs 230 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2:voltage=400 V", - "then": { - "en": "Type 2 (mennekes) outputs 400 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "current-7", - "question": { - "en": "What current do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" - }, - "freeform": { - "key": "socket:type2:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:current=16 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 16 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2:current=32 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 32 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "power-output-7", - "question": { - "en": "What power output does a single plug of type Type 2 (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" - }, - "freeform": { - "key": "socket:type2:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:output=11 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 11 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2:output=22 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 22 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", - "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "voltage-8", - "question": { - "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type2_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:voltage=500 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 500 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:type2_combo:voltage=920 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 920 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "current-8", - "question": { - "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "freeform": { - "key": "socket:type2_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:current=125 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 125 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:type2_combo:current=350 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 350 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "power-output-8", - "question": { - "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "freeform": { - "key": "socket:type2_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:output=50 kw", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 50 kw", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", - "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "voltage-9", - "question": { - "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type2_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:voltage=230 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 230 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2_cable:voltage=400 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 400 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "current-9", - "question": { - "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" - }, - "freeform": { - "key": "socket:type2_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:current=16 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 16 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2_cable:current=32 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 32 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "power-output-9", - "question": { - "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "freeform": { - "key": "socket:type2_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:output=11 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 11 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2_cable:output=22 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 22 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", - "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "voltage-10", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "current-10", - "question": { - "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:current=125 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:current=350 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "power-output-10", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type Tesla Supercharger (destination) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", - "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-11", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " - }, - "render": { - "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=480 V", - "then": { - "en": "Tesla Supercharger (destination) outputs 480 volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-11", - "question": { - "en": "What current do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=125 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 125 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=350 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 350 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-11", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=120 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 120 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=150 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 150 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=250 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 250 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", - "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", - "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-12", - "question": { - "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=230 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:tesla_destination:voltage=400 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-12", - "question": { - "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=16 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=32 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-12", - "question": { - "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=11 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=22 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-13", - "question": { - "en": "How much plugs of type USB to charge phones and small electronics are available here?", - "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" - }, - "render": { - "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", - "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "voltage-13", - "question": { - "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " - }, - "render": { - "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" - }, - "freeform": { - "key": "socket:USB-A:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:voltage=5 V", - "then": { - "en": "USB to charge phones and small electronics outputs 5 volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "current-13", - "question": { - "en": "What current do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" - }, - "freeform": { - "key": "socket:USB-A:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:current=1 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 1 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" - } - }, - { - "if": "socket:socket:USB-A:current=2 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 2 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "power-output-13", - "question": { - "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", - "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" - }, - "freeform": { - "key": "socket:USB-A:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:output=5w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 5w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" - } - }, - { - "if": "socket:socket:USB-A:output=10w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 10w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "Authentication", - "question": { - "en": "What kind of authentication is available at the charging station?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "No authentication is needed" - } - } - ] - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - }, - "it": { - "0": { - "then": "Non appartiene a una rete" - } - }, - "ja": { - "0": { - "then": "大規模な運営チェーンの一部ではない" - } - }, - "ru": { - "0": { - "then": "Не является частью более крупной сети" - } - }, - "zh_Hant": { - "0": { - "then": "不屬於大型網路" - } - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?" - }, - "mappings": [ - { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)" - } - } - ] - }, - { - "id": "fee/charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" - }, - "freeform": { - "key": "charge", - "addExtraTags": [ - "fee=yes" - ] - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no", - "charge=" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - } - } - ] - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { - "or": [ - "fee=yes", - "charge~*" - ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" + }, + { + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging stations are available here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" + ] + } + }, + { + "if": { + "and": [ + "socket:schuko~*", + "socket:schuko!=1" + ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } + }, + { + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" + ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" + ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" + ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country!=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + }, + "hideInAnswer": true + } + ] + }, + { + "id": "plugs-0", + "question": { + "en": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", + "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", + "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "voltage-0", + "question": { + "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" + }, + "freeform": { + "key": "socket:schuko:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:voltage=230 V", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "current-0", + "question": { + "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" + }, + "freeform": { + "key": "socket:schuko:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:current=16 A", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "power-output-0", + "question": { + "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" + }, + "render": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" + }, + "freeform": { + "key": "socket:schuko:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:output=3.6 kw", + "then": { + "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", + "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", + "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", + "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "voltage-1", + "question": { + "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" + }, + "freeform": { + "key": "socket:typee:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:voltage=230 V", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "current-1", + "question": { + "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" + }, + "freeform": { + "key": "socket:typee:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:current=16 A", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "power-output-1", + "question": { + "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" + }, + "render": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" + }, + "freeform": { + "key": "socket:typee:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:output=3 kw", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" + } + }, + { + "if": "socket:socket:typee:output=22 kw", + "then": { + "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", + "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type Chademo are available here?", + "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Chademo plugs of type Chademo available here", + "nl": "Hier zijn Chademo stekkers van het type Chademo" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "voltage-2", + "question": { + "en": "What voltage do the plugs with Chademo offer?", + "nl": "Welke spanning levert de stekker van type Chademo " + }, + "render": { + "en": "Chademo outputs {socket:chademo:voltage} volt", + "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" + }, + "freeform": { + "key": "socket:chademo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:voltage=500 V", + "then": { + "en": "Chademo outputs 500 volt", + "nl": "Chademo heeft een spanning van 500 volt" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "current-2", + "question": { + "en": "What current do the plugs with Chademo offer?", + "nl": "Welke stroom levert de stekker van type Chademo ?" + }, + "render": { + "en": "Chademo outputs at most {socket:chademo:current}A", + "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" + }, + "freeform": { + "key": "socket:chademo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:current=120 A", + "then": { + "en": "Chademo outputs at most 120 A", + "nl": "Chademo levert een stroom van maximaal 120 A" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "power-output-2", + "question": { + "en": "What power output does a single plug of type Chademo offer?", + "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" + }, + "render": { + "en": "Chademo outputs at most {socket:chademo:output}", + "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" + }, + "freeform": { + "key": "socket:chademo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:output=50 kw", + "then": { + "en": "Chademo outputs at most 50 kw", + "nl": "Chademo levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type Type 1 with cable (J1772) are available here?", + "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", + "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "voltage-3", + "question": { + "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " + }, + "render": { + "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type1_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:voltage=200 V", + "then": { + "en": "Type 1 with cable (J1772) outputs 200 volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1_cable:voltage=240 V", + "then": { + "en": "Type 1 with cable (J1772) outputs 240 volt", + "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "current-3", + "question": { + "en": "What current do the plugs with Type 1 with cable (J1772) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" + }, + "render": { + "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", + "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" + }, + "freeform": { + "key": "socket:type1_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:current=32 A", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 32 A", + "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "power-output-3", + "question": { + "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" + }, + "render": { + "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" + }, + "freeform": { + "key": "socket:type1_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:output=3.7 kw", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1_cable:output=7 kw", + "then": { + "en": "Type 1 with cable (J1772) outputs at most 7 kw", + "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type Type 1 without cable (J1772) are available here?", + "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", + "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "voltage-4", + "question": { + "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " + }, + "render": { + "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" + }, + "freeform": { + "key": "socket:type1:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:voltage=200 V", + "then": { + "en": "Type 1 without cable (J1772) outputs 200 volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1:voltage=240 V", + "then": { + "en": "Type 1 without cable (J1772) outputs 240 volt", + "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "current-4", + "question": { + "en": "What current do the plugs with Type 1 without cable (J1772) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" + }, + "render": { + "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", + "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" + }, + "freeform": { + "key": "socket:type1:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:current=32 A", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 32 A", + "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "power-output-4", + "question": { + "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" + }, + "render": { + "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" + }, + "freeform": { + "key": "socket:type1:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:output=3.7 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1:output=6.6 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" + } + }, + { + "if": "socket:socket:type1:output=7 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 7 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" + } + }, + { + "if": "socket:socket:type1:output=7.2 kw", + "then": { + "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", + "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", + "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", + "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "voltage-5", + "question": { + "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type1_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:voltage=400 V", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" + } + }, + { + "if": "socket:socket:type1_combo:voltage=1000 V", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "current-5", + "question": { + "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" + }, + "freeform": { + "key": "socket:type1_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:current=50 A", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" + } + }, + { + "if": "socket:socket:type1_combo:current=125 A", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "power-output-5", + "question": { + "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" + }, + "render": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" + }, + "freeform": { + "key": "socket:type1_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:output=50 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=62.5 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=150 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=350 kw", + "then": { + "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", + "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type Tesla Supercharger are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", + "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "voltage-6", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger " + }, + "render": { + "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", + "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:voltage=480 V", + "then": { + "en": "Tesla Supercharger outputs 480 volt", + "nl": "Tesla Supercharger heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "current-6", + "question": { + "en": "What current do the plugs with Tesla Supercharger offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" + }, + "render": { + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", + "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:current=125 A", + "then": { + "en": "Tesla Supercharger outputs at most 125 A", + "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger:current=350 A", + "then": { + "en": "Tesla Supercharger outputs at most 350 A", + "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "power-output-6", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" + }, + "render": { + "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", + "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:output=120 kw", + "then": { + "en": "Tesla Supercharger outputs at most 120 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=150 kw", + "then": { + "en": "Tesla Supercharger outputs at most 150 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=250 kw", + "then": { + "en": "Tesla Supercharger outputs at most 250 kw", + "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type Type 2 (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", + "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "voltage-7", + "question": { + "en": "What voltage do the plugs with Type 2 (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " + }, + "render": { + "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", + "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" + }, + "freeform": { + "key": "socket:type2:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:voltage=230 V", + "then": { + "en": "Type 2 (mennekes) outputs 230 volt", + "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2:voltage=400 V", + "then": { + "en": "Type 2 (mennekes) outputs 400 volt", + "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "current-7", + "question": { + "en": "What current do the plugs with Type 2 (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" + }, + "render": { + "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" + }, + "freeform": { + "key": "socket:type2:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:current=16 A", + "then": { + "en": "Type 2 (mennekes) outputs at most 16 A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2:current=32 A", + "then": { + "en": "Type 2 (mennekes) outputs at most 32 A", + "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "power-output-7", + "question": { + "en": "What power output does a single plug of type Type 2 (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" + }, + "render": { + "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" + }, + "freeform": { + "key": "socket:type2:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:output=11 kw", + "then": { + "en": "Type 2 (mennekes) outputs at most 11 kw", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2:output=22 kw", + "then": { + "en": "Type 2 (mennekes) outputs at most 22 kw", + "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", + "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "voltage-8", + "question": { + "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type2_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:voltage=500 V", + "then": { + "en": "Type 2 CCS (mennekes) outputs 500 volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:type2_combo:voltage=920 V", + "then": { + "en": "Type 2 CCS (mennekes) outputs 920 volt", + "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "current-8", + "question": { + "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" + }, + "freeform": { + "key": "socket:type2_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:current=125 A", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 125 A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:type2_combo:current=350 A", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 350 A", + "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "power-output-8", + "question": { + "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" + }, + "render": { + "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" + }, + "freeform": { + "key": "socket:type2_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:output=50 kw", + "then": { + "en": "Type 2 CCS (mennekes) outputs at most 50 kw", + "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", + "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", + "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "voltage-9", + "question": { + "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", + "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type2_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:voltage=230 V", + "then": { + "en": "Type 2 with cable (mennekes) outputs 230 volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2_cable:voltage=400 V", + "then": { + "en": "Type 2 with cable (mennekes) outputs 400 volt", + "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "current-9", + "question": { + "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", + "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" + }, + "freeform": { + "key": "socket:type2_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:current=16 A", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 16 A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2_cable:current=32 A", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 32 A", + "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "power-output-9", + "question": { + "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" + }, + "render": { + "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" + }, + "freeform": { + "key": "socket:type2_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:output=11 kw", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 11 kw", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2_cable:output=22 kw", + "then": { + "en": "Type 2 with cable (mennekes) outputs at most 22 kw", + "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", + "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "voltage-10", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "current-10", + "question": { + "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:current=125 A", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:current=350 A", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "power-output-10", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" + }, + "render": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", + "then": { + "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", + "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type Tesla Supercharger (destination) are available here?", + "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", + "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-11", + "question": { + "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", + "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " + }, + "render": { + "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", + "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=480 V", + "then": { + "en": "Tesla Supercharger (destination) outputs 480 volt", + "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-11", + "question": { + "en": "What current do the plugs with Tesla Supercharger (destination) offer?", + "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" + }, + "render": { + "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=125 A", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 125 A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=350 A", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 350 A", + "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-11", + "question": { + "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" + }, + "render": { + "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=120 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 120 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=150 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 150 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=250 kw", + "then": { + "en": "Tesla Supercharger (destination) outputs at most 250 kw", + "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", + "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" + }, + "render": { + "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", + "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-12", + "question": { + "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=230 V", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:tesla_destination:voltage=400 V", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-12", + "question": { + "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=16 A", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=32 A", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-12", + "question": { + "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", + "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" + }, + "render": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=11 kw", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=22 kw", + "then": { + "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", + "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type USB to charge phones and small electronics are available here?", + "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" + }, + "render": { + "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", + "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "voltage-13", + "question": { + "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " + }, + "render": { + "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" + }, + "freeform": { + "key": "socket:USB-A:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:voltage=5 V", + "then": { + "en": "USB to charge phones and small electronics outputs 5 volt", + "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "current-13", + "question": { + "en": "What current do the plugs with USB to charge phones and small electronics offer?", + "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" + }, + "freeform": { + "key": "socket:USB-A:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:current=1 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 1 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" + } + }, + { + "if": "socket:socket:USB-A:current=2 A", + "then": { + "en": "USB to charge phones and small electronics outputs at most 2 A", + "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "power-output-13", + "question": { + "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", + "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" + }, + "render": { + "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" + }, + "freeform": { + "key": "socket:USB-A:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:output=5w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 5w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" + } + }, + { + "if": "socket:socket:USB-A:output=10w", + "then": { + "en": "USB to charge phones and small electronics outputs at most 10w", + "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "Authentication", + "question": { + "en": "What kind of authentication is available at the charging station?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "No authentication is needed" + } + } + ] + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + }, + "it": { + "0": { + "then": "Non appartiene a una rete" + } + }, + "ja": { + "0": { + "then": "大規模な運営チェーンの一部ではない" + } + }, + "ru": { + "0": { + "then": "Не является частью более крупной сети" + } + }, + "zh_Hant": { + "0": { + "then": "不屬於大型網路" + } + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)" + } + } + ] + }, + { + "id": "fee/charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" + }, + "freeform": { + "key": "charge", + "addExtraTags": [ + "fee=yes" + ] + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no", + "charge=" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + } + } + ] + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk" + } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } + } + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" + } + } + ] + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}" + }, + "question": { + "en": "Is this charging station part of a network?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ + { + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network" + } + }, + { + "if": "network=none", + "then": { + "en": "Not part of a bigger network" + }, + "hideInAnswer": true + }, + { + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" + } + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?" + }, + "render": { + "en": "This charging station is operated by {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ + { + "if": { + "and": [ + "network:={operator}" + ] + }, + "then": { + "en": "Actually, {operator} is the network" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" + } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?" + }, + "render": { + "en": "In case of problems, call {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?" + }, + "render": { + "en": "In case of problems, send an email to {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website of the operator?" + }, + "render": { + "en": "More info on {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?" + }, + "render": { + "en": "Reference number is {ref}" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" + }, + "mappings": [ + { + "if": "operational_status=broken", + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + } + }, + { + "if": { + "and": [ + "construction:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + } + }, + { + "if": { + "and": [ + "disused:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + } + }, + { + "if": { + "and": [ + "amenity=charging_station", + "operational_status=" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging" + } + } + ] + } + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" } - } ] - } }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" - }, - "mappings": [ + "iconOverlays": [ { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd" - } + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross_bottom_right:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg", + "badge": true + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg", + "badge": true } - ] + ], + "width": { + "render": "8" }, - { - "id": "Network", - "render": { - "en": "Part of the network {network}" - }, - "question": { - "en": "Is this charging station part of a network?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ + "iconSize": { + "render": "50,50,bottom" + }, + "color": { + "render": "#00f" + }, + "presets": [ { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "Charging station" + }, + "preciseInput": { + "preferredBackground": "map" + } } - ] - }, - { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?" - }, - "render": { - "en": "This charging station is operated by {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ + ], + "wayHandling": 1, + "filter": [ { - "if": { - "and": [ - "network:={operator}" + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] + }, + { + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] + }, + { + "id": "connection_type", + "options": [ + { + "question": { + "en": "All connectors", + "nl": "Alle types" + } + }, + { + "question": { + "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", + "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", + "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a Chademo connector", + "nl": "Heeft een Chademo " + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a Type 1 with cable (J1772) connector", + "nl": "Heeft een Type 1 met kabel (J1772) " + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a Type 1 without cable (J1772) connector", + "nl": "Heeft een Type 1 zonder kabel (J1772) " + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", + "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger connector", + "nl": "Heeft een Tesla Supercharger " + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a Type 2 (mennekes) connector", + "nl": "Heeft een Type 2 (mennekes) " + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a Type 2 CCS (mennekes) connector", + "nl": "Heeft een Type 2 CCS (mennekes) " + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a Type 2 with cable (mennekes) connector", + "nl": "Heeft een Type 2 met kabel (J1772) " + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", + "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a Tesla Supercharger (destination) connector", + "nl": "Heeft een Tesla Supercharger (destination) " + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", + "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a USB to charge phones and small electronics connector", + "nl": "Heeft een USB om GSMs en kleine electronica op te laden " + }, + "osmTags": "socket:USB-A~*" + } ] - }, - "then": { - "en": "Actually, {operator} is the network" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" } - ] - }, - { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?" - }, - "render": { - "en": "In case of problems, call {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?" - }, - "render": { - "en": "In case of problems, send an email to {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?" - }, - "render": { - "en": "More info on {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?" - }, - "render": { - "en": "Reference number is {ref}" - }, - "freeform": { - "key": "ref" - } - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?" - }, - "mappings": [ + ], + "units": [ { - "if": "operational_status=broken", - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot" - } + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ + { + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut" + } + }, + { + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren" + }, + "humanSingular": { + "en": " hour", + "nl": " uur" + } + }, + { + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day" + }, + "humanSingular": { + "en": " day", + "nl": " dag" + } + } + ] }, { - "if": { - "and": [ - "planned:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" - } + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage" + ], + "applicableUnits": [ + { + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt" + } + } + ], + "eraseInvalidValues": true }, { - "if": { - "and": [ - "construction:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" - } + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current" + ], + "applicableUnits": [ + { + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } + } + ], + "eraseInvalidValues": true }, { - "if": { - "and": [ - "disused:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" - } - }, - { - "if": { - "and": [ - "amenity=charging_station", - "operational_status=" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" - } + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt" + } + } + ], + "eraseInvalidValues": true } - ] - }, - { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging" - } - } - ] - } - ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } ] - }, - "iconOverlays": [ - { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross_bottom_right:#c22;" - }, - { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true - }, - { - "if": { - "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } - ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true - } - ], - "width": { - "render": "8" - }, - "iconSize": { - "render": "50,50,bottom" - }, - "color": { - "render": "#00f" - }, - "presets": [ - { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "Charging station" - }, - "preciseInput": { - "preferredBackground": "map" - } - } - ], - "wayHandling": 1, - "filter": [ - { - "id": "vehicle-type", - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen" - } - }, - { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen" - }, - "osmTags": "bicycle=yes" - }, - { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" - ] - } - } - ] - }, - { - "id": "working", - "options": [ - { - "question": { - "en": "Only working charging stations" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" - ] - } - } - ] - }, - { - "id": "connection_type", - "options": [ - { - "question": { - "en": "All connectors", - "nl": "Alle types" - } - }, - { - "question": { - "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", - "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", - "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a Chademo connector", - "nl": "Heeft een Chademo " - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a Type 1 with cable (J1772) connector", - "nl": "Heeft een Type 1 met kabel (J1772) " - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a Type 1 without cable (J1772) connector", - "nl": "Heeft een Type 1 zonder kabel (J1772) " - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", - "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger connector", - "nl": "Heeft een Tesla Supercharger " - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a Type 2 (mennekes) connector", - "nl": "Heeft een Type 2 (mennekes) " - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a Type 2 CCS (mennekes) connector", - "nl": "Heeft een Type 2 CCS (mennekes) " - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a Type 2 with cable (mennekes) connector", - "nl": "Heeft een Type 2 met kabel (J1772) " - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", - "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger (destination) connector", - "nl": "Heeft een Tesla Supercharger (destination) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", - "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a USB to charge phones and small electronics connector", - "nl": "Heeft een USB om GSMs en kleine electronica op te laden " - }, - "osmTags": "socket:USB-A~*" - } - ] - } - ], - "units": [ - { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ - { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut" - } - }, - { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren" - }, - "humanSingular": { - "en": " hour", - "nl": " uur" - } - }, - { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day" - }, - "humanSingular": { - "en": " day", - "nl": " dag" - } - } - ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage", - "socket:USB-A:voltage" - ], - "applicableUnits": [ - { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current", - "socket:USB-A:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output", - "socket:USB-A:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt" - } - }, - { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt" - } - } - ], - "eraseInvalidValues": true - } - ] } \ No newline at end of file diff --git a/assets/svg/loading.svg b/assets/svg/loading.svg index 0d884dfe9..d7202fda4 100644 --- a/assets/svg/loading.svg +++ b/assets/svg/loading.svg @@ -1,63 +1,38 @@ - - - - image/svg+xml - - - - - - - - + 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" + fill="none" + viewBox="0 0 25 25" + version="1.1" + id="svg6"> + + + + image/svg+xml + + + + + + + + diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 4cbe196ae..08e6fd3d2 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -812,6 +812,10 @@ video { margin: 0px; } +.m-4 { + margin: 1rem; +} + .m-2 { margin: 0.5rem; } @@ -820,8 +824,8 @@ video { margin: 0.75rem; } -.m-4 { - margin: 1rem; +.m-6 { + margin: 1.5rem; } .my-2 { @@ -844,18 +848,14 @@ video { margin-right: 1rem; } -.-ml-1 { - margin-left: -0.25rem; -} - -.mr-3 { - margin-right: 0.75rem; -} - .ml-3 { margin-left: 0.75rem; } +.ml-2 { + margin-left: 0.5rem; +} + .mb-2 { margin-bottom: 0.5rem; } @@ -892,10 +892,6 @@ video { margin-top: 0px; } -.ml-2 { - margin-left: 0.5rem; -} - .mt-4 { margin-top: 1rem; } @@ -916,10 +912,18 @@ video { margin-bottom: 0.25rem; } +.mr-3 { + margin-right: 0.75rem; +} + .mb-4 { margin-bottom: 1rem; } +.ml-4 { + margin-left: 1rem; +} + .box-border { box-sizing: border-box; } @@ -968,10 +972,6 @@ video { height: 100%; } -.h-5 { - height: 1.25rem; -} - .h-24 { height: 6rem; } @@ -988,6 +988,10 @@ video { height: 3rem; } +.h-5 { + height: 1.25rem; +} + .h-screen { height: 100vh; } @@ -1032,10 +1036,6 @@ video { width: 100%; } -.w-5 { - width: 1.25rem; -} - .w-10 { width: 2.5rem; } @@ -1394,6 +1394,18 @@ video { padding: 0px; } +.pl-6 { + padding-left: 1.5rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + +.pl-2 { + padding-left: 0.5rem; +} + .pt-3 { padding-top: 0.75rem; } @@ -1422,10 +1434,6 @@ video { padding-top: 1.5rem; } -.pl-2 { - padding-left: 0.5rem; -} - .pl-5 { padding-left: 1.25rem; } @@ -1787,6 +1795,7 @@ svg, img { width: 100%; height: 100%; display: unset; + vertical-align: unset; } .mapcontrol svg path { @@ -1876,6 +1885,11 @@ li::marker { color: var(--subtle-detail-color-contrast); } +.normal-background { + background: var(--background-color); + color: var(--foreground-color) +} + .subtle-lighter { color: var(--subtle-detail-color-light-contrast); } diff --git a/css/wikipedia.css b/css/wikipedia.css new file mode 100644 index 000000000..6bc812f20 --- /dev/null +++ b/css/wikipedia.css @@ -0,0 +1,32 @@ +/* This stylesheet reimplements a few classes from wikipedia to show their articles prettily */ + +.wikipedia-article { + font-family: sans-serif; +} + +.wikipedia-article .tright { + float: right; + clear: right; +} + +.wikipedia-article .thumb { + background: var(--subtle-detail-color); + margin: 1rem; + padding: 0.5rem; + border: 1px solid var(--subtle-detail-color-light-contrast); + border-radius: 0.5rem; +} + +.wikipedia-article a { + color: #0645ad; + background: none; +} + +.wikipedia-article a:hover a:focus { + text-decoration: underline; +} + +.wikipedia-article p { + margin-bottom: 0.5rem; +} + diff --git a/index.css b/index.css index 4361e9503..3c590d974 100644 --- a/index.css +++ b/index.css @@ -92,6 +92,7 @@ svg, img { width: 100%; height: 100%; display: unset; + vertical-align: unset; } .mapcontrol svg path { @@ -179,6 +180,11 @@ li::marker { color: var(--subtle-detail-color-contrast); } +.normal-background { + background: var(--background-color); + color: var(--foreground-color) +} + .subtle-lighter { color: var(--subtle-detail-color-light-contrast); } diff --git a/langs/en.json b/langs/en.json index e035d72d3..451e5c227 100644 --- a/langs/en.json +++ b/langs/en.json @@ -62,6 +62,7 @@ "readMessages": "You have unread messages. Read these before deleting a point - someone might have feedback" }, "general": { + "loading": "Loading...", "pdf": { "generatedWith": "Generated with MapComplete.osm.be", "attr": "Map data © OpenStreetMap Contributors, reusable under ODbL", @@ -215,6 +216,9 @@ }, "histogram": { "error_loading": "Could not load the histogram" + }, + "wikipedia": { + "wikipediaboxTitle": "Wikipedia" } }, "favourite": { diff --git a/test.html b/test.html index dd8563939..e3f35827b 100644 --- a/test.html +++ b/test.html @@ -10,6 +10,8 @@ + + diff --git a/test.ts b/test.ts index 5dcdc0ce0..49c4379e0 100644 --- a/test.ts +++ b/test.ts @@ -1,31 +1,14 @@ -import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; -import TagRenderingQuestion from "./UI/Popup/TagRenderingQuestion"; -import {UIEventSource} from "./Logic/UIEventSource"; -import {VariableUiElement} from "./UI/Base/VariableUIElement"; +import Wikipedia from "./Logic/Web/Wikipedia"; +import {FixedUiElement} from "./UI/Base/FixedUiElement"; +import WikipediaBox from "./UI/WikipediaBox"; +import Loading from "./UI/Base/Loading"; -const theme = AllKnownLayouts.allKnownLayouts.get("charging_stations") -const tagRendering = theme.layers[0].tagRenderings.filter(tr => tr.id === "Available_charging_stations (generated)")[0] -const tag = new UIEventSource({ - id: "node/42", - amenity:"charging_station", - bicycle:"yes", - car:"no", - "motorcar":"no", - "hgv":"no", - bus:"no" +new WikipediaBox({ + pagename: "Poertoren", + language: "nl" }) -window.tags = tag - -//const q = -new VariableUiElement(tag.map(_ => new TagRenderingQuestion(tag, tagRendering) )) - .SetStyle("width: 100px") + .SetStyle("max-height: 20rem;") .AttachTo("maindiv") -window.setTimeout(_ => { - tag.data.bicycle="no" - tag.data.car = "yes" - tag.ping() - console.log("Pinged") -}, 2500) \ No newline at end of file From 625afc302d005756e477b14837716023ce696a53 Mon Sep 17 00:00:00 2001 From: Tobias Date: Sat, 2 Oct 2021 21:32:50 +0200 Subject: [PATCH 088/110] Readme: Add link to presentation about MapComplete This presentation gives a good overview about the project. Lets link it here. Also, some highlighting for more structure / "scannability" of the content. --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1373391a5..566ab402a 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,30 @@ > Let a thousand flowers bloom -MapComplete is an OpenStreetMap viewer and editor. It shows map features on a certain topic, and allows to see, edit and +**MapComplete is an OpenStreetMap viewer and editor.** It shows map features on a certain topic, and allows to see, edit and add new features to the map. It can be seen as a webversion [crossover of StreetComplete and MapContrib](Docs/MapComplete_vs_other_editors.md). It tries to be just as easy to use as StreetComplete, but it allows to focus on one single theme per instance (e.g. nature, bicycle infrastructure, ...) -The design goals of MapComplete are to be: +**The design goals** of MapComplete are to be: - Easy to use, both on web and on mobile - Easy to deploy (by not having a backend) - Easy to set up a custom theme - Easy to fall down the rabbit hole of OSM -The basic functionality is to download some map features from Overpass and then ask certain questions. An answer is sent +**The basic functionality is** to download some map features from Overpass and then ask certain questions. An answer is sent back to directly to OpenStreetMap. Furthermore, it shows images present in the `image` tag or, if a `wikidata` or `wikimedia_commons`-tag is present, it follows those to get these images too. -An explicit non-goal of MapComplete is to modify geometries of ways. Although adding a point to a way or splitting a way +**An explicit non-goal** of MapComplete is to modify geometries of ways. Although adding a point to a way or splitting a way in two parts might be added one day. +**More about MapComplete:** [Watch Pieter's talk on the 2021 State Of The Map Conference](https://media.ccc.de/v/sotm2021-9448-introduction-and-review-of-mapcomplete) ([YouTube](https://www.youtube.com/watch?v=zTtMn6fNbYY)) about the history, vision and future of MapComplete. + # Creating your own theme It is possible to quickly make and distribute your own theme From deb8b8cc598b5d67954f72ba190b20cb2a563c10 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 2 Oct 2021 22:27:44 +0200 Subject: [PATCH 089/110] Fix rendering bug --- UI/Popup/TagRenderingQuestion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 06c5b3dc3..9f99b2bb2 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -48,7 +48,7 @@ export default class TagRenderingQuestion extends VariableUiElement { const applicableMappings = UIEventSource.ListStabilized(tags.map(tags => { const applicableMappings : {if: TagsFilter, then: any, ifnot?: TagsFilter}[] = [] - for (const mapping of configuration.mappings) { + for (const mapping of configuration.mappings ?? []) { if (mapping.hideInAnswer === true) { continue } From 1edf829cad03670e23e1dc11854022dfd54ba9d9 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 2 Oct 2021 22:28:27 +0200 Subject: [PATCH 090/110] Add etymology theme --- assets/themes/etymology.json | 86 ++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 assets/themes/etymology.json diff --git a/assets/themes/etymology.json b/assets/themes/etymology.json new file mode 100644 index 000000000..a8ca9dfc9 --- /dev/null +++ b/assets/themes/etymology.json @@ -0,0 +1,86 @@ +{ + "id": "etymology", + "title": { + "en": "Open Etymology Map", + "nl": "Open Etymology-kaart" + }, + "shortDescription": { + "en": "What is the origin of a toponym?", + "nl": "Wat is de oorsprong van een plaatsnaam?" + }, + "description": { + "en": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. The information comes from Wpikipedia.", + "nl": "Op deze kaart zie je waar een plaats naar is vernoemd. De straten, gebouwen, ... komen uit OpenStreetMap, waar een link naar Wikidata werd gelegd. De informatie komt uit wikipedia." + }, + "language": [ + "en", + "nl" + ], + "maintainer": "", + "icon": "./assets/svg/bug.svg", + "version": "0", + "startLat": 0, + "startLon": 0, + "startZoom": 1, + "widenFactor": 2, + "socialImage": "", + "layers": [ + { + "id": "has_a_name", + "name": { + "en": "Has etymolgy", + "nl": "Heeft etymology info" + }, + "minzoom": 12, + "source": { + "osmTags": { + "or": [ + "name:etymology:wikidata~*", + "name:etymology~*" + ] + } + }, + "title": { + "render": { + "*": "{name}" + } + }, + "description": { + "en": "All objects which have an etymology known", + "nl": "Alle lagen met een gelinkt etymology" + }, + "tagRenderings": [ + { + "id": "simple etymology", + "render": { + "en": "Named after {name:etymology}", + "nl": "Vernoemd naar {name:etymology}" + }, + "freeform": { + "key": "name:etymology" + } + }, + { + "id": "wikipedia-etymology", + "render": { + "*": "{wikipedia(name:etymology:wikidata):max-height:20rem}" + } + } + ], + "icon": { + "render": "./assets/svg/bug.svg" + }, + "width": { + "render": "8" + }, + "iconSize": { + "render": "40,40,center" + }, + "color": { + "render": "#00f" + }, + "presets": [] + } + ], + "roamingRenderings": [] +} \ No newline at end of file From 8b4442c8cc858df5a6ab2a260e00050d1c166138 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 2 Oct 2021 22:31:16 +0200 Subject: [PATCH 091/110] Add wikipedia box --- Logic/ImageProviders/WikidataImageProvider.ts | 30 ++-- .../ImageProviders/WikimediaImageProvider.ts | 4 +- Logic/Web/Wikidata.ts | 89 +++++++++++ Logic/Web/Wikipedia.ts | 49 ++++-- UI/Base/Loading.ts | 4 +- UI/ShowDataLayer/ShowDataLayer.ts | 5 +- UI/SpecialVisualizations.ts | 47 ++++-- UI/WikipediaBox.ts | 139 +++++++++++++----- .../layers/nature_reserve/nature_reserve.json | 3 +- .../observation_tower/observation_tower.json | 3 +- assets/svg/loading.svg | 72 +++++++-- assets/tagRenderings/questions.json | 3 + css/index-tailwind-output.css | 26 +--- css/wikipedia.css | 20 ++- index.css | 2 - index.html | 1 + langs/en.json | 5 +- langs/themes/en.json | 16 ++ langs/themes/nl.json | 16 ++ test.ts | 16 +- 20 files changed, 401 insertions(+), 149 deletions(-) create mode 100644 Logic/Web/Wikidata.ts diff --git a/Logic/ImageProviders/WikidataImageProvider.ts b/Logic/ImageProviders/WikidataImageProvider.ts index 8576de5d9..ea333dfbd 100644 --- a/Logic/ImageProviders/WikidataImageProvider.ts +++ b/Logic/ImageProviders/WikidataImageProvider.ts @@ -3,6 +3,7 @@ import ImageProvider, {ProvidedImage} from "./ImageProvider"; import BaseUIElement from "../../UI/BaseUIElement"; import Svg from "../../Svg"; import {WikimediaImageProvider} from "./WikimediaImageProvider"; +import Wikidata from "../Web/Wikidata"; export class WikidataImageProvider extends ImageProvider { @@ -22,31 +23,18 @@ export class WikidataImageProvider extends ImageProvider { } public async ExtractUrls(key: string, value: string): Promise[]> { - const wikidataUrl = "https://www.wikidata.org/wiki/" - if (value.startsWith(wikidataUrl)) { - value = value.substring(wikidataUrl.length) - } - if(value.startsWith("http")){ - // Probably some random link in the image field - we skip it - return undefined - } - if (!value.startsWith("Q")) { - value = "Q" + value - } - const url = "https://www.wikidata.org/wiki/Special:EntityData/" + value + ".json"; - const response = await Utils.downloadJson(url) - const entity = response.entities[value]; - const commons = entity.sitelinks.commonswiki; + const entity = await Wikidata.LoadWikidataEntry(value) + + const allImages : Promise[] = [] // P18 is the claim 'depicted in this image' - const image = entity.claims.P18?.[0]?.mainsnak?.datavalue?.value; - const allImages = [] - if (image !== undefined) { - // We found a 'File://' - const promises = await WikimediaImageProvider.singleton.ExtractUrls(key, image) + for (const img of Array.from(entity.claims.get("P18") ?? [])) { + const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined, img) allImages.push(...promises) } + + const commons =entity.wikisites.get("commons") if (commons !== undefined) { - const promises = await WikimediaImageProvider.singleton.ExtractUrls(commons, image) + const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined , commons) allImages.push(...promises) } return allImages diff --git a/Logic/ImageProviders/WikimediaImageProvider.ts b/Logic/ImageProviders/WikimediaImageProvider.ts index 20f69a7e4..0d2f6c1d0 100644 --- a/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/Logic/ImageProviders/WikimediaImageProvider.ts @@ -44,7 +44,6 @@ export class WikimediaImageProvider extends ImageProvider { if (continueParameter !== undefined) { url = `${url}&cmcontinue=${continueParameter}`; } - console.debug("Loading a wikimedia category: ", url) const response = await Utils.downloadJson(url) const members = response.query?.categorymembers ?? []; const imageOverview: string[] = members.map(member => member.title); @@ -135,8 +134,7 @@ export class WikimediaImageProvider extends ImageProvider { } public async ExtractUrls(key: string, value: string): Promise[]> { - - if(key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){ + if(key !== undefined && key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){ return [] } diff --git a/Logic/Web/Wikidata.ts b/Logic/Web/Wikidata.ts new file mode 100644 index 000000000..3b60a2e6b --- /dev/null +++ b/Logic/Web/Wikidata.ts @@ -0,0 +1,89 @@ +import {Utils} from "../../Utils"; + + +export interface WikidataResponse { + + id: string, + labels: Map, + descriptions: Map, + claims: Map>, + wikisites: Map + commons: string +} + +/** + * Utility functions around wikidata + */ +export default class Wikidata { + + private static ParseResponse(entity: any): WikidataResponse { + const labels = new Map() + for (const labelName in entity.labels) { + // The labelname is the language code + labels.set(labelName, entity.labels[labelName].value) + } + + const descr = new Map() + for (const labelName in entity.descriptions) { + // The labelname is the language code + descr.set(labelName, entity.descriptions[labelName].value) + } + + const sitelinks = new Map(); + for (const labelName in entity.sitelinks) { + // labelName is `${language}wiki` + const language = labelName.substring(0, labelName.length - 4) + const title = entity.sitelinks[labelName].title + sitelinks.set(language, title) + } + + const commons = sitelinks.get("commons") + sitelinks.delete("commons") + + const claims = new Map>(); + for (const claimId of entity.claims) { + + const claimsList: any[] = entity.claims[claimId] + const values = new Set() + for (const claim of claimsList) { + const value = claim.mainsnak.datavalue.value; + values.add(value) + } + claims.set(claimId, values); + } + + return { + claims: claims, + descriptions: descr, + id: entity.id, + labels: labels, + wikisites: sitelinks, + commons: commons + } + } + + /** + * Loads a wikidata page + * @returns the entity of the given value + */ + public static async LoadWikidataEntry(value: string | number): Promise { + const wikidataUrl = "https://www.wikidata.org/wiki/" + if (typeof value === "number") { + value = "Q" + value + } + if (value.startsWith(wikidataUrl)) { + value = value.substring(wikidataUrl.length) + } + if (value.startsWith("http")) { + // Probably some random link in the image field - we skip it + return undefined + } + if (!value.startsWith("Q")) { + value = "Q" + value + } + const url = "https://www.wikidata.org/wiki/Special:EntityData/" + value + ".json"; + const response = await Utils.downloadJson(url) + return Wikidata.ParseResponse(response.entities[value]); + } + +} \ No newline at end of file diff --git a/Logic/Web/Wikipedia.ts b/Logic/Web/Wikipedia.ts index 7c1260a28..34bde4fc5 100644 --- a/Logic/Web/Wikipedia.ts +++ b/Logic/Web/Wikipedia.ts @@ -2,7 +2,8 @@ * Some usefull utility functions around the wikipedia API */ import {Utils} from "../../Utils"; -import WikipediaBox from "../../UI/WikipediaBox"; +import {UIEventSource} from "../UIEventSource"; +import Wikidata from "./Wikidata"; export default class Wikipedia { @@ -14,22 +15,36 @@ export default class Wikipedia { private static readonly classesToRemove = [ "shortdescription", "sidebar", - "infobox", + "infobox","infobox_v2", + "noprint", + "ambox", "mw-editsection", + "mw-selflink", "hatnote" // Often redirects ] - public static async GetArticle(options: { + private static readonly _cache = new Map>() + + public static GetArticle(options: { pageName: string, - language?: "en" | string, - section?: number, + language?: "en" | string}): UIEventSource<{ success: string } | { error: any }>{ + const key = (options.language ?? "en")+":"+options.pageName + const cached = Wikipedia._cache.get(key) + if(cached !== undefined){ + return cached + } + const v = UIEventSource.FromPromiseWithErr(Wikipedia.GetArticleAsync(options)) + Wikipedia._cache.set(key, v) + return v; + } + + public static async GetArticleAsync(options: { + pageName: string, + language?: "en" | string }): Promise { - let section = "" - if (options.section !== undefined) { - section = "§ion=" + options.section - } - const url = `https://${options.language ?? "en"}.wikipedia.org/w/api.php?action=parse${section}&format=json&origin=*&prop=text&page=` + options.pageName + const language = options.language ?? "en" + const url = `https://${language}.wikipedia.org/w/api.php?action=parse&format=json&origin=*&prop=text&page=` + options.pageName const response = await Utils.downloadJson(url) const html = response["parse"]["text"]["*"]; @@ -43,7 +58,19 @@ export default class Wikipedia { toRemoveElement.parentElement?.removeChild(toRemoveElement) } } - return content.innerHTML; + + const links = Array.from(content.getElementsByTagName("a")) + + console.log("Links are", links) + // Rewrite relative links to absolute links + open them in a new tab + links.filter(link => link.getAttribute("href")?.startsWith("/") ?? false). + forEach(link => { + link.target = '_blank' + // note: link.getAttribute("href") gets the textual value, link.href is the rewritten version which'll contain the host for relative paths + link.href = `https://${language}.wikipedia.org${link.getAttribute("href")}`; + }) + + return content.innerHTML } } \ No newline at end of file diff --git a/UI/Base/Loading.ts b/UI/Base/Loading.ts index 03d0e3fdc..c2636c1f3 100644 --- a/UI/Base/Loading.ts +++ b/UI/Base/Loading.ts @@ -9,9 +9,9 @@ export default class Loading extends Combine { const t = Translations.T(msg ) ?? Translations.t.general.loading.Clone(); t.SetClass("pl-2") super([ - Svg.loading_svg().SetClass("animate-spin").SetStyle("width: 1.5rem; height: 1.5rem; margin-bottom: 4px;"), + Svg.loading_svg().SetClass("animate-spin").SetStyle("width: 1.5rem; height: 1.5rem;"), t ]) - this.SetClass("flex m-1") + this.SetClass("flex p-1") } } \ No newline at end of file diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index b8771aa7a..a0244a33f 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -225,7 +225,6 @@ export default class ShowDataLayer { popup.setContent(`
Popup for ${feature.properties.id} ${feature.geometry.type}
`) leafletLayer.on("popupopen", () => { - console.trace(`Opening the popup for ${feature.properties.id} ${feature.geometry.type}`) if (infobox === undefined) { const tags = State.state.allElements.getEventSourceById(feature.properties.id); infobox = new FeatureInfoBox(tags, layer); @@ -239,7 +238,9 @@ export default class ShowDataLayer { } infobox.AttachTo(id) infobox.Activate(); - State.state.selectedElement.setData(feature) + if (State.state?.selectedElement?.data?.properties?.id !== feature.properties.id) { + State.state.selectedElement.setData(feature) + } }); diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 5801e1d88..de45911db 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -26,6 +26,7 @@ import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSou import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer"; import Minimap from "./Base/Minimap"; import AllImageProviders from "../Logic/ImageProviders/AllImageProviders"; +import WikipediaBox from "./WikipediaBox"; export interface SpecialVisualization { funcName: string, @@ -84,6 +85,20 @@ export default class SpecialVisualizations { return new ImageUploadFlow(tags, args[0]) } }, + { + funcName: "wikipedia", + docs: "A box showing the corresponding wikipedia article - based on the wikidata tag", + args: [ + { + name: "keyToShowWikipediaFor", + doc: "Use the wikidata entry from this key to show the wikipedia article for", + defaultValue: "wikidata" + } + ], + example: "`{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height", + constr: (_, tagsSource, args) => + new WikipediaBox( tagsSource.map(tags => tags[args[0]])) + }, { funcName: "minimap", docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div", @@ -153,10 +168,10 @@ export default class SpecialVisualizations { } }) - new ShowDataMultiLayer( + new ShowDataMultiLayer( { leafletMap: minimap["leafletMap"], - enablePopups : false, + enablePopups: false, zoomToFeatures: true, layers: State.state.filteredLayers, features: new StaticFeatureSource(featuresToShow, true) @@ -351,17 +366,17 @@ export default class SpecialVisualizations { const key = args [0] return new VariableUiElement( tagSource.map(tags => tags[key]).map(value => { - if (value === undefined) { - return undefined - } - const allUnits = [].concat(...state.layoutToUse.layers.map(lyr => lyr.units)) - const unit = allUnits.filter(unit => unit.isApplicableToKey(key))[0] - if (unit === undefined) { - return value; - } - return unit.asHumanLongValue(value); + if (value === undefined) { + return undefined + } + const allUnits = [].concat(...state.layoutToUse.layers.map(lyr => lyr.units)) + const unit = allUnits.filter(unit => unit.isApplicableToKey(key))[0] + if (unit === undefined) { + return value; + } + return unit.asHumanLongValue(value); - }) + }) ) } }, @@ -411,8 +426,8 @@ There are also some technicalities in your theme to keep in mind: } return kv }) - const rewrittenTags : UIEventSource = tagSource.map(tags => { - const newTags : Tag [] = [] + const rewrittenTags: UIEventSource = tagSource.map(tags => { + const newTags: Tag [] = [] for (const [key, value] of tgsSpec) { if (value.startsWith('$')) { const origKey = value.substring(1) @@ -446,9 +461,9 @@ There are also some technicalities in your theme to keep in mind: [ new Title(viz.funcName, 3), viz.docs, - new Table(["name", "default", "description"], + viz.args.length > 0 ? new Table(["name", "default", "description"], viz.args.map(arg => [arg.name, arg.defaultValue ?? "undefined", arg.doc]) - ), + ) : undefined, new Title("Example usage", 4), new FixedUiElement( viz.example ?? "`{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}`" diff --git a/UI/WikipediaBox.ts b/UI/WikipediaBox.ts index c3ab0fab7..f5c582ed2 100644 --- a/UI/WikipediaBox.ts +++ b/UI/WikipediaBox.ts @@ -8,46 +8,109 @@ import BaseUIElement from "./BaseUIElement"; import Title from "./Base/Title"; import Translations from "./i18n/Translations"; import Svg from "../Svg"; +import Wikidata from "../Logic/Web/Wikidata"; +import Locale from "./i18n/Locale"; -export default class WikipediaBox extends Combine{ - - constructor(options: { - pagename: string, - language: string - }) { - - const htmlContent = UIEventSource.FromPromiseWithErr(Wikipedia.GetArticle({ - pageName: options.pagename, - language: options.language, - removeInfoBoxes: true - })) - - const contents : UIEventSource = htmlContent.map(htmlContent => { - if(htmlContent === undefined){ - // Still loading - return new Loading("Loading wikipedia page").SetClass("p-4") - } - if(htmlContent["success"] !== undefined){ - return new FixedUiElement(htmlContent["success"]).SetClass("wikipedia-article") - } - if(htmlContent["error"]){ - return new FixedUiElement(htmlContent["error"]).SetClass("alert p-4") - } - - return undefined - - }) - - const scrollable = new Combine([new VariableUiElement(contents).SetClass("block pl-6 pt-2")]) - .SetClass("block overflow-auto normal-background rounded-lg") - super([ - new Combine([Svg.wikipedia_svg().SetStyle("width: 1.5rem").SetClass("mr-3"), - new Title(Translations.t.general.wikipedia.wikipediaboxTitle, 2)]).SetClass("flex"), - scrollable]) - - this - .SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col") +export default class WikipediaBox extends Combine { + + private static async ExtractWikiPages(wikidata): Promise> { + return (await Wikidata.LoadWikidataEntry(wikidata)).wikisites } + private static _cache = new Map() + + constructor(wikidataId: string | UIEventSource) { + const wp = Translations.t.general.wikipedia; + if(typeof wikidataId === "string"){ + wikidataId = new UIEventSource(wikidataId) + } + + const knownPages = new UIEventSource<{success:Map}|{error:any}>(undefined) + + wikidataId.addCallbackAndRunD(wikidataId => { + WikipediaBox.ExtractWikiPages(wikidataId).then(pages => { + knownPages.setData({success:pages}) + }).catch(err=> { + knownPages.setData({error: err}) + }) + }) + + const cachedPages = new Map() + + const contents = new VariableUiElement( + knownPages.map(pages => { + + if (pages === undefined) { + return new Loading(wp.loading.Clone()) + } + if (pages["error"] !== undefined) { + return wp.failed.Clone().SetClass("alert p-4") + } + const dict: Map = pages["success"] + + const preferredLanguage = [Locale.language.data, "en", Array.from(dict.keys())[0]] + let language + let pagetitle; + let i = 0 + do { + language = preferredLanguage[i] + pagetitle = dict.get(language) + i++; + if(i >= preferredLanguage.length){ + return wp.noWikipediaPage.Clone() + } + } while (pagetitle === undefined) + + if(cachedPages.has(language)){ + return cachedPages.get(language) + } + + const page = WikipediaBox.createContents(pagetitle, language); + cachedPages.set(language, page) + return page + }, [Locale.language]) + ).SetClass("overflow-auto normal-background rounded-lg") + + + super([ + new Combine([Svg.wikipedia_ui().SetStyle("width: 1.5rem").SetClass("mr-3"), + new Title(Translations.t.general.wikipedia.wikipediaboxTitle.Clone(), 2)]).SetClass("flex"), + contents]) + + this + .SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col") + } + + /** + * Returns the actual content in a scrollable way + * @param pagename + * @param language + * @private + */ + private static createContents(pagename: string, language: string): BaseUIElement { + const htmlContent = Wikipedia.GetArticle({ + pageName: pagename, + language: language + }) + const wp = Translations.t.general.wikipedia + const contents: UIEventSource = htmlContent.map(htmlContent => { + if (htmlContent === undefined) { + // Still loading + return new Loading(wp.loading.Clone()) + } + if (htmlContent["success"] !== undefined) { + return new FixedUiElement(htmlContent["success"]).SetClass("wikipedia-article") + } + if (htmlContent["error"]) { + return wp.failed.Clone().SetClass("alert p-4") + } + + return undefined + }) + + return new Combine([new VariableUiElement(contents).SetClass("block pl-6 pt-2")]) + .SetClass("block") + } + } \ No newline at end of file diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index c6fb976af..29d2e56da 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -392,7 +392,8 @@ } ], "id": "Surface area" - } + }, + "wikipedia" ], "wayHandling": 2, "icon": { diff --git a/assets/layers/observation_tower/observation_tower.json b/assets/layers/observation_tower/observation_tower.json index fa9a00262..8443b1213 100644 --- a/assets/layers/observation_tower/observation_tower.json +++ b/assets/layers/observation_tower/observation_tower.json @@ -122,7 +122,8 @@ }, "id": "Payment methods" }, - "wheelchair-access" + "wheelchair-access", + "wikipedia" ], "wayHandling": 1, "icon": { diff --git a/assets/svg/loading.svg b/assets/svg/loading.svg index d7202fda4..27dc3ac6c 100644 --- a/assets/svg/loading.svg +++ b/assets/svg/loading.svg @@ -5,12 +5,17 @@ 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" - fill="none" - viewBox="0 0 25 25" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + viewBox="0 0 24.022156 24.021992" version="1.1" - id="svg6"> + id="svg9" + sodipodi:docname="loading.svg" + inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" + width="24.022156" + height="24.021992"> + id="metadata13"> @@ -21,18 +26,53 @@ + - + id="defs4"> + + + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.26200151;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.26635515" + id="path821" + sodipodi:type="arc" + sodipodi:cx="12.010992" + sodipodi:cy="12.010992" + sodipodi:rx="10.379992" + sodipodi:ry="10.379992" + sodipodi:start="0" + sodipodi:end="6.2828013" + sodipodi:open="true" + d="M 22.390984,12.010992 A 10.379992,10.379992 0 0 1 12.011989,22.390984 10.379992,10.379992 0 0 1 1.6310007,12.012985 10.379992,10.379992 0 0 1 12.008003,1.6310009 10.379992,10.379992 0 0 1 22.390983,12.007006" /> + diff --git a/assets/tagRenderings/questions.json b/assets/tagRenderings/questions.json index 19a2babaa..509749aa0 100644 --- a/assets/tagRenderings/questions.json +++ b/assets/tagRenderings/questions.json @@ -2,6 +2,9 @@ "images": { "render": "{image_carousel()}{image_upload()}" }, + "wikipedia": { + "render": "{wikipedia():max-height:25rem}" + }, "reviews": { "render": "{reviews()}" }, diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 08e6fd3d2..adb7b1ee9 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -812,10 +812,6 @@ video { margin: 0px; } -.m-4 { - margin: 1rem; -} - .m-2 { margin: 0.5rem; } @@ -824,8 +820,8 @@ video { margin: 0.75rem; } -.m-6 { - margin: 1.5rem; +.m-4 { + margin: 1rem; } .my-2 { @@ -852,8 +848,8 @@ video { margin-left: 0.75rem; } -.ml-2 { - margin-left: 0.5rem; +.mr-3 { + margin-right: 0.75rem; } .mb-2 { @@ -892,6 +888,10 @@ video { margin-top: 0px; } +.ml-2 { + margin-left: 0.5rem; +} + .mt-4 { margin-top: 1rem; } @@ -912,18 +912,10 @@ video { margin-bottom: 0.25rem; } -.mr-3 { - margin-right: 0.75rem; -} - .mb-4 { margin-bottom: 1rem; } -.ml-4 { - margin-left: 1rem; -} - .box-border { box-sizing: border-box; } @@ -1794,8 +1786,6 @@ svg, img { box-sizing: content-box; width: 100%; height: 100%; - display: unset; - vertical-align: unset; } .mapcontrol svg path { diff --git a/css/wikipedia.css b/css/wikipedia.css index 6bc812f20..99d25c437 100644 --- a/css/wikipedia.css +++ b/css/wikipedia.css @@ -1,7 +1,7 @@ /* This stylesheet reimplements a few classes from wikipedia to show their articles prettily */ .wikipedia-article { - font-family: sans-serif; + font-family: sans-serif !important; } .wikipedia-article .tright { @@ -9,6 +9,12 @@ clear: right; } +.wikipedia-article svg, img { + width: unset; + height: unset; + display: unset; +} + .wikipedia-article .thumb { background: var(--subtle-detail-color); margin: 1rem; @@ -17,15 +23,17 @@ border-radius: 0.5rem; } -.wikipedia-article a { - color: #0645ad; - background: none; +.wikipedia-article a:hover a:focus { + text-decoration: underline !important; } -.wikipedia-article a:hover a:focus { - text-decoration: underline; +.wikipedia-article a { + color: #0645ad !important; + background: none !important; + text-decoration: none; } + .wikipedia-article p { margin-bottom: 0.5rem; } diff --git a/index.css b/index.css index 3c590d974..d5cd06476 100644 --- a/index.css +++ b/index.css @@ -91,8 +91,6 @@ svg, img { box-sizing: content-box; width: 100%; height: 100%; - display: unset; - vertical-align: unset; } .mapcontrol svg path { diff --git a/index.html b/index.html index 777dd921c..a65572209 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,7 @@ + diff --git a/langs/en.json b/langs/en.json index 451e5c227..306ea8e80 100644 --- a/langs/en.json +++ b/langs/en.json @@ -218,7 +218,10 @@ "error_loading": "Could not load the histogram" }, "wikipedia": { - "wikipediaboxTitle": "Wikipedia" + "wikipediaboxTitle": "Wikipedia", + "failed":"Loading the wikipedia entry failed", + "loading": "Loading Wikipedia...", + "noWikipediaPage": "This wikidata item has no corresponding wikipedia page yet." } }, "favourite": { diff --git a/langs/themes/en.json b/langs/themes/en.json index b62768bef..9dcb4b54a 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -743,6 +743,22 @@ "description": "On this map, publicly accessible drinking water spots are shown and can be easily added", "title": "Drinking Water" }, + "etymology": { + "description": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. The information comes from Wpikipedia.", + "layers": { + "0": { + "description": "All objects which have an etymology known", + "name": "Has etymolgy", + "tagRenderings": { + "simple etymology": { + "render": "Named after {name:etymology}" + } + } + } + }, + "shortDescription": "What is the origin of a toponym?", + "title": "Open Etymology Map" + }, "facadegardens": { "description": "Facade gardens, green facades and trees in the city not only bring peace and quiet, but also a more beautiful city, greater biodiversity, a cooling effect and better air quality.
Klimaan VZW and Mechelen Klimaatneutraal want to map existing and new facade gardens as an example for people who want to build their own garden or for city walkers who love nature.
More info about the project at klimaan.be.", "layers": { diff --git a/langs/themes/nl.json b/langs/themes/nl.json index e3220df4d..409d96fea 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -624,6 +624,22 @@ "description": "Op deze kaart staan publiek toegankelijke drinkwaterpunten en kan je makkelijk een nieuw drinkwaterpunt toevoegen", "title": "Drinkwaterpunten" }, + "etymology": { + "description": "Op deze kaart zie je waar een plaats naar is vernoemd. De straten, gebouwen, ... komen uit OpenStreetMap, waar een link naar Wikidata werd gelegd. De informatie komt uit wikipedia.", + "layers": { + "0": { + "description": "Alle lagen met een gelinkt etymology", + "name": "Heeft etymology info", + "tagRenderings": { + "simple etymology": { + "render": "Vernoemd naar {name:etymology}" + } + } + } + }, + "shortDescription": "Wat is de oorsprong van een plaatsnaam?", + "title": "Open Etymology-kaart" + }, "facadegardens": { "description": "Ontharde voortuintjes, groene gevels en bomen ín de stad brengen naast rust ook een mooiere stad, een grotere biodiversiteit, een verkoelend effect en een betere luchtkwaliteit.
Klimaan VZW en 'Mechelen Klimaatneutraal' willen met het project Klim(t)aan je Gevel bestaande en nieuwe geveltuintjes in kaart brengen als voorbeeld voor mensen zelf een tuintje willen aanleggen of voor stadwandelaars die houden van de natuur.
Meer info over het project op klimaan.be.", "layers": { diff --git a/test.ts b/test.ts index 49c4379e0..0c746b6eb 100644 --- a/test.ts +++ b/test.ts @@ -1,14 +1,8 @@ -import Wikipedia from "./Logic/Web/Wikipedia"; -import {FixedUiElement} from "./UI/Base/FixedUiElement"; +import Wikidata from "./Logic/Web/Wikidata"; import WikipediaBox from "./UI/WikipediaBox"; -import Loading from "./UI/Base/Loading"; +import Locale from "./UI/i18n/Locale"; +import LanguagePicker from "./UI/LanguagePicker"; - -new WikipediaBox({ - pagename: "Poertoren", - language: "nl" -}) - .SetStyle("max-height: 20rem;") +new WikipediaBox("Q177").SetStyle("max-height: 25rem") .AttachTo("maindiv") - - +LanguagePicker.CreateLanguagePicker(["en","nl","fr","de"]).AttachTo("extradiv") \ No newline at end of file From 393d5d8932b4753a3d71aeaa62918d768fbe40a6 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 01:38:39 +0200 Subject: [PATCH 092/110] Update paths of images in Nature reserves --- assets/layers/nature_reserve/nature_reserve.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index 29d2e56da..786f5e87b 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -151,7 +151,7 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door Natuurpunt" + "nl": "Dit gebied wordt beheerd door Natuurpunt" } }, { @@ -161,7 +161,7 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door {operator}" + "nl": "Dit gebied wordt beheerd door {operator}" }, "hideInAnswer": true }, @@ -172,7 +172,7 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" + "nl": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" } } ], @@ -397,7 +397,7 @@ ], "wayHandling": 2, "icon": { - "render": "./assets/themes/buurtnatuur/nature_reserve.svg" + "render": "./assets/layers/nature_reserve/nature_reserve.svg" }, "width": { "render": "1" From a89d303ecd0937573b56c4dec6b6a3df3caea57b Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 01:38:57 +0200 Subject: [PATCH 093/110] Further stabilization of wikipedia box --- InitUiElements.ts | 1 - Logic/ImageProviders/WikidataImageProvider.ts | 7 +- Logic/Osm/Overpass.ts | 3 +- Logic/UIEventSource.ts | 24 +++++- Logic/Web/Wikidata.ts | 51 +++++++++--- Logic/Web/Wikipedia.ts | 1 - UI/BigComponents/AllDownloads.ts | 8 +- UI/BigComponents/FilterView.ts | 1 - UI/ExportPDF.ts | 10 +-- UI/WikipediaBox.ts | 82 +++++++++---------- langs/en.json | 1 + package.json | 2 +- scripts/generateCache.ts | 54 +++++------- test/TestAll.ts | 4 +- test/Wikidata.spec.test.ts | 29 +++++++ 15 files changed, 169 insertions(+), 109 deletions(-) create mode 100644 test/Wikidata.spec.test.ts diff --git a/InitUiElements.ts b/InitUiElements.ts index 94500ec34..8fafd7677 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -150,7 +150,6 @@ export class InitUiElements { if (userDetails === undefined) { return false; } - console.log("Adding home location of ", userDetails) const home = userDetails.home; if (home === undefined) { return userDetails.loggedIn; // If logged in, the home is not set and we unregister. If not logged in, we stay registered if a login still comes diff --git a/Logic/ImageProviders/WikidataImageProvider.ts b/Logic/ImageProviders/WikidataImageProvider.ts index ea333dfbd..f24f58aca 100644 --- a/Logic/ImageProviders/WikidataImageProvider.ts +++ b/Logic/ImageProviders/WikidataImageProvider.ts @@ -23,7 +23,10 @@ export class WikidataImageProvider extends ImageProvider { } public async ExtractUrls(key: string, value: string): Promise[]> { - const entity = await Wikidata.LoadWikidataEntry(value) + const entity = await Wikidata.LoadWikidataEntryAsync(value) + if(entity === undefined){ + return [] + } const allImages : Promise[] = [] // P18 is the claim 'depicted in this image' @@ -32,7 +35,7 @@ export class WikidataImageProvider extends ImageProvider { allImages.push(...promises) } - const commons =entity.wikisites.get("commons") + const commons = entity.commons if (commons !== undefined) { const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined , commons) allImages.push(...promises) diff --git a/Logic/Osm/Overpass.ts b/Logic/Osm/Overpass.ts index 47b92e278..97911a3b6 100644 --- a/Logic/Osm/Overpass.ts +++ b/Logic/Osm/Overpass.ts @@ -18,7 +18,8 @@ export class Overpass { private _relationTracker: RelationsTracker; - constructor(filter: TagsFilter, extraScripts: string[], + constructor(filter: TagsFilter, + extraScripts: string[], interpreterUrl: string, timeout: UIEventSource, relationTracker: RelationsTracker, diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index c30363f97..10922ca14 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -1,4 +1,5 @@ import {Utils} from "../Utils"; +import * as Events from "events"; export class UIEventSource { @@ -32,14 +33,14 @@ export class UIEventSource { return []; } - public static flatten(source: UIEventSource>, possibleSources: UIEventSource[]): UIEventSource { + public static flatten(source: UIEventSource>, possibleSources?: UIEventSource[]): UIEventSource { const sink = new UIEventSource(source.data?.data); source.addCallback((latestData) => { sink.setData(latestData?.data); }); - for (const possibleSource of possibleSources) { + for (const possibleSource of possibleSources ?? []) { possibleSource?.addCallback(() => { sink.setData(source.data?.data); }) @@ -186,6 +187,25 @@ export class UIEventSource { } } + /** + * Monadic bind function + */ + public bind(f: ((t: T) => UIEventSource)): UIEventSource{ + const sink = new UIEventSource( undefined ) + const seenEventSources = new Set>(); + this.addCallbackAndRun(data => { + const eventSource = f(data) + if(eventSource === undefined){ + sink.setData(undefined) + }else if(!seenEventSources.has(eventSource)){ + eventSource.addCallbackAndRun(mappedData => sink.setData(mappedData)) + seenEventSources.add(eventSource) + } + }) + + return sink; + } + /** * Monoidal map: * Given a function 'f', will construct a new UIEventSource where the contents will always be "f(this.data)' diff --git a/Logic/Web/Wikidata.ts b/Logic/Web/Wikidata.ts index 3b60a2e6b..ae981ee82 100644 --- a/Logic/Web/Wikidata.ts +++ b/Logic/Web/Wikidata.ts @@ -1,4 +1,5 @@ import {Utils} from "../../Utils"; +import {UIEventSource} from "../UIEventSource"; export interface WikidataResponse { @@ -62,15 +63,23 @@ export default class Wikidata { } } - /** - * Loads a wikidata page - * @returns the entity of the given value - */ - public static async LoadWikidataEntry(value: string | number): Promise { - const wikidataUrl = "https://www.wikidata.org/wiki/" - if (typeof value === "number") { - value = "Q" + value + private static readonly _cache = new Map>() + public static LoadWikidataEntry(value: string | number): UIEventSource<{success: WikidataResponse} | {error: any}> { + const key = this.ExtractKey(value) + const cached = Wikidata._cache.get(key) + if(cached !== undefined){ + return cached } + const src = UIEventSource.FromPromiseWithErr(Wikidata.LoadWikidataEntryAsync(key)) + Wikidata._cache.set(key, src) + return src; + } + + private static ExtractKey(value: string | number) : number{ + if (typeof value === "number") { + return value + } + const wikidataUrl = "https://www.wikidata.org/wiki/" if (value.startsWith(wikidataUrl)) { value = value.substring(wikidataUrl.length) } @@ -78,12 +87,30 @@ export default class Wikidata { // Probably some random link in the image field - we skip it return undefined } - if (!value.startsWith("Q")) { - value = "Q" + value + if (value.startsWith("Q")) { + value = value.substring(1) } - const url = "https://www.wikidata.org/wiki/Special:EntityData/" + value + ".json"; + const n = Number(value) + if(isNaN(n)){ + return undefined + } + return n; + } + + /** + * Loads a wikidata page + * @returns the entity of the given value + */ + public static async LoadWikidataEntryAsync(value: string | number): Promise { + const id = Wikidata.ExtractKey(value) + if(id === undefined){ + console.warn("Could not extract a wikidata entry from", value) + return undefined; + } + console.log("Requesting wikidata with id", id) + const url = "https://www.wikidata.org/wiki/Special:EntityData/Q" + id + ".json"; const response = await Utils.downloadJson(url) - return Wikidata.ParseResponse(response.entities[value]); + return Wikidata.ParseResponse(response.entities["Q" + id]) } } \ No newline at end of file diff --git a/Logic/Web/Wikipedia.ts b/Logic/Web/Wikipedia.ts index 34bde4fc5..dedfa144d 100644 --- a/Logic/Web/Wikipedia.ts +++ b/Logic/Web/Wikipedia.ts @@ -61,7 +61,6 @@ export default class Wikipedia { const links = Array.from(content.getElementsByTagName("a")) - console.log("Links are", links) // Rewrite relative links to absolute links + open them in a new tab links.filter(link => link.getAttribute("href")?.startsWith("/") ?? false). forEach(link => { diff --git a/UI/BigComponents/AllDownloads.ts b/UI/BigComponents/AllDownloads.ts index 2170eb496..3df26f53d 100644 --- a/UI/BigComponents/AllDownloads.ts +++ b/UI/BigComponents/AllDownloads.ts @@ -39,13 +39,13 @@ export default class AllDownloads extends ScrollableFullScreen { const loading = Svg.loading_svg().SetClass("animate-rotate"); + const dloadTrans = Translations.t.general.download const icon = new Toggle(loading, Svg.floppy_ui(), isExporting); const text = new Toggle( - new FixedUiElement("Exporting..."), - + dloadTrans.exporting.Clone(), new Combine([ - Translations.t.general.download.downloadAsPdf.Clone().SetClass("font-bold"), - Translations.t.general.download.downloadAsPdfHelper.Clone()] + dloadTrans.downloadAsPdf.Clone().SetClass("font-bold"), + dloadTrans.downloadAsPdfHelper.Clone()] ).SetClass("flex flex-col") .onClick(() => { generatePdf() diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts index ffcdbc3c9..336d77405 100644 --- a/UI/BigComponents/FilterView.ts +++ b/UI/BigComponents/FilterView.ts @@ -121,7 +121,6 @@ export default class FilterView extends VariableUiElement { listFilterElements.map((input) => input[1].data) ); - console.log(listTagsFilters, oldValue) flayer.appliedFilters.setData(listTagsFilters); }) ); diff --git a/UI/ExportPDF.ts b/UI/ExportPDF.ts index 54879b9b2..1bb57fb1c 100644 --- a/UI/ExportPDF.ts +++ b/UI/ExportPDF.ts @@ -32,7 +32,7 @@ export default class ExportPDF { private readonly mapH = 210; private readonly scaling = 2 private readonly freeDivId: string; - private readonly _layout: UIEventSource; + private readonly _layout: LayoutConfig; private _screenhotTaken = false; constructor( @@ -41,7 +41,7 @@ export default class ExportPDF { location: UIEventSource, background?: UIEventSource features: FeaturePipeline, - layout: UIEventSource + layout: LayoutConfig } ) { @@ -87,7 +87,6 @@ export default class ExportPDF { minimap.leafletMap .addCallbackAndRunD(leaflet => { const bounds = BBox.fromLeafletBounds(leaflet.getBounds().pad(0.2)) options.features.GetTilesPerLayerWithin(bounds, tile => { - console.log("REndering", tile.name) new ShowDataLayer( { features: tile, @@ -108,13 +107,13 @@ export default class ExportPDF { } private async CreatePdf(leaflet: L.Map) { + console.log("PDF creation started") const t = Translations.t.general.pdf; - const layout = this._layout.data + const layout = this._layout const screenshotter = new SimpleMapScreenshoter(); //minimap op index.html -> hidden daar alles op doen en dan weg //minimap - leaflet map ophalen - boundaries ophalen - State.state.featurePipeline screenshotter.addTo(leaflet); - console.log("Taking screenshot") let doc = new jsPDF('landscape'); @@ -164,7 +163,6 @@ export default class ExportPDF { const imgSource = layout.icon const imgType = imgSource.substr(imgSource.lastIndexOf(".") + 1); img.src = imgSource - console.log(imgType) if (imgType.toLowerCase() === "svg") { new FixedUiElement("").AttachTo(this.freeDivId) diff --git a/UI/WikipediaBox.ts b/UI/WikipediaBox.ts index f5c582ed2..af3e27c7e 100644 --- a/UI/WikipediaBox.ts +++ b/UI/WikipediaBox.ts @@ -8,78 +8,69 @@ import BaseUIElement from "./BaseUIElement"; import Title from "./Base/Title"; import Translations from "./i18n/Translations"; import Svg from "../Svg"; -import Wikidata from "../Logic/Web/Wikidata"; +import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata"; import Locale from "./i18n/Locale"; +import Toggle from "./Input/Toggle"; -export default class WikipediaBox extends Combine { +export default class WikipediaBox extends Toggle { - private static async ExtractWikiPages(wikidata): Promise> { - return (await Wikidata.LoadWikidataEntry(wikidata)).wikisites - } - - - private static _cache = new Map() constructor(wikidataId: string | UIEventSource) { const wp = Translations.t.general.wikipedia; - if(typeof wikidataId === "string"){ + if (typeof wikidataId === "string") { wikidataId = new UIEventSource(wikidataId) } - - const knownPages = new UIEventSource<{success:Map}|{error:any}>(undefined) - - wikidataId.addCallbackAndRunD(wikidataId => { - WikipediaBox.ExtractWikiPages(wikidataId).then(pages => { - knownPages.setData({success:pages}) - }).catch(err=> { - knownPages.setData({error: err}) - }) - }) - - const cachedPages = new Map() - - const contents = new VariableUiElement( - knownPages.map(pages => { - if (pages === undefined) { + + const wikibox = wikidataId + .bind(id => { + console.log("Wikidata is", id) + if(id === undefined){ + return undefined + } + console.log("Initing load WIkidataentry with id", id) + return Wikidata.LoadWikidataEntry(id); + }) + .map(maybewikidata => { + if (maybewikidata === undefined) { return new Loading(wp.loading.Clone()) } - if (pages["error"] !== undefined) { + if (maybewikidata["error"] !== undefined) { return wp.failed.Clone().SetClass("alert p-4") } - const dict: Map = pages["success"] + const wikidata = maybewikidata["success"] + console.log("Got wikidata response", wikidata) + if (wikidata.wikisites.size === 0) { + return wp.noWikipediaPage.Clone() + } - const preferredLanguage = [Locale.language.data, "en", Array.from(dict.keys())[0]] + const preferredLanguage = [Locale.language.data, "en", Array.from(wikidata.wikisites.keys())[0]] let language let pagetitle; let i = 0 do { language = preferredLanguage[i] - pagetitle = dict.get(language) + pagetitle = wikidata.wikisites.get(language) i++; - if(i >= preferredLanguage.length){ - return wp.noWikipediaPage.Clone() - } } while (pagetitle === undefined) - - if(cachedPages.has(language)){ - return cachedPages.get(language) - } - - const page = WikipediaBox.createContents(pagetitle, language); - cachedPages.set(language, page) - return page + return WikipediaBox.createContents(pagetitle, language) }, [Locale.language]) + + + const contents = new VariableUiElement( + wikibox ).SetClass("overflow-auto normal-background rounded-lg") - super([ + const mainContent = new Combine([ new Combine([Svg.wikipedia_ui().SetStyle("width: 1.5rem").SetClass("mr-3"), new Title(Translations.t.general.wikipedia.wikipediaboxTitle.Clone(), 2)]).SetClass("flex"), - contents]) - - this - .SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col") + contents]).SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col") + super( + mainContent, + undefined, + wikidataId.map(id => id !== undefined) + ) } /** @@ -103,6 +94,7 @@ export default class WikipediaBox extends Combine { return new FixedUiElement(htmlContent["success"]).SetClass("wikipedia-article") } if (htmlContent["error"]) { + console.warn("Loading wikipage failed due to", htmlContent["error"]) return wp.failed.Clone().SetClass("alert p-4") } diff --git a/langs/en.json b/langs/en.json index 306ea8e80..650963864 100644 --- a/langs/en.json +++ b/langs/en.json @@ -174,6 +174,7 @@ "downloadAsPdf": "Download a PDF of the current map", "downloadAsPdfHelper": "Ideal to print the current map", "downloadGeojson": "Download visible data as geojson", + "exporting": "Exporting...", "downloadGeoJsonHelper": "Compatible with QGIS, ArcGIS, ESRI, ...", "downloadCSV": "Download visible data as CSV", "downloadCSVHelper": "Compatible with LibreOffice Calc, Excel, …", diff --git a/package.json b/package.json index 2505a2740..d7485c874 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "start": "npm run start:prepare && npm-run-all --parallel start:parallel:*", "strt": "npm run start:prepare && npm run start:parallel:parcel", "start:prepare": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory", - "start:parallel:parcel": "parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.png vendor/* vendor/*/*", + "start:parallel:parcel": "parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/layers/*/*.jpg assets/layers/*/*.png assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.jpg assets/themes/*/*.png vendor/* vendor/*/*", "start:parallel:tailwindcli": "tailwindcss -i index.css -o css/index-tailwind-output.css --watch", "test": "ts-node test/TestAll.ts", "init": "npm ci && npm run generate && npm run generate:editor-layer-index && npm run generate:layouts && npm run clean", diff --git a/scripts/generateCache.ts b/scripts/generateCache.ts index a5a915bde..e207cf67e 100644 --- a/scripts/generateCache.ts +++ b/scripts/generateCache.ts @@ -2,9 +2,6 @@ * Generates a collection of geojson files based on an overpass query for a given theme */ import {Utils} from "../Utils"; - -Utils.runningFromConsole = true - import {Overpass} from "../Logic/Osm/Overpass"; import {existsSync, readFileSync, writeFileSync} from "fs"; import {TagsFilter} from "../Logic/Tags/TagsFilter"; @@ -22,12 +19,13 @@ import FilteredLayer from "../Models/FilteredLayer"; import FeatureSource, {FeatureSourceForLayer} from "../Logic/FeatureSource/FeatureSource"; import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; import TiledFeatureSource from "../Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource"; +import Constants from "../Models/Constants"; ScriptUtils.fixUtils() -function createOverpassObject(theme: LayoutConfig, relationTracker: RelationsTracker) { +function createOverpassObject(theme: LayoutConfig, relationTracker: RelationsTracker, backend: string) { let filters: TagsFilter[] = []; let extraScripts: string[] = []; for (const layer of theme.layers) { @@ -58,7 +56,7 @@ function createOverpassObject(theme: LayoutConfig, relationTracker: RelationsTra if (filters.length + extraScripts.length === 0) { throw "Nothing to download! The theme doesn't declare anything to download" } - return new Overpass(new Or(filters), extraScripts, new UIEventSource("https://overpass.kumi.systems/api/interpreter"), //https://overpass-api.de/api/interpreter"), + return new Overpass(new Or(filters), extraScripts, backend, new UIEventSource(60), relationTracker); } @@ -71,7 +69,7 @@ function geoJsonName(targetDir: string, x: number, y: number, z: number): string } /// Downloads the given feature and saves them to disk -async function downloadRaw(targetdir: string, r: TileRange, overpass: Overpass)/* : {failed: number, skipped :number} */ { +async function downloadRaw(targetdir: string, r: TileRange, theme: LayoutConfig, relationTracker: RelationsTracker)/* : {failed: number, skipped :number} */ { let downloaded = 0 let failed = 0 let skipped = 0 @@ -93,35 +91,28 @@ async function downloadRaw(targetdir: string, r: TileRange, overpass: Overpass)/ east: Math.max(boundsArr[0][1], boundsArr[1][1]), west: Math.min(boundsArr[0][1], boundsArr[1][1]) } + const overpass = createOverpassObject(theme, relationTracker, Constants.defaultOverpassUrls[(downloaded + failed) % Constants.defaultOverpassUrls.length]) const url = overpass.buildQuery("[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]") - await ScriptUtils.DownloadJSON(url) - .then(json => { - if (json.elements.length === 0) { - console.log("Got an empty response!") - if ((json.remark ?? "").startsWith("runtime error")) { - console.error("Got a runtime error: ", json.remark) - failed++; - return - } + try { - } - - - console.log("Got the response - writing to ", filename) - writeFileSync(filename, JSON.stringify(json, null, " ")); + const json = await ScriptUtils.DownloadJSON(url) + if (json.elements.length === 0) { + console.log("Got an empty response!") + if ((json.remark ?? "").startsWith("runtime error")) { + console.error("Got a runtime error: ", json.remark) + failed++; } - ) - .catch(err => { - console.log(url) - console.log("Could not download - probably hit the rate limit; waiting a bit. (" + err + ")") - failed++; - return ScriptUtils.sleep(60000).then(() => console.log("Waiting is done")) - }) - if (x < r.xend || y < r.yend) { - console.debug("Cooling down 10s") - await ScriptUtils.sleep(10000) + } else { + console.log("Got the response - writing to ", filename) + writeFileSync(filename, JSON.stringify(json, null, " ")); + } + } catch (err) { + console.log(url) + console.log("Could not download - probably hit the rate limit; waiting a bit. (" + err + ")") + failed++; + await ScriptUtils.sleep(1000) } } } @@ -291,11 +282,10 @@ async function main(args: string[]) { return } const relationTracker = new RelationsTracker() - const overpass = createOverpassObject(theme, relationTracker) let failed = 0; do { - const cachingResult = await downloadRaw(targetdir, tileRange, overpass) + const cachingResult = await downloadRaw(targetdir, tileRange, theme, relationTracker) failed = cachingResult.failed if (failed > 0) { await ScriptUtils.sleep(30000) diff --git a/test/TestAll.ts b/test/TestAll.ts index 6123681fb..bdb92afdb 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -10,6 +10,7 @@ import RelationSplitHandlerSpec from "./RelationSplitHandler.spec"; import SplitActionSpec from "./SplitAction.spec"; import {Utils} from "../Utils"; import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec"; +import WikidataSpecTest from "./Wikidata.spec.test"; ScriptUtils.fixUtils() @@ -23,7 +24,8 @@ const allTests = [ new UnitsSpec(), new RelationSplitHandlerSpec(), new SplitActionSpec(), - new TileFreshnessCalculatorSpec() + new TileFreshnessCalculatorSpec(), + new WikidataSpecTest() ] Utils.externalDownloadFunction = async (url) => { diff --git a/test/Wikidata.spec.test.ts b/test/Wikidata.spec.test.ts new file mode 100644 index 000000000..000a77217 --- /dev/null +++ b/test/Wikidata.spec.test.ts @@ -0,0 +1,29 @@ +import Wikidata from "../Logic/Web/Wikidata"; +import * as assert from "assert"; +import {equal} from "assert"; +import T from "./TestHelper"; +import {Utils} from "../Utils"; + +export default class WikidataSpecTest extends T { + constructor() { + super("Wikidata", + [ + ["download wikidata", + async () => { + + Utils.injectJsonDownloadForTests( + "https://www.wikidata.org/wiki/Special:EntityData/Q14517013.json" , + {"entities":{"Q14517013":{"pageid":16187848,"ns":0,"title":"Q14517013","lastrevid":1408823680,"modified":"2021-04-26T07:35:01Z","type":"item","id":"Q14517013","labels":{"nl":{"language":"nl","value":"Vredesmolen"},"en":{"language":"en","value":"Peace Mill"}},"descriptions":{"nl":{"language":"nl","value":"molen in West-Vlaanderen"}},"aliases":{},"claims":{"P625":[{"mainsnak":{"snaktype":"value","property":"P625","hash":"d86538f14e8cca00bbf30fb029829aacbc6903a0","datavalue":{"value":{"latitude":50.99444,"longitude":2.92528,"altitude":null,"precision":0.0001,"globe":"http://www.wikidata.org/entity/Q2"},"type":"globecoordinate"},"datatype":"globe-coordinate"},"type":"statement","id":"Q14517013$DBFBFD69-F54D-4C92-A7F4-A44F876E5776","rank":"normal","references":[{"hash":"732ec1c90a6f0694c7db9a71bf09fe7f2b674172","snaks":{"P143":[{"snaktype":"value","property":"P143","hash":"9123b0de1cc9c3954366ba797d598e4e1ea4146f","datavalue":{"value":{"entity-type":"item","numeric-id":10000,"id":"Q10000"},"type":"wikibase-entityid"},"datatype":"wikibase-item"}]},"snaks-order":["P143"]}]}],"P17":[{"mainsnak":{"snaktype":"value","property":"P17","hash":"c2859f311753176d6bdfa7da54ceeeac7acb52c8","datavalue":{"value":{"entity-type":"item","numeric-id":31,"id":"Q31"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q14517013$C12E4DA5-44E1-41ED-BF3D-C84381246429","rank":"normal"}],"P18":[{"mainsnak":{"snaktype":"value","property":"P18","hash":"af765166ecaa7d01ea800812b5b356886b8849a0","datavalue":{"value":"Klerken Vredesmolen R01.jpg","type":"string"},"datatype":"commonsMedia"},"type":"statement","id":"Q14517013$5291801E-11BE-4CE7-8F42-D0D6A120F390","rank":"normal"}],"P2867":[{"mainsnak":{"snaktype":"value","property":"P2867","hash":"b1c627972ba2cc71e3567d2fb56cb5f90dd64007","datavalue":{"value":"893","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q14517013$2aff9dcd-4d24-cd92-b5af-f6268425695f","rank":"normal"}],"P31":[{"mainsnak":{"snaktype":"value","property":"P31","hash":"9b48263bb51c506553aac2281ae331353b5c9002","datavalue":{"value":{"entity-type":"item","numeric-id":38720,"id":"Q38720"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q14517013$46dd9d89-4999-eee6-20a4-c4f6650b1d9c","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P31","hash":"a1d6f3409c57de0361c68263c9397a99dabe19ea","datavalue":{"value":{"entity-type":"item","numeric-id":3851468,"id":"Q3851468"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q14517013$C83A8B1F-7798-493A-86C9-EC0EFEE356B3","rank":"normal"},{"mainsnak":{"snaktype":"value","property":"P31","hash":"ee5ba9185bdf9f0eb80b52e1cdc70c5883fac95a","datavalue":{"value":{"entity-type":"item","numeric-id":623605,"id":"Q623605"},"type":"wikibase-entityid"},"datatype":"wikibase-item"},"type":"statement","id":"Q14517013$CF74DC2E-6814-4755-9BAD-6EE9FEF637DD","rank":"normal"}],"P2671":[{"mainsnak":{"snaktype":"value","property":"P2671","hash":"83fb38a3c6407f7d0d7bb051d1c31cea8ae26975","datavalue":{"value":"/g/121cb15z","type":"string"},"datatype":"external-id"},"type":"statement","id":"Q14517013$E6FFEF32-0131-42FD-9C66-1A406B68059A","rank":"normal"}]},"sitelinks":{"commonswiki":{"site":"commonswiki","title":"Category:Vredesmolen, Klerken","badges":[],"url":"https://commons.wikimedia.org/wiki/Category:Vredesmolen,_Klerken"},"nlwiki":{"site":"nlwiki","title":"Vredesmolen","badges":[],"url":"https://nl.wikipedia.org/wiki/Vredesmolen"}}}}} + ) + + + const wdata = await Wikidata.LoadWikidataEntryAsync(14517013) + T.isTrue(wdata.wikisites.has("nl"), "dutch for wikisite not found") + equal("Vredesmolen", wdata.wikisites.get("nl")) + } + + ] + ]); + } + +} \ No newline at end of file From 5bcb879dfe7e3005923bb2357fd7595edb8fb10c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 01:57:36 +0200 Subject: [PATCH 094/110] Fix local caching by hiding latlon2country import --- Logic/MetaTagging.ts | 4 ++-- Logic/Osm/OsmObject.ts | 1 - Logic/SimpleMetaTagger.ts | 6 +----- UI/WikipediaBox.ts | 1 + index.ts | 5 ++++- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index ee4209f4a..47d7f8a35 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -77,7 +77,7 @@ export default class MetaTagging { } if(somethingChanged){ - State.state.allElements.getEventSourceById(feature.properties.id).ping() + State.state?.allElements?.getEventSourceById(feature.properties.id)?.ping() } } } @@ -150,7 +150,7 @@ export default class MetaTagging { for (const f of functions) { f(params, feature); } - State.state.allElements.getEventSourceById(feature.properties.id).ping(); + State.state?.allElements?.getEventSourceById(feature.properties.id)?.ping(); } catch (e) { console.error("While calculating a tag value: ", e) } diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index 4538622d1..9c1efbae9 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -177,7 +177,6 @@ export abstract class OsmObject { private static constructPolygonFeatures(): Map, blacklist: boolean }> { const result = new Map, blacklist: boolean }>(); - for (const polygonFeature of polygon_features) { const key = polygonFeature.key; diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 360bb54e5..eb1a6a49d 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -1,15 +1,11 @@ import {GeoOperations} from "./GeoOperations"; import State from "../State"; -import {And} from "./Tags/And"; -import {Tag} from "./Tags/Tag"; -import {Or} from "./Tags/Or"; import {Utils} from "../Utils"; import opening_hours from "opening_hours"; import Combine from "../UI/Base/Combine"; import BaseUIElement from "../UI/BaseUIElement"; import Title from "../UI/Base/Title"; import {FixedUiElement} from "../UI/Base/FixedUiElement"; -import CountryCoder from "latlon2country/index"; const cardinalDirections = { @@ -21,7 +17,7 @@ const cardinalDirections = { export default class SimpleMetaTagger { - private static coder: CountryCoder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); + public static coder: any; public static readonly objectMetaInfo = new SimpleMetaTagger( { keys: ["_last_edit:contributor", diff --git a/UI/WikipediaBox.ts b/UI/WikipediaBox.ts index af3e27c7e..7f0d392a6 100644 --- a/UI/WikipediaBox.ts +++ b/UI/WikipediaBox.ts @@ -66,6 +66,7 @@ export default class WikipediaBox extends Toggle { new Combine([Svg.wikipedia_ui().SetStyle("width: 1.5rem").SetClass("mr-3"), new Title(Translations.t.general.wikipedia.wikipediaboxTitle.Clone(), 2)]).SetClass("flex"), contents]).SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col") + .SetStyle("max-height: inherit") super( mainContent, undefined, diff --git a/index.ts b/index.ts index 0614e40a2..1e98ae890 100644 --- a/index.ts +++ b/index.ts @@ -13,11 +13,14 @@ import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"; import Constants from "./Models/Constants"; import MinimapImplementation from "./UI/Base/MinimapImplementation"; +import CountryCoder from "latlon2country/index"; +import SimpleMetaTagger from "./Logic/SimpleMetaTagger"; MinimapImplementation.initialize() // Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts ValidatedTextField.bestLayerAt = (location, layerPref) => AvailableBaseLayers.SelectBestLayerAccordingTo(location, layerPref) - + + SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); let defaultLayout = "" // --------------------- Special actions based on the parameters ----------------- From 23136f7e7a6b5a7760c0b8a535ce657025e8b52e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 02:11:06 +0200 Subject: [PATCH 095/110] Small fixes to the 'generateCache' script --- Logic/Osm/Overpass.ts | 2 +- Models/Constants.ts | 5 ++--- scripts/ScriptUtils.ts | 3 +-- scripts/generateCache.ts | 15 +++++++-------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Logic/Osm/Overpass.ts b/Logic/Osm/Overpass.ts index 97911a3b6..257133307 100644 --- a/Logic/Osm/Overpass.ts +++ b/Logic/Osm/Overpass.ts @@ -42,7 +42,7 @@ export class Overpass { } const self = this; const json = await Utils.downloadJson(query) - console.log("Got json!", json) + if (json.elements.length === 0 && json.remark !== undefined) { console.warn("Timeout or other runtime error while querying overpass", json.remark); throw `Runtime error (timeout or similar)${json.remark}` diff --git a/Models/Constants.ts b/Models/Constants.ts index 50cfcf750..ab7c007cd 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -12,10 +12,9 @@ export default class Constants { "https://overpass-api.de/api/interpreter", // 'Fair usage' "https://overpass.kumi.systems/api/interpreter", - // "https://overpass.nchc.org.tw/api/interpreter", + // Offline: "https://overpass.nchc.org.tw/api/interpreter", "https://overpass.openstreetmap.ru/cgi/interpreter", - // The french api, only 1000 per day per project allowed, so we put it as last resort - "https://overpass.openstreetmap.fr/api/interpreter" + // Doesn't support nwr "https://overpass.openstreetmap.fr/api/interpreter" ] diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts index 22cdd0d93..5469b5645 100644 --- a/scripts/ScriptUtils.ts +++ b/scripts/ScriptUtils.ts @@ -53,7 +53,7 @@ export default class ScriptUtils { try { headers = headers ?? {} headers.accept = "application/json" - console.log("Fetching", url) + console.log("ScriptUtils.DownloadJson(", url.substring(0,40), url.length > 40 ? "...":"" ,")") const urlObj = new URL(url) https.get({ host: urlObj.host, @@ -72,7 +72,6 @@ export default class ScriptUtils { res.addListener('end', function () { const result = parts.join("") try { - console.log("Fetched", result) resolve(JSON.parse(result)) } catch (e) { console.error("Could not parse the following as JSON:", result) diff --git a/scripts/generateCache.ts b/scripts/generateCache.ts index e207cf67e..1377b3153 100644 --- a/scripts/generateCache.ts +++ b/scripts/generateCache.ts @@ -97,17 +97,16 @@ async function downloadRaw(targetdir: string, r: TileRange, theme: LayoutConfig, try { const json = await ScriptUtils.DownloadJSON(url) - if (json.elements.length === 0) { - console.log("Got an empty response!") - if ((json.remark ?? "").startsWith("runtime error")) { - console.error("Got a runtime error: ", json.remark) - failed++; - } + if ((json.remark ?? "").startsWith("runtime error")) { + console.error("Got a runtime error: ", json.remark) + failed++; + }else if (json.elements.length === 0) { + console.log("Got an empty response! Writing anyway") + } + - } else { console.log("Got the response - writing to ", filename) writeFileSync(filename, JSON.stringify(json, null, " ")); - } } catch (err) { console.log(url) console.log("Could not download - probably hit the rate limit; waiting a bit. (" + err + ")") From 4aaf8a841bfc3d009816f99fe0fc169682d2128c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 02:11:17 +0200 Subject: [PATCH 096/110] Version bump --- Models/Constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Models/Constants.ts b/Models/Constants.ts index ab7c007cd..76769cdae 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.1-rc1"; + public static vNumber = "0.10.1-rc2"; public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" From 2813c31a93919c038b70fe79bc03f4b6c8f7fa92 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 02:50:11 +0200 Subject: [PATCH 097/110] Temporary fix to questions --- UI/Popup/EditableTagRendering.ts | 2 +- UI/Popup/TagRenderingQuestion.ts | 74 ++++++++++++++------------------ package.json | 1 + 3 files changed, 34 insertions(+), 43 deletions(-) diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 5d7657fa5..585a75fee 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -50,7 +50,7 @@ export default class EditableTagRendering extends Toggle { const question = new Lazy(() => { - return new TagRenderingQuestion(tags, configuration, + return new TagRenderingQuestion(tags, configuration, { units: units, cancelButton: Translations.t.general.cancel.Clone() diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 9f99b2bb2..576189175 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -31,7 +31,7 @@ import {Unit} from "../../Models/Unit"; * Shows the question element. * Note that the value _migh_ already be known, e.g. when selected or when changing the value */ -export default class TagRenderingQuestion extends VariableUiElement { +export default class TagRenderingQuestion extends Combine { constructor(tags: UIEventSource, configuration: TagRenderingConfig, @@ -45,7 +45,7 @@ export default class TagRenderingQuestion extends VariableUiElement { ) { - const applicableMappings = + /* const applicableMappings = UIEventSource.ListStabilized(tags.map(tags => { const applicableMappings : {if: TagsFilter, then: any, ifnot?: TagsFilter}[] = [] for (const mapping of configuration.mappings ?? []) { @@ -69,7 +69,8 @@ export default class TagRenderingQuestion extends VariableUiElement { applicableMappings.map(applicableMappings => { return TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options) }) - ) + )*/ + super([TagRenderingQuestion.GenerateFullQuestion(tags, (configuration.mappings??[]).filter(mapping => mapping.hideInAnswer !== undefined), configuration, options)]) } private static GenerateFullQuestion(tags: UIEventSource, @@ -98,17 +99,21 @@ export default class TagRenderingQuestion extends VariableUiElement { console.error("MultiAnswer failed - probably not a single option was possible", configuration) throw "MultiAnswer failed - probably not a single option was possible" } - const save = async () => { + inputElement.GetValue().addCallbackAndRun(s => console.trace("Current selection is ", s)) + const save = () => { + console.log("OnSaveTriggered", inputElement) const selection = inputElement.GetValue().data; + console.log("Saving changes", selection) if (selection) { - await (State.state?.changes ?? new Changes()) + (State.state?.changes ?? new Changes()) .applyAction(new ChangeTagAction( tags.data.id, selection, tags.data - )) - } - - if (options.afterSave) { - options.afterSave(); + )).then(_ => { + console.log("Tagchanges applied") + }) + if (options.afterSave) { + options.afterSave(); + } } } @@ -155,13 +160,15 @@ export default class TagRenderingQuestion extends VariableUiElement { private static GenerateInputElement(configuration: TagRenderingConfig, applicableMappings: {if: TagsFilter, then: any, ifnot?: TagsFilter}[], - applicableUnit: Unit, tagsSource: UIEventSource): InputElement { + applicableUnit: Unit, + tagsSource: UIEventSource) + : InputElement { let inputEls: InputElement[]; const ifNotsPresent = applicableMappings.some(mapping => mapping.ifnot !== undefined) - function allIfNotsExcept(excludeIndex: number): UIEventSource { + function allIfNotsExcept(excludeIndex: number): TagsFilter[] { if (configuration.mappings === undefined || configuration.mappings.length === 0) { return undefined } @@ -172,34 +179,17 @@ export default class TagRenderingQuestion extends VariableUiElement { // The multianswer will do the ifnot configuration themself return undefined } - return tagsSource.map(currentTags => { - const negativeMappings = [] - - for (let i = 0; i < configuration.mappings.length; i++) { - const mapping = configuration.mappings[i]; - if (i === excludeIndex || mapping.ifnot === undefined) { - continue - } - - const hidden = mapping.hideInAnswer - if (hidden === undefined) { - negativeMappings.push(mapping.ifnot) - continue - } - if (hidden === true) { - continue - } - - if ((hidden).matchesProperties(currentTags)) { - // This option is currently hidden - continue - } - negativeMappings.push(mapping.ifnot) + + const negativeMappings = [] + for (let i = 0; i < applicableMappings.length; i++) { + const mapping = applicableMappings[i]; + if (i === excludeIndex || mapping.ifnot === undefined) { + continue } - - return Utils.NoNull(negativeMappings) - }) + negativeMappings.push(mapping.ifnot) + } + return Utils.NoNull(negativeMappings) } @@ -213,7 +203,7 @@ export default class TagRenderingQuestion extends VariableUiElement { const dropdown: InputElement = new DropDown("", applicableMappings.map((mapping, i) => { return { - value: new And([mapping.if, ...allIfNotsExcept(i).data]), + value: new And([mapping.if, ...allIfNotsExcept(i)]), shown: Translations.WT(mapping.then).Clone() } }) @@ -356,11 +346,11 @@ export default class TagRenderingQuestion extends VariableUiElement { mapping: { if: TagsFilter, then: Translation, - }, ifNot?: UIEventSource): InputElement { + }, ifNot?: TagsFilter[]): InputElement { - let tagging: TagsFilter | UIEventSource = mapping.if; + let tagging: TagsFilter = mapping.if; if (ifNot !== undefined) { - tagging = ifNot.map(ifNots => new And([mapping.if, ...ifNots])) + tagging = new And([mapping.if, ...ifNot]) } return new FixedInputElement( diff --git a/package.json b/package.json index d7485c874..4f8cc5a9d 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "start:prepare": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory", "start:parallel:parcel": "parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/layers/*/*.jpg assets/layers/*/*.png assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.jpg assets/themes/*/*.png vendor/* vendor/*/*", "start:parallel:tailwindcli": "tailwindcss -i index.css -o css/index-tailwind-output.css --watch", + "generate:css": "tailwindcss -i index.css -o css/index-tailwind-output.css", "test": "ts-node test/TestAll.ts", "init": "npm ci && npm run generate && npm run generate:editor-layer-index && npm run generate:layouts && npm run clean", "add-weblate-upstream": "git remote add weblate-layers https://hosted.weblate.org/git/mapcomplete/layer-translations/ ; git remote update weblate-layers", From b6b20ed3ca86af3de4dddff64b86efd96c2a2787 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 20:50:18 +0200 Subject: [PATCH 098/110] Fix bug in bind, fix bug in tagrenderingquestion --- Logic/UIEventSource.ts | 53 +++++++++++++++++--------------- UI/Popup/TagRenderingQuestion.ts | 22 ++++++++----- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index 10922ca14..55770bb71 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -69,7 +69,7 @@ export class UIEventSource { * @param promise * @constructor */ - public static FromPromise(promise : Promise): UIEventSource{ + public static FromPromise(promise: Promise): UIEventSource { const src = new UIEventSource(undefined) promise?.then(d => src.setData(d)) promise?.catch(err => console.warn("Promise failed:", err)) @@ -82,9 +82,9 @@ export class UIEventSource { * @param promise * @constructor */ - public static FromPromiseWithErr(promise : Promise): UIEventSource<{success: T} | {error: any}>{ - const src = new UIEventSource<{success: T}|{error: any}>(undefined) - promise?.then(d => src.setData({success:d})) + public static FromPromiseWithErr(promise: Promise): UIEventSource<{ success: T } | { error: any }> { + const src = new UIEventSource<{ success: T } | { error: any }>(undefined) + promise?.then(d => src.setData({success: d})) promise?.catch(err => src.setData({error: err})) return src } @@ -97,42 +97,42 @@ export class UIEventSource { * src.addCallback(_ => console.log("src pinged")) * stable.addCallback(_ => console.log("stable pinged)) * src.setDate([...src.data]) - * + * * This will only trigger 'src pinged' - * + * * @param src * @constructor */ - public static ListStabilized(src: UIEventSource) : UIEventSource{ - + public static ListStabilized(src: UIEventSource): UIEventSource { + const stable = new UIEventSource(src.data) src.addCallback(list => { - if(list === undefined){ + if (list === undefined) { stable.setData(undefined) return; } const oldList = stable.data - if(oldList === list){ + if (oldList === list) { return; } - if(oldList.length !== list.length){ + if (oldList.length !== list.length) { stable.setData(list); return; } for (let i = 0; i < list.length; i++) { - if(oldList[i] !== list[i]){ + if (oldList[i] !== list[i]) { stable.setData(list); return; } } - + // No actual changes, so we don't do anything return; }) return stable } - + /** * Adds a callback * @@ -190,21 +190,26 @@ export class UIEventSource { /** * Monadic bind function */ - public bind(f: ((t: T) => UIEventSource)): UIEventSource{ - const sink = new UIEventSource( undefined ) + public bind(f: ((t: T) => UIEventSource)): UIEventSource { + const mapped = this.map(f) + const sink = new UIEventSource(undefined) const seenEventSources = new Set>(); - this.addCallbackAndRun(data => { - const eventSource = f(data) - if(eventSource === undefined){ + mapped.addCallbackAndRun(newEventSource => { + + if (newEventSource === undefined) { sink.setData(undefined) - }else if(!seenEventSources.has(eventSource)){ - eventSource.addCallbackAndRun(mappedData => sink.setData(mappedData)) - seenEventSources.add(eventSource) + } else if (!seenEventSources.has(newEventSource)) { + seenEventSources.add(newEventSource) + newEventSource.addCallbackAndRun(resultData => { + if (mapped.data === newEventSource) { + sink.setData(resultData); + } + }) } }) - + return sink; - } + } /** * Monoidal map: diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 576189175..e95d3abfe 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -70,7 +70,10 @@ export default class TagRenderingQuestion extends Combine { return TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options) }) )*/ - super([TagRenderingQuestion.GenerateFullQuestion(tags, (configuration.mappings??[]).filter(mapping => mapping.hideInAnswer !== undefined), configuration, options)]) + + const applicableMappings = Utils.NoNull((configuration.mappings??[]).filter(mapping => mapping.hideInAnswer !== undefined)) + + super([TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options)]) } private static GenerateFullQuestion(tags: UIEventSource, @@ -163,6 +166,13 @@ export default class TagRenderingQuestion extends Combine { applicableUnit: Unit, tagsSource: UIEventSource) : InputElement { + + // FreeForm input will be undefined if not present; will already contain a special input element if applicable + const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); + + + + const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 let inputEls: InputElement[]; @@ -173,11 +183,11 @@ export default class TagRenderingQuestion extends Combine { return undefined } if (!ifNotsPresent) { - return undefined + return [] } if (configuration.multiAnswer) { // The multianswer will do the ifnot configuration themself - return undefined + return [] } const negativeMappings = [] @@ -193,9 +203,7 @@ export default class TagRenderingQuestion extends Combine { } - const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); - const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 - + if (applicableMappings.length < 8 || configuration.multiAnswer || hasImages || ifNotsPresent) { inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i))); inputEls = Utils.NoNull(inputEls); @@ -226,7 +234,7 @@ export default class TagRenderingQuestion extends Combine { } if (configuration.multiAnswer) { - return TagRenderingQuestion.GenerateMultiAnswer(configuration, inputEls, ff, configuration.mappings.map(mp => mp.ifnot)) + return TagRenderingQuestion.GenerateMultiAnswer(configuration, inputEls, ff, applicableMappings.map(mp => mp.ifnot)) } else { return new RadioButton(inputEls, {selectFirstAsDefault: false}) } From abae813606e6262df96d7f0cfdaa2d5b9e2bdbbc Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 21:44:43 +0200 Subject: [PATCH 099/110] Fix dynamism in questions with new VariableInputElement --- UI/Base/VariableUIElement.ts | 1 - UI/Input/VariableInputElement.ts | 35 +++++++++++++++ UI/Popup/EditableTagRendering.ts | 10 ++--- UI/Popup/QuestionBox.ts | 6 ++- UI/Popup/TagRenderingQuestion.ts | 75 +++++++++++++------------------- test.ts | 32 +++++++++++--- 6 files changed, 98 insertions(+), 61 deletions(-) create mode 100644 UI/Input/VariableInputElement.ts diff --git a/UI/Base/VariableUIElement.ts b/UI/Base/VariableUIElement.ts index 7c895b9cb..6ee720627 100644 --- a/UI/Base/VariableUIElement.ts +++ b/UI/Base/VariableUIElement.ts @@ -7,7 +7,6 @@ export class VariableUiElement extends BaseUIElement { constructor(contents: UIEventSource) { super(); this._contents = contents; - } protected InnerConstructElement(): HTMLElement { diff --git a/UI/Input/VariableInputElement.ts b/UI/Input/VariableInputElement.ts new file mode 100644 index 000000000..1918dfe9a --- /dev/null +++ b/UI/Input/VariableInputElement.ts @@ -0,0 +1,35 @@ +import {InputElement} from "./InputElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import BaseUIElement from "../BaseUIElement"; +import {VariableUiElement} from "../Base/VariableUIElement"; + +export default class VariableInputElement extends InputElement { + + private readonly value: UIEventSource; + private readonly element: BaseUIElement + public readonly IsSelected: UIEventSource; + private readonly upstream: UIEventSource>; + + constructor(upstream: UIEventSource>) { + + super() + this.upstream = upstream; + this.value = upstream.bind(v => v.GetValue()) + this.element = new VariableUiElement(upstream) + this.IsSelected = upstream.bind(v => v.IsSelected) + } + + GetValue(): UIEventSource { + return this.value; + } + + protected InnerConstructElement(): HTMLElement { + return this.element.ConstructElement(); + } + + + IsValid(t: T): boolean { + return this.upstream.data.IsValid(t); + } + +} \ No newline at end of file diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 585a75fee..2be62b01b 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -25,6 +25,7 @@ export default class EditableTagRendering extends Toggle { const renderingIsShown = tags.map(tags => configuration.IsKnown(tags) && (configuration?.condition?.matchesProperties(tags) ?? true)) + super( new Lazy(() => EditableTagRendering.CreateRendering(tags, configuration, units, editMode)), undefined, @@ -49,8 +50,8 @@ export default class EditableTagRendering extends Toggle { ]).SetClass("flex justify-between w-full") - const question = new Lazy(() => { - return new TagRenderingQuestion(tags, configuration, + const question = new Lazy(() => + new TagRenderingQuestion(tags, configuration, { units: units, cancelButton: Translations.t.general.cancel.Clone() @@ -61,10 +62,7 @@ export default class EditableTagRendering extends Toggle { afterSave: () => { editMode.setData(false) } - }) - - - }) + })) rendering = new Toggle( diff --git a/UI/Popup/QuestionBox.ts b/UI/Popup/QuestionBox.ts index 2b7fdfd7a..1192ff8ee 100644 --- a/UI/Popup/QuestionBox.ts +++ b/UI/Popup/QuestionBox.ts @@ -7,6 +7,7 @@ import BaseUIElement from "../BaseUIElement"; import {VariableUiElement} from "../Base/VariableUIElement"; import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; import {Unit} from "../../Models/Unit"; +import Lazy from "../Base/Lazy"; /** @@ -27,7 +28,8 @@ export default class QuestionBox extends VariableUiElement { } const tagRenderingQuestions = tagRenderings - .map((tagRendering, i) => new TagRenderingQuestion(tagsSource, tagRendering, + .map((tagRendering, i) => + new Lazy(() => new TagRenderingQuestion(tagsSource, tagRendering, { units: units, afterSave: () => { @@ -41,7 +43,7 @@ export default class QuestionBox extends VariableUiElement { skippedQuestions.ping(); }) } - )); + ))); const skippedQuestionsButton = Translations.t.general.skippedQuestions.Clone() .onClick(() => { diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index e95d3abfe..40a68609d 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -26,6 +26,7 @@ import InputElementWrapper from "../Input/InputElementWrapper"; import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"; import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; import {Unit} from "../../Models/Unit"; +import VariableInputElement from "../Input/VariableInputElement"; /** * Shows the question element. @@ -45,48 +46,29 @@ export default class TagRenderingQuestion extends Combine { ) { - /* const applicableMappings = + const applicableMappingsSrc = UIEventSource.ListStabilized(tags.map(tags => { - const applicableMappings : {if: TagsFilter, then: any, ifnot?: TagsFilter}[] = [] + const applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[] = [] for (const mapping of configuration.mappings ?? []) { if (mapping.hideInAnswer === true) { continue } if (mapping.hideInAnswer === false || mapping.hideInAnswer === undefined) { - applicableMappings.push(mapping) + applicableMappings.push(mapping) continue } - const condition = mapping.hideInAnswer; + const condition = mapping.hideInAnswer; const isShown = !condition.matchesProperties(tags) - if(isShown){ + if (isShown) { applicableMappings.push(mapping) } } return applicableMappings })); - super( - applicableMappings.map(applicableMappings => { - return TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options) - }) - )*/ - - const applicableMappings = Utils.NoNull((configuration.mappings??[]).filter(mapping => mapping.hideInAnswer !== undefined)) - - super([TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options)]) - } - - private static GenerateFullQuestion(tags: UIEventSource, - applicableMappings: {if: TagsFilter, then: any, ifnot?: TagsFilter}[], - configuration: TagRenderingConfig, - options?: { - units?: Unit[], - afterSave?: () => void, - cancelButton?: BaseUIElement, - saveButtonConstr?: (src: UIEventSource) => BaseUIElement, - bottomText?: (src: UIEventSource) => BaseUIElement - } - ) { + applicableMappingsSrc.addCallbackAndRun(appl => console.log("Currently applicable renderings are:", appl.map(m => m.then.txt).join(", "))) + + if (configuration === undefined) { throw "A question is needed for a question visualization" } @@ -96,13 +78,14 @@ export default class TagRenderingQuestion extends Combine { .SetClass("question-text"); - const inputElement: InputElement = TagRenderingQuestion.GenerateInputElement(configuration, applicableMappings, applicableUnit, tags) + const inputElement: InputElement = + new VariableInputElement(applicableMappingsSrc.map(applicableMappings => + TagRenderingQuestion.GenerateInputElement(configuration, applicableMappings, applicableUnit, tags) + )) + + + // inputElement.GetValue().addCallbackAndRun(s => console.trace(configuration.question.txt, "Current selection is ", s)) - if (inputElement === undefined) { - console.error("MultiAnswer failed - probably not a single option was possible", configuration) - throw "MultiAnswer failed - probably not a single option was possible" - } - inputElement.GetValue().addCallbackAndRun(s => console.trace("Current selection is ", s)) const save = () => { console.log("OnSaveTriggered", inputElement) const selection = inputElement.GetValue().data; @@ -112,7 +95,7 @@ export default class TagRenderingQuestion extends Combine { .applyAction(new ChangeTagAction( tags.data.id, selection, tags.data )).then(_ => { - console.log("Tagchanges applied") + console.log("Tagchanges applied") }) if (options.afterSave) { options.afterSave(); @@ -151,27 +134,26 @@ export default class TagRenderingQuestion extends Combine { ) ).SetClass("block break-all") } - return new Combine([ + super([ question, inputElement, options.cancelButton, saveButton, - bottomTags] - ).SetClass("question") - + bottomTags]) + this.SetClass("question") } - private static GenerateInputElement(configuration: TagRenderingConfig, - applicableMappings: {if: TagsFilter, then: any, ifnot?: TagsFilter}[], + + private static GenerateInputElement(configuration: TagRenderingConfig, + applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[], applicableUnit: Unit, tagsSource: UIEventSource) - : InputElement { + : InputElement { // FreeForm input will be undefined if not present; will already contain a special input element if applicable const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); - const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 let inputEls: InputElement[]; @@ -189,7 +171,7 @@ export default class TagRenderingQuestion extends Combine { // The multianswer will do the ifnot configuration themself return [] } - + const negativeMappings = [] for (let i = 0; i < applicableMappings.length; i++) { @@ -203,7 +185,7 @@ export default class TagRenderingQuestion extends Combine { } - + if (applicableMappings.length < 8 || configuration.multiAnswer || hasImages || ifNotsPresent) { inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i))); inputEls = Utils.NoNull(inputEls); @@ -226,6 +208,9 @@ export default class TagRenderingQuestion extends Combine { if (inputEls.length == 0) { + if(ff === undefined){ + throw "Error: could not generate a question: freeform and all mappings are undefined" + } return ff; } @@ -358,7 +343,7 @@ export default class TagRenderingQuestion extends Combine { let tagging: TagsFilter = mapping.if; if (ifNot !== undefined) { - tagging = new And([mapping.if, ...ifNot]) + tagging = new And([mapping.if, ...ifNot]) } return new FixedInputElement( diff --git a/test.ts b/test.ts index 0c746b6eb..e6f2a9966 100644 --- a/test.ts +++ b/test.ts @@ -1,8 +1,26 @@ -import Wikidata from "./Logic/Web/Wikidata"; -import WikipediaBox from "./UI/WikipediaBox"; -import Locale from "./UI/i18n/Locale"; -import LanguagePicker from "./UI/LanguagePicker"; +import FeatureInfoBox from "./UI/Popup/FeatureInfoBox"; +import {UIEventSource} from "./Logic/UIEventSource"; +import AllKnownLayers from "./Customizations/AllKnownLayers"; +import State from "./State"; +import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; -new WikipediaBox("Q177").SetStyle("max-height: 25rem") - .AttachTo("maindiv") -LanguagePicker.CreateLanguagePicker(["en","nl","fr","de"]).AttachTo("extradiv") \ No newline at end of file +State.state = new State(AllKnownLayouts.allKnownLayouts.get("charging_stations")) +State.state.changes.pendingChanges.setData([]) +const geojson = { + type: "Feature", + geometry: { + type: "Point", + coordinates: [51.0, 4] + }, + properties: + { + id: "node/42", + amenity: "charging_station", + } +} +State.state.allElements.addOrGetElement(geojson) +const tags = State.state.allElements.getEventSourceById("node/42") +new FeatureInfoBox( + tags, + AllKnownLayers.sharedLayers.get("charging_station") +).AttachTo("maindiv") \ No newline at end of file From 27a43fdbe019ed82c4492f6924c9d778a15c54ec Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 21:53:31 +0200 Subject: [PATCH 100/110] Changes: expand try block; remove console.logs --- Logic/Osm/Changes.ts | 14 +++++++------- UI/Popup/TagRenderingQuestion.ts | 7 ------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index 570bf553b..866c78c7a 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -111,14 +111,14 @@ export class Changes { } private async flushChangesAsync(flushreason: string = undefined): Promise { - console.log("Beginning upload... " + flushreason ?? ""); - // At last, we build the changeset and upload const self = this; - const pending = self.pendingChanges.data; - const neededIds = Changes.GetNeededIds(pending) - const osmObjects = await Promise.all(neededIds.map(id => OsmObject.DownloadObjectAsync(id))); - console.log("Got the fresh objects!", osmObjects, "pending: ", pending) try { + console.log("Beginning upload... " + flushreason ?? ""); + // At last, we build the changeset and upload + const pending = self.pendingChanges.data; + const neededIds = Changes.GetNeededIds(pending) + const osmObjects = await Promise.all(neededIds.map(id => OsmObject.DownloadObjectAsync(id))); + console.log("Got the fresh objects!", osmObjects, "pending: ", pending) const changes: { newObjects: OsmObject[], modifiedObjects: OsmObject[] @@ -311,6 +311,6 @@ export class Changes { } public registerIdRewrites(mappings: Map): void { - + } } \ No newline at end of file diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 40a68609d..a4eb83b0f 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -66,9 +66,6 @@ export default class TagRenderingQuestion extends Combine { return applicableMappings })); - applicableMappingsSrc.addCallbackAndRun(appl => console.log("Currently applicable renderings are:", appl.map(m => m.then.txt).join(", "))) - - if (configuration === undefined) { throw "A question is needed for a question visualization" } @@ -84,12 +81,8 @@ export default class TagRenderingQuestion extends Combine { )) - // inputElement.GetValue().addCallbackAndRun(s => console.trace(configuration.question.txt, "Current selection is ", s)) - const save = () => { - console.log("OnSaveTriggered", inputElement) const selection = inputElement.GetValue().data; - console.log("Saving changes", selection) if (selection) { (State.state?.changes ?? new Changes()) .applyAction(new ChangeTagAction( From 65470cbac54009aed249d8b5af74a84d331787fd Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 4 Oct 2021 00:18:08 +0200 Subject: [PATCH 101/110] Fix bug where metatagging would not fully calculate in some cases --- Logic/FeatureSource/FeaturePipeline.ts | 7 ++++--- Logic/MetaTagging.ts | 10 +++++++++- Logic/SimpleMetaTagger.ts | 10 +++++----- UI/Base/MinimapImplementation.ts | 2 +- UI/BigComponents/UserBadge.ts | 2 +- css/index-tailwind-output.css | 18 ++++++++++++------ css/wikipedia.css | 2 +- index.ts | 3 +-- 8 files changed, 34 insertions(+), 20 deletions(-) diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 0dff97544..3aa0af356 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -353,20 +353,21 @@ export default class FeaturePipeline { private applyMetaTags(src: FeatureSourceForLayer) { const self = this - console.debug("Applying metatagging onto ", src.name) window.setTimeout( () => { + console.debug("Applying metatagging onto ", src.name) + const layerDef = src.layer.layerDef; MetaTagging.addMetatags( src.features.data, { memberships: this.relationTracker, getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox) }, - src.layer.layerDef, + layerDef, { includeDates: true, // We assume that the non-dated metatags are already set by the cache generator - includeNonDates: !src.layer.layerDef.source.isOsmCacheLayer + includeNonDates: layerDef.source.geojsonSource === undefined || !layerDef.source.isOsmCacheLayer } ) }, diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index 47d7f8a35..9f96e7730 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -61,7 +61,15 @@ export default class MetaTagging { // All keys are already defined, we probably already ran this one continue } - somethingChanged = somethingChanged || metatag.applyMetaTagsOnFeature(feature, freshness) + const newValueAdded = metatag.applyMetaTagsOnFeature(feature, freshness) + /* Note that the expression: + * `somethingChanged = newValueAdded || metatag.applyMetaTagsOnFeature(feature, freshness)` + * Is WRONG + * + * IF something changed is `true` due to an earlier run, it will short-circuit and _not_ evaluate the right hand of the OR, + * thus not running an update! + */ + somethingChanged = newValueAdded || somethingChanged } catch (e) { console.error("Could not calculate metatag for ", metatag.keys.join(","), ":", e, e.stack) } diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index eb1a6a49d..2845223f8 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -146,14 +146,14 @@ export default class SimpleMetaTagger { private static country = new SimpleMetaTagger( { keys: ["_country"], - doc: "The country code of the property (with latlon2country)" + doc: "The country code of the property (with latlon2country)", + includesDates: false }, - feature => { - + ((feature, _) => { let centerPoint: any = GeoOperations.centerpoint(feature); const lat = centerPoint.geometry.coordinates[1]; const lon = centerPoint.geometry.coordinates[0]; - + SimpleMetaTagger.coder?.GetCountryCodeFor(lon, lat, (countries: string[]) => { try { const oldCountry = feature.properties["_country"]; @@ -167,7 +167,7 @@ export default class SimpleMetaTagger { } }) return false; - } + }) ) private static isOpen = new SimpleMetaTagger( { diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts index 110235437..9ec90bd31 100644 --- a/UI/Base/MinimapImplementation.ts +++ b/UI/Base/MinimapImplementation.ts @@ -118,7 +118,7 @@ export default class MinimapImplementation extends BaseUIElement implements Mini self.InitMap(); self.leafletMap?.data?.invalidateSize() } catch (e) { - console.error("Could not construct a minimap:", e) + console.warn("Could not construct a minimap:", e) } }); diff --git a/UI/BigComponents/UserBadge.ts b/UI/BigComponents/UserBadge.ts index 3aef14044..f16d1c5f6 100644 --- a/UI/BigComponents/UserBadge.ts +++ b/UI/BigComponents/UserBadge.ts @@ -133,7 +133,7 @@ export default class UserBadge extends Toggle { ) - this.SetClass("shadow rounded-full h-min overflow-hidden block w-max") + this.SetClass("shadow rounded-full h-min overflow-hidden block w-full md:w-max") } diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index adb7b1ee9..054952756 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -1062,12 +1062,6 @@ video { width: min-content; } -.w-max { - width: -webkit-max-content; - width: -moz-max-content; - width: max-content; -} - .w-6 { width: 1.5rem; } @@ -1076,6 +1070,12 @@ video { width: 50%; } +.w-max { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + .min-w-min { min-width: -webkit-min-content; min-width: -moz-min-content; @@ -2339,6 +2339,12 @@ li::marker { width: auto; } + .md\:w-max { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + } + .md\:grid-flow-row { grid-auto-flow: row; } diff --git a/css/wikipedia.css b/css/wikipedia.css index 99d25c437..e636fb8dc 100644 --- a/css/wikipedia.css +++ b/css/wikipedia.css @@ -9,7 +9,7 @@ clear: right; } -.wikipedia-article svg, img { +.wikipedia-article svg, .wikipedia-article img { width: unset; height: unset; display: unset; diff --git a/index.ts b/index.ts index 1e98ae890..e5e58a6f0 100644 --- a/index.ts +++ b/index.ts @@ -19,8 +19,7 @@ import SimpleMetaTagger from "./Logic/SimpleMetaTagger"; MinimapImplementation.initialize() // Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts ValidatedTextField.bestLayerAt = (location, layerPref) => AvailableBaseLayers.SelectBestLayerAccordingTo(location, layerPref) - - SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); +SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); let defaultLayout = "" // --------------------- Special actions based on the parameters ----------------- From c4b77dd9d638eb082740d743c11c3ab530491ccc Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 4 Oct 2021 00:19:04 +0200 Subject: [PATCH 102/110] Version bump --- Models/Constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Models/Constants.ts b/Models/Constants.ts index 76769cdae..88348d1c4 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.1-rc2"; + public static vNumber = "0.10.1-rc3"; public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" From 214a2335de487e25e4d105aba4b7a4f4e0652bef Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 4 Oct 2021 14:24:21 +0200 Subject: [PATCH 103/110] Small style tweaks --- .../charging_station/charging_station.json | 6704 ++++++++--------- assets/layers/charging_station/csvToJson.ts | 4 +- 2 files changed, 3354 insertions(+), 3354 deletions(-) diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 03be186b6..6b9974e29 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -1,3379 +1,3379 @@ { - "id": "charging_station", - "name": { - "en": "Charging stations", - "it": "Stazioni di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjoner", - "ru": "Зарядные станции", - "zh_Hant": "充電站" - }, - "minzoom": 10, - "source": { - "osmTags": { - "or": [ - "amenity=charging_station", - "disused:amenity=charging_station", - "planned:amenity=charging_station", - "construction:amenity=charging_station" + "id": "charging_station", + "name": { + "en": "Charging stations", + "it": "Stazioni di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjoner", + "ru": "Зарядные станции", + "zh_Hant": "充電站" + }, + "minzoom": 10, + "source": { + "osmTags": { + "or": [ + "amenity=charging_station", + "disused:amenity=charging_station", + "planned:amenity=charging_station", + "construction:amenity=charging_station" + ] + } + }, + "title": { + "render": { + "en": "Charging station", + "it": "Stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "Ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + } + }, + "description": { + "en": "A charging station", + "it": "Una stazione di ricarica", + "ja": "充電ステーション", + "nb_NO": "En ladestasjon", + "ru": "Зарядная станция", + "zh_Hant": "充電站" + }, + "calculatedTags": [ + "motorcar=feat.properties.motorcar ?? feat.properties.car" + ], + "tagRenderings": [ + "images", + { + "id": "Type", + "question": { + "en": "Which vehicles are allowed to charge here?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "bicycle=yes", + "ifnot": "bicycle=no", + "then": { + "en": "bicycles can be charged here" + } + }, + { + "if": "motorcar=yes", + "extraTags": "car=", + "ifnot": { + "and": [ + "car=", + "motorcar=no" ] + }, + "then": { + "en": "Cars can be charged here" + } + }, + { + "if": "scooter=yes", + "ifnot": "scooter=no", + "then": { + "en": "Scooters can be charged here" + } + }, + { + "if": "hgv=yes", + "ifnot": "hgv=no", + "then": { + "en": "Heavy good vehicles (such as trucks) can be charged here" + } + }, + { + "if": "bus=yes", + "ifnot": "bus=no", + "then": { + "en": "Buses can be charged here" + } } + ] }, - "title": { - "render": { - "en": "Charging station", - "it": "Stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "Ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - } - }, - "description": { - "en": "A charging station", - "it": "Una stazione di ricarica", - "ja": "充電ステーション", - "nb_NO": "En ladestasjon", - "ru": "Зарядная станция", - "zh_Hant": "充電站" - }, - "calculatedTags": [ - "motorcar=feat.properties.motorcar ?? feat.properties.car" - ], - "tagRenderings": [ - "images", - { - "id": "Type", - "question": { - "en": "Which vehicles are allowed to charge here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "bicycle=yes", - "ifnot": "bicycle=no", - "then": { - "en": "bicycles can be charged here" - } - }, - { - "if": "motorcar=yes", - "extraTags": "car=", - "ifnot": { - "and": [ - "car=", - "motorcar=no" - ] - }, - "then": { - "en": "Cars can be charged here" - } - }, - { - "if": "scooter=yes", - "ifnot": "scooter=no", - "then": { - "en": "Scooters can be charged here" - } - }, - { - "if": "hgv=yes", - "ifnot": "hgv=no", - "then": { - "en": "Heavy good vehicles (such as trucks) can be charged here" - } - }, - { - "if": "bus=yes", - "ifnot": "bus=no", - "then": { - "en": "Buses can be charged here" - } - } - ] - }, - { - "id": "access", - "question": { - "en": "Who is allowed to use this charging station?" - }, - "render": { - "en": "Access is {access}" - }, - "freeform": { - "key": "access", - "addExtraTags": [ - "fixme=Freeform field used for access - doublecheck the value" - ] - }, - "mappings": [ - { - "if": "access=yes", - "then": "Anyone can use this charging station (payment might be needed)" - }, - { - "if": { - "or": [ - "access=permissive", - "access=public" - ] - }, - "then": "Anyone can use this charging station (payment might be needed)", - "hideInAnswer": true - }, - { - "if": "access=customers", - "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " - }, - { - "if": "access=private", - "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" - } - ] - }, - { - "id": "capacity", - "render": { - "en": "{capacity} vehicles can be charged here at the same time", - "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" - }, - "question": { - "en": "How much vehicles can be charged here at the same time?", - "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" - }, - "freeform": { - "key": "capacity", - "type": "pnat" - } - }, - { - "id": "Available_charging_stations (generated)", - "question": { - "en": "Which charging stations are available here?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "socket:schuko=1", - "ifnot": "socket:schuko=", - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": { - "or": [ - "_country!=be", - "_country!=fr", - "_country!=ma", - "_country!=tn", - "_country!=pl", - "_country!=cs", - "_country!=sk", - "_country!=mo" - ] - } - }, - { - "if": { - "and": [ - "socket:schuko~*", - "socket:schuko!=1" - ] - }, - "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:typee=1", - "ifnot": "socket:typee=", - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - } - }, - { - "if": { - "and": [ - "socket:typee~*", - "socket:typee!=1" - ] - }, - "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:chademo=1", - "ifnot": "socket:chademo=", - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:chademo~*", - "socket:chademo!=1" - ] - }, - "then": { - "en": "
Chademo
", - "nl": "
Chademo
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_cable=1", - "ifnot": "socket:type1_cable=", - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=1" - ] - }, - "then": { - "en": "
Type 1 with cable (J1772)
", - "nl": "
Type 1 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1=1", - "ifnot": "socket:type1=", - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1~*", - "socket:type1!=1" - ] - }, - "then": { - "en": "
Type 1 without cable (J1772)
", - "nl": "
Type 1 zonder kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type1_combo=1", - "ifnot": "socket:type1_combo=", - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=1" - ] - }, - "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger=1", - "ifnot": "socket:tesla_supercharger=", - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger
", - "nl": "
Tesla Supercharger
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2=1", - "ifnot": "socket:type2=", - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2~*", - "socket:type2!=1" - ] - }, - "then": { - "en": "
Type 2 (mennekes)
", - "nl": "
Type 2 (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_combo=1", - "ifnot": "socket:type2_combo=", - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=1" - ] - }, - "then": { - "en": "
Type 2 CCS (mennekes)
", - "nl": "
Type 2 CCS (mennekes)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:type2_cable=1", - "ifnot": "socket:type2_cable=", - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=1" - ] - }, - "then": { - "en": "
Type 2 with cable (mennekes)
", - "nl": "
Type 2 met kabel (J1772)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_supercharger_ccs=1", - "ifnot": "socket:tesla_supercharger_ccs=", - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country!=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla Supercharger (destination)
", - "nl": "
Tesla Supercharger (destination)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:tesla_destination=1", - "ifnot": "socket:tesla_destination=", - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": { - "or": [ - { - "and": [ - "car=no", - "motorcar=no", - "hgv=no", - "bus=no" - ] - }, - { - "and": [ - { - "or": [ - "bicycle=yes", - "scooter=yes" - ] - }, - "car!=yes", - "motorcar!=yes", - "hgv!=yes", - "bus!=yes" - ] - }, - { - "or": [ - "_country=us" - ] - } - ] - } - }, - { - "if": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=1" - ] - }, - "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" - }, - "hideInAnswer": true - }, - { - "if": "socket:USB-A=1", - "ifnot": "socket:USB-A=", - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - } - }, - { - "if": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=1" - ] - }, - "then": { - "en": "
USB to charge phones and small electronics
", - "nl": "
USB om GSMs en kleine electronica op te laden
" - }, - "hideInAnswer": true - } - ] - }, - { - "id": "plugs-0", - "question": { - "en": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", - "nl": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", - "nl": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" - }, - "freeform": { - "key": "socket:schuko", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "voltage-0", - "question": { - "en": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" - }, - "freeform": { - "key": "socket:schuko:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:voltage=230 V", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "current-0", - "question": { - "en": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" - }, - "freeform": { - "key": "socket:schuko:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:current=16 A", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "power-output-0", - "question": { - "en": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?" - }, - "render": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" - }, - "freeform": { - "key": "socket:schuko:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:schuko:output=3.6 kw", - "then": { - "en": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw", - "nl": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" - } - } - ], - "condition": { - "and": [ - "socket:schuko~*", - "socket:schuko!=0" - ] - } - }, - { - "id": "plugs-1", - "question": { - "en": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", - "nl": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here", - "nl": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" - }, - "freeform": { - "key": "socket:typee", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "voltage-1", - "question": { - "en": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) " - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" - }, - "freeform": { - "key": "socket:typee:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:voltage=230 V", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "current-1", - "question": { - "en": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" - }, - "freeform": { - "key": "socket:typee:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:current=16 A", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "power-output-1", - "question": { - "en": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?" - }, - "render": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" - }, - "freeform": { - "key": "socket:typee:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:typee:output=3 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" - } - }, - { - "if": "socket:socket:typee:output=22 kw", - "then": { - "en": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw", - "nl": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:typee~*", - "socket:typee!=0" - ] - } - }, - { - "id": "plugs-2", - "question": { - "en": "How much plugs of type Chademo are available here?", - "nl": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Chademo plugs of type Chademo available here", - "nl": "Hier zijn Chademo stekkers van het type Chademo" - }, - "freeform": { - "key": "socket:chademo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "voltage-2", - "question": { - "en": "What voltage do the plugs with Chademo offer?", - "nl": "Welke spanning levert de stekker van type Chademo " - }, - "render": { - "en": "Chademo outputs {socket:chademo:voltage} volt", - "nl": "Chademo heeft een spanning van {socket:chademo:voltage} volt" - }, - "freeform": { - "key": "socket:chademo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:voltage=500 V", - "then": { - "en": "Chademo outputs 500 volt", - "nl": "Chademo heeft een spanning van 500 volt" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "current-2", - "question": { - "en": "What current do the plugs with Chademo offer?", - "nl": "Welke stroom levert de stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:current}A", - "nl": "Chademo levert een stroom van maximaal {socket:chademo:current}A" - }, - "freeform": { - "key": "socket:chademo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:current=120 A", - "then": { - "en": "Chademo outputs at most 120 A", - "nl": "Chademo levert een stroom van maximaal 120 A" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "power-output-2", - "question": { - "en": "What power output does a single plug of type Chademo offer?", - "nl": "Welk vermogen levert een enkele stekker van type Chademo ?" - }, - "render": { - "en": "Chademo outputs at most {socket:chademo:output}", - "nl": "Chademo levert een vermogen van maximaal {socket:chademo:output}" - }, - "freeform": { - "key": "socket:chademo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:chademo:output=50 kw", - "then": { - "en": "Chademo outputs at most 50 kw", - "nl": "Chademo levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:chademo~*", - "socket:chademo!=0" - ] - } - }, - { - "id": "plugs-3", - "question": { - "en": "How much plugs of type Type 1 with cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here", - "nl": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type1_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "voltage-3", - "question": { - "en": "What voltage do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) " - }, - "render": { - "en": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type1_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:voltage=200 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 200 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1_cable:voltage=240 V", - "then": { - "en": "Type 1 with cable (J1772) outputs 240 volt", - "nl": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "current-3", - "question": { - "en": "What current do the plugs with Type 1 with cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" - }, - "freeform": { - "key": "socket:type1_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:current=32 A", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 32 A", - "nl": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "power-output-3", - "question": { - "en": "What power output does a single plug of type Type 1 with cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?" - }, - "render": { - "en": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" - }, - "freeform": { - "key": "socket:type1_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_cable:output=3.7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1_cable:output=7 kw", - "then": { - "en": "Type 1 with cable (J1772) outputs at most 7 kw", - "nl": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_cable~*", - "socket:type1_cable!=0" - ] - } - }, - { - "id": "plugs-4", - "question": { - "en": "How much plugs of type Type 1 without cable (J1772) are available here?", - "nl": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here", - "nl": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" - }, - "freeform": { - "key": "socket:type1", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "voltage-4", - "question": { - "en": "What voltage do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) " - }, - "render": { - "en": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" - }, - "freeform": { - "key": "socket:type1:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:voltage=200 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 200 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" - } - }, - { - "if": "socket:socket:type1:voltage=240 V", - "then": { - "en": "Type 1 without cable (J1772) outputs 240 volt", - "nl": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "current-4", - "question": { - "en": "What current do the plugs with Type 1 without cable (J1772) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" - }, - "freeform": { - "key": "socket:type1:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:current=32 A", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 32 A", - "nl": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "power-output-4", - "question": { - "en": "What power output does a single plug of type Type 1 without cable (J1772) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?" - }, - "render": { - "en": "Type 1 without cable (J1772) outputs at most {socket:type1:output}", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" - }, - "freeform": { - "key": "socket:type1:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1:output=3.7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 3.7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" - } - }, - { - "if": "socket:socket:type1:output=6.6 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 6.6 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" - } - }, - { - "if": "socket:socket:type1:output=7 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" - } - }, - { - "if": "socket:socket:type1:output=7.2 kw", - "then": { - "en": "Type 1 without cable (J1772) outputs at most 7.2 kw", - "nl": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1~*", - "socket:type1!=0" - ] - } - }, - { - "id": "plugs-5", - "question": { - "en": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", - "nl": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here", - "nl": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" - }, - "freeform": { - "key": "socket:type1_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "voltage-5", - "question": { - "en": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type1_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:voltage=400 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" - } - }, - { - "if": "socket:socket:type1_combo:voltage=1000 V", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "current-5", - "question": { - "en": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" - }, - "freeform": { - "key": "socket:type1_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:current=50 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" - } - }, - { - "if": "socket:socket:type1_combo:current=125 A", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "power-output-5", - "question": { - "en": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?" - }, - "render": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" - }, - "freeform": { - "key": "socket:type1_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type1_combo:output=50 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=62.5 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=150 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:type1_combo:output=350 kw", - "then": { - "en": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw", - "nl": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" - } - } - ], - "condition": { - "and": [ - "socket:type1_combo~*", - "socket:type1_combo!=0" - ] - } - }, - { - "id": "plugs-6", - "question": { - "en": "How much plugs of type Tesla Supercharger are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger plugs of type Tesla Supercharger available here", - "nl": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" - }, - "freeform": { - "key": "socket:tesla_supercharger", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "voltage-6", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger " - }, - "render": { - "en": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt", - "nl": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:voltage=480 V", - "then": { - "en": "Tesla Supercharger outputs 480 volt", - "nl": "Tesla Supercharger heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "current-6", - "question": { - "en": "What current do the plugs with Tesla Supercharger offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A", - "nl": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:current=125 A", - "then": { - "en": "Tesla Supercharger outputs at most 125 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger:current=350 A", - "then": { - "en": "Tesla Supercharger outputs at most 350 A", - "nl": "Tesla Supercharger levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "power-output-6", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?" - }, - "render": { - "en": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}", - "nl": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger:output=120 kw", - "then": { - "en": "Tesla Supercharger outputs at most 120 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=150 kw", - "then": { - "en": "Tesla Supercharger outputs at most 150 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_supercharger:output=250 kw", - "then": { - "en": "Tesla Supercharger outputs at most 250 kw", - "nl": "Tesla Supercharger levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger~*", - "socket:tesla_supercharger!=0" - ] - } - }, - { - "id": "plugs-7", - "question": { - "en": "How much plugs of type Type 2 (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here", - "nl": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" - }, - "freeform": { - "key": "socket:type2", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "voltage-7", - "question": { - "en": "What voltage do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 (mennekes) " - }, - "render": { - "en": "Type 2 (mennekes) outputs {socket:type2:voltage} volt", - "nl": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" - }, - "freeform": { - "key": "socket:type2:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:voltage=230 V", - "then": { - "en": "Type 2 (mennekes) outputs 230 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2:voltage=400 V", - "then": { - "en": "Type 2 (mennekes) outputs 400 volt", - "nl": "Type 2 (mennekes) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "current-7", - "question": { - "en": "What current do the plugs with Type 2 (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:current}A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" - }, - "freeform": { - "key": "socket:type2:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:current=16 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 16 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2:current=32 A", - "then": { - "en": "Type 2 (mennekes) outputs at most 32 A", - "nl": "Type 2 (mennekes) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "power-output-7", - "question": { - "en": "What power output does a single plug of type Type 2 (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?" - }, - "render": { - "en": "Type 2 (mennekes) outputs at most {socket:type2:output}", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" - }, - "freeform": { - "key": "socket:type2:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2:output=11 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 11 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2:output=22 kw", - "then": { - "en": "Type 2 (mennekes) outputs at most 22 kw", - "nl": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2~*", - "socket:type2!=0" - ] - } - }, - { - "id": "plugs-8", - "question": { - "en": "How much plugs of type Type 2 CCS (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here", - "nl": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" - }, - "freeform": { - "key": "socket:type2_combo", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "voltage-8", - "question": { - "en": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) " - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" - }, - "freeform": { - "key": "socket:type2_combo:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:voltage=500 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 500 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:type2_combo:voltage=920 V", - "then": { - "en": "Type 2 CCS (mennekes) outputs 920 volt", - "nl": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "current-8", - "question": { - "en": "What current do the plugs with Type 2 CCS (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" - }, - "freeform": { - "key": "socket:type2_combo:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:current=125 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 125 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:type2_combo:current=350 A", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 350 A", - "nl": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "power-output-8", - "question": { - "en": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?" - }, - "render": { - "en": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" - }, - "freeform": { - "key": "socket:type2_combo:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_combo:output=50 kw", - "then": { - "en": "Type 2 CCS (mennekes) outputs at most 50 kw", - "nl": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_combo~*", - "socket:type2_combo!=0" - ] - } - }, - { - "id": "plugs-9", - "question": { - "en": "How much plugs of type Type 2 with cable (mennekes) are available here?", - "nl": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here", - "nl": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" - }, - "freeform": { - "key": "socket:type2_cable", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "voltage-9", - "question": { - "en": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) " - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" - }, - "freeform": { - "key": "socket:type2_cable:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:voltage=230 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 230 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:type2_cable:voltage=400 V", - "then": { - "en": "Type 2 with cable (mennekes) outputs 400 volt", - "nl": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "current-9", - "question": { - "en": "What current do the plugs with Type 2 with cable (mennekes) offer?", - "nl": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" - }, - "freeform": { - "key": "socket:type2_cable:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:current=16 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 16 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:type2_cable:current=32 A", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 32 A", - "nl": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "power-output-9", - "question": { - "en": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?" - }, - "render": { - "en": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" - }, - "freeform": { - "key": "socket:type2_cable:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:type2_cable:output=11 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 11 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:type2_cable:output=22 kw", - "then": { - "en": "Type 2 with cable (mennekes) outputs at most 22 kw", - "nl": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:type2_cable~*", - "socket:type2_cable!=0" - ] - } - }, - { - "id": "plugs-10", - "question": { - "en": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here", - "nl": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "voltage-10", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "current-10", - "question": { - "en": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:current=125 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_supercharger_ccs:current=350 A", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "power-output-10", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?" - }, - "render": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" - }, - "freeform": { - "key": "socket:tesla_supercharger_ccs:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", - "then": { - "en": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw", - "nl": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_supercharger_ccs~*", - "socket:tesla_supercharger_ccs!=0" - ] - } - }, - { - "id": "plugs-11", - "question": { - "en": "How much plugs of type Tesla Supercharger (destination) are available here?", - "nl": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here", - "nl": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-11", - "question": { - "en": "What voltage do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke spanning levert de stekker van type Tesla Supercharger (destination) " - }, - "render": { - "en": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=480 V", - "then": { - "en": "Tesla Supercharger (destination) outputs 480 volt", - "nl": "Tesla Supercharger (destination) heeft een spanning van 480 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-11", - "question": { - "en": "What current do the plugs with Tesla Supercharger (destination) offer?", - "nl": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=125 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 125 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=350 A", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 350 A", - "nl": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-11", - "question": { - "en": "What power output does a single plug of type Tesla Supercharger (destination) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?" - }, - "render": { - "en": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=120 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 120 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=150 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 150 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=250 kw", - "then": { - "en": "Tesla Supercharger (destination) outputs at most 250 kw", - "nl": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-12", - "question": { - "en": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", - "nl": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?" - }, - "render": { - "en": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", - "nl": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" - }, - "freeform": { - "key": "socket:tesla_destination", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "voltage-12", - "question": { - "en": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" - }, - "freeform": { - "key": "socket:tesla_destination:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:voltage=230 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" - } - }, - { - "if": "socket:socket:tesla_destination:voltage=400 V", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "current-12", - "question": { - "en": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" - }, - "freeform": { - "key": "socket:tesla_destination:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:current=16 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" - } - }, - { - "if": "socket:socket:tesla_destination:current=32 A", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "power-output-12", - "question": { - "en": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "nl": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?" - }, - "render": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" - }, - "freeform": { - "key": "socket:tesla_destination:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:tesla_destination:output=11 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" - } - }, - { - "if": "socket:socket:tesla_destination:output=22 kw", - "then": { - "en": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw", - "nl": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" - } - } - ], - "condition": { - "and": [ - "socket:tesla_destination~*", - "socket:tesla_destination!=0" - ] - } - }, - { - "id": "plugs-13", - "question": { - "en": "How much plugs of type USB to charge phones and small electronics are available here?", - "nl": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?" - }, - "render": { - "en": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here", - "nl": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" - }, - "freeform": { - "key": "socket:USB-A", - "type": "pnat" - }, - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "voltage-13", - "question": { - "en": "What voltage do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden " - }, - "render": { - "en": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" - }, - "freeform": { - "key": "socket:USB-A:voltage", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:voltage=5 V", - "then": { - "en": "USB to charge phones and small electronics outputs 5 volt", - "nl": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "current-13", - "question": { - "en": "What current do the plugs with USB to charge phones and small electronics offer?", - "nl": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" - }, - "freeform": { - "key": "socket:USB-A:current", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:current=1 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 1 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" - } - }, - { - "if": "socket:socket:USB-A:current=2 A", - "then": { - "en": "USB to charge phones and small electronics outputs at most 2 A", - "nl": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "power-output-13", - "question": { - "en": "What power output does a single plug of type USB to charge phones and small electronics offer?", - "nl": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?" - }, - "render": { - "en": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" - }, - "freeform": { - "key": "socket:USB-A:output", - "type": "pfloat" - }, - "mappings": [ - { - "if": "socket:socket:USB-A:output=5w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 5w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" - } - }, - { - "if": "socket:socket:USB-A:output=10w", - "then": { - "en": "USB to charge phones and small electronics outputs at most 10w", - "nl": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" - } - } - ], - "condition": { - "and": [ - "socket:USB-A~*", - "socket:USB-A!=0" - ] - } - }, - { - "id": "Authentication", - "question": { - "en": "What kind of authentication is available at the charging station?", - "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", - "ja": "この充電ステーションはいつオープンしますか?", - "nb_NO": "Når åpnet denne ladestasjonen?", - "ru": "В какое время работает эта зарядная станция?", - "zh_Hant": "何時是充電站開放使用的時間?" - }, - "multiAnswer": true, - "mappings": [ - { - "if": "authentication:membership_card=yes", - "ifnot": "authentication:membership_card=no", - "then": { - "en": "Authentication by a membership card" - } - }, - { - "if": "authentication:app=yes", - "ifnot": "authentication:app=no", - "then": { - "en": "Authentication by an app" - } - }, - { - "if": "authentication:phone_call=yes", - "ifnot": "authentication:phone_call=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:short_message=yes", - "ifnot": "authentication:short_message=no", - "then": { - "en": "Authentication via phone call is available" - } - }, - { - "if": "authentication:nfc=yes", - "ifnot": "authentication:nfc=no", - "then": { - "en": "Authentication via NFC is available" - } - }, - { - "if": "authentication:money_card=yes", - "ifnot": "authentication:money_card=no", - "then": { - "en": "Authentication via Money Card is available" - } - }, - { - "if": "authentication:debit_card=yes", - "ifnot": "authentication:debit_card=no", - "then": { - "en": "Authentication via debit card is available" - } - }, - { - "if": "authentication:none=yes", - "ifnot": "authentication:none=no", - "then": { - "en": "No authentication is needed" - } - } - ] - }, - { - "id": "Auth phone", - "render": { - "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", - "it": "{network}", - "ja": "{network}", - "nb_NO": "{network}", - "ru": "{network}", - "zh_Hant": "{network}" - }, - "question": { - "en": "What's the phone number for authentication call or SMS?", - "it": "A quale rete appartiene questa stazione di ricarica?", - "ja": "この充電ステーションの運営チェーンはどこですか?", - "ru": "К какой сети относится эта станция?", - "zh_Hant": "充電站所屬的網路是?" - }, - "freeform": { - "key": "authentication:phone_call:number", - "type": "phone" - }, - "condition": { - "or": [ - "authentication:phone_call=yes", - "authentication:short_message=yes" - ] - }, - "it": { - "0": { - "then": "Non appartiene a una rete" - } - }, - "ja": { - "0": { - "then": "大規模な運営チェーンの一部ではない" - } - }, - "ru": { - "0": { - "then": "Не является частью более крупной сети" - } - }, - "zh_Hant": { - "0": { - "then": "不屬於大型網路" - } - } - }, - { - "id": "OH", - "render": "{opening_hours_table(opening_hours)}", - "freeform": { - "key": "opening_hours", - "type": "opening_hours" - }, - "question": { - "en": "When is this charging station opened?" - }, - "mappings": [ - { - "if": "opening_hours=24/7", - "then": { - "en": "24/7 opened (including holidays)" - } - } - ] - }, - { - "id": "fee/charge", - "question": { - "en": "How much does one have to pay to use this charging station?", - "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" - }, - "freeform": { - "key": "charge", - "addExtraTags": [ - "fee=yes" - ] - }, - "render": { - "en": "Using this charging station costs {charge}", - "nl": "Dit oplaadpunt gebruiken kost {charge}" - }, - "mappings": [ - { - "if": { - "and": [ - "fee=no", - "charge=" - ] - }, - "then": { - "nl": "Gratis te gebruiken", - "en": "Free to use" - } - } - ] - }, - { - "id": "payment-options", - "builtin": "payment-options", - "override": { - "condition": { - "or": [ - "fee=yes", - "charge~*" - ] - }, - "mappings+": [ - { - "if": "payment:app=yes", - "ifnot": "payment:app=no", - "then": { - "en": "Payment is done using a dedicated app", - "nl": "Betalen via een app van het netwerk" - } - }, - { - "if": "payment:membership_card=yes", - "ifnot": "payment:membership_card=no", - "then": { - "en": "Payment is done using a membership card", - "nl": "Betalen via een lidkaart van het netwerk" - } - } - ] - } - }, - { - "id": "maxstay", - "question": { - "en": "What is the maximum amount of time one is allowed to stay here?", - "nl": "Hoelang mag een voertuig hier blijven staan?" - }, - "freeform": { - "key": "maxstay" - }, - "render": { - "en": "One can stay at most {canonical(maxstay)}", - "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" - }, - "mappings": [ - { - "if": "maxstay=unlimited", - "then": { - "en": "No timelimit on leaving your vehicle here", - "nl": "Geen maximum parkeertijd" - } - } - ] - }, - { - "id": "Network", - "render": { - "en": "Part of the network {network}" - }, - "question": { - "en": "Is this charging station part of a network?" - }, - "freeform": { - "key": "network" - }, - "mappings": [ - { - "if": "no:network=yes", - "then": { - "en": "Not part of a bigger network" - } - }, - { - "if": "network=none", - "then": { - "en": "Not part of a bigger network" - }, - "hideInAnswer": true - }, - { - "if": "network=AeroVironment", - "then": "AeroVironment" - }, - { - "if": "network=Blink", - "then": "Blink" - }, - { - "if": "network=eVgo", - "then": "eVgo" - } - ] - }, - { - "id": "Operator", - "question": { - "en": "Who is the operator of this charging station?" - }, - "render": { - "en": "This charging station is operated by {operator}" - }, - "freeform": { - "key": "operator" - }, - "mappings": [ - { - "if": { - "and": [ - "network:={operator}" - ] - }, - "then": { - "en": "Actually, {operator} is the network" - }, - "addExtraTags": [ - "operator=" - ], - "hideInAnswer": "operator=" - } - ] - }, - { - "id": "phone", - "question": { - "en": "What number can one call if there is a problem with this charging station?" - }, - "render": { - "en": "In case of problems, call {phone}" - }, - "freeform": { - "key": "phone", - "type": "phone" - } - }, - { - "id": "email", - "question": { - "en": "What is the email address of the operator?" - }, - "render": { - "en": "In case of problems, send an email to {email}" - }, - "freeform": { - "key": "email", - "type": "email" - } - }, - { - "id": "website", - "question": { - "en": "What is the website of the operator?" - }, - "render": { - "en": "More info on {website}" - }, - "freeform": { - "key": "website", - "type": "url" - } - }, - "level", - { - "id": "ref", - "question": { - "en": "What is the reference number of this charging station?" - }, - "render": { - "en": "Reference number is {ref}" - }, - "freeform": { - "key": "ref" - } - }, - { - "id": "Operational status", - "question": { - "en": "Is this charging point in use?", - "nl": "Is dit oplaadpunt operationeel?" - }, - "mappings": [ - { - "if": "operational_status=broken", - "then": { - "en": "This charging station is broken", - "nl": "Dit oplaadpunt is kapot" - } - }, - { - "if": { - "and": [ - "planned:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is planned here", - "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" - } - }, - { - "if": { - "and": [ - "construction:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "A charging station is constructed here", - "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" - } - }, - { - "if": { - "and": [ - "disused:amenity=charging_station", - "amenity=" - ] - }, - "then": { - "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", - "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" - } - }, - { - "if": { - "and": [ - "amenity=charging_station", - "operational_status=" - ] - }, - "then": { - "en": "This charging station works", - "nl": "Dit oplaadpunt werkt" - } - } - ] - }, - { - "id": "Parking:fee", - "question": { - "en": "Does one have to pay a parking fee while charging?" - }, - "mappings": [ - { - "if": "parking:fee=no", - "then": { - "en": "No additional parking cost while charging" - } - }, - { - "if": "parking:fee=yes", - "then": { - "en": "An additional parking fee should be paid while charging" - } - } - ] - } - ], - "icon": { - "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", - "mappings": [ - { - "if": "bicycle=yes", - "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" - }, - { - "if": { - "or": [ - "car=yes", - "motorcar=yes" - ] - }, - "then": "pin:#fff;./assets/themes/charging_stations/car.svg" - } + { + "id": "access", + "question": { + "en": "Who is allowed to use this charging station?" + }, + "render": { + "en": "Access is {access}" + }, + "freeform": { + "key": "access", + "addExtraTags": [ + "fixme=Freeform field used for access - doublecheck the value" ] + }, + "mappings": [ + { + "if": "access=yes", + "then": "Anyone can use this charging station (payment might be needed)" + }, + { + "if": { + "or": [ + "access=permissive", + "access=public" + ] + }, + "then": "Anyone can use this charging station (payment might be needed)", + "hideInAnswer": true + }, + { + "if": "access=customers", + "then": "Only customers of the place this station belongs to can use this charging station
E.g. a charging station operated by hotel which is only usable by their guests " + }, + { + "if": "access=private", + "then": "Not accessible to the general public (e.g. only accessible to the owners, employees, ...)" + } + ] }, - "iconOverlays": [ + { + "id": "capacity", + "render": { + "en": "{capacity} vehicles can be charged here at the same time", + "nl": "{capacity} voertuigen kunnen hier op hetzelfde moment opgeladen worden" + }, + "question": { + "en": "How much vehicles can be charged here at the same time?", + "nl": "Hoeveel voertuigen kunnen hier opgeladen worden?" + }, + "freeform": { + "key": "capacity", + "type": "pnat" + } + }, + { + "id": "Available_charging_stations (generated)", + "question": { + "en": "Which charging stations are available here?" + }, + "multiAnswer": true, + "mappings": [ { - "if": { - "or": [ - "disused:amenity=charging_station", - "operational_status=broken" - ] - }, - "then": "cross_bottom_right:#c22;" + "if": "socket:schuko=1", + "ifnot": "socket:schuko=", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": { + "or": [ + "_country!=be", + "_country!=fr", + "_country!=ma", + "_country!=tn", + "_country!=pl", + "_country!=cs", + "_country!=sk", + "_country!=mo" + ] + } }, { - "if": { - "or": [ - "proposed:amenity=charging_station", - "planned:amenity=charging_station" - ] - }, - "then": "./assets/layers/charging_station/under_construction.svg", - "badge": true + "if": { + "and": [ + "socket:schuko~*", + "socket:schuko!=1" + ] + }, + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "hideInAnswer": true }, { - "if": { + "if": "socket:typee=1", + "ifnot": "socket:typee=", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + } + }, + { + "if": { + "and": [ + "socket:typee~*", + "socket:typee!=1" + ] + }, + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:chademo=1", + "ifnot": "socket:chademo=", + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": { + "or": [ + { "and": [ - "bicycle=yes", - { - "or": [ - "motorcar=yes", - "car=yes" - ] - } + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" ] - }, - "then": "circle:#fff;./assets/themes/charging_stations/car.svg", - "badge": true - } - ], - "width": { - "render": "8" - }, - "iconSize": { - "render": "50,50,bottom" - }, - "color": { - "render": "#00f" - }, - "presets": [ + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, { - "tags": [ - "amenity=charging_station" - ], - "title": { - "en": "Charging station" - }, - "preciseInput": { - "preferredBackground": "map" + "if": { + "and": [ + "socket:chademo~*", + "socket:chademo!=1" + ] + }, + "then": { + "en": "
Chademo
", + "nl": "
Chademo
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_cable=1", + "ifnot": "socket:type1_cable=", + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=1" + ] + }, + "then": { + "en": "
Type 1 with cable (J1772)
", + "nl": "
Type 1 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1=1", + "ifnot": "socket:type1=", + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1~*", + "socket:type1!=1" + ] + }, + "then": { + "en": "
Type 1 without cable (J1772)
", + "nl": "
Type 1 zonder kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type1_combo=1", + "ifnot": "socket:type1_combo=", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=1" + ] + }, + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger=1", + "ifnot": "socket:tesla_supercharger=", + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger
", + "nl": "
Tesla Supercharger
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2=1", + "ifnot": "socket:type2=", + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2~*", + "socket:type2!=1" + ] + }, + "then": { + "en": "
Type 2 (mennekes)
", + "nl": "
Type 2 (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_combo=1", + "ifnot": "socket:type2_combo=", + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=1" + ] + }, + "then": { + "en": "
Type 2 CCS (mennekes)
", + "nl": "
Type 2 CCS (mennekes)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:type2_cable=1", + "ifnot": "socket:type2_cable=", + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=1" + ] + }, + "then": { + "en": "
Type 2 with cable (mennekes)
", + "nl": "
Type 2 met kabel (J1772)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_supercharger_ccs=1", + "ifnot": "socket:tesla_supercharger_ccs=", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country!=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla Supercharger (destination)
", + "nl": "
Tesla Supercharger (destination)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:tesla_destination=1", + "ifnot": "socket:tesla_destination=", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "car=no", + "motorcar=no", + "hgv=no", + "bus=no" + ] + }, + { + "and": [ + { + "or": [ + "bicycle=yes", + "scooter=yes" + ] + }, + "car!=yes", + "motorcar!=yes", + "hgv!=yes", + "bus!=yes" + ] + }, + { + "or": [ + "_country=us" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=1" + ] + }, + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "hideInAnswer": true + }, + { + "if": "socket:USB-A=1", + "ifnot": "socket:USB-A=", + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + } + }, + { + "if": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=1" + ] + }, + "then": { + "en": "
USB to charge phones and small electronics
", + "nl": "
USB om GSMs en kleine electronica op te laden
" + }, + "hideInAnswer": true + } + ] + }, + { + "id": "plugs-0", + "question": { + "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Schuko wall plug without ground pin (CEE7/4 type F)
plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", + "nl": "Hier zijn
Schuko stekker zonder aardingspin (CEE7/4 type F)
stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + }, + "freeform": { + "key": "socket:schuko", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "voltage-0", + "question": { + "en": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" + }, + "freeform": { + "key": "socket:schuko:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:voltage=230 V", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "current-0", + "question": { + "en": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" + }, + "freeform": { + "key": "socket:schuko:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:current=16 A", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "power-output-0", + "question": { + "en": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + }, + "render": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" + }, + "freeform": { + "key": "socket:schuko:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:schuko:output=3.6 kw", + "then": { + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" + } + } + ], + "condition": { + "and": [ + "socket:schuko~*", + "socket:schuko!=0" + ] + } + }, + { + "id": "plugs-1", + "question": { + "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
European wall plug with ground pin (CEE7/4 type E)
plugs of type European wall plug with ground pin (CEE7/4 type E) available here", + "nl": "Hier zijn
Europese stekker met aardingspin (CEE7/4 type E)
stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + }, + "freeform": { + "key": "socket:typee", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "voltage-1", + "question": { + "en": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" + }, + "freeform": { + "key": "socket:typee:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:voltage=230 V", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "current-1", + "question": { + "en": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" + }, + "freeform": { + "key": "socket:typee:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:current=16 A", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "power-output-1", + "question": { + "en": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + }, + "render": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" + }, + "freeform": { + "key": "socket:typee:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:typee:output=3 kw", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" + } + }, + { + "if": "socket:socket:typee:output=22 kw", + "then": { + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:typee~*", + "socket:typee!=0" + ] + } + }, + { + "id": "plugs-2", + "question": { + "en": "How much plugs of type
Chademo
are available here?", + "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Chademo
plugs of type Chademo available here", + "nl": "Hier zijn
Chademo
stekkers van het type Chademo" + }, + "freeform": { + "key": "socket:chademo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "voltage-2", + "question": { + "en": "What voltage do the plugs with
Chademo
offer?", + "nl": "Welke spanning levert de stekker van type
Chademo
" + }, + "render": { + "en": "
Chademo
outputs {socket:chademo:voltage} volt", + "nl": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" + }, + "freeform": { + "key": "socket:chademo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:voltage=500 V", + "then": { + "en": "
Chademo
outputs 500 volt", + "nl": "
Chademo
heeft een spanning van 500 volt" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "current-2", + "question": { + "en": "What current do the plugs with
Chademo
offer?", + "nl": "Welke stroom levert de stekker van type
Chademo
?" + }, + "render": { + "en": "
Chademo
outputs at most {socket:chademo:current}A", + "nl": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" + }, + "freeform": { + "key": "socket:chademo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:current=120 A", + "then": { + "en": "
Chademo
outputs at most 120 A", + "nl": "
Chademo
levert een stroom van maximaal 120 A" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "power-output-2", + "question": { + "en": "What power output does a single plug of type
Chademo
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Chademo
?" + }, + "render": { + "en": "
Chademo
outputs at most {socket:chademo:output}", + "nl": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" + }, + "freeform": { + "key": "socket:chademo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:chademo:output=50 kw", + "then": { + "en": "
Chademo
outputs at most 50 kw", + "nl": "
Chademo
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:chademo~*", + "socket:chademo!=0" + ] + } + }, + { + "id": "plugs-3", + "question": { + "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Type 1 with cable (J1772)
plugs of type Type 1 with cable (J1772) available here", + "nl": "Hier zijn
Type 1 met kabel (J1772)
stekkers van het type Type 1 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type1_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "voltage-3", + "question": { + "en": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type1_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:voltage=200 V", + "then": { + "en": "
Type 1 with cable (J1772)
outputs 200 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1_cable:voltage=240 V", + "then": { + "en": "
Type 1 with cable (J1772)
outputs 240 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "current-3", + "question": { + "en": "What current do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" + }, + "freeform": { + "key": "socket:type1_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:current=32 A", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "power-output-3", + "question": { + "en": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" + }, + "freeform": { + "key": "socket:type1_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_cable:output=3.7 kw", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1_cable:output=7 kw", + "then": { + "en": "
Type 1 with cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_cable~*", + "socket:type1_cable!=0" + ] + } + }, + { + "id": "plugs-4", + "question": { + "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Type 1 without cable (J1772)
plugs of type Type 1 without cable (J1772) available here", + "nl": "Hier zijn
Type 1 zonder kabel (J1772)
stekkers van het type Type 1 zonder kabel (J1772)" + }, + "freeform": { + "key": "socket:type1", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "voltage-4", + "question": { + "en": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" + }, + "freeform": { + "key": "socket:type1:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:voltage=200 V", + "then": { + "en": "
Type 1 without cable (J1772)
outputs 200 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" + } + }, + { + "if": "socket:socket:type1:voltage=240 V", + "then": { + "en": "
Type 1 without cable (J1772)
outputs 240 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "current-4", + "question": { + "en": "What current do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" + }, + "freeform": { + "key": "socket:type1:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:current=32 A", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "power-output-4", + "question": { + "en": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?" + }, + "render": { + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" + }, + "freeform": { + "key": "socket:type1:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1:output=3.7 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + } + }, + { + "if": "socket:socket:type1:output=6.6 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 6.6 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" + } + }, + { + "if": "socket:socket:type1:output=7 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" + } + }, + { + "if": "socket:socket:type1:output=7.2 kw", + "then": { + "en": "
Type 1 without cable (J1772)
outputs at most 7.2 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1~*", + "socket:type1!=0" + ] + } + }, + { + "id": "plugs-5", + "question": { + "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Type 1 CCS (aka Type 1 Combo)
plugs of type Type 1 CCS (aka Type 1 Combo) available here", + "nl": "Hier zijn
Type 1 CCS (ook gekend als Type 1 Combo)
stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" + }, + "freeform": { + "key": "socket:type1_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "voltage-5", + "question": { + "en": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type1_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:voltage=400 V", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" + } + }, + { + "if": "socket:socket:type1_combo:voltage=1000 V", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "current-5", + "question": { + "en": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" + }, + "freeform": { + "key": "socket:type1_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:current=50 A", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" + } + }, + { + "if": "socket:socket:type1_combo:current=125 A", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "power-output-5", + "question": { + "en": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + }, + "render": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" + }, + "freeform": { + "key": "socket:type1_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type1_combo:output=50 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=62.5 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=150 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:type1_combo:output=350 kw", + "then": { + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" + } + } + ], + "condition": { + "and": [ + "socket:type1_combo~*", + "socket:type1_combo!=0" + ] + } + }, + { + "id": "plugs-6", + "question": { + "en": "How much plugs of type
Tesla Supercharger
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Tesla Supercharger
plugs of type Tesla Supercharger available here", + "nl": "Hier zijn
Tesla Supercharger
stekkers van het type Tesla Supercharger" + }, + "freeform": { + "key": "socket:tesla_supercharger", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "voltage-6", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger
" + }, + "render": { + "en": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt", + "nl": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:voltage=480 V", + "then": { + "en": "
Tesla Supercharger
outputs 480 volt", + "nl": "
Tesla Supercharger
heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "current-6", + "question": { + "en": "What current do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger
?" + }, + "render": { + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:current=125 A", + "then": { + "en": "
Tesla Supercharger
outputs at most 125 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger:current=350 A", + "then": { + "en": "
Tesla Supercharger
outputs at most 350 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "power-output-6", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?" + }, + "render": { + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger:output=120 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 120 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=150 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 150 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_supercharger:output=250 kw", + "then": { + "en": "
Tesla Supercharger
outputs at most 250 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger~*", + "socket:tesla_supercharger!=0" + ] + } + }, + { + "id": "plugs-7", + "question": { + "en": "How much plugs of type
Type 2 (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Type 2 (mennekes)
plugs of type Type 2 (mennekes) available here", + "nl": "Hier zijn
Type 2 (mennekes)
stekkers van het type Type 2 (mennekes)" + }, + "freeform": { + "key": "socket:type2", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "voltage-7", + "question": { + "en": "What voltage do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 (mennekes)
" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" + }, + "freeform": { + "key": "socket:type2:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:voltage=230 V", + "then": { + "en": "
Type 2 (mennekes)
outputs 230 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2:voltage=400 V", + "then": { + "en": "
Type 2 (mennekes)
outputs 400 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "current-7", + "question": { + "en": "What current do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" + }, + "freeform": { + "key": "socket:type2:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:current=16 A", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 16 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2:current=32 A", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 32 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "power-output-7", + "question": { + "en": "What power output does a single plug of type
Type 2 (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?" + }, + "render": { + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:output}", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" + }, + "freeform": { + "key": "socket:type2:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2:output=11 kw", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2:output=22 kw", + "then": { + "en": "
Type 2 (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2~*", + "socket:type2!=0" + ] + } + }, + { + "id": "plugs-8", + "question": { + "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Type 2 CCS (mennekes)
plugs of type Type 2 CCS (mennekes) available here", + "nl": "Hier zijn
Type 2 CCS (mennekes)
stekkers van het type Type 2 CCS (mennekes)" + }, + "freeform": { + "key": "socket:type2_combo", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "voltage-8", + "question": { + "en": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" + }, + "freeform": { + "key": "socket:type2_combo:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:voltage=500 V", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs 500 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:type2_combo:voltage=920 V", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs 920 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "current-8", + "question": { + "en": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" + }, + "freeform": { + "key": "socket:type2_combo:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:current=125 A", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 125 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:type2_combo:current=350 A", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 350 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "power-output-8", + "question": { + "en": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?" + }, + "render": { + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" + }, + "freeform": { + "key": "socket:type2_combo:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_combo:output=50 kw", + "then": { + "en": "
Type 2 CCS (mennekes)
outputs at most 50 kw", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_combo~*", + "socket:type2_combo!=0" + ] + } + }, + { + "id": "plugs-9", + "question": { + "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Type 2 with cable (mennekes)
plugs of type Type 2 with cable (mennekes) available here", + "nl": "Hier zijn
Type 2 met kabel (J1772)
stekkers van het type Type 2 met kabel (J1772)" + }, + "freeform": { + "key": "socket:type2_cable", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "voltage-9", + "question": { + "en": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" + }, + "freeform": { + "key": "socket:type2_cable:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:voltage=230 V", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs 230 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:type2_cable:voltage=400 V", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs 400 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "current-9", + "question": { + "en": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" + }, + "freeform": { + "key": "socket:type2_cable:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:current=16 A", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 16 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:type2_cable:current=32 A", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 32 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "power-output-9", + "question": { + "en": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?" + }, + "render": { + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" + }, + "freeform": { + "key": "socket:type2_cable:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:type2_cable:output=11 kw", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:type2_cable:output=22 kw", + "then": { + "en": "
Type 2 with cable (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:type2_cable~*", + "socket:type2_cable!=0" + ] + } + }, + { + "id": "plugs-10", + "question": { + "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Tesla Supercharger CCS (a branded type2_css)
plugs of type Tesla Supercharger CCS (a branded type2_css) available here", + "nl": "Hier zijn
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "voltage-10", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "current-10", + "question": { + "en": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:current=125 A", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_supercharger_ccs:current=350 A", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "power-output-10", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + }, + "render": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + }, + "freeform": { + "key": "socket:tesla_supercharger_ccs:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", + "then": { + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_supercharger_ccs~*", + "socket:tesla_supercharger_ccs!=0" + ] + } + }, + { + "id": "plugs-11", + "question": { + "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Tesla Supercharger (destination)
plugs of type Tesla Supercharger (destination) available here", + "nl": "Hier zijn
Tesla Supercharger (destination)
stekkers van het type Tesla Supercharger (destination)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-11", + "question": { + "en": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=480 V", + "then": { + "en": "
Tesla Supercharger (destination)
outputs 480 volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-11", + "question": { + "en": "What current do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=125 A", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 125 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=350 A", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 350 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-11", + "question": { + "en": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?" + }, + "render": { + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=120 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 120 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=150 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 150 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=250 kw", + "then": { + "en": "
Tesla Supercharger (destination)
outputs at most 250 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-12", + "question": { + "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", + "nl": "Hier zijn
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + }, + "freeform": { + "key": "socket:tesla_destination", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "voltage-12", + "question": { + "en": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" + }, + "freeform": { + "key": "socket:tesla_destination:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:voltage=230 V", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" + } + }, + { + "if": "socket:socket:tesla_destination:voltage=400 V", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "current-12", + "question": { + "en": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" + }, + "freeform": { + "key": "socket:tesla_destination:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:current=16 A", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" + } + }, + { + "if": "socket:socket:tesla_destination:current=32 A", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "power-output-12", + "question": { + "en": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + }, + "render": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" + }, + "freeform": { + "key": "socket:tesla_destination:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:tesla_destination:output=11 kw", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" + } + }, + { + "if": "socket:socket:tesla_destination:output=22 kw", + "then": { + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" + } + } + ], + "condition": { + "and": [ + "socket:tesla_destination~*", + "socket:tesla_destination!=0" + ] + } + }, + { + "id": "plugs-13", + "question": { + "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are
USB to charge phones and small electronics
plugs of type USB to charge phones and small electronics available here", + "nl": "Hier zijn
USB om GSMs en kleine electronica op te laden
stekkers van het type USB om GSMs en kleine electronica op te laden" + }, + "freeform": { + "key": "socket:USB-A", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "voltage-13", + "question": { + "en": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" + }, + "freeform": { + "key": "socket:USB-A:voltage", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:voltage=5 V", + "then": { + "en": "
USB to charge phones and small electronics
outputs 5 volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "current-13", + "question": { + "en": "What current do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" + }, + "freeform": { + "key": "socket:USB-A:current", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:current=1 A", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 1 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" + } + }, + { + "if": "socket:socket:USB-A:current=2 A", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 2 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "power-output-13", + "question": { + "en": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?" + }, + "render": { + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" + }, + "freeform": { + "key": "socket:USB-A:output", + "type": "pfloat" + }, + "mappings": [ + { + "if": "socket:socket:USB-A:output=5w", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 5w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" + } + }, + { + "if": "socket:socket:USB-A:output=10w", + "then": { + "en": "
USB to charge phones and small electronics
outputs at most 10w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" + } + } + ], + "condition": { + "and": [ + "socket:USB-A~*", + "socket:USB-A!=0" + ] + } + }, + { + "id": "Authentication", + "question": { + "en": "What kind of authentication is available at the charging station?", + "it": "Quali sono gli orari di apertura di questa stazione di ricarica?", + "ja": "この充電ステーションはいつオープンしますか?", + "nb_NO": "Når åpnet denne ladestasjonen?", + "ru": "В какое время работает эта зарядная станция?", + "zh_Hant": "何時是充電站開放使用的時間?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "authentication:membership_card=yes", + "ifnot": "authentication:membership_card=no", + "then": { + "en": "Authentication by a membership card" + } + }, + { + "if": "authentication:app=yes", + "ifnot": "authentication:app=no", + "then": { + "en": "Authentication by an app" + } + }, + { + "if": "authentication:phone_call=yes", + "ifnot": "authentication:phone_call=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:short_message=yes", + "ifnot": "authentication:short_message=no", + "then": { + "en": "Authentication via phone call is available" + } + }, + { + "if": "authentication:nfc=yes", + "ifnot": "authentication:nfc=no", + "then": { + "en": "Authentication via NFC is available" + } + }, + { + "if": "authentication:money_card=yes", + "ifnot": "authentication:money_card=no", + "then": { + "en": "Authentication via Money Card is available" + } + }, + { + "if": "authentication:debit_card=yes", + "ifnot": "authentication:debit_card=no", + "then": { + "en": "Authentication via debit card is available" + } + }, + { + "if": "authentication:none=yes", + "ifnot": "authentication:none=no", + "then": { + "en": "No authentication is needed" + } + } + ] + }, + { + "id": "Auth phone", + "render": { + "en": "Authenticate by calling or SMS'ing to {authentication:phone_call:number}", + "it": "{network}", + "ja": "{network}", + "nb_NO": "{network}", + "ru": "{network}", + "zh_Hant": "{network}" + }, + "question": { + "en": "What's the phone number for authentication call or SMS?", + "it": "A quale rete appartiene questa stazione di ricarica?", + "ja": "この充電ステーションの運営チェーンはどこですか?", + "ru": "К какой сети относится эта станция?", + "zh_Hant": "充電站所屬的網路是?" + }, + "freeform": { + "key": "authentication:phone_call:number", + "type": "phone" + }, + "condition": { + "or": [ + "authentication:phone_call=yes", + "authentication:short_message=yes" + ] + }, + "it": { + "0": { + "then": "Non appartiene a una rete" + } + }, + "ja": { + "0": { + "then": "大規模な運営チェーンの一部ではない" + } + }, + "ru": { + "0": { + "then": "Не является частью более крупной сети" + } + }, + "zh_Hant": { + "0": { + "then": "不屬於大型網路" + } + } + }, + { + "id": "OH", + "render": "{opening_hours_table(opening_hours)}", + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "question": { + "en": "When is this charging station opened?" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7 opened (including holidays)" + } + } + ] + }, + { + "id": "fee/charge", + "question": { + "en": "How much does one have to pay to use this charging station?", + "nl": "Hoeveel kost het gebruik van dit oplaadpunt?" + }, + "freeform": { + "key": "charge", + "addExtraTags": [ + "fee=yes" + ] + }, + "render": { + "en": "Using this charging station costs {charge}", + "nl": "Dit oplaadpunt gebruiken kost {charge}" + }, + "mappings": [ + { + "if": { + "and": [ + "fee=no", + "charge=" + ] + }, + "then": { + "nl": "Gratis te gebruiken", + "en": "Free to use" + } + } + ] + }, + { + "id": "payment-options", + "builtin": "payment-options", + "override": { + "condition": { + "or": [ + "fee=yes", + "charge~*" + ] + }, + "mappings+": [ + { + "if": "payment:app=yes", + "ifnot": "payment:app=no", + "then": { + "en": "Payment is done using a dedicated app", + "nl": "Betalen via een app van het netwerk" } + }, + { + "if": "payment:membership_card=yes", + "ifnot": "payment:membership_card=no", + "then": { + "en": "Payment is done using a membership card", + "nl": "Betalen via een lidkaart van het netwerk" + } + } + ] + } + }, + { + "id": "maxstay", + "question": { + "en": "What is the maximum amount of time one is allowed to stay here?", + "nl": "Hoelang mag een voertuig hier blijven staan?" + }, + "freeform": { + "key": "maxstay" + }, + "render": { + "en": "One can stay at most {canonical(maxstay)}", + "nl": "De maximale parkeertijd hier is {canonical(maxstay)}" + }, + "mappings": [ + { + "if": "maxstay=unlimited", + "then": { + "en": "No timelimit on leaving your vehicle here", + "nl": "Geen maximum parkeertijd" + } } - ], - "wayHandling": 1, - "filter": [ + ] + }, + { + "id": "Network", + "render": { + "en": "Part of the network {network}" + }, + "question": { + "en": "Is this charging station part of a network?" + }, + "freeform": { + "key": "network" + }, + "mappings": [ { - "id": "vehicle-type", - "options": [ - { - "question": { - "en": "All vehicle types", - "nl": "Alle voertuigen" - } - }, - { - "question": { - "en": "Charging station for bicycles", - "nl": "Oplaadpunten voor fietsen" - }, - "osmTags": "bicycle=yes" - }, - { - "question": { - "en": "Charging station for cars", - "nl": "Oplaadpunten voor auto's" - }, - "osmTags": { - "or": [ - "car=yes", - "motorcar=yes" - ] - } - } - ] + "if": "no:network=yes", + "then": { + "en": "Not part of a bigger network" + } }, { - "id": "working", - "options": [ - { - "question": { - "en": "Only working charging stations" - }, - "osmTags": { - "and": [ - "operational_status!=broken", - "amenity=charging_station" - ] - } - } - ] + "if": "network=none", + "then": { + "en": "Not part of a bigger network" + }, + "hideInAnswer": true }, { - "id": "connection_type", - "options": [ - { - "question": { - "en": "All connectors", - "nl": "Alle types" - } - }, - { - "question": { - "en": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector", - "nl": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " - }, - "osmTags": "socket:schuko~*" - }, - { - "question": { - "en": "Has a European wall plug with ground pin (CEE7/4 type E) connector", - "nl": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " - }, - "osmTags": "socket:typee~*" - }, - { - "question": { - "en": "Has a Chademo connector", - "nl": "Heeft een Chademo " - }, - "osmTags": "socket:chademo~*" - }, - { - "question": { - "en": "Has a Type 1 with cable (J1772) connector", - "nl": "Heeft een Type 1 met kabel (J1772) " - }, - "osmTags": "socket:type1_cable~*" - }, - { - "question": { - "en": "Has a Type 1 without cable (J1772) connector", - "nl": "Heeft een Type 1 zonder kabel (J1772) " - }, - "osmTags": "socket:type1~*" - }, - { - "question": { - "en": "Has a Type 1 CCS (aka Type 1 Combo) connector", - "nl": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " - }, - "osmTags": "socket:type1_combo~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger connector", - "nl": "Heeft een Tesla Supercharger " - }, - "osmTags": "socket:tesla_supercharger~*" - }, - { - "question": { - "en": "Has a Type 2 (mennekes) connector", - "nl": "Heeft een Type 2 (mennekes) " - }, - "osmTags": "socket:type2~*" - }, - { - "question": { - "en": "Has a Type 2 CCS (mennekes) connector", - "nl": "Heeft een Type 2 CCS (mennekes) " - }, - "osmTags": "socket:type2_combo~*" - }, - { - "question": { - "en": "Has a Type 2 with cable (mennekes) connector", - "nl": "Heeft een Type 2 met kabel (J1772) " - }, - "osmTags": "socket:type2_cable~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger CCS (a branded type2_css) connector", - "nl": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " - }, - "osmTags": "socket:tesla_supercharger_ccs~*" - }, - { - "question": { - "en": "Has a Tesla Supercharger (destination) connector", - "nl": "Heeft een Tesla Supercharger (destination) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector", - "nl": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " - }, - "osmTags": "socket:tesla_destination~*" - }, - { - "question": { - "en": "Has a USB to charge phones and small electronics connector", - "nl": "Heeft een USB om GSMs en kleine electronica op te laden " - }, - "osmTags": "socket:USB-A~*" - } - ] + "if": "network=AeroVironment", + "then": "AeroVironment" + }, + { + "if": "network=Blink", + "then": "Blink" + }, + { + "if": "network=eVgo", + "then": "eVgo" } - ], - "units": [ + ] + }, + { + "id": "Operator", + "question": { + "en": "Who is the operator of this charging station?" + }, + "render": { + "en": "This charging station is operated by {operator}" + }, + "freeform": { + "key": "operator" + }, + "mappings": [ { - "appliesToKey": [ - "maxstay" - ], - "applicableUnits": [ - { - "canonicalDenomination": "minutes", - "canonicalDenominationSingular": "minute", - "alternativeDenomination": [ - "m", - "min", - "mins", - "minuten", - "mns" - ], - "human": { - "en": " minutes", - "nl": " minuten" - }, - "humanSingular": { - "en": " minute", - "nl": " minuut" - } - }, - { - "canonicalDenomination": "hours", - "canonicalDenominationSingular": "hour", - "alternativeDenomination": [ - "h", - "hrs", - "hours", - "u", - "uur", - "uren" - ], - "human": { - "en": " hours", - "nl": " uren" - }, - "humanSingular": { - "en": " hour", - "nl": " uur" - } - }, - { - "canonicalDenomination": "days", - "canonicalDenominationSingular": "day", - "alternativeDenomination": [ - "dys", - "dagen", - "dag" - ], - "human": { - "en": " days", - "nl": " day" - }, - "humanSingular": { - "en": " day", - "nl": " dag" - } - } + "if": { + "and": [ + "network:={operator}" ] - }, - { - "appliesToKey": [ - "socket:schuko:voltage", - "socket:typee:voltage", - "socket:chademo:voltage", - "socket:type1_cable:voltage", - "socket:type1:voltage", - "socket:type1_combo:voltage", - "socket:tesla_supercharger:voltage", - "socket:type2:voltage", - "socket:type2_combo:voltage", - "socket:type2_cable:voltage", - "socket:tesla_supercharger_ccs:voltage", - "socket:tesla_destination:voltage", - "socket:tesla_destination:voltage", - "socket:USB-A:voltage" - ], - "applicableUnits": [ - { - "canonicalDenomination": "V", - "alternativeDenomination": [ - "v", - "volt", - "voltage", - "V", - "Volt" - ], - "human": { - "en": "Volts", - "nl": "volt" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:current", - "socket:typee:current", - "socket:chademo:current", - "socket:type1_cable:current", - "socket:type1:current", - "socket:type1_combo:current", - "socket:tesla_supercharger:current", - "socket:type2:current", - "socket:type2_combo:current", - "socket:type2_cable:current", - "socket:tesla_supercharger_ccs:current", - "socket:tesla_destination:current", - "socket:tesla_destination:current", - "socket:USB-A:current" - ], - "applicableUnits": [ - { - "canonicalDenomination": "A", - "alternativeDenomination": [ - "a", - "amp", - "amperage", - "A" - ], - "human": { - "en": "A", - "nl": "A" - } - } - ], - "eraseInvalidValues": true - }, - { - "appliesToKey": [ - "socket:schuko:output", - "socket:typee:output", - "socket:chademo:output", - "socket:type1_cable:output", - "socket:type1:output", - "socket:type1_combo:output", - "socket:tesla_supercharger:output", - "socket:type2:output", - "socket:type2_combo:output", - "socket:type2_cable:output", - "socket:tesla_supercharger_ccs:output", - "socket:tesla_destination:output", - "socket:tesla_destination:output", - "socket:USB-A:output" - ], - "applicableUnits": [ - { - "canonicalDenomination": "kW", - "alternativeDenomination": [ - "kilowatt" - ], - "human": { - "en": "kilowatt", - "nl": "kilowatt" - } - }, - { - "canonicalDenomination": "mW", - "alternativeDenomination": [ - "megawatt" - ], - "human": { - "en": "megawatt", - "nl": "megawatt" - } - } - ], - "eraseInvalidValues": true + }, + "then": { + "en": "Actually, {operator} is the network" + }, + "addExtraTags": [ + "operator=" + ], + "hideInAnswer": "operator=" } + ] + }, + { + "id": "phone", + "question": { + "en": "What number can one call if there is a problem with this charging station?" + }, + "render": { + "en": "In case of problems, call {phone}" + }, + "freeform": { + "key": "phone", + "type": "phone" + } + }, + { + "id": "email", + "question": { + "en": "What is the email address of the operator?" + }, + "render": { + "en": "In case of problems, send an email to {email}" + }, + "freeform": { + "key": "email", + "type": "email" + } + }, + { + "id": "website", + "question": { + "en": "What is the website of the operator?" + }, + "render": { + "en": "More info on {website}" + }, + "freeform": { + "key": "website", + "type": "url" + } + }, + "level", + { + "id": "ref", + "question": { + "en": "What is the reference number of this charging station?" + }, + "render": { + "en": "Reference number is {ref}" + }, + "freeform": { + "key": "ref" + } + }, + { + "id": "Operational status", + "question": { + "en": "Is this charging point in use?", + "nl": "Is dit oplaadpunt operationeel?" + }, + "mappings": [ + { + "if": "operational_status=broken", + "then": { + "en": "This charging station is broken", + "nl": "Dit oplaadpunt is kapot" + } + }, + { + "if": { + "and": [ + "planned:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is planned here", + "nl": "Hier zal binnenkort een oplaadpunt gebouwd worden" + } + }, + { + "if": { + "and": [ + "construction:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "A charging station is constructed here", + "nl": "Hier wordt op dit moment een oplaadpunt gebouwd" + } + }, + { + "if": { + "and": [ + "disused:amenity=charging_station", + "amenity=" + ] + }, + "then": { + "en": "This charging station has beed permanently disabled and is not in use anymore but is still visible", + "nl": "Dit oplaadpunt is niet meer in gebruik maar is wel nog aanwezig" + } + }, + { + "if": { + "and": [ + "amenity=charging_station", + "operational_status=" + ] + }, + "then": { + "en": "This charging station works", + "nl": "Dit oplaadpunt werkt" + } + } + ] + }, + { + "id": "Parking:fee", + "question": { + "en": "Does one have to pay a parking fee while charging?" + }, + "mappings": [ + { + "if": "parking:fee=no", + "then": { + "en": "No additional parking cost while charging" + } + }, + { + "if": "parking:fee=yes", + "then": { + "en": "An additional parking fee should be paid while charging" + } + } + ] + } + ], + "icon": { + "render": "pin:#fff;./assets/themes/charging_stations/plug.svg", + "mappings": [ + { + "if": "bicycle=yes", + "then": "pin:#fff;./assets/themes/charging_stations/bicycle.svg" + }, + { + "if": { + "or": [ + "car=yes", + "motorcar=yes" + ] + }, + "then": "pin:#fff;./assets/themes/charging_stations/car.svg" + } ] + }, + "iconOverlays": [ + { + "if": { + "or": [ + "disused:amenity=charging_station", + "operational_status=broken" + ] + }, + "then": "cross_bottom_right:#c22;" + }, + { + "if": { + "or": [ + "proposed:amenity=charging_station", + "planned:amenity=charging_station" + ] + }, + "then": "./assets/layers/charging_station/under_construction.svg", + "badge": true + }, + { + "if": { + "and": [ + "bicycle=yes", + { + "or": [ + "motorcar=yes", + "car=yes" + ] + } + ] + }, + "then": "circle:#fff;./assets/themes/charging_stations/car.svg", + "badge": true + } + ], + "width": { + "render": "8" + }, + "iconSize": { + "render": "50,50,bottom" + }, + "color": { + "render": "#00f" + }, + "presets": [ + { + "tags": [ + "amenity=charging_station" + ], + "title": { + "en": "Charging station" + }, + "preciseInput": { + "preferredBackground": "map" + } + } + ], + "wayHandling": 1, + "filter": [ + { + "id": "vehicle-type", + "options": [ + { + "question": { + "en": "All vehicle types", + "nl": "Alle voertuigen" + } + }, + { + "question": { + "en": "Charging station for bicycles", + "nl": "Oplaadpunten voor fietsen" + }, + "osmTags": "bicycle=yes" + }, + { + "question": { + "en": "Charging station for cars", + "nl": "Oplaadpunten voor auto's" + }, + "osmTags": { + "or": [ + "car=yes", + "motorcar=yes" + ] + } + } + ] + }, + { + "id": "working", + "options": [ + { + "question": { + "en": "Only working charging stations" + }, + "osmTags": { + "and": [ + "operational_status!=broken", + "amenity=charging_station" + ] + } + } + ] + }, + { + "id": "connection_type", + "options": [ + { + "question": { + "en": "All connectors", + "nl": "Alle types" + } + }, + { + "question": { + "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", + "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + }, + "osmTags": "socket:schuko~*" + }, + { + "question": { + "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", + "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" + }, + "osmTags": "socket:typee~*" + }, + { + "question": { + "en": "Has a
Chademo
connector", + "nl": "Heeft een
Chademo
" + }, + "osmTags": "socket:chademo~*" + }, + { + "question": { + "en": "Has a
Type 1 with cable (J1772)
connector", + "nl": "Heeft een
Type 1 met kabel (J1772)
" + }, + "osmTags": "socket:type1_cable~*" + }, + { + "question": { + "en": "Has a
Type 1 without cable (J1772)
connector", + "nl": "Heeft een
Type 1 zonder kabel (J1772)
" + }, + "osmTags": "socket:type1~*" + }, + { + "question": { + "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", + "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" + }, + "osmTags": "socket:type1_combo~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger
connector", + "nl": "Heeft een
Tesla Supercharger
" + }, + "osmTags": "socket:tesla_supercharger~*" + }, + { + "question": { + "en": "Has a
Type 2 (mennekes)
connector", + "nl": "Heeft een
Type 2 (mennekes)
" + }, + "osmTags": "socket:type2~*" + }, + { + "question": { + "en": "Has a
Type 2 CCS (mennekes)
connector", + "nl": "Heeft een
Type 2 CCS (mennekes)
" + }, + "osmTags": "socket:type2_combo~*" + }, + { + "question": { + "en": "Has a
Type 2 with cable (mennekes)
connector", + "nl": "Heeft een
Type 2 met kabel (J1772)
" + }, + "osmTags": "socket:type2_cable~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", + "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + }, + "osmTags": "socket:tesla_supercharger_ccs~*" + }, + { + "question": { + "en": "Has a
Tesla Supercharger (destination)
connector", + "nl": "Heeft een
Tesla Supercharger (destination)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", + "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + }, + "osmTags": "socket:tesla_destination~*" + }, + { + "question": { + "en": "Has a
USB to charge phones and small electronics
connector", + "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "osmTags": "socket:USB-A~*" + } + ] + } + ], + "units": [ + { + "appliesToKey": [ + "maxstay" + ], + "applicableUnits": [ + { + "canonicalDenomination": "minutes", + "canonicalDenominationSingular": "minute", + "alternativeDenomination": [ + "m", + "min", + "mins", + "minuten", + "mns" + ], + "human": { + "en": " minutes", + "nl": " minuten" + }, + "humanSingular": { + "en": " minute", + "nl": " minuut" + } + }, + { + "canonicalDenomination": "hours", + "canonicalDenominationSingular": "hour", + "alternativeDenomination": [ + "h", + "hrs", + "hours", + "u", + "uur", + "uren" + ], + "human": { + "en": " hours", + "nl": " uren" + }, + "humanSingular": { + "en": " hour", + "nl": " uur" + } + }, + { + "canonicalDenomination": "days", + "canonicalDenominationSingular": "day", + "alternativeDenomination": [ + "dys", + "dagen", + "dag" + ], + "human": { + "en": " days", + "nl": " day" + }, + "humanSingular": { + "en": " day", + "nl": " dag" + } + } + ] + }, + { + "appliesToKey": [ + "socket:schuko:voltage", + "socket:typee:voltage", + "socket:chademo:voltage", + "socket:type1_cable:voltage", + "socket:type1:voltage", + "socket:type1_combo:voltage", + "socket:tesla_supercharger:voltage", + "socket:type2:voltage", + "socket:type2_combo:voltage", + "socket:type2_cable:voltage", + "socket:tesla_supercharger_ccs:voltage", + "socket:tesla_destination:voltage", + "socket:tesla_destination:voltage", + "socket:USB-A:voltage" + ], + "applicableUnits": [ + { + "canonicalDenomination": "V", + "alternativeDenomination": [ + "v", + "volt", + "voltage", + "V", + "Volt" + ], + "human": { + "en": "Volts", + "nl": "volt" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:current", + "socket:typee:current", + "socket:chademo:current", + "socket:type1_cable:current", + "socket:type1:current", + "socket:type1_combo:current", + "socket:tesla_supercharger:current", + "socket:type2:current", + "socket:type2_combo:current", + "socket:type2_cable:current", + "socket:tesla_supercharger_ccs:current", + "socket:tesla_destination:current", + "socket:tesla_destination:current", + "socket:USB-A:current" + ], + "applicableUnits": [ + { + "canonicalDenomination": "A", + "alternativeDenomination": [ + "a", + "amp", + "amperage", + "A" + ], + "human": { + "en": "A", + "nl": "A" + } + } + ], + "eraseInvalidValues": true + }, + { + "appliesToKey": [ + "socket:schuko:output", + "socket:typee:output", + "socket:chademo:output", + "socket:type1_cable:output", + "socket:type1:output", + "socket:type1_combo:output", + "socket:tesla_supercharger:output", + "socket:type2:output", + "socket:type2_combo:output", + "socket:type2_cable:output", + "socket:tesla_supercharger_ccs:output", + "socket:tesla_destination:output", + "socket:tesla_destination:output", + "socket:USB-A:output" + ], + "applicableUnits": [ + { + "canonicalDenomination": "kW", + "alternativeDenomination": [ + "kilowatt" + ], + "human": { + "en": "kilowatt", + "nl": "kilowatt" + } + }, + { + "canonicalDenomination": "mW", + "alternativeDenomination": [ + "megawatt" + ], + "human": { + "en": "megawatt", + "nl": "megawatt" + } + } + ], + "eraseInvalidValues": true + } + ] } \ No newline at end of file diff --git a/assets/layers/charging_station/csvToJson.ts b/assets/layers/charging_station/csvToJson.ts index 14567f525..9c529883f 100644 --- a/assets/layers/charging_station/csvToJson.ts +++ b/assets/layers/charging_station/csvToJson.ts @@ -127,8 +127,8 @@ function run(file, protojson) { } overview_question_answers.push(no_ask_json) - const descrWithImage_en = `${e.description.get("en")} ` - const descrWithImage_nl = `${e.description.get("nl")} ` + const descrWithImage_en = `
${e.description.get("en")}
` + const descrWithImage_nl = `
${e.description.get("nl")}
` questions.push({ "id": "plugs-" + i, From 6dd7f5cdcbe41620a1fe9971cb1ea1a9cb7ffbd1 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 4 Oct 2021 19:47:23 +0200 Subject: [PATCH 104/110] Styling: add flex shrink to filter selection button --- Models/Constants.ts | 2 +- UI/BigComponents/FilterView.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Models/Constants.ts b/Models/Constants.ts index 88348d1c4..99f3d28bb 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.1-rc3"; + public static vNumber = "0.10.1-rc4"; public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts index 336d77405..f5e48161f 100644 --- a/UI/BigComponents/FilterView.ts +++ b/UI/BigComponents/FilterView.ts @@ -33,7 +33,7 @@ export default class FilterView extends VariableUiElement { // Name is not defined: we hide this one return undefined; } - const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem"; + const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem;flex-shrink: 0;"; const icon = new Combine([Svg.checkbox_filled]).SetStyle(iconStyle); const iconUnselected = new Combine([Svg.checkbox_empty]).SetStyle( From 820a0cf5605c9d416a02c7b06ee997920441af92 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 5 Oct 2021 12:18:19 +0200 Subject: [PATCH 105/110] Styling tweaks --- Models/Constants.ts | 2 +- .../charging_station/charging_station.json | 804 +++++++++--------- assets/layers/charging_station/csvToJson.ts | 4 +- 3 files changed, 405 insertions(+), 405 deletions(-) diff --git a/Models/Constants.ts b/Models/Constants.ts index 99f3d28bb..9a9250eae 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.10.1-rc4"; + public static vNumber = "0.10.1-rc5"; public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 6b9974e29..55891d6e6 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -760,12 +760,12 @@ { "id": "plugs-0", "question": { - "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", - "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Schuko wall plug without ground pin (CEE7/4 type F)
plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", - "nl": "Hier zijn
Schuko stekker zonder aardingspin (CEE7/4 type F)
stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + "en": "There are
Schuko wall plug without ground pin (CEE7/4 type F)
plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", + "nl": "Hier zijn
Schuko stekker zonder aardingspin (CEE7/4 type F)
stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" }, "freeform": { "key": "socket:schuko", @@ -781,12 +781,12 @@ { "id": "voltage-0", "question": { - "en": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + "en": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" }, "freeform": { "key": "socket:schuko:voltage", @@ -796,8 +796,8 @@ { "if": "socket:socket:schuko:voltage=230 V", "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" } } ], @@ -811,12 +811,12 @@ { "id": "current-0", "question": { - "en": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + "en": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" }, "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" }, "freeform": { "key": "socket:schuko:current", @@ -826,8 +826,8 @@ { "if": "socket:socket:schuko:current=16 A", "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" } } ], @@ -841,12 +841,12 @@ { "id": "power-output-0", "question": { - "en": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" + "en": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?" }, "render": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" }, "freeform": { "key": "socket:schuko:output", @@ -856,8 +856,8 @@ { "if": "socket:socket:schuko:output=3.6 kw", "then": { - "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw", - "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" + "en": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw", + "nl": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" } } ], @@ -871,12 +871,12 @@ { "id": "plugs-1", "question": { - "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", - "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" + "en": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
European wall plug with ground pin (CEE7/4 type E)
plugs of type European wall plug with ground pin (CEE7/4 type E) available here", - "nl": "Hier zijn
Europese stekker met aardingspin (CEE7/4 type E)
stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + "en": "There are
European wall plug with ground pin (CEE7/4 type E)
plugs of type European wall plug with ground pin (CEE7/4 type E) available here", + "nl": "Hier zijn
Europese stekker met aardingspin (CEE7/4 type E)
stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" }, "freeform": { "key": "socket:typee", @@ -892,12 +892,12 @@ { "id": "voltage-1", "question": { - "en": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
" + "en": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
" }, "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" }, "freeform": { "key": "socket:typee:voltage", @@ -907,8 +907,8 @@ { "if": "socket:socket:typee:voltage=230 V", "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" } } ], @@ -922,12 +922,12 @@ { "id": "current-1", "question": { - "en": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + "en": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" }, "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" }, "freeform": { "key": "socket:typee:current", @@ -937,8 +937,8 @@ { "if": "socket:socket:typee:current=16 A", "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" } } ], @@ -952,12 +952,12 @@ { "id": "power-output-1", "question": { - "en": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" + "en": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?" }, "render": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" }, "freeform": { "key": "socket:typee:output", @@ -967,15 +967,15 @@ { "if": "socket:socket:typee:output=3 kw", "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" } }, { "if": "socket:socket:typee:output=22 kw", "then": { - "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw", - "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" + "en": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw", + "nl": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" } } ], @@ -989,12 +989,12 @@ { "id": "plugs-2", "question": { - "en": "How much plugs of type
Chademo
are available here?", - "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" + "en": "How much plugs of type
Chademo
are available here?", + "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Chademo
plugs of type Chademo available here", - "nl": "Hier zijn
Chademo
stekkers van het type Chademo" + "en": "There are
Chademo
plugs of type Chademo available here", + "nl": "Hier zijn
Chademo
stekkers van het type Chademo" }, "freeform": { "key": "socket:chademo", @@ -1010,12 +1010,12 @@ { "id": "voltage-2", "question": { - "en": "What voltage do the plugs with
Chademo
offer?", - "nl": "Welke spanning levert de stekker van type
Chademo
" + "en": "What voltage do the plugs with
Chademo
offer?", + "nl": "Welke spanning levert de stekker van type
Chademo
" }, "render": { - "en": "
Chademo
outputs {socket:chademo:voltage} volt", - "nl": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" + "en": "
Chademo
outputs {socket:chademo:voltage} volt", + "nl": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" }, "freeform": { "key": "socket:chademo:voltage", @@ -1025,8 +1025,8 @@ { "if": "socket:socket:chademo:voltage=500 V", "then": { - "en": "
Chademo
outputs 500 volt", - "nl": "
Chademo
heeft een spanning van 500 volt" + "en": "
Chademo
outputs 500 volt", + "nl": "
Chademo
heeft een spanning van 500 volt" } } ], @@ -1040,12 +1040,12 @@ { "id": "current-2", "question": { - "en": "What current do the plugs with
Chademo
offer?", - "nl": "Welke stroom levert de stekker van type
Chademo
?" + "en": "What current do the plugs with
Chademo
offer?", + "nl": "Welke stroom levert de stekker van type
Chademo
?" }, "render": { - "en": "
Chademo
outputs at most {socket:chademo:current}A", - "nl": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" + "en": "
Chademo
outputs at most {socket:chademo:current}A", + "nl": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" }, "freeform": { "key": "socket:chademo:current", @@ -1055,8 +1055,8 @@ { "if": "socket:socket:chademo:current=120 A", "then": { - "en": "
Chademo
outputs at most 120 A", - "nl": "
Chademo
levert een stroom van maximaal 120 A" + "en": "
Chademo
outputs at most 120 A", + "nl": "
Chademo
levert een stroom van maximaal 120 A" } } ], @@ -1070,12 +1070,12 @@ { "id": "power-output-2", "question": { - "en": "What power output does a single plug of type
Chademo
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Chademo
?" + "en": "What power output does a single plug of type
Chademo
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Chademo
?" }, "render": { - "en": "
Chademo
outputs at most {socket:chademo:output}", - "nl": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" + "en": "
Chademo
outputs at most {socket:chademo:output}", + "nl": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" }, "freeform": { "key": "socket:chademo:output", @@ -1085,8 +1085,8 @@ { "if": "socket:socket:chademo:output=50 kw", "then": { - "en": "
Chademo
outputs at most 50 kw", - "nl": "
Chademo
levert een vermogen van maximaal 50 kw" + "en": "
Chademo
outputs at most 50 kw", + "nl": "
Chademo
levert een vermogen van maximaal 50 kw" } } ], @@ -1100,12 +1100,12 @@ { "id": "plugs-3", "question": { - "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 1 with cable (J1772)
plugs of type Type 1 with cable (J1772) available here", - "nl": "Hier zijn
Type 1 met kabel (J1772)
stekkers van het type Type 1 met kabel (J1772)" + "en": "There are
Type 1 with cable (J1772)
plugs of type Type 1 with cable (J1772) available here", + "nl": "Hier zijn
Type 1 met kabel (J1772)
stekkers van het type Type 1 met kabel (J1772)" }, "freeform": { "key": "socket:type1_cable", @@ -1121,12 +1121,12 @@ { "id": "voltage-3", "question": { - "en": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
" + "en": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
" }, "render": { - "en": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" + "en": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" }, "freeform": { "key": "socket:type1_cable:voltage", @@ -1136,15 +1136,15 @@ { "if": "socket:socket:type1_cable:voltage=200 V", "then": { - "en": "
Type 1 with cable (J1772)
outputs 200 volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" + "en": "
Type 1 with cable (J1772)
outputs 200 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" } }, { "if": "socket:socket:type1_cable:voltage=240 V", "then": { - "en": "
Type 1 with cable (J1772)
outputs 240 volt", - "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" + "en": "
Type 1 with cable (J1772)
outputs 240 volt", + "nl": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" } } ], @@ -1158,12 +1158,12 @@ { "id": "current-3", "question": { - "en": "What current do the plugs with
Type 1 with cable (J1772)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?" + "en": "What current do the plugs with
Type 1 with cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?" }, "render": { - "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A", - "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" }, "freeform": { "key": "socket:type1_cable:current", @@ -1173,8 +1173,8 @@ { "if": "socket:socket:type1_cable:current=32 A", "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 32 A", - "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" + "en": "
Type 1 with cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" } } ], @@ -1188,12 +1188,12 @@ { "id": "power-output-3", "question": { - "en": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?" + "en": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?" }, "render": { - "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" + "en": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" }, "freeform": { "key": "socket:type1_cable:output", @@ -1203,15 +1203,15 @@ { "if": "socket:socket:type1_cable:output=3.7 kw", "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 3.7 kw", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + "en": "
Type 1 with cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" } }, { "if": "socket:socket:type1_cable:output=7 kw", "then": { - "en": "
Type 1 with cable (J1772)
outputs at most 7 kw", - "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" + "en": "
Type 1 with cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" } } ], @@ -1225,12 +1225,12 @@ { "id": "plugs-4", "question": { - "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 1 without cable (J1772)
plugs of type Type 1 without cable (J1772) available here", - "nl": "Hier zijn
Type 1 zonder kabel (J1772)
stekkers van het type Type 1 zonder kabel (J1772)" + "en": "There are
Type 1 without cable (J1772)
plugs of type Type 1 without cable (J1772) available here", + "nl": "Hier zijn
Type 1 zonder kabel (J1772)
stekkers van het type Type 1 zonder kabel (J1772)" }, "freeform": { "key": "socket:type1", @@ -1246,12 +1246,12 @@ { "id": "voltage-4", "question": { - "en": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
" + "en": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
" }, "render": { - "en": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" + "en": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" }, "freeform": { "key": "socket:type1:voltage", @@ -1261,15 +1261,15 @@ { "if": "socket:socket:type1:voltage=200 V", "then": { - "en": "
Type 1 without cable (J1772)
outputs 200 volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" + "en": "
Type 1 without cable (J1772)
outputs 200 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" } }, { "if": "socket:socket:type1:voltage=240 V", "then": { - "en": "
Type 1 without cable (J1772)
outputs 240 volt", - "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" + "en": "
Type 1 without cable (J1772)
outputs 240 volt", + "nl": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" } } ], @@ -1283,12 +1283,12 @@ { "id": "current-4", "question": { - "en": "What current do the plugs with
Type 1 without cable (J1772)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?" + "en": "What current do the plugs with
Type 1 without cable (J1772)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?" }, "render": { - "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A", - "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" }, "freeform": { "key": "socket:type1:current", @@ -1298,8 +1298,8 @@ { "if": "socket:socket:type1:current=32 A", "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 32 A", - "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" + "en": "
Type 1 without cable (J1772)
outputs at most 32 A", + "nl": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" } } ], @@ -1313,12 +1313,12 @@ { "id": "power-output-4", "question": { - "en": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?" + "en": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?" }, "render": { - "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" + "en": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" }, "freeform": { "key": "socket:type1:output", @@ -1328,29 +1328,29 @@ { "if": "socket:socket:type1:output=3.7 kw", "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 3.7 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" + "en": "
Type 1 without cable (J1772)
outputs at most 3.7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" } }, { "if": "socket:socket:type1:output=6.6 kw", "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 6.6 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" + "en": "
Type 1 without cable (J1772)
outputs at most 6.6 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" } }, { "if": "socket:socket:type1:output=7 kw", "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 7 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" + "en": "
Type 1 without cable (J1772)
outputs at most 7 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" } }, { "if": "socket:socket:type1:output=7.2 kw", "then": { - "en": "
Type 1 without cable (J1772)
outputs at most 7.2 kw", - "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" + "en": "
Type 1 without cable (J1772)
outputs at most 7.2 kw", + "nl": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" } } ], @@ -1364,12 +1364,12 @@ { "id": "plugs-5", "question": { - "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", - "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 1 CCS (aka Type 1 Combo)
plugs of type Type 1 CCS (aka Type 1 Combo) available here", - "nl": "Hier zijn
Type 1 CCS (ook gekend als Type 1 Combo)
stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" + "en": "There are
Type 1 CCS (aka Type 1 Combo)
plugs of type Type 1 CCS (aka Type 1 Combo) available here", + "nl": "Hier zijn
Type 1 CCS (ook gekend als Type 1 Combo)
stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" }, "freeform": { "key": "socket:type1_combo", @@ -1385,12 +1385,12 @@ { "id": "voltage-5", "question": { - "en": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
" + "en": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" }, "freeform": { "key": "socket:type1_combo:voltage", @@ -1400,15 +1400,15 @@ { "if": "socket:socket:type1_combo:voltage=400 V", "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" } }, { "if": "socket:socket:type1_combo:voltage=1000 V", "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" } } ], @@ -1422,12 +1422,12 @@ { "id": "current-5", "question": { - "en": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + "en": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" }, "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" }, "freeform": { "key": "socket:type1_combo:current", @@ -1437,15 +1437,15 @@ { "if": "socket:socket:type1_combo:current=50 A", "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" } }, { "if": "socket:socket:type1_combo:current=125 A", "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" } } ], @@ -1459,12 +1459,12 @@ { "id": "power-output-5", "question": { - "en": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" + "en": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?" }, "render": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" }, "freeform": { "key": "socket:type1_combo:output", @@ -1474,29 +1474,29 @@ { "if": "socket:socket:type1_combo:output=50 kw", "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" } }, { "if": "socket:socket:type1_combo:output=62.5 kw", "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" } }, { "if": "socket:socket:type1_combo:output=150 kw", "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" } }, { "if": "socket:socket:type1_combo:output=350 kw", "then": { - "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw", - "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" + "en": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw", + "nl": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" } } ], @@ -1510,12 +1510,12 @@ { "id": "plugs-6", "question": { - "en": "How much plugs of type
Tesla Supercharger
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" + "en": "How much plugs of type
Tesla Supercharger
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Tesla Supercharger
plugs of type Tesla Supercharger available here", - "nl": "Hier zijn
Tesla Supercharger
stekkers van het type Tesla Supercharger" + "en": "There are
Tesla Supercharger
plugs of type Tesla Supercharger available here", + "nl": "Hier zijn
Tesla Supercharger
stekkers van het type Tesla Supercharger" }, "freeform": { "key": "socket:tesla_supercharger", @@ -1531,12 +1531,12 @@ { "id": "voltage-6", "question": { - "en": "What voltage do the plugs with
Tesla Supercharger
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger
" + "en": "What voltage do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger
" }, "render": { - "en": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt", - "nl": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" + "en": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt", + "nl": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" }, "freeform": { "key": "socket:tesla_supercharger:voltage", @@ -1546,8 +1546,8 @@ { "if": "socket:socket:tesla_supercharger:voltage=480 V", "then": { - "en": "
Tesla Supercharger
outputs 480 volt", - "nl": "
Tesla Supercharger
heeft een spanning van 480 volt" + "en": "
Tesla Supercharger
outputs 480 volt", + "nl": "
Tesla Supercharger
heeft een spanning van 480 volt" } } ], @@ -1561,12 +1561,12 @@ { "id": "current-6", "question": { - "en": "What current do the plugs with
Tesla Supercharger
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger
?" + "en": "What current do the plugs with
Tesla Supercharger
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger
?" }, "render": { - "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" }, "freeform": { "key": "socket:tesla_supercharger:current", @@ -1576,15 +1576,15 @@ { "if": "socket:socket:tesla_supercharger:current=125 A", "then": { - "en": "
Tesla Supercharger
outputs at most 125 A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal 125 A" + "en": "
Tesla Supercharger
outputs at most 125 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:tesla_supercharger:current=350 A", "then": { - "en": "
Tesla Supercharger
outputs at most 350 A", - "nl": "
Tesla Supercharger
levert een stroom van maximaal 350 A" + "en": "
Tesla Supercharger
outputs at most 350 A", + "nl": "
Tesla Supercharger
levert een stroom van maximaal 350 A" } } ], @@ -1598,12 +1598,12 @@ { "id": "power-output-6", "question": { - "en": "What power output does a single plug of type
Tesla Supercharger
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?" + "en": "What power output does a single plug of type
Tesla Supercharger
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?" }, "render": { - "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" + "en": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" }, "freeform": { "key": "socket:tesla_supercharger:output", @@ -1613,22 +1613,22 @@ { "if": "socket:socket:tesla_supercharger:output=120 kw", "then": { - "en": "
Tesla Supercharger
outputs at most 120 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" + "en": "
Tesla Supercharger
outputs at most 120 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" } }, { "if": "socket:socket:tesla_supercharger:output=150 kw", "then": { - "en": "
Tesla Supercharger
outputs at most 150 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" + "en": "
Tesla Supercharger
outputs at most 150 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" } }, { "if": "socket:socket:tesla_supercharger:output=250 kw", "then": { - "en": "
Tesla Supercharger
outputs at most 250 kw", - "nl": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" + "en": "
Tesla Supercharger
outputs at most 250 kw", + "nl": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" } } ], @@ -1642,12 +1642,12 @@ { "id": "plugs-7", "question": { - "en": "How much plugs of type
Type 2 (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Type 2 (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 2 (mennekes)
plugs of type Type 2 (mennekes) available here", - "nl": "Hier zijn
Type 2 (mennekes)
stekkers van het type Type 2 (mennekes)" + "en": "There are
Type 2 (mennekes)
plugs of type Type 2 (mennekes) available here", + "nl": "Hier zijn
Type 2 (mennekes)
stekkers van het type Type 2 (mennekes)" }, "freeform": { "key": "socket:type2", @@ -1663,12 +1663,12 @@ { "id": "voltage-7", "question": { - "en": "What voltage do the plugs with
Type 2 (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 (mennekes)
" + "en": "What voltage do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 (mennekes)
" }, "render": { - "en": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" + "en": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" }, "freeform": { "key": "socket:type2:voltage", @@ -1678,15 +1678,15 @@ { "if": "socket:socket:type2:voltage=230 V", "then": { - "en": "
Type 2 (mennekes)
outputs 230 volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van 230 volt" + "en": "
Type 2 (mennekes)
outputs 230 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 230 volt" } }, { "if": "socket:socket:type2:voltage=400 V", "then": { - "en": "
Type 2 (mennekes)
outputs 400 volt", - "nl": "
Type 2 (mennekes)
heeft een spanning van 400 volt" + "en": "
Type 2 (mennekes)
outputs 400 volt", + "nl": "
Type 2 (mennekes)
heeft een spanning van 400 volt" } } ], @@ -1700,12 +1700,12 @@ { "id": "current-7", "question": { - "en": "What current do the plugs with
Type 2 (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?" + "en": "What current do the plugs with
Type 2 (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?" }, "render": { - "en": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" }, "freeform": { "key": "socket:type2:current", @@ -1715,15 +1715,15 @@ { "if": "socket:socket:type2:current=16 A", "then": { - "en": "
Type 2 (mennekes)
outputs at most 16 A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" + "en": "
Type 2 (mennekes)
outputs at most 16 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" } }, { "if": "socket:socket:type2:current=32 A", "then": { - "en": "
Type 2 (mennekes)
outputs at most 32 A", - "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" + "en": "
Type 2 (mennekes)
outputs at most 32 A", + "nl": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" } } ], @@ -1737,12 +1737,12 @@ { "id": "power-output-7", "question": { - "en": "What power output does a single plug of type
Type 2 (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?" + "en": "What power output does a single plug of type
Type 2 (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?" }, "render": { - "en": "
Type 2 (mennekes)
outputs at most {socket:type2:output}", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" + "en": "
Type 2 (mennekes)
outputs at most {socket:type2:output}", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" }, "freeform": { "key": "socket:type2:output", @@ -1752,15 +1752,15 @@ { "if": "socket:socket:type2:output=11 kw", "then": { - "en": "
Type 2 (mennekes)
outputs at most 11 kw", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" + "en": "
Type 2 (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" } }, { "if": "socket:socket:type2:output=22 kw", "then": { - "en": "
Type 2 (mennekes)
outputs at most 22 kw", - "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" + "en": "
Type 2 (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" } } ], @@ -1774,12 +1774,12 @@ { "id": "plugs-8", "question": { - "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 2 CCS (mennekes)
plugs of type Type 2 CCS (mennekes) available here", - "nl": "Hier zijn
Type 2 CCS (mennekes)
stekkers van het type Type 2 CCS (mennekes)" + "en": "There are
Type 2 CCS (mennekes)
plugs of type Type 2 CCS (mennekes) available here", + "nl": "Hier zijn
Type 2 CCS (mennekes)
stekkers van het type Type 2 CCS (mennekes)" }, "freeform": { "key": "socket:type2_combo", @@ -1795,12 +1795,12 @@ { "id": "voltage-8", "question": { - "en": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
" + "en": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
" }, "render": { - "en": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" + "en": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" }, "freeform": { "key": "socket:type2_combo:voltage", @@ -1810,15 +1810,15 @@ { "if": "socket:socket:type2_combo:voltage=500 V", "then": { - "en": "
Type 2 CCS (mennekes)
outputs 500 volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" + "en": "
Type 2 CCS (mennekes)
outputs 500 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" } }, { "if": "socket:socket:type2_combo:voltage=920 V", "then": { - "en": "
Type 2 CCS (mennekes)
outputs 920 volt", - "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" + "en": "
Type 2 CCS (mennekes)
outputs 920 volt", + "nl": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" } } ], @@ -1832,12 +1832,12 @@ { "id": "current-8", "question": { - "en": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?" + "en": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?" }, "render": { - "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" }, "freeform": { "key": "socket:type2_combo:current", @@ -1847,15 +1847,15 @@ { "if": "socket:socket:type2_combo:current=125 A", "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 125 A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" + "en": "
Type 2 CCS (mennekes)
outputs at most 125 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:type2_combo:current=350 A", "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 350 A", - "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" + "en": "
Type 2 CCS (mennekes)
outputs at most 350 A", + "nl": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" } } ], @@ -1869,12 +1869,12 @@ { "id": "power-output-8", "question": { - "en": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?" + "en": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?" }, "render": { - "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}", - "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" + "en": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" }, "freeform": { "key": "socket:type2_combo:output", @@ -1884,8 +1884,8 @@ { "if": "socket:socket:type2_combo:output=50 kw", "then": { - "en": "
Type 2 CCS (mennekes)
outputs at most 50 kw", - "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" + "en": "
Type 2 CCS (mennekes)
outputs at most 50 kw", + "nl": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" } } ], @@ -1899,12 +1899,12 @@ { "id": "plugs-9", "question": { - "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", - "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 2 with cable (mennekes)
plugs of type Type 2 with cable (mennekes) available here", - "nl": "Hier zijn
Type 2 met kabel (J1772)
stekkers van het type Type 2 met kabel (J1772)" + "en": "There are
Type 2 with cable (mennekes)
plugs of type Type 2 with cable (mennekes) available here", + "nl": "Hier zijn
Type 2 met kabel (J1772)
stekkers van het type Type 2 met kabel (J1772)" }, "freeform": { "key": "socket:type2_cable", @@ -1920,12 +1920,12 @@ { "id": "voltage-9", "question": { - "en": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", - "nl": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
" + "en": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
" }, "render": { - "en": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" + "en": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" }, "freeform": { "key": "socket:type2_cable:voltage", @@ -1935,15 +1935,15 @@ { "if": "socket:socket:type2_cable:voltage=230 V", "then": { - "en": "
Type 2 with cable (mennekes)
outputs 230 volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" + "en": "
Type 2 with cable (mennekes)
outputs 230 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" } }, { "if": "socket:socket:type2_cable:voltage=400 V", "then": { - "en": "
Type 2 with cable (mennekes)
outputs 400 volt", - "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" + "en": "
Type 2 with cable (mennekes)
outputs 400 volt", + "nl": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" } } ], @@ -1957,12 +1957,12 @@ { "id": "current-9", "question": { - "en": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", - "nl": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?" + "en": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", + "nl": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?" }, "render": { - "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" }, "freeform": { "key": "socket:type2_cable:current", @@ -1972,15 +1972,15 @@ { "if": "socket:socket:type2_cable:current=16 A", "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 16 A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" + "en": "
Type 2 with cable (mennekes)
outputs at most 16 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" } }, { "if": "socket:socket:type2_cable:current=32 A", "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 32 A", - "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" + "en": "
Type 2 with cable (mennekes)
outputs at most 32 A", + "nl": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" } } ], @@ -1994,12 +1994,12 @@ { "id": "power-output-9", "question": { - "en": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?" + "en": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?" }, "render": { - "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" + "en": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" }, "freeform": { "key": "socket:type2_cable:output", @@ -2009,15 +2009,15 @@ { "if": "socket:socket:type2_cable:output=11 kw", "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 11 kw", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" + "en": "
Type 2 with cable (mennekes)
outputs at most 11 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" } }, { "if": "socket:socket:type2_cable:output=22 kw", "then": { - "en": "
Type 2 with cable (mennekes)
outputs at most 22 kw", - "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" + "en": "
Type 2 with cable (mennekes)
outputs at most 22 kw", + "nl": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" } } ], @@ -2031,12 +2031,12 @@ { "id": "plugs-10", "question": { - "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Tesla Supercharger CCS (a branded type2_css)
plugs of type Tesla Supercharger CCS (a branded type2_css) available here", - "nl": "Hier zijn
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + "en": "There are
Tesla Supercharger CCS (a branded type2_css)
plugs of type Tesla Supercharger CCS (a branded type2_css) available here", + "nl": "Hier zijn
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" }, "freeform": { "key": "socket:tesla_supercharger_ccs", @@ -2052,12 +2052,12 @@ { "id": "voltage-10", "question": { - "en": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + "en": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" }, "freeform": { "key": "socket:tesla_supercharger_ccs:voltage", @@ -2067,15 +2067,15 @@ { "if": "socket:socket:tesla_supercharger_ccs:voltage=500 V", "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" } }, { "if": "socket:socket:tesla_supercharger_ccs:voltage=920 V", "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" } } ], @@ -2089,12 +2089,12 @@ { "id": "current-10", "question": { - "en": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + "en": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" }, "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" }, "freeform": { "key": "socket:tesla_supercharger_ccs:current", @@ -2104,15 +2104,15 @@ { "if": "socket:socket:tesla_supercharger_ccs:current=125 A", "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:tesla_supercharger_ccs:current=350 A", "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" } } ], @@ -2126,12 +2126,12 @@ { "id": "power-output-10", "question": { - "en": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" + "en": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?" }, "render": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" }, "freeform": { "key": "socket:tesla_supercharger_ccs:output", @@ -2141,8 +2141,8 @@ { "if": "socket:socket:tesla_supercharger_ccs:output=50 kw", "then": { - "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw", - "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" + "en": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw", + "nl": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" } } ], @@ -2156,12 +2156,12 @@ { "id": "plugs-11", "question": { - "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Tesla Supercharger (destination)
plugs of type Tesla Supercharger (destination) available here", - "nl": "Hier zijn
Tesla Supercharger (destination)
stekkers van het type Tesla Supercharger (destination)" + "en": "There are
Tesla Supercharger (destination)
plugs of type Tesla Supercharger (destination) available here", + "nl": "Hier zijn
Tesla Supercharger (destination)
stekkers van het type Tesla Supercharger (destination)" }, "freeform": { "key": "socket:tesla_destination", @@ -2177,12 +2177,12 @@ { "id": "voltage-11", "question": { - "en": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
" + "en": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
" }, "render": { - "en": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt", - "nl": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" + "en": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" }, "freeform": { "key": "socket:tesla_destination:voltage", @@ -2192,8 +2192,8 @@ { "if": "socket:socket:tesla_destination:voltage=480 V", "then": { - "en": "
Tesla Supercharger (destination)
outputs 480 volt", - "nl": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" + "en": "
Tesla Supercharger (destination)
outputs 480 volt", + "nl": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" } } ], @@ -2207,12 +2207,12 @@ { "id": "current-11", "question": { - "en": "What current do the plugs with
Tesla Supercharger (destination)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?" + "en": "What current do the plugs with
Tesla Supercharger (destination)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?" }, "render": { - "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" }, "freeform": { "key": "socket:tesla_destination:current", @@ -2222,15 +2222,15 @@ { "if": "socket:socket:tesla_destination:current=125 A", "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 125 A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" + "en": "
Tesla Supercharger (destination)
outputs at most 125 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" } }, { "if": "socket:socket:tesla_destination:current=350 A", "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 350 A", - "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" + "en": "
Tesla Supercharger (destination)
outputs at most 350 A", + "nl": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" } } ], @@ -2244,12 +2244,12 @@ { "id": "power-output-11", "question": { - "en": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?" + "en": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?" }, "render": { - "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" + "en": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" }, "freeform": { "key": "socket:tesla_destination:output", @@ -2259,22 +2259,22 @@ { "if": "socket:socket:tesla_destination:output=120 kw", "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 120 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" + "en": "
Tesla Supercharger (destination)
outputs at most 120 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" } }, { "if": "socket:socket:tesla_destination:output=150 kw", "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 150 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" + "en": "
Tesla Supercharger (destination)
outputs at most 150 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" } }, { "if": "socket:socket:tesla_destination:output=250 kw", "then": { - "en": "
Tesla Supercharger (destination)
outputs at most 250 kw", - "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" + "en": "
Tesla Supercharger (destination)
outputs at most 250 kw", + "nl": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" } } ], @@ -2288,12 +2288,12 @@ { "id": "plugs-12", "question": { - "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", - "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" + "en": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", - "nl": "Hier zijn
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + "en": "There are
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", + "nl": "Hier zijn
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" }, "freeform": { "key": "socket:tesla_destination", @@ -2309,12 +2309,12 @@ { "id": "voltage-12", "question": { - "en": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + "en": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" }, "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" }, "freeform": { "key": "socket:tesla_destination:voltage", @@ -2324,15 +2324,15 @@ { "if": "socket:socket:tesla_destination:voltage=230 V", "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" } }, { "if": "socket:socket:tesla_destination:voltage=400 V", "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" } } ], @@ -2346,12 +2346,12 @@ { "id": "current-12", "question": { - "en": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + "en": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" }, "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" }, "freeform": { "key": "socket:tesla_destination:current", @@ -2361,15 +2361,15 @@ { "if": "socket:socket:tesla_destination:current=16 A", "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" } }, { "if": "socket:socket:tesla_destination:current=32 A", "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" } } ], @@ -2383,12 +2383,12 @@ { "id": "power-output-12", "question": { - "en": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" + "en": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?" }, "render": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" }, "freeform": { "key": "socket:tesla_destination:output", @@ -2398,15 +2398,15 @@ { "if": "socket:socket:tesla_destination:output=11 kw", "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" } }, { "if": "socket:socket:tesla_destination:output=22 kw", "then": { - "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw", - "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" + "en": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw", + "nl": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" } } ], @@ -2420,12 +2420,12 @@ { "id": "plugs-13", "question": { - "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", - "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" + "en": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" }, "render": { - "en": "There are
USB to charge phones and small electronics
plugs of type USB to charge phones and small electronics available here", - "nl": "Hier zijn
USB om GSMs en kleine electronica op te laden
stekkers van het type USB om GSMs en kleine electronica op te laden" + "en": "There are
USB to charge phones and small electronics
plugs of type USB to charge phones and small electronics available here", + "nl": "Hier zijn
USB om GSMs en kleine electronica op te laden
stekkers van het type USB om GSMs en kleine electronica op te laden" }, "freeform": { "key": "socket:USB-A", @@ -2441,12 +2441,12 @@ { "id": "voltage-13", "question": { - "en": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", - "nl": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
" + "en": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
" }, "render": { - "en": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt", - "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" + "en": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" }, "freeform": { "key": "socket:USB-A:voltage", @@ -2456,8 +2456,8 @@ { "if": "socket:socket:USB-A:voltage=5 V", "then": { - "en": "
USB to charge phones and small electronics
outputs 5 volt", - "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" + "en": "
USB to charge phones and small electronics
outputs 5 volt", + "nl": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" } } ], @@ -2471,12 +2471,12 @@ { "id": "current-13", "question": { - "en": "What current do the plugs with
USB to charge phones and small electronics
offer?", - "nl": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?" + "en": "What current do the plugs with
USB to charge phones and small electronics
offer?", + "nl": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?" }, "render": { - "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" }, "freeform": { "key": "socket:USB-A:current", @@ -2486,15 +2486,15 @@ { "if": "socket:socket:USB-A:current=1 A", "then": { - "en": "
USB to charge phones and small electronics
outputs at most 1 A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" + "en": "
USB to charge phones and small electronics
outputs at most 1 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" } }, { "if": "socket:socket:USB-A:current=2 A", "then": { - "en": "
USB to charge phones and small electronics
outputs at most 2 A", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" + "en": "
USB to charge phones and small electronics
outputs at most 2 A", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" } } ], @@ -2508,12 +2508,12 @@ { "id": "power-output-13", "question": { - "en": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", - "nl": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?" + "en": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?" }, "render": { - "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" + "en": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" }, "freeform": { "key": "socket:USB-A:output", @@ -2523,15 +2523,15 @@ { "if": "socket:socket:USB-A:output=5w", "then": { - "en": "
USB to charge phones and small electronics
outputs at most 5w", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" + "en": "
USB to charge phones and small electronics
outputs at most 5w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" } }, { "if": "socket:socket:USB-A:output=10w", "then": { - "en": "
USB to charge phones and small electronics
outputs at most 10w", - "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" + "en": "
USB to charge phones and small electronics
outputs at most 10w", + "nl": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" } } ], @@ -3102,99 +3102,99 @@ }, { "question": { - "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", - "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" + "en": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector", + "nl": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "osmTags": "socket:schuko~*" }, { "question": { - "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", - "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" + "en": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector", + "nl": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" }, "osmTags": "socket:typee~*" }, { "question": { - "en": "Has a
Chademo
connector", - "nl": "Heeft een
Chademo
" + "en": "Has a
Chademo
connector", + "nl": "Heeft een
Chademo
" }, "osmTags": "socket:chademo~*" }, { "question": { - "en": "Has a
Type 1 with cable (J1772)
connector", - "nl": "Heeft een
Type 1 met kabel (J1772)
" + "en": "Has a
Type 1 with cable (J1772)
connector", + "nl": "Heeft een
Type 1 met kabel (J1772)
" }, "osmTags": "socket:type1_cable~*" }, { "question": { - "en": "Has a
Type 1 without cable (J1772)
connector", - "nl": "Heeft een
Type 1 zonder kabel (J1772)
" + "en": "Has a
Type 1 without cable (J1772)
connector", + "nl": "Heeft een
Type 1 zonder kabel (J1772)
" }, "osmTags": "socket:type1~*" }, { "question": { - "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", - "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" + "en": "Has a
Type 1 CCS (aka Type 1 Combo)
connector", + "nl": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "osmTags": "socket:type1_combo~*" }, { "question": { - "en": "Has a
Tesla Supercharger
connector", - "nl": "Heeft een
Tesla Supercharger
" + "en": "Has a
Tesla Supercharger
connector", + "nl": "Heeft een
Tesla Supercharger
" }, "osmTags": "socket:tesla_supercharger~*" }, { "question": { - "en": "Has a
Type 2 (mennekes)
connector", - "nl": "Heeft een
Type 2 (mennekes)
" + "en": "Has a
Type 2 (mennekes)
connector", + "nl": "Heeft een
Type 2 (mennekes)
" }, "osmTags": "socket:type2~*" }, { "question": { - "en": "Has a
Type 2 CCS (mennekes)
connector", - "nl": "Heeft een
Type 2 CCS (mennekes)
" + "en": "Has a
Type 2 CCS (mennekes)
connector", + "nl": "Heeft een
Type 2 CCS (mennekes)
" }, "osmTags": "socket:type2_combo~*" }, { "question": { - "en": "Has a
Type 2 with cable (mennekes)
connector", - "nl": "Heeft een
Type 2 met kabel (J1772)
" + "en": "Has a
Type 2 with cable (mennekes)
connector", + "nl": "Heeft een
Type 2 met kabel (J1772)
" }, "osmTags": "socket:type2_cable~*" }, { "question": { - "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", - "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" + "en": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector", + "nl": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "osmTags": "socket:tesla_supercharger_ccs~*" }, { "question": { - "en": "Has a
Tesla Supercharger (destination)
connector", - "nl": "Heeft een
Tesla Supercharger (destination)
" + "en": "Has a
Tesla Supercharger (destination)
connector", + "nl": "Heeft een
Tesla Supercharger (destination)
" }, "osmTags": "socket:tesla_destination~*" }, { "question": { - "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", - "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" + "en": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector", + "nl": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" }, "osmTags": "socket:tesla_destination~*" }, { "question": { - "en": "Has a
USB to charge phones and small electronics
connector", - "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" + "en": "Has a
USB to charge phones and small electronics
connector", + "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" }, "osmTags": "socket:USB-A~*" } diff --git a/assets/layers/charging_station/csvToJson.ts b/assets/layers/charging_station/csvToJson.ts index 9c529883f..739c163b4 100644 --- a/assets/layers/charging_station/csvToJson.ts +++ b/assets/layers/charging_station/csvToJson.ts @@ -127,8 +127,8 @@ function run(file, protojson) { } overview_question_answers.push(no_ask_json) - const descrWithImage_en = `
${e.description.get("en")}
` - const descrWithImage_nl = `
${e.description.get("nl")}
` + const descrWithImage_en = `
${e.description.get("en")}
` + const descrWithImage_nl = `
${e.description.get("nl")}
` questions.push({ "id": "plugs-" + i, From ee70fc4caa43be90995239506c619781b3e6e35e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 5 Oct 2021 14:37:40 +0200 Subject: [PATCH 106/110] Translation reset --- langs/layers/en.json | 402 +++++++++++++++++++++--------------------- langs/layers/nl.json | 408 +++++++++++++++++++++---------------------- 2 files changed, 405 insertions(+), 405 deletions(-) diff --git a/langs/layers/en.json b/langs/layers/en.json index bb4b25dce..2a9ab0bce 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -882,46 +882,46 @@ "question": "All connectors" }, "1": { - "question": "Has a Schuko wall plug without ground pin (CEE7/4 type F) connector" + "question": "Has a
Schuko wall plug without ground pin (CEE7/4 type F)
connector" }, "2": { - "question": "Has a European wall plug with ground pin (CEE7/4 type E) connector" + "question": "Has a
European wall plug with ground pin (CEE7/4 type E)
connector" }, "3": { - "question": "Has a Chademo connector" + "question": "Has a
Chademo
connector" }, "4": { - "question": "Has a Type 1 with cable (J1772) connector" + "question": "Has a
Type 1 with cable (J1772)
connector" }, "5": { - "question": "Has a Type 1 without cable (J1772) connector" + "question": "Has a
Type 1 without cable (J1772)
connector" }, "6": { - "question": "Has a Type 1 CCS (aka Type 1 Combo) connector" + "question": "Has a
Type 1 CCS (aka Type 1 Combo)
connector" }, "7": { - "question": "Has a Tesla Supercharger connector" + "question": "Has a
Tesla Supercharger
connector" }, "8": { - "question": "Has a Type 2 (mennekes) connector" + "question": "Has a
Type 2 (mennekes)
connector" }, "9": { - "question": "Has a Type 2 CCS (mennekes) connector" + "question": "Has a
Type 2 CCS (mennekes)
connector" }, "10": { - "question": "Has a Type 2 with cable (mennekes) connector" + "question": "Has a
Type 2 with cable (mennekes)
connector" }, "11": { - "question": "Has a Tesla Supercharger CCS (a branded type2_css) connector" + "question": "Has a
Tesla Supercharger CCS (a branded type2_css)
connector" }, "12": { - "question": "Has a Tesla Supercharger (destination) connector" + "question": "Has a
Tesla Supercharger (destination)
connector" }, "13": { - "question": "Has a Tesla supercharger (destination (A Type 2 with cable branded as tesla) connector" + "question": "Has a
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
connector" }, "14": { - "question": "Has a USB to charge phones and small electronics connector" + "question": "Has a
USB to charge phones and small electronics
connector" } } } @@ -1146,155 +1146,155 @@ "current-0": { "mappings": { "0": { - "then": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 16 A" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 16 A" } }, - "question": "What current do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "render": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:current}A" + "question": "What current do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "render": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:current}A" }, "current-1": { "mappings": { "0": { - "then": "European wall plug with ground pin (CEE7/4 type E) outputs at most 16 A" + "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 16 A" } }, - "question": "What current do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "render": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:current}A" + "question": "What current do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "render": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:current}A" }, "current-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs at most 125 A" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 125 A" }, "1": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs at most 350 A" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 350 A" } }, - "question": "What current do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "render": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:current}A" + "question": "What current do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "render": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:current}A" }, "current-11": { "mappings": { "0": { - "then": "Tesla Supercharger (destination) outputs at most 125 A" + "then": "
Tesla Supercharger (destination)
outputs at most 125 A" }, "1": { - "then": "Tesla Supercharger (destination) outputs at most 350 A" + "then": "
Tesla Supercharger (destination)
outputs at most 350 A" } }, - "question": "What current do the plugs with Tesla Supercharger (destination) offer?", - "render": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:current}A" + "question": "What current do the plugs with
Tesla Supercharger (destination)
offer?", + "render": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:current}A" }, "current-12": { "mappings": { "0": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 16 A" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 16 A" }, "1": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 32 A" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 32 A" } }, - "question": "What current do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "render": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:current}A" + "question": "What current do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "render": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:current}A" }, "current-13": { "mappings": { "0": { - "then": "USB to charge phones and small electronics outputs at most 1 A" + "then": "
USB to charge phones and small electronics
outputs at most 1 A" }, "1": { - "then": "USB to charge phones and small electronics outputs at most 2 A" + "then": "
USB to charge phones and small electronics
outputs at most 2 A" } }, - "question": "What current do the plugs with USB to charge phones and small electronics offer?", - "render": "USB to charge phones and small electronics outputs at most {socket:USB-A:current}A" + "question": "What current do the plugs with
USB to charge phones and small electronics
offer?", + "render": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A" }, "current-2": { "mappings": { "0": { - "then": "Chademo outputs at most 120 A" + "then": "
Chademo
outputs at most 120 A" } }, - "question": "What current do the plugs with Chademo offer?", - "render": "Chademo outputs at most {socket:chademo:current}A" + "question": "What current do the plugs with
Chademo
offer?", + "render": "
Chademo
outputs at most {socket:chademo:current}A" }, "current-3": { "mappings": { "0": { - "then": "Type 1 with cable (J1772) outputs at most 32 A" + "then": "
Type 1 with cable (J1772)
outputs at most 32 A" } }, - "question": "What current do the plugs with Type 1 with cable (J1772) offer?", - "render": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:current}A" + "question": "What current do the plugs with
Type 1 with cable (J1772)
offer?", + "render": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:current}A" }, "current-4": { "mappings": { "0": { - "then": "Type 1 without cable (J1772) outputs at most 32 A" + "then": "
Type 1 without cable (J1772)
outputs at most 32 A" } }, - "question": "What current do the plugs with Type 1 without cable (J1772) offer?", - "render": "Type 1 without cable (J1772) outputs at most {socket:type1:current}A" + "question": "What current do the plugs with
Type 1 without cable (J1772)
offer?", + "render": "
Type 1 without cable (J1772)
outputs at most {socket:type1:current}A" }, "current-5": { "mappings": { "0": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 A" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 A" }, "1": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 125 A" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 125 A" } }, - "question": "What current do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "render": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:current}A" + "question": "What current do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "render": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:current}A" }, "current-6": { "mappings": { "0": { - "then": "Tesla Supercharger outputs at most 125 A" + "then": "
Tesla Supercharger
outputs at most 125 A" }, "1": { - "then": "Tesla Supercharger outputs at most 350 A" + "then": "
Tesla Supercharger
outputs at most 350 A" } }, - "question": "What current do the plugs with Tesla Supercharger offer?", - "render": "Tesla Supercharger outputs at most {socket:tesla_supercharger:current}A" + "question": "What current do the plugs with
Tesla Supercharger
offer?", + "render": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:current}A" }, "current-7": { "mappings": { "0": { - "then": "Type 2 (mennekes) outputs at most 16 A" + "then": "
Type 2 (mennekes)
outputs at most 16 A" }, "1": { - "then": "Type 2 (mennekes) outputs at most 32 A" + "then": "
Type 2 (mennekes)
outputs at most 32 A" } }, - "question": "What current do the plugs with Type 2 (mennekes) offer?", - "render": "Type 2 (mennekes) outputs at most {socket:type2:current}A" + "question": "What current do the plugs with
Type 2 (mennekes)
offer?", + "render": "
Type 2 (mennekes)
outputs at most {socket:type2:current}A" }, "current-8": { "mappings": { "0": { - "then": "Type 2 CCS (mennekes) outputs at most 125 A" + "then": "
Type 2 CCS (mennekes)
outputs at most 125 A" }, "1": { - "then": "Type 2 CCS (mennekes) outputs at most 350 A" + "then": "
Type 2 CCS (mennekes)
outputs at most 350 A" } }, - "question": "What current do the plugs with Type 2 CCS (mennekes) offer?", - "render": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:current}A" + "question": "What current do the plugs with
Type 2 CCS (mennekes)
offer?", + "render": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:current}A" }, "current-9": { "mappings": { "0": { - "then": "Type 2 with cable (mennekes) outputs at most 16 A" + "then": "
Type 2 with cable (mennekes)
outputs at most 16 A" }, "1": { - "then": "Type 2 with cable (mennekes) outputs at most 32 A" + "then": "
Type 2 with cable (mennekes)
outputs at most 32 A" } }, - "question": "What current do the plugs with Type 2 with cable (mennekes) offer?", - "render": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:current}A" + "question": "What current do the plugs with
Type 2 with cable (mennekes)
offer?", + "render": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:current}A" }, "email": { "question": "What is the email address of the operator?", @@ -1335,234 +1335,234 @@ "render": "In case of problems, call {phone}" }, "plugs-0": { - "question": "How much plugs of type Schuko wall plug without ground pin (CEE7/4 type F) are available here?", - "render": "There are Schuko wall plug without ground pin (CEE7/4 type F) plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here" + "question": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", + "render": "There are
Schuko wall plug without ground pin (CEE7/4 type F)
plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here" }, "plugs-1": { - "question": "How much plugs of type European wall plug with ground pin (CEE7/4 type E) are available here?", - "render": "There are European wall plug with ground pin (CEE7/4 type E) plugs of type European wall plug with ground pin (CEE7/4 type E) available here" + "question": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", + "render": "There are
European wall plug with ground pin (CEE7/4 type E)
plugs of type European wall plug with ground pin (CEE7/4 type E) available here" }, "plugs-10": { - "question": "How much plugs of type Tesla Supercharger CCS (a branded type2_css) are available here?", - "render": "There are Tesla Supercharger CCS (a branded type2_css) plugs of type Tesla Supercharger CCS (a branded type2_css) available here" + "question": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", + "render": "There are
Tesla Supercharger CCS (a branded type2_css)
plugs of type Tesla Supercharger CCS (a branded type2_css) available here" }, "plugs-11": { - "question": "How much plugs of type Tesla Supercharger (destination) are available here?", - "render": "There are Tesla Supercharger (destination) plugs of type Tesla Supercharger (destination) available here" + "question": "How much plugs of type
Tesla Supercharger (destination)
are available here?", + "render": "There are
Tesla Supercharger (destination)
plugs of type Tesla Supercharger (destination) available here" }, "plugs-12": { - "question": "How much plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) are available here?", - "render": "There are Tesla supercharger (destination (A Type 2 with cable branded as tesla) plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here" + "question": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", + "render": "There are
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here" }, "plugs-13": { - "question": "How much plugs of type USB to charge phones and small electronics are available here?", - "render": "There are USB to charge phones and small electronics plugs of type USB to charge phones and small electronics available here" + "question": "How much plugs of type
USB to charge phones and small electronics
are available here?", + "render": "There are
USB to charge phones and small electronics
plugs of type USB to charge phones and small electronics available here" }, "plugs-2": { - "question": "How much plugs of type Chademo are available here?", - "render": "There are Chademo plugs of type Chademo available here" + "question": "How much plugs of type
Chademo
are available here?", + "render": "There are
Chademo
plugs of type Chademo available here" }, "plugs-3": { - "question": "How much plugs of type Type 1 with cable (J1772) are available here?", - "render": "There are Type 1 with cable (J1772) plugs of type Type 1 with cable (J1772) available here" + "question": "How much plugs of type
Type 1 with cable (J1772)
are available here?", + "render": "There are
Type 1 with cable (J1772)
plugs of type Type 1 with cable (J1772) available here" }, "plugs-4": { - "question": "How much plugs of type Type 1 without cable (J1772) are available here?", - "render": "There are Type 1 without cable (J1772) plugs of type Type 1 without cable (J1772) available here" + "question": "How much plugs of type
Type 1 without cable (J1772)
are available here?", + "render": "There are
Type 1 without cable (J1772)
plugs of type Type 1 without cable (J1772) available here" }, "plugs-5": { - "question": "How much plugs of type Type 1 CCS (aka Type 1 Combo) are available here?", - "render": "There are Type 1 CCS (aka Type 1 Combo) plugs of type Type 1 CCS (aka Type 1 Combo) available here" + "question": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", + "render": "There are
Type 1 CCS (aka Type 1 Combo)
plugs of type Type 1 CCS (aka Type 1 Combo) available here" }, "plugs-6": { - "question": "How much plugs of type Tesla Supercharger are available here?", - "render": "There are Tesla Supercharger plugs of type Tesla Supercharger available here" + "question": "How much plugs of type
Tesla Supercharger
are available here?", + "render": "There are
Tesla Supercharger
plugs of type Tesla Supercharger available here" }, "plugs-7": { - "question": "How much plugs of type Type 2 (mennekes) are available here?", - "render": "There are Type 2 (mennekes) plugs of type Type 2 (mennekes) available here" + "question": "How much plugs of type
Type 2 (mennekes)
are available here?", + "render": "There are
Type 2 (mennekes)
plugs of type Type 2 (mennekes) available here" }, "plugs-8": { - "question": "How much plugs of type Type 2 CCS (mennekes) are available here?", - "render": "There are Type 2 CCS (mennekes) plugs of type Type 2 CCS (mennekes) available here" + "question": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", + "render": "There are
Type 2 CCS (mennekes)
plugs of type Type 2 CCS (mennekes) available here" }, "plugs-9": { - "question": "How much plugs of type Type 2 with cable (mennekes) are available here?", - "render": "There are Type 2 with cable (mennekes) plugs of type Type 2 with cable (mennekes) available here" + "question": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", + "render": "There are
Type 2 with cable (mennekes)
plugs of type Type 2 with cable (mennekes) available here" }, "power-output-0": { "mappings": { "0": { - "then": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most 3.6 kw" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most 3.6 kw" } }, - "question": "What power output does a single plug of type Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "render": "Schuko wall plug without ground pin (CEE7/4 type F) outputs at most {socket:schuko:output}" + "question": "What power output does a single plug of type
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "render": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs at most {socket:schuko:output}" }, "power-output-1": { "mappings": { "0": { - "then": "European wall plug with ground pin (CEE7/4 type E) outputs at most 3 kw" + "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 3 kw" }, "1": { - "then": "European wall plug with ground pin (CEE7/4 type E) outputs at most 22 kw" + "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most 22 kw" } }, - "question": "What power output does a single plug of type European wall plug with ground pin (CEE7/4 type E) offer?", - "render": "European wall plug with ground pin (CEE7/4 type E) outputs at most {socket:typee:output}" + "question": "What power output does a single plug of type
European wall plug with ground pin (CEE7/4 type E)
offer?", + "render": "
European wall plug with ground pin (CEE7/4 type E)
outputs at most {socket:typee:output}" }, "power-output-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs at most 50 kw" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most 50 kw" } }, - "question": "What power output does a single plug of type Tesla Supercharger CCS (a branded type2_css) offer?", - "render": "Tesla Supercharger CCS (a branded type2_css) outputs at most {socket:tesla_supercharger_ccs:output}" + "question": "What power output does a single plug of type
Tesla Supercharger CCS (a branded type2_css)
offer?", + "render": "
Tesla Supercharger CCS (a branded type2_css)
outputs at most {socket:tesla_supercharger_ccs:output}" }, "power-output-11": { "mappings": { "0": { - "then": "Tesla Supercharger (destination) outputs at most 120 kw" + "then": "
Tesla Supercharger (destination)
outputs at most 120 kw" }, "1": { - "then": "Tesla Supercharger (destination) outputs at most 150 kw" + "then": "
Tesla Supercharger (destination)
outputs at most 150 kw" }, "2": { - "then": "Tesla Supercharger (destination) outputs at most 250 kw" + "then": "
Tesla Supercharger (destination)
outputs at most 250 kw" } }, - "question": "What power output does a single plug of type Tesla Supercharger (destination) offer?", - "render": "Tesla Supercharger (destination) outputs at most {socket:tesla_destination:output}" + "question": "What power output does a single plug of type
Tesla Supercharger (destination)
offer?", + "render": "
Tesla Supercharger (destination)
outputs at most {socket:tesla_destination:output}" }, "power-output-12": { "mappings": { "0": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 11 kw" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 11 kw" }, "1": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most 22 kw" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most 22 kw" } }, - "question": "What power output does a single plug of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "render": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs at most {socket:tesla_destination:output}" + "question": "What power output does a single plug of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "render": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs at most {socket:tesla_destination:output}" }, "power-output-13": { "mappings": { "0": { - "then": "USB to charge phones and small electronics outputs at most 5w" + "then": "
USB to charge phones and small electronics
outputs at most 5w" }, "1": { - "then": "USB to charge phones and small electronics outputs at most 10w" + "then": "
USB to charge phones and small electronics
outputs at most 10w" } }, - "question": "What power output does a single plug of type USB to charge phones and small electronics offer?", - "render": "USB to charge phones and small electronics outputs at most {socket:USB-A:output}" + "question": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", + "render": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}" }, "power-output-2": { "mappings": { "0": { - "then": "Chademo outputs at most 50 kw" + "then": "
Chademo
outputs at most 50 kw" } }, - "question": "What power output does a single plug of type Chademo offer?", - "render": "Chademo outputs at most {socket:chademo:output}" + "question": "What power output does a single plug of type
Chademo
offer?", + "render": "
Chademo
outputs at most {socket:chademo:output}" }, "power-output-3": { "mappings": { "0": { - "then": "Type 1 with cable (J1772) outputs at most 3.7 kw" + "then": "
Type 1 with cable (J1772)
outputs at most 3.7 kw" }, "1": { - "then": "Type 1 with cable (J1772) outputs at most 7 kw" + "then": "
Type 1 with cable (J1772)
outputs at most 7 kw" } }, - "question": "What power output does a single plug of type Type 1 with cable (J1772) offer?", - "render": "Type 1 with cable (J1772) outputs at most {socket:type1_cable:output}" + "question": "What power output does a single plug of type
Type 1 with cable (J1772)
offer?", + "render": "
Type 1 with cable (J1772)
outputs at most {socket:type1_cable:output}" }, "power-output-4": { "mappings": { "0": { - "then": "Type 1 without cable (J1772) outputs at most 3.7 kw" + "then": "
Type 1 without cable (J1772)
outputs at most 3.7 kw" }, "1": { - "then": "Type 1 without cable (J1772) outputs at most 6.6 kw" + "then": "
Type 1 without cable (J1772)
outputs at most 6.6 kw" }, "2": { - "then": "Type 1 without cable (J1772) outputs at most 7 kw" + "then": "
Type 1 without cable (J1772)
outputs at most 7 kw" }, "3": { - "then": "Type 1 without cable (J1772) outputs at most 7.2 kw" + "then": "
Type 1 without cable (J1772)
outputs at most 7.2 kw" } }, - "question": "What power output does a single plug of type Type 1 without cable (J1772) offer?", - "render": "Type 1 without cable (J1772) outputs at most {socket:type1:output}" + "question": "What power output does a single plug of type
Type 1 without cable (J1772)
offer?", + "render": "
Type 1 without cable (J1772)
outputs at most {socket:type1:output}" }, "power-output-5": { "mappings": { "0": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 50 kw" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 50 kw" }, "1": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 62.5 kw" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 62.5 kw" }, "2": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 150 kw" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 150 kw" }, "3": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs at most 350 kw" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs at most 350 kw" } }, - "question": "What power output does a single plug of type Type 1 CCS (aka Type 1 Combo) offer?", - "render": "Type 1 CCS (aka Type 1 Combo) outputs at most {socket:type1_combo:output}" + "question": "What power output does a single plug of type
Type 1 CCS (aka Type 1 Combo)
offer?", + "render": "
Type 1 CCS (aka Type 1 Combo)
outputs at most {socket:type1_combo:output}" }, "power-output-6": { "mappings": { "0": { - "then": "Tesla Supercharger outputs at most 120 kw" + "then": "
Tesla Supercharger
outputs at most 120 kw" }, "1": { - "then": "Tesla Supercharger outputs at most 150 kw" + "then": "
Tesla Supercharger
outputs at most 150 kw" }, "2": { - "then": "Tesla Supercharger outputs at most 250 kw" + "then": "
Tesla Supercharger
outputs at most 250 kw" } }, - "question": "What power output does a single plug of type Tesla Supercharger offer?", - "render": "Tesla Supercharger outputs at most {socket:tesla_supercharger:output}" + "question": "What power output does a single plug of type
Tesla Supercharger
offer?", + "render": "
Tesla Supercharger
outputs at most {socket:tesla_supercharger:output}" }, "power-output-7": { "mappings": { "0": { - "then": "Type 2 (mennekes) outputs at most 11 kw" + "then": "
Type 2 (mennekes)
outputs at most 11 kw" }, "1": { - "then": "Type 2 (mennekes) outputs at most 22 kw" + "then": "
Type 2 (mennekes)
outputs at most 22 kw" } }, - "question": "What power output does a single plug of type Type 2 (mennekes) offer?", - "render": "Type 2 (mennekes) outputs at most {socket:type2:output}" + "question": "What power output does a single plug of type
Type 2 (mennekes)
offer?", + "render": "
Type 2 (mennekes)
outputs at most {socket:type2:output}" }, "power-output-8": { "mappings": { "0": { - "then": "Type 2 CCS (mennekes) outputs at most 50 kw" + "then": "
Type 2 CCS (mennekes)
outputs at most 50 kw" } }, - "question": "What power output does a single plug of type Type 2 CCS (mennekes) offer?", - "render": "Type 2 CCS (mennekes) outputs at most {socket:type2_combo:output}" + "question": "What power output does a single plug of type
Type 2 CCS (mennekes)
offer?", + "render": "
Type 2 CCS (mennekes)
outputs at most {socket:type2_combo:output}" }, "power-output-9": { "mappings": { "0": { - "then": "Type 2 with cable (mennekes) outputs at most 11 kw" + "then": "
Type 2 with cable (mennekes)
outputs at most 11 kw" }, "1": { - "then": "Type 2 with cable (mennekes) outputs at most 22 kw" + "then": "
Type 2 with cable (mennekes)
outputs at most 22 kw" } }, - "question": "What power output does a single plug of type Type 2 with cable (mennekes) offer?", - "render": "Type 2 with cable (mennekes) outputs at most {socket:type2_cable:output}" + "question": "What power output does a single plug of type
Type 2 with cable (mennekes)
offer?", + "render": "
Type 2 with cable (mennekes)
outputs at most {socket:type2_cable:output}" }, "ref": { "question": "What is the reference number of this charging station?", @@ -1571,152 +1571,152 @@ "voltage-0": { "mappings": { "0": { - "then": "Schuko wall plug without ground pin (CEE7/4 type F) outputs 230 volt" + "then": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs 230 volt" } }, - "question": "What voltage do the plugs with Schuko wall plug without ground pin (CEE7/4 type F) offer?", - "render": "Schuko wall plug without ground pin (CEE7/4 type F) outputs {socket:schuko:voltage} volt" + "question": "What voltage do the plugs with
Schuko wall plug without ground pin (CEE7/4 type F)
offer?", + "render": "
Schuko wall plug without ground pin (CEE7/4 type F)
outputs {socket:schuko:voltage} volt" }, "voltage-1": { "mappings": { "0": { - "then": "European wall plug with ground pin (CEE7/4 type E) outputs 230 volt" + "then": "
European wall plug with ground pin (CEE7/4 type E)
outputs 230 volt" } }, - "question": "What voltage do the plugs with European wall plug with ground pin (CEE7/4 type E) offer?", - "render": "European wall plug with ground pin (CEE7/4 type E) outputs {socket:typee:voltage} volt" + "question": "What voltage do the plugs with
European wall plug with ground pin (CEE7/4 type E)
offer?", + "render": "
European wall plug with ground pin (CEE7/4 type E)
outputs {socket:typee:voltage} volt" }, "voltage-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs 500 volt" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs 500 volt" }, "1": { - "then": "Tesla Supercharger CCS (a branded type2_css) outputs 920 volt" + "then": "
Tesla Supercharger CCS (a branded type2_css)
outputs 920 volt" } }, - "question": "What voltage do the plugs with Tesla Supercharger CCS (a branded type2_css) offer?", - "render": "Tesla Supercharger CCS (a branded type2_css) outputs {socket:tesla_supercharger_ccs:voltage} volt" + "question": "What voltage do the plugs with
Tesla Supercharger CCS (a branded type2_css)
offer?", + "render": "
Tesla Supercharger CCS (a branded type2_css)
outputs {socket:tesla_supercharger_ccs:voltage} volt" }, "voltage-11": { "mappings": { "0": { - "then": "Tesla Supercharger (destination) outputs 480 volt" + "then": "
Tesla Supercharger (destination)
outputs 480 volt" } }, - "question": "What voltage do the plugs with Tesla Supercharger (destination) offer?", - "render": "Tesla Supercharger (destination) outputs {socket:tesla_destination:voltage} volt" + "question": "What voltage do the plugs with
Tesla Supercharger (destination)
offer?", + "render": "
Tesla Supercharger (destination)
outputs {socket:tesla_destination:voltage} volt" }, "voltage-12": { "mappings": { "0": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 230 volt" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 230 volt" }, "1": { - "then": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs 400 volt" + "then": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs 400 volt" } }, - "question": "What voltage do the plugs with Tesla supercharger (destination (A Type 2 with cable branded as tesla) offer?", - "render": "Tesla supercharger (destination (A Type 2 with cable branded as tesla) outputs {socket:tesla_destination:voltage} volt" + "question": "What voltage do the plugs with
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
offer?", + "render": "
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
outputs {socket:tesla_destination:voltage} volt" }, "voltage-13": { "mappings": { "0": { - "then": "USB to charge phones and small electronics outputs 5 volt" + "then": "
USB to charge phones and small electronics
outputs 5 volt" } }, - "question": "What voltage do the plugs with USB to charge phones and small electronics offer?", - "render": "USB to charge phones and small electronics outputs {socket:USB-A:voltage} volt" + "question": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", + "render": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt" }, "voltage-2": { "mappings": { "0": { - "then": "Chademo outputs 500 volt" + "then": "
Chademo
outputs 500 volt" } }, - "question": "What voltage do the plugs with Chademo offer?", - "render": "Chademo outputs {socket:chademo:voltage} volt" + "question": "What voltage do the plugs with
Chademo
offer?", + "render": "
Chademo
outputs {socket:chademo:voltage} volt" }, "voltage-3": { "mappings": { "0": { - "then": "Type 1 with cable (J1772) outputs 200 volt" + "then": "
Type 1 with cable (J1772)
outputs 200 volt" }, "1": { - "then": "Type 1 with cable (J1772) outputs 240 volt" + "then": "
Type 1 with cable (J1772)
outputs 240 volt" } }, - "question": "What voltage do the plugs with Type 1 with cable (J1772) offer?", - "render": "Type 1 with cable (J1772) outputs {socket:type1_cable:voltage} volt" + "question": "What voltage do the plugs with
Type 1 with cable (J1772)
offer?", + "render": "
Type 1 with cable (J1772)
outputs {socket:type1_cable:voltage} volt" }, "voltage-4": { "mappings": { "0": { - "then": "Type 1 without cable (J1772) outputs 200 volt" + "then": "
Type 1 without cable (J1772)
outputs 200 volt" }, "1": { - "then": "Type 1 without cable (J1772) outputs 240 volt" + "then": "
Type 1 without cable (J1772)
outputs 240 volt" } }, - "question": "What voltage do the plugs with Type 1 without cable (J1772) offer?", - "render": "Type 1 without cable (J1772) outputs {socket:type1:voltage} volt" + "question": "What voltage do the plugs with
Type 1 without cable (J1772)
offer?", + "render": "
Type 1 without cable (J1772)
outputs {socket:type1:voltage} volt" }, "voltage-5": { "mappings": { "0": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs 400 volt" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs 400 volt" }, "1": { - "then": "Type 1 CCS (aka Type 1 Combo) outputs 1000 volt" + "then": "
Type 1 CCS (aka Type 1 Combo)
outputs 1000 volt" } }, - "question": "What voltage do the plugs with Type 1 CCS (aka Type 1 Combo) offer?", - "render": "Type 1 CCS (aka Type 1 Combo) outputs {socket:type1_combo:voltage} volt" + "question": "What voltage do the plugs with
Type 1 CCS (aka Type 1 Combo)
offer?", + "render": "
Type 1 CCS (aka Type 1 Combo)
outputs {socket:type1_combo:voltage} volt" }, "voltage-6": { "mappings": { "0": { - "then": "Tesla Supercharger outputs 480 volt" + "then": "
Tesla Supercharger
outputs 480 volt" } }, - "question": "What voltage do the plugs with Tesla Supercharger offer?", - "render": "Tesla Supercharger outputs {socket:tesla_supercharger:voltage} volt" + "question": "What voltage do the plugs with
Tesla Supercharger
offer?", + "render": "
Tesla Supercharger
outputs {socket:tesla_supercharger:voltage} volt" }, "voltage-7": { "mappings": { "0": { - "then": "Type 2 (mennekes) outputs 230 volt" + "then": "
Type 2 (mennekes)
outputs 230 volt" }, "1": { - "then": "Type 2 (mennekes) outputs 400 volt" + "then": "
Type 2 (mennekes)
outputs 400 volt" } }, - "question": "What voltage do the plugs with Type 2 (mennekes) offer?", - "render": "Type 2 (mennekes) outputs {socket:type2:voltage} volt" + "question": "What voltage do the plugs with
Type 2 (mennekes)
offer?", + "render": "
Type 2 (mennekes)
outputs {socket:type2:voltage} volt" }, "voltage-8": { "mappings": { "0": { - "then": "Type 2 CCS (mennekes) outputs 500 volt" + "then": "
Type 2 CCS (mennekes)
outputs 500 volt" }, "1": { - "then": "Type 2 CCS (mennekes) outputs 920 volt" + "then": "
Type 2 CCS (mennekes)
outputs 920 volt" } }, - "question": "What voltage do the plugs with Type 2 CCS (mennekes) offer?", - "render": "Type 2 CCS (mennekes) outputs {socket:type2_combo:voltage} volt" + "question": "What voltage do the plugs with
Type 2 CCS (mennekes)
offer?", + "render": "
Type 2 CCS (mennekes)
outputs {socket:type2_combo:voltage} volt" }, "voltage-9": { "mappings": { "0": { - "then": "Type 2 with cable (mennekes) outputs 230 volt" + "then": "
Type 2 with cable (mennekes)
outputs 230 volt" }, "1": { - "then": "Type 2 with cable (mennekes) outputs 400 volt" + "then": "
Type 2 with cable (mennekes)
outputs 400 volt" } }, - "question": "What voltage do the plugs with Type 2 with cable (mennekes) offer?", - "render": "Type 2 with cable (mennekes) outputs {socket:type2_cable:voltage} volt" + "question": "What voltage do the plugs with
Type 2 with cable (mennekes)
offer?", + "render": "
Type 2 with cable (mennekes)
outputs {socket:type2_cable:voltage} volt" }, "website": { "question": "What is the website of the operator?", diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 003476b3c..7efa1a6d8 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -984,46 +984,46 @@ "question": "Alle types" }, "1": { - "question": "Heeft een Schuko stekker zonder aardingspin (CEE7/4 type F) " + "question": "Heeft een
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "2": { - "question": "Heeft een Europese stekker met aardingspin (CEE7/4 type E) " + "question": "Heeft een
Europese stekker met aardingspin (CEE7/4 type E)
" }, "3": { - "question": "Heeft een Chademo " + "question": "Heeft een
Chademo
" }, "4": { - "question": "Heeft een Type 1 met kabel (J1772) " + "question": "Heeft een
Type 1 met kabel (J1772)
" }, "5": { - "question": "Heeft een Type 1 zonder kabel (J1772) " + "question": "Heeft een
Type 1 zonder kabel (J1772)
" }, "6": { - "question": "Heeft een Type 1 CCS (ook gekend als Type 1 Combo) " + "question": "Heeft een
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "7": { - "question": "Heeft een Tesla Supercharger " + "question": "Heeft een
Tesla Supercharger
" }, "8": { - "question": "Heeft een Type 2 (mennekes) " + "question": "Heeft een
Type 2 (mennekes)
" }, "9": { - "question": "Heeft een Type 2 CCS (mennekes) " + "question": "Heeft een
Type 2 CCS (mennekes)
" }, "10": { - "question": "Heeft een Type 2 met kabel (J1772) " + "question": "Heeft een
Type 2 met kabel (J1772)
" }, "11": { - "question": "Heeft een Tesla Supercharger CCS (een type2 CCS met Tesla-logo) " + "question": "Heeft een
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "12": { - "question": "Heeft een Tesla Supercharger (destination) " + "question": "Heeft een
Tesla Supercharger (destination)
" }, "13": { - "question": "Heeft een Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) " + "question": "Heeft een
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" }, "14": { - "question": "Heeft een USB om GSMs en kleine electronica op te laden " + "question": "Heeft een
USB om GSMs en kleine electronica op te laden
" } } } @@ -1144,155 +1144,155 @@ "current-0": { "mappings": { "0": { - "then": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal 16 A" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal 16 A" } }, - "question": "Welke stroom levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?", - "render": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een stroom van maximaal {socket:schuko:current}A" + "question": "Welke stroom levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?", + "render": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een stroom van maximaal {socket:schuko:current}A" }, "current-1": { "mappings": { "0": { - "then": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal 16 A" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal 16 A" } }, - "question": "Welke stroom levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?", - "render": "Europese stekker met aardingspin (CEE7/4 type E) levert een stroom van maximaal {socket:typee:current}A" + "question": "Welke stroom levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?", + "render": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een stroom van maximaal {socket:typee:current}A" }, "current-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 125 A" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 125 A" }, "1": { - "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal 350 A" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?", - "render": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" + "question": "Welke stroom levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?", + "render": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een stroom van maximaal {socket:tesla_supercharger_ccs:current}A" }, "current-11": { "mappings": { "0": { - "then": "Tesla Supercharger (destination) levert een stroom van maximaal 125 A" + "then": "
Tesla Supercharger (destination)
levert een stroom van maximaal 125 A" }, "1": { - "then": "Tesla Supercharger (destination) levert een stroom van maximaal 350 A" + "then": "
Tesla Supercharger (destination)
levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type Tesla Supercharger (destination) ?", - "render": "Tesla Supercharger (destination) levert een stroom van maximaal {socket:tesla_destination:current}A" + "question": "Welke stroom levert de stekker van type
Tesla Supercharger (destination)
?", + "render": "
Tesla Supercharger (destination)
levert een stroom van maximaal {socket:tesla_destination:current}A" }, "current-12": { "mappings": { "0": { - "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 16 A" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 16 A" }, "1": { - "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal 32 A" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?", - "render": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een stroom van maximaal {socket:tesla_destination:current}A" + "question": "Welke stroom levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?", + "render": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een stroom van maximaal {socket:tesla_destination:current}A" }, "current-13": { "mappings": { "0": { - "then": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 1 A" + "then": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 1 A" }, "1": { - "then": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal 2 A" + "then": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal 2 A" } }, - "question": "Welke stroom levert de stekker van type USB om GSMs en kleine electronica op te laden ?", - "render": "USB om GSMs en kleine electronica op te laden levert een stroom van maximaal {socket:USB-A:current}A" + "question": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?", + "render": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" }, "current-2": { "mappings": { "0": { - "then": "Chademo levert een stroom van maximaal 120 A" + "then": "
Chademo
levert een stroom van maximaal 120 A" } }, - "question": "Welke stroom levert de stekker van type Chademo ?", - "render": "Chademo levert een stroom van maximaal {socket:chademo:current}A" + "question": "Welke stroom levert de stekker van type
Chademo
?", + "render": "
Chademo
levert een stroom van maximaal {socket:chademo:current}A" }, "current-3": { "mappings": { "0": { - "then": "Type 1 met kabel (J1772) levert een stroom van maximaal 32 A" + "then": "
Type 1 met kabel (J1772)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type Type 1 met kabel (J1772) ?", - "render": "Type 1 met kabel (J1772) levert een stroom van maximaal {socket:type1_cable:current}A" + "question": "Welke stroom levert de stekker van type
Type 1 met kabel (J1772)
?", + "render": "
Type 1 met kabel (J1772)
levert een stroom van maximaal {socket:type1_cable:current}A" }, "current-4": { "mappings": { "0": { - "then": "Type 1 zonder kabel (J1772) levert een stroom van maximaal 32 A" + "then": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type Type 1 zonder kabel (J1772) ?", - "render": "Type 1 zonder kabel (J1772) levert een stroom van maximaal {socket:type1:current}A" + "question": "Welke stroom levert de stekker van type
Type 1 zonder kabel (J1772)
?", + "render": "
Type 1 zonder kabel (J1772)
levert een stroom van maximaal {socket:type1:current}A" }, "current-5": { "mappings": { "0": { - "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 50 A" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 50 A" }, "1": { - "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal 125 A" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal 125 A" } }, - "question": "Welke stroom levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?", - "render": "Type 1 CCS (ook gekend als Type 1 Combo) levert een stroom van maximaal {socket:type1_combo:current}A" + "question": "Welke stroom levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?", + "render": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een stroom van maximaal {socket:type1_combo:current}A" }, "current-6": { "mappings": { "0": { - "then": "Tesla Supercharger levert een stroom van maximaal 125 A" + "then": "
Tesla Supercharger
levert een stroom van maximaal 125 A" }, "1": { - "then": "Tesla Supercharger levert een stroom van maximaal 350 A" + "then": "
Tesla Supercharger
levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type Tesla Supercharger ?", - "render": "Tesla Supercharger levert een stroom van maximaal {socket:tesla_supercharger:current}A" + "question": "Welke stroom levert de stekker van type
Tesla Supercharger
?", + "render": "
Tesla Supercharger
levert een stroom van maximaal {socket:tesla_supercharger:current}A" }, "current-7": { "mappings": { "0": { - "then": "Type 2 (mennekes) levert een stroom van maximaal 16 A" + "then": "
Type 2 (mennekes)
levert een stroom van maximaal 16 A" }, "1": { - "then": "Type 2 (mennekes) levert een stroom van maximaal 32 A" + "then": "
Type 2 (mennekes)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type Type 2 (mennekes) ?", - "render": "Type 2 (mennekes) levert een stroom van maximaal {socket:type2:current}A" + "question": "Welke stroom levert de stekker van type
Type 2 (mennekes)
?", + "render": "
Type 2 (mennekes)
levert een stroom van maximaal {socket:type2:current}A" }, "current-8": { "mappings": { "0": { - "then": "Type 2 CCS (mennekes) levert een stroom van maximaal 125 A" + "then": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 125 A" }, "1": { - "then": "Type 2 CCS (mennekes) levert een stroom van maximaal 350 A" + "then": "
Type 2 CCS (mennekes)
levert een stroom van maximaal 350 A" } }, - "question": "Welke stroom levert de stekker van type Type 2 CCS (mennekes) ?", - "render": "Type 2 CCS (mennekes) levert een stroom van maximaal {socket:type2_combo:current}A" + "question": "Welke stroom levert de stekker van type
Type 2 CCS (mennekes)
?", + "render": "
Type 2 CCS (mennekes)
levert een stroom van maximaal {socket:type2_combo:current}A" }, "current-9": { "mappings": { "0": { - "then": "Type 2 met kabel (J1772) levert een stroom van maximaal 16 A" + "then": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 16 A" }, "1": { - "then": "Type 2 met kabel (J1772) levert een stroom van maximaal 32 A" + "then": "
Type 2 met kabel (J1772)
levert een stroom van maximaal 32 A" } }, - "question": "Welke stroom levert de stekker van type Type 2 met kabel (J1772) ?", - "render": "Type 2 met kabel (J1772) levert een stroom van maximaal {socket:type2_cable:current}A" + "question": "Welke stroom levert de stekker van type
Type 2 met kabel (J1772)
?", + "render": "
Type 2 met kabel (J1772)
levert een stroom van maximaal {socket:type2_cable:current}A" }, "fee/charge": { "mappings": { @@ -1325,384 +1325,384 @@ } }, "plugs-0": { - "question": "Hoeveel stekkers van type Schuko stekker zonder aardingspin (CEE7/4 type F) heeft dit oplaadpunt?", - "render": "Hier zijn Schuko stekker zonder aardingspin (CEE7/4 type F) stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + "question": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?", + "render": "Hier zijn
Schuko stekker zonder aardingspin (CEE7/4 type F)
stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" }, "plugs-1": { - "question": "Hoeveel stekkers van type Europese stekker met aardingspin (CEE7/4 type E) heeft dit oplaadpunt?", - "render": "Hier zijn Europese stekker met aardingspin (CEE7/4 type E) stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + "question": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?", + "render": "Hier zijn
Europese stekker met aardingspin (CEE7/4 type E)
stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" }, "plugs-10": { - "question": "Hoeveel stekkers van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft dit oplaadpunt?", - "render": "Hier zijn Tesla Supercharger CCS (een type2 CCS met Tesla-logo) stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + "question": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?", + "render": "Hier zijn
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" }, "plugs-11": { - "question": "Hoeveel stekkers van type Tesla Supercharger (destination) heeft dit oplaadpunt?", - "render": "Hier zijn Tesla Supercharger (destination) stekkers van het type Tesla Supercharger (destination)" + "question": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?", + "render": "Hier zijn
Tesla Supercharger (destination)
stekkers van het type Tesla Supercharger (destination)" }, "plugs-12": { - "question": "Hoeveel stekkers van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft dit oplaadpunt?", - "render": "Hier zijn Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + "question": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?", + "render": "Hier zijn
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" }, "plugs-13": { - "question": "Hoeveel stekkers van type USB om GSMs en kleine electronica op te laden heeft dit oplaadpunt?", - "render": "Hier zijn USB om GSMs en kleine electronica op te laden stekkers van het type USB om GSMs en kleine electronica op te laden" + "question": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?", + "render": "Hier zijn
USB om GSMs en kleine electronica op te laden
stekkers van het type USB om GSMs en kleine electronica op te laden" }, "plugs-2": { - "question": "Hoeveel stekkers van type Chademo heeft dit oplaadpunt?", - "render": "Hier zijn Chademo stekkers van het type Chademo" + "question": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?", + "render": "Hier zijn
Chademo
stekkers van het type Chademo" }, "plugs-3": { - "question": "Hoeveel stekkers van type Type 1 met kabel (J1772) heeft dit oplaadpunt?", - "render": "Hier zijn Type 1 met kabel (J1772) stekkers van het type Type 1 met kabel (J1772)" + "question": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?", + "render": "Hier zijn
Type 1 met kabel (J1772)
stekkers van het type Type 1 met kabel (J1772)" }, "plugs-4": { - "question": "Hoeveel stekkers van type Type 1 zonder kabel (J1772) heeft dit oplaadpunt?", - "render": "Hier zijn Type 1 zonder kabel (J1772) stekkers van het type Type 1 zonder kabel (J1772)" + "question": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?", + "render": "Hier zijn
Type 1 zonder kabel (J1772)
stekkers van het type Type 1 zonder kabel (J1772)" }, "plugs-5": { - "question": "Hoeveel stekkers van type Type 1 CCS (ook gekend als Type 1 Combo) heeft dit oplaadpunt?", - "render": "Hier zijn Type 1 CCS (ook gekend als Type 1 Combo) stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" + "question": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?", + "render": "Hier zijn
Type 1 CCS (ook gekend als Type 1 Combo)
stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" }, "plugs-6": { - "question": "Hoeveel stekkers van type Tesla Supercharger heeft dit oplaadpunt?", - "render": "Hier zijn Tesla Supercharger stekkers van het type Tesla Supercharger" + "question": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?", + "render": "Hier zijn
Tesla Supercharger
stekkers van het type Tesla Supercharger" }, "plugs-7": { - "question": "Hoeveel stekkers van type Type 2 (mennekes) heeft dit oplaadpunt?", - "render": "Hier zijn Type 2 (mennekes) stekkers van het type Type 2 (mennekes)" + "question": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?", + "render": "Hier zijn
Type 2 (mennekes)
stekkers van het type Type 2 (mennekes)" }, "plugs-8": { - "question": "Hoeveel stekkers van type Type 2 CCS (mennekes) heeft dit oplaadpunt?", - "render": "Hier zijn Type 2 CCS (mennekes) stekkers van het type Type 2 CCS (mennekes)" + "question": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?", + "render": "Hier zijn
Type 2 CCS (mennekes)
stekkers van het type Type 2 CCS (mennekes)" }, "plugs-9": { - "question": "Hoeveel stekkers van type Type 2 met kabel (J1772) heeft dit oplaadpunt?", - "render": "Hier zijn Type 2 met kabel (J1772) stekkers van het type Type 2 met kabel (J1772)" + "question": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?", + "render": "Hier zijn
Type 2 met kabel (J1772)
stekkers van het type Type 2 met kabel (J1772)" }, "power-output-0": { "mappings": { "0": { - "then": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal 3.6 kw" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal 3.6 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ?", - "render": "Schuko stekker zonder aardingspin (CEE7/4 type F) levert een vermogen van maximaal {socket:schuko:output}" + "question": "Welk vermogen levert een enkele stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
?", + "render": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
levert een vermogen van maximaal {socket:schuko:output}" }, "power-output-1": { "mappings": { "0": { - "then": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 3 kw" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 3 kw" }, "1": { - "then": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal 22 kw" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Europese stekker met aardingspin (CEE7/4 type E) ?", - "render": "Europese stekker met aardingspin (CEE7/4 type E) levert een vermogen van maximaal {socket:typee:output}" + "question": "Welk vermogen levert een enkele stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
?", + "render": "
Europese stekker met aardingspin (CEE7/4 type E)
levert een vermogen van maximaal {socket:typee:output}" }, "power-output-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal 50 kw" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal 50 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ?", - "render": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" + "question": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
?", + "render": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
levert een vermogen van maximaal {socket:tesla_supercharger_ccs:output}" }, "power-output-11": { "mappings": { "0": { - "then": "Tesla Supercharger (destination) levert een vermogen van maximaal 120 kw" + "then": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 120 kw" }, "1": { - "then": "Tesla Supercharger (destination) levert een vermogen van maximaal 150 kw" + "then": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 150 kw" }, "2": { - "then": "Tesla Supercharger (destination) levert een vermogen van maximaal 250 kw" + "then": "
Tesla Supercharger (destination)
levert een vermogen van maximaal 250 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Tesla Supercharger (destination) ?", - "render": "Tesla Supercharger (destination) levert een vermogen van maximaal {socket:tesla_destination:output}" + "question": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger (destination)
?", + "render": "
Tesla Supercharger (destination)
levert een vermogen van maximaal {socket:tesla_destination:output}" }, "power-output-12": { "mappings": { "0": { - "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 11 kw" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 11 kw" }, "1": { - "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal 22 kw" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ?", - "render": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) levert een vermogen van maximaal {socket:tesla_destination:output}" + "question": "Welk vermogen levert een enkele stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
?", + "render": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
levert een vermogen van maximaal {socket:tesla_destination:output}" }, "power-output-13": { "mappings": { "0": { - "then": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 5w" + "then": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 5w" }, "1": { - "then": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal 10w" + "then": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal 10w" } }, - "question": "Welk vermogen levert een enkele stekker van type USB om GSMs en kleine electronica op te laden ?", - "render": "USB om GSMs en kleine electronica op te laden levert een vermogen van maximaal {socket:USB-A:output}" + "question": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?", + "render": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" }, "power-output-2": { "mappings": { "0": { - "then": "Chademo levert een vermogen van maximaal 50 kw" + "then": "
Chademo
levert een vermogen van maximaal 50 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Chademo ?", - "render": "Chademo levert een vermogen van maximaal {socket:chademo:output}" + "question": "Welk vermogen levert een enkele stekker van type
Chademo
?", + "render": "
Chademo
levert een vermogen van maximaal {socket:chademo:output}" }, "power-output-3": { "mappings": { "0": { - "then": "Type 1 met kabel (J1772) levert een vermogen van maximaal 3.7 kw" + "then": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 3.7 kw" }, "1": { - "then": "Type 1 met kabel (J1772) levert een vermogen van maximaal 7 kw" + "then": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal 7 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Type 1 met kabel (J1772) ?", - "render": "Type 1 met kabel (J1772) levert een vermogen van maximaal {socket:type1_cable:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 1 met kabel (J1772)
?", + "render": "
Type 1 met kabel (J1772)
levert een vermogen van maximaal {socket:type1_cable:output}" }, "power-output-4": { "mappings": { "0": { - "then": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 3.7 kw" + "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 3.7 kw" }, "1": { - "then": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 6.6 kw" + "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 6.6 kw" }, "2": { - "then": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7 kw" + "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7 kw" }, "3": { - "then": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal 7.2 kw" + "then": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal 7.2 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Type 1 zonder kabel (J1772) ?", - "render": "Type 1 zonder kabel (J1772) levert een vermogen van maximaal {socket:type1:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 1 zonder kabel (J1772)
?", + "render": "
Type 1 zonder kabel (J1772)
levert een vermogen van maximaal {socket:type1:output}" }, "power-output-5": { "mappings": { "0": { - "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 50 kw" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 50 kw" }, "1": { - "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 62.5 kw" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 62.5 kw" }, "2": { - "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 150 kw" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 150 kw" }, "3": { - "then": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal 350 kw" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal 350 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ?", - "render": "Type 1 CCS (ook gekend als Type 1 Combo) levert een vermogen van maximaal {socket:type1_combo:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
?", + "render": "
Type 1 CCS (ook gekend als Type 1 Combo)
levert een vermogen van maximaal {socket:type1_combo:output}" }, "power-output-6": { "mappings": { "0": { - "then": "Tesla Supercharger levert een vermogen van maximaal 120 kw" + "then": "
Tesla Supercharger
levert een vermogen van maximaal 120 kw" }, "1": { - "then": "Tesla Supercharger levert een vermogen van maximaal 150 kw" + "then": "
Tesla Supercharger
levert een vermogen van maximaal 150 kw" }, "2": { - "then": "Tesla Supercharger levert een vermogen van maximaal 250 kw" + "then": "
Tesla Supercharger
levert een vermogen van maximaal 250 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Tesla Supercharger ?", - "render": "Tesla Supercharger levert een vermogen van maximaal {socket:tesla_supercharger:output}" + "question": "Welk vermogen levert een enkele stekker van type
Tesla Supercharger
?", + "render": "
Tesla Supercharger
levert een vermogen van maximaal {socket:tesla_supercharger:output}" }, "power-output-7": { "mappings": { "0": { - "then": "Type 2 (mennekes) levert een vermogen van maximaal 11 kw" + "then": "
Type 2 (mennekes)
levert een vermogen van maximaal 11 kw" }, "1": { - "then": "Type 2 (mennekes) levert een vermogen van maximaal 22 kw" + "then": "
Type 2 (mennekes)
levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Type 2 (mennekes) ?", - "render": "Type 2 (mennekes) levert een vermogen van maximaal {socket:type2:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 2 (mennekes)
?", + "render": "
Type 2 (mennekes)
levert een vermogen van maximaal {socket:type2:output}" }, "power-output-8": { "mappings": { "0": { - "then": "Type 2 CCS (mennekes) levert een vermogen van maximaal 50 kw" + "then": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal 50 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Type 2 CCS (mennekes) ?", - "render": "Type 2 CCS (mennekes) levert een vermogen van maximaal {socket:type2_combo:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 2 CCS (mennekes)
?", + "render": "
Type 2 CCS (mennekes)
levert een vermogen van maximaal {socket:type2_combo:output}" }, "power-output-9": { "mappings": { "0": { - "then": "Type 2 met kabel (J1772) levert een vermogen van maximaal 11 kw" + "then": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 11 kw" }, "1": { - "then": "Type 2 met kabel (J1772) levert een vermogen van maximaal 22 kw" + "then": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal 22 kw" } }, - "question": "Welk vermogen levert een enkele stekker van type Type 2 met kabel (J1772) ?", - "render": "Type 2 met kabel (J1772) levert een vermogen van maximaal {socket:type2_cable:output}" + "question": "Welk vermogen levert een enkele stekker van type
Type 2 met kabel (J1772)
?", + "render": "
Type 2 met kabel (J1772)
levert een vermogen van maximaal {socket:type2_cable:output}" }, "voltage-0": { "mappings": { "0": { - "then": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van 230 volt" + "then": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van 230 volt" } }, - "question": "Welke spanning levert de stekker van type Schuko stekker zonder aardingspin (CEE7/4 type F) ", - "render": "Schuko stekker zonder aardingspin (CEE7/4 type F) heeft een spanning van {socket:schuko:voltage} volt" + "question": "Welke spanning levert de stekker van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
", + "render": "
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft een spanning van {socket:schuko:voltage} volt" }, "voltage-1": { "mappings": { "0": { - "then": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van 230 volt" + "then": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van 230 volt" } }, - "question": "Welke spanning levert de stekker van type Europese stekker met aardingspin (CEE7/4 type E) ", - "render": "Europese stekker met aardingspin (CEE7/4 type E) heeft een spanning van {socket:typee:voltage} volt" + "question": "Welke spanning levert de stekker van type
Europese stekker met aardingspin (CEE7/4 type E)
", + "render": "
Europese stekker met aardingspin (CEE7/4 type E)
heeft een spanning van {socket:typee:voltage} volt" }, "voltage-10": { "mappings": { "0": { - "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 500 volt" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 500 volt" }, "1": { - "then": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van 920 volt" + "then": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van 920 volt" } }, - "question": "Welke spanning levert de stekker van type Tesla Supercharger CCS (een type2 CCS met Tesla-logo) ", - "render": "Tesla Supercharger CCS (een type2 CCS met Tesla-logo) heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" + "question": "Welke spanning levert de stekker van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
", + "render": "
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft een spanning van {socket:tesla_supercharger_ccs:voltage} volt" }, "voltage-11": { "mappings": { "0": { - "then": "Tesla Supercharger (destination) heeft een spanning van 480 volt" + "then": "
Tesla Supercharger (destination)
heeft een spanning van 480 volt" } }, - "question": "Welke spanning levert de stekker van type Tesla Supercharger (destination) ", - "render": "Tesla Supercharger (destination) heeft een spanning van {socket:tesla_destination:voltage} volt" + "question": "Welke spanning levert de stekker van type
Tesla Supercharger (destination)
", + "render": "
Tesla Supercharger (destination)
heeft een spanning van {socket:tesla_destination:voltage} volt" }, "voltage-12": { "mappings": { "0": { - "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 230 volt" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 230 volt" }, "1": { - "then": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van 400 volt" + "then": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van 400 volt" } }, - "question": "Welke spanning levert de stekker van type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) ", - "render": "Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo) heeft een spanning van {socket:tesla_destination:voltage} volt" + "question": "Welke spanning levert de stekker van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
", + "render": "
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft een spanning van {socket:tesla_destination:voltage} volt" }, "voltage-13": { "mappings": { "0": { - "then": "USB om GSMs en kleine electronica op te laden heeft een spanning van 5 volt" + "then": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van 5 volt" } }, - "question": "Welke spanning levert de stekker van type USB om GSMs en kleine electronica op te laden ", - "render": "USB om GSMs en kleine electronica op te laden heeft een spanning van {socket:USB-A:voltage} volt" + "question": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
", + "render": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" }, "voltage-2": { "mappings": { "0": { - "then": "Chademo heeft een spanning van 500 volt" + "then": "
Chademo
heeft een spanning van 500 volt" } }, - "question": "Welke spanning levert de stekker van type Chademo ", - "render": "Chademo heeft een spanning van {socket:chademo:voltage} volt" + "question": "Welke spanning levert de stekker van type
Chademo
", + "render": "
Chademo
heeft een spanning van {socket:chademo:voltage} volt" }, "voltage-3": { "mappings": { "0": { - "then": "Type 1 met kabel (J1772) heeft een spanning van 200 volt" + "then": "
Type 1 met kabel (J1772)
heeft een spanning van 200 volt" }, "1": { - "then": "Type 1 met kabel (J1772) heeft een spanning van 240 volt" + "then": "
Type 1 met kabel (J1772)
heeft een spanning van 240 volt" } }, - "question": "Welke spanning levert de stekker van type Type 1 met kabel (J1772) ", - "render": "Type 1 met kabel (J1772) heeft een spanning van {socket:type1_cable:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 1 met kabel (J1772)
", + "render": "
Type 1 met kabel (J1772)
heeft een spanning van {socket:type1_cable:voltage} volt" }, "voltage-4": { "mappings": { "0": { - "then": "Type 1 zonder kabel (J1772) heeft een spanning van 200 volt" + "then": "
Type 1 zonder kabel (J1772)
heeft een spanning van 200 volt" }, "1": { - "then": "Type 1 zonder kabel (J1772) heeft een spanning van 240 volt" + "then": "
Type 1 zonder kabel (J1772)
heeft een spanning van 240 volt" } }, - "question": "Welke spanning levert de stekker van type Type 1 zonder kabel (J1772) ", - "render": "Type 1 zonder kabel (J1772) heeft een spanning van {socket:type1:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 1 zonder kabel (J1772)
", + "render": "
Type 1 zonder kabel (J1772)
heeft een spanning van {socket:type1:voltage} volt" }, "voltage-5": { "mappings": { "0": { - "then": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 400 volt" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 400 volt" }, "1": { - "then": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van 1000 volt" + "then": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van 1000 volt" } }, - "question": "Welke spanning levert de stekker van type Type 1 CCS (ook gekend als Type 1 Combo) ", - "render": "Type 1 CCS (ook gekend als Type 1 Combo) heeft een spanning van {socket:type1_combo:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 1 CCS (ook gekend als Type 1 Combo)
", + "render": "
Type 1 CCS (ook gekend als Type 1 Combo)
heeft een spanning van {socket:type1_combo:voltage} volt" }, "voltage-6": { "mappings": { "0": { - "then": "Tesla Supercharger heeft een spanning van 480 volt" + "then": "
Tesla Supercharger
heeft een spanning van 480 volt" } }, - "question": "Welke spanning levert de stekker van type Tesla Supercharger ", - "render": "Tesla Supercharger heeft een spanning van {socket:tesla_supercharger:voltage} volt" + "question": "Welke spanning levert de stekker van type
Tesla Supercharger
", + "render": "
Tesla Supercharger
heeft een spanning van {socket:tesla_supercharger:voltage} volt" }, "voltage-7": { "mappings": { "0": { - "then": "Type 2 (mennekes) heeft een spanning van 230 volt" + "then": "
Type 2 (mennekes)
heeft een spanning van 230 volt" }, "1": { - "then": "Type 2 (mennekes) heeft een spanning van 400 volt" + "then": "
Type 2 (mennekes)
heeft een spanning van 400 volt" } }, - "question": "Welke spanning levert de stekker van type Type 2 (mennekes) ", - "render": "Type 2 (mennekes) heeft een spanning van {socket:type2:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 2 (mennekes)
", + "render": "
Type 2 (mennekes)
heeft een spanning van {socket:type2:voltage} volt" }, "voltage-8": { "mappings": { "0": { - "then": "Type 2 CCS (mennekes) heeft een spanning van 500 volt" + "then": "
Type 2 CCS (mennekes)
heeft een spanning van 500 volt" }, "1": { - "then": "Type 2 CCS (mennekes) heeft een spanning van 920 volt" + "then": "
Type 2 CCS (mennekes)
heeft een spanning van 920 volt" } }, - "question": "Welke spanning levert de stekker van type Type 2 CCS (mennekes) ", - "render": "Type 2 CCS (mennekes) heeft een spanning van {socket:type2_combo:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 2 CCS (mennekes)
", + "render": "
Type 2 CCS (mennekes)
heeft een spanning van {socket:type2_combo:voltage} volt" }, "voltage-9": { "mappings": { "0": { - "then": "Type 2 met kabel (J1772) heeft een spanning van 230 volt" + "then": "
Type 2 met kabel (J1772)
heeft een spanning van 230 volt" }, "1": { - "then": "Type 2 met kabel (J1772) heeft een spanning van 400 volt" + "then": "
Type 2 met kabel (J1772)
heeft een spanning van 400 volt" } }, - "question": "Welke spanning levert de stekker van type Type 2 met kabel (J1772) ", - "render": "Type 2 met kabel (J1772) heeft een spanning van {socket:type2_cable:voltage} volt" + "question": "Welke spanning levert de stekker van type
Type 2 met kabel (J1772)
", + "render": "
Type 2 met kabel (J1772)
heeft een spanning van {socket:type2_cable:voltage} volt" } }, "units": { @@ -2848,13 +2848,13 @@ "Operator tag": { "mappings": { "0": { - "then": "Dit gebied wordt beheerd door Natuurpunt" + "then": "Dit gebied wordt beheerd door Natuurpunt" }, "1": { - "then": "Dit gebied wordt beheerd door {operator}" + "then": "Dit gebied wordt beheerd door {operator}" }, "2": { - "then": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" + "then": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" } }, "question": "Wie beheert dit gebied?", From 9a1c58384eba61b3b3b9d6dfe288004fd0a26fcf Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 5 Oct 2021 19:32:09 +0200 Subject: [PATCH 107/110] Add bosch 3pin socket --- assets/layers/charging_station/bosch-3pin.svg | 119 ++++++++++++++++++ assets/layers/charging_station/types.csv | 1 + 2 files changed, 120 insertions(+) create mode 100644 assets/layers/charging_station/bosch-3pin.svg diff --git a/assets/layers/charging_station/bosch-3pin.svg b/assets/layers/charging_station/bosch-3pin.svg new file mode 100644 index 000000000..4295a1a75 --- /dev/null +++ b/assets/layers/charging_station/bosch-3pin.svg @@ -0,0 +1,119 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/assets/layers/charging_station/types.csv b/assets/layers/charging_station/types.csv index 98d93f585..b8d6da5cf 100644 --- a/assets/layers/charging_station/types.csv +++ b/assets/layers/charging_station/types.csv @@ -13,3 +13,4 @@ socket:tesla_supercharger_ccs,Type2_CCS.svg,Tesla Supercharger CCS (a bra socket:tesla_destination,Tesla-hpwc-model-s.svg,Tesla Supercharger (destination),us,,480,125;350,120 kW;150 kW;250 kW,Tesla Supercharger (destination),car;motorcar;hgv;bus,bicycle;scooter socket:tesla_destination,Type2_tethered.svg,Tesla supercharger (destination (A Type 2 with cable branded as tesla),,us,230;400,16;32,11 kW;22 kW,Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo),car;motorcar;hgv;bus,bicycle;scooter socket:USB-A,usb_port.svg,USB to charge phones and small electronics,,,5,1;2,5W;10W,USB om GSMs en kleine electronica op te laden,*, +socket:bosch_3pin,bosch-3pin.svg,Bosch Active Connect with cable,,,,,,Bosch Active Connect aan een kabel,bicycle,car;motorcar;hgv;bus From ae62bc3ffdafd1de9cfdc5fcb527841cdcc6d748 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 5 Oct 2021 22:21:49 +0200 Subject: [PATCH 108/110] Add missing license info --- .../charging_station/bosch-3pin_working.svg | 128 ++++++++++++++++++ .../layers/charging_station/license_info.json | 32 +++++ 2 files changed, 160 insertions(+) create mode 100644 assets/layers/charging_station/bosch-3pin_working.svg diff --git a/assets/layers/charging_station/bosch-3pin_working.svg b/assets/layers/charging_station/bosch-3pin_working.svg new file mode 100644 index 000000000..c532e8118 --- /dev/null +++ b/assets/layers/charging_station/bosch-3pin_working.svg @@ -0,0 +1,128 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/charging_station/license_info.json b/assets/layers/charging_station/license_info.json index 0445cb394..967e0daff 100644 --- a/assets/layers/charging_station/license_info.json +++ b/assets/layers/charging_station/license_info.json @@ -20,6 +20,22 @@ "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chademo_type4.svg" ] }, + { + "path": "Entladebuchse_Bosch_Active_Performance-1024x685.jpg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + }, + { + "path": "Entladebuchse_Bosch_Active_Performance-1024x685_straightened.jpg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + }, { "path": "Tesla-hpwc-model-s.svg", "license": "CC-BY-SA 4.0", @@ -99,6 +115,22 @@ ], "sources": [] }, + { + "path": "bosch-3pin.svg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + }, + { + "path": "bosch-3pin_working.svg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + }, { "path": "under_construction.svg", "license": "Public domain", From fec4a40279d9b19f1910eb07be5fb29671eef6fe Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 6 Oct 2021 00:31:47 +0200 Subject: [PATCH 109/110] Effectively show the number of plugs in the popup instad of the type --- .../charging_station/bosch-3pin_working.svg | 128 ----------- .../charging_station/charging_station.json | 202 +++++++++++++++--- assets/layers/charging_station/csvToJson.ts | 4 +- .../layers/charging_station/license_info.json | 26 +-- langs/layers/en.json | 53 +++-- langs/layers/nl.json | 53 +++-- 6 files changed, 252 insertions(+), 214 deletions(-) delete mode 100644 assets/layers/charging_station/bosch-3pin_working.svg diff --git a/assets/layers/charging_station/bosch-3pin_working.svg b/assets/layers/charging_station/bosch-3pin_working.svg deleted file mode 100644 index c532e8118..000000000 --- a/assets/layers/charging_station/bosch-3pin_working.svg +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 55891d6e6..49636eba5 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -754,6 +754,49 @@ "nl": "
USB om GSMs en kleine electronica op te laden
" }, "hideInAnswer": true + }, + { + "if": "socket:bosch_3pin=1", + "ifnot": "socket:bosch_3pin=", + "then": { + "en": "
Bosch Active Connect with cable
", + "nl": "
Bosch Active Connect aan een kabel
" + }, + "hideInAnswer": { + "or": [ + { + "and": [ + "bicycle=no" + ] + }, + { + "and": [ + { + "or": [ + "car=yes", + "motorcar=yes", + "hgv=yes", + "bus=yes" + ] + }, + "bicycle!=yes" + ] + } + ] + } + }, + { + "if": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=1" + ] + }, + "then": { + "en": "
Bosch Active Connect with cable
", + "nl": "
Bosch Active Connect aan een kabel
" + }, + "hideInAnswer": true } ] }, @@ -764,8 +807,8 @@ "nl": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Schuko wall plug without ground pin (CEE7/4 type F)
plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here", - "nl": "Hier zijn
Schuko stekker zonder aardingspin (CEE7/4 type F)
stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + "en": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here", + "nl": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "freeform": { "key": "socket:schuko", @@ -875,8 +918,8 @@ "nl": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
European wall plug with ground pin (CEE7/4 type E)
plugs of type European wall plug with ground pin (CEE7/4 type E) available here", - "nl": "Hier zijn
Europese stekker met aardingspin (CEE7/4 type E)
stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + "en": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here", + "nl": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" }, "freeform": { "key": "socket:typee", @@ -993,8 +1036,8 @@ "nl": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Chademo
plugs of type Chademo available here", - "nl": "Hier zijn
Chademo
stekkers van het type Chademo" + "en": "There are {socket:chademo} plugs of type
Chademo
available here", + "nl": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" }, "freeform": { "key": "socket:chademo", @@ -1104,8 +1147,8 @@ "nl": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 1 with cable (J1772)
plugs of type Type 1 with cable (J1772) available here", - "nl": "Hier zijn
Type 1 met kabel (J1772)
stekkers van het type Type 1 met kabel (J1772)" + "en": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here", + "nl": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" }, "freeform": { "key": "socket:type1_cable", @@ -1229,8 +1272,8 @@ "nl": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 1 without cable (J1772)
plugs of type Type 1 without cable (J1772) available here", - "nl": "Hier zijn
Type 1 zonder kabel (J1772)
stekkers van het type Type 1 zonder kabel (J1772)" + "en": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here", + "nl": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" }, "freeform": { "key": "socket:type1", @@ -1368,8 +1411,8 @@ "nl": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 1 CCS (aka Type 1 Combo)
plugs of type Type 1 CCS (aka Type 1 Combo) available here", - "nl": "Hier zijn
Type 1 CCS (ook gekend als Type 1 Combo)
stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" + "en": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here", + "nl": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "freeform": { "key": "socket:type1_combo", @@ -1514,8 +1557,8 @@ "nl": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Tesla Supercharger
plugs of type Tesla Supercharger available here", - "nl": "Hier zijn
Tesla Supercharger
stekkers van het type Tesla Supercharger" + "en": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here", + "nl": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" }, "freeform": { "key": "socket:tesla_supercharger", @@ -1646,8 +1689,8 @@ "nl": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 2 (mennekes)
plugs of type Type 2 (mennekes) available here", - "nl": "Hier zijn
Type 2 (mennekes)
stekkers van het type Type 2 (mennekes)" + "en": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here", + "nl": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" }, "freeform": { "key": "socket:type2", @@ -1778,8 +1821,8 @@ "nl": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 2 CCS (mennekes)
plugs of type Type 2 CCS (mennekes) available here", - "nl": "Hier zijn
Type 2 CCS (mennekes)
stekkers van het type Type 2 CCS (mennekes)" + "en": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here", + "nl": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" }, "freeform": { "key": "socket:type2_combo", @@ -1903,8 +1946,8 @@ "nl": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Type 2 with cable (mennekes)
plugs of type Type 2 with cable (mennekes) available here", - "nl": "Hier zijn
Type 2 met kabel (J1772)
stekkers van het type Type 2 met kabel (J1772)" + "en": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here", + "nl": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" }, "freeform": { "key": "socket:type2_cable", @@ -2035,8 +2078,8 @@ "nl": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Tesla Supercharger CCS (a branded type2_css)
plugs of type Tesla Supercharger CCS (a branded type2_css) available here", - "nl": "Hier zijn
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + "en": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here", + "nl": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "freeform": { "key": "socket:tesla_supercharger_ccs", @@ -2160,8 +2203,8 @@ "nl": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Tesla Supercharger (destination)
plugs of type Tesla Supercharger (destination) available here", - "nl": "Hier zijn
Tesla Supercharger (destination)
stekkers van het type Tesla Supercharger (destination)" + "en": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" }, "freeform": { "key": "socket:tesla_destination", @@ -2292,8 +2335,8 @@ "nl": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?" }, "render": { - "en": "There are
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here", - "nl": "Hier zijn
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + "en": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here", + "nl": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" }, "freeform": { "key": "socket:tesla_destination", @@ -2424,8 +2467,8 @@ "nl": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?" }, "render": { - "en": "There are
USB to charge phones and small electronics
plugs of type USB to charge phones and small electronics available here", - "nl": "Hier zijn
USB om GSMs en kleine electronica op te laden
stekkers van het type USB om GSMs en kleine electronica op te laden" + "en": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here", + "nl": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" }, "freeform": { "key": "socket:USB-A", @@ -2542,6 +2585,93 @@ ] } }, + { + "id": "plugs-14", + "question": { + "en": "How much plugs of type
Bosch Active Connect with cable
are available here?", + "nl": "Hoeveel stekkers van type
Bosch Active Connect aan een kabel
heeft dit oplaadpunt?" + }, + "render": { + "en": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with cable
available here", + "nl": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect aan een kabel
" + }, + "freeform": { + "key": "socket:bosch_3pin", + "type": "pnat" + }, + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "voltage-14", + "question": { + "en": "What voltage do the plugs with
Bosch Active Connect with cable
offer?", + "nl": "Welke spanning levert de stekker van type
Bosch Active Connect aan een kabel
" + }, + "render": { + "en": "
Bosch Active Connect with cable
outputs {socket:bosch_3pin:voltage} volt", + "nl": "
Bosch Active Connect aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" + }, + "freeform": { + "key": "socket:bosch_3pin:voltage", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "current-14", + "question": { + "en": "What current do the plugs with
Bosch Active Connect with cable
offer?", + "nl": "Welke stroom levert de stekker van type
Bosch Active Connect aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with cable
outputs at most {socket:bosch_3pin:current}A", + "nl": "
Bosch Active Connect aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" + }, + "freeform": { + "key": "socket:bosch_3pin:current", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, + { + "id": "power-output-14", + "question": { + "en": "What power output does a single plug of type
Bosch Active Connect with cable
offer?", + "nl": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect aan een kabel
?" + }, + "render": { + "en": "
Bosch Active Connect with cable
outputs at most {socket:bosch_3pin:output}", + "nl": "
Bosch Active Connect aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" + }, + "freeform": { + "key": "socket:bosch_3pin:output", + "type": "pfloat" + }, + "mappings": [], + "condition": { + "and": [ + "socket:bosch_3pin~*", + "socket:bosch_3pin!=0" + ] + } + }, { "id": "Authentication", "question": { @@ -3197,6 +3327,13 @@ "nl": "Heeft een
USB om GSMs en kleine electronica op te laden
" }, "osmTags": "socket:USB-A~*" + }, + { + "question": { + "en": "Has a
Bosch Active Connect with cable
connector", + "nl": "Heeft een
Bosch Active Connect aan een kabel
" + }, + "osmTags": "socket:bosch_3pin~*" } ] } @@ -3280,7 +3417,8 @@ "socket:tesla_supercharger_ccs:voltage", "socket:tesla_destination:voltage", "socket:tesla_destination:voltage", - "socket:USB-A:voltage" + "socket:USB-A:voltage", + "socket:bosch_3pin:voltage" ], "applicableUnits": [ { @@ -3315,7 +3453,8 @@ "socket:tesla_supercharger_ccs:current", "socket:tesla_destination:current", "socket:tesla_destination:current", - "socket:USB-A:current" + "socket:USB-A:current", + "socket:bosch_3pin:current" ], "applicableUnits": [ { @@ -3349,7 +3488,8 @@ "socket:tesla_supercharger_ccs:output", "socket:tesla_destination:output", "socket:tesla_destination:output", - "socket:USB-A:output" + "socket:USB-A:output", + "socket:bosch_3pin:output" ], "applicableUnits": [ { diff --git a/assets/layers/charging_station/csvToJson.ts b/assets/layers/charging_station/csvToJson.ts index 739c163b4..c630297c0 100644 --- a/assets/layers/charging_station/csvToJson.ts +++ b/assets/layers/charging_station/csvToJson.ts @@ -137,8 +137,8 @@ function run(file, protojson) { nl: `Hoeveel stekkers van type ${descrWithImage_nl} heeft dit oplaadpunt?`, }, render: { - en: `There are ${descrWithImage_en} plugs of type ${e.description.get("en")} available here`, - nl: `Hier zijn ${descrWithImage_nl} stekkers van het type ${e.description.get("nl")}` + en: `There are {${e.key}} plugs of type ${descrWithImage_en} available here`, + nl: `Hier zijn {${e.key}} stekkers van het type ${descrWithImage_nl}` }, freeform: { key: e.key, diff --git a/assets/layers/charging_station/license_info.json b/assets/layers/charging_station/license_info.json index 967e0daff..dc2de4d3d 100644 --- a/assets/layers/charging_station/license_info.json +++ b/assets/layers/charging_station/license_info.json @@ -20,22 +20,6 @@ "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chademo_type4.svg" ] }, - { - "path": "Entladebuchse_Bosch_Active_Performance-1024x685.jpg", - "license": "CC0", - "authors": [ - "Pieter Vander Vennet" - ], - "sources": [] - }, - { - "path": "Entladebuchse_Bosch_Active_Performance-1024x685_straightened.jpg", - "license": "CC0", - "authors": [ - "Pieter Vander Vennet" - ], - "sources": [] - }, { "path": "Tesla-hpwc-model-s.svg", "license": "CC-BY-SA 4.0", @@ -123,14 +107,6 @@ ], "sources": [] }, - { - "path": "bosch-3pin_working.svg", - "license": "CC0", - "authors": [ - "Pieter Vander Vennet" - ], - "sources": [] - }, { "path": "under_construction.svg", "license": "Public domain", @@ -151,4 +127,4 @@ "https://thenounproject.com/term/usb-port/94768/" ] } -] \ No newline at end of file +] diff --git a/langs/layers/en.json b/langs/layers/en.json index 2a9ab0bce..ac297439b 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -922,6 +922,9 @@ }, "14": { "question": "Has a
USB to charge phones and small electronics
connector" + }, + "15": { + "question": "Has a
Bosch Active Connect with cable
connector" } } } @@ -1051,6 +1054,12 @@ }, "27": { "then": "
USB to charge phones and small electronics
" + }, + "28": { + "then": "
Bosch Active Connect with cable
" + }, + "29": { + "then": "
Bosch Active Connect with cable
" } }, "question": "Which charging stations are available here?" @@ -1209,6 +1218,10 @@ "question": "What current do the plugs with
USB to charge phones and small electronics
offer?", "render": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:current}A" }, + "current-14": { + "question": "What current do the plugs with
Bosch Active Connect with cable
offer?", + "render": "
Bosch Active Connect with cable
outputs at most {socket:bosch_3pin:current}A" + }, "current-2": { "mappings": { "0": { @@ -1336,59 +1349,63 @@ }, "plugs-0": { "question": "How much plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
are available here?", - "render": "There are
Schuko wall plug without ground pin (CEE7/4 type F)
plugs of type Schuko wall plug without ground pin (CEE7/4 type F) available here" + "render": "There are {socket:schuko} plugs of type
Schuko wall plug without ground pin (CEE7/4 type F)
available here" }, "plugs-1": { "question": "How much plugs of type
European wall plug with ground pin (CEE7/4 type E)
are available here?", - "render": "There are
European wall plug with ground pin (CEE7/4 type E)
plugs of type European wall plug with ground pin (CEE7/4 type E) available here" + "render": "There are {socket:typee} plugs of type
European wall plug with ground pin (CEE7/4 type E)
available here" }, "plugs-10": { "question": "How much plugs of type
Tesla Supercharger CCS (a branded type2_css)
are available here?", - "render": "There are
Tesla Supercharger CCS (a branded type2_css)
plugs of type Tesla Supercharger CCS (a branded type2_css) available here" + "render": "There are {socket:tesla_supercharger_ccs} plugs of type
Tesla Supercharger CCS (a branded type2_css)
available here" }, "plugs-11": { "question": "How much plugs of type
Tesla Supercharger (destination)
are available here?", - "render": "There are
Tesla Supercharger (destination)
plugs of type Tesla Supercharger (destination) available here" + "render": "There are {socket:tesla_destination} plugs of type
Tesla Supercharger (destination)
available here" }, "plugs-12": { "question": "How much plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
are available here?", - "render": "There are
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
plugs of type Tesla supercharger (destination (A Type 2 with cable branded as tesla) available here" + "render": "There are {socket:tesla_destination} plugs of type
Tesla supercharger (destination (A Type 2 with cable branded as tesla)
available here" }, "plugs-13": { "question": "How much plugs of type
USB to charge phones and small electronics
are available here?", - "render": "There are
USB to charge phones and small electronics
plugs of type USB to charge phones and small electronics available here" + "render": "There are {socket:USB-A} plugs of type
USB to charge phones and small electronics
available here" + }, + "plugs-14": { + "question": "How much plugs of type
Bosch Active Connect with cable
are available here?", + "render": "There are {socket:bosch_3pin} plugs of type
Bosch Active Connect with cable
available here" }, "plugs-2": { "question": "How much plugs of type
Chademo
are available here?", - "render": "There are
Chademo
plugs of type Chademo available here" + "render": "There are {socket:chademo} plugs of type
Chademo
available here" }, "plugs-3": { "question": "How much plugs of type
Type 1 with cable (J1772)
are available here?", - "render": "There are
Type 1 with cable (J1772)
plugs of type Type 1 with cable (J1772) available here" + "render": "There are {socket:type1_cable} plugs of type
Type 1 with cable (J1772)
available here" }, "plugs-4": { "question": "How much plugs of type
Type 1 without cable (J1772)
are available here?", - "render": "There are
Type 1 without cable (J1772)
plugs of type Type 1 without cable (J1772) available here" + "render": "There are {socket:type1} plugs of type
Type 1 without cable (J1772)
available here" }, "plugs-5": { "question": "How much plugs of type
Type 1 CCS (aka Type 1 Combo)
are available here?", - "render": "There are
Type 1 CCS (aka Type 1 Combo)
plugs of type Type 1 CCS (aka Type 1 Combo) available here" + "render": "There are {socket:type1_combo} plugs of type
Type 1 CCS (aka Type 1 Combo)
available here" }, "plugs-6": { "question": "How much plugs of type
Tesla Supercharger
are available here?", - "render": "There are
Tesla Supercharger
plugs of type Tesla Supercharger available here" + "render": "There are {socket:tesla_supercharger} plugs of type
Tesla Supercharger
available here" }, "plugs-7": { "question": "How much plugs of type
Type 2 (mennekes)
are available here?", - "render": "There are
Type 2 (mennekes)
plugs of type Type 2 (mennekes) available here" + "render": "There are {socket:type2} plugs of type
Type 2 (mennekes)
available here" }, "plugs-8": { "question": "How much plugs of type
Type 2 CCS (mennekes)
are available here?", - "render": "There are
Type 2 CCS (mennekes)
plugs of type Type 2 CCS (mennekes) available here" + "render": "There are {socket:type2_combo} plugs of type
Type 2 CCS (mennekes)
available here" }, "plugs-9": { "question": "How much plugs of type
Type 2 with cable (mennekes)
are available here?", - "render": "There are
Type 2 with cable (mennekes)
plugs of type Type 2 with cable (mennekes) available here" + "render": "There are {socket:type2_cable} plugs of type
Type 2 with cable (mennekes)
available here" }, "power-output-0": { "mappings": { @@ -1459,6 +1476,10 @@ "question": "What power output does a single plug of type
USB to charge phones and small electronics
offer?", "render": "
USB to charge phones and small electronics
outputs at most {socket:USB-A:output}" }, + "power-output-14": { + "question": "What power output does a single plug of type
Bosch Active Connect with cable
offer?", + "render": "
Bosch Active Connect with cable
outputs at most {socket:bosch_3pin:output}" + }, "power-output-2": { "mappings": { "0": { @@ -1628,6 +1649,10 @@ "question": "What voltage do the plugs with
USB to charge phones and small electronics
offer?", "render": "
USB to charge phones and small electronics
outputs {socket:USB-A:voltage} volt" }, + "voltage-14": { + "question": "What voltage do the plugs with
Bosch Active Connect with cable
offer?", + "render": "
Bosch Active Connect with cable
outputs {socket:bosch_3pin:voltage} volt" + }, "voltage-2": { "mappings": { "0": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 7efa1a6d8..0ff5387f3 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -1024,6 +1024,9 @@ }, "14": { "question": "Heeft een
USB om GSMs en kleine electronica op te laden
" + }, + "15": { + "question": "Heeft een
Bosch Active Connect aan een kabel
" } } } @@ -1114,6 +1117,12 @@ }, "27": { "then": "
USB om GSMs en kleine electronica op te laden
" + }, + "28": { + "then": "
Bosch Active Connect aan een kabel
" + }, + "29": { + "then": "
Bosch Active Connect aan een kabel
" } } }, @@ -1207,6 +1216,10 @@ "question": "Welke stroom levert de stekker van type
USB om GSMs en kleine electronica op te laden
?", "render": "
USB om GSMs en kleine electronica op te laden
levert een stroom van maximaal {socket:USB-A:current}A" }, + "current-14": { + "question": "Welke stroom levert de stekker van type
Bosch Active Connect aan een kabel
?", + "render": "
Bosch Active Connect aan een kabel
levert een stroom van maximaal {socket:bosch_3pin:current}A" + }, "current-2": { "mappings": { "0": { @@ -1326,59 +1339,63 @@ }, "plugs-0": { "question": "Hoeveel stekkers van type
Schuko stekker zonder aardingspin (CEE7/4 type F)
heeft dit oplaadpunt?", - "render": "Hier zijn
Schuko stekker zonder aardingspin (CEE7/4 type F)
stekkers van het type Schuko stekker zonder aardingspin (CEE7/4 type F)" + "render": "Hier zijn {socket:schuko} stekkers van het type
Schuko stekker zonder aardingspin (CEE7/4 type F)
" }, "plugs-1": { "question": "Hoeveel stekkers van type
Europese stekker met aardingspin (CEE7/4 type E)
heeft dit oplaadpunt?", - "render": "Hier zijn
Europese stekker met aardingspin (CEE7/4 type E)
stekkers van het type Europese stekker met aardingspin (CEE7/4 type E)" + "render": "Hier zijn {socket:typee} stekkers van het type
Europese stekker met aardingspin (CEE7/4 type E)
" }, "plugs-10": { "question": "Hoeveel stekkers van type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
heeft dit oplaadpunt?", - "render": "Hier zijn
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
stekkers van het type Tesla Supercharger CCS (een type2 CCS met Tesla-logo)" + "render": "Hier zijn {socket:tesla_supercharger_ccs} stekkers van het type
Tesla Supercharger CCS (een type2 CCS met Tesla-logo)
" }, "plugs-11": { "question": "Hoeveel stekkers van type
Tesla Supercharger (destination)
heeft dit oplaadpunt?", - "render": "Hier zijn
Tesla Supercharger (destination)
stekkers van het type Tesla Supercharger (destination)" + "render": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla Supercharger (destination)
" }, "plugs-12": { "question": "Hoeveel stekkers van type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
heeft dit oplaadpunt?", - "render": "Hier zijn
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
stekkers van het type Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)" + "render": "Hier zijn {socket:tesla_destination} stekkers van het type
Tesla supercharger (destination (Een Type 2 met kabel en Tesla-logo)
" }, "plugs-13": { "question": "Hoeveel stekkers van type
USB om GSMs en kleine electronica op te laden
heeft dit oplaadpunt?", - "render": "Hier zijn
USB om GSMs en kleine electronica op te laden
stekkers van het type USB om GSMs en kleine electronica op te laden" + "render": "Hier zijn {socket:USB-A} stekkers van het type
USB om GSMs en kleine electronica op te laden
" + }, + "plugs-14": { + "question": "Hoeveel stekkers van type
Bosch Active Connect aan een kabel
heeft dit oplaadpunt?", + "render": "Hier zijn {socket:bosch_3pin} stekkers van het type
Bosch Active Connect aan een kabel
" }, "plugs-2": { "question": "Hoeveel stekkers van type
Chademo
heeft dit oplaadpunt?", - "render": "Hier zijn
Chademo
stekkers van het type Chademo" + "render": "Hier zijn {socket:chademo} stekkers van het type
Chademo
" }, "plugs-3": { "question": "Hoeveel stekkers van type
Type 1 met kabel (J1772)
heeft dit oplaadpunt?", - "render": "Hier zijn
Type 1 met kabel (J1772)
stekkers van het type Type 1 met kabel (J1772)" + "render": "Hier zijn {socket:type1_cable} stekkers van het type
Type 1 met kabel (J1772)
" }, "plugs-4": { "question": "Hoeveel stekkers van type
Type 1 zonder kabel (J1772)
heeft dit oplaadpunt?", - "render": "Hier zijn
Type 1 zonder kabel (J1772)
stekkers van het type Type 1 zonder kabel (J1772)" + "render": "Hier zijn {socket:type1} stekkers van het type
Type 1 zonder kabel (J1772)
" }, "plugs-5": { "question": "Hoeveel stekkers van type
Type 1 CCS (ook gekend als Type 1 Combo)
heeft dit oplaadpunt?", - "render": "Hier zijn
Type 1 CCS (ook gekend als Type 1 Combo)
stekkers van het type Type 1 CCS (ook gekend als Type 1 Combo)" + "render": "Hier zijn {socket:type1_combo} stekkers van het type
Type 1 CCS (ook gekend als Type 1 Combo)
" }, "plugs-6": { "question": "Hoeveel stekkers van type
Tesla Supercharger
heeft dit oplaadpunt?", - "render": "Hier zijn
Tesla Supercharger
stekkers van het type Tesla Supercharger" + "render": "Hier zijn {socket:tesla_supercharger} stekkers van het type
Tesla Supercharger
" }, "plugs-7": { "question": "Hoeveel stekkers van type
Type 2 (mennekes)
heeft dit oplaadpunt?", - "render": "Hier zijn
Type 2 (mennekes)
stekkers van het type Type 2 (mennekes)" + "render": "Hier zijn {socket:type2} stekkers van het type
Type 2 (mennekes)
" }, "plugs-8": { "question": "Hoeveel stekkers van type
Type 2 CCS (mennekes)
heeft dit oplaadpunt?", - "render": "Hier zijn
Type 2 CCS (mennekes)
stekkers van het type Type 2 CCS (mennekes)" + "render": "Hier zijn {socket:type2_combo} stekkers van het type
Type 2 CCS (mennekes)
" }, "plugs-9": { "question": "Hoeveel stekkers van type
Type 2 met kabel (J1772)
heeft dit oplaadpunt?", - "render": "Hier zijn
Type 2 met kabel (J1772)
stekkers van het type Type 2 met kabel (J1772)" + "render": "Hier zijn {socket:type2_cable} stekkers van het type
Type 2 met kabel (J1772)
" }, "power-output-0": { "mappings": { @@ -1449,6 +1466,10 @@ "question": "Welk vermogen levert een enkele stekker van type
USB om GSMs en kleine electronica op te laden
?", "render": "
USB om GSMs en kleine electronica op te laden
levert een vermogen van maximaal {socket:USB-A:output}" }, + "power-output-14": { + "question": "Welk vermogen levert een enkele stekker van type
Bosch Active Connect aan een kabel
?", + "render": "
Bosch Active Connect aan een kabel
levert een vermogen van maximaal {socket:bosch_3pin:output}" + }, "power-output-2": { "mappings": { "0": { @@ -1614,6 +1635,10 @@ "question": "Welke spanning levert de stekker van type
USB om GSMs en kleine electronica op te laden
", "render": "
USB om GSMs en kleine electronica op te laden
heeft een spanning van {socket:USB-A:voltage} volt" }, + "voltage-14": { + "question": "Welke spanning levert de stekker van type
Bosch Active Connect aan een kabel
", + "render": "
Bosch Active Connect aan een kabel
heeft een spanning van {socket:bosch_3pin:voltage} volt" + }, "voltage-2": { "mappings": { "0": { From b0c8dbf0783e85e36bcf7ca76176e636df5e2efd Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 6 Oct 2021 00:45:16 +0200 Subject: [PATCH 110/110] Remove obsolete step from github actions --- .github/workflows/theme_validation_and_deploy.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/theme_validation_and_deploy.yml b/.github/workflows/theme_validation_and_deploy.yml index fd7efe0d8..76ccd8159 100644 --- a/.github/workflows/theme_validation_and_deploy.yml +++ b/.github/workflows/theme_validation_and_deploy.yml @@ -23,9 +23,6 @@ jobs: - name: create generated dir run: mkdir ./assets/generated - - name: create stub themes - run: "echo '{\"layers\": [], \"themes\": []}' > ./assets/generated/known_layers_and_themes.json" - - name: Prepare deploy run: npm run prepare-deploy