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 0a559a77cf..f9e6d485fc 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 27f4225b9a..7440a067a2 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 b650d819ba..4d4110935f 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 b3b10ac2ab..4deb3e01f7 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 13f2d287e5..8d8973b7b7 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 b7628b51b3..957a74db48 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 056765e58d..da4a4bc204 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 9484441290..0306d72849 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 161489fb52..713b2b65ba 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 a1a4c91494..eeebd2e5b9 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 6b0afe450f..f353809015 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 0000000000..ecf00cd66d --- /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 0000000000..92774e2f19 --- /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 0000000000..1d690bae68 --- /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 ef6ae16987..6af9e5a57a 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 e48e5805f6..09709dc9fa 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 f03166d07c..3068d25fd2 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 9152bf463c..415c17cd18 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 ee8c836fec..c3cadc1e09 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 e34a83cb39..b57b6bab3d 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 4b127a7dac..86cacab201 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 0000000000..e156438b11 --- /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 0000000000..bf5f1b9db0 --- /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 0000000000..56d57373ee --- /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 0000000000..0c6c0e5c46 --- /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 0000000000..ced6a1fb13 --- /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 0000000000..0c6fb07154 --- /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 0000000000..d97172c5db --- /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 797e8d341a..2a1ecef841 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 11e2fd8715..a26f4a025a 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 84a89327e2..0c28e14888 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 5d55518ba9..1f614cfec5 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 2a8c449cd7..94eaf954be 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 d97172c5db..552f9c1eb8 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 0000000000..9d0cb6e3b0 --- /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 e464a60b8e..b3b9211952 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 87d91bb0ce..0000000000 --- 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 718f711b12..0000000000 --- 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 0c28e14888..0000000000 --- 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 354adb75ab..0000000000 --- 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 3ae5c90152..0000000000 --- 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 0000000000..296f318b75 --- /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 3d1b794c6d..17d6db690c 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 0000000000..67a12af35f --- /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 0000000000..165f672cf3 --- /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 e072b948ea..c8c28b5c01 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 d48cc5f4bd..de3157da2f 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 458b278d0f..42b0b0ba31 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 0000000000..6237a2ddb2 --- /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 b2e5fba149..54e1137a04 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 0000000000..e69de29bb2 diff --git a/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts b/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts new file mode 100644 index 0000000000..931b85d3c0 --- /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 0000000000..36b0c1b015 --- /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 0000000000..e69de29bb2 diff --git a/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts b/Logic/FeatureSource/TiledFeatureSource/TileHierarchy.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts b/Logic/FeatureSource/TiledFeatureSource/TiledFromLocalStorageSource.ts new file mode 100644 index 0000000000..58c1fcaebd --- /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 e69de29bb2..0000000000 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 c8c28b5c01..0000000000 --- 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 0000000000..7ffe1b02a2 --- /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 0000000000..768a935fc4 --- /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 0000000000..e69de29bb2 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 ced30b29f8..661e51d07c 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 0000000000..e69de29bb2 diff --git a/UI/ShowDataLayer/ShowDataMultiLayer.ts b/UI/ShowDataLayer/ShowDataMultiLayer.ts new file mode 100644 index 0000000000..5bff575cbc --- /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 e3e61ce2ee..353192dc3c 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 1a2175dfc2..96c43bda33 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 4d4110935f..e788c76165 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 5a63d5cb28..07ff7f4a79 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 9c954bdfb4..6a4c2da254 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 4deb3e01f7..dd75ac3562 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 9d0cb6e3b0..9cb9ce7f2e 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 b3b9211952..2408464700 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 9b4f2271dc..0507c733d3 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 5e91b4567f..ec4a052be4 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 624658e798..791882e117 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 296f318b75..bb39660fa8 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 17d6db690c..574258455a 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 67a12af35f..6848f9f261 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 165f672cf3..e6d24fbca4 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 de3157da2f..c1c846602f 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 42b0b0ba31..99f4224781 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 6237a2ddb2..d4c316ec4c 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 7ffe1b02a2..0cc58d6569 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 54e1137a04..37ee94b8a0 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 e69de29bb2..357db85d41 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 931b85d3c0..43021587c0 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 36b0c1b015..93b51c6ad6 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 e69de29bb2..d905d2f65e 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 768a935fc4..10d0c17425 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 58c1fcaebd..7f3e5776e6 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 81cc3d066e..9fde927990 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 72a593d299..3f992dbce2 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 1f614cfec5..de17314470 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 f91d794fa0..575c114f7c 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 44915d4e12..55dd996817 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 f6ef4c83e9..2f82889e36 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 735b495126..f0528e77dc 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 7440a067a2..6e8f3e0fcd 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 d1c85f2266..705c6174a7 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 539ab8c6fb..0b62cc0cce 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 3068d25fd2..f337b1a198 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 b57b6bab3d..1128101123 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 db9d21f4fb..1d223bd2bd 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 27e9a251f1..8c08873cbc 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 947536b05e..876265b861 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 0c8cf36008..26909a00e0 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 e69de29bb2..a55bc7e386 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 353a4c38b6..2170eb4967 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 2d322a9612..7e70fa3670 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 7a0bcef815..542326314d 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 e291bf6b78..466b8d2172 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 a73ac1ce57..eba8b60b33 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 1d690bae68..f62b912fd8 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 db58abac68..b3e848d4d9 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 6a3729bc56..673f1d3256 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 62efc2eadd..fe1ef5c655 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 aea7561b06..f1a113daa8 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 69835e105d..7614077c5d 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 6c6b15948e..108065b68b 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 3162f5a875..4eb39d7e95 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 875fce9cbe..a18c887f80 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 9185cf24ca..ca0980592b 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 661e51d07c..927a1008d6 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 e69de29bb2..3494723516 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 5bff575cbc..924274586f 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 6af9e5a57a..7814cbbdcf 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 4cfc525209..606dccb32d 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 7663f584bf..f6acd494bb 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 e70dc2b787..fd1043c8a6 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 625dfa0ead..0862fbb379 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 9dee5a8a36..2134d0fba7 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 4b45e98993..4616fae02d 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 14e03b4d99..21cb172123 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 552f9c1eb8..654bb21773 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 06f2ea8510..0614e40a2b 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 09709dc9fa..fdfc46fe54 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 d7910039a3..9149f19660 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 d774fea795..3b24c50cde 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 94eaf954be..e96960d801 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 8baeb62fdc..31f2aaa7d3 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 702c40f4dd..d150215e74 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 c043bc2245..c894d54429 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 466b8d2172..7467158264 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 be3f1d6e54..db93b4f8fc 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 9dcb0f1dd7..cb2aa258f8 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 4f2b63c2d7..30aab5b27e 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 353192dc3c..3f591a3d14 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 07ff7f4a79..95a744df6d 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 6a4c2da254..2dcd8450f9 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 ec4a052be4..80f1949f18 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 c1c846602f..0000000000 --- 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 8c08873cbc..54af37fd46 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 927a1008d6..73a68247cb 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 95a744df6d..c76febd41d 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 520be0ed8d..b5bc6f1516 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 dd75ac3562..8b6834e09e 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 9cb9ce7f2e..331168bb8e 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 0507c733d3..bab2225bf6 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 80f1949f18..2fe254eb1f 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 bb39660fa8..d16e2c5588 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 574258455a..fb349ae5d5 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 0000000000..f4619b2aa8 --- /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 d4c316ec4c..ad8d7be5d3 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 10d0c17425..7b9f44b9cb 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 7f3e5776e6..0e4e091a32 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 36bafcbeea..784f4d4dc6 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 86cacab201..4841180bdd 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 178a269278..25542bf18b 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 0308ca8f61..784b192ba1 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 0000000000..dfca2d0fa0 --- /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 591051fca2..0000000000 --- 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 8ee1731e43..cee912dca1 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 f36ffa4e0e..aac1f08e9f 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 a09c1b25ce..a10ec8d0c0 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 705c6174a7..42282ea2a6 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 ff1df3616b..0c463c689e 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 065766e47a..36ecc76803 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 0b62cc0cce..b3f31280e5 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 54af37fd46..105e7ef059 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 26909a00e0..963d39db1f 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 a55bc7e386..5ebf74fc75 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 eba8b60b33..a4a3e826a6 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 673f1d3256..57f651e9ae 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 a18c887f80..a350e38d36 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 694ef48cb2..b7e33546ba 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 ca0980592b..01fb00d05b 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 73a68247cb..cdda7889b8 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 606dccb32d..f3e82d5740 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 a2cf274dd6..7f2c93fcf8 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 876656d1f5..0d033f2d72 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 f6acd494bb..3645da8555 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 f8e8840dbd..0bee39135f 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 dcff182373..385b171528 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 1b4c786d42..7c8df5cd0d 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 b91804ea97..7446095d95 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 8b607ea37a..e9d9b4e957 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 f863483ddf..a0b6cb896c 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 94a3db1b42..b6e4509d65 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 d150215e74..3551ba5f8e 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 c894d54429..5a8377125f 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 c34a6c3033..86a1c8f6ec 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 c83d9efa19..ac21096441 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 49254fd62b..9b3d713ac0 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 a2f7cca479..0b6cf3b472 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 563c5ade6b..e26dae4d6d 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 0000000000..8a5174da38 --- /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 96afa16fcb..a85d3c58dd 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 8eff8b56db..bd0ec56576 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 f080c0aa6b..971b64396e 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 d4b071d6ed..cb6cc27565 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 3d316535a8..e0ffdee101 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 0975621e1d..fd630257d0 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 e3e2455bf3..01e09a7175 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 e788c76165..1e7ace7cd9 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 b5bc6f1516..49661f289c 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 2fe254eb1f..c6206eac4c 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 bab2225bf6..a35879d960 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 0e4e091a32..e88a1d82d2 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 dfca2d0fa0..42562c035d 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 cee912dca1..085aab4130 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 aac1f08e9f..83545b30ab 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 a10ec8d0c0..8027a18d9c 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 291b49a4fc..fadd9c1059 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 2f82889e36..de6b65287e 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 3c312ce2e3..856ab2736f 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 1128101123..dd0d472edc 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 f3e82d5740..9f13734985 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 e9d9b4e957..9d09f0c7a4 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 8a5174da38..0b5261f136 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 0000000000..6e4ed77a9a --- /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 bd0ec56576..1a25a0afc8 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 0d033f2d72..876656d1f5 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 cb2aa258f8..9c5ce26fdb 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 9d09f0c7a4..45ad1a90c0 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 9821d40399..709534fcc2 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 b6749039b8..8c7d6b5148 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 f5f59afd81..2bdc852c92 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 5ebf74fc75..00fbc0f093 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 0bee39135f..b3b653d4fc 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 60103264f6..c8b17cc731 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 8b6834e09e..04f7a64f6a 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 654bb21773..e43f867ed6 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 9fa81f836f..f95b7d284f 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 8ac21489c2..132d0eed63 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 9fa81f836f..292f3f9949 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 3c57957da6..7803889991 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 bf49576eef..377dd430d3 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 439aac490e..6142ea9a20 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 0000000000..e3d669e7cd --- /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 377dd430d3..bb063beece 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 0eccf7729b..777dd921cf 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 7803889991..8981f9c641 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 c8b17cc731..f7fd84e3da 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 ca0c0cdaaf..0000000000 --- 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 7d9320cc49..fe744d0b2c 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 62f74e0ae2..297e470e3e 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 316bb7aa70..3b272cc48b 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 c3cadc1e09..18c9fe3b4e 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 00fbc0f093..1761d1adaa 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 39714ef8a8..f33620e806 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 e43f867ed6..b89d25e605 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 5cced5a6b9..73e97e9a43 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 c36b22b771..0bc6506d1e 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 1e7ace7cd9..7ddafacd87 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 04f7a64f6a..5839fca086 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 c6206eac4c..1e033dad77 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 fb349ae5d5..44ae285432 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 6848f9f261..70d5a566ce 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 e6d24fbca4..6222fc729d 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 99f4224781..b310970617 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 ad8d7be5d3..b3a476f5fd 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 0cc58d6569..d5795a1d51 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 37ee94b8a0..cb36c4b21a 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 357db85d41..8843b182a8 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 43021587c0..a6ef04458c 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 62a7c9e5ec..2dcf3e6d2b 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 e88a1d82d2..3e879bd4a3 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 66fd90053b..dd806ded44 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 7841c899b1..1f0f097b76 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 4325d3e37f..f85f3228a3 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 3f992dbce2..ae5808a01d 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 32c7625235..cd5aea5d5d 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 5745fa4ad1..c7eda184b4 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 01d18f8cd8..5a5e826120 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 6e8f3e0fcd..4a2dac23b1 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 d6f212ac60..66ca7227b1 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 18c9fe3b4e..2ed02ae6db 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 dd0d472edc..b632d8a29b 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 e1dba5532f..215d5a76f0 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 4a158ec0a5..da8114f8ca 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 fbc3bb564e..cf53daf10d 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 99462acbdb..a11db93192 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 30040482fe..9afd9b8155 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 a5ee19266c..fe3f2cf096 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 a350e38d36..0621eccc87 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 4e5c313f5b..4c5a13ea51 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 01fb00d05b..b1df0a5fa8 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 0000000000..82e247f742 --- /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 cdda7889b8..278ea35974 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 3494723516..2323ce0671 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 0000000000..a2fa322b6f --- /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 9f13734985..477598f796 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 c726b52dd8..03dc010ee6 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 3645da8555..8b06519339 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 26dbcc65ad..64b18a306e 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 786d7e2206..a4715ce5ac 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 a43292dbf9..d94f88597a 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 fe40e2d342..3a8fb3e8e9 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 5703e2ed50..f643399382 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 0f9817e46d..75622d3a5b 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 9c82e4d443..9c4f2a1894 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 1f9b704f89..c7ec682d10 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 7b24124e5f..592b9f5445 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 b193b4dafd..16745dc60c 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 44a2520dbb..143fcddc38 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 85a91aa7d3..09312a9ea9 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 7446095d95..8af56b8866 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 2e1016d77b..47a6267368 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 f3e28a1ef4..08a87063c6 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 416f9d1614..fed26b5607 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 33d83c0f5d..fec9038cc6 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 60d6e8a234..58b350f89b 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 229a32751f..7c9aec38bd 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 37efacae04..dee3ca22f7 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 538bc0414f..98bf595744 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 c5cdd37959..c6e7a1f84d 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 e34cdce30b..52d757a9da 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 9aeb9e4983..0f8004e2c0 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 4616fae02d..f0e921d387 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 2bdd0cbad6..3ab5a6b58f 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 bb25e8bfd2..ca322052ce 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 02e26cdbd2..d9df14a7b3 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 bd824ee670..5c65a702a5 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 418aa36bc4..2908a88b47 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 09209f3f41..706268e3d9 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 21cb172123..a87466742e 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 8672982153..752d793b3d 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 7f1fdeebda..4db24cdd69 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 001045fd11..a61633b23b 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 be5567d5dc..c09b548e8a 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 e343d9ea20..1d1567de42 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 ea26fdeca8..52961293a3 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 b89d25e605..7de4c3a366 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 2a1ecef841..eb95d85ebb 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 5ee75eb7ef..6e56d5f216 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 3551ba5f8e..22cdd0d931 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 5a8377125f..a5a915bdeb 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 ce05cf32b3..6fda20b0b5 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 c7eda184b4..c02ed04e4e 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 8b06519339..abd2824032 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 7ddafacd87..20181fc145 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 f4619b2aa8..0a1c67f411 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 83545b30ab..828ba7d781 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 dcf0d135aa..aae51d4eee 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 5a5e826120..5d955eb043 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 8027a18d9c..d95e0fa49c 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 105e7ef059..04b9728b3c 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 57f651e9ae..fb2c93a0a9 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 7de4c3a366..d4708deac9 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 fdfc46fe54..9687f11a99 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 8fed7dd348..e2b1d65916 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 0bc6506d1e..c3e57766a1 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 1e033dad77..a24d6f516c 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 856ab2736f..887649fc4d 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 b632d8a29b..9e63250390 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 82e247f742..e4085f2cd6 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 278ea35974..0d02bcd4e1 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 bba09af121..59c1d03c14 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 52961293a3..c609a43f11 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 c3e57766a1..19f1e33408 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 dd806ded44..cfef09e5d2 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 b74f0f5425..a938e9c725 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 963d39db1f..e276c82810 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 86a1c8f6ec..c70e45da58 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 7a556b20d2..49447afd5e 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 19f1e33408..f3593556fd 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 70d5a566ce..65c6df0ecc 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 c02ed04e4e..2a85e62924 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 7f1b0c8de7..91c9e0015c 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 6c387270fc..68ffb44832 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 464919d764..2dd9f69d69 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 c49f9f3eba..7151e38542 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 6e3fae5f72..249b3dacbf 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 fb2c93a0a9..5dca2cfc2b 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 01e2853f9c..1a581b8a0b 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 803d39ffac..e5977e8bbc 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 ae3a12b1dc..f3caa23219 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 111afe533a..73aced6ad6 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 08e33a2292..4a5b04f533 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 c05bb64530..1bac9814d2 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 5a3551d52e..dab52299c9 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 d5576a1b8d..cc21f23f8c 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 9f7fedf32d..c8ee818949 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 9cea92fdeb..53dea9cc87 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 0000000000..398ce8f723 --- /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 0c6fb07154..7d805cee45 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 d4708deac9..8dd07bf47d 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 49447afd5e..372402648e 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 35cfb010cb..57dbbf0ba9 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 f3593556fd..202e64149e 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 20181fc145..d77f326e5b 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 0000000000..e642c6ce3e --- /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 2dcd8450f9..f39d1106f0 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 5839fca086..76c28c5a9b 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 a24d6f516c..fab051aa56 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 791882e117..7d603d1f6b 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 d16e2c5588..f9a0cbe1a1 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 44ae285432..b1797d0aec 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 65c6df0ecc..6b433f88d0 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 6222fc729d..301f1bc557 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 b310970617..6835767363 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 b3a476f5fd..b06aae6d27 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 0000000000..2bed332987 --- /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 d905d2f65e..f2e43069d1 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 d8ab294ceb..844754854d 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 2dcf3e6d2b..4950ea328f 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 3e879bd4a3..4b578b7da2 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 cfef09e5d2..3fa10f7a23 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 5d955eb043..4e1a9297a3 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 d95e0fa49c..4538622d12 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 de6b65287e..2bf1b4e705 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 215d5a76f0..b61f192a09 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 04b9728b3c..85dfc9cfdc 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 e276c82810..32cdadbd63 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 1761d1adaa..110235437a 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 7e70fa3670..9b1ddb0132 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 7467158264..500d28e191 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 a4a3e826a6..f6574f04bc 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 b3e848d4d9..7ee2a74e64 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 e5a41b733b..0000000000 --- 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 5dca2cfc2b..ca758ab80a 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 f1a113daa8..54879b9b25 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 0621eccc87..262d9a7db0 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 b1df0a5fa8..17e1bd013b 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 e4085f2cd6..f313e741f7 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 477598f796..aced008ad6 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 571ff46e22..d7930f8547 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 c8ee818949..da9b3ae90f 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 929ea6c401..d113492add 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 3a8fb3e8e9..c0c85b13be 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 d9df14a7b3..88296088dc 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 c09b548e8a..7bef2f129f 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 3108920033..c96912b5d7 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 c70e45da58..e758de8cd8 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 372402648e..56e07acaf1 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 0b6cf3b472..9d7b114f50 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 202e64149e..40932dfd27 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 96c43bda33..d193d81329 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 d77f326e5b..2487039542 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 49661f289c..699a33aa84 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 fab051aa56..d65771da2a 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 2bed332987..a7485facff 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 25542bf18b..68919bd126 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 828ba7d781..b4bf3b4eca 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 a938e9c725..4b047fc7df 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 85dfc9cfdc..0af8e5a648 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 9b1ddb0132..4715567982 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 542326314d..c18c8d2875 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 500d28e191..f23d987479 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 f6574f04bc..8ee36ecec6 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 9d8fa44f93..01e4b9f95b 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 9b97ef4417..32e1b0ce08 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 8c4add4d97..75c5b6f556 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 62f74e0ae2..006b1823ac 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 2a80535236..432541395d 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 fe8f8bc987..147ad0f50a 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 262d9a7db0..e826773abd 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 7814cbbdcf..5b15cfdaa6 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 aced008ad6..424784205f 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 c0c85b13be..bc251907b8 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 53dea9cc87..08b7c8556e 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 88296088dc..2896e5fbc4 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 7bef2f129f..aeea7f8064 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 e2b1d65916..b62768bef6 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 ca03e3fb58..688dfd2fed 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 f9d8ab6da8..4bd9edfad8 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 d13b4e9c0e..f5d1a14150 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 f23d987479..552f6f3576 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 f62b912fd8..1604052647 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 ca758ab80a..5e8a7b46d7 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 fe3f2cf096..800eec53cb 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 432541395d..4eec0b85c5 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 147ad0f50a..fe8f8bc987 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 b81e82f47e..6025ff890d 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 5f0881d293..ea6839e14c 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 0d02bcd4e1..ade1860aea 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 a2fa322b6f..1bb6367277 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 f313e741f7..4b1685981d 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 40932dfd27..d0eb8b0d43 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 699a33aa84..91ec89b0c8 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 91193ea6d1..ae51094aea 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 d65771da2a..e8d270f0b6 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 6b433f88d0..0c2c9d92ae 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 2a85e62924..ee4209f4ae 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 b4bf3b4eca..570bf553be 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 20fbbbcbf9..fae2fd70ba 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 91c9e0015c..a7f79bf39f 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 0000000000..ea6760612d --- /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 aeea7f8064..2b303ea145 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 a1e3dee82a..e3220df4d6 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 2487039542..fa5de3eed3 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 e8d270f0b6..3db3a039d4 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 2bf1b4e705..47b92e278a 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 3a993c8e06..0000000000 --- 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 a7f79bf39f..b9855a2930 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 887649fc4d..73caee7929 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 9e63250390..4f85516325 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 0af8e5a648..00dac3bb50 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 424784205f..bd5cd62a5a 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 0000000000..374abea1e6 --- /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 5a94c40ad6..af5bbd10bc 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 1a581b8a0b..38f8e19703 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 40cce1af90..43af3910c7 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 f25c712458..6f240fa4fd 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 dab52299c9..c6fb976af1 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 1646d143e2..5459e47cb7 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 dbe67a9afa..c414410fe6 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 da9b3ae90f..68bc21fe60 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 f0e921d387..1ed0b7e88b 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 301f1bc557..b341a0dd3b 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 8843b182a8..fcfdbea5a4 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 b9855a2930..d524c6689e 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 d98acb5820..b95c28ac54 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 7ab5758737..a3a428462c 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 9746ea958c..44e99ca4cd 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 abe9d2b7f3..a13231cd6f 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 009a50fc0b..52c61d899c 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 44f1a21bab..aa261176f8 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 2842a2d33f..5fb6aa09b0 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 7aacdc7aaa..54f3e27c2b 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 d5e8c40508..b8585841fe 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 e22f109440..c515216b93 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 8b133247b6..dec4d1973b 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 7c91508bba..64b52a16b9 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 602d1ea854..923a5d76d5 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 2c6485f51a..4e776b4523 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 90b74fe669..360a52c611 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 994ecb4863..ccacafa7f4 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 ea1b1ded29..c6f0ac35dc 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 875fce9cbe..adc4414727 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 75118da951..bbef4187a0 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 bf49576eef..bfdfad61a7 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 c34a6c3033..51c8177a1d 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 331168bb8e..22e6a7ba81 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 4b578b7da2..3ad933d7e4 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 45ad1a90c0..39f75ac78e 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 c2d089e513..0000000000 --- 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 3ad933d7e4..016537968e 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 e616e59e7e..c1fe3bb45d 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 0000000000..170829fe07 --- /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 1f0f097b76..0000000000 --- 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 0000000000..67154d945a --- /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 f85f3228a3..289dad1f8a 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 0000000000..b5954693c7 --- /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 ae5808a01d..45342bc140 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 0000000000..3bb7d3c91d --- /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 cd5aea5d5d..0000000000 --- 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 0000000000..cf4c1837b9 --- /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 42282ea2a6..5f51dc004b 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 d524c6689e..4e588a057c 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 7614077c5d..919a9ac171 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 9afd9b8155..f57911e93e 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 4e7d032d28..c20f520802 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 5b15cfdaa6..4aba67e7d6 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 1ed0b7e88b..c5345a55f2 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 9b3d713ac0..0000000000 --- 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 1a25a0afc8..0efa98dd4b 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 00dac3bb50..7279136b30 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 01e09a7175..3abf8e8e25 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 c1fe3bb45d..18589f635a 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 cf4c1837b9..b8940a5209 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 2dd9f69d69..7a032c6de3 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 5e8a7b46d7..ad6a73ec52 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 4aba67e7d6..5801e1d886 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 cc21f23f8c..64b6230c16 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 3abf8e8e25..43ce441d7d 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 fa5de3eed3..4f4f7a77f8 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 22e6a7ba81..2894d56b82 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 3db3a039d4..5d5b3e1663 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 0000000000..6b397f5408 --- /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 a7485facff..5641d5b980 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 844754854d..6fd3dae65c 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 016537968e..6f815f48d5 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 4b047fc7df..360bb54e5a 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 4e588a057c..855f9dd2b9 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 b61f192a09..da30e498ce 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 0efa98dd4b..6123681fb8 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 0000000000..6305b9a5dc --- /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 ea6760612d..d6b68d1133 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 888ee00a9f..42f09f53e9 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 68bc21fe60..8b10b35ee7 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 54b5f056cd..6f8dcb65fe 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 30aab5b27e..fb2e939bd4 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 c5345a55f2..7ebcab3b57 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 a3a428462c..c107bdc35e 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 d253c31611..b7a918c9ce 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 60330bb638..4ba738f837 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 249b3dacbf..ffcdbc3c92 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 0000000000..445b81c1e4 --- /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 f3caa23219..5a8385357d 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 4a5b04f533..1d0bab2f78 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 96d5a1ef7c..0445cb394a 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 4b76be6a72..ad7f9cc17b 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 0000000000..f813f20f06 --- /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 0306d72849..22ca1d0a4b 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 3935c515f9..e20cde9583 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 7ea31a10fa..80c3e557eb 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 e3d669e7cd..1f7b87e18f 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 9149f19660..a6472bf0f0 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 bddc9c2557..a4be84c4f4 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 15aa758322..32386eea3a 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 99462acbdb..ff2d75b835 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 d82f795d43..ee2e8d9506 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 aadc6d9ee1..f9920b1d6a 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 32386eea3a..affad21920 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 ff2d75b835..d0152bbd54 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 e20cde9583..9c9aa44714 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 1f7b87e18f..a4f2972838 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 a6472bf0f0..3a55d9b00c 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 a4be84c4f4..97668f8abd 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 54b89561a8..dfa5644685 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 27165f7761..0000000000 --- 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 06c0b7fdf6..fd7efe0d87 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 b5a33871a5..8f71a462a1 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 e3d669e7cd..568e2072f8 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 5a8385357d..a643a80253 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 4cbe196aed..821461d814 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 8900f0068d..bb4b25dced 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 0ba838cc7d..ae21e436ee 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 855f9dd2b9..52ed3c81b4 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 e642c6ce3e..a6f350cf83 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 5d5b3e1663..b0931766a6 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 f7fd84e3da..5c2e83ab6e 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 da30e498ce..06ce4b765e 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 ade1860aea..e180cdf5d3 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 c76febd41d..4401159846 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 18589f635a..92818379f2 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 67154d945a..efce6aa924 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 45342bc140..3fa25a534d 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 3bb7d3c91d..8576de5d90 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 b8940a5209..20f69a7e40 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 919a9ac171..b0bb60d804 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 c20f520802..9932da6164 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 821461d814..4cbe196aed 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 0000000000..e2b846cd8b --- /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 4c5a13ea51..f40118a05e 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 73caee7929..89439f529b 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 1e8fae148e..a2e55d71c9 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 4f85516325..8b10f6c54a 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 796c52e8db..15bff437af 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 8b10b35ee7..d7592efc74 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 7ebcab3b57..4b49e7465f 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 969f9e9b74..d97c235cba 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 b0931766a6..e34d882ad3 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 06ce4b765e..7a542a2135 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 75c5b6f556..fec58bb29f 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 e180cdf5d3..73c78f9455 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 bd5cd62a5a..5c04c95472 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 7dd34cd93e..e20e313f08 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 0000000000..8e0cc69297 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.json merge=json diff --git a/Docs/Development_deployment.md b/Docs/Development_deployment.md index aceae49e70..659ecc5d05 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 4cbe196aed..4ed8a97b92 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 8981f9c641..d0501ac778 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 5c2e83ab6e..6081d4af3f 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 d1b6d31294..702ae077cb 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 a643a80253..3441ee183b 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 fb2e939bd4..19a2babaa1 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 ae21e436ee..003476b3cc 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 a015f43013..9524e139d5 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 38a5868bc5..6599af377e 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 4ed8a97b92..4cbe196aed 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 6081d4af3f..2505a2740f 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 4401159846..73e69d72cd 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 52ed3c81b4..50cfcf7505 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 73c78f9455..b8771aa7a0 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 4b42959acd..25db913cd6 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 d0eb8b0d43..94500ec346 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 e34d882ad3..0dff975442 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 5f51dc004b..8401445d11 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 0000000000..e69de29bb2 diff --git a/UI/Input/FixedInputElement.ts b/UI/Input/FixedInputElement.ts index ee2e8d9506..479aba16cb 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 7653cc62f4..73310559b3 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 f40118a05e..5d7657fa57 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 ea6839e14c..06c5b3dc38 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 445b81c1e4..bc88af2d72 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 3441ee183b..850420aad3 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 2bd8d787a5..14567f5254 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 ad7f9cc17b..98d93f5856 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 e20e313f08..5dcdc0ce0b 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 8401445d11..c30363f97e 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 e69de29bb2..7c1260a280 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 8711dec0d5..03d0e3fdc4 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 0000000000..c3ab0fab77 --- /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 850420aad3..03be186b65 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 0d884dfe9d..d7202fda40 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 4cbe196aed..08e6fd3d2e 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 0000000000..6bc812f206 --- /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 4361e9503f..3c590d974d 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 e035d72d3d..451e5c227f 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 dd85639392..e3f35827bb 100644 --- a/test.html +++ b/test.html @@ -10,6 +10,8 @@ + + diff --git a/test.ts b/test.ts index 5dcdc0ce0b..49c4379e06 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 1373391a51..566ab402a8 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 06c5b3dc38..9f99b2bb2d 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 0000000000..a8ca9dfc9c --- /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 8576de5d90..ea333dfbd5 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 20f69a7e40..0d2f6c1d0f 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 0000000000..3b60a2e6bd --- /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 7c1260a280..34bde4fc51 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 03d0e3fdc4..c2636c1f33 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 b8771aa7a0..a0244a33fa 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 5801e1d886..de45911db0 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 c3ab0fab77..f5c582ed28 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 c6fb976af1..29d2e56da4 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 fa9a002621..8443b12134 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 d7202fda40..27dc3ac6c0 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 19a2babaa1..509749aa03 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 08e6fd3d2e..adb7b1ee9b 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 6bc812f206..99d25c4373 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 3c590d974d..d5cd064762 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 777dd921cf..a655722097 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,7 @@ + diff --git a/langs/en.json b/langs/en.json index 451e5c227f..306ea8e802 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 b62768bef6..9dcb4b54af 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 e3220df4d6..409d96feaa 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 49c4379e06..0c746b6ebe 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 29d2e56da4..786f5e87bc 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 94500ec346..8fafd7677d 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 ea333dfbd5..f24f58acae 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 47b92e278a..97911a3b6d 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 c30363f97e..10922ca14a 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 3b60a2e6bd..ae981ee822 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 34bde4fc51..dedfa144d9 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 2170eb4967..3df26f53de 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 ffcdbc3c92..336d77405f 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 54879b9b25..1bb57fb1c2 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 f5c582ed28..af3e27c7e4 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 306ea8e802..650963864f 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 2505a2740f..d7485c8743 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 a5a915bdeb..e207cf67e1 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 6123681fb8..bdb92afdb1 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 0000000000..000a772176 --- /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 ee4209f4ae..47d7f8a357 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 4538622d12..9c1efbae98 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 360bb54e5a..eb1a6a49d8 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 af3e27c7e4..7f0d392a60 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 0614e40a2b..1e98ae890b 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 97911a3b6d..257133307f 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 50cfcf7505..ab7c007cdb 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 22cdd0d931..5469b56451 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 e207cf67e1..1377b3153e 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 ab7c007cdb..76769cdae5 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 5d7657fa57..585a75fee9 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 9f99b2bb2d..576189175d 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 d7485c8743..4f8cc5a9d6 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 10922ca14a..55770bb713 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 576189175d..e95d3abfe7 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 7c895b9cb5..6ee720627e 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 0000000000..1918dfe9a3 --- /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 585a75fee9..2be62b01be 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 2b7fdfd7a1..1192ff8ee6 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 e95d3abfe7..40a68609db 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 0c746b6ebe..e6f2a99662 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 570bf553be..866c78c7a2 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 40a68609db..a4eb83b0fb 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 0dff975442..3aa0af3562 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 47d7f8a357..9f96e77309 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 eb1a6a49d8..2845223f8d 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 110235437a..9ec90bd313 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 3aef14044c..f16d1c5f6e 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 adb7b1ee9b..0549527564 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 99d25c4373..e636fb8dc2 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 1e98ae890b..e5e58a6f0a 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 76769cdae5..88348d1c4c 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 03be186b65..6b9974e298 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 14567f5254..9c529883fc 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 88348d1c4c..99f3d28bb7 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 336d77405f..f5e48161fd 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 99f3d28bb7..9a9250eae9 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 6b9974e298..55891d6e6f 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 9c529883fc..739c163b43 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 bb4b25dced..2a9ab0bce4 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 003476b3cc..7efa1a6d8a 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 0000000000..4295a1a751 --- /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 98d93f5856..b8d6da5cff 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 0000000000..c532e8118f --- /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 0445cb394a..967e0daffd 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 c532e8118f..0000000000 --- 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 55891d6e6f..49636eba5c 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 739c163b43..c630297c01 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 967e0daffd..dc2de4d3d2 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 2a9ab0bce4..ac297439b2 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 7efa1a6d8a..0ff5387f3f 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 fd7efe0d87..76ccd81598 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