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": {
"osmTags": {
"$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": {
"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": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering",
"anyOf": [
{
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
"description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
},
{
"type": "null"
}
]
]
}
},
"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",
@ -352,7 +345,7 @@
]
},
"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": [
{
"$ref": "#/definitions/DeleteConfigJson"
@ -404,7 +397,6 @@
},
"required": [
"id",
"mapRendering",
"source"
],
"definitions": {
@ -1179,7 +1171,7 @@
}
},
"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"
},
"default": {
@ -1214,7 +1206,7 @@
]
},
"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",
"items": {
"type": "string"
@ -1375,7 +1367,7 @@
}
},
"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"
},
"default": {
@ -1410,7 +1402,7 @@
]
},
"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",
"items": {
"type": "string"

View file

@ -36,7 +36,7 @@ export default {
"properties": {
"osmTags": {
"$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": {
"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": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering",
"anyOf": [
{
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
"description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
},
{
"type": "null"
}
]
]
}
},
"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",
@ -352,7 +345,7 @@ export default {
]
},
"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": [
{
"$ref": "#/definitions/DeleteConfigJson"
@ -404,7 +397,6 @@ export default {
},
"required": [
"id",
"mapRendering",
"source"
],
"definitions": {
@ -1167,7 +1159,7 @@ export default {
}
},
"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"
},
"default": {
@ -1202,7 +1194,7 @@ export default {
]
},
"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",
"items": {
"type": "string"
@ -1362,7 +1354,7 @@ export default {
}
},
"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"
},
"default": {
@ -1397,7 +1389,7 @@ export default {
]
},
"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",
"items": {
"type": "string"

View file

@ -1076,7 +1076,7 @@
}
},
"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"
},
"default": {
@ -1111,7 +1111,7 @@
]
},
"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",
"items": {
"type": "string"
@ -1272,7 +1272,7 @@
}
},
"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"
},
"default": {
@ -1307,7 +1307,7 @@
]
},
"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",
"items": {
"type": "string"
@ -1767,7 +1767,7 @@
"properties": {
"osmTags": {
"$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": {
"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": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering",
"anyOf": [
{
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
"description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
},
{
"type": "null"
}
]
]
}
},
"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",
@ -2083,7 +2076,7 @@
]
},
"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": [
{
"$ref": "#/definitions/DeleteConfigJson"
@ -2135,7 +2128,6 @@
},
"required": [
"id",
"mapRendering",
"source"
],
"additionalProperties": false
@ -2177,7 +2169,7 @@
"properties": {
"osmTags": {
"$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": {
"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": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering",
"anyOf": [
{
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
"description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
},
{
"type": "null"
}
]
]
}
},
"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",
@ -2493,7 +2478,7 @@
]
},
"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": [
{
"$ref": "#/definitions/DeleteConfigJson"
@ -2579,4 +2564,4 @@
},
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false
}
}

View file

@ -1064,7 +1064,7 @@ export default {
}
},
"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"
},
"default": {
@ -1099,7 +1099,7 @@ export default {
]
},
"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",
"items": {
"type": "string"
@ -1259,7 +1259,7 @@ export default {
}
},
"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"
},
"default": {
@ -1294,7 +1294,7 @@ export default {
]
},
"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",
"items": {
"type": "string"
@ -1746,7 +1746,7 @@ export default {
"properties": {
"osmTags": {
"$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": {
"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": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering",
"anyOf": [
{
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
"description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
},
{
"type": "null"
}
]
]
}
},
"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",
@ -2062,7 +2055,7 @@ export default {
]
},
"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": [
{
"$ref": "#/definitions/DeleteConfigJson"
@ -2114,7 +2107,6 @@ export default {
},
"required": [
"id",
"mapRendering",
"source"
]
},
@ -2155,7 +2147,7 @@ export default {
"properties": {
"osmTags": {
"$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": {
"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": {
"description": "Visualisation of the items on the map\n\ngroup: maprendering",
"anyOf": [
{
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
]
"description": "Visualisation of the items on the map\nSet 'null' explicitly if you do not want a maprendering\ngroup: maprendering",
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/default_4"
},
{
"$ref": "#/definitions/default_5"
},
{
"$ref": "#/definitions/default<default|default|default[]|default[]>"
}
},
{
"type": "null"
}
]
]
}
},
"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",
@ -2471,7 +2456,7 @@ export default {
]
},
"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": [
{
"$ref": "#/definitions/DeleteConfigJson"
@ -2554,4 +2539,4 @@ export default {
}
},
"$schema": "http://json-schema.org/draft-07/schema#"
}
}

View file

@ -44,7 +44,7 @@
}
},
"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"
},
"default": {
@ -79,7 +79,7 @@
]
},
"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",
"items": {
"type": "string"

View file

@ -44,7 +44,7 @@ export default {
}
},
"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"
},
"default": {
@ -79,7 +79,7 @@ export default {
]
},
"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",
"items": {
"type": "string"

View file

@ -200,9 +200,6 @@ function addMetafields(fieldnames: string[], fullSchema: JsonSchema): ConfigMeta
if (schemePart.description === undefined) {
return
}
if (path.length === 2 && path[0] === "mappings" && path[1] === "if") {
console.log("HI")
}
const type = schemePart.items?.anyOf ?? schemePart.type ?? schemePart.anyOf
let description = schemePart.description
@ -291,10 +288,10 @@ function validateMeta(path: ConfigMeta): string | undefined {
return undefined
}
if (path.hints.question === undefined && !Array.isArray(path.type)) {
/* return (
return (
ctx +
" does not have a question set. As such, MapComplete-studio users will not be able to set this property"
)//*/
) //*/
}
return undefined
@ -347,9 +344,9 @@ function main() {
})
}
const errs: string[] = []
// errs = extractMeta("LayerConfigJson", "layerconfigmeta", allDefinitions)
// errs.push(...extractMeta("LayoutConfigJson", "layoutconfigmeta", allDefinitions))
// errs.push(...extractMeta("TagRenderingConfigJson", "tagrenderingconfigmeta", allDefinitions))
errs.push(...extractMeta("LayerConfigJson", "layerconfigmeta", allDefinitions))
// errs.push(...extractMeta("LayoutConfigJson", "layoutconfigmeta", allDefinitions))
errs.push(...extractMeta("TagRenderingConfigJson", "tagrenderingconfigmeta", allDefinitions))
errs.push(
...extractMeta(
"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._tagRenderingsByLabel = new Map<string, TagRenderingConfigJson[]>()
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)
if (withLabel === undefined) {
withLabel = []
@ -193,7 +193,7 @@ class ExpandTagRendering extends Conversion<
for (let foundTr of indirect) {
foundTr = Utils.Clone<any>(foundTr)
Utils.Merge(tagRenderingConfigJson["override"] ?? {}, foundTr)
foundTr.id = tagRenderingConfigJson.id ?? foundTr.id
foundTr["id"] = tagRenderingConfigJson["id"] ?? foundTr["id"]
result.push(foundTr)
}
} else {
@ -239,9 +239,9 @@ class ExpandTagRendering extends Conversion<
matchingTrs = layerTrs
} else if (id.startsWith("*")) {
const id_ = id.substring(1)
matchingTrs = layerTrs.filter((tr) => tr.labels?.indexOf(id_) >= 0)
matchingTrs = layerTrs.filter((tr) => tr["labels"]?.indexOf(id_) >= 0)
} 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:")
@ -489,7 +489,15 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
}
}
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 }
}
}

View file

@ -64,8 +64,7 @@ export interface LayerConfigJson {
| {
/**
* 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
/**
@ -238,21 +237,19 @@ export interface LayerConfigJson {
/**
* Visualisation of the items on the map
*
* Set 'null' explicitly if you do not want a maprendering
* group: maprendering
*/
mapRendering:
| null
| (
| PointRenderingConfigJson
mapRendering?: (
| PointRenderingConfigJson
| LineRenderingConfigJson
| RewritableConfigJson<
| LineRenderingConfigJson
| RewritableConfigJson<
| LineRenderingConfigJson
| PointRenderingConfigJson
| LineRenderingConfigJson[]
| PointRenderingConfigJson[]
>
)[]
| PointRenderingConfigJson
| LineRenderingConfigJson[]
| PointRenderingConfigJson[]
>
)[]
/**
* 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.
*
* group: editing
* types: use an advanced delete configuration ; boolean
* types: Use an advanced delete configuration ; boolean
* iftrue: Allow deletion
* iffalse: Do not allow deletion
*

View file

@ -260,7 +260,7 @@ export interface QuestionableTagRenderingConfigJson extends TagRenderingConfigJs
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[]
}

View file

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

View file

@ -4,30 +4,59 @@
* Note that all values are stringified
*/
import { UIEventSource } from "../../Logic/UIEventSource"
import type { ValidatorType } from "./Validators"
import InputHelpers from "./InputHelpers"
import ToSvelte from "../Base/ToSvelte.svelte"
import type { Feature } from "geojson"
import BaseUIElement from "../BaseUIElement"
import { VariableUiElement } from "../Base/VariableUIElement"
import { UIEventSource } from "../../Logic/UIEventSource";
import type { ValidatorType } from "./Validators";
import InputHelpers from "./InputHelpers";
import ToSvelte from "../Base/ToSvelte.svelte";
import type { Feature } from "geojson";
import BaseUIElement from "../BaseUIElement";
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 value: UIEventSource<string>
export let type: ValidatorType;
export let value: UIEventSource<string>;
export let feature: Feature
export let args: (string | number | boolean)[] = undefined
export let feature: Feature;
export let args: (string | number | boolean)[] = undefined;
let properties = { feature, args: args ?? [] }
let construct = new UIEventSource<(value, extraProperties) => BaseUIElement>(undefined)
let properties = { feature, args: args ?? [] };
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>
{#if construct !== undefined}
<ToSvelte
construct={() =>
{#if type === "translation" }
<TranslationInput {value} on:submit={() => dispatch("submit")} />
{: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)))}
/>
/>
{/if}
{/if}

View file

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

View file

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

View file

@ -66,5 +66,5 @@
/>
{/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>

View file

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

View file

@ -1,69 +1,75 @@
<script lang="ts">
import EditLayerState from "./EditLayerState";
import layerSchemaRaw from "../../assets/schemas/layerconfigmeta.json"
import Region from "./Region.svelte";
import TabbedGroup from "../Base/TabbedGroup.svelte";
import {UIEventSource} from "../../Logic/UIEventSource";
import type {ConfigMeta} from "./configMeta";
import {Utils} from "../../Utils";
import EditLayerState, { LayerStateSender } from "./EditLayerState";
import layerSchemaRaw from "../../assets/schemas/layerconfigmeta.json";
import Region from "./Region.svelte";
import TabbedGroup from "../Base/TabbedGroup.svelte";
import { Store, UIEventSource } from "../../Logic/UIEventSource";
import type { ConfigMeta } from "./configMeta";
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
let state = new EditLayerState(layerSchema)
state.configuration.setData(drinking_water)
/**
* 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[]> = {};
for (const region of allNames) {
perRegion[region] = layerSchema.filter(meta => meta.hints.group === region);
}
const perRegion: Record<string, ConfigMeta[]> = {}
for (const region of allNames) {
perRegion[region] = layerSchema.filter(meta => meta.hints.group === region)
const baselayerRegions: string[] = ["Basic", "presets", "filters", "advanced", "expert"];
for (const baselayerRegion of baselayerRegions) {
if (perRegion[baselayerRegion] === undefined) {
console.error("BaseLayerRegions in editLayer: no items have group '" + baselayerRegion + "\"");
}
const baselayerRegions: string[] = ["Basic", "presets","filters","advanced","expert"]
for (const baselayerRegion of baselayerRegions) {
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 )
}
const leftoverRegions: string[] = allNames.filter(r => regionBlacklist.indexOf(r) < 0 && baselayerRegions.indexOf(r) < 0);
const title: Store<string> = state.getStoreFor(["id"]);
</script>
<h3>Edit layer</h3>
<h3>Editing layer {$title}</h3>
<h4>Leftover regions</h4>
{leftoverRegions.join("; ")}
<div class="m4">
{allNames}
<TabbedGroup tab={new UIEventSource(1)}>
<TabbedGroup tab={new UIEventSource(2)}>
<div slot="title0">General properties</div>
<div class="flex flex-col" slot="content0">
{#each baselayerRegions as region}
<Region {state} configs={perRegion[region]} title={region}/>
{/each}
{#each leftoverRegions as region}
<Region {state} configs={perRegion[region]} title={region}/>
{/each}
{#each baselayerRegions as region}
<Region {state} configs={perRegion[region]} title={region} />
{/each}
{#each leftoverRegions as region}
<Region {state} configs={perRegion[region]} title={region} />
{/each}
</div>
<div slot="title1">Information panel (questions and answers)</div>
<div slot="content1">
<Region {state} configs={perRegion["tagrenderings"]} title="Popup contents">
<div slot="description">
The bulk of the popup content
</div>
</Region>
<Region {state} configs={perRegion["title"]} title="Popup title"/>
<Region {state} configs={perRegion["editing"]} title="Other editing elements"/>
<Region configs={perRegion["title"]} {state} title="Popup title" />
<Region configs={perRegion["tagrenderings"]} {state} title="Popup contents"/>
<Region configs={perRegion["editing"]} {state} title="Other editing elements" />
</div>
<div slot="title2">Rendering on the map</div>
<div slot="content2">
TODO: rendering on the map
<Region configs={perRegion["maprendering"]} {state} />
</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>

View file

@ -2,6 +2,30 @@ import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { ConfigMeta } from "./configMeta"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
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 {
public readonly osmConnection: OsmConnection
@ -15,13 +39,17 @@ export default class EditLayerState {
constructor(schema: ConfigMeta[]) {
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 = {
featureSwitchIsDebugging: new UIEventSource<boolean>(true),
}
this.configuration.addCallback((config) => {
// console.log("Current config is", Utils.Clone(config))
})
console.log("Configuration store:", this.configuration)
}
public getCurrentValueFor(path: ReadonlyArray<string | number>): any | undefined {
@ -78,12 +106,17 @@ export default class EditLayerState {
description: origConfig.description ?? "A translatable object",
}
}
public getSchema(path: string[]): ConfigMeta[] {
return this.schema.filter(
const schemas = this.schema.filter(
(sch) =>
sch !== undefined &&
!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) {

View file

@ -13,7 +13,11 @@ export let title: string | undefined = undefined;
export let path: (string | number)[] = [];
</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">
<h3>{title}</h3>
<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>
{:else}
<div class="literal-code">
{JSON.stringify(configs, null, " ")}
</div>
<div class="pl-2 flex flex-col gap-y-1 w-full">
{#each configs as config}
<SchemaBasedInput {state} path={path.concat(config.path)} schema={config} />

View file

@ -121,7 +121,7 @@
<div class="flex">
<button on:click={() => createItem()}>Add {article} {singular}</button>
{#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}
<slot name="extra-button" />
</div>

View file

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

View file

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

View file

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

View file

@ -22,3 +22,22 @@ export interface ConfigMeta {
required: boolean
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,
"hints": {
"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",
"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,
"hints": {},
"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": [

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```"
},
{
"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": [
"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_"
},
{
"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": [
"freeform"
@ -178,6 +310,40 @@
"type": "array",
"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": [
"mappings",

View file

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