Studio: first working version for basic layer properties

This commit is contained in:
Pieter Vander Vennet 2023-06-23 16:14:43 +02:00
parent 5a49677d5a
commit f18a2b9184
41 changed files with 4446 additions and 852 deletions

View file

@ -39,8 +39,9 @@ export class ExtractImages extends Conversion<
return (
metapath.type?.some(
(t) =>
t["$ref"] == "#/definitions/TagRenderingConfigJson" ||
t["$ref"] == "#/definitions/QuestionableTagRenderingConfigJson"
t !== null &&
(t["$ref"] == "#/definitions/TagRenderingConfigJson" ||
t["$ref"] == "#/definitions/QuestionableTagRenderingConfigJson")
) ?? false
)
}

View file

@ -52,6 +52,10 @@ export class UpdateLegacyLayer extends DesugaringStep<
delete preset["preciseInput"]
}
}
if (typeof preset.snapToLayer === "string") {
preset.snapToLayer = [preset.snapToLayer]
}
}
if (config.tagRenderings !== undefined) {

View file

@ -1,6 +1,15 @@
import { TagConfigJson } from "./TagConfigJson"
export interface DeleteConfigJson {
/***
* By default, the contributor needs 20 previous changesets to delete points edited by others.
* For some small features (e.g. bicycle racks) this is too much and this requirement can be lowered or dropped, which can be done here.
*
* type: nat
* question: How many changesets must a contributor have before being allowed to delete a point?
*/
neededChangesets?: number
/***
* By default, three reasons to delete a point are shown:
*
@ -18,12 +27,17 @@ export interface DeleteConfigJson {
*/
extraDeleteReasons?: {
/**
* The text that will be shown to the user - translatable
* The text that will be shown to the user as option for why this point does not exist anymore.
* Note that the most common reasons (test point, does not exist anymore, cannot be found) are already offered by default
*
* question: For what extra reason might this feature be removed in real-life?
*/
explanation: string | any
/**
* The text that will be uploaded into the changeset or will be used in the fixme in case of a soft deletion
* Should be a few words, in english
*
* question: What should be added to the changeset as delete reason?
*/
changesetMessage: string
}[]
@ -39,10 +53,14 @@ export interface DeleteConfigJson {
/**
* The tags that will be given to the object.
* This must remove tags so that the 'source/osmTags' won't match anymore
*
* question: What tags should be applied to the object?
*/
if: TagConfigJson
/**
* The human explanation for the options
*
* question: What text should be shown to the contributor for this reason?
*/
then: string | any
}[]
@ -53,6 +71,7 @@ export interface DeleteConfigJson {
* It is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!
*
* Example (note that "amenity=" erases the 'amenity'-key alltogether):
*
* ```
* {
* "and": ["disussed:amenity=public_bookcase", "amenity="]
@ -60,22 +79,26 @@ export interface DeleteConfigJson {
* ```
*
* or (notice the use of the ':='-tag to copy the old value of 'shop=*' into 'disused:shop='):
*
* ```
* {
* "and": ["disused:shop:={shop}", "shop="]
* }
* ```
*
* question: If a hard delete is not possible, what tags should be applied to mark this feature as deleted?
* type: tag
*/
softDeletionTags?: TagConfigJson
/***
* By default, the contributor needs 20 previous changesets to delete points edited by others.
* For some small features (e.g. bicycle racks) this is too much and this requirement can be lowered or dropped, which can be done here.
*/
neededChangesets?: number
/**
* Set this flag if the default delete reasons should be omitted from the dialog.
* This requires at least one extraDeleteReason or nonDeleteMapping
*
* question: Should the default delete reasons be hidden?
* iftrue: Hide the default delete reasons
* iffalse: Show the default delete reasons
* ifunset: Show the default delete reasons (default behaviour)
*/
omitDefaultDeleteReasons?: false | boolean
}

View file

@ -17,7 +17,7 @@ export interface LayerConfigJson {
* The id of this layer.
* This should be a simple, lowercase, human readable string that is used to identify the layer.
*
* group: basic
* group: Basic
* question: What is the identifier of this layer?
*/
id: string
@ -27,7 +27,7 @@ export interface LayerConfigJson {
*
* ifunset: This will hide the layer in the layer control, making it not filterable and not toggleable
*
* group: basic
* group: Basic
* question: What is the name of this layer?
*/
name?: string | Record<string, string>
@ -36,7 +36,7 @@ export interface LayerConfigJson {
* A description for the features shown in this layer.
* This often resembles the introduction of the wiki.osm.org-page for this feature.
*
* group: basic
* group: Basic
* question: How would you describe the features that are shown on this layer?
*/
description?: string | Record<string, string>
@ -54,7 +54,8 @@ export interface LayerConfigJson {
* Note: a source must always be defined. 'special' is only allowed if this is a builtin-layer
*
* types: Load data with specific tags from OpenStreetMap ; Load data from an external geojson source ;
* group: basic
* typesdefault: 0
* group: Basic
*/
source:
| "special"
@ -71,6 +72,7 @@ export interface LayerConfigJson {
* The maximum amount of seconds that a tile is allowed to linger in the cache
*
* type: nat
* default: 30 days
*/
maxCacheAge?: number
}
@ -128,45 +130,31 @@ export interface LayerConfigJson {
* "_some_key:=some_javascript_expression"
* ]
*
* group: advanced
* See the full documentation on [https://github.com/pietervdvn/MapComplete/blob/master/Docs/CalculatedTags.md]
*
* group: expert
* question: What extra attributes should be calculated with javascript?
*
*/
calculatedTags?: string[]
/**
* If set, this layer will not query overpass; but it'll still match the tags above which are by chance returned by other layers.
* Works well together with 'passAllFeatures', to add decoration
*
* group: advanced
*/
doNotDownload?: boolean
/**
* If set, only features matching this extra tag will be shown.
* This is useful to hide certain features from view.
*
* The default value is 'yes'
* This is useful to hide certain features from view based on a calculated tag or if the features are provided by a different layer.
*
* question: What other tags should features match for being shown?
* group: advanced
* ifunset: all features which match the 'source'-specification are shown.
*/
isShown?: TagConfigJson
/**
* Advanced option - might be set by the theme compiler
*
* If true, this data will _always_ be loaded, even if the theme is disabled
*
* group: advanced
*/
forceLoad?: false | boolean
/**
* The minimum needed zoomlevel required to start loading and displaying the data.
* This can be used to only show common features (e.g. a bicycle parking) only when the map is zoomed in very much (17).
* This prevents cluttering the map with thousands of parkings if one is looking to an entire city.
*
* Default: 0
* group: basic
* group: Basic
* type: nat
* question: At what zoom level should features of the layer be shown?
* ifunset: Always load this layer, even if the entire world is in view.
@ -175,8 +163,12 @@ export interface LayerConfigJson {
/**
* Indicates if this layer is shown by default;
* can be used to hide a layer from start, or to load the layer but only to show it where appropriate (e.g. for snapping to it)
* can be used to hide a layer from start, or to load the layer but only to show it when appropriate (e.g. for advanced users)
*
* question: Should this layer be enabled when opening the map for the first time?
* iftrue: the layer is enabled when opening the map
* iffalse: the layer is hidden until the contributor enables it. (If the filter-popup is disabled, this might never get enabled nor loaded)
* default: true
* group: advanced
*/
shownByDefault?: true | boolean
@ -185,7 +177,7 @@ export interface LayerConfigJson {
* The zoom level at which point the data is hidden again
* Default: 100 (thus: always visible
*
* group: advanced
* group: expert
*/
minzoomVisible?: number
@ -196,6 +188,19 @@ export interface LayerConfigJson {
*/
title?: string | TagRenderingConfigJson
/**
*
* Question: Should the information for this layer be shown in the sidebar or in a splash screen?
*
* If set, open the selectedElementView in a floatOver instead of on the right.
*
* iftrue: show the infobox in the splashscreen floating over the entire UI
* iffalse: show the infobox in a sidebar on the right
* group: advanced
* default: sidebar
*/
popupInFloatover?: boolean
/**
* Small icons shown next to the title.
* If not specified, the OsmLink and wikipedia links will be used by default.
@ -228,11 +233,40 @@ export interface LayerConfigJson {
/**
* If set, this layer will pass all the features it receives onto the next layer.
* This is ideal for decoration, e.g. directions on cameras
*
* group: advanced
* iftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers
* iffalse: normal behaviour: don't pass features allong
* question: should this layer pass features to the next layers?
* group: expert
*/
passAllFeatures?: boolean
/**
* If set, this layer will not query overpass; but it'll still match the tags above which are by chance returned by other layers.
* Works well together with 'passAllFeatures', to add decoration
* The opposite of `forceLoad`
*
* iftrue: Do not attempt to query the data for this layer from overpass/the OSM API
* iffalse: download the data as usual
* group: expert
* question: Should this layer be downloaded or is the data provided by a different layer (which has 'passAllFeatures'-set)?
* default: false
*/
doNotDownload?: boolean
/**
* Advanced option - might be set by the theme compiler
*
* If true, this data will _always_ be loaded, even if the theme is disabled by a filter or hidden.
* The opposite of `doNotDownload`
*
* question: Should this layer be forcibly loaded?
* ifftrue: always download this layer, even if it is disabled
* iffalse: only download data for this layer when needed (default)
* default: false
* group: expert
*/
forceLoad?: false | boolean
/**
* Presets for this layer.
* A preset shows up when clicking the map on a without data (or when right-clicking/long-pressing);
@ -246,7 +280,7 @@ export interface LayerConfigJson {
* Note: the icon of the preset is determined automatically based on the tags and the icon above. Don't worry about that!
* NB: if no presets are defined, the popup to add new points doesn't show up at all
*
* group: basic
* group: presets
*/
presets?: {
/**
@ -290,9 +324,16 @@ export interface LayerConfigJson {
exampleImages?: string[]
/**
* If specified, these layers will be shown to and the new point will be snapped towards it
* question: Should the created point be snapped to a line layer?
*
* If specified, these layers will be shown in the precise location picker and the new point will be snapped towards it.
* For example, this can be used to snap against `walls_and_buildings` (e.g. to attach a defibrillator, an entrance, an artwork, ... to the wall)
* or to snap an obstacle (such as a bollard) to the `cycleways_and_roads`.
*
* suggestions: return Array.from(layers.keys()).map(key => ({if: "value="+key, then: key+" - "+layers.get(key).description}))
*/
snapToLayer?: string | string[]
snapToLayer?: string[]
/**
* If specified, a new point will only be snapped if it is within this range.
* Distance in meter
@ -362,62 +403,70 @@ export interface LayerConfigJson {
* - undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future
* - or: a hash with options (see below)
*
* The delete dialog
* =================
* ### The delete dialog
*
*
*
#### Hard deletion if enough experience
* #### Hard deletion if enough experience
A feature can only be deleted from OpenStreetMap by mapcomplete if:
* A feature can only be deleted from OpenStreetMap by mapcomplete if:
- It is a node
- No ways or relations use the node
- The logged-in user has enough experience OR the user is the only one to have edited the point previously
- The logged-in user has no unread messages (or has a ton of experience)
- The user did not select one of the 'non-delete-options' (see below)
In all other cases, a 'soft deletion' is used.
#### Soft deletion
A 'soft deletion' is when the point isn't deleted from OSM but retagged so that it'll won't how up in the mapcomplete theme anymore.
This makes it look like it was deleted, without doing damage. A fixme will be added to the point.
Note that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme
#### No-delete options
In 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").
However, 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!)
The 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.
A no-delete option is offered as 'reason to delete it', but secretly retags.
group: editing
*/
deletion?: boolean | DeleteConfigJson
* - It is a node
* - No ways or relations use the node
* - The logged-in user has enough experience OR the user is the only one to have edited the point previously
* - The logged-in user has no unread messages (or has a ton of experience)
* - The user did not select one of the 'non-delete-options' (see below)
*
* In all other cases, a 'soft deletion' is used.
*
* #### Soft deletion
*
* A '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.
* This makes it look like it was deleted, without doing damage. A fixme will be added to the point.
*
* Note that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme
*
* ##### No-delete options
*
* In 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").
* However, 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!)
*
* The 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.
* A no-delete option is offered as 'reason to delete it', but secretly retags.
*
* group: editing
* types: use an advanced delete configuration ; boolean
* iftrue: Allow deletion
* iffalse: Do not allow deletion
*
**/
deletion?: DeleteConfigJson | boolean
/**
* Indicates if a point can be moved and configures the modalities.
* Indicates if a point can be moved and why.
*
* A feature can be moved by MapComplete if:
*
* - It is a point
* - The point is _not_ part of a way or a a relation.
*
* Off by default. Can be enabled by setting this flag or by configuring.
*
* types: use an advanced move configuration ; boolean
* group: editing
* question: Is deleting a point allowed?
* iftrue: Allow contributors to move a point (for accuracy or a relocation)
* iffalse: Don't allow contributors to move points
* ifunset: Don't allow contributors to move points (default)
*/
allowMove?: boolean | MoveConfigJson
allowMove?: MoveConfigJson | boolean
/**
* If set, a 'split this way' button is shown on objects rendered as LineStrings, e.g. highways.
*
* If the way is part of a relation, MapComplete will attempt to update this relation as well
*
* question: Should the contributor be able to split ways using this layer?
* iftrue: enable the 'split-roads'-component
* iffalse: don't enable the split-roads componenet
* ifunset: don't enable the split-roads component
* group: editing
*/
allowSplit?: boolean
@ -432,12 +481,9 @@ export interface LayerConfigJson {
/**
* If set, synchronizes whether or not this layer is enabled.
*
* no: Do not sync at all, always revert to default
* local: keep selection on local storage
* theme-only: sync via OSM, but this layer will only be toggled in this theme
* global: all layers with this ID will be synced accross all themes
*
* group: advanced
* question: Should enabling/disabling the layer be saved (locally or in the cloud)?
* suggestions: return [{if: "value=no", then: "Don't save, always revert to the default"}, {if: "value=local", then: "Save selection in local storage"}, {if: "value=theme-only", then: "Save the state in the OSM-usersettings, but apply on this theme only (default)"}, {if: "value=global", then: "Save in OSM-usersettings and toggle on _all_ themes using this layer"}]
*/
syncSelection?: "no" | "local" | "theme-only" | "global"
@ -446,17 +492,10 @@ export interface LayerConfigJson {
*
* no-question-hint-check: disables a check in MiscTagRenderingChecks which complains about 'div', 'span' or 'class=subtle'-HTML elements in the tagRendering
*
* group: special
* group: hidden
*/
"#"?: string | "no-question-hint-check"
/**
* If set, open the selectedElementView in a floatOver instead of on the right
*
* group: advanced
*/
popupInFloatover?: boolean
/**
* _Set automatically by MapComplete, please ignore_
*

View file

@ -109,7 +109,7 @@ export interface MappingConfigJson {
* {
* "if": "crossing:marking=rainbow",
* "then": "This is a rainbow crossing",
* "addExtraTags": "not:crossing:marking="
* "addExtraTags": ["not:crossing:marking="]
* }
* ```
*

View file

@ -238,13 +238,7 @@ export default class LayerConfig extends WithContextLoader {
throw "Layer " + this.id + " still uses the old 'preciseInput'-field"
}
if (pr.snapToLayer !== undefined) {
let snapToLayers: string[]
if (typeof pr.snapToLayer === "string") {
snapToLayers = [pr.snapToLayer]
} else {
snapToLayers = pr.snapToLayer
}
let snapToLayers = pr.snapToLayer
preciseInput = {
snapToLayers,
maxSnapDistance: pr.maxSnapDistance ?? 10,