Studo: WIP

This commit is contained in:
Pieter Vander Vennet 2023-09-15 01:16:33 +02:00
parent 7ebb3d721c
commit 338599454c
30 changed files with 42794 additions and 749 deletions

View file

@ -36,7 +36,7 @@
"properties": { "properties": {
"osmTags": { "osmTags": {
"$ref": "#/definitions/TagConfigJson", "$ref": "#/definitions/TagConfigJson",
"description": "question: Which tags must be present on the feature to show it in this layer?\n\n Every source must set which tags have to be present in order to load the given layer." "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer."
}, },
"maxCacheAge": { "maxCacheAge": {
"description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days", "description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days",
@ -167,28 +167,21 @@
] ]
}, },
"mapRendering": { "mapRendering": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering", "description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"anyOf": [ "type": "array",
{ "items": {
"type": "array", "anyOf": [
"items": { {
"anyOf": [ "$ref": "#/definitions/default_4"
{ },
"$ref": "#/definitions/default_4" {
}, "$ref": "#/definitions/default_5"
{ },
"$ref": "#/definitions/default_5" {
}, "$ref": "#/definitions/default<default|default|default[]|default[]>"
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
} }
}, ]
{ }
"type": "null"
}
]
}, },
"passAllFeatures": { "passAllFeatures": {
"description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert",
@ -352,7 +345,7 @@
] ]
}, },
"deletion": { "deletion": {
"description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion", "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion",
"anyOf": [ "anyOf": [
{ {
"$ref": "#/definitions/DeleteConfigJson" "$ref": "#/definitions/DeleteConfigJson"
@ -404,7 +397,6 @@
}, },
"required": [ "required": [
"id", "id",
"mapRendering",
"source" "source"
], ],
"definitions": { "definitions": {
@ -1179,7 +1171,7 @@
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -1214,7 +1206,7 @@
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
@ -1375,7 +1367,7 @@
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -1410,7 +1402,7 @@
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"

View file

@ -36,7 +36,7 @@ export default {
"properties": { "properties": {
"osmTags": { "osmTags": {
"$ref": "#/definitions/TagConfigJson", "$ref": "#/definitions/TagConfigJson",
"description": "question: Which tags must be present on the feature to show it in this layer?\n\n Every source must set which tags have to be present in order to load the given layer." "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer."
}, },
"maxCacheAge": { "maxCacheAge": {
"description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days", "description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days",
@ -167,28 +167,21 @@ export default {
] ]
}, },
"mapRendering": { "mapRendering": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering", "description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"anyOf": [ "type": "array",
{ "items": {
"type": "array", "anyOf": [
"items": { {
"anyOf": [ "$ref": "#/definitions/default_4"
{ },
"$ref": "#/definitions/default_4" {
}, "$ref": "#/definitions/default_5"
{ },
"$ref": "#/definitions/default_5" {
}, "$ref": "#/definitions/default<default|default|default[]|default[]>"
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
} }
}, ]
{ }
"type": "null"
}
]
}, },
"passAllFeatures": { "passAllFeatures": {
"description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert",
@ -352,7 +345,7 @@ export default {
] ]
}, },
"deletion": { "deletion": {
"description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion", "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion",
"anyOf": [ "anyOf": [
{ {
"$ref": "#/definitions/DeleteConfigJson" "$ref": "#/definitions/DeleteConfigJson"
@ -404,7 +397,6 @@ export default {
}, },
"required": [ "required": [
"id", "id",
"mapRendering",
"source" "source"
], ],
"definitions": { "definitions": {
@ -1167,7 +1159,7 @@ export default {
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -1202,7 +1194,7 @@ export default {
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
@ -1362,7 +1354,7 @@ export default {
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -1397,7 +1389,7 @@ export default {
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"

View file

@ -1076,7 +1076,7 @@
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -1111,7 +1111,7 @@
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
@ -1272,7 +1272,7 @@
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -1307,7 +1307,7 @@
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
@ -1767,7 +1767,7 @@
"properties": { "properties": {
"osmTags": { "osmTags": {
"$ref": "#/definitions/TagConfigJson", "$ref": "#/definitions/TagConfigJson",
"description": "question: Which tags must be present on the feature to show it in this layer?\n\n Every source must set which tags have to be present in order to load the given layer." "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer."
}, },
"maxCacheAge": { "maxCacheAge": {
"description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days", "description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days",
@ -1898,28 +1898,21 @@
] ]
}, },
"mapRendering": { "mapRendering": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering", "description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"anyOf": [ "type": "array",
{ "items": {
"type": "array", "anyOf": [
"items": { {
"anyOf": [ "$ref": "#/definitions/default_4"
{ },
"$ref": "#/definitions/default_4" {
}, "$ref": "#/definitions/default_5"
{ },
"$ref": "#/definitions/default_5" {
}, "$ref": "#/definitions/default<default|default|default[]|default[]>"
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
} }
}, ]
{ }
"type": "null"
}
]
}, },
"passAllFeatures": { "passAllFeatures": {
"description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert",
@ -2083,7 +2076,7 @@
] ]
}, },
"deletion": { "deletion": {
"description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion", "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion",
"anyOf": [ "anyOf": [
{ {
"$ref": "#/definitions/DeleteConfigJson" "$ref": "#/definitions/DeleteConfigJson"
@ -2135,7 +2128,6 @@
}, },
"required": [ "required": [
"id", "id",
"mapRendering",
"source" "source"
], ],
"additionalProperties": false "additionalProperties": false
@ -2177,7 +2169,7 @@
"properties": { "properties": {
"osmTags": { "osmTags": {
"$ref": "#/definitions/TagConfigJson", "$ref": "#/definitions/TagConfigJson",
"description": "question: Which tags must be present on the feature to show it in this layer?\n\n Every source must set which tags have to be present in order to load the given layer." "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer."
}, },
"maxCacheAge": { "maxCacheAge": {
"description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days", "description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days",
@ -2308,28 +2300,21 @@
] ]
}, },
"mapRendering": { "mapRendering": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering", "description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"anyOf": [ "type": "array",
{ "items": {
"type": "array", "anyOf": [
"items": { {
"anyOf": [ "$ref": "#/definitions/default_4"
{ },
"$ref": "#/definitions/default_4" {
}, "$ref": "#/definitions/default_5"
{ },
"$ref": "#/definitions/default_5" {
}, "$ref": "#/definitions/default<default|default|default[]|default[]>"
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
} }
}, ]
{ }
"type": "null"
}
]
}, },
"passAllFeatures": { "passAllFeatures": {
"description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert",
@ -2493,7 +2478,7 @@
] ]
}, },
"deletion": { "deletion": {
"description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion", "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion",
"anyOf": [ "anyOf": [
{ {
"$ref": "#/definitions/DeleteConfigJson" "$ref": "#/definitions/DeleteConfigJson"
@ -2579,4 +2564,4 @@
}, },
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false "additionalProperties": false
} }

View file

@ -1064,7 +1064,7 @@ export default {
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -1099,7 +1099,7 @@ export default {
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
@ -1259,7 +1259,7 @@ export default {
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -1294,7 +1294,7 @@ export default {
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
@ -1746,7 +1746,7 @@ export default {
"properties": { "properties": {
"osmTags": { "osmTags": {
"$ref": "#/definitions/TagConfigJson", "$ref": "#/definitions/TagConfigJson",
"description": "question: Which tags must be present on the feature to show it in this layer?\n\n Every source must set which tags have to be present in order to load the given layer." "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer."
}, },
"maxCacheAge": { "maxCacheAge": {
"description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days", "description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days",
@ -1877,28 +1877,21 @@ export default {
] ]
}, },
"mapRendering": { "mapRendering": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering", "description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"anyOf": [ "type": "array",
{ "items": {
"type": "array", "anyOf": [
"items": { {
"anyOf": [ "$ref": "#/definitions/default_4"
{ },
"$ref": "#/definitions/default_4" {
}, "$ref": "#/definitions/default_5"
{ },
"$ref": "#/definitions/default_5" {
}, "$ref": "#/definitions/default<default|default|default[]|default[]>"
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
} }
}, ]
{ }
"type": "null"
}
]
}, },
"passAllFeatures": { "passAllFeatures": {
"description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert",
@ -2062,7 +2055,7 @@ export default {
] ]
}, },
"deletion": { "deletion": {
"description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion", "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion",
"anyOf": [ "anyOf": [
{ {
"$ref": "#/definitions/DeleteConfigJson" "$ref": "#/definitions/DeleteConfigJson"
@ -2114,7 +2107,6 @@ export default {
}, },
"required": [ "required": [
"id", "id",
"mapRendering",
"source" "source"
] ]
}, },
@ -2155,7 +2147,7 @@ export default {
"properties": { "properties": {
"osmTags": { "osmTags": {
"$ref": "#/definitions/TagConfigJson", "$ref": "#/definitions/TagConfigJson",
"description": "question: Which tags must be present on the feature to show it in this layer?\n\n Every source must set which tags have to be present in order to load the given layer." "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer."
}, },
"maxCacheAge": { "maxCacheAge": {
"description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days", "description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat\ndefault: 30 days",
@ -2286,28 +2278,21 @@ export default {
] ]
}, },
"mapRendering": { "mapRendering": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering", "description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"anyOf": [ "type": "array",
{ "items": {
"type": "array", "anyOf": [
"items": { {
"anyOf": [ "$ref": "#/definitions/default_4"
{ },
"$ref": "#/definitions/default_4" {
}, "$ref": "#/definitions/default_5"
{ },
"$ref": "#/definitions/default_5" {
}, "$ref": "#/definitions/default<default|default|default[]|default[]>"
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
} }
}, ]
{ }
"type": "null"
}
]
}, },
"passAllFeatures": { "passAllFeatures": {
"description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert",
@ -2471,7 +2456,7 @@ export default {
] ]
}, },
"deletion": { "deletion": {
"description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion", "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion",
"anyOf": [ "anyOf": [
{ {
"$ref": "#/definitions/DeleteConfigJson" "$ref": "#/definitions/DeleteConfigJson"
@ -2554,4 +2539,4 @@ export default {
} }
}, },
"$schema": "http://json-schema.org/draft-07/schema#" "$schema": "http://json-schema.org/draft-07/schema#"
} }

View file

@ -44,7 +44,7 @@
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -79,7 +79,7 @@
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"

View file

@ -44,7 +44,7 @@ export default {
} }
}, },
"inline": { "inline": {
"description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: do not show the", "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question",
"type": "boolean" "type": "boolean"
}, },
"default": { "default": {
@ -79,7 +79,7 @@ export default {
] ]
}, },
"labels": { "labels": {
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away", "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"

View file

@ -200,9 +200,6 @@ function addMetafields(fieldnames: string[], fullSchema: JsonSchema): ConfigMeta
if (schemePart.description === undefined) { if (schemePart.description === undefined) {
return return
} }
if (path.length === 2 && path[0] === "mappings" && path[1] === "if") {
console.log("HI")
}
const type = schemePart.items?.anyOf ?? schemePart.type ?? schemePart.anyOf const type = schemePart.items?.anyOf ?? schemePart.type ?? schemePart.anyOf
let description = schemePart.description let description = schemePart.description
@ -291,10 +288,10 @@ function validateMeta(path: ConfigMeta): string | undefined {
return undefined return undefined
} }
if (path.hints.question === undefined && !Array.isArray(path.type)) { if (path.hints.question === undefined && !Array.isArray(path.type)) {
/* return ( return (
ctx + ctx +
" does not have a question set. As such, MapComplete-studio users will not be able to set this property" " does not have a question set. As such, MapComplete-studio users will not be able to set this property"
)//*/ ) //*/
} }
return undefined return undefined
@ -347,9 +344,9 @@ function main() {
}) })
} }
const errs: string[] = [] const errs: string[] = []
// errs = extractMeta("LayerConfigJson", "layerconfigmeta", allDefinitions) errs.push(...extractMeta("LayerConfigJson", "layerconfigmeta", allDefinitions))
// errs.push(...extractMeta("LayoutConfigJson", "layoutconfigmeta", allDefinitions)) // errs.push(...extractMeta("LayoutConfigJson", "layoutconfigmeta", allDefinitions))
// errs.push(...extractMeta("TagRenderingConfigJson", "tagrenderingconfigmeta", allDefinitions)) errs.push(...extractMeta("TagRenderingConfigJson", "tagrenderingconfigmeta", allDefinitions))
errs.push( errs.push(
...extractMeta( ...extractMeta(
"QuestionableTagRenderingConfigJson", "QuestionableTagRenderingConfigJson",

99
scripts/studioServer.ts Normal file
View file

@ -0,0 +1,99 @@
import * as fs from "node:fs"
import * as http from "node:http"
import * as path from "node:path"
import { ReadStream } from "fs"
import ScriptUtils from "./ScriptUtils"
const PORT = 1235
const CORS = "http://localhost:1234,https://mapcomplete.org,https://dev.mapcomplete.org"
const MIME_TYPES = {
default: "application/octet-stream",
html: "text/html; charset=UTF-8",
js: "application/javascript",
css: "text/css",
png: "image/png",
jpg: "image/jpg",
gif: "image/gif",
ico: "image/x-icon",
svg: "image/svg+xml",
json: "application/json",
}
const STATIC_PATH = path.join(process.cwd(), "./assets")
const toBool = [() => true, () => false]
const prepareFile: (url) => Promise<{ ext: string; found: boolean; stream: ReadStream }> = async (
url
) => {
const paths = [STATIC_PATH, url]
if (url.endsWith("/")) paths.push("index.html")
const filePath = path.join(...paths)
const pathTraversal = !filePath.startsWith(STATIC_PATH)
const exists = await fs.promises.access(filePath).then(...toBool)
const found = !pathTraversal && exists
const streamPath = found ? filePath : STATIC_PATH + "/404.html"
const ext = path.extname(streamPath).substring(1).toLowerCase()
const stream = fs.createReadStream(streamPath)
return { found, ext, stream }
}
http.createServer(async (req, res) => {
try {
console.log(req.method + " " + req.url, "from:", req.headers.origin)
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
)
res.setHeader("Access-Control-Allow-Origin", req.headers.origin ?? "*")
if (req.method === "OPTIONS") {
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, UPDATE")
res.writeHead(204, { "Content-Type": MIME_TYPES.html })
res.end()
return
}
if (req.method === "POST" || req.method === "UPDATE") {
const paths = req.url.split("/")
console.log("Got an update to:", paths.join("/"))
for (let i = 1; i < paths.length; i++) {
const p = paths.slice(0, i)
const dir = STATIC_PATH + p.join("/")
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
}
}
req.pipe(fs.createWriteStream(STATIC_PATH + paths.join("/") + ".new.json"))
res.writeHead(200, { "Content-Type": MIME_TYPES.html })
res.write("<html><body>OK</body></html>", "utf8")
res.end()
return
}
if (req.url.endsWith("/overview")) {
console.log("Giving overview")
const allFiles = ScriptUtils.readDirRecSync(STATIC_PATH)
.filter((p) => p.endsWith(".json") && !p.endsWith("license_info.json"))
.map((p) => p.substring(STATIC_PATH.length + 1))
res.writeHead(200, { "Content-Type": MIME_TYPES.json })
res.write(JSON.stringify({ allFiles }))
res.end()
return
}
if (!fs.existsSync(STATIC_PATH + req.url)) {
res.writeHead(404, { "Content-Type": MIME_TYPES.html })
res.write("<html><body><p>Not found...</p></body></html>")
res.end()
return
}
const file = await prepareFile(req.url)
const statusCode = file.found ? 200 : 404
const mimeType = MIME_TYPES[file.ext] || MIME_TYPES.default
res.writeHead(statusCode, { "Content-Type": mimeType })
file.stream.pipe(res)
res.end()
} catch (e) {
console.error(e)
}
}).listen(PORT)
console.log(`Server running at http://127.0.0.1:${PORT}/`)

View file

@ -149,7 +149,7 @@ class ExpandTagRendering extends Conversion<
this._options = options this._options = options
this._tagRenderingsByLabel = new Map<string, TagRenderingConfigJson[]>() this._tagRenderingsByLabel = new Map<string, TagRenderingConfigJson[]>()
for (const trconfig of state.tagRenderings?.values() ?? []) { for (const trconfig of state.tagRenderings?.values() ?? []) {
for (const label of trconfig.labels ?? []) { for (const label of trconfig["labels"] ?? []) {
let withLabel = this._tagRenderingsByLabel.get(label) let withLabel = this._tagRenderingsByLabel.get(label)
if (withLabel === undefined) { if (withLabel === undefined) {
withLabel = [] withLabel = []
@ -193,7 +193,7 @@ class ExpandTagRendering extends Conversion<
for (let foundTr of indirect) { for (let foundTr of indirect) {
foundTr = Utils.Clone<any>(foundTr) foundTr = Utils.Clone<any>(foundTr)
Utils.Merge(tagRenderingConfigJson["override"] ?? {}, foundTr) Utils.Merge(tagRenderingConfigJson["override"] ?? {}, foundTr)
foundTr.id = tagRenderingConfigJson.id ?? foundTr.id foundTr["id"] = tagRenderingConfigJson["id"] ?? foundTr["id"]
result.push(foundTr) result.push(foundTr)
} }
} else { } else {
@ -239,9 +239,9 @@ class ExpandTagRendering extends Conversion<
matchingTrs = layerTrs matchingTrs = layerTrs
} else if (id.startsWith("*")) { } else if (id.startsWith("*")) {
const id_ = id.substring(1) const id_ = id.substring(1)
matchingTrs = layerTrs.filter((tr) => tr.labels?.indexOf(id_) >= 0) matchingTrs = layerTrs.filter((tr) => tr["labels"]?.indexOf(id_) >= 0)
} else { } else {
matchingTrs = layerTrs.filter((tr) => tr.id === id || tr.labels?.indexOf(id) >= 0) matchingTrs = layerTrs.filter((tr) => tr["id"] === id || tr["labels"]?.indexOf(id) >= 0)
} }
const contextWriter = new AddContextToTranslations<TagRenderingConfigJson>("layers:") const contextWriter = new AddContextToTranslations<TagRenderingConfigJson>("layers:")
@ -489,7 +489,15 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
} }
} }
json = JSON.parse(JSON.stringify(json)) json = JSON.parse(JSON.stringify(json))
json.freeform.inline ??= true if (typeof json.freeform === "string") {
errors.push("At " + context + ": 'freeform' is a string, but should be an object")
return { result: json, errors }
}
try {
json.freeform.inline ??= true
} catch (e) {
errors.push("At " + context + ": " + e.message)
}
return { result: json, errors } return { result: json, errors }
} }
} }

View file

@ -64,8 +64,7 @@ export interface LayerConfigJson {
| { | {
/** /**
* question: Which tags must be present on the feature to show it in this layer? * question: Which tags must be present on the feature to show it in this layer?
* * Every source must set which tags have to be present in order to load the given layer.
* Every source must set which tags have to be present in order to load the given layer.
*/ */
osmTags: TagConfigJson osmTags: TagConfigJson
/** /**
@ -238,21 +237,19 @@ export interface LayerConfigJson {
/** /**
* Visualisation of the items on the map * Visualisation of the items on the map
* * Set 'null' explicitly if you do not want a maprendering
* group: maprendering * group: maprendering
*/ */
mapRendering: mapRendering?: (
| null | PointRenderingConfigJson
| ( | LineRenderingConfigJson
| PointRenderingConfigJson | RewritableConfigJson<
| LineRenderingConfigJson | LineRenderingConfigJson
| RewritableConfigJson< | PointRenderingConfigJson
| LineRenderingConfigJson | LineRenderingConfigJson[]
| PointRenderingConfigJson | PointRenderingConfigJson[]
| LineRenderingConfigJson[] >
| PointRenderingConfigJson[] )[]
>
)[]
/** /**
* If set, this layer will pass all the features it receives onto the next layer. * If set, this layer will pass all the features it receives onto the next layer.
@ -467,7 +464,7 @@ export interface LayerConfigJson {
* A no-delete option is offered as 'reason to delete it', but secretly retags. * A no-delete option is offered as 'reason to delete it', but secretly retags.
* *
* group: editing * group: editing
* types: use an advanced delete configuration ; boolean * types: Use an advanced delete configuration ; boolean
* iftrue: Allow deletion * iftrue: Allow deletion
* iffalse: Do not allow deletion * iffalse: Do not allow deletion
* *

View file

@ -260,7 +260,7 @@ export interface QuestionableTagRenderingConfigJson extends TagRenderingConfigJs
questionHint?: string | Translatable questionHint?: string | Translatable
/** /**
* A list of labels. These are strings that are used for various purposes, e.g. to filter them away * A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer
*/ */
labels?: string[] labels?: string[]
} }

View file

@ -2,7 +2,7 @@
import { UIEventSource } from "../../../Logic/UIEventSource"; import { UIEventSource } from "../../../Logic/UIEventSource";
import LanguageUtils from "../../../Utils/LanguageUtils"; import LanguageUtils from "../../../Utils/LanguageUtils";
import { onDestroy } from "svelte"; import { createEventDispatcher, onDestroy } from "svelte";
import ValidatedInput from "../ValidatedInput.svelte"; import ValidatedInput from "../ValidatedInput.svelte";
export let value: UIEventSource<string> = new UIEventSource<string>(""); export let value: UIEventSource<string> = new UIEventSource<string>("");
@ -18,6 +18,7 @@
const allLanguages: string[] = LanguageUtils.usedLanguagesSorted; const allLanguages: string[] = LanguageUtils.usedLanguagesSorted;
let currentLang = new UIEventSource("en"); let currentLang = new UIEventSource("en");
const currentVal = new UIEventSource<string>(""); const currentVal = new UIEventSource<string>("");
let dispatch = createEventDispatcher<{ submit }>()
function update() { function update() {
const v = currentVal.data; const v = currentVal.data;
@ -49,5 +50,5 @@
</option> </option>
{/each} {/each}
</select> </select>
<ValidatedInput type="string" value={currentVal} /> <ValidatedInput type="string" value={currentVal} on:submit={() => dispatch("submit")} />
</div> </div>

View file

@ -4,30 +4,59 @@
* Note that all values are stringified * Note that all values are stringified
*/ */
import { UIEventSource } from "../../Logic/UIEventSource" import { UIEventSource } from "../../Logic/UIEventSource";
import type { ValidatorType } from "./Validators" import type { ValidatorType } from "./Validators";
import InputHelpers from "./InputHelpers" import InputHelpers from "./InputHelpers";
import ToSvelte from "../Base/ToSvelte.svelte" import ToSvelte from "../Base/ToSvelte.svelte";
import type { Feature } from "geojson" import type { Feature } from "geojson";
import BaseUIElement from "../BaseUIElement" import BaseUIElement from "../BaseUIElement";
import { VariableUiElement } from "../Base/VariableUIElement" import { VariableUiElement } from "../Base/VariableUIElement";
import { createEventDispatcher } from "svelte";
import ImageHelper from "./Helpers/ImageHelper.svelte";
import TranslationInput from "./Helpers/TranslationInput.svelte";
import TagInput from "./Helpers/TagInput.svelte";
import SimpleTagInput from "./Helpers/SimpleTagInput.svelte";
import DirectionInput from "./Helpers/DirectionInput.svelte";
import DateInput from "./Helpers/DateInput.svelte";
import ColorInput from "./Helpers/ColorInput.svelte";
export let type: ValidatorType export let type: ValidatorType;
export let value: UIEventSource<string> export let value: UIEventSource<string>;
export let feature: Feature export let feature: Feature;
export let args: (string | number | boolean)[] = undefined export let args: (string | number | boolean)[] = undefined;
let properties = { feature, args: args ?? [] } let properties = { feature, args: args ?? [] };
let construct = new UIEventSource<(value, extraProperties) => BaseUIElement>(undefined) let construct = new UIEventSource<(value, extraProperties) => BaseUIElement>(undefined);
$: { $: {
construct.setData(InputHelpers.AvailableInputHelpers[type]) const helper = InputHelpers.AvailableInputHelpers[type];
construct.setData(helper);
} }
let dispatch = createEventDispatcher<{ selected, submit }>();
</script> </script>
{#if construct !== undefined} {#if type === "translation" }
<ToSvelte <TranslationInput {value} on:submit={() => dispatch("submit")} />
construct={() => {:else if type === "direction"}
<DirectionInput {value} mapProperties={InputHelpers.constructMapProperties(properties)} />
{:else if type === "date"}
<DateInput { value } />
{:else if type === "color"}
<ColorInput { value } />
{:else if type === "image"}
<ImageHelper { value } />
{:else if type === "tag"}
<TagInput { value } />
{:else if type === "simple_tag"}
<SimpleTagInput { value } />
{:else if $construct !== undefined}
{#if isBaseUIElement}
<ToSvelte
construct={() =>
new VariableUiElement(construct.mapD((construct) => construct(value, properties)))} new VariableUiElement(construct.mapD((construct) => construct(value, properties)))}
/> />
{/if}
{/if} {/if}

View file

@ -1,10 +1,7 @@
import { ValidatorType } from "./Validators" import { ValidatorType } from "./Validators"
import { UIEventSource } from "../../Logic/UIEventSource" import { UIEventSource } from "../../Logic/UIEventSource"
import SvelteUIElement from "../Base/SvelteUIElement"
import DirectionInput from "./Helpers/DirectionInput.svelte"
import { MapProperties } from "../../Models/MapProperties" import { MapProperties } from "../../Models/MapProperties"
import DateInput from "./Helpers/DateInput.svelte"
import ColorInput from "./Helpers/ColorInput.svelte"
import BaseUIElement from "../BaseUIElement" import BaseUIElement from "../BaseUIElement"
import OpeningHoursInput from "../OpeningHours/OpeningHoursInput" import OpeningHoursInput from "../OpeningHours/OpeningHoursInput"
import WikidataSearchBox from "../Wikipedia/WikidataSearchBox" import WikidataSearchBox from "../Wikipedia/WikidataSearchBox"
@ -13,10 +10,6 @@ import { Utils } from "../../Utils"
import Locale from "../i18n/Locale" import Locale from "../i18n/Locale"
import { Feature } from "geojson" import { Feature } from "geojson"
import { GeoOperations } from "../../Logic/GeoOperations" import { GeoOperations } from "../../Logic/GeoOperations"
import ImageHelper from "./Helpers/ImageHelper.svelte"
import TranslationInput from "./Helpers/TranslationInput.svelte"
import TagInput from "./Helpers/TagInput.svelte"
import SimpleTagInput from "./Helpers/SimpleTagInput.svelte"
export interface InputHelperProperties { export interface InputHelperProperties {
/** /**
@ -39,6 +32,9 @@ export interface InputHelperProperties {
} }
export default class InputHelpers { export default class InputHelpers {
/**
* @deprecated
*/
public static readonly AvailableInputHelpers: Readonly< public static readonly AvailableInputHelpers: Readonly<
Partial< Partial<
Record< Record<
@ -50,30 +46,21 @@ export default class InputHelpers {
> >
> >
> = { > = {
direction: (value, properties) => // TODO: remake in svelte,move selection logic to 'inputHelper.svelte'
new SvelteUIElement(DirectionInput, {
value,
mapProperties: InputHelpers.constructMapProperties(properties),
}),
date: (value) => new SvelteUIElement(DateInput, { value }),
color: (value) => new SvelteUIElement(ColorInput, { value }),
opening_hours: (value) => new OpeningHoursInput(value), opening_hours: (value) => new OpeningHoursInput(value),
wikidata: InputHelpers.constructWikidataHelper, wikidata: InputHelpers.constructWikidataHelper,
image: (value) => new SvelteUIElement(ImageHelper, { value }),
translation: (value) => new SvelteUIElement(TranslationInput, { value }),
tag: (value) => new SvelteUIElement(TagInput, { value }),
simple_tag: (value) => new SvelteUIElement(SimpleTagInput, { value }),
} as const } as const
public static hideInputField: string[] = ["translation", "simple_tag", "tag"] public static hideInputField: string[] = ["translation", "simple_tag", "tag"]
// noinspection JSUnusedLocalSymbols
/** /**
* Constructs a mapProperties-object for the given properties. * Constructs a mapProperties-object for the given properties.
* Assumes that the first helper-args contains the desired zoom-level * Assumes that the first helper-args contains the desired zoom-level
* @param properties * @param properties
* @private * @private
*/ */
private static constructMapProperties( public static constructMapProperties(
properties: InputHelperProperties properties: InputHelperProperties
): Partial<MapProperties> { ): Partial<MapProperties> {
let location = properties?.mapProperties?.location let location = properties?.mapProperties?.location

View file

@ -110,7 +110,7 @@
placeholder={_placeholder}></textarea> placeholder={_placeholder}></textarea>
</form> </form>
{:else} {:else}
<form class="inline-flex" on:submit={() => dispatch("submit")}> <form class="inline-flex" on:submit|preventDefault={() => dispatch("submit")}>
<input <input
bind:this={htmlElem} bind:this={htmlElem}
bind:value={$_value} bind:value={$_value}

View file

@ -66,5 +66,5 @@
/> />
{/if} {/if}
<InputHelper args={config.freeform.helperArgs} {feature} type={config.freeform.type} {value} /> <InputHelper args={config.freeform.helperArgs} {feature} type={config.freeform.type} {value} on:submit={() => dispatch("submit")} />
</div> </div>

View file

@ -1,52 +1,52 @@
<script lang="ts"> <script lang="ts">
import {ImmutableStore, Store, UIEventSource} from "../../../Logic/UIEventSource" import { ImmutableStore, UIEventSource } from "../../../Logic/UIEventSource";
import type { SpecialVisualizationState } from "../../SpecialVisualization" import type { SpecialVisualizationState } from "../../SpecialVisualization";
import Tr from "../../Base/Tr.svelte" import Tr from "../../Base/Tr.svelte";
import type { Feature } from "geojson" import type { Feature } from "geojson";
import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig" import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig";
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig" import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig";
import { TagsFilter } from "../../../Logic/Tags/TagsFilter" import { TagsFilter } from "../../../Logic/Tags/TagsFilter";
import FreeformInput from "./FreeformInput.svelte" import FreeformInput from "./FreeformInput.svelte";
import Translations from "../../i18n/Translations.js" import Translations from "../../i18n/Translations.js";
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction" import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction";
import { createEventDispatcher, onDestroy } from "svelte" import { createEventDispatcher, onDestroy } from "svelte";
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
import SpecialTranslation from "./SpecialTranslation.svelte" import SpecialTranslation from "./SpecialTranslation.svelte";
import TagHint from "../TagHint.svelte" import TagHint from "../TagHint.svelte";
import LoginToggle from "../../Base/LoginToggle.svelte" import LoginToggle from "../../Base/LoginToggle.svelte";
import SubtleButton from "../../Base/SubtleButton.svelte" import SubtleButton from "../../Base/SubtleButton.svelte";
import Loading from "../../Base/Loading.svelte" import Loading from "../../Base/Loading.svelte";
import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte" import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte";
import { Translation } from "../../i18n/Translation" import { Translation } from "../../i18n/Translation";
import Constants from "../../../Models/Constants" import Constants from "../../../Models/Constants";
import { Unit } from "../../../Models/Unit" import { Unit } from "../../../Models/Unit";
import UserRelatedState from "../../../Logic/State/UserRelatedState" import UserRelatedState from "../../../Logic/State/UserRelatedState";
import { twJoin } from "tailwind-merge" import { twJoin } from "tailwind-merge";
export let config: TagRenderingConfig export let config: TagRenderingConfig;
export let tags: UIEventSource<Record<string, string>> export let tags: UIEventSource<Record<string, string>>;
export let selectedElement: Feature export let selectedElement: Feature;
export let state: SpecialVisualizationState export let state: SpecialVisualizationState;
export let layer: LayerConfig | undefined export let layer: LayerConfig | undefined;
let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined) let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined);
let unit: Unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key)) let unit: Unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key));
// Will be bound if a freeform is available // Will be bound if a freeform is available
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key]) let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key]);
let selectedMapping: number = undefined let selectedMapping: number = undefined;
let checkedMappings: boolean[] let checkedMappings: boolean[];
$: { $: {
let tgs = $tags let tgs = $tags;
mappings = config.mappings?.filter((m) => { mappings = config.mappings?.filter((m) => {
if (typeof m.hideInAnswer === "boolean") { if (typeof m.hideInAnswer === "boolean") {
return !m.hideInAnswer return !m.hideInAnswer;
} }
return !m.hideInAnswer.matchesProperties(tgs) return !m.hideInAnswer.matchesProperties(tgs);
}) });
// We received a new config -> reinit // We received a new config -> reinit
unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key)) unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key));
if ( if (
config.mappings?.length > 0 && config.mappings?.length > 0 &&
@ -54,23 +54,23 @@
) { ) {
checkedMappings = [ checkedMappings = [
...config.mappings.map((_) => false), ...config.mappings.map((_) => false),
false /*One element extra in case a freeform value is added*/, false /*One element extra in case a freeform value is added*/
] ];
} }
if (config.freeform?.key) { if (config.freeform?.key) {
if (!config.multiAnswer) { if (!config.multiAnswer) {
// Somehow, setting multianswer freeform values is broken if this is not set // Somehow, setting multianswer freeform values is broken if this is not set
freeformInput.setData(tgs[config.freeform.key]) freeformInput.setData(tgs[config.freeform.key]);
} }
} else { } else {
freeformInput.setData(undefined) freeformInput.setData(undefined);
} }
feedback.setData(undefined) feedback.setData(undefined);
} }
export let selectedTags: TagsFilter = undefined export let selectedTags: TagsFilter = undefined;
let mappings: Mapping[] = config?.mappings let mappings: Mapping[] = config?.mappings;
let searchTerm: UIEventSource<string> = new UIEventSource("") let searchTerm: UIEventSource<string> = new UIEventSource("");
$: { $: {
try { try {
@ -79,10 +79,10 @@
selectedMapping, selectedMapping,
checkedMappings, checkedMappings,
tags.data tags.data
) );
} catch (e) { } catch (e) {
console.error("Could not calculate changeSpecification:", e) console.error("Could not calculate changeSpecification:", e);
selectedTags = undefined selectedTags = undefined;
} }
} }
@ -91,53 +91,53 @@
config: TagRenderingConfig config: TagRenderingConfig
applied: TagsFilter applied: TagsFilter
} }
}>() }>();
function onSave() { function onSave() {
if (selectedTags === undefined) { if (selectedTags === undefined) {
return return;
} }
if (layer === undefined || layer?.source === null) { if (layer === undefined || layer?.source === null) {
/** /**
* This is a special, priviliged layer. * This is a special, priviliged layer.
* We simply apply the tags onto the records * We simply apply the tags onto the records
*/ */
const kv = selectedTags.asChange(tags.data) const kv = selectedTags.asChange(tags.data);
for (const { k, v } of kv) { for (const { k, v } of kv) {
if (v === undefined) { if (v === undefined) {
delete tags.data[k] delete tags.data[k];
} else { } else {
tags.data[k] = v tags.data[k] = v;
} }
} }
tags.ping() tags.ping();
return return;
} }
dispatch("saved", { config, applied: selectedTags }) dispatch("saved", { config, applied: selectedTags });
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, { const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, {
theme: state.layout.id, theme: state.layout.id,
changeType: "answer", changeType: "answer"
}) });
freeformInput.setData(undefined) freeformInput.setData(undefined);
selectedMapping = undefined selectedMapping = undefined;
selectedTags = undefined selectedTags = undefined;
change change
.CreateChangeDescriptions() .CreateChangeDescriptions()
.then((changes) => state.changes.applyChanges(changes)) .then((changes) => state.changes.applyChanges(changes))
.catch(console.error) .catch(console.error);
} }
let featureSwitchIsTesting = state.featureSwitchIsTesting ?? new ImmutableStore(false) let featureSwitchIsTesting = state.featureSwitchIsTesting ?? new ImmutableStore(false);
let featureSwitchIsDebugging = state.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false) let featureSwitchIsDebugging = state.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false);
let showTags = state.userRelatedState?.showTags ?? new ImmutableStore(undefined) let showTags = state.userRelatedState?.showTags ?? new ImmutableStore(undefined);
let numberOfCs = state.osmConnection.userDetails.data.csCount let numberOfCs = state.osmConnection.userDetails.data.csCount;
onDestroy( onDestroy(
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => { state.osmConnection?.userDetails?.addCallbackAndRun((ud) => {
numberOfCs = ud.csCount numberOfCs = ud.csCount;
}) })
) );
</script> </script>
{#if config.question !== undefined} {#if config.question !== undefined}
@ -218,6 +218,7 @@
value={freeformInput} value={freeformInput}
on:selected={() => (selectedMapping = config.mappings?.length)} on:selected={() => (selectedMapping = config.mappings?.length)}
on:submit={onSave} on:submit={onSave}
submit={onSave}
/> />
</label> </label>
{/if} {/if}

View file

@ -1,69 +1,75 @@
<script lang="ts"> <script lang="ts">
import EditLayerState from "./EditLayerState"; import EditLayerState, { LayerStateSender } from "./EditLayerState";
import layerSchemaRaw from "../../assets/schemas/layerconfigmeta.json" import layerSchemaRaw from "../../assets/schemas/layerconfigmeta.json";
import Region from "./Region.svelte"; import Region from "./Region.svelte";
import TabbedGroup from "../Base/TabbedGroup.svelte"; import TabbedGroup from "../Base/TabbedGroup.svelte";
import {UIEventSource} from "../../Logic/UIEventSource"; import { Store, UIEventSource } from "../../Logic/UIEventSource";
import type {ConfigMeta} from "./configMeta"; import type { ConfigMeta } from "./configMeta";
import {Utils} from "../../Utils"; import { Utils } from "../../Utils";
import drinking_water from "../../../assets/layers/drinking_water/drinking_water.json" const layerSchema: ConfigMeta[] = <any>layerSchemaRaw;
let state = new EditLayerState(layerSchema);
state.configuration.setData({});
const configuration = state.configuration;
new LayerStateSender("http://localhost:1235", state);
/**
* Blacklist of regions for the general area tab
* These are regions which are handled by a different tab
*/
const regionBlacklist = ["hidden", undefined, "infobox", "tagrenderings", "maprendering", "editing", "title"];
const allNames = Utils.Dedup(layerSchema.map(meta => meta.hints.group));
const layerSchema: ConfigMeta[] = <any> layerSchemaRaw const perRegion: Record<string, ConfigMeta[]> = {};
let state = new EditLayerState(layerSchema) for (const region of allNames) {
state.configuration.setData(drinking_water) perRegion[region] = layerSchema.filter(meta => meta.hints.group === region);
/** }
* Blacklist for the general area tab
*/
const regionBlacklist = ["hidden",undefined,"infobox", "tagrenderings","maprendering", "editing", "title"]
const allNames = Utils.Dedup(layerSchema.map(meta => meta.hints.group))
const perRegion: Record<string, ConfigMeta[]> = {} const baselayerRegions: string[] = ["Basic", "presets", "filters", "advanced", "expert"];
for (const region of allNames) { for (const baselayerRegion of baselayerRegions) {
perRegion[region] = layerSchema.filter(meta => meta.hints.group === region) if (perRegion[baselayerRegion] === undefined) {
console.error("BaseLayerRegions in editLayer: no items have group '" + baselayerRegion + "\"");
} }
}
const baselayerRegions: string[] = ["Basic", "presets","filters","advanced","expert"] const leftoverRegions: string[] = allNames.filter(r => regionBlacklist.indexOf(r) < 0 && baselayerRegions.indexOf(r) < 0);
for (const baselayerRegion of baselayerRegions) { const title: Store<string> = state.getStoreFor(["id"]);
if(perRegion[baselayerRegion] === undefined){
console.error("BaseLayerRegions in editLayer: no items have group '"+baselayerRegion+'"')
}
}
const leftoverRegions : string[] = allNames.filter(r => regionBlacklist.indexOf(r) <0 && baselayerRegions.indexOf(r) <0 )
</script> </script>
<h3>Edit layer</h3> <h3>Editing layer {$title}</h3>
<h4>Leftover regions</h4>
{leftoverRegions.join("; ")}
<div class="m4"> <div class="m4">
{allNames} <TabbedGroup tab={new UIEventSource(2)}>
<TabbedGroup tab={new UIEventSource(1)}>
<div slot="title0">General properties</div> <div slot="title0">General properties</div>
<div class="flex flex-col" slot="content0"> <div class="flex flex-col" slot="content0">
{#each baselayerRegions as region} {#each baselayerRegions as region}
<Region {state} configs={perRegion[region]} title={region}/> <Region {state} configs={perRegion[region]} title={region} />
{/each} {/each}
{#each leftoverRegions as region}
{#each leftoverRegions as region} <Region {state} configs={perRegion[region]} title={region} />
<Region {state} configs={perRegion[region]} title={region}/> {/each}
{/each}
</div> </div>
<div slot="title1">Information panel (questions and answers)</div> <div slot="title1">Information panel (questions and answers)</div>
<div slot="content1"> <div slot="content1">
<Region {state} configs={perRegion["tagrenderings"]} title="Popup contents"> <Region configs={perRegion["title"]} {state} title="Popup title" />
<div slot="description"> <Region configs={perRegion["tagrenderings"]} {state} title="Popup contents"/>
The bulk of the popup content <Region configs={perRegion["editing"]} {state} title="Other editing elements" />
</div>
</Region>
<Region {state} configs={perRegion["title"]} title="Popup title"/>
<Region {state} configs={perRegion["editing"]} title="Other editing elements"/>
</div> </div>
<div slot="title2">Rendering on the map</div> <div slot="title2">Rendering on the map</div>
<div slot="content2"> <div slot="content2">
TODO: rendering on the map <Region configs={perRegion["maprendering"]} {state} />
</div> </div>
</TabbedGroup> <div slot="title3">Configuration file</div>
<div slot="content3">
<div>
Below, you'll find the raw configuration file in `.json`-format.
This is mostly for debugging purposes
</div>
<div class="literal-code">
{JSON.stringify($configuration, null, " ")}
</div>
</div>
</TabbedGroup>
</div> </div>

View file

@ -2,6 +2,30 @@ import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { ConfigMeta } from "./configMeta" import { ConfigMeta } from "./configMeta"
import { Store, UIEventSource } from "../../Logic/UIEventSource" import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
import { QueryParameters } from "../../Logic/Web/QueryParameters"
/**
* Sends changes back to the server
*/
export class LayerStateSender {
constructor(serverLocation: string, layerState: EditLayerState) {
layerState.configuration.addCallback(async (config) => {
// console.log("Current config is", Utils.Clone(config))
const id = config.id
if (id === undefined) {
console.log("No id found in layer, not updating")
return
}
const response = await fetch(`${serverLocation}/layers/${id}/${id}.json`, {
method: "POST",
headers: {
"Content-Type": "application/json;charset=utf-8",
},
body: JSON.stringify(config, null, " "),
})
})
}
}
export default class EditLayerState { export default class EditLayerState {
public readonly osmConnection: OsmConnection public readonly osmConnection: OsmConnection
@ -15,13 +39,17 @@ export default class EditLayerState {
constructor(schema: ConfigMeta[]) { constructor(schema: ConfigMeta[]) {
this.schema = schema this.schema = schema
this.osmConnection = new OsmConnection({}) this.osmConnection = new OsmConnection({
oauth_token: QueryParameters.GetQueryParameter(
"oauth_token",
undefined,
"Used to complete the login"
),
})
this.featureSwitches = { this.featureSwitches = {
featureSwitchIsDebugging: new UIEventSource<boolean>(true), featureSwitchIsDebugging: new UIEventSource<boolean>(true),
} }
this.configuration.addCallback((config) => { console.log("Configuration store:", this.configuration)
// console.log("Current config is", Utils.Clone(config))
})
} }
public getCurrentValueFor(path: ReadonlyArray<string | number>): any | undefined { public getCurrentValueFor(path: ReadonlyArray<string | number>): any | undefined {
@ -78,12 +106,17 @@ export default class EditLayerState {
description: origConfig.description ?? "A translatable object", description: origConfig.description ?? "A translatable object",
} }
} }
public getSchema(path: string[]): ConfigMeta[] { public getSchema(path: string[]): ConfigMeta[] {
return this.schema.filter( const schemas = this.schema.filter(
(sch) => (sch) =>
sch !== undefined && sch !== undefined &&
!path.some((part, i) => !(sch.path.length == path.length && sch.path[i] === part)) !path.some((part, i) => !(sch.path.length == path.length && sch.path[i] === part))
) )
if (schemas.length == 0) {
console.warn("No schemas found for path", path.join("."))
}
return schemas
} }
public setValueAt(path: ReadonlyArray<string | number>, v: any) { public setValueAt(path: ReadonlyArray<string | number>, v: any) {

View file

@ -13,7 +13,11 @@ export let title: string | undefined = undefined;
export let path: (string | number)[] = []; export let path: (string | number)[] = [];
</script> </script>
{#if title} {#if configs === undefined}
Bug: 'Region' received 'undefined'
{:else if configs.length === 0}
Bug: Region received empty list as configuration
{:else if title}
<div class="w-full flex flex-col"> <div class="w-full flex flex-col">
<h3>{title}</h3> <h3>{title}</h3>
<div class="pl-2 border border-black flex flex-col gap-y-1 w-full"> <div class="pl-2 border border-black flex flex-col gap-y-1 w-full">
@ -24,6 +28,9 @@ export let path: (string | number)[] = [];
</div> </div>
</div> </div>
{:else} {:else}
<div class="literal-code">
{JSON.stringify(configs, null, " ")}
</div>
<div class="pl-2 flex flex-col gap-y-1 w-full"> <div class="pl-2 flex flex-col gap-y-1 w-full">
{#each configs as config} {#each configs as config}
<SchemaBasedInput {state} path={path.concat(config.path)} schema={config} /> <SchemaBasedInput {state} path={path.concat(config.path)} schema={config} />

View file

@ -121,7 +121,7 @@
<div class="flex"> <div class="flex">
<button on:click={() => createItem()}>Add {article} {singular}</button> <button on:click={() => createItem()}>Add {article} {singular}</button>
{#if path.length === 1 && path[0] === "tagRenderings"} {#if path.length === 1 && path[0] === "tagRenderings"}
<button on:click={() => {createItem();}}>Add a builtin tagRendering</button> <button on:click={() => {createItem("images");}}>Add a builtin tagRendering</button>
{/if} {/if}
<slot name="extra-button" /> <slot name="extra-button" />
</div> </div>

View file

@ -11,21 +11,21 @@
import EditLayerState from "./EditLayerState"; import EditLayerState from "./EditLayerState";
import { onDestroy } from "svelte"; import { onDestroy } from "svelte";
import type { JsonSchemaType } from "./jsonSchema"; import type { JsonSchemaType } from "./jsonSchema";
import { ConfigMetaUtils } from "./configMeta.ts"
export let state: EditLayerState export let state: EditLayerState
export let path: (string | number)[] = [] export let path: (string | number)[] = []
export let schema: ConfigMeta export let schema: ConfigMeta
let value = new UIEventSource<string>(undefined) let value = new UIEventSource<string>(undefined)
const isTranslation = schema.hints.typehint === "translation" || schema.hints.typehint === "rendered" || ConfigMetaUtils.isTranslation(schema)
let type = schema.hints.typehint ?? "string" let type = schema.hints.typehint ?? "string"
if(type === "rendered"){ if(isTranslation){
type = "translation" type = "translation"
} }
if(type.endsWith("[]")){ if(type.endsWith("[]")){
type = type.substring(0, type.length - 2) type = type.substring(0, type.length - 2)
} }
const isTranslation =schema.hints.typehint === "translation" || schema.hints.typehint === "rendered"
const configJson: QuestionableTagRenderingConfigJson = { const configJson: QuestionableTagRenderingConfigJson = {
id: path.join("_"), id: path.join("_"),

View file

@ -5,7 +5,7 @@
import SchemaBasedArray from "./SchemaBasedArray.svelte"; import SchemaBasedArray from "./SchemaBasedArray.svelte";
import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte"; import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte";
import SchemaBasedTranslationInput from "./SchemaBasedTranslationInput.svelte"; import SchemaBasedTranslationInput from "./SchemaBasedTranslationInput.svelte";
import { ConfigMetaUtils } from "./configMeta.ts"
export let schema: ConfigMeta; export let schema: ConfigMeta;
export let state: EditLayerState; export let state: EditLayerState;
export let path: (string | number)[] = []; export let path: (string | number)[] = [];
@ -16,8 +16,6 @@
<SchemaBasedArray {path} {state} {schema} /> <SchemaBasedArray {path} {state} {schema} />
{:else if schema.type === "array"} {:else if schema.type === "array"}
<SchemaBasedArray {path} {state} {schema} /> <SchemaBasedArray {path} {state} {schema} />
{:else if schema.type === "translation"}
<SchemaBasedTranslationInput {path} {state} {schema} />
{:else if schema.hints.types} {:else if schema.hints.types}
<SchemaBasedMultiType {path} {state} {schema} /> <SchemaBasedMultiType {path} {state} {schema} />
{:else} {:else}

View file

@ -45,12 +45,12 @@
configJson.mappings.unshift( configJson.mappings.unshift(
{ {
if: "direct=true", if: "direct=true",
then: "Yes " + (schema.hints.iftrue ?? ""), then: (schema.hints.iftrue ?? "Yes"),
addExtraTags: ["value="] addExtraTags: ["value="]
}, },
{ {
if: "direct=false", if: "direct=false",
then: "No " + (schema.hints.iffalse ?? ""), then: (schema.hints.iffalse ?? "No"),
addExtraTags: ["value="] addExtraTags: ["value="]
} }
); );
@ -95,7 +95,6 @@
} }
possibleTypes.sort((a, b) => b.optionalMatches - a.optionalMatches); possibleTypes.sort((a, b) => b.optionalMatches - a.optionalMatches);
possibleTypes.sort((a, b) => b.matchingPropertiesCount - a.matchingPropertiesCount); possibleTypes.sort((a, b) => b.matchingPropertiesCount - a.matchingPropertiesCount);
console.log(">>> possible types", possibleTypes)
if (possibleTypes.length > 0) { if (possibleTypes.length > 0) {
tags.setData({ value: "" + possibleTypes[0].index }); tags.setData({ value: "" + possibleTypes[0].index });
} }
@ -122,13 +121,19 @@
onDestroy(tags.addCallbackAndRun(tags => { onDestroy(tags.addCallbackAndRun(tags => {
const oldOption = chosenOption; const oldOption = chosenOption;
chosenOption = tags["value"] ? Number(tags["value"]) : defaultOption; chosenOption = tags["value"] ? Number(tags["value"]) : defaultOption;
const type = schema.type[chosenOption];
console.log("Subtype is", type, {chosenOption, oldOption, schema});
if (chosenOption !== oldOption) { if (chosenOption !== oldOption) {
// Reset the values beneath // Reset the values beneath
subSchemas = []; subSchemas = [];
state.setValueAt(path, undefined); const o = state.getCurrentValueFor(path) ?? {}
console.log({o})
for(const key of type?.required ?? []){
console.log(key)
o[key] ??= {}
}
state.setValueAt(path, o);
} }
const type = schema.type[chosenOption];
console.log("Subtype is", type);
if (!type) { if (!type) {
return; return;
} }
@ -148,6 +153,7 @@
for (const crumble of Object.keys(type.properties)) { for (const crumble of Object.keys(type.properties)) {
subSchemas.push(...(state.getSchema([...cleanPath, crumble]))); subSchemas.push(...(state.getSchema([...cleanPath, crumble])));
} }
console.log("Got subschemas for", path, ":", subSchemas)
})); }));

View file

@ -22,3 +22,22 @@ export interface ConfigMeta {
required: boolean required: boolean
description: string description: string
} }
export class ConfigMetaUtils {
static isTranslation(configMeta: ConfigMeta) {
/* {
"$ref": "#/definitions/Record<string,string>"
},
{
"type": "string"
}*/
if (!configMeta.type) {
return false
}
if (Array.isArray(configMeta.type)) {
return configMeta.type.some((t) => t["$ref"] === "#/definitions/Record<string,string>")
} else {
return configMeta.type["$ref"] === "#/definitions/Record<string,string>"
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -601,7 +601,8 @@
"required": false, "required": false,
"hints": { "hints": {
"question": "Show the freeform as box within the question?", "question": "Show the freeform as box within the question?",
"ifunset": "do not show the" "iftrue": "show the freeform input field as a small field within the question",
"ifunset": "show the freeform input field full-width"
}, },
"type": "boolean", "type": "boolean",
"description": "Instead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout." "description": "Instead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout."
@ -661,7 +662,7 @@
"required": false, "required": false,
"hints": {}, "hints": {},
"type": "array", "type": "array",
"description": "A list of labels. These are strings that are used for various purposes, e.g. to filter them away" "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer"
}, },
{ {
"path": [ "path": [

View file

@ -126,6 +126,72 @@
], ],
"description": "\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```" "description": "\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```"
}, },
{
"path": [
"condition",
"and"
],
"required": false,
"hints": {
"typehint": "tag"
},
"type": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
},
{
"type": "object",
"properties": {
"or": {
"type": "array",
"items": {
"$ref": "#/definitions/TagConfigJson"
}
}
},
"required": [
"or"
]
},
{
"type": "string"
}
],
"description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n"
},
{
"path": [
"condition",
"or"
],
"required": false,
"hints": {
"typehint": "tag"
},
"type": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
},
{
"type": "object",
"properties": {
"or": {
"type": "array",
"items": {
"$ref": "#/definitions/TagConfigJson"
}
}
},
"required": [
"or"
]
},
{
"type": "string"
}
],
"description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n"
},
{ {
"path": [ "path": [
"metacondition" "metacondition"
@ -147,6 +213,72 @@
], ],
"description": "\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_" "description": "\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
}, },
{
"path": [
"metacondition",
"and"
],
"required": false,
"hints": {
"typehint": "tag"
},
"type": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
},
{
"type": "object",
"properties": {
"or": {
"type": "array",
"items": {
"$ref": "#/definitions/TagConfigJson"
}
}
},
"required": [
"or"
]
},
{
"type": "string"
}
],
"description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n"
},
{
"path": [
"metacondition",
"or"
],
"required": false,
"hints": {
"typehint": "tag"
},
"type": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
},
{
"type": "object",
"properties": {
"or": {
"type": "array",
"items": {
"$ref": "#/definitions/TagConfigJson"
}
}
},
"required": [
"or"
]
},
{
"type": "string"
}
],
"description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n"
},
{ {
"path": [ "path": [
"freeform" "freeform"
@ -178,6 +310,40 @@
"type": "array", "type": "array",
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes" "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes"
}, },
{
"path": [
"mappings",
"if"
],
"required": true,
"hints": {
"typehint": "tag",
"question": "When should this single mapping match?"
},
"type": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
},
{
"type": "object",
"properties": {
"or": {
"type": "array",
"items": {
"$ref": "#/definitions/TagConfigJson"
}
}
},
"required": [
"or"
]
},
{
"type": "string"
}
],
"description": "\nIf this condition is met, then the text under `then` will be shown.\nIf no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM.\n\nFor example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'}\n\nThis can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'}"
},
{ {
"path": [ "path": [
"mappings", "mappings",

View file

@ -1,19 +1,16 @@
import LayoutConfig from "./Models/ThemeConfig/LayoutConfig" import { Utils } from "./Utils"
import * as theme from "./assets/generated/themes/bookcases.json"
import ThemeViewState from "./Models/ThemeViewState"
import Combine from "./UI/Base/Combine"
import SpecialVisualizations from "./UI/SpecialVisualizations"
function testspecial() { class Test {
const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ? : new AllKnownLayoutsLazy().get(qp.data) public async test() {
const state = new ThemeViewState(layout) await Utils.waitFor(0)
const response = await fetch("http://localhost:1235/layers/atm/atm.json", {
const all = SpecialVisualizations.specialVisualizations.map((s) => method: "POST",
SpecialVisualizations.renderExampleOfSpecial(state, s) headers: {
) "Content-Type": "application/json;charset=utf-8",
new Combine(all).AttachTo("maindiv") },
body: JSON.stringify({}),
})
}
} }
/*/ new Test().test()
testspecial()
//*/