commit
8034f9a83a
87 changed files with 4302 additions and 1063 deletions
|
@ -24,23 +24,23 @@ the switch ;) ). If you are using Visual Studio Code you can use
|
|||
a [WSL Remote](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) window, or use the
|
||||
Devcontainer (see more details later).
|
||||
|
||||
You need at least 3Gb available to run MapComplete.
|
||||
|
||||
To develop and build MapComplete, you
|
||||
|
||||
0. Make a fork and clone the repository. (We recommend a shallow clone with `git clone --filter=blob:none <repo>`)
|
||||
0. Install `python3` if you do not have it already
|
||||
1. Install `python3` if you do not have it already
|
||||
- On linux: `sudo apt install python3`
|
||||
- On windows: find the latest download on the [Python Releases for Windows page](https://www.python.org/downloads/windows/)
|
||||
0. Install the nodejs version specified in [/.tool-versions](/.tool-versions)
|
||||
2. Install the nodejs version specified in [/.tool-versions](/.tool-versions)
|
||||
- On linux: install npm first `sudo apt install npm`, then install `n` using npm: ` npm install -g n`, which can
|
||||
then install node with `n install <node-version>`
|
||||
- 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/
|
||||
0. Run `npm run init` which …
|
||||
then install node with `n install <node-version>`. You can [use asdf to manage your runtime versions](https://asdf-vm.com/).
|
||||
- Windows: install nodeJS: https://nodejs.org/en/download/
|
||||
3. 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`
|
||||
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`
|
||||
or `userlayout=true#<layout configuration>` as [Query parameter](URL_Parameters.md). Note that the shorter URLs (
|
||||
e.g. `bookcases.html`, `aed.html`, ...) _don't_ exist on the development version.
|
||||
|
||||
|
|
31
Docs/Personas/Xavier.md
Normal file
31
Docs/Personas/Xavier.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Persona: Xavier (activist mapping campaign, data activism)
|
||||
|
||||
## Background
|
||||
|
||||
Name: Xavier
|
||||
Age: 68
|
||||
Occupation: Cycling activists
|
||||
Computer skills: Basic
|
||||
|
||||
|
||||
## Interests & Influences
|
||||
|
||||
Xavier is volunteer with the local cycle association.
|
||||
Together, they want that more people take the bicycle and that cycling becomes safer.
|
||||
|
||||
## Motivations
|
||||
|
||||
In order to create a safer cycling environment, Xavier wants to urge the local municipality to remove bollards from the cycleways if those bollards don't add a lot of value.
|
||||
But then, they first need to know _where_ all those bollards are.
|
||||
|
||||
## Goals
|
||||
|
||||
Create a map of all bollards
|
||||
|
||||
## Needs and expectations
|
||||
|
||||
Easily add bollards to a map, easily show an overview map of where all the bollards are
|
||||
|
||||
## Pain points & frustrations
|
||||
|
||||
It is hard to create such a map and get an overview of this...
|
8
Docs/Personas/_README.md
Normal file
8
Docs/Personas/_README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Personas
|
||||
|
||||
In this directory, you'll find some possible user profiles and how they interact with MapComplete.
|
||||
|
||||
They serve as a baseline, to indicate what type of interactions are important to support for MapComplete.
|
||||
In other words, these persona's are a part of the vision on what MapComplete should (not) be.
|
||||
|
||||
Note that the UserTests might also give a good idea on where usability might go wrong in small, individual steps.
|
20
Docs/Personas/_TEMPLATE.md
Normal file
20
Docs/Personas/_TEMPLATE.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Persona: NAME (scenario summary)
|
||||
|
||||
## Background
|
||||
|
||||
Name:
|
||||
Age:
|
||||
Occupation:
|
||||
Computer skills:
|
||||
Demography:
|
||||
|
||||
|
||||
## Interests & Influences
|
||||
|
||||
## Motivations
|
||||
|
||||
## Goals
|
||||
|
||||
## Needs and expectations
|
||||
|
||||
## Pain points & frustrations
|
|
@ -76,40 +76,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -120,6 +122,10 @@
|
|||
"canonicalDenomination"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
|
|
|
@ -74,40 +74,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -117,6 +119,9 @@ export default {
|
|||
"required": [
|
||||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
|
|
|
@ -271,10 +271,7 @@
|
|||
"description": "If specified, a new point will only be snapped if it is within this range.\nDistance in meter\n\nDefault: 10",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredBackground"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
|
@ -426,7 +423,6 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/default_2"
|
||||
|
@ -524,40 +520,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -569,6 +567,10 @@
|
|||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
@ -865,6 +867,28 @@
|
|||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": {
|
||||
"description": "A snippet of css code",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cssClasses": {
|
||||
"description": "A snippet of css-classes. They can be space-separated",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -1421,6 +1445,7 @@
|
|||
"additionalProperties": false
|
||||
},
|
||||
"default_2": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
|
@ -1435,11 +1460,15 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DenominationConfigJson"
|
||||
}
|
||||
},
|
||||
"defaultInput": {
|
||||
"description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
|
@ -271,10 +271,7 @@ export default {
|
|||
"description": "If specified, a new point will only be snapped if it is within this range.\nDistance in meter\n\nDefault: 10",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredBackground"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
|
@ -426,7 +423,6 @@ export default {
|
|||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/default_2"
|
||||
|
@ -522,40 +518,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -566,6 +564,9 @@ export default {
|
|||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
@ -858,6 +859,28 @@ export default {
|
|||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": {
|
||||
"description": "A snippet of css code",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cssClasses": {
|
||||
"description": "A snippet of css-classes. They can be space-separated",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -1405,6 +1428,7 @@ export default {
|
|||
}
|
||||
},
|
||||
"default_2": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
|
@ -1419,11 +1443,15 @@ export default {
|
|||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DenominationConfigJson"
|
||||
}
|
||||
},
|
||||
"defaultInput": {
|
||||
"description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
|
@ -354,40 +354,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -399,6 +401,10 @@
|
|||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
@ -695,6 +701,28 @@
|
|||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": {
|
||||
"description": "A snippet of css code",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cssClasses": {
|
||||
"description": "A snippet of css-classes. They can be space-separated",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -1251,6 +1279,7 @@
|
|||
"additionalProperties": false
|
||||
},
|
||||
"default_2": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
|
@ -1265,11 +1294,15 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DenominationConfigJson"
|
||||
}
|
||||
},
|
||||
"defaultInput": {
|
||||
"description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -1594,10 +1627,7 @@
|
|||
"description": "If specified, a new point will only be snapped if it is within this range.\nDistance in meter\n\nDefault: 10",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredBackground"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
|
@ -1749,7 +1779,6 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/default_2"
|
||||
|
|
|
@ -352,40 +352,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -396,6 +398,9 @@ export default {
|
|||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
@ -688,6 +693,28 @@ export default {
|
|||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": {
|
||||
"description": "A snippet of css code",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cssClasses": {
|
||||
"description": "A snippet of css-classes. They can be space-separated",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -1235,6 +1262,7 @@ export default {
|
|||
}
|
||||
},
|
||||
"default_2": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
|
@ -1249,11 +1277,15 @@ export default {
|
|||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DenominationConfigJson"
|
||||
}
|
||||
},
|
||||
"defaultInput": {
|
||||
"description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -1575,10 +1607,7 @@ export default {
|
|||
"description": "If specified, a new point will only be snapped if it is within this range.\nDistance in meter\n\nDefault: 10",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredBackground"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
|
@ -1730,7 +1759,6 @@ export default {
|
|||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/default_2"
|
||||
|
|
|
@ -163,40 +163,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -208,6 +210,10 @@
|
|||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -161,40 +161,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -205,6 +207,9 @@ export default {
|
|||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -176,40 +176,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -221,6 +223,10 @@
|
|||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -174,40 +174,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -218,6 +220,9 @@ export default {
|
|||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -86,40 +86,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -130,6 +132,10 @@
|
|||
"canonicalDenomination"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
|
|
|
@ -84,40 +84,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -127,6 +129,9 @@ export default {
|
|||
"required": [
|
||||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
|
|
|
@ -80,6 +80,28 @@
|
|||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": {
|
||||
"description": "A snippet of css code",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cssClasses": {
|
||||
"description": "A snippet of css-classes. They can be space-separated",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -161,40 +183,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -206,6 +230,10 @@
|
|||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -80,6 +80,28 @@ export default {
|
|||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": {
|
||||
"description": "A snippet of css code",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cssClasses": {
|
||||
"description": "A snippet of css-classes. They can be space-separated",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -159,40 +181,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -203,6 +227,9 @@ export default {
|
|||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -169,40 +169,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -214,6 +216,10 @@
|
|||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -167,40 +167,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -211,6 +213,9 @@ export default {
|
|||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -108,40 +108,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -153,6 +155,10 @@
|
|||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -106,40 +106,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -150,6 +152,9 @@ export default {
|
|||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
|
|
@ -111,40 +111,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -156,6 +158,10 @@
|
|||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
@ -452,6 +458,28 @@
|
|||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": {
|
||||
"description": "A snippet of css code",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cssClasses": {
|
||||
"description": "A snippet of css-classes. They can be space-separated",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -1008,6 +1036,7 @@
|
|||
"additionalProperties": false
|
||||
},
|
||||
"default_2": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
|
@ -1022,11 +1051,15 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DenominationConfigJson"
|
||||
}
|
||||
},
|
||||
"defaultInput": {
|
||||
"description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
|
@ -109,40 +109,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -153,6 +155,9 @@ export default {
|
|||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one",
|
||||
"type": "object",
|
||||
|
@ -445,6 +450,28 @@ export default {
|
|||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": {
|
||||
"description": "A snippet of css code",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cssClasses": {
|
||||
"description": "A snippet of css-classes. They can be space-separated",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -992,6 +1019,7 @@ export default {
|
|||
}
|
||||
},
|
||||
"default_2": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
|
@ -1006,11 +1034,15 @@ export default {
|
|||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DenominationConfigJson"
|
||||
}
|
||||
},
|
||||
"defaultInput": {
|
||||
"description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
|
@ -13,11 +14,15 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DenominationConfigJson"
|
||||
}
|
||||
},
|
||||
"defaultInput": {
|
||||
"description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -100,40 +105,42 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -144,6 +151,10 @@
|
|||
"canonicalDenomination"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export default {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
|
@ -13,11 +14,15 @@ export default {
|
|||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DenominationConfigJson"
|
||||
}
|
||||
},
|
||||
"defaultInput": {
|
||||
"description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -98,40 +103,42 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
"useAsDefaultInput": {
|
||||
"description": "Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).\nIf unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes",
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Record<string,string>"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
|
@ -141,6 +148,9 @@ export default {
|
|||
"required": [
|
||||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"Record<string,string>": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
|
|
54
Docs/Sponsors.md
Normal file
54
Docs/Sponsors.md
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Sponsors
|
||||
|
||||
For transparency, here is a short overview of sponsors to MapComplete.
|
||||
This ties in closely to the history of the project as well.
|
||||
|
||||
## Current sponsors
|
||||
|
||||
There is a [Liberapay](https://liberapay.com/pietervdvn).
|
||||
|
||||
NL-Net is currently sponsoring Pietervdvn as well [for specific improvements](https://github.com/pietervdvn/MapComplete/issues?q=is%3Aissue+is%3Aopen+label%3ANLNet).
|
||||
|
||||
## Historical sponsors
|
||||
|
||||
The following organisations ordered a theme to Pieter Vander Vennet and thus sponsered the project.
|
||||
As these projects are finished, they have no relation with the project anymore.
|
||||
|
||||
Most recent projects are first:
|
||||
|
||||
### BOSA (OSOC 2022)
|
||||
|
||||
See https://osoc.be/editions/2022/removing-obstacles-with-open-data
|
||||
|
||||
|
||||
### Antwerpse Zuidrand (2020 - 2021)
|
||||
|
||||
Antwerpse Zuidrand commissioned a map with [playgrounds and hiking routes](https://mapcomplete.osm.be/speelplekken)
|
||||
|
||||
### OSM UK (2021)
|
||||
|
||||
OSM UK commissioned an [address importer](https://mapcomplete.osm.be/uk_addresses)
|
||||
|
||||
### Toerisme Vlaanderen (2021 - 2022)
|
||||
|
||||
Visit Flanders commissioned a theme with electrical charging stations for ebikes, benches, parks, playgrounds, ... and ordered the 'note import'-flow.
|
||||
For more information, see https://toerismevlaanderen.be/nl/pinjepunt
|
||||
|
||||
|
||||
### Natuurpunt and Provincie Oost-Vlaanderen (OSOC 2021)
|
||||
|
||||
See https://osoc.be/editions/2021/nature-moves and https://osoc.be/editions/2021/bikeinfrastructure
|
||||
|
||||
|
||||
### Brussels Mobility (OSOC 2020)
|
||||
|
||||
When the first version of Buurtnatuur.be was online, Brussels Mobility sponsored an [Open Summer of Code Project](https://osoc.be/editions/2020/cyclofix), resulting in the cyclofix theme.
|
||||
The codebase was refactored in order to support multiple themes just before the start of OSOC.
|
||||
|
||||
|
||||
### Belgian Green Party (2020)
|
||||
|
||||
The first version of what would later become 'MapComplete' was financed by the Belgian Green Party.
|
||||
With the Corona-lockdown in full swing, they wanted to do something with _nature and green spaces near people, maybe inventorizing all parks, forests and nature reserves_.
|
||||
|
||||
[The original website](https://buurtnatuur.be/) can still be visited, the oldest version still online.
|
63
Docs/UserTests/2023-01-02 Ad Hoc - cyclestreets.md
Normal file
63
Docs/UserTests/2023-01-02 Ad Hoc - cyclestreets.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
# Ad Hoc User test
|
||||
|
||||
Subject: K Vs
|
||||
Tech Skills: basic computer skills
|
||||
Demography: F, 50-60 yo
|
||||
Language: Dutch
|
||||
Medium: Android phone(s), DuckDuckGo browser + Fennec Browser
|
||||
|
||||
## Task
|
||||
|
||||
A street nearby has become a cyclestreet. Mark the correct segment of the street as cyclestreet - this will require *splitting* the screen.
|
||||
|
||||
## How it went
|
||||
|
||||
K takes her phone and opens up MC via the DuckDuckGo browser.
|
||||
She is not logged in.
|
||||
The button 'Login met OpenStreetMap indien je de kaart wilt aanpassen' is confusing, as she 'wants to change an attribute, not the geometry' (1)
|
||||
|
||||
As she has forgotten her password, she switches to the phone of the examinator.
|
||||
|
||||
She scrolls manually through all the themes. As the new road is actually part of a cycle zone and not a cycle street, the theme is considered but there is some doubt if it is truly the right theme (2). She opens the theme after all.
|
||||
|
||||
She is a bit confused as why the 'login'-button is lacking on the welcome screen. (3)
|
||||
|
||||
She proceeds to the map view.
|
||||
|
||||
As the street to turn into a cyclestreet is nearby, the street is already in view.
|
||||
However, the cyclestreet theme uses a 'shizophrenic' approach: cyclestreets are visible on all zoom levels, but non-cyclestreets are only visible when zoomed in a lot.
|
||||
As such, the street is only visible in the background layer. Zooming in _would_ show the street overlay.
|
||||
|
||||
The tester attempts to turn this street into a cyclestreet by long pressing the map, but this does nothing. (Long-pressing the map causes the 'new item' to popup on the screen if a new POI can be placed; but this theme does _not_ allow the creation of new POI).
|
||||
|
||||
Instead of zooming in, a search via the search bar is attempted. She slightly misspells the name and omits the 'street'-part, causing the map to jump to a small village on the other side of the world. (4) (5)
|
||||
|
||||
The examinator steps in to set the tester on the right track again and zoom in so that the 'all streets'-overlay is visible.
|
||||
|
||||
The tester continues the attempt by _long_ pressing the 'pencil'-icon on the street. Long pressing yields the right-click menu of fennec, to download the pencil icon. (6) Swiping 'back' removes it, but she swipes back twice accidentally, opening the theme that was opened previously. After some stumbling, the correct theme is opened again.
|
||||
|
||||
The examinator steps in to indicate that a _short_ press will open up the popup. The examinator also indicates that only a **part** of the street is a cyclestreet (the examinator has local knowledge about the actual situation there).
|
||||
|
||||
With the popup open, the button to 'split' ("Knip deze weg in kleinere segmenten (om andere eigenschappen toe te kennen per segment)") is easily found.
|
||||
|
||||
Tapping the map to add a cut is easily found. However, the 'confirm' button is hidden as the dialog is not scrolled into view completely. (7)
|
||||
While experimenting, an extra, unnecessary cut is made - but this unneccessary cut is easily removed again by tapping it again.
|
||||
|
||||
The test subject also insists on making a second cut, to be very precise; resulting a 10 meter stretch of road which is not a cyclestreet.
|
||||
|
||||
When the confirm-button is moved into view, the test subject is confused. The Dutch 'Knip weg' can be translated as 'cut the road' but also as 'remove cut'. (8)
|
||||
|
||||
When the cut is made, the shorter segment is selected and the popup of this segment opens. The tester however doesn't realize that it is the _other_ segment that should be marked as a cyclestreet. (9) The examinator steps in to close the popup, after which the tester opens the correct segment and marks it as a cyclestreet.
|
||||
|
||||
|
||||
## To improve
|
||||
|
||||
1. Change wording on the login button to 'if you want to make changes' 'als je iets wilt wijzigen'
|
||||
2. Dutch theme title should mention 'cycle zones' as well
|
||||
3. Add a 'welcome back <username>' to the welcome panel
|
||||
4. Rethink the search flow
|
||||
5. Think about the behaviour of a long press when no presets are defined. Zooming in would be acceptable
|
||||
6. Long-pressing/right-clicking a POI on the map should open the popup as well
|
||||
7. Scroll the cut-dialog into view when opening
|
||||
8. Don't use 'Knip', but use 'Deel deze weg op' in Dutch Translations
|
||||
9. Close the popup when a road is split
|
|
@ -120,7 +120,6 @@ export default class GeoLocationHandler {
|
|||
const state = this._state
|
||||
this.geolocationState.currentGPSLocation.addCallbackAndRun((location) => {
|
||||
if (location === undefined) {
|
||||
state.currentUserLocation?.features?.setData([])
|
||||
return
|
||||
}
|
||||
const feature = {
|
||||
|
|
|
@ -45,20 +45,6 @@ export default class SelectedFeatureHandler {
|
|||
|
||||
const self = this
|
||||
hash.addCallback(() => self.setSelectedElementFromHash())
|
||||
|
||||
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
|
||||
}
|
||||
self.setSelectedElementFromHash()
|
||||
})
|
||||
|
||||
this.initialLoad()
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@ import ScrollableFullScreen from "../../UI/Base/ScrollableFullScreen"
|
|||
import BaseUIElement from "../../UI/BaseUIElement"
|
||||
|
||||
/**
|
||||
* The stray-click-hanlders adds a marker to the map if no feature was clicked.
|
||||
* The stray-click-handler adds a marker to the map if no feature was clicked.
|
||||
* Shows the given uiToShow-element in the messagebox
|
||||
*
|
||||
* Note: the actual implementation is in StrayClickHandlerImplementation
|
||||
*/
|
||||
export default class StrayClickHandler {
|
||||
public static construct = (
|
||||
|
|
|
@ -178,7 +178,7 @@ export class BBox {
|
|||
])
|
||||
}
|
||||
|
||||
toLeaflet() {
|
||||
toLeaflet(): [[number, number], [number, number]] {
|
||||
return [
|
||||
[this.minLat, this.minLon],
|
||||
[this.maxLat, this.maxLon],
|
||||
|
|
|
@ -28,6 +28,8 @@ export default class UserDetails {
|
|||
}
|
||||
}
|
||||
|
||||
export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable"
|
||||
|
||||
export class OsmConnection {
|
||||
public static readonly oauth_configs = {
|
||||
osm: {
|
||||
|
@ -46,6 +48,13 @@ export class OsmConnection {
|
|||
public auth
|
||||
public userDetails: UIEventSource<UserDetails>
|
||||
public isLoggedIn: Store<boolean>
|
||||
public gpxServiceIsOnline: UIEventSource<OsmServiceState> = new UIEventSource<OsmServiceState>(
|
||||
"unknown"
|
||||
)
|
||||
public apiIsOnline: UIEventSource<OsmServiceState> = new UIEventSource<OsmServiceState>(
|
||||
"unknown"
|
||||
)
|
||||
|
||||
public loadingStatus = new UIEventSource<"not-attempted" | "loading" | "error" | "logged-in">(
|
||||
"not-attempted"
|
||||
)
|
||||
|
@ -62,7 +71,7 @@ export class OsmConnection {
|
|||
private readonly _singlePage: boolean
|
||||
private isChecking = false
|
||||
|
||||
constructor(options: {
|
||||
constructor(options?: {
|
||||
dryRun?: UIEventSource<boolean>
|
||||
fakeUser?: false | boolean
|
||||
oauth_token?: UIEventSource<string>
|
||||
|
@ -71,6 +80,7 @@ export class OsmConnection {
|
|||
osmConfiguration?: "osm" | "osm-test"
|
||||
attemptLogin?: true | boolean
|
||||
}) {
|
||||
options = options ?? {}
|
||||
this.fakeUser = options.fakeUser ?? false
|
||||
this._singlePage = options.singlePage ?? true
|
||||
this._oauth_config =
|
||||
|
@ -93,7 +103,13 @@ export class OsmConnection {
|
|||
ud.totalMessages = 42
|
||||
}
|
||||
const self = this
|
||||
this.isLoggedIn = this.userDetails.map((user) => user.loggedIn)
|
||||
this.UpdateCapabilities()
|
||||
this.isLoggedIn = this.userDetails.map(
|
||||
(user) =>
|
||||
user.loggedIn &&
|
||||
(self.apiIsOnline.data === "unknown" || self.apiIsOnline.data === "online"),
|
||||
[this.apiIsOnline]
|
||||
)
|
||||
this.isLoggedIn.addCallback((isLoggedIn) => {
|
||||
if (self.userDetails.data.loggedIn == false && isLoggedIn == true) {
|
||||
// We have an inconsistency: the userdetails say we _didn't_ log in, but this actor says we do
|
||||
|
@ -106,7 +122,10 @@ export class OsmConnection {
|
|||
|
||||
this.updateAuthObject()
|
||||
|
||||
this.preferencesHandler = new OsmPreferences(this.auth, this)
|
||||
this.preferencesHandler = new OsmPreferences(
|
||||
this.auth,
|
||||
<any /*This is needed to make the tests work*/>this
|
||||
)
|
||||
|
||||
if (options.oauth_token?.data !== undefined) {
|
||||
console.log(options.oauth_token.data)
|
||||
|
@ -130,7 +149,13 @@ export class OsmConnection {
|
|||
}
|
||||
|
||||
public CreateChangesetHandler(allElements: ElementStorage, changes: Changes) {
|
||||
return new ChangesetHandler(this._dryRun, this, allElements, changes, this.auth)
|
||||
return new ChangesetHandler(
|
||||
this._dryRun,
|
||||
<any>/*casting is needed to make the tests work*/ this,
|
||||
allElements,
|
||||
changes,
|
||||
this.auth
|
||||
)
|
||||
}
|
||||
|
||||
public GetPreference(
|
||||
|
@ -159,11 +184,17 @@ export class OsmConnection {
|
|||
this.loadingStatus.setData("not-attempted")
|
||||
}
|
||||
|
||||
/**
|
||||
* The backend host, without path or trailing '/'
|
||||
*
|
||||
* new OsmConnection().Backend() // => "https://www.openstreetmap.org"
|
||||
*/
|
||||
public Backend(): string {
|
||||
return this._oauth_config.url
|
||||
}
|
||||
|
||||
public AttemptLogin() {
|
||||
this.UpdateCapabilities()
|
||||
this.loadingStatus.setData("loading")
|
||||
if (this.fakeUser) {
|
||||
this.loadingStatus.setData("logged-in")
|
||||
|
@ -503,4 +534,26 @@ export class OsmConnection {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
private UpdateCapabilities(): void {
|
||||
const self = this
|
||||
this.FetchCapabilities().then(({ api, gpx }) => {
|
||||
self.apiIsOnline.setData(api)
|
||||
self.gpxServiceIsOnline.setData(gpx)
|
||||
})
|
||||
}
|
||||
|
||||
private async FetchCapabilities(): Promise<{ api: OsmServiceState; gpx: OsmServiceState }> {
|
||||
const result = await Utils.downloadAdvanced(this.Backend() + "/api/0.6/capabilities")
|
||||
if (result["content"] === undefined) {
|
||||
console.log("Something went wrong:", result)
|
||||
return { api: "unreachable", gpx: "unreachable" }
|
||||
}
|
||||
const xmlRaw = result["content"]
|
||||
const parsed = new DOMParser().parseFromString(xmlRaw, "text/xml")
|
||||
const statusEl = parsed.getElementsByTagName("status")[0]
|
||||
const api = <OsmServiceState>statusEl.getAttribute("api")
|
||||
const gpx = <OsmServiceState>statusEl.getAttribute("gpx")
|
||||
return { api, gpx }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ export abstract class OsmObject {
|
|||
if (rawData["error"] !== undefined && rawData["statuscode"] === 410) {
|
||||
return "deleted"
|
||||
}
|
||||
return rawData["contents"].elements[0].tags
|
||||
return rawData["content"].elements[0].tags
|
||||
}
|
||||
|
||||
static async DownloadObjectAsync(
|
||||
|
|
|
@ -230,10 +230,12 @@ export abstract class Store<T> {
|
|||
|
||||
const newSource = new UIEventSource<T>(this.data)
|
||||
|
||||
const self = this
|
||||
this.addCallback((latestData) => {
|
||||
window.setTimeout(() => {
|
||||
if (this.data == latestData) {
|
||||
// compare by reference
|
||||
if (self.data == latestData) {
|
||||
// compare by reference.
|
||||
// Note that 'latestData' and 'self.data' are both from the same UIEVentSource, but both are dereferenced at a different time
|
||||
newSource.setData(latestData)
|
||||
}
|
||||
}, millisToStabilize)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Utils } from "../Utils"
|
||||
|
||||
export default class Constants {
|
||||
public static vNumber = "0.25.3"
|
||||
public static vNumber = "0.25.4"
|
||||
|
||||
public static ImgurApiKey = "7070e7167f0a25a"
|
||||
public static readonly mapillary_client_token_v4 =
|
||||
|
|
|
@ -15,7 +15,7 @@ export class Denomination {
|
|||
private readonly _human: Translation
|
||||
private readonly _humanSingular?: Translation
|
||||
|
||||
constructor(json: DenominationConfigJson, context: string) {
|
||||
constructor(json: DenominationConfigJson, useAsDefaultInput: boolean, context: string) {
|
||||
context = `${context}.unit(${json.canonicalDenomination})`
|
||||
this.canonical = json.canonicalDenomination.trim()
|
||||
if (this.canonical === undefined) {
|
||||
|
@ -35,7 +35,7 @@ export class Denomination {
|
|||
throw `${context} uses the old 'default'-key. Use "useIfNoUnitGiven" or "useAsDefaultInput" instead`
|
||||
}
|
||||
this.useIfNoUnitGiven = json.useIfNoUnitGiven
|
||||
this.useAsDefaultInput = json.useAsDefaultInput ?? json.useIfNoUnitGiven
|
||||
this.useAsDefaultInput = useAsDefaultInput ?? json.useIfNoUnitGiven
|
||||
|
||||
this._human = Translations.T(json.human, context + "human")
|
||||
this._humanSingular = Translations.T(json.humanSingular, context + "humanSingular")
|
||||
|
@ -69,7 +69,7 @@ export class Denomination {
|
|||
* human: {
|
||||
* en: "meter"
|
||||
* }
|
||||
* }, "test")
|
||||
* }, false, "test")
|
||||
* unit.canonicalValue("42m", true) // =>"42 m"
|
||||
* unit.canonicalValue("42", true) // =>"42 m"
|
||||
* unit.canonicalValue("42 m", true) // =>"42 m"
|
||||
|
@ -84,7 +84,7 @@ export class Denomination {
|
|||
* human: {
|
||||
* en: "meter"
|
||||
* }
|
||||
* }, "test")
|
||||
* }, false, "test")
|
||||
* unit.canonicalValue("42m", true) // =>"42"
|
||||
* unit.canonicalValue("42", true) // =>"42"
|
||||
* unit.canonicalValue("42 m", true) // =>"42"
|
||||
|
|
|
@ -389,62 +389,7 @@ export interface LayerConfigJson {
|
|||
allowSplit?: boolean
|
||||
|
||||
/**
|
||||
* In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)
|
||||
*
|
||||
* Sometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)
|
||||
*
|
||||
* This brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)
|
||||
*
|
||||
* Not only do we want to write consistent data to OSM, we also want to present this consistently to the user.
|
||||
* This is handled by defining units.
|
||||
*
|
||||
* # Rendering
|
||||
*
|
||||
* To render a value with long (human) denomination, use {canonical(key)}
|
||||
*
|
||||
* # Usage
|
||||
*
|
||||
* First of all, you define which keys have units applied, for example:
|
||||
*
|
||||
* ```
|
||||
* units: [
|
||||
* appliesTo: ["maxspeed", "maxspeed:hgv", "maxspeed:bus"]
|
||||
* applicableUnits: [
|
||||
* ...
|
||||
* ]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* ApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:
|
||||
*
|
||||
* ```
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "km/h",
|
||||
* alternativeDenomination: ["km/u", "kmh", "kph"]
|
||||
* default: true,
|
||||
* human: {
|
||||
* en: "kilometer/hour",
|
||||
* nl: "kilometer/uur"
|
||||
* },
|
||||
* humanShort: {
|
||||
* en: "km/h",
|
||||
* nl: "km/u"
|
||||
* }
|
||||
* },
|
||||
* {
|
||||
* canoncialDenomination: "mph",
|
||||
* ... similar for miles an hour ...
|
||||
* }
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* If this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:
|
||||
* every value will be parsed and the canonical extension will be added add presented to the other parts of the code.
|
||||
*
|
||||
* Also, if a freeform text field is used, an extra dropdown with applicable denominations will be given
|
||||
*
|
||||
* @see UnitConfigJson
|
||||
*/
|
||||
units?: UnitConfigJson[]
|
||||
|
||||
|
|
|
@ -1,3 +1,61 @@
|
|||
/**
|
||||
* In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)
|
||||
*
|
||||
* Sometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)
|
||||
*
|
||||
* This brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)
|
||||
*
|
||||
* Not only do we want to write consistent data to OSM, we also want to present this consistently to the user.
|
||||
* This is handled by defining units.
|
||||
*
|
||||
* # Rendering
|
||||
*
|
||||
* To render a value with long (human) denomination, use {canonical(key)}
|
||||
*
|
||||
* # Usage
|
||||
*
|
||||
* First of all, you define which keys have units applied, for example:
|
||||
*
|
||||
* ```
|
||||
* units: [
|
||||
* appliesTo: ["maxspeed", "maxspeed:hgv", "maxspeed:bus"]
|
||||
* applicableUnits: [
|
||||
* ...
|
||||
* ]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* ApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:
|
||||
*
|
||||
* ```
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "km/h",
|
||||
* alternativeDenomination: ["km/u", "kmh", "kph"]
|
||||
* default: true,
|
||||
* human: {
|
||||
* en: "kilometer/hour",
|
||||
* nl: "kilometer/uur"
|
||||
* },
|
||||
* humanShort: {
|
||||
* en: "km/h",
|
||||
* nl: "km/u"
|
||||
* }
|
||||
* },
|
||||
* {
|
||||
* canoncialDenomination: "mph",
|
||||
* ... similar for miles an hour ...
|
||||
* }
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* If this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:
|
||||
* every value will be parsed and the canonical extension will be added add presented to the other parts of the code.
|
||||
*
|
||||
* Also, if a freeform text field is used, an extra dropdown with applicable denominations will be given
|
||||
*
|
||||
*/
|
||||
export default interface UnitConfigJson {
|
||||
/**
|
||||
* Every key from this list will be normalized.
|
||||
|
@ -11,9 +69,19 @@ export default interface UnitConfigJson {
|
|||
*/
|
||||
eraseInvalidValues?: boolean
|
||||
/**
|
||||
* The possible denominations
|
||||
* The possible denominations for this unit.
|
||||
* For length, denominations could be "meter", "kilometer", "miles", "foot"
|
||||
*/
|
||||
applicableUnits: DenominationConfigJson[]
|
||||
|
||||
/**
|
||||
* In some cases, the default denomination is not the most user friendly to input.
|
||||
* E.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.
|
||||
*
|
||||
* When a default input method should be used, this can be specified by setting the canonical denomination here, e.g.
|
||||
* `defaultInput: "cm"`. This must be a denomination which appears in the applicableUnits
|
||||
*/
|
||||
defaultInput?: string
|
||||
}
|
||||
|
||||
export interface DenominationConfigJson {
|
||||
|
@ -28,12 +96,6 @@ export interface DenominationConfigJson {
|
|||
*/
|
||||
useIfNoUnitGiven?: boolean | string[]
|
||||
|
||||
/**
|
||||
* Use this value as default denomination when the user inputs a value (e.g. to force using 'centimeters' instead of 'meters' by default).
|
||||
* If unset for all values, this will use 'useIfNoUnitGiven'. If at least one denomination has this set, this will default to false
|
||||
*/
|
||||
useAsDefaultInput?: boolean | string[]
|
||||
|
||||
/**
|
||||
* The canonical value for this denomination which will be added to the value in OSM.
|
||||
* e.g. "m" for meters
|
||||
|
@ -46,12 +108,15 @@ export interface DenominationConfigJson {
|
|||
|
||||
/**
|
||||
* The canonical denomination in the case that the unit is precisely '1'.
|
||||
* Used for display purposes
|
||||
* Used for display purposes only.
|
||||
*
|
||||
* E.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here
|
||||
*/
|
||||
canonicalDenominationSingular?: string
|
||||
|
||||
/**
|
||||
* A list of alternative values which can occur in the OSM database - used for parsing.
|
||||
* E.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well
|
||||
*/
|
||||
alternativeDenomination?: string[]
|
||||
|
||||
|
@ -62,16 +127,16 @@ export interface DenominationConfigJson {
|
|||
* "fr": "metre"
|
||||
* }
|
||||
*/
|
||||
human?: string | any
|
||||
human?: string | Record<string, string>
|
||||
|
||||
/**
|
||||
* The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.
|
||||
* {
|
||||
* "en": "minute",
|
||||
* "nl": "minuut"x²
|
||||
* "nl": "minuut"
|
||||
* }
|
||||
*/
|
||||
humanSingular?: string | any
|
||||
humanSingular?: string | Record<string, string>
|
||||
|
||||
/**
|
||||
* If set, then the canonical value will be prefixed instead, e.g. for '€'
|
||||
|
|
|
@ -60,6 +60,44 @@ export class Unit {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* // Should detect invalid defaultInput
|
||||
* let threwError = false
|
||||
* try{
|
||||
* Unit.fromJson({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "m",
|
||||
* useIfNoUnitGiven: true,
|
||||
* human: "meter"
|
||||
* }
|
||||
* ]
|
||||
* },"test")
|
||||
* }catch(e){
|
||||
* threwError = true
|
||||
* }
|
||||
* threwError // => true
|
||||
*
|
||||
* // Should work
|
||||
* Unit.fromJson({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "m",
|
||||
* useIfNoUnitGiven: true,
|
||||
* humen: "meter"
|
||||
* },
|
||||
* {
|
||||
* canonicalDenomination: "cm",
|
||||
* human: "centimeter"
|
||||
* }
|
||||
* ]
|
||||
* }, "test")
|
||||
*/
|
||||
static fromJson(json: UnitConfigJson, ctx: string) {
|
||||
const appliesTo = json.appliesToKey
|
||||
for (let i = 0; i < appliesTo.length; i++) {
|
||||
|
@ -74,14 +112,15 @@ export class Unit {
|
|||
}
|
||||
// Some keys do have unit handling
|
||||
|
||||
if (json.applicableUnits.some((denom) => denom.useAsDefaultInput !== undefined)) {
|
||||
json.applicableUnits.forEach((denom) => {
|
||||
denom.useAsDefaultInput = denom.useAsDefaultInput ?? false
|
||||
})
|
||||
}
|
||||
|
||||
const applicable = json.applicableUnits.map(
|
||||
(u, i) => new Denomination(u, `${ctx}.units[${i}]`)
|
||||
(u, i) =>
|
||||
new Denomination(
|
||||
u,
|
||||
u.canonicalDenomination === undefined
|
||||
? undefined
|
||||
: u.canonicalDenomination.trim() === json.defaultInput,
|
||||
`${ctx}.units[${i}]`
|
||||
)
|
||||
)
|
||||
return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false)
|
||||
}
|
||||
|
|
|
@ -8,11 +8,8 @@ import { Utils } from "../Utils"
|
|||
import LanguagePicker1 from "./LanguagePicker"
|
||||
import IndexText from "./BigComponents/IndexText"
|
||||
import FeaturedMessage from "./BigComponents/FeaturedMessage"
|
||||
import Toggle from "./Input/Toggle"
|
||||
import { SubtleButton } from "./Base/SubtleButton"
|
||||
import { VariableUiElement } from "./Base/VariableUIElement"
|
||||
import Svg from "../Svg"
|
||||
import { ImportViewerLinks } from "./BigComponents/UserInformation"
|
||||
import { LoginToggle } from "./Popup/LoginButton"
|
||||
import UserSurveyPanel from "./UserSurveyPanel"
|
||||
|
||||
export default class AllThemesGui {
|
||||
|
@ -31,13 +28,7 @@ export default class AllThemesGui {
|
|||
new FeaturedMessage().SetClass("mb-4 block"),
|
||||
new Combine([new UserSurveyPanel()]).SetClass("flex justify-center"),
|
||||
new MoreScreen(state, true),
|
||||
new Toggle(
|
||||
undefined,
|
||||
new SubtleButton(undefined, Translations.t.index.logIn)
|
||||
.SetStyle("height:min-content")
|
||||
.onClick(() => state.osmConnection.AttemptLogin()),
|
||||
state.osmConnection.isLoggedIn
|
||||
),
|
||||
new LoginToggle(undefined, Translations.t.index.logIn, state),
|
||||
new ImportViewerLinks(state.osmConnection),
|
||||
Translations.t.general.aboutMapcomplete
|
||||
.Subs({ osmcha_link: Utils.OsmChaLinkFor(7) })
|
||||
|
|
|
@ -414,6 +414,7 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
|
|||
map.on("contextmenu", function (e) {
|
||||
// @ts-ignore
|
||||
lastClickLocation?.setData({ lat: e.latlng.lat, lon: e.latlng.lng })
|
||||
map.setZoom(map.getZoom() + 1)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ export default class ScrollableFullScreen {
|
|||
private hashToShow: string
|
||||
private _fullscreencomponent: BaseUIElement
|
||||
private _resetScrollSignal: UIEventSource<void> = new UIEventSource<void>(undefined)
|
||||
private _setHash: boolean
|
||||
|
||||
constructor(
|
||||
title: (options: { mode: string }) => BaseUIElement,
|
||||
|
@ -34,13 +35,14 @@ export default class ScrollableFullScreen {
|
|||
hashToShow: string,
|
||||
isShown: UIEventSource<boolean> = new UIEventSource<boolean>(false),
|
||||
options?: {
|
||||
setHash?: true | boolean
|
||||
setHash?: boolean
|
||||
}
|
||||
) {
|
||||
this.hashToShow = hashToShow
|
||||
this.isShown = isShown
|
||||
this._setHash = options?.setHash ?? true
|
||||
|
||||
if (hashToShow === undefined) {
|
||||
if ((hashToShow === undefined || hashToShow === "") && this._setHash) {
|
||||
throw "HashToShow should be defined as it is vital for the 'back' key functionality"
|
||||
}
|
||||
|
||||
|
@ -55,8 +57,7 @@ export default class ScrollableFullScreen {
|
|||
)
|
||||
|
||||
const self = this
|
||||
const setHash = options?.setHash ?? true
|
||||
if (setHash) {
|
||||
if (this._setHash) {
|
||||
Hash.hash.addCallback((h) => {
|
||||
if (h === undefined) {
|
||||
isShown.setData(false)
|
||||
|
@ -64,13 +65,10 @@ export default class ScrollableFullScreen {
|
|||
})
|
||||
}
|
||||
|
||||
isShown.addCallback((isShown) => {
|
||||
isShown.addCallbackD((isShown) => {
|
||||
if (isShown) {
|
||||
// We first must set the hash, then activate the panel
|
||||
// If the order is wrong, this will cause the panel to disactivate again
|
||||
if (setHash) {
|
||||
Hash.hash.setData(hashToShow)
|
||||
}
|
||||
ScrollableFullScreen._currentlyOpen = self
|
||||
self.Activate()
|
||||
} else {
|
||||
|
@ -81,6 +79,10 @@ export default class ScrollableFullScreen {
|
|||
ScrollableFullScreen.collapse()
|
||||
}
|
||||
})
|
||||
if (isShown.data) {
|
||||
ScrollableFullScreen._currentlyOpen = self
|
||||
this.Activate()
|
||||
}
|
||||
}
|
||||
|
||||
private static initEmpty(): FixedUiElement {
|
||||
|
@ -114,6 +116,9 @@ export default class ScrollableFullScreen {
|
|||
* @constructor
|
||||
*/
|
||||
public Activate(): void {
|
||||
if (this.hashToShow && this.hashToShow !== "" && this._setHash) {
|
||||
Hash.hash.setData(this.hashToShow)
|
||||
}
|
||||
this.isShown.setData(true)
|
||||
this._fullscreencomponent.AttachTo("fullscreen")
|
||||
const fs = document.getElementById("fullscreen")
|
||||
|
@ -157,4 +162,8 @@ export default class ScrollableFullScreen {
|
|||
"fixed top-0 left-0 right-0 h-screen w-screen desktop:max-h-65vh md:w-auto md:relative z-above-controls md:rounded-xl overflow-hidden"
|
||||
)
|
||||
}
|
||||
|
||||
static ActivateCurrent() {
|
||||
ScrollableFullScreen._currentlyOpen?.Activate()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,13 +31,19 @@ export default abstract class BaseUIElement {
|
|||
throw "SEVERE: could not attach UIElement to " + divId
|
||||
}
|
||||
|
||||
while (element.firstChild) {
|
||||
//The list is LIVE so it will re-index each call
|
||||
element.removeChild(element.firstChild)
|
||||
let alreadyThere = false
|
||||
const elementToAdd = this.ConstructElement()
|
||||
const childs = Array.from(element.childNodes)
|
||||
for (const child of childs) {
|
||||
if (child === elementToAdd) {
|
||||
alreadyThere = true
|
||||
continue
|
||||
}
|
||||
element.removeChild(child)
|
||||
}
|
||||
const el = this.ConstructElement()
|
||||
if (el !== undefined) {
|
||||
element.appendChild(el)
|
||||
|
||||
if (elementToAdd !== undefined && !alreadyThere) {
|
||||
element.appendChild(elementToAdd)
|
||||
}
|
||||
|
||||
return this
|
||||
|
|
59
UI/BigComponents/ActionButtons.ts
Normal file
59
UI/BigComponents/ActionButtons.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import Combine from "../Base/Combine"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import Loc from "../../Models/Loc"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import Svg from "../../Svg"
|
||||
import { Utils } from "../../Utils"
|
||||
import { MapillaryLink } from "./MapillaryLink"
|
||||
import TranslatorsPanel from "./TranslatorsPanel"
|
||||
import { OpenIdEditor, OpenJosm } from "./CopyrightPanel"
|
||||
|
||||
export class ActionButtons extends Combine {
|
||||
constructor(state: {
|
||||
readonly layoutToUse: LayoutConfig
|
||||
readonly currentBounds: Store<BBox>
|
||||
readonly locationControl: Store<Loc>
|
||||
readonly osmConnection: OsmConnection
|
||||
readonly isTranslator: Store<boolean>
|
||||
}) {
|
||||
const imgSize = "h-6 w-6"
|
||||
const iconStyle = "height: 1.5rem; width: 1.5rem"
|
||||
const t = Translations.t.general.attribution
|
||||
|
||||
super([
|
||||
new SubtleButton(Svg.liberapay_ui(), t.donate, {
|
||||
url: "https://liberapay.com/pietervdvn/",
|
||||
newTab: true,
|
||||
imgSize,
|
||||
}),
|
||||
new SubtleButton(Svg.bug_ui(), t.openIssueTracker, {
|
||||
url: "https://github.com/pietervdvn/MapComplete/issues",
|
||||
newTab: true,
|
||||
imgSize,
|
||||
}),
|
||||
new SubtleButton(
|
||||
Svg.statistics_ui(),
|
||||
t.openOsmcha.Subs({ theme: state.layoutToUse.title }),
|
||||
{
|
||||
url: Utils.OsmChaLinkFor(31, state.layoutToUse.id),
|
||||
newTab: true,
|
||||
imgSize,
|
||||
}
|
||||
),
|
||||
new SubtleButton(Svg.mastodon_ui(), t.followOnMastodon, {
|
||||
url: "https://en.osm.town/@MapComplete",
|
||||
newTab: true,
|
||||
imgSize,
|
||||
}),
|
||||
new OpenIdEditor(state, iconStyle),
|
||||
new MapillaryLink(state, iconStyle),
|
||||
new OpenJosm(state, iconStyle).SetClass("hidden-on-mobile"),
|
||||
new TranslatorsPanel(state, iconStyle),
|
||||
])
|
||||
this.SetClass("block w-full link-no-underline")
|
||||
}
|
||||
}
|
|
@ -23,13 +23,10 @@ import Constants from "../../Models/Constants"
|
|||
import ContributorCount from "../../Logic/ContributorCount"
|
||||
import Img from "../Base/Img"
|
||||
import { TypedTranslation } from "../i18n/Translation"
|
||||
import TranslatorsPanel from "./TranslatorsPanel"
|
||||
import { MapillaryLink } from "./MapillaryLink"
|
||||
import FullWelcomePaneWithTabs from "./FullWelcomePaneWithTabs"
|
||||
|
||||
export class OpenIdEditor extends VariableUiElement {
|
||||
constructor(
|
||||
state: { locationControl: UIEventSource<Loc> },
|
||||
state: { readonly locationControl: Store<Loc> },
|
||||
iconStyle?: string,
|
||||
objectId?: string
|
||||
) {
|
||||
|
@ -125,38 +122,6 @@ export default class CopyrightPanel extends Combine {
|
|||
}) {
|
||||
const t = Translations.t.general.attribution
|
||||
const layoutToUse = state.layoutToUse
|
||||
const imgSize = "h-6 w-6"
|
||||
const iconStyle = "height: 1.5rem; width: 1.5rem"
|
||||
const actionButtons = [
|
||||
new SubtleButton(Svg.liberapay_ui(), t.donate, {
|
||||
url: "https://liberapay.com/pietervdvn/",
|
||||
newTab: true,
|
||||
imgSize,
|
||||
}),
|
||||
new SubtleButton(Svg.bug_ui(), t.openIssueTracker, {
|
||||
url: "https://github.com/pietervdvn/MapComplete/issues",
|
||||
newTab: true,
|
||||
imgSize,
|
||||
}),
|
||||
new SubtleButton(
|
||||
Svg.statistics_ui(),
|
||||
t.openOsmcha.Subs({ theme: state.layoutToUse.title }),
|
||||
{
|
||||
url: Utils.OsmChaLinkFor(31, state.layoutToUse.id),
|
||||
newTab: true,
|
||||
imgSize,
|
||||
}
|
||||
),
|
||||
new SubtleButton(Svg.mastodon_ui(), t.followOnMastodon, {
|
||||
url: "https://en.osm.town/@MapComplete",
|
||||
newTab: true,
|
||||
imgSize,
|
||||
}),
|
||||
new OpenIdEditor(state, iconStyle),
|
||||
new MapillaryLink(state, iconStyle),
|
||||
new OpenJosm(state, iconStyle),
|
||||
new TranslatorsPanel(state, iconStyle),
|
||||
]
|
||||
|
||||
const iconAttributions = layoutToUse.usedImages.map(CopyrightPanel.IconAttribution)
|
||||
|
||||
|
@ -213,7 +178,6 @@ export default class CopyrightPanel extends Combine {
|
|||
CopyrightPanel.CodeContributors(contributors, t.codeContributionsBy),
|
||||
CopyrightPanel.CodeContributors(translators, t.translatedBy),
|
||||
new FixedUiElement("MapComplete " + Constants.vNumber).SetClass("font-bold"),
|
||||
new Combine(actionButtons).SetClass("block w-full link-no-underline"),
|
||||
new Title(t.iconAttribution.title, 3),
|
||||
...iconAttributions,
|
||||
].map((e) => e?.SetClass("mt-4"))
|
||||
|
|
|
@ -37,12 +37,13 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
|||
featurePipeline: FeaturePipeline
|
||||
backgroundLayer: UIEventSource<BaseLayer>
|
||||
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||
} & UserRelatedState
|
||||
} & UserRelatedState,
|
||||
guistate?: { userInfoIsOpened: UIEventSource<boolean> }
|
||||
) {
|
||||
const layoutToUse = state.layoutToUse
|
||||
super(
|
||||
() => layoutToUse.title.Clone(),
|
||||
() => FullWelcomePaneWithTabs.GenerateContents(state, currentTab, isShown),
|
||||
() => FullWelcomePaneWithTabs.GenerateContents(state, currentTab, isShown, guistate),
|
||||
"welcome",
|
||||
isShown
|
||||
)
|
||||
|
@ -60,12 +61,13 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
|||
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||
} & UserRelatedState,
|
||||
isShown: UIEventSource<boolean>,
|
||||
currentTab: UIEventSource<number>
|
||||
currentTab: UIEventSource<number>,
|
||||
guistate?: { userInfoIsOpened: UIEventSource<boolean> }
|
||||
): { header: string | BaseUIElement; content: BaseUIElement }[] {
|
||||
const tabs: { header: string | BaseUIElement; content: BaseUIElement }[] = [
|
||||
{
|
||||
header: `<img src='${state.layoutToUse.icon}'>`,
|
||||
content: new ThemeIntroductionPanel(isShown, currentTab, state),
|
||||
content: new ThemeIntroductionPanel(isShown, currentTab, state, guistate),
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -113,11 +115,12 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
|||
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||
} & UserRelatedState,
|
||||
currentTab: UIEventSource<number>,
|
||||
isShown: UIEventSource<boolean>
|
||||
isShown: UIEventSource<boolean>,
|
||||
guistate?: { userInfoIsOpened: UIEventSource<boolean> }
|
||||
) {
|
||||
const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab)
|
||||
const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab, guistate)
|
||||
const tabsWithAboutMc = [
|
||||
...FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab),
|
||||
...FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab, guistate),
|
||||
]
|
||||
|
||||
tabsWithAboutMc.push({
|
||||
|
|
|
@ -14,17 +14,10 @@ import FeatureInfoBox from "../Popup/FeatureInfoBox"
|
|||
import CopyrightPanel from "./CopyrightPanel"
|
||||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
||||
import Hotkeys from "../Base/Hotkeys"
|
||||
import { DefaultGuiState } from "../DefaultGuiState"
|
||||
|
||||
export default class LeftControls extends Combine {
|
||||
constructor(
|
||||
state: FeaturePipelineState,
|
||||
guiState: {
|
||||
currentViewControlIsOpened: UIEventSource<boolean>
|
||||
downloadControlIsOpened: UIEventSource<boolean>
|
||||
filterViewIsOpened: UIEventSource<boolean>
|
||||
copyrightViewIsOpened: UIEventSource<boolean>
|
||||
}
|
||||
) {
|
||||
constructor(state: FeaturePipelineState, guiState: DefaultGuiState) {
|
||||
const currentViewFL = state.currentView?.layer
|
||||
const currentViewAction = new Toggle(
|
||||
new Lazy(() => {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import Loc from "../../Models/Loc"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import Svg from "../../Svg"
|
||||
import Combine from "../Base/Combine"
|
||||
import Title from "../Base/Title"
|
||||
|
||||
export class MapillaryLink extends VariableUiElement {
|
||||
constructor(state: { locationControl: UIEventSource<Loc> }, iconStyle?: string) {
|
||||
constructor(state: { readonly locationControl: Store<Loc> }, iconStyle?: string) {
|
||||
const t = Translations.t.general.attribution
|
||||
super(
|
||||
state.locationControl.map((location) => {
|
||||
|
@ -17,12 +16,14 @@ export class MapillaryLink extends VariableUiElement {
|
|||
}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}`
|
||||
return new SubtleButton(
|
||||
Svg.mapillary_black_ui().SetStyle(iconStyle),
|
||||
new Combine([t.openMapillary.SetClass("font-bold"), t.mapillaryHelp]),
|
||||
new Combine([t.openMapillary.SetClass("font-bold"), t.mapillaryHelp]).SetClass(
|
||||
"flex flex-col link-no-underline"
|
||||
),
|
||||
{
|
||||
url: mapillaryLink,
|
||||
newTab: true,
|
||||
}
|
||||
).SetClass("flex flex-col link-no-underline")
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
|
@ -110,25 +110,18 @@ export default class ShareScreen extends Combine {
|
|||
{ urlName: "fs-search", human: tr.fsSearch },
|
||||
{ urlName: "fs-welcome-message", human: tr.fsWelcomeMessage },
|
||||
{ urlName: "fs-layers", human: tr.fsLayers },
|
||||
{ urlName: "layer-control-toggle", human: tr.fsLayerControlToggle, reverse: true },
|
||||
{ urlName: "fs-add-new", human: tr.fsAddNew },
|
||||
{ urlName: "fs-geolocation", human: tr.fsGeolocation },
|
||||
]
|
||||
|
||||
for (const swtch of switches) {
|
||||
const checkbox = new CheckBox(Translations.W(swtch.human), !swtch.reverse)
|
||||
const checkbox = new CheckBox(Translations.W(swtch.human))
|
||||
optionCheckboxes.push(checkbox)
|
||||
optionParts.push(
|
||||
checkbox.GetValue().map((isEn) => {
|
||||
if (isEn) {
|
||||
if (swtch.reverse) {
|
||||
return `${swtch.urlName}=true`
|
||||
}
|
||||
return null
|
||||
} else {
|
||||
if (swtch.reverse) {
|
||||
return null
|
||||
}
|
||||
return `${swtch.urlName}=false`
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Asks to add a feature at the last clicked location, at least if zoom is sufficient
|
||||
*/
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Svg from "../../Svg"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import Combine from "../Base/Combine"
|
||||
|
@ -28,6 +28,7 @@ import Hash from "../../Logic/Web/Hash"
|
|||
import { GlobalFilter } from "../../Logic/State/MapState"
|
||||
import { WayId } from "../../Models/OsmFeature"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import { LoginToggle } from "../Popup/LoginButton"
|
||||
|
||||
/*
|
||||
* The SimpleAddUI is a single panel, which can have multiple states:
|
||||
|
@ -44,7 +45,7 @@ export interface PresetInfo extends PresetConfig {
|
|||
boundsFactor?: 0.25 | number
|
||||
}
|
||||
|
||||
export default class SimpleAddUI extends Toggle {
|
||||
export default class SimpleAddUI extends LoginToggle {
|
||||
/**
|
||||
*
|
||||
* @param isShown
|
||||
|
@ -59,6 +60,7 @@ export default class SimpleAddUI extends Toggle {
|
|||
filterViewIsOpened: UIEventSource<boolean>,
|
||||
state: {
|
||||
featureSwitchIsTesting: UIEventSource<boolean>
|
||||
featureSwitchUserbadge: Store<boolean>
|
||||
layoutToUse: LayoutConfig
|
||||
osmConnection: OsmConnection
|
||||
changes: Changes
|
||||
|
@ -74,10 +76,6 @@ export default class SimpleAddUI extends Toggle {
|
|||
},
|
||||
takeLocationFrom?: UIEventSource<{ lat: number; lon: number }>
|
||||
) {
|
||||
const loginButton = new SubtleButton(
|
||||
Svg.osm_logo_ui(),
|
||||
Translations.t.general.add.pleaseLogin.Clone()
|
||||
).onClick(() => state.osmConnection.AttemptLogin())
|
||||
const readYourMessages = new Combine([
|
||||
Translations.t.general.readYourMessages.Clone().SetClass("alert"),
|
||||
new SubtleButton(Svg.envelope_ui(), Translations.t.general.goToInbox, {
|
||||
|
@ -187,8 +185,8 @@ export default class SimpleAddUI extends Toggle {
|
|||
userdetails.unreadMessages == 0
|
||||
)
|
||||
),
|
||||
loginButton,
|
||||
state.osmConnection.isLoggedIn
|
||||
Translations.t.general.add.pleaseLogin,
|
||||
state
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,16 @@ import LanguagePicker from "../LanguagePicker"
|
|||
import Translations from "../i18n/Translations"
|
||||
import Toggle from "../Input/Toggle"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { LoginToggle } from "../Popup/LoginButton"
|
||||
import Svg from "../../Svg"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import FullWelcomePaneWithTabs from "./FullWelcomePaneWithTabs"
|
||||
import LoggedInUserIndicator from "../LoggedInUserIndicator"
|
||||
import { ActionButtons } from "./ActionButtons"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import Loc from "../../Models/Loc"
|
||||
import UserSurveyPanel from "../UserSurveyPanel"
|
||||
|
||||
export default class ThemeIntroductionPanel extends Combine {
|
||||
|
@ -21,7 +25,11 @@ export default class ThemeIntroductionPanel extends Combine {
|
|||
featureSwitchUserbadge: UIEventSource<boolean>
|
||||
layoutToUse: LayoutConfig
|
||||
osmConnection: OsmConnection
|
||||
}
|
||||
currentBounds: Store<BBox>
|
||||
locationControl: UIEventSource<Loc>
|
||||
isTranslator: Store<boolean>
|
||||
},
|
||||
guistate?: { userInfoIsOpened: UIEventSource<boolean> }
|
||||
) {
|
||||
const t = Translations.t.general
|
||||
const layout = state.layoutToUse
|
||||
|
@ -37,9 +45,18 @@ export default class ThemeIntroductionPanel extends Combine {
|
|||
})
|
||||
.SetClass("only-on-mobile")
|
||||
|
||||
const loggedInUserInfo = new LoggedInUserIndicator(state.osmConnection, {
|
||||
firstLine: Translations.t.general.welcomeBack.Clone(),
|
||||
})
|
||||
if (guistate?.userInfoIsOpened) {
|
||||
loggedInUserInfo.onClick(() => {
|
||||
guistate.userInfoIsOpened.setData(true)
|
||||
})
|
||||
}
|
||||
|
||||
const loginStatus = new Toggle(
|
||||
new LoginToggle(
|
||||
undefined,
|
||||
loggedInUserInfo,
|
||||
new Combine([
|
||||
Translations.t.general.loginWithOpenStreetMap.SetClass("text-xl font-bold"),
|
||||
Translations.t.general.loginOnlyNeededToEdit.Clone().SetClass("font-bold"),
|
||||
|
@ -62,10 +79,10 @@ export default class ThemeIntroductionPanel extends Combine {
|
|||
]).SetClass("flex flex-col mt-2"),
|
||||
|
||||
toTheMap,
|
||||
loginStatus.SetClass("block"),
|
||||
loginStatus.SetClass("block mt-6 pt-2 md:border-t-2 border-dotted border-gray-400"),
|
||||
layout.descriptionTail?.Clone().SetClass("block mt-4"),
|
||||
|
||||
languagePicker?.SetClass("block mt-4"),
|
||||
languagePicker?.SetClass("block mt-4 pb-8 border-b-2 border-dotted border-gray-400"),
|
||||
|
||||
Toggle.If(state.featureSwitchMoreQuests, () =>
|
||||
new Combine([
|
||||
|
@ -80,6 +97,7 @@ export default class ThemeIntroductionPanel extends Combine {
|
|||
.SetClass("h-12"),
|
||||
]).SetClass("flex flex-col mt-6")
|
||||
),
|
||||
new ActionButtons(state),
|
||||
|
||||
...layout.CustomCodeSnippets(),
|
||||
])
|
||||
|
|
|
@ -66,7 +66,7 @@ class TranslatorsPanelContent extends Combine {
|
|||
"missingLayers:",
|
||||
missingLayers
|
||||
)
|
||||
return [
|
||||
return Utils.NoNull([
|
||||
hasMissingTheme
|
||||
? new Link(
|
||||
"themes:" + layout.id + ".* (zen mode)",
|
||||
|
@ -86,7 +86,7 @@ class TranslatorsPanelContent extends Combine {
|
|||
(context) =>
|
||||
new Link(context, LinkToWeblate.hrefToWeblate(language, context), true)
|
||||
),
|
||||
]
|
||||
])
|
||||
}
|
||||
|
||||
// "translationCompleteness": "Translations for {theme} in {language} are at {percentage}: {translated} out of {total}",
|
||||
|
|
|
@ -4,30 +4,22 @@ import { FixedInputElement } from "../Input/FixedInputElement"
|
|||
import Combine from "../Base/Combine"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { TextField } from "../Input/TextField"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Title from "../Base/Title"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import Svg from "../../Svg"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
import { LoginToggle } from "../Popup/LoginButton"
|
||||
|
||||
export default class UploadTraceToOsmUI extends Toggle {
|
||||
private static createDefault(s: string, defaultValue: string) {
|
||||
if (defaultValue.length < 1) {
|
||||
throw "Default value should have some characters"
|
||||
}
|
||||
if (s === undefined || s === null || s === "") {
|
||||
return defaultValue
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
export default class UploadTraceToOsmUI extends LoginToggle {
|
||||
constructor(
|
||||
trace: (title: string) => string,
|
||||
state: {
|
||||
layoutToUse: LayoutConfig
|
||||
osmConnection: OsmConnection
|
||||
readonly featureSwitchUserbadge: Store<boolean>
|
||||
},
|
||||
options?: {
|
||||
whenUploaded?: () => void | Promise<void>
|
||||
|
@ -119,15 +111,41 @@ export default class UploadTraceToOsmUI extends Toggle {
|
|||
]).SetClass("flex flex-col p-4 rounded border-2 m-2 border-subtle")
|
||||
|
||||
super(
|
||||
new Combine([Svg.confirm_svg().SetClass("w-12 h-12 mr-2"), t.uploadFinished]).SetClass(
|
||||
"flex p-2 rounded-xl border-2 subtle-border items-center"
|
||||
),
|
||||
new Toggle(
|
||||
confirmPanel,
|
||||
new SubtleButton(Svg.upload_svg(), t.title).onClick(() => clicked.setData(true)),
|
||||
clicked
|
||||
new Toggle(
|
||||
new Combine([
|
||||
Svg.confirm_svg().SetClass("w-12 h-12 mr-2"),
|
||||
t.uploadFinished,
|
||||
]).SetClass("flex p-2 rounded-xl border-2 subtle-border items-center"),
|
||||
new Toggle(
|
||||
confirmPanel,
|
||||
new SubtleButton(Svg.upload_svg(), t.title).onClick(() =>
|
||||
clicked.setData(true)
|
||||
),
|
||||
clicked
|
||||
),
|
||||
uploadFinished
|
||||
),
|
||||
new Combine([
|
||||
Svg.invalid_ui().SetClass("w-8 h-8 m-2"),
|
||||
t.gpxServiceOffline.SetClass("p-2"),
|
||||
]).SetClass("flex border alert items-center"),
|
||||
state.osmConnection.gpxServiceIsOnline.map(
|
||||
(serviceState) => serviceState === "online"
|
||||
)
|
||||
),
|
||||
uploadFinished
|
||||
undefined,
|
||||
state
|
||||
)
|
||||
}
|
||||
|
||||
private static createDefault(s: string, defaultValue: string) {
|
||||
if (defaultValue.length < 1) {
|
||||
throw "Default value should have some characters"
|
||||
}
|
||||
if (s === undefined || s === null || s === "") {
|
||||
return defaultValue
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,12 +140,17 @@ class UserInformationMainPanel extends Combine {
|
|||
}
|
||||
|
||||
export default class UserInformationPanel extends ScrollableFullScreen {
|
||||
constructor(state: {
|
||||
layoutToUse: LayoutConfig
|
||||
osmConnection: OsmConnection
|
||||
locationControl: UIEventSource<Loc>
|
||||
}) {
|
||||
const isOpened = new UIEventSource<boolean>(false)
|
||||
constructor(
|
||||
state: {
|
||||
layoutToUse: LayoutConfig
|
||||
osmConnection: OsmConnection
|
||||
locationControl: UIEventSource<Loc>
|
||||
},
|
||||
options?: {
|
||||
isOpened?: UIEventSource<boolean>
|
||||
}
|
||||
) {
|
||||
const isOpened = options?.isOpened ?? new UIEventSource<boolean>(false)
|
||||
super(
|
||||
() => {
|
||||
return new VariableUiElement(
|
||||
|
|
|
@ -33,7 +33,6 @@ import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler"
|
|||
import { GeoLocationState } from "../Logic/State/GeoLocationState"
|
||||
import Hotkeys from "./Base/Hotkeys"
|
||||
import AvailableBaseLayers from "../Logic/Actors/AvailableBaseLayers"
|
||||
import { Translation } from "./i18n/Translation"
|
||||
|
||||
/**
|
||||
* The default MapComplete GUI initializer
|
||||
|
@ -56,6 +55,7 @@ export default class DefaultGUI {
|
|||
public setup() {
|
||||
this.SetupUIElements()
|
||||
this.SetupMap()
|
||||
ScrollableFullScreen.ActivateCurrent()
|
||||
|
||||
if (
|
||||
this.state.layoutToUse.customCss !== undefined &&
|
||||
|
@ -203,39 +203,43 @@ export default class DefaultGUI {
|
|||
const guiState = this.guiState
|
||||
|
||||
const self = this
|
||||
new Combine([
|
||||
Toggle.If(state.featureSwitchUserbadge, () => {
|
||||
const userInfo = new UserInformationPanel(state)
|
||||
|
||||
const mapControl = new MapControlButton(
|
||||
new VariableUiElement(
|
||||
state.osmConnection.userDetails.map((ud) => {
|
||||
if (ud?.img === undefined) {
|
||||
return Svg.person_ui().SetClass("mt-1 block")
|
||||
}
|
||||
return new Img(ud?.img)
|
||||
})
|
||||
).SetClass("block rounded-full overflow-hidden"),
|
||||
{
|
||||
dontStyle: true,
|
||||
}
|
||||
).onClick(() => userInfo.Activate())
|
||||
const userInfoMapControl = Toggle.If(state.featureSwitchUserbadge, () => {
|
||||
console.log("Guistate is", guiState)
|
||||
new UserInformationPanel(state, {
|
||||
isOpened: guiState.userInfoIsOpened,
|
||||
})
|
||||
|
||||
return new LoginToggle(
|
||||
mapControl,
|
||||
Translations.t.general.loginWithOpenStreetMap,
|
||||
state
|
||||
)
|
||||
}),
|
||||
Toggle.If(
|
||||
state.featureSwitchExtraLinkEnabled,
|
||||
() => new ExtraLinkButton(state, state.layoutToUse.extraLink)
|
||||
),
|
||||
Toggle.If(state.featureSwitchWelcomeMessage, () => self.InitWelcomeMessage()),
|
||||
Toggle.If(state.featureSwitchIsTesting, () =>
|
||||
new FixedUiElement("TESTING").SetClass("alert m-2 border-2 border-black")
|
||||
),
|
||||
])
|
||||
const mapControl = new MapControlButton(
|
||||
new VariableUiElement(
|
||||
state.osmConnection.userDetails.map((ud) => {
|
||||
if (ud?.img === undefined) {
|
||||
return Svg.person_ui().SetClass("mt-1 block")
|
||||
}
|
||||
return new Img(ud?.img)
|
||||
})
|
||||
).SetClass("block rounded-full overflow-hidden"),
|
||||
{
|
||||
dontStyle: true,
|
||||
}
|
||||
).onClick(() => {
|
||||
self.guiState.userInfoIsOpened.setData(true)
|
||||
})
|
||||
|
||||
return new LoginToggle(mapControl, Translations.t.general.loginWithOpenStreetMap, state)
|
||||
})
|
||||
const extraLink = Toggle.If(
|
||||
state.featureSwitchExtraLinkEnabled,
|
||||
() => new ExtraLinkButton(state, state.layoutToUse.extraLink)
|
||||
)
|
||||
|
||||
const welcomeMessageMapControl = Toggle.If(state.featureSwitchWelcomeMessage, () =>
|
||||
self.InitWelcomeMessage()
|
||||
)
|
||||
const testingBadge = Toggle.If(state.featureSwitchIsTesting, () =>
|
||||
new FixedUiElement("TESTING").SetClass("alert m-2 border-2 border-black")
|
||||
)
|
||||
new Combine([welcomeMessageMapControl, userInfoMapControl, extraLink, testingBadge])
|
||||
.SetClass("flex flex-col")
|
||||
.AttachTo("top-left")
|
||||
|
||||
|
@ -273,26 +277,16 @@ export default class DefaultGUI {
|
|||
|
||||
new CenterMessageBox(state).AttachTo("centermessage")
|
||||
document?.getElementById("centermessage")?.classList?.add("pointer-events-none")
|
||||
|
||||
// We have to ping the welcomeMessageIsOpened and other isOpened-stuff to activate the FullScreenMessage if needed
|
||||
for (const state of guiState.allFullScreenStates) {
|
||||
if (state.data) {
|
||||
state.ping()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* At last, if the map moves or an element is selected, we close all the panels just as well
|
||||
*/
|
||||
|
||||
state.selectedElement.addCallbackAndRunD((_) => {
|
||||
guiState.allFullScreenStates.forEach((s) => s.setData(false))
|
||||
})
|
||||
}
|
||||
|
||||
private InitWelcomeMessage(): BaseUIElement {
|
||||
const isOpened = this.guiState.welcomeMessageIsOpened
|
||||
new FullWelcomePaneWithTabs(isOpened, this.guiState.welcomeMessageOpenedTab, this.state)
|
||||
new FullWelcomePaneWithTabs(
|
||||
isOpened,
|
||||
this.guiState.welcomeMessageOpenedTab,
|
||||
this.state,
|
||||
this.guiState
|
||||
)
|
||||
|
||||
// ?-Button on Desktop, opens panel with close-X.
|
||||
const help = new MapControlButton(Svg.help_svg())
|
||||
|
|
|
@ -4,13 +4,22 @@ import Hash from "../Logic/Web/Hash"
|
|||
|
||||
export class DefaultGuiState {
|
||||
static state: DefaultGuiState
|
||||
public readonly welcomeMessageIsOpened: UIEventSource<boolean>
|
||||
public readonly downloadControlIsOpened: UIEventSource<boolean>
|
||||
public readonly filterViewIsOpened: UIEventSource<boolean>
|
||||
public readonly copyrightViewIsOpened: UIEventSource<boolean>
|
||||
public readonly currentViewControlIsOpened: UIEventSource<boolean>
|
||||
|
||||
public readonly welcomeMessageIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(
|
||||
false
|
||||
)
|
||||
public readonly downloadControlIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(
|
||||
false
|
||||
)
|
||||
public readonly filterViewIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
||||
public readonly copyrightViewIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(
|
||||
false
|
||||
)
|
||||
public readonly currentViewControlIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(
|
||||
false
|
||||
)
|
||||
public readonly userInfoIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
||||
public readonly welcomeMessageOpenedTab: UIEventSource<number>
|
||||
public readonly allFullScreenStates: UIEventSource<boolean>[] = []
|
||||
|
||||
constructor() {
|
||||
this.welcomeMessageOpenedTab = UIEventSource.asFloat(
|
||||
|
@ -20,68 +29,19 @@ export class DefaultGuiState {
|
|||
`The tab that is shown in the welcome-message.`
|
||||
)
|
||||
)
|
||||
this.welcomeMessageIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"welcome-control-toggle",
|
||||
false,
|
||||
"Whether or not the welcome panel is shown"
|
||||
)
|
||||
this.downloadControlIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"download-control-toggle",
|
||||
false,
|
||||
"Whether or not the download panel is shown"
|
||||
)
|
||||
this.filterViewIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"filter-toggle",
|
||||
false,
|
||||
"Whether or not the filter view is shown"
|
||||
)
|
||||
this.copyrightViewIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"copyright-toggle",
|
||||
false,
|
||||
"Whether or not the copyright view is shown"
|
||||
)
|
||||
this.currentViewControlIsOpened = QueryParameters.GetBooleanQueryParameter(
|
||||
"currentview-toggle",
|
||||
false,
|
||||
"Whether or not the current view box is shown"
|
||||
)
|
||||
const states = {
|
||||
const sources = {
|
||||
welcome: this.welcomeMessageIsOpened,
|
||||
download: this.downloadControlIsOpened,
|
||||
filters: this.filterViewIsOpened,
|
||||
copyright: this.copyrightViewIsOpened,
|
||||
currentview: this.currentViewControlIsOpened,
|
||||
welcome: this.welcomeMessageIsOpened,
|
||||
userinfo: this.userInfoIsOpened,
|
||||
}
|
||||
Hash.hash.addCallbackAndRunD((hash) => {
|
||||
hash = hash.toLowerCase()
|
||||
states[hash]?.setData(true)
|
||||
})
|
||||
|
||||
sources[Hash.hash.data?.toLowerCase()]?.setData(true)
|
||||
|
||||
if (Hash.hash.data === "" || Hash.hash.data === undefined) {
|
||||
this.welcomeMessageIsOpened.setData(true)
|
||||
}
|
||||
|
||||
this.allFullScreenStates.push(
|
||||
this.downloadControlIsOpened,
|
||||
this.filterViewIsOpened,
|
||||
this.copyrightViewIsOpened,
|
||||
this.welcomeMessageIsOpened,
|
||||
this.currentViewControlIsOpened
|
||||
)
|
||||
|
||||
for (let i = 0; i < this.allFullScreenStates.length; i++) {
|
||||
const fullScreenState = this.allFullScreenStates[i]
|
||||
for (let j = 0; j < this.allFullScreenStates.length; j++) {
|
||||
if (i == j) {
|
||||
continue
|
||||
}
|
||||
const otherState = this.allFullScreenStates[j]
|
||||
fullScreenState.addCallbackAndRunD((isOpened) => {
|
||||
if (isOpened) {
|
||||
otherState.setData(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
|||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import Loading from "../Base/Loading"
|
||||
import {LoginToggle} from "../Popup/LoginButton";
|
||||
|
||||
export class ImageUploadFlow extends Toggle {
|
||||
private static readonly uploadCountsPerId = new Map<string, UIEventSource<number>>()
|
||||
|
@ -180,16 +181,12 @@ export class ImageUploadFlow extends Toggle {
|
|||
chosenLicense.SetClass("subtle text-sm"),
|
||||
]).SetClass("flex flex-col image-upload-flow mt-4 mb-8 text-center")
|
||||
|
||||
const pleaseLoginButton = t.pleaseLogin
|
||||
.Clone()
|
||||
.onClick(() => state.osmConnection.AttemptLogin())
|
||||
.SetClass("login-button-friendly")
|
||||
super(
|
||||
new Toggle(
|
||||
new LoginToggle(
|
||||
/*We can show the actual upload button!*/
|
||||
uploadFlow,
|
||||
/* User not logged in*/ pleaseLoginButton,
|
||||
state?.osmConnection?.isLoggedIn
|
||||
/* User not logged in*/ t.pleaseLogin.Clone(),
|
||||
state
|
||||
),
|
||||
undefined /* Nothing as the user badge is disabled*/,
|
||||
state?.featureSwitchUserbadge
|
||||
|
|
42
UI/LoggedInUserIndicator.ts
Normal file
42
UI/LoggedInUserIndicator.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { VariableUiElement } from "./Base/VariableUIElement"
|
||||
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
||||
import Svg from "../Svg"
|
||||
import Img from "./Base/Img"
|
||||
import Combine from "./Base/Combine"
|
||||
import { FixedUiElement } from "./Base/FixedUiElement"
|
||||
import BaseUIElement from "./BaseUIElement"
|
||||
|
||||
export default class LoggedInUserIndicator extends VariableUiElement {
|
||||
constructor(
|
||||
osmConnection: OsmConnection,
|
||||
options?: {
|
||||
size?: "small" | "medium" | "large"
|
||||
firstLine?: BaseUIElement
|
||||
}
|
||||
) {
|
||||
options = options ?? {}
|
||||
let size = "w-8 h-8 mr-2"
|
||||
if (options.size == "medium") {
|
||||
size = "w-16 h-16 mr-4"
|
||||
} else if (options.size == "large") {
|
||||
size = "w-32 h-32 mr-6"
|
||||
}
|
||||
super(
|
||||
osmConnection.userDetails.mapD((ud) => {
|
||||
let img = Svg.person_svg().SetClass(
|
||||
"rounded-full border border-black overflow-hidden"
|
||||
)
|
||||
if (ud.img) {
|
||||
img = new Img(ud.img)
|
||||
}
|
||||
let contents: BaseUIElement = new FixedUiElement(ud.name).SetClass("font-bold")
|
||||
if (options?.firstLine) {
|
||||
contents = new Combine([options.firstLine, contents]).SetClass("flex flex-col")
|
||||
}
|
||||
return new Combine([img.SetClass("rounded-full " + size), contents]).SetClass(
|
||||
"flex items-center"
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import { SubstitutedTranslation } from "../SubstitutedTranslation"
|
|||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion"
|
||||
import { OsmId } from "../../Models/OsmFeature"
|
||||
import { LoginToggle } from "./LoginButton"
|
||||
|
||||
export default class DeleteWizard extends Toggle {
|
||||
/**
|
||||
|
@ -122,6 +123,40 @@ export default class DeleteWizard extends Toggle {
|
|||
]).SetClass("flex mt-2 justify-between"),
|
||||
]).SetClass("question")
|
||||
|
||||
const deleteFlow = new Toggle(
|
||||
new Toggle(
|
||||
new Toggle(
|
||||
deleteDialog,
|
||||
new SubtleButton(Svg.envelope_ui(), t.readMessages),
|
||||
state.osmConnection.userDetails.map(
|
||||
(ud) =>
|
||||
ud.csCount >
|
||||
Constants.userJourney.addNewPointWithUnreadMessagesUnlock ||
|
||||
ud.unreadMessages == 0
|
||||
)
|
||||
),
|
||||
|
||||
deleteButton,
|
||||
confirm
|
||||
),
|
||||
new VariableUiElement(
|
||||
deleteAbility.canBeDeleted.map((cbd) =>
|
||||
new Combine([
|
||||
Svg.delete_not_allowed_svg()
|
||||
.SetStyle("height: 2rem; width: auto")
|
||||
.SetClass("mr-2"),
|
||||
new Combine([
|
||||
t.cannotBeDeleted,
|
||||
cbd.reason.SetClass("subtle"),
|
||||
t.useSomethingElse.SetClass("subtle"),
|
||||
]).SetClass("flex flex-col"),
|
||||
]).SetClass("flex m-2 p-2 rounded-lg bg-gray-200 bg-gray-200")
|
||||
)
|
||||
),
|
||||
|
||||
deleteAbility.canBeDeleted.map((cbd) => allowSoftDeletion || cbd.canBeDeleted !== false)
|
||||
)
|
||||
|
||||
super(
|
||||
new Toggle(
|
||||
new Combine([
|
||||
|
@ -130,52 +165,19 @@ export default class DeleteWizard extends Toggle {
|
|||
),
|
||||
t.isDeleted,
|
||||
]).SetClass("flex m-2 rounded-full"),
|
||||
new Toggle(
|
||||
new Toggle(
|
||||
new Toggle(
|
||||
new Toggle(
|
||||
deleteDialog,
|
||||
new SubtleButton(Svg.envelope_ui(), t.readMessages),
|
||||
state.osmConnection.userDetails.map(
|
||||
(ud) =>
|
||||
ud.csCount >
|
||||
Constants.userJourney
|
||||
.addNewPointWithUnreadMessagesUnlock ||
|
||||
ud.unreadMessages == 0
|
||||
)
|
||||
),
|
||||
|
||||
deleteButton,
|
||||
confirm
|
||||
),
|
||||
new VariableUiElement(
|
||||
deleteAbility.canBeDeleted.map((cbd) =>
|
||||
new Combine([
|
||||
Svg.delete_not_allowed_svg()
|
||||
.SetStyle("height: 2rem; width: auto")
|
||||
.SetClass("mr-2"),
|
||||
new Combine([
|
||||
t.cannotBeDeleted,
|
||||
cbd.reason.SetClass("subtle"),
|
||||
t.useSomethingElse.SetClass("subtle"),
|
||||
]).SetClass("flex flex-col"),
|
||||
]).SetClass("flex m-2 p-2 rounded-lg bg-gray-200 bg-gray-200")
|
||||
)
|
||||
),
|
||||
|
||||
deleteAbility.canBeDeleted.map(
|
||||
(cbd) => allowSoftDeletion || cbd.canBeDeleted !== false
|
||||
)
|
||||
),
|
||||
|
||||
t.loginToDelete.onClick(state.osmConnection.AttemptLogin),
|
||||
state.osmConnection.isLoggedIn
|
||||
),
|
||||
new LoginToggle(deleteFlow, undefined, state),
|
||||
isDeleted
|
||||
),
|
||||
undefined,
|
||||
isShown
|
||||
)
|
||||
|
||||
const self = this
|
||||
confirm.addCallbackAndRunD((dialogIsOpened) => {
|
||||
if (dialogIsOpened) {
|
||||
self.ScrollIntoView()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private static constructConfirmButton(
|
||||
|
|
|
@ -88,7 +88,7 @@ export class LanguageElement implements SpecialVisualization {
|
|||
if (mode === undefined || mode.length == 0) {
|
||||
mode = "multi"
|
||||
}
|
||||
if (item_render === undefined) {
|
||||
if (item_render === undefined || item_render.trim() === "") {
|
||||
item_render = "{language()}"
|
||||
}
|
||||
if (all_render === undefined || all_render.length == 0) {
|
||||
|
@ -100,8 +100,17 @@ export class LanguageElement implements SpecialVisualization {
|
|||
mode
|
||||
)
|
||||
}
|
||||
if (single_render.indexOf("{language()") < 0 || item_render.indexOf("{language()") < 0) {
|
||||
throw "Error while calling language_chooser: render_single_language and render_list_item must contain '{language()}'"
|
||||
if (single_render.indexOf("{language()") < 0) {
|
||||
throw (
|
||||
"Error while calling language_chooser: render_single_language must contain '{language()}' but it is " +
|
||||
single_render
|
||||
)
|
||||
}
|
||||
if (item_render.indexOf("{language()") < 0) {
|
||||
throw (
|
||||
"Error while calling language_chooser: render_list_item must contain '{language()}' but it is " +
|
||||
item_render
|
||||
)
|
||||
}
|
||||
if (all_render.indexOf("{list()") < 0) {
|
||||
throw "Error while calling language_chooser: render_all must contain '{list()}'"
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import Svg from "../../Svg"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import Toggle from "../Input/Toggle"
|
||||
import { OsmConnection, OsmServiceState } from "../../Logic/Osm/OsmConnection"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import Loading from "../Base/Loading"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import Combine from "../Base/Combine"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
|
||||
class LoginButton extends SubtleButton {
|
||||
constructor(
|
||||
|
@ -23,30 +25,67 @@ class LoginButton extends SubtleButton {
|
|||
}
|
||||
|
||||
export class LoginToggle extends VariableUiElement {
|
||||
/**
|
||||
* Constructs an element which shows 'el' if the user is logged in
|
||||
* If not logged in, 'text' is shown on the button which invites to login.
|
||||
*
|
||||
* If logging in is not possible for some reason, an appropriate error message is shown
|
||||
*
|
||||
* State contains the 'osmConnection' to work with
|
||||
*/
|
||||
constructor(
|
||||
el: BaseUIElement,
|
||||
text: BaseUIElement | string,
|
||||
state: {
|
||||
osmConnection: OsmConnection
|
||||
readonly osmConnection: OsmConnection
|
||||
readonly featureSwitchUserbadge: Store<boolean>
|
||||
}
|
||||
) {
|
||||
const loading = new Loading("Trying to log in...")
|
||||
const login = new LoginButton(text, state)
|
||||
super(
|
||||
state.osmConnection.loadingStatus.map((osmConnectionState) => {
|
||||
if (osmConnectionState === "loading") {
|
||||
return loading
|
||||
}
|
||||
if (osmConnectionState === "not-attempted") {
|
||||
return login
|
||||
}
|
||||
if (osmConnectionState === "logged-in") {
|
||||
return el
|
||||
}
|
||||
const login = text === undefined ? undefined : new LoginButton(text, state)
|
||||
const t = Translations.t.general
|
||||
const offlineModes: Partial<Record<OsmServiceState, Translation>> = {
|
||||
offline: t.loginFailedOfflineMode,
|
||||
unreachable: t.loginFailedUnreachableMode,
|
||||
readonly: t.loginFailedReadonlyMode,
|
||||
}
|
||||
|
||||
// Error!
|
||||
return new LoginButton(Translations.t.general.loginFailed, state, Svg.invalid_svg())
|
||||
})
|
||||
super(
|
||||
state.osmConnection.loadingStatus.map(
|
||||
(osmConnectionState) => {
|
||||
if (state.featureSwitchUserbadge.data == false) {
|
||||
// All features to login with are disabled
|
||||
return undefined
|
||||
}
|
||||
|
||||
const apiState = state.osmConnection.apiIsOnline.data
|
||||
const apiTranslation = offlineModes[apiState]
|
||||
if (apiTranslation !== undefined) {
|
||||
return new Combine([
|
||||
Svg.invalid_svg().SetClass("w-8 h-8 m-2 shrink-0"),
|
||||
apiTranslation,
|
||||
]).SetClass("flex items-center alert max-w-64")
|
||||
}
|
||||
|
||||
if (osmConnectionState === "loading") {
|
||||
return loading
|
||||
}
|
||||
if (osmConnectionState === "not-attempted") {
|
||||
return login
|
||||
}
|
||||
if (osmConnectionState === "logged-in") {
|
||||
return el
|
||||
}
|
||||
|
||||
// Error!
|
||||
return new LoginButton(
|
||||
Translations.t.general.loginFailed,
|
||||
state,
|
||||
Svg.invalid_svg()
|
||||
)
|
||||
},
|
||||
[state.featureSwitchUserbadge, state.osmConnection.apiIsOnline]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import SearchAndGo from "../BigComponents/SearchAndGo"
|
|||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import { And } from "../../Logic/Tags/And"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import { LoginToggle } from "./LoginButton"
|
||||
|
||||
interface MoveReason {
|
||||
text: Translation | string
|
||||
|
@ -220,7 +221,7 @@ export default class MoveWizard extends Toggle {
|
|||
|
||||
const dialogClasses = "p-2 md:p-4 m-2 border border-gray-400 rounded-xl flex flex-col"
|
||||
|
||||
const moveFlow = new Toggle(
|
||||
const moveFlow = new LoginToggle(
|
||||
new VariableUiElement(
|
||||
currentStep.map((currentStep) => {
|
||||
switch (currentStep) {
|
||||
|
@ -246,8 +247,8 @@ export default class MoveWizard extends Toggle {
|
|||
}
|
||||
})
|
||||
),
|
||||
loginButton,
|
||||
state.osmConnection.isLoggedIn
|
||||
undefined,
|
||||
state
|
||||
)
|
||||
let id = featureToMove.properties.id
|
||||
const backend = state.osmConnection._oauth_config.url
|
||||
|
@ -284,5 +285,13 @@ export default class MoveWizard extends Toggle {
|
|||
]).SetClass("flex m-2 p-2 rounded-lg bg-gray-200"),
|
||||
moveDisallowedReason.map((r) => r === undefined)
|
||||
)
|
||||
|
||||
const self = this
|
||||
currentStep.addCallback((state) => {
|
||||
if (state === "start") {
|
||||
return
|
||||
}
|
||||
self.ScrollIntoView()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { VariableUiElement } from "../Base/VariableUIElement"
|
|||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { Unit } from "../../Models/Unit"
|
||||
import Lazy from "../Base/Lazy"
|
||||
import { OsmServiceState } from "../../Logic/Osm/OsmConnection"
|
||||
|
||||
/**
|
||||
* Generates all the questions, one by one
|
||||
|
@ -119,30 +120,34 @@ export default class QuestionBox extends VariableUiElement {
|
|||
)
|
||||
|
||||
super(
|
||||
questionsToAsk.map((allQuestions) => {
|
||||
const els: BaseUIElement[] = []
|
||||
if (
|
||||
options.showAllQuestionsAtOnce === true ||
|
||||
options.showAllQuestionsAtOnce["data"]
|
||||
) {
|
||||
els.push(...questionsToAsk.data)
|
||||
} else {
|
||||
els.push(allQuestions[0])
|
||||
}
|
||||
questionsToAsk.map(
|
||||
(allQuestions) => {
|
||||
const apiState: OsmServiceState = state.osmConnection.apiIsOnline.data
|
||||
if (apiState !== "online" && apiState !== "unknown") {
|
||||
return undefined
|
||||
}
|
||||
const els: BaseUIElement[] = []
|
||||
if (
|
||||
options.showAllQuestionsAtOnce === true ||
|
||||
options.showAllQuestionsAtOnce["data"]
|
||||
) {
|
||||
els.push(...questionsToAsk.data)
|
||||
} else {
|
||||
els.push(allQuestions[0])
|
||||
}
|
||||
|
||||
if (skippedQuestions.data.length > 0) {
|
||||
els.push(skippedQuestionsButton)
|
||||
}
|
||||
if (skippedQuestions.data.length > 0) {
|
||||
els.push(skippedQuestionsButton)
|
||||
}
|
||||
|
||||
return new Combine(els).SetClass("block mb-8")
|
||||
})
|
||||
return new Combine(els).SetClass("block mb-8")
|
||||
},
|
||||
[state.osmConnection.apiIsOnline]
|
||||
)
|
||||
)
|
||||
|
||||
this.skippedQuestions = skippedQuestions
|
||||
this.restingQuestions = questionsToAsk
|
||||
focus = () =>
|
||||
this.ScrollIntoView({
|
||||
onlyIfPartiallyHidden: true,
|
||||
})
|
||||
focus = () => this.ScrollIntoView()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ import BaseLayer from "../../Models/BaseLayer"
|
|||
import FilteredLayer from "../../Models/FilteredLayer"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
||||
import { LoginToggle } from "./LoginButton"
|
||||
|
||||
export default class SplitRoadWizard extends Combine {
|
||||
// @ts-ignore
|
||||
|
@ -54,6 +56,7 @@ export default class SplitRoadWizard extends Combine {
|
|||
changes: Changes
|
||||
layoutToUse: LayoutConfig
|
||||
allElements: ElementStorage
|
||||
selectedElement: UIEventSource<any>
|
||||
}
|
||||
) {
|
||||
const t = Translations.t.split
|
||||
|
@ -79,16 +82,8 @@ export default class SplitRoadWizard extends Combine {
|
|||
hasBeenSplit
|
||||
)
|
||||
)
|
||||
splitButton.onClick(() => {
|
||||
splitClicked.setData(true)
|
||||
})
|
||||
|
||||
// Only show the splitButton if logged in, else show login prompt
|
||||
const loginBtn = t.loginToSplit
|
||||
.Clone()
|
||||
.onClick(() => state.osmConnection.AttemptLogin())
|
||||
.SetClass("login-button-friendly")
|
||||
const splitToggle = new Toggle(splitButton, loginBtn, state.osmConnection.isLoggedIn)
|
||||
const splitToggle = new LoginToggle(splitButton, t.loginToSplit.Clone(), state)
|
||||
|
||||
// Save button
|
||||
const saveButton = new Button(t.split.Clone(), async () => {
|
||||
|
@ -110,10 +105,13 @@ export default class SplitRoadWizard extends Combine {
|
|||
// We throw away the old map and splitpoints, and create a new map from scratch
|
||||
splitPoints.setData([])
|
||||
leafletMap.setData(SplitRoadWizard.setupMapComponent(id, splitPoints, state))
|
||||
|
||||
// Close the popup. The contributor has to select a segment again to make sure they continue editing the correct segment; see #1219
|
||||
ScrollableFullScreen.collapse()
|
||||
})
|
||||
|
||||
saveButton.SetClass("btn btn-primary mr-3")
|
||||
const disabledSaveButton = new Button("Split", undefined)
|
||||
const disabledSaveButton = new Button(t.split.Clone(), undefined)
|
||||
disabledSaveButton.SetClass("btn btn-disabled mr-3")
|
||||
// Only show the save button if there are split points defined
|
||||
const saveToggle = new Toggle(
|
||||
|
@ -147,6 +145,11 @@ export default class SplitRoadWizard extends Combine {
|
|||
new Toggle(mapView, splitToggle, splitClicked),
|
||||
])
|
||||
this.dialogIsOpened = splitClicked
|
||||
const self = this
|
||||
splitButton.onClick(() => {
|
||||
splitClicked.setData(true)
|
||||
self.ScrollIntoView()
|
||||
})
|
||||
}
|
||||
|
||||
private static setupMapComponent(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { InputElement } from "../Input/InputElement"
|
||||
import { Review } from "../../Logic/Web/Review"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { TextField } from "../Input/TextField"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Combine from "../Base/Combine"
|
||||
|
@ -11,6 +11,7 @@ import CheckBoxes from "../Input/Checkboxes"
|
|||
import UserDetails, { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import Toggle from "../Input/Toggle"
|
||||
import { LoginToggle } from "../Popup/LoginButton"
|
||||
|
||||
export default class ReviewForm extends InputElement<Review> {
|
||||
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
||||
|
@ -20,11 +21,21 @@ export default class ReviewForm extends InputElement<Review> {
|
|||
private _saveButton: BaseUIElement
|
||||
private readonly _isAffiliated: BaseUIElement
|
||||
private readonly _postingAs: BaseUIElement
|
||||
private readonly _osmConnection: OsmConnection
|
||||
private readonly _state: {
|
||||
readonly osmConnection: OsmConnection
|
||||
readonly featureSwitchUserbadge: Store<boolean>
|
||||
}
|
||||
|
||||
constructor(onSave: (r: Review, doneSaving: () => void) => void, osmConnection: OsmConnection) {
|
||||
constructor(
|
||||
onSave: (r: Review, doneSaving: () => void) => void,
|
||||
state: {
|
||||
readonly osmConnection: OsmConnection
|
||||
readonly featureSwitchUserbadge: Store<boolean>
|
||||
}
|
||||
) {
|
||||
super()
|
||||
this._osmConnection = osmConnection
|
||||
this._state = state
|
||||
const osmConnection = state.osmConnection
|
||||
this._value = new UIEventSource({
|
||||
made_by_user: new UIEventSource<boolean>(true),
|
||||
rating: undefined,
|
||||
|
@ -112,12 +123,11 @@ export default class ReviewForm extends InputElement<Review> {
|
|||
" border: 2px solid var(--subtle-detail-color-contrast)"
|
||||
)
|
||||
|
||||
const connection = this._osmConnection
|
||||
const login = Translations.t.reviews.plz_login
|
||||
.Clone()
|
||||
.onClick(() => connection.AttemptLogin())
|
||||
|
||||
return new Toggle(form, login, connection.isLoggedIn).ConstructElement()
|
||||
return new LoginToggle(
|
||||
form,
|
||||
Translations.t.reviews.plz_login.Clone(),
|
||||
this._state
|
||||
).ConstructElement()
|
||||
}
|
||||
|
||||
IsValid(r: Review): boolean {
|
||||
|
|
|
@ -4,8 +4,10 @@ import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
|
|||
import { ElementStorage } from "../../Logic/ElementStorage"
|
||||
import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource"
|
||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
||||
import { LeafletMouseEvent } from "leaflet"
|
||||
import { LeafletMouseEvent, PathOptions } from "leaflet"
|
||||
import Hash from "../../Logic/Web/Hash"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import { Utils } from "../../Utils"
|
||||
/*
|
||||
// import 'leaflet-polylineoffset';
|
||||
We don't actually import it here. It is imported in the 'MinimapImplementation'-class, which'll result in a patched 'L' object.
|
||||
|
@ -47,6 +49,7 @@ export default class ShowDataLayerImplementation {
|
|||
string,
|
||||
{ feature: any; activateFunc: (event: LeafletMouseEvent) => void }
|
||||
>()
|
||||
|
||||
private readonly showDataLayerid: number
|
||||
private readonly createPopup: (
|
||||
tags: UIEventSource<any>,
|
||||
|
@ -81,7 +84,7 @@ export default class ShowDataLayerImplementation {
|
|||
}
|
||||
const self = this
|
||||
|
||||
options.leafletMap.addCallback((_) => {
|
||||
options.leafletMap.addCallback(() => {
|
||||
return self.update(options)
|
||||
})
|
||||
|
||||
|
@ -112,6 +115,10 @@ export default class ShowDataLayerImplementation {
|
|||
})
|
||||
|
||||
this._selectedElement?.addCallbackAndRunD((selected) => {
|
||||
if (selected === undefined) {
|
||||
ScrollableFullScreen.collapse()
|
||||
return
|
||||
}
|
||||
self.openPopupOfSelectedElement(selected)
|
||||
})
|
||||
|
||||
|
@ -171,17 +178,8 @@ export default class ShowDataLayerImplementation {
|
|||
}
|
||||
|
||||
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),
|
||||
})
|
||||
|
||||
this.geoLayer = new L.LayerGroup()
|
||||
|
||||
const selfLayer = this.geoLayer
|
||||
const allFeats = this._features.features.data
|
||||
|
@ -189,6 +187,31 @@ export default class ShowDataLayerImplementation {
|
|||
if (feat === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Why not one geojson layer with _all_ features, and attaching a right-click onto every feature individually?
|
||||
// Because that somehow doesn't work :(
|
||||
const feature = feat
|
||||
const geojsonLayer = L.geoJSON(feature, {
|
||||
style: (feature) => <PathOptions>self.createStyleFor(feature),
|
||||
pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng),
|
||||
onEachFeature: (feature, leafletLayer) =>
|
||||
self.postProcessFeature(feature, leafletLayer),
|
||||
})
|
||||
if (feature.geometry.type === "Point") {
|
||||
geojsonLayer.on({
|
||||
contextmenu: (e) => {
|
||||
const o = self.leafletLayersPerId.get(feature?.properties?.id)
|
||||
o?.activateFunc(<LeafletMouseEvent>e)
|
||||
Utils.preventDefaultOnMouseEvent(e.originalEvent)
|
||||
},
|
||||
dblclick: (e) => {
|
||||
const o = self.leafletLayersPerId.get(feature?.properties?.id)
|
||||
o?.activateFunc(<LeafletMouseEvent>e)
|
||||
Utils.preventDefaultOnMouseEvent(e.originalEvent)
|
||||
},
|
||||
})
|
||||
}
|
||||
this.geoLayer.addLayer(geojsonLayer)
|
||||
try {
|
||||
if (feat.geometry.type === "LineString") {
|
||||
const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates)
|
||||
|
@ -229,7 +252,7 @@ export default class ShowDataLayerImplementation {
|
|||
return self.geoLayer !== selfLayer
|
||||
})
|
||||
} else {
|
||||
this.geoLayer.addData(feat)
|
||||
geojsonLayer.addData(feat)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
|
@ -242,14 +265,14 @@ export default class ShowDataLayerImplementation {
|
|||
}
|
||||
}
|
||||
|
||||
if (options.zoomToFeatures ?? false) {
|
||||
if (this.geoLayer.getLayers().length > 0) {
|
||||
try {
|
||||
const bounds = this.geoLayer.getBounds()
|
||||
mp.fitBounds(bounds, { animate: false })
|
||||
} catch (e) {
|
||||
console.debug("Invalid bounds", e)
|
||||
}
|
||||
if ((options.zoomToFeatures ?? false) && allFeats.length > 0) {
|
||||
let bound = undefined
|
||||
for (const feat of allFeats) {
|
||||
const fbound = BBox.get(feat)
|
||||
bound = bound?.unionWith(fbound) ?? fbound
|
||||
}
|
||||
if (bound !== undefined) {
|
||||
mp.fitBounds(bound?.toLeaflet(), { animate: false })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,29 +335,7 @@ export default class ShowDataLayerImplementation {
|
|||
icon: L.divIcon(style),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Post processing - basically adding the popup
|
||||
* @param feature
|
||||
* @param leafletLayer
|
||||
* @private
|
||||
*/
|
||||
private postProcessFeature(feature, leafletLayer: L.Evented) {
|
||||
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!
|
||||
return
|
||||
}
|
||||
const key = feature.properties.id
|
||||
if (this.leafletLayersPerId.has(key)) {
|
||||
const activate = this.leafletLayersPerId.get(key)
|
||||
leafletLayer.addEventListener("click", activate.activateFunc)
|
||||
if (Hash.hash.data === key) {
|
||||
activate.activateFunc(null)
|
||||
}
|
||||
return
|
||||
}
|
||||
private createActivateFunction(feature, key: string, layer: LayerConfig): (event) => void {
|
||||
let infobox: ScrollableFullScreen = undefined
|
||||
const self = this
|
||||
|
||||
|
@ -354,17 +355,36 @@ export default class ShowDataLayerImplementation {
|
|||
self._selectedElement.setData(
|
||||
self.allElements.ContainingFeatures.get(feature.id) ?? feature
|
||||
)
|
||||
event?.originalEvent?.preventDefault()
|
||||
event?.originalEvent?.stopPropagation()
|
||||
event?.originalEvent?.stopImmediatePropagation()
|
||||
if (event?.originalEvent) {
|
||||
// This is a total workaround, as 'preventDefault' and everything above seems to be not working
|
||||
event.originalEvent["dismissed"] = true
|
||||
}
|
||||
}
|
||||
return activate
|
||||
}
|
||||
/**
|
||||
* Post processing - basically adding the popup
|
||||
* @param feature
|
||||
* @param leafletLayer
|
||||
* @private
|
||||
*/
|
||||
private postProcessFeature(feature, leafletLayer: L.Evented) {
|
||||
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!
|
||||
return
|
||||
}
|
||||
const key = feature.properties.id
|
||||
let activate: (event) => void
|
||||
if (this.leafletLayersPerId.has(key)) {
|
||||
activate = this.leafletLayersPerId.get(key).activateFunc
|
||||
} else {
|
||||
activate = this.createActivateFunction(feature, key, layer)
|
||||
}
|
||||
|
||||
leafletLayer.addEventListener("click", activate)
|
||||
|
||||
// We also have to open on rightclick, doubleclick, ... as users sometimes do this. See #1219
|
||||
leafletLayer.on({
|
||||
dblclick: activate,
|
||||
contextmenu: activate,
|
||||
click: activate,
|
||||
})
|
||||
// Add the feature to the index to open the popup when needed
|
||||
this.leafletLayersPerId.set(key, {
|
||||
feature: feature,
|
||||
|
|
|
@ -219,7 +219,7 @@ export default class SpecialVisualizations {
|
|||
)
|
||||
const form = new ReviewForm(
|
||||
(r, whenDone) => mangrove.AddReview(r, whenDone),
|
||||
state.osmConnection
|
||||
state
|
||||
)
|
||||
return new ReviewElement(mangrove.GetSubjectUri(), mangrove.GetReviews(), form)
|
||||
},
|
||||
|
|
|
@ -42,7 +42,13 @@ export default class Translations {
|
|||
*
|
||||
*/
|
||||
static T(
|
||||
t: string | undefined | null | Translation | TypedTranslation<object>,
|
||||
t:
|
||||
| string
|
||||
| Record<string, string>
|
||||
| undefined
|
||||
| null
|
||||
| Translation
|
||||
| TypedTranslation<object>,
|
||||
context = undefined
|
||||
): TypedTranslation<object> {
|
||||
if (t === undefined || t === null) {
|
||||
|
|
12
Utils.ts
12
Utils.ts
|
@ -900,7 +900,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
url: string,
|
||||
maxCacheTimeMs: number,
|
||||
headers?: any
|
||||
): Promise<any | { error: string; url: string; statuscode?: number }> {
|
||||
): Promise<{ content: any } | { error: string; url: string; statuscode?: number }> {
|
||||
const cached = Utils._download_cache.get(url)
|
||||
if (cached !== undefined) {
|
||||
if (new Date().getTime() - cached.timestamp <= maxCacheTimeMs) {
|
||||
|
@ -1074,6 +1074,16 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
)
|
||||
}
|
||||
|
||||
public static preventDefaultOnMouseEvent(event: any) {
|
||||
event?.originalEvent?.preventDefault()
|
||||
event?.originalEvent?.stopPropagation()
|
||||
event?.originalEvent?.stopImmediatePropagation()
|
||||
if (event?.originalEvent) {
|
||||
// This is a total workaround, as 'preventDefault' and everything above seems to be not working
|
||||
event.originalEvent["dismissed"] = true
|
||||
}
|
||||
}
|
||||
|
||||
public static OsmChaLinkFor(daysInThePast, theme = undefined): string {
|
||||
const now = new Date()
|
||||
const lastWeek = new Date(now.getTime() - daysInThePast * 24 * 60 * 60 * 1000)
|
||||
|
|
|
@ -32,17 +32,7 @@
|
|||
]
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"or": [
|
||||
"amenity=atm",
|
||||
{
|
||||
"and": [
|
||||
"amenity=bank",
|
||||
"atm=yes"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
"osmTags": "amenity=atm"
|
||||
},
|
||||
"minzoom": 13,
|
||||
"presets": [
|
||||
|
@ -59,26 +49,7 @@
|
|||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
{
|
||||
"builtin": "images",
|
||||
"override": {
|
||||
"condition": "amenity!=bank"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bank-images",
|
||||
"render": "{image_carousel()}",
|
||||
"condition": "amenity=bank"
|
||||
},
|
||||
{
|
||||
"id": "atm-in-bank-notice",
|
||||
"condition": "amenity=bank",
|
||||
"render": {
|
||||
"en": "This ATM is located in or near a bank",
|
||||
"de": "Dieser Geldautomat befindet sich in oder in der Nähe einer Bank",
|
||||
"nl": "Deze geldautomaat bevindt zich in of bij een bank"
|
||||
}
|
||||
},
|
||||
"images",
|
||||
{
|
||||
"id": "name",
|
||||
"render": {
|
||||
|
@ -140,12 +111,7 @@
|
|||
"nl": "Deze geldautomaat wordt beheerd door {operator}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"builtin": "opening_hours",
|
||||
"override": {
|
||||
"condition": "amenity!=bank"
|
||||
}
|
||||
},
|
||||
"opening_hours",
|
||||
{
|
||||
"id": "cash_out",
|
||||
"question": {
|
||||
|
@ -276,6 +242,18 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"allowMove": {
|
||||
"enableImproveAccuracy": true,
|
||||
"enableRelocation": false
|
||||
},
|
||||
"deletion": {
|
||||
"softDeletionTags": {
|
||||
"and": [
|
||||
"disused:amenity=atm",
|
||||
"amenity="
|
||||
]
|
||||
}
|
||||
},
|
||||
"filter": [
|
||||
"open_now",
|
||||
{
|
||||
|
@ -292,4 +270,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
70
assets/layers/bank/bank.json
Normal file
70
assets/layers/bank/bank.json
Normal file
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"id": "bank",
|
||||
"description": {
|
||||
"en": "A financial institution to deposit money"
|
||||
},
|
||||
"name": {
|
||||
"en": "Banks"
|
||||
},
|
||||
"title": {
|
||||
"render": "Bank",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "name~*",
|
||||
"then": "{name}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"osmTags": "amenity=bank"
|
||||
},
|
||||
"mapRendering": [
|
||||
{
|
||||
"icon": "circle:white;./assets/layers/bank/bank.svg",
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
{
|
||||
"id": "has_atm",
|
||||
"question": {
|
||||
"en": "Does this bank have an ATM?"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "atm=yes",
|
||||
"then": {
|
||||
"en": "This bank has an ATM"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "atm=no",
|
||||
"then": {
|
||||
"en": "This bank does <b>not</b> have an ATM"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "atm=separate",
|
||||
"then": {
|
||||
"en": "This bank does have an ATM, but it is mapped as a different icon"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"filter": [
|
||||
"open_now",
|
||||
{
|
||||
"id": "has_atm",
|
||||
"options": [{
|
||||
"question": {
|
||||
"en": "With an ATM"
|
||||
},
|
||||
"osmTags": "atm=yes"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
22
assets/layers/bank/bank.svg
Normal file
22
assets/layers/bank/bank.svg
Normal file
|
@ -0,0 +1,22 @@
|
|||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
id="svg2"> <metadata id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs id="defs6"/>
|
||||
<rect width="16" height="16" x="0" y="0" id="canvas" style="fill:none;stroke:none;visibility:hidden"/>
|
||||
<path d="m 0,2 0,7 c 0,0.627119 0.423729,1 1,1 l 12,0 c 0.576271,0 1,-0.423729 1,-1 L 14,2 0,2 z m 3,1 8,0 c 0,1 1,2 2,2 l 0,2 C 12,7 11,8 11,9 L 3,9 C 3,8 2,7 1,7 L 1,5 C 2,5 3,4 3,3 z M 7,4 C 5.343146,4 4,4.8954305 4,6 4,7.10457 5.343146,8 7,8 8.656855,8 10,7.10457 10,6 10,4.8954305 8.656855,4 7,4 z M 0,10.5 C 0,11.626577 0.448696,12 1,12 l 12,0 c 0.551304,0 1,-0.445333 1,-1.5 -0.288136,0.271186 -0.559322,0.5 -1,0.5 L 1,11 C 0.559322,11 0.271186,10.754237 0,10.5 z" id="bank" style="fill:#734a08;fill-opacity:1;stroke:none" transform="translate(1,1)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
12
assets/layers/bank/license_info.json
Normal file
12
assets/layers/bank/license_info.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
[
|
||||
{
|
||||
"path": "bank.svg",
|
||||
"license": "CC0",
|
||||
"authors": [
|
||||
"nebulon42"
|
||||
],
|
||||
"sources": [
|
||||
"https://github.com/gmgeo/osmic/blob/master/money/bank-14.svg"
|
||||
]
|
||||
}
|
||||
]
|
|
@ -226,5 +226,17 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "Climbing gym",
|
||||
"nl": "Klimzaal"
|
||||
},
|
||||
"tags": [
|
||||
"leisure=sports_centre",
|
||||
"sport=climbing"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -205,6 +205,7 @@
|
|||
"elevator:width",
|
||||
"elevator:depth"
|
||||
],
|
||||
"defaultInput": "cm",
|
||||
"applicableUnits": [
|
||||
{
|
||||
"canonicalDenomination": "m",
|
||||
|
@ -221,7 +222,6 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"useAsDefaultInput": true,
|
||||
"canonicalDenomination": "cm",
|
||||
"alternativeDenomination": [
|
||||
"centimeter",
|
||||
|
@ -238,4 +238,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -473,6 +473,7 @@
|
|||
"kerb:height",
|
||||
"width"
|
||||
],
|
||||
"defaultInput": "cm",
|
||||
"applicableUnits": [
|
||||
{
|
||||
"useIfNoUnitGiven": true,
|
||||
|
@ -489,7 +490,6 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"useAsDefaultInput": true,
|
||||
"canonicalDenomination": "cm",
|
||||
"alternativeDenomination": [
|
||||
"centimeter",
|
||||
|
@ -506,4 +506,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
"width",
|
||||
"_biggest_width"
|
||||
],
|
||||
"defaultUnit": "cm",
|
||||
"applicableUnits": [
|
||||
{
|
||||
"useIfNoUnitGiven": true,
|
||||
|
@ -79,7 +80,6 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"useAsDefaultInput": true,
|
||||
"canonicalDenomination": "cm",
|
||||
"alternativeDenomination": [
|
||||
"centimeter",
|
||||
|
@ -150,4 +150,4 @@
|
|||
"condition": "_biggest_width_id~*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -284,7 +284,7 @@
|
|||
"da": "Hvad er webstedet for {title()}?",
|
||||
"cs": "Jaká je webová stránka {title()}?"
|
||||
},
|
||||
"render": "<a href='{website}' target='_blank'>{website}</a>",
|
||||
"render": "<a href='{website}' rel='nofollow noopener noreferrer' target='_blank'>{website}</a>",
|
||||
"freeform": {
|
||||
"key": "website",
|
||||
"type": "url",
|
||||
|
@ -295,7 +295,7 @@
|
|||
"mappings": [
|
||||
{
|
||||
"if": "contact:website~*",
|
||||
"then": "<a href='{contact:website}' target='_blank'>{contact:website}</a>",
|
||||
"then": "<a href='{contact:website}' rel='nofollow noopener noreferrer' target='_blank'>{contact:website}</a>",
|
||||
"hideInAnswer": true
|
||||
}
|
||||
]
|
||||
|
@ -1711,4 +1711,4 @@
|
|||
"fr": "Le nom du réseau est <b>{internet_access:ssid}</b>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,32 @@
|
|||
"startLon": 0,
|
||||
"startZoom": 0,
|
||||
"layers": [
|
||||
"atm"
|
||||
"atm",
|
||||
{
|
||||
"builtin": "bank",
|
||||
"override": {
|
||||
"id": "banks_with_atm",
|
||||
"name": null,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and+": [
|
||||
"atm=yes"
|
||||
]
|
||||
}
|
||||
},
|
||||
"filter": [
|
||||
"open_now"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"builtin": "bank",
|
||||
"override": {
|
||||
"minzoom": 18,
|
||||
"filter": {
|
||||
"sameAs": "bank"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,11 @@
|
|||
{
|
||||
"builtin": "cycleways_and_roads",
|
||||
"override": {
|
||||
"mapRendering": null,
|
||||
"title": null
|
||||
"title": null,
|
||||
"forceLoad": true,
|
||||
"minzoom": 18,
|
||||
"passAllFeatures": true,
|
||||
"shownByDefault": false
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -95,4 +98,4 @@
|
|||
},
|
||||
"stairs"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -551,8 +551,10 @@
|
|||
"centroid"
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"deletion": true,
|
||||
"allowMove": { "enableImproveAccuracy": true, "enableRelocation": false }
|
||||
}
|
||||
],
|
||||
"credits": "joost schouppe; stla"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -811,14 +811,22 @@ video {
|
|||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.mr-4 {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.mr-6 {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.ml-4 {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
@ -827,10 +835,6 @@ video {
|
|||
margin-bottom: 6rem;
|
||||
}
|
||||
|
||||
.mr-4 {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
@ -964,6 +968,18 @@ video {
|
|||
height: 16rem;
|
||||
}
|
||||
|
||||
.h-8 {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.h-16 {
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.h-32 {
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
.h-10 {
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
@ -972,6 +988,10 @@ video {
|
|||
height: 3rem;
|
||||
}
|
||||
|
||||
.h-6 {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.h-4 {
|
||||
height: 1rem;
|
||||
}
|
||||
|
@ -988,26 +1008,10 @@ video {
|
|||
height: 2.75rem;
|
||||
}
|
||||
|
||||
.h-6 {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.h-8 {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.h-32 {
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
.h-96 {
|
||||
height: 24rem;
|
||||
}
|
||||
|
||||
.h-16 {
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.h-0 {
|
||||
height: 0px;
|
||||
}
|
||||
|
@ -1052,6 +1056,18 @@ video {
|
|||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.w-8 {
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.w-16 {
|
||||
width: 4rem;
|
||||
}
|
||||
|
||||
.w-32 {
|
||||
width: 8rem;
|
||||
}
|
||||
|
||||
.w-10 {
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
@ -1076,10 +1092,6 @@ video {
|
|||
width: 2.75rem;
|
||||
}
|
||||
|
||||
.w-8 {
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.w-min {
|
||||
width: -webkit-min-content;
|
||||
width: min-content;
|
||||
|
@ -1094,14 +1106,6 @@ video {
|
|||
width: 24rem;
|
||||
}
|
||||
|
||||
.w-32 {
|
||||
width: 8rem;
|
||||
}
|
||||
|
||||
.w-16 {
|
||||
width: 4rem;
|
||||
}
|
||||
|
||||
.w-auto {
|
||||
width: auto;
|
||||
}
|
||||
|
@ -1378,6 +1382,14 @@ video {
|
|||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.border-b-2 {
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
|
||||
.border-dotted {
|
||||
border-style: dotted;
|
||||
}
|
||||
|
||||
.border-black {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(0 0 0 / var(--tw-border-opacity));
|
||||
|
@ -1491,6 +1503,11 @@ video {
|
|||
padding: 0.125rem;
|
||||
}
|
||||
|
||||
.px-2 {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.px-0 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
|
@ -1553,6 +1570,10 @@ video {
|
|||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.pb-8 {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.pl-5 {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
@ -1868,6 +1889,11 @@ body {
|
|||
box-sizing: initial !important;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon img {
|
||||
-webkit-touch-callout: none;
|
||||
/* prevent callout to copy image, etc when tap to hold */
|
||||
}
|
||||
|
||||
.leaflet-control-attribution {
|
||||
display: block ruby;
|
||||
}
|
||||
|
@ -2742,6 +2768,10 @@ input {
|
|||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
.md\:border-t-2 {
|
||||
border-top-width: 2px;
|
||||
}
|
||||
|
||||
.md\:p-1 {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
|
|
@ -113,6 +113,10 @@ body {
|
|||
box-sizing: initial !important;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon img {
|
||||
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
|
||||
}
|
||||
|
||||
.leaflet-control-attribution {
|
||||
display: block ruby;
|
||||
}
|
||||
|
|
|
@ -188,6 +188,9 @@
|
|||
"loading": "Loading…",
|
||||
"loadingTheme": "Loading {theme}…",
|
||||
"loginFailed": "Logging in into OpenStreetMap failed",
|
||||
"loginFailedOfflineMode": "OpenStreetMap.org is currently not available due to maintenance. Making edits will be possible soon",
|
||||
"loginFailedReadonlyMode": "OpenStreetMap.org is currently in readonly mode due to maintenance. Making edits will be possible soon",
|
||||
"loginFailedUnreachableMode": "OpenStreetMap.org is currently not reachable. Are you connected to the internet or do you block third parties? Try again later",
|
||||
"loginOnlyNeededToEdit": "if you want to make changes",
|
||||
"loginToStart": "Log in to answer this question",
|
||||
"loginWithOpenStreetMap": "Login with OpenStreetMap",
|
||||
|
@ -284,6 +287,7 @@
|
|||
"uploadGpx": {
|
||||
"choosePermission": "Choose below if your track should be shared:",
|
||||
"confirm": "Confirm upload",
|
||||
"gpxServiceOffline": "The GPX-service is currently offline - uploading is currently not possible. Try again later.",
|
||||
"intro0": "By uploading your track, OpenStreetMap.org will retain a full copy of the track.",
|
||||
"intro1": "You will be able to download your track again and to load them into OpenStreetMap editing programs",
|
||||
"meta": {
|
||||
|
@ -327,7 +331,7 @@
|
|||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday"
|
||||
},
|
||||
"welcomeBack": "You are logged in, welcome back!",
|
||||
"welcomeBack": "Welcome back!",
|
||||
"welcomeExplanation": {
|
||||
"addNew": "Tap the map to add a new POI.",
|
||||
"browseMoreMaps": "Discover more maps",
|
||||
|
|
|
@ -14,6 +14,7 @@ describe("Unit", () => {
|
|||
nl: " megawatt",
|
||||
},
|
||||
},
|
||||
false,
|
||||
"test"
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue