forked from MapComplete/MapComplete
529 lines
22 KiB
TypeScript
529 lines
22 KiB
TypeScript
import { TagConfigJson } from "./TagConfigJson"
|
|
import { TagRenderingConfigJson } from "./TagRenderingConfigJson"
|
|
import FilterConfigJson from "./FilterConfigJson"
|
|
import { DeleteConfigJson } from "./DeleteConfigJson"
|
|
import UnitConfigJson from "./UnitConfigJson"
|
|
import MoveConfigJson from "./MoveConfigJson"
|
|
import PointRenderingConfigJson from "./PointRenderingConfigJson"
|
|
import LineRenderingConfigJson from "./LineRenderingConfigJson"
|
|
import { QuestionableTagRenderingConfigJson } from "./QuestionableTagRenderingConfigJson"
|
|
import RewritableConfigJson from "./RewritableConfigJson"
|
|
|
|
/**
|
|
* Configuration for a single layer
|
|
*/
|
|
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
|
|
* question: What is the identifier of this layer?
|
|
*/
|
|
id: string
|
|
|
|
/**
|
|
* Used in the layer control panel to toggle a layer on and of.
|
|
*
|
|
* ifunset: This will hide the layer in the layer control, making it not filterable and not toggleable
|
|
*
|
|
* group: Basic
|
|
* question: What is the name of this layer?
|
|
*/
|
|
name?: string | Record<string, string>
|
|
|
|
/**
|
|
* 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
|
|
* question: How would you describe the features that are shown on this layer?
|
|
*/
|
|
description?: string | Record<string, string>
|
|
|
|
/**
|
|
*
|
|
* Question: Where should the data be fetched from?
|
|
*
|
|
* This determines where the data for the layer is fetched: from OSM or from an external geojson dataset.
|
|
*
|
|
* If no 'geojson' is defined, data will be fetched from overpass and the OSM-API.
|
|
*
|
|
* Every source _must_ define which tags _must_ be present in order to be picked up.
|
|
*
|
|
* 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 ;
|
|
* typesdefault: 0
|
|
* group: Basic
|
|
*/
|
|
source:
|
|
| "special"
|
|
| "special:library"
|
|
| {
|
|
/**
|
|
* 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.
|
|
*/
|
|
osmTags: TagConfigJson
|
|
/**
|
|
* question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?
|
|
* The maximum amount of seconds that a tile is allowed to linger in the cache
|
|
*
|
|
* type: nat
|
|
* default: 30 days
|
|
*/
|
|
maxCacheAge?: number
|
|
}
|
|
| {
|
|
/**
|
|
* The actual source of the data to load, if loaded via geojson.
|
|
*
|
|
* # A single geojson-file
|
|
* source: {geoJson: "https://my.source.net/some-geo-data.geojson"}
|
|
* fetches a geojson from a third party source
|
|
*
|
|
* # A tiled geojson source
|
|
* source: {geoJson: "https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson", geoJsonZoomLevel: 14}
|
|
* to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer
|
|
*
|
|
* Some API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max}
|
|
*
|
|
* question: What is the URL of the geojson?
|
|
* type: url
|
|
*/
|
|
geoJson: string
|
|
/**
|
|
* To load a tiled geojson layer, set the zoomlevel of the tiles
|
|
*
|
|
* question: If using a tiled geojson, what is the zoomlevel of the tiles?
|
|
* ifunset: This is not a tiled geojson
|
|
*/
|
|
geoJsonZoomLevel?: number
|
|
/**
|
|
* Indicates that the upstream geojson data is OSM-derived.
|
|
* Useful for e.g. merging or for scripts generating this cache.
|
|
* This also indicates that making changes on this data is possible
|
|
*
|
|
* question: Is this geojson a cache of OpenStreetMap data?
|
|
* ifunset: This is not an OpenStreetMap cache
|
|
* iftrue: this is based on OpenStreetMap and can thus be edited
|
|
*/
|
|
isOsmCache?: boolean
|
|
/**
|
|
* Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this
|
|
*
|
|
* question: Does this geojson use EPSG:900913 instead of WGS84 as projection?
|
|
* iftrue: This geojson uses EPSG:900913 instead of WGS84
|
|
* ifunset: This geojson uses WGS84 just like most geojson (default)
|
|
*/
|
|
mercatorCrs?: boolean
|
|
/**
|
|
* Some API's have an id-field, but give it a different name.
|
|
* Setting this key will rename this field into 'id'
|
|
*
|
|
* ifunset: An id with key `id` will be assigned automatically if no attribute `id` is set
|
|
* inline: This geojson uses <b>{value}</b> as attribute to set the id
|
|
* question: What is the name of the attribute containing the ID of the object?
|
|
*/
|
|
idKey?: string
|
|
}
|
|
|
|
/**
|
|
*
|
|
* A list of extra tags to calculate, specified as "keyToAssignTo=javascript-expression".
|
|
* There are a few extra functions available. Refer to <a>Docs/CalculatedTags.md</a> for more information
|
|
* The functions will be run in order, e.g.
|
|
* [
|
|
* "_max_overlap_m2=Math.max(...feat.overlapsWith("someOtherLayer").map(o => o.overlap))
|
|
* "_max_overlap_ratio=Number(feat._max_overlap_m2)/feat.area
|
|
* ]
|
|
*
|
|
* The specified tags are evaluated lazily. E.g. if a calculated tag is only used in the popup (e.g. the number of nearby features),
|
|
* the expensive calculation will only be performed then for that feature. This avoids clogging up the contributors PC when all features are loaded.
|
|
*
|
|
* If a tag has to be evaluated strictly, use ':=' instead:
|
|
*
|
|
* [
|
|
* "_some_key:=some_javascript_expression"
|
|
* ]
|
|
*
|
|
* 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, only features matching this extra tag will be shown.
|
|
* 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
|
|
|
|
/**
|
|
* 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
|
|
* 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.
|
|
*/
|
|
minzoom?: number
|
|
|
|
/**
|
|
* 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 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
|
|
|
|
/**
|
|
* The zoom level at which point the data is hidden again
|
|
* Default: 100 (thus: always visible
|
|
*
|
|
* group: expert
|
|
*/
|
|
minzoomVisible?: number
|
|
|
|
/**
|
|
* The title shown in a popup for elements of this layer.
|
|
*
|
|
* group: infobox
|
|
*/
|
|
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.
|
|
* Use an empty array to hide them.
|
|
* Note that "defaults" will insert all the default titleIcons (which are added automatically)
|
|
*
|
|
* Type: icon[]
|
|
* group: infobox
|
|
*/
|
|
titleIcons?: (string | TagRenderingConfigJson)[] | ["defaults"]
|
|
|
|
/**
|
|
* Visualisation of the items on the map
|
|
*
|
|
* group: maprendering
|
|
*/
|
|
mapRendering:
|
|
| null
|
|
| (
|
|
| PointRenderingConfigJson
|
|
| LineRenderingConfigJson
|
|
| RewritableConfigJson<
|
|
| LineRenderingConfigJson
|
|
| PointRenderingConfigJson
|
|
| LineRenderingConfigJson[]
|
|
| PointRenderingConfigJson[]
|
|
>
|
|
)[]
|
|
|
|
/**
|
|
* 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
|
|
* 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);
|
|
* it will prompt the user to add a new point.
|
|
*
|
|
* The most important aspect are the tags, which define which tags the new point will have;
|
|
* The title is shown in the dialog, along with the first sentence of the description.
|
|
*
|
|
* Upon confirmation, the full description is shown beneath the buttons - perfect to add pictures and examples.
|
|
*
|
|
* 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: presets
|
|
*/
|
|
presets?: {
|
|
/**
|
|
* The title - shown on the 'add-new'-button.
|
|
*
|
|
* This should include the article of the noun, e.g. 'a hydrant', 'a bicycle pump'.
|
|
* This text will be inserted into `Add {category} here`, becoming `Add a hydrant here`.
|
|
*
|
|
* Do _not_ indicate 'new': 'add a new shop here' is incorrect, as the shop might have existed forever, it could just be unmapped!
|
|
*
|
|
* question: What is the word to describe this object?
|
|
* inline: Add <b>{value}</b> here
|
|
*/
|
|
title: string | Record<string, string>
|
|
/**
|
|
* A single tag (encoded as <code>key=value</code>) out of all the tags to add onto the newly created point.
|
|
* Note that the icon in the UI will be chosen automatically based on the tags provided here.
|
|
*
|
|
* question: What tag should be added to the new object?
|
|
* type: simple_tag
|
|
*/
|
|
tags: string[]
|
|
/**
|
|
* An extra explanation of what the feature is, if it is not immediately clear from the title alone.
|
|
*
|
|
* The _first sentence_ of the description is shown on the button of the `add` menu.
|
|
* The full description is shown in the confirmation dialog.
|
|
*
|
|
* (The first sentence is until the first '.'-character in the description)
|
|
*
|
|
* question: How would you describe this feature?
|
|
*/
|
|
description?: string | Record<string, string>
|
|
|
|
/**
|
|
* The URL of an example image which shows a real-life example of what such a feature might look like.
|
|
*
|
|
* Type: image
|
|
* question: What is the URL of an image showing such a feature?
|
|
*/
|
|
exampleImages?: string[]
|
|
|
|
/**
|
|
* 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[]
|
|
|
|
/**
|
|
* question: What is the maximum distance in the location-input that a point can be moved to be snapped to a way?
|
|
*
|
|
* inline: a point is snapped if the location input is at most <b>{value}m</b> away from an object
|
|
*
|
|
* If specified, a new point will only be snapped if it is within this range.
|
|
* If further away, it'll be placed in the center of the location input
|
|
* Distance in meter
|
|
*
|
|
* Default: 10
|
|
*/
|
|
maxSnapDistance?: number
|
|
}[]
|
|
|
|
/**
|
|
* All the tag renderings.
|
|
* A tag rendering is a block that either shows the known value or asks a question.
|
|
*
|
|
* Refer to the class `TagRenderingConfigJson` to see the possibilities.
|
|
*
|
|
* Note that we can also use a string here - where the string refers to a tag rendering defined in `assets/questions/questions.json`,
|
|
* where a few very general questions are defined e.g. website, phone number, ...
|
|
* Furthermore, _all_ the questions of another layer can be reused with `otherlayer.*`
|
|
* If you need only a single of the tagRenderings, use `otherlayer.tagrenderingId`
|
|
* If one or more questions have a 'group' or 'label' set, select all the entries with the corresponding group or label with `otherlayer.*group`
|
|
* Remark: if a tagRendering is 'lent' from another layer, the 'source'-tags are copied and added as condition.
|
|
* If they are not wanted, remove them with an override
|
|
*
|
|
* A special value is 'questions', which indicates the location of the questions box. If not specified, it'll be appended to the bottom of the featureInfobox.
|
|
*
|
|
* At last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings.
|
|
* This is mainly create questions for a 'left' and a 'right' side of the road.
|
|
* These will be grouped and questions will be asked together
|
|
*
|
|
* group: tagrenderings
|
|
*/
|
|
tagRenderings?: (
|
|
| string
|
|
| {
|
|
id?: string
|
|
builtin: string | string[]
|
|
override: Partial<QuestionableTagRenderingConfigJson>
|
|
}
|
|
| QuestionableTagRenderingConfigJson
|
|
| (RewritableConfigJson<
|
|
(
|
|
| string
|
|
| { builtin: string; override: Partial<QuestionableTagRenderingConfigJson> }
|
|
| QuestionableTagRenderingConfigJson
|
|
)[]
|
|
> & { id: string })
|
|
)[]
|
|
|
|
/**
|
|
* All the extra questions for filtering.
|
|
* If a string is given, mapComplete will search in 'filters.json' for the appropriate filter or will try to parse it as `layername.filterid` and us that one
|
|
*
|
|
* group: filters
|
|
*/
|
|
filter?: (FilterConfigJson | string)[] | { sameAs: string }
|
|
|
|
/**
|
|
* This block defines under what circumstances the delete dialog is shown for objects of this layer.
|
|
* If set, a dialog is shown to the user to (soft) delete the point.
|
|
* The dialog is built to be user friendly and to prevent mistakes.
|
|
* If deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.
|
|
*
|
|
* To configure, the following values are possible:
|
|
*
|
|
* - false: never ever show the delete button
|
|
* - true: show the default delete button
|
|
* - 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
|
|
*
|
|
*
|
|
*
|
|
* #### Hard deletion if enough experience
|
|
|
|
* 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 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 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.
|
|
*
|
|
* 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?: 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
|
|
|
|
/**
|
|
* @see UnitConfigJson
|
|
*
|
|
* group: editing
|
|
*/
|
|
units?: UnitConfigJson[]
|
|
|
|
/**
|
|
* If set, synchronizes whether or not this layer is enabled.
|
|
*
|
|
* 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"
|
|
|
|
/**
|
|
* Used for comments and/or to disable some checks
|
|
*
|
|
* no-question-hint-check: disables a check in MiscTagRenderingChecks which complains about 'div', 'span' or 'class=subtle'-HTML elements in the tagRendering
|
|
*
|
|
* group: hidden
|
|
*/
|
|
"#"?: string | "no-question-hint-check"
|
|
|
|
/**
|
|
* _Set automatically by MapComplete, please ignore_
|
|
*
|
|
* group: hidden
|
|
*/
|
|
fullNodeDatabase?: boolean
|
|
}
|