Merge branch 'develop' into post-partner
57
.github/workflows/codeql-analysis.yml
vendored
|
@ -13,12 +13,7 @@ name: "CodeQL"
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop, master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ develop ]
|
||||
schedule:
|
||||
- cron: '21 18 * * 3'
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
@ -37,34 +32,34 @@ jobs:
|
|||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
|
|
@ -1,14 +1,33 @@
|
|||
import * as known_layers from "../assets/generated/known_layers_and_themes.json"
|
||||
import {Utils} from "../Utils";
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||
import {TagRenderingConfigJson} from "../Models/ThemeConfig/Json/TagRenderingConfigJson";
|
||||
import SharedTagRenderings from "./SharedTagRenderings";
|
||||
import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson";
|
||||
import WithContextLoader from "../Models/ThemeConfig/WithContextLoader";
|
||||
|
||||
export default class AllKnownLayers {
|
||||
|
||||
public static inited = (_ => {
|
||||
WithContextLoader.getKnownTagRenderings = (id => AllKnownLayers.getTagRendering(id))
|
||||
return true
|
||||
})()
|
||||
|
||||
|
||||
// Must be below the list...
|
||||
public static sharedLayers: Map<string, LayerConfig> = AllKnownLayers.getSharedLayers();
|
||||
public static sharedLayersJson: Map<string, any> = AllKnownLayers.getSharedLayersJson();
|
||||
|
||||
|
||||
public static added_by_default: string[] = ["gps_location","gps_location_history", "home_location", "gps_track",]
|
||||
public static no_include: string[] = [ "conflation", "left_right_style"]
|
||||
/**
|
||||
* Layer IDs of layers which have special properties through built-in hooks
|
||||
*/
|
||||
public static priviliged_layers: string[] = [...AllKnownLayers.added_by_default, "type_node",...AllKnownLayers.no_include]
|
||||
|
||||
|
||||
|
||||
private static getSharedLayers(): Map<string, LayerConfig> {
|
||||
const sharedLayers = new Map<string, LayerConfig>();
|
||||
for (const layer of known_layers.layers) {
|
||||
|
@ -16,7 +35,6 @@ export default class AllKnownLayers {
|
|||
// @ts-ignore
|
||||
const parsed = new LayerConfig(layer, "shared_layers")
|
||||
sharedLayers.set(layer.id, parsed);
|
||||
sharedLayers[layer.id] = parsed;
|
||||
} catch (e) {
|
||||
if (!Utils.runningFromConsole) {
|
||||
console.error("CRITICAL: Could not parse a layer configuration!", layer.id, " due to", e)
|
||||
|
@ -48,7 +66,7 @@ export default class AllKnownLayers {
|
|||
return sharedLayers;
|
||||
}
|
||||
|
||||
private static getSharedLayersJson(): Map<string, any> {
|
||||
private static getSharedLayersJson(): Map<string, LayerConfigJson> {
|
||||
const sharedLayers = new Map<string, any>();
|
||||
for (const layer of known_layers.layers) {
|
||||
sharedLayers.set(layer.id, layer);
|
||||
|
@ -57,5 +75,41 @@ export default class AllKnownLayers {
|
|||
return sharedLayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appropriate tagRenderingJSON
|
||||
* Allows to steal them from other layers.
|
||||
* This will add the tags of the layer to the configuration though!
|
||||
* @param renderingId
|
||||
*/
|
||||
static getTagRendering(renderingId: string): TagRenderingConfigJson {
|
||||
if(renderingId.indexOf(".") < 0){
|
||||
return SharedTagRenderings.SharedTagRenderingJson.get(renderingId)
|
||||
}
|
||||
|
||||
const [layerId, id] = renderingId.split(".")
|
||||
const layer = AllKnownLayers.getSharedLayersJson().get(layerId)
|
||||
if(layer === undefined){
|
||||
if(Utils.runningFromConsole){
|
||||
// Probably generating the layer overview
|
||||
return <TagRenderingConfigJson> {
|
||||
id: "dummy"
|
||||
}
|
||||
}
|
||||
throw "Builtin layer "+layerId+" not found"
|
||||
}
|
||||
const renderings = layer?.tagRenderings ?? []
|
||||
for (const rendering of renderings) {
|
||||
if(rendering["id"] === id){
|
||||
const found = <TagRenderingConfigJson> JSON.parse(JSON.stringify(rendering))
|
||||
if(found.condition === undefined){
|
||||
found.condition = layer.source.osmTags
|
||||
}else{
|
||||
found.condition = {and: [found.condition, layer.source.osmTags]}
|
||||
}
|
||||
return found
|
||||
}
|
||||
}
|
||||
throw `The rendering with id ${id} was not found in the builtin layer ${layerId}. Try one of ${Utils.NoNull(renderings.map(r => r["id"])).join(", ")}`
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ import AllKnownLayers from "./AllKnownLayers";
|
|||
import * as known_themes from "../assets/generated/known_layers_and_themes.json"
|
||||
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||
import BaseUIElement from "../UI/BaseUIElement";
|
||||
import Combine from "../UI/Base/Combine";
|
||||
import Title from "../UI/Base/Title";
|
||||
import List from "../UI/Base/List";
|
||||
|
||||
export class AllKnownLayouts {
|
||||
|
||||
|
@ -9,16 +13,16 @@ export class AllKnownLayouts {
|
|||
public static allKnownLayouts: Map<string, LayoutConfig> = AllKnownLayouts.AllLayouts();
|
||||
public static layoutsList: LayoutConfig[] = AllKnownLayouts.GenerateOrderedList(AllKnownLayouts.allKnownLayouts);
|
||||
|
||||
public static AllPublicLayers(){
|
||||
const allLayers : LayerConfig[] = []
|
||||
public static AllPublicLayers() {
|
||||
const allLayers: LayerConfig[] = []
|
||||
const seendIds = new Set<string>()
|
||||
const publicLayouts = AllKnownLayouts.layoutsList.filter(l => !l.hideFromOverview)
|
||||
for (const layout of publicLayouts) {
|
||||
if(layout.hideFromOverview){
|
||||
if (layout.hideFromOverview) {
|
||||
continue
|
||||
}
|
||||
for (const layer of layout.layers) {
|
||||
if(seendIds.has(layer.id)){
|
||||
if (seendIds.has(layer.id)) {
|
||||
continue
|
||||
}
|
||||
seendIds.add(layer.id)
|
||||
|
@ -29,6 +33,50 @@ export class AllKnownLayouts {
|
|||
return allLayers
|
||||
}
|
||||
|
||||
public static GenLayerOverviewText(): BaseUIElement {
|
||||
for (const id of AllKnownLayers.priviliged_layers) {
|
||||
if (!AllKnownLayers.sharedLayers.has(id)) {
|
||||
throw "Priviliged layer definition not found: " + id
|
||||
}
|
||||
}
|
||||
const allLayers: LayerConfig[] = Array.from(AllKnownLayers.sharedLayers.values())
|
||||
|
||||
const themesPerLayer = new Map<string, string[]>()
|
||||
|
||||
for (const layout of Array.from(AllKnownLayouts.allKnownLayouts.values())) {
|
||||
if(layout.hideFromOverview){
|
||||
continue
|
||||
}
|
||||
for (const layer of layout.layers) {
|
||||
if (!themesPerLayer.has(layer.id)) {
|
||||
themesPerLayer.set(layer.id, [])
|
||||
}
|
||||
themesPerLayer.get(layer.id).push(layout.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let popularLayerCutoff = 2;
|
||||
const popupalLayers = allLayers.filter((layer) => themesPerLayer.get(layer.id)?.length >= 2)
|
||||
.filter(layer => AllKnownLayers.priviliged_layers.indexOf(layer.id) < 0)
|
||||
|
||||
return new Combine([
|
||||
new Title("Special and other useful layers", 1),
|
||||
"MapComplete has a few data layers available in the theme which have special properties through builtin-hooks. Furthermore, there are some normal layers (which are built from normal Theme-config files) but are so general that they get a mention here.",
|
||||
new Title("Priviliged layers", 1),
|
||||
new List(AllKnownLayers.priviliged_layers.map(id => "[" + id + "](#" + id + ")")),
|
||||
...AllKnownLayers.priviliged_layers
|
||||
.map(id => AllKnownLayers.sharedLayers.get(id))
|
||||
.map((l) => l.GenerateDocumentation(themesPerLayer.get(l.id), AllKnownLayers.added_by_default.indexOf(l.id) >= 0, AllKnownLayers.no_include.indexOf(l.id) < 0)),
|
||||
new Title("Frequently reused layers", 1),
|
||||
"The following layers are used by at least "+popularLayerCutoff+" mapcomplete themes and might be interesting for your custom theme too",
|
||||
new List(popupalLayers.map(layer => "[" + layer.id + "](#" + layer.id + ")")),
|
||||
...popupalLayers.map((layer) => layer.GenerateDocumentation(themesPerLayer.get(layer.id)))
|
||||
])
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static GenerateOrderedList(allKnownLayouts: Map<string, LayoutConfig>): LayoutConfig[] {
|
||||
const keys = ["personal", "cyclofix", "hailhydrant", "bookcases", "toilets", "aed"]
|
||||
const list = []
|
||||
|
|
|
@ -15,7 +15,7 @@ export default class SharedTagRenderings {
|
|||
const d = new Map<string, TagRenderingConfig>()
|
||||
for (const key of Array.from(configJsons.keys())) {
|
||||
try {
|
||||
d.set(key, new TagRenderingConfig(configJsons.get(key), `SharedTagRenderings.${key}`))
|
||||
d.set(key, new TagRenderingConfig(configJsons.get(key), `SharedTagRenderings.${key}`))
|
||||
} catch (e) {
|
||||
if (!Utils.runningFromConsole) {
|
||||
console.error("BUG: could not parse", key, " from questions.json or icons.json - this error happened during the build step of the SharedTagRenderings", e)
|
||||
|
@ -38,7 +38,10 @@ export default class SharedTagRenderings {
|
|||
dict.set(key, <TagRenderingConfigJson>icons[key])
|
||||
}
|
||||
|
||||
dict.forEach((value, key) => value.id = key)
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
236
Docs/BuiltinLayers.md
Normal file
|
@ -0,0 +1,236 @@
|
|||
|
||||
|
||||
Special and other useful layers
|
||||
=================================
|
||||
|
||||
MapComplete has a few data layers available in the theme which have special properties through builtin-hooks. Furthermore, there are some normal layers (which are built from normal Theme-config files) but are so general that they get a mention here.
|
||||
|
||||
Priviliged layers
|
||||
===================
|
||||
|
||||
|
||||
|
||||
- [gps_location](#gps_location)
|
||||
- [home_location](#home_location)
|
||||
- [gps_track](#gps_track)
|
||||
- [type_node](#type_node)
|
||||
- [conflation](#conflation)
|
||||
- [left_right_style](#left_right_style)
|
||||
|
||||
|
||||
### gps_location
|
||||
|
||||
|
||||
|
||||
Meta layer showing the current location of the user. Add this to your theme and override the icon to change the appearance of the current location. The object will always have `id=gps` and will have _all_ the properties included in the [`Coordinates`-object](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationCoordinates) returned by the browser.
|
||||
|
||||
[Go to the source code](../assets/layers/gps_location/gps_location.json)
|
||||
|
||||
|
||||
|
||||
- **This layer is included automatically in every theme. This layer might contain no points**
|
||||
- Not clickable by default. If you import this layer in your theme, override `title` to make this clickable
|
||||
- Not visible in the layer selection by default. If you want to make this layer toggable, override `name`
|
||||
|
||||
|
||||
### home_location
|
||||
|
||||
|
||||
|
||||
Meta layer showing the home location of the user. The home location can be set in the [profile settings](https://www.openstreetmap.org/profile/edit) of OpenStreetMap.
|
||||
|
||||
[Go to the source code](../assets/layers/home_location/home_location.json)
|
||||
|
||||
|
||||
|
||||
- **This layer is included automatically in every theme. This layer might contain no points**
|
||||
- Not clickable by default. If you import this layer in your theme, override `title` to make this clickable
|
||||
- Not visible in the layer selection by default. If you want to make this layer toggable, override `name`
|
||||
|
||||
|
||||
### gps_track
|
||||
|
||||
|
||||
|
||||
Meta layer showing the previou locations of the user. Add this to your theme and override the icon to change the appearance of the current location.
|
||||
|
||||
[Go to the source code](../assets/layers/gps_track/gps_track.json)
|
||||
|
||||
|
||||
|
||||
- **This layer is included automatically in every theme. This layer might contain no points**
|
||||
- Not visible in the layer selection by default. If you want to make this layer toggable, override `name`
|
||||
|
||||
|
||||
### type_node
|
||||
|
||||
|
||||
|
||||
This is a priviliged meta_layer which exports _every_ point in OSM. This only works if zoomed below the point that the full tile is loaded (and not loaded via Overpass). Note that this point will also contain a property `parent_ways` which contains all the ways this node is part of as a list. This is mainly used for extremely specialized themes, which do advanced conflations. Expert use only.
|
||||
|
||||
[Go to the source code](../assets/layers/type_node/type_node.json)
|
||||
|
||||
|
||||
|
||||
- Not rendered on the map by default. If you want to rendering this on the map, override `mapRenderings`
|
||||
|
||||
|
||||
### conflation
|
||||
|
||||
|
||||
|
||||
If the import-button is set to conflate two ways, a preview is shown. This layer defines how this preview is rendered. This layer cannot be included in a theme.
|
||||
|
||||
[Go to the source code](../assets/layers/conflation/conflation.json)
|
||||
|
||||
|
||||
|
||||
- This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data.
|
||||
|
||||
|
||||
### left_right_style
|
||||
|
||||
|
||||
|
||||
Special meta-style which will show one single line, either on the left or on the right depending on the id. This is used in the small popups with left_right roads. Cannot be included in a theme
|
||||
|
||||
[Go to the source code](../assets/layers/left_right_style/left_right_style.json)
|
||||
|
||||
|
||||
|
||||
- This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data.
|
||||
|
||||
|
||||
Frequently reused layers
|
||||
==========================
|
||||
|
||||
The following layers are used by at least 2 mapcomplete themes and might be interesting for your custom theme too
|
||||
|
||||
- [bicycle_library](#bicycle_library)
|
||||
- [drinking_water](#drinking_water)
|
||||
- [food](#food)
|
||||
- [map](#map)
|
||||
- [all_streets](#all_streets)
|
||||
|
||||
|
||||
### bicycle_library
|
||||
|
||||
|
||||
|
||||
A facility where bicycles can be lent for longer period of times
|
||||
|
||||
[Go to the source code](../assets/layers/bicycle_library/bicycle_library.json)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Themes using this layer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- [bicyclelib](https://mapcomplete.osm.be/bicyclelib)
|
||||
- [cyclofix](https://mapcomplete.osm.be/cyclofix)
|
||||
|
||||
|
||||
### drinking_water
|
||||
|
||||
|
||||
|
||||
[Go to the source code](../assets/layers/drinking_water/drinking_water.json)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Themes using this layer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- [cyclofix](https://mapcomplete.osm.be/cyclofix)
|
||||
- [drinking_water](https://mapcomplete.osm.be/drinking_water)
|
||||
- [nature](https://mapcomplete.osm.be/nature)
|
||||
|
||||
|
||||
### food
|
||||
|
||||
|
||||
|
||||
[Go to the source code](../assets/layers/food/food.json)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Themes using this layer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- [food](https://mapcomplete.osm.be/food)
|
||||
- [fritures](https://mapcomplete.osm.be/fritures)
|
||||
|
||||
|
||||
### map
|
||||
|
||||
|
||||
|
||||
A map, meant for tourists which is permanently installed in the public space
|
||||
|
||||
[Go to the source code](../assets/layers/map/map.json)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Themes using this layer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- [maps](https://mapcomplete.osm.be/maps)
|
||||
- [nature](https://mapcomplete.osm.be/nature)
|
||||
|
||||
|
||||
### all_streets
|
||||
|
||||
|
||||
|
||||
[Go to the source code](../assets/layers/all_streets/all_streets.json)
|
||||
|
||||
|
||||
|
||||
- Not rendered on the map by default. If you want to rendering this on the map, override `mapRenderings`
|
||||
|
||||
|
||||
|
||||
|
||||
#### Themes using this layer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- [cyclestreets](https://mapcomplete.osm.be/cyclestreets)
|
||||
- [street_lighting](https://mapcomplete.osm.be/street_lighting)
|
||||
|
||||
|
||||
This document is autogenerated from AllKnownLayers.ts
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
|
||||
Metatags
|
||||
==========
|
||||
|
||||
|
@ -11,6 +12,7 @@ The are calculated automatically on every feature when the data arrives in the w
|
|||
**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object
|
||||
|
||||
|
||||
|
||||
Metatags calculated by MapComplete
|
||||
------------------------------------
|
||||
|
||||
|
@ -19,6 +21,7 @@ The are calculated automatically on every feature when the data arrives in the w
|
|||
The following values are always calculated, by default, by MapComplete and are available automatically on all elements in every theme
|
||||
|
||||
|
||||
|
||||
### _lat, _lon
|
||||
|
||||
|
||||
|
@ -28,6 +31,7 @@ The latitude and longitude of the point (or centerpoint in the case of a way/are
|
|||
|
||||
|
||||
|
||||
|
||||
### _layer
|
||||
|
||||
|
||||
|
@ -37,6 +41,7 @@ The layer-id to which this feature belongs. Note that this might be return any a
|
|||
|
||||
|
||||
|
||||
|
||||
### _surface, _surface:ha
|
||||
|
||||
|
||||
|
@ -46,6 +51,7 @@ The surface area of the feature, in square meters and in hectare. Not set on poi
|
|||
This is a lazy metatag and is only calculated when needed
|
||||
|
||||
|
||||
|
||||
### _length, _length:km
|
||||
|
||||
|
||||
|
@ -55,6 +61,7 @@ The total length of a feature in meters (and in kilometers, rounded to one decim
|
|||
|
||||
|
||||
|
||||
|
||||
### Theme-defined keys
|
||||
|
||||
|
||||
|
@ -64,6 +71,7 @@ If 'units' is defined in the layoutConfig, then this metatagger will rewrite the
|
|||
|
||||
|
||||
|
||||
|
||||
### _country
|
||||
|
||||
|
||||
|
@ -73,6 +81,7 @@ The country code of the property (with latlon2country)
|
|||
|
||||
|
||||
|
||||
|
||||
### _isOpen, _isOpen:description
|
||||
|
||||
|
||||
|
@ -82,6 +91,7 @@ If 'opening_hours' is present, it will add the current state of the feature (bei
|
|||
This is a lazy metatag and is only calculated when needed
|
||||
|
||||
|
||||
|
||||
### _direction:numerical, _direction:leftright
|
||||
|
||||
|
||||
|
@ -91,6 +101,7 @@ _direction:numerical is a normalized, numerical direction based on 'camera:direc
|
|||
|
||||
|
||||
|
||||
|
||||
### _now:date, _now:datetime, _loaded:date, _loaded:_datetime
|
||||
|
||||
|
||||
|
@ -100,6 +111,7 @@ Adds the time that the data got loaded - pretty much the time of downloading fro
|
|||
|
||||
|
||||
|
||||
|
||||
### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number, _backend
|
||||
|
||||
|
||||
|
@ -109,6 +121,7 @@ Information about the last edit of this object.
|
|||
|
||||
|
||||
|
||||
|
||||
### sidewalk:left, sidewalk:right, generic_key:left:property, generic_key:right:property
|
||||
|
||||
|
||||
|
@ -118,6 +131,7 @@ Rewrites tags from 'generic_key:both:property' as 'generic_key:left:property' an
|
|||
|
||||
|
||||
|
||||
|
||||
Calculating tags with Javascript
|
||||
----------------------------------
|
||||
|
||||
|
@ -173,6 +187,7 @@ Some advanced functions are available on **feat** as well:
|
|||
- [memberships](#memberships)
|
||||
- [get](#get)
|
||||
|
||||
|
||||
### distanceTo
|
||||
|
||||
Calculates the distance between the feature and a specified point in kilometer. The input should either be a pair of coordinates, a geojson feature or the ID of an object
|
||||
|
@ -180,6 +195,7 @@ Some advanced functions are available on **feat** as well:
|
|||
0. feature OR featureID OR longitude
|
||||
1. undefined OR latitude
|
||||
|
||||
|
||||
### overlapWith
|
||||
|
||||
Gives a list of features from the specified layer which this feature (partly) overlaps with. A point which is embedded in the feature is detected as well.If the current feature is a point, all features that this point is embeded in are given.
|
||||
|
@ -191,12 +207,14 @@ For example to get all objects which overlap or embed from a layer, use `_contai
|
|||
|
||||
0. ...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)
|
||||
|
||||
|
||||
### closest
|
||||
|
||||
Given either a list of geojson features or a single layer name, gives the single object which is nearest to the feature. In the case of ways/polygons, only the centerpoint is considered. Returns a single geojson feature or undefined if nothing is found (or not yet laoded)
|
||||
|
||||
0. list of features or a layer name or '*' to get all features
|
||||
|
||||
|
||||
### closestn
|
||||
|
||||
Given either a list of geojson features or a single layer name, gives the n closest objects which are nearest to the feature (excluding the feature itself). In the case of ways/polygons, only the centerpoint is considered. Returns a list of `{feat: geojson, distance:number}` the empty list if nothing is found (or not yet loaded)
|
||||
|
@ -208,6 +226,7 @@ If a 'unique tag key' is given, the tag with this key will only appear once (e.g
|
|||
2. unique tag key (optional)
|
||||
3. maxDistanceInMeters (optional)
|
||||
|
||||
|
||||
### memberships
|
||||
|
||||
Gives a list of `{role: string, relation: Relation}`-objects, containing all the relations that this feature is part of.
|
||||
|
@ -216,9 +235,12 @@ For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tag
|
|||
|
||||
|
||||
|
||||
|
||||
### get
|
||||
|
||||
Gets the property of the feature, parses it (as JSON) and returns it. Might return 'undefined' if not defined, null, ...
|
||||
|
||||
0. key
|
||||
Generated from SimpleMetaTagger, ExtraFunction
|
||||
|
||||
|
||||
This document is autogenerated from SimpleMetaTagger, ExtraFunction
|
|
@ -1,32 +1,42 @@
|
|||
|
||||
Rights of contributors
|
||||
======================
|
||||
Rights of contributors
|
||||
======================
|
||||
|
||||
If a contributor is quite active within MapComplete, this contributor might be granted access to the main repository.
|
||||
|
||||
If you have access to the repository, you can make a fork of an already existing branch and push this new branch to github.
|
||||
This means that this branch will be _automatically built_ and be **deployed** to `https://pietervdvn.github.io/mc/<branchname>`. You can see the deploy process on [Github Actions](https://github.com/pietervdvn/MapComplete/actions).
|
||||
Don't worry about pushing too much. These deploys are free and totally automatic. They might fail if something is wrong, but this will hinder no-one.
|
||||
If you have access to the repository, you can make a fork of an already existing branch and push this new branch to
|
||||
github. This means that this branch will be _automatically built_ and be **deployed**
|
||||
to `https://pietervdvn.github.io/mc/<branchname>`. You can see the deploy process
|
||||
on [Github Actions](https://github.com/pietervdvn/MapComplete/actions). Don't worry about pushing too much. These
|
||||
deploys are free and totally automatic. They might fail if something is wrong, but this will hinder no-one.
|
||||
|
||||
Additionaly, some other maintainer might step in and merge the latest develop with your branch, making later pull requests easier.
|
||||
Additionaly, some other maintainer might step in and merge the latest develop with your branch, making later pull
|
||||
requests easier.
|
||||
|
||||
Don't worry about bugs
|
||||
----------------------
|
||||
|
||||
As a non-admin contributor, you can _not_ make changes to the `master` nor to the `develop` branch. This is because, as soon as master is changed, this is built and deployed on `mapcomplete.osm.be`, which a lot of people use. An error there will cause a lot of grieve.
|
||||
As a non-admin contributor, you can _not_ make changes to the `master` nor to the `develop` branch. This is because, as
|
||||
soon as master is changed, this is built and deployed on `mapcomplete.osm.be`, which a lot of people use. An error there
|
||||
will cause a lot of grieve.
|
||||
|
||||
A push on `develop` is automatically deployed to [pietervdvn.github.io/mc/develop] and is used by quite some people to. People using this version should know that this is a testing ground for new features and might contain a bug every now and then.
|
||||
A push on `develop` is automatically deployed to [pietervdvn.github.io/mc/develop] and is used by quite some people to.
|
||||
People using this version should know that this is a testing ground for new features and might contain a bug every now
|
||||
and then.
|
||||
|
||||
In other words, to get your theme deployed on the main instances, you'll still have to create a pull request. The maintainers will then doublecheck and pull it in.
|
||||
In other words, to get your theme deployed on the main instances, you'll still have to create a pull request. The
|
||||
maintainers will then doublecheck and pull it in.
|
||||
|
||||
If you have a local repository
|
||||
------------------------------
|
||||
|
||||
If you have made a fork earlier and have received contributor rights, you need to tell your local git repository that pushing to the main repository is possible.
|
||||
If you have made a fork earlier and have received contributor rights, you need to tell your local git repository that
|
||||
pushing to the main repository is possible.
|
||||
|
||||
To do this:
|
||||
|
||||
1. type `git remote add upstream git@github.com:pietervdvn/MapComplete`
|
||||
2. Run `git push upstream` to push your latest changes to the main repo (and not your fork). Running `git push` will push to your fork.
|
||||
2. Run `git push upstream` to push your latest changes to the main repo (and not your fork). Running `git push` will
|
||||
push to your fork.
|
||||
|
||||
Alternatively, if you don't have any unmerged changes, you can remove your local copy and clone `pietervdvn/MapComplete` again to start fresh.
|
||||
Alternatively, if you don't have any unmerged changes, you can remove your local copy and clone `pietervdvn/MapComplete`
|
||||
again to start fresh.
|
|
@ -28,14 +28,15 @@ To develop and build MapComplete, you
|
|||
|
||||
0. Make a fork and clone the repository.
|
||||
0. Install the nodejs version specified in [.tool-versions](./.tool-versions)
|
||||
- On linux: install npm first `sudo apt install npm`, then install `n` using npm: ` npm install -g n`, which can then install node with `n install <node-version>`
|
||||
- You can [use asdf to manage your runtime versions](https://asdf-vm.com/).
|
||||
- On linux: install npm first `sudo apt install npm`, then install `n` using npm: ` npm install -g n`, which can
|
||||
then install node with `n install <node-version>`
|
||||
- You can [use asdf to manage your runtime versions](https://asdf-vm.com/).
|
||||
0. Install `npm`. Linux: `sudo apt install npm` (or your favourite package manager), Windows: install
|
||||
nodeJS: https://nodejs.org/en/download/
|
||||
0. On iOS, install `wget` (`brew install wget`)
|
||||
0. Run `npm run init` which …
|
||||
- runs `npm install`
|
||||
- generates some additional dependencies and files
|
||||
- runs `npm install`
|
||||
- generates some additional dependencies and files
|
||||
0. Run `npm run start` to host a local testversion at http://localhost:1234/index.html
|
||||
0. By default, a landing page with available themes is served. In order to load a single theme, use `layout=themename`
|
||||
or `userlayout=true#<layout configuration>` as [Query parameter](URL_Parameters.md). Note that the shorter URLs (
|
||||
|
@ -106,7 +107,8 @@ Try removing `node_modules`, `package-lock.json` and `.cache`
|
|||
Misc setup
|
||||
----------
|
||||
|
||||
The json-git-merger is used to quickly merge translation files, [documentation here](https://github.com/jonatanpedersen/git-json-merge#single-project--directory)
|
||||
~~The json-git-merger is used to quickly merge translation files, [documentation here](https://github.com/jonatanpedersen/git-json-merge#single-project--directory).~~
|
||||
This merge driver is broken and would sometimes drop new questions or duplicate them... Not a good idea!
|
||||
|
||||
Overview of package.json-scripts
|
||||
--------------------------------
|
||||
|
|
|
@ -105,11 +105,68 @@ Every field is documented in the source code itself - you can find them here:
|
|||
- [The `TagRendering`](https://github.com/pietervdvn/MapComplete/blob/master/Models/ThemeConfig/Json/TagRenderingConfigJson.ts)
|
||||
- At last, the exact semantics of tags is documented [here](Tags_format.md)
|
||||
|
||||
A JSON-schema file is available in Docs/Schemas - use LayoutConfig.schema.json to validate a theme file.
|
||||
|
||||
### MetaTags
|
||||
|
||||
There are few tags available that are calculated for convenience - e.g. the country an object is located
|
||||
at. [An overview of all these metatags is available here](Docs/CalculatedTags.md)
|
||||
|
||||
|
||||
### TagRendering groups
|
||||
|
||||
A tagRendering can have a `group`-attribute, which acts as a tag.
|
||||
All tagRenderings with the same group name will be rendered together, in the same order as they were defined.
|
||||
|
||||
For example, if the defined tagrenderings have groups `A A B A A B B B`, the group order is `A B` and first all tagrenderings from group A will be rendered (thus numbers 0, 1, 3 and 4) followed by the question box for this group.
|
||||
Then, all the tagRenderings for group B will be shown, thus number 2, 5, 6 and 7, again followed by their questionbox.
|
||||
|
||||
Additionally, every tagrendering will receive a the groupname as class in the HTML, which can be used to hook up custom CSS.
|
||||
|
||||
If no group tag is given, the group is `` (empty string)
|
||||
|
||||
### Deciding the questions position
|
||||
|
||||
By default, the questions are shown just beneath their group.
|
||||
|
||||
To override this behaviour, one can add a tagrendering with id `questions` to move the questions up.
|
||||
|
||||
To add a title to the questions, one can add a `render` and a condition.
|
||||
|
||||
To change the behaviour of the questionbox to show _all_ questions at once, one can use a helperArgs in the freeform field with option `showAllQuestions`.
|
||||
|
||||
For example, to show the questions on top, use:
|
||||
|
||||
```
|
||||
"tagRenderings": [
|
||||
{ "id": "questions" }
|
||||
{ ... some tagrendering ... }
|
||||
{ ... more tagrendering ...}
|
||||
]
|
||||
```
|
||||
|
||||
To show _all_ the questions of a group at once in the middle of the tagrenderings, with a header, use:
|
||||
|
||||
```
|
||||
"tagRenderings": [
|
||||
{
|
||||
"id": "questions" ,
|
||||
"group": "groupname",
|
||||
"render": {
|
||||
"en": "<h3>Technical questions</h3>The following questions are very technical!<br />{questions}
|
||||
},
|
||||
"freeform": {
|
||||
"key": "questions",
|
||||
"helperArgs": {
|
||||
"showAllQuestions": true
|
||||
}
|
||||
}
|
||||
}
|
||||
{ ... some tagrendering ... }
|
||||
{ ... more tagrendering ...}
|
||||
]
|
||||
```
|
||||
|
||||
Some hints
|
||||
------------
|
||||
|
||||
|
@ -173,16 +230,7 @@ Instead, make one layer for one kind of object and change the icon based on attr
|
|||
|
||||
Using layers as filters - this doesn't work!
|
||||
|
||||
_All_ data is downloaded in one go and cached locally first. The layer selection (bottom left of the live app) then
|
||||
selects _anything_ that matches the criteria. This match is then passed of to the rendering layer, which selects the
|
||||
layer independently. This means that a feature can show up, even if it's layer is unselected!
|
||||
|
||||
For example, in the [cyclofix-theme](https://mapcomplete.osm.org/cyclofix), there is the layer with _bike-wash_ for do
|
||||
it yourself bikecleaning - points marked with `service:bicycle:cleaning`. However, a bicycle repair shop can offer this
|
||||
service too!
|
||||
|
||||
If all the layers are deselected except the bike wash layer, a shop having this tag will still match and will still show
|
||||
up as shop.
|
||||
Use the `filter`-functionality instead
|
||||
|
||||
### Not reading the .JSON-specs
|
||||
|
||||
|
|
|
@ -1,145 +1,181 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.43.0 (0)
|
||||
-->
|
||||
<!-- Title: G Pages: 1 -->
|
||||
<svg width="664pt" height="566pt"
|
||||
viewBox="0.00 0.00 664.25 566.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 562)">
|
||||
<title>G</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-562 660.25,-562 660.25,4 -4,4"/>
|
||||
<!-- init -->
|
||||
<g id="node1" class="node">
|
||||
<title>init</title>
|
||||
<polygon fill="none" stroke="black" points="242.25,-558 188.25,-558 188.25,-522 242.25,-522 242.25,-558"/>
|
||||
<text text-anchor="middle" x="215.25" y="-536.3" font-family="Times,serif" font-size="14.00">init</text>
|
||||
</g>
|
||||
<!-- denied -->
|
||||
<g id="node2" class="node">
|
||||
<title>denied</title>
|
||||
<ellipse fill="none" stroke="black" cx="42.25" cy="-279" rx="42.49" ry="18"/>
|
||||
<text text-anchor="middle" x="42.25" y="-275.3" font-family="Times,serif" font-size="14.00">denied</text>
|
||||
</g>
|
||||
<!-- init->denied -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>init->denied</title>
|
||||
<path fill="none" stroke="black" d="M188.23,-531.67C143.21,-517.82 54.16,-483.1 17.25,-417 -2.35,-381.91 14.04,-334.64 27.95,-305.79"/>
|
||||
<polygon fill="black" stroke="black" points="31.12,-307.26 32.51,-296.76 24.88,-304.1 31.12,-307.26"/>
|
||||
<text text-anchor="middle" x="132.25" y="-405.8" font-family="Times,serif" font-size="14.00">geolocation permanently denied</text>
|
||||
</g>
|
||||
<!-- getting_location -->
|
||||
<g id="node3" class="node">
|
||||
<title>getting_location</title>
|
||||
<ellipse fill="none" stroke="black" cx="366.25" cy="-279" rx="85.29" ry="18"/>
|
||||
<text text-anchor="middle" x="366.25" y="-275.3" font-family="Times,serif" font-size="14.00">getting_location</text>
|
||||
</g>
|
||||
<!-- init->getting_location -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>init->getting_location</title>
|
||||
<path fill="none" stroke="black" d="M242.41,-538.69C294.16,-537.46 403.84,-531.59 427.25,-504 481.59,-439.95 469.34,-387.69 427.25,-315 424.07,-309.52 419.68,-304.83 414.7,-300.82"/>
|
||||
<polygon fill="black" stroke="black" points="416.68,-297.93 406.47,-295.09 412.67,-303.68 416.68,-297.93"/>
|
||||
<text text-anchor="middle" x="559.75" y="-405.8" font-family="Times,serif" font-size="14.00">previously granted flag set</text>
|
||||
</g>
|
||||
<!-- idle -->
|
||||
<g id="node4" class="node">
|
||||
<title>idle</title>
|
||||
<ellipse fill="none" stroke="black" cx="255.25" cy="-453" rx="27.9" ry="18"/>
|
||||
<text text-anchor="middle" x="255.25" y="-449.3" font-family="Times,serif" font-size="14.00">idle</text>
|
||||
</g>
|
||||
<!-- init->idle -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>init->idle</title>
|
||||
<path fill="none" stroke="black" d="M212.27,-521.58C211.37,-511.6 211.62,-499.1 216.25,-489 218.98,-483.03 223.28,-477.64 228.03,-472.99"/>
|
||||
<polygon fill="black" stroke="black" points="230.48,-475.49 235.73,-466.29 225.89,-470.21 230.48,-475.49"/>
|
||||
<text text-anchor="middle" x="321.75" y="-492.8" font-family="Times,serif" font-size="14.00">previously granted flag unset</text>
|
||||
</g>
|
||||
<!-- location_found -->
|
||||
<g id="node6" class="node">
|
||||
<title>location_found</title>
|
||||
<ellipse fill="none" stroke="black" cx="366.25" cy="-192" rx="77.99" ry="18"/>
|
||||
<text text-anchor="middle" x="366.25" y="-188.3" font-family="Times,serif" font-size="14.00">location_found</text>
|
||||
</g>
|
||||
<!-- getting_location->location_found -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>getting_location->location_found</title>
|
||||
<path fill="none" stroke="black" d="M366.25,-260.8C366.25,-249.16 366.25,-233.55 366.25,-220.24"/>
|
||||
<polygon fill="black" stroke="black" points="369.75,-220.18 366.25,-210.18 362.75,-220.18 369.75,-220.18"/>
|
||||
<text text-anchor="middle" x="417.25" y="-231.8" font-family="Times,serif" font-size="14.00">location found</text>
|
||||
</g>
|
||||
<!-- request_permission -->
|
||||
<g id="node5" class="node">
|
||||
<title>request_permission</title>
|
||||
<ellipse fill="none" stroke="black" cx="264.25" cy="-366" rx="102.08" ry="18"/>
|
||||
<text text-anchor="middle" x="264.25" y="-362.3" font-family="Times,serif" font-size="14.00">request_permission</text>
|
||||
</g>
|
||||
<!-- idle->request_permission -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>idle->request_permission</title>
|
||||
<path fill="none" stroke="black" d="M282.79,-448.82C302.7,-444.93 328.29,-436.26 341.25,-417 349.95,-404.06 340.76,-393.72 326.08,-385.86"/>
|
||||
<polygon fill="black" stroke="black" points="327.44,-382.63 316.9,-381.53 324.45,-388.96 327.44,-382.63"/>
|
||||
<text text-anchor="middle" x="371.75" y="-405.8" font-family="Times,serif" font-size="14.00">on click</text>
|
||||
</g>
|
||||
<!-- request_permission->denied -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>request_permission->denied</title>
|
||||
<path fill="none" stroke="black" d="M202.76,-351.6C180.78,-345.99 156.06,-338.72 134.25,-330 113.26,-321.61 90.96,-309.57 73.57,-299.42"/>
|
||||
<polygon fill="black" stroke="black" points="75.34,-296.4 64.96,-294.3 71.77,-302.42 75.34,-296.4"/>
|
||||
<text text-anchor="middle" x="206.25" y="-318.8" font-family="Times,serif" font-size="14.00">permanently denied</text>
|
||||
</g>
|
||||
<!-- request_permission->getting_location -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>request_permission->getting_location</title>
|
||||
<path fill="none" stroke="black" d="M271.38,-348C276.49,-337.43 284.24,-324.16 294.25,-315 300.73,-309.07 308.39,-303.93 316.27,-299.56"/>
|
||||
<polygon fill="black" stroke="black" points="317.92,-302.64 325.2,-294.94 314.71,-296.43 317.92,-302.64"/>
|
||||
<text text-anchor="middle" x="360.75" y="-318.8" font-family="Times,serif" font-size="14.00">granted (sets flag)</text>
|
||||
</g>
|
||||
<!-- request_permission->idle -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>request_permission->idle</title>
|
||||
<path fill="none" stroke="black" d="M257.1,-384.15C255.12,-389.74 253.24,-396.04 252.25,-402 251.02,-409.35 250.95,-417.37 251.4,-424.8"/>
|
||||
<polygon fill="black" stroke="black" points="247.92,-425.14 252.32,-434.78 254.89,-424.5 247.92,-425.14"/>
|
||||
<text text-anchor="middle" x="294.75" y="-405.8" font-family="Times,serif" font-size="14.00">not granted</text>
|
||||
</g>
|
||||
<!-- open_lock -->
|
||||
<g id="node7" class="node">
|
||||
<title>open_lock</title>
|
||||
<ellipse fill="none" stroke="black" cx="333.25" cy="-105" rx="55.79" ry="18"/>
|
||||
<text text-anchor="middle" x="333.25" y="-101.3" font-family="Times,serif" font-size="14.00">open_lock</text>
|
||||
</g>
|
||||
<!-- location_found->open_lock -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>location_found->open_lock</title>
|
||||
<path fill="none" stroke="black" d="M359.57,-173.8C354.98,-161.97 348.79,-146.03 343.56,-132.58"/>
|
||||
<polygon fill="black" stroke="black" points="346.68,-130.94 339.8,-122.89 340.16,-133.47 346.68,-130.94"/>
|
||||
<text text-anchor="middle" x="448.25" y="-144.8" font-family="Times,serif" font-size="14.00">on click (zooms to location)</text>
|
||||
</g>
|
||||
<!-- open_lock->location_found -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>open_lock->location_found</title>
|
||||
<path fill="none" stroke="black" d="M295.44,-118.33C275.01,-127.12 256.04,-140.15 267.25,-156 273.92,-165.44 283.37,-172.35 293.8,-177.41"/>
|
||||
<polygon fill="black" stroke="black" points="292.6,-180.7 303.17,-181.39 295.34,-174.26 292.6,-180.7"/>
|
||||
<text text-anchor="middle" x="305.25" y="-144.8" font-family="Times,serif" font-size="14.00">after 3 sec</text>
|
||||
</g>
|
||||
<!-- closed_lock -->
|
||||
<g id="node8" class="node">
|
||||
<title>closed_lock</title>
|
||||
<ellipse fill="none" stroke="black" cx="454.25" cy="-18" rx="63.09" ry="18"/>
|
||||
<text text-anchor="middle" x="454.25" y="-14.3" font-family="Times,serif" font-size="14.00">closed_lock</text>
|
||||
</g>
|
||||
<!-- open_lock->closed_lock -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>open_lock->closed_lock</title>
|
||||
<path fill="none" stroke="black" d="M328.89,-87.05C327.23,-76.5 327.17,-63.24 334.25,-54 346.04,-38.59 364.24,-29.68 382.92,-24.6"/>
|
||||
<polygon fill="black" stroke="black" points="383.78,-28 392.71,-22.28 382.17,-21.18 383.78,-28"/>
|
||||
<text text-anchor="middle" x="447.75" y="-57.8" font-family="Times,serif" font-size="14.00">on click (locks zoom to location)</text>
|
||||
</g>
|
||||
<!-- closed_lock->location_found -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>closed_lock->location_found</title>
|
||||
<path fill="none" stroke="black" d="M513.08,-24.74C531.5,-29.64 549.95,-38.41 561.25,-54 588.04,-90.95 580.67,-122.9 549.25,-156 535.31,-170.68 491.24,-179.37 449.85,-184.42"/>
|
||||
<polygon fill="black" stroke="black" points="449.22,-180.97 439.68,-185.6 450.02,-187.93 449.22,-180.97"/>
|
||||
<text text-anchor="middle" x="604.75" y="-101.3" font-family="Times,serif" font-size="14.00">on click</text>
|
||||
</g>
|
||||
</g>
|
||||
viewBox="0.00 0.00 664.25 566.00" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 562)">
|
||||
<title>G</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-562 660.25,-562 660.25,4 -4,4"/>
|
||||
<!-- init -->
|
||||
<g id="node1" class="node">
|
||||
<title>init</title>
|
||||
<polygon fill="none" stroke="black" points="242.25,-558 188.25,-558 188.25,-522 242.25,-522 242.25,-558"/>
|
||||
<text text-anchor="middle" x="215.25" y="-536.3" font-family="Times,serif" font-size="14.00">init</text>
|
||||
</g>
|
||||
<!-- denied -->
|
||||
<g id="node2" class="node">
|
||||
<title>denied</title>
|
||||
<ellipse fill="none" stroke="black" cx="42.25" cy="-279" rx="42.49" ry="18"/>
|
||||
<text text-anchor="middle" x="42.25" y="-275.3" font-family="Times,serif" font-size="14.00">denied</text>
|
||||
</g>
|
||||
<!-- init->denied -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>init->denied</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M188.23,-531.67C143.21,-517.82 54.16,-483.1 17.25,-417 -2.35,-381.91 14.04,-334.64 27.95,-305.79"/>
|
||||
<polygon fill="black" stroke="black" points="31.12,-307.26 32.51,-296.76 24.88,-304.1 31.12,-307.26"/>
|
||||
<text text-anchor="middle" x="132.25" y="-405.8" font-family="Times,serif" font-size="14.00">geolocation
|
||||
permanently denied
|
||||
</text>
|
||||
</g>
|
||||
<!-- getting_location -->
|
||||
<g id="node3" class="node">
|
||||
<title>getting_location</title>
|
||||
<ellipse fill="none" stroke="black" cx="366.25" cy="-279" rx="85.29" ry="18"/>
|
||||
<text text-anchor="middle" x="366.25" y="-275.3" font-family="Times,serif" font-size="14.00">
|
||||
getting_location
|
||||
</text>
|
||||
</g>
|
||||
<!-- init->getting_location -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>init->getting_location</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M242.41,-538.69C294.16,-537.46 403.84,-531.59 427.25,-504 481.59,-439.95 469.34,-387.69 427.25,-315 424.07,-309.52 419.68,-304.83 414.7,-300.82"/>
|
||||
<polygon fill="black" stroke="black" points="416.68,-297.93 406.47,-295.09 412.67,-303.68 416.68,-297.93"/>
|
||||
<text text-anchor="middle" x="559.75" y="-405.8" font-family="Times,serif" font-size="14.00">previously
|
||||
granted flag set
|
||||
</text>
|
||||
</g>
|
||||
<!-- idle -->
|
||||
<g id="node4" class="node">
|
||||
<title>idle</title>
|
||||
<ellipse fill="none" stroke="black" cx="255.25" cy="-453" rx="27.9" ry="18"/>
|
||||
<text text-anchor="middle" x="255.25" y="-449.3" font-family="Times,serif" font-size="14.00">idle</text>
|
||||
</g>
|
||||
<!-- init->idle -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>init->idle</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M212.27,-521.58C211.37,-511.6 211.62,-499.1 216.25,-489 218.98,-483.03 223.28,-477.64 228.03,-472.99"/>
|
||||
<polygon fill="black" stroke="black" points="230.48,-475.49 235.73,-466.29 225.89,-470.21 230.48,-475.49"/>
|
||||
<text text-anchor="middle" x="321.75" y="-492.8" font-family="Times,serif" font-size="14.00">previously
|
||||
granted flag unset
|
||||
</text>
|
||||
</g>
|
||||
<!-- location_found -->
|
||||
<g id="node6" class="node">
|
||||
<title>location_found</title>
|
||||
<ellipse fill="none" stroke="black" cx="366.25" cy="-192" rx="77.99" ry="18"/>
|
||||
<text text-anchor="middle" x="366.25" y="-188.3" font-family="Times,serif" font-size="14.00">
|
||||
location_found
|
||||
</text>
|
||||
</g>
|
||||
<!-- getting_location->location_found -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>getting_location->location_found</title>
|
||||
<path fill="none" stroke="black" d="M366.25,-260.8C366.25,-249.16 366.25,-233.55 366.25,-220.24"/>
|
||||
<polygon fill="black" stroke="black" points="369.75,-220.18 366.25,-210.18 362.75,-220.18 369.75,-220.18"/>
|
||||
<text text-anchor="middle" x="417.25" y="-231.8" font-family="Times,serif" font-size="14.00">location
|
||||
found
|
||||
</text>
|
||||
</g>
|
||||
<!-- request_permission -->
|
||||
<g id="node5" class="node">
|
||||
<title>request_permission</title>
|
||||
<ellipse fill="none" stroke="black" cx="264.25" cy="-366" rx="102.08" ry="18"/>
|
||||
<text text-anchor="middle" x="264.25" y="-362.3" font-family="Times,serif" font-size="14.00">
|
||||
request_permission
|
||||
</text>
|
||||
</g>
|
||||
<!-- idle->request_permission -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>idle->request_permission</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M282.79,-448.82C302.7,-444.93 328.29,-436.26 341.25,-417 349.95,-404.06 340.76,-393.72 326.08,-385.86"/>
|
||||
<polygon fill="black" stroke="black" points="327.44,-382.63 316.9,-381.53 324.45,-388.96 327.44,-382.63"/>
|
||||
<text text-anchor="middle" x="371.75" y="-405.8" font-family="Times,serif" font-size="14.00">on click</text>
|
||||
</g>
|
||||
<!-- request_permission->denied -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>request_permission->denied</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M202.76,-351.6C180.78,-345.99 156.06,-338.72 134.25,-330 113.26,-321.61 90.96,-309.57 73.57,-299.42"/>
|
||||
<polygon fill="black" stroke="black" points="75.34,-296.4 64.96,-294.3 71.77,-302.42 75.34,-296.4"/>
|
||||
<text text-anchor="middle" x="206.25" y="-318.8" font-family="Times,serif" font-size="14.00">permanently
|
||||
denied
|
||||
</text>
|
||||
</g>
|
||||
<!-- request_permission->getting_location -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>request_permission->getting_location</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M271.38,-348C276.49,-337.43 284.24,-324.16 294.25,-315 300.73,-309.07 308.39,-303.93 316.27,-299.56"/>
|
||||
<polygon fill="black" stroke="black" points="317.92,-302.64 325.2,-294.94 314.71,-296.43 317.92,-302.64"/>
|
||||
<text text-anchor="middle" x="360.75" y="-318.8" font-family="Times,serif" font-size="14.00">granted (sets
|
||||
flag)
|
||||
</text>
|
||||
</g>
|
||||
<!-- request_permission->idle -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>request_permission->idle</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M257.1,-384.15C255.12,-389.74 253.24,-396.04 252.25,-402 251.02,-409.35 250.95,-417.37 251.4,-424.8"/>
|
||||
<polygon fill="black" stroke="black" points="247.92,-425.14 252.32,-434.78 254.89,-424.5 247.92,-425.14"/>
|
||||
<text text-anchor="middle" x="294.75" y="-405.8" font-family="Times,serif" font-size="14.00">not granted
|
||||
</text>
|
||||
</g>
|
||||
<!-- open_lock -->
|
||||
<g id="node7" class="node">
|
||||
<title>open_lock</title>
|
||||
<ellipse fill="none" stroke="black" cx="333.25" cy="-105" rx="55.79" ry="18"/>
|
||||
<text text-anchor="middle" x="333.25" y="-101.3" font-family="Times,serif" font-size="14.00">open_lock
|
||||
</text>
|
||||
</g>
|
||||
<!-- location_found->open_lock -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>location_found->open_lock</title>
|
||||
<path fill="none" stroke="black" d="M359.57,-173.8C354.98,-161.97 348.79,-146.03 343.56,-132.58"/>
|
||||
<polygon fill="black" stroke="black" points="346.68,-130.94 339.8,-122.89 340.16,-133.47 346.68,-130.94"/>
|
||||
<text text-anchor="middle" x="448.25" y="-144.8" font-family="Times,serif" font-size="14.00">on click (zooms
|
||||
to location)
|
||||
</text>
|
||||
</g>
|
||||
<!-- open_lock->location_found -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>open_lock->location_found</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M295.44,-118.33C275.01,-127.12 256.04,-140.15 267.25,-156 273.92,-165.44 283.37,-172.35 293.8,-177.41"/>
|
||||
<polygon fill="black" stroke="black" points="292.6,-180.7 303.17,-181.39 295.34,-174.26 292.6,-180.7"/>
|
||||
<text text-anchor="middle" x="305.25" y="-144.8" font-family="Times,serif" font-size="14.00">after 3 sec
|
||||
</text>
|
||||
</g>
|
||||
<!-- closed_lock -->
|
||||
<g id="node8" class="node">
|
||||
<title>closed_lock</title>
|
||||
<ellipse fill="none" stroke="black" cx="454.25" cy="-18" rx="63.09" ry="18"/>
|
||||
<text text-anchor="middle" x="454.25" y="-14.3" font-family="Times,serif" font-size="14.00">closed_lock
|
||||
</text>
|
||||
</g>
|
||||
<!-- open_lock->closed_lock -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>open_lock->closed_lock</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M328.89,-87.05C327.23,-76.5 327.17,-63.24 334.25,-54 346.04,-38.59 364.24,-29.68 382.92,-24.6"/>
|
||||
<polygon fill="black" stroke="black" points="383.78,-28 392.71,-22.28 382.17,-21.18 383.78,-28"/>
|
||||
<text text-anchor="middle" x="447.75" y="-57.8" font-family="Times,serif" font-size="14.00">on click (locks
|
||||
zoom to location)
|
||||
</text>
|
||||
</g>
|
||||
<!-- closed_lock->location_found -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>closed_lock->location_found</title>
|
||||
<path fill="none" stroke="black"
|
||||
d="M513.08,-24.74C531.5,-29.64 549.95,-38.41 561.25,-54 588.04,-90.95 580.67,-122.9 549.25,-156 535.31,-170.68 491.24,-179.37 449.85,-184.42"/>
|
||||
<polygon fill="black" stroke="black" points="449.22,-180.97 439.68,-185.6 450.02,-187.93 449.22,-180.97"/>
|
||||
<text text-anchor="middle" x="604.75" y="-101.3" font-family="Times,serif" font-size="14.00">on click</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 10 KiB |
|
@ -6,13 +6,15 @@ Some highlights of new releases.
|
|||
0.10
|
||||
----
|
||||
|
||||
The 0.10 version contains a lot of refactorings on various core of the application, namely in the rendering stack, the fetching of data and uploading.
|
||||
The 0.10 version contains a lot of refactorings on various core of the application, namely in the rendering stack, the
|
||||
fetching of data and uploading.
|
||||
|
||||
Some highlights are:
|
||||
|
||||
1. The addition of fallback overpass servers
|
||||
2. Fetching data from OSM directly (especially useful in the personal theme)
|
||||
3. Splitting all the features per tile (with a maximum amount of features per tile, splitting further if needed), making everything a ton faster
|
||||
3. Splitting all the features per tile (with a maximum amount of features per tile, splitting further if needed), making
|
||||
everything a ton faster
|
||||
4. If a tile has too much features, the featuers are not shown. Instead, a rectangle with the feature amount is shown.
|
||||
|
||||
Furthermore, it contains a few new themes and theme updates:
|
||||
|
@ -31,9 +33,8 @@ Other various small improvements:
|
|||
0.8 and 0.9
|
||||
-----------
|
||||
|
||||
Addition of filters per layer
|
||||
Addition of a download-as-pdf for select themes
|
||||
Addition of a download-as-geojson and download-as-csv for select themes
|
||||
Addition of filters per layer Addition of a download-as-pdf for select themes Addition of a download-as-geojson and
|
||||
download-as-csv for select themes
|
||||
|
||||
...
|
||||
|
||||
|
|
39
Docs/Schemas/AndOrTagConfigJson.schema.json
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson",
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
37
Docs/Schemas/AndOrTagConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
export default {
|
||||
"$ref": "#/definitions/AndOrTagConfigJson",
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
93
Docs/Schemas/DeleteConfigJson.schema.json
Normal file
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"extraDeleteReasons": {
|
||||
"description": "*\nBy default, three reasons to delete a point are shown:\n\n- The point does not exist anymore\n- The point was a testing point\n- THe point could not be found\n\nHowever, for some layers, there might be different or more specific reasons for deletion which can be user friendly to set, e.g.:\n\n- the shop has closed\n- the climbing route has been closed of for nature conservation reasons\n- ...\n\nThese reasons can be stated here and will be shown in the list of options the user can choose from",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"explanation": {
|
||||
"description": "The text that will be shown to the user - translatable"
|
||||
},
|
||||
"changesetMessage": {
|
||||
"description": "The text that will be uploaded into the changeset or will be used in the fixme in case of a soft deletion\nShould be a few words, in english",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changesetMessage",
|
||||
"explanation"
|
||||
]
|
||||
}
|
||||
},
|
||||
"nonDeleteMappings": {
|
||||
"description": "In some cases, a (starting) contributor might wish to delete a feature even though deletion is not appropriate.\n(The most relevant case are small paths running over private property. These should be marked as 'private' instead of deleted, as the community might trace the path again from aerial imagery, gettting us back to the original situation).\n\nBy adding a 'nonDeleteMapping', an option can be added into the list which will retag the feature.\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
"then": {}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"softDeletionTags": {
|
||||
"description": "In some cases, the contributor is not allowed to delete the current feature (e.g. because it isn't a point, the point is referenced by a relation or the user isn't experienced enough).\nTo still offer the user a 'delete'-option, the feature is retagged with these tags. This is a soft deletion, as the point isn't actually removed from OSM but rather marked as 'disused'\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!\n\nExample (note that \"amenity=\" erases the 'amenity'-key alltogether):\n```\n{\n \"and\": [\"disussed:amenity=public_bookcase\", \"amenity=\"]\n}\n```\n\nor (notice the use of the ':='-tag to copy the old value of 'shop=*' into 'disused:shop='):\n```\n{\n \"and\": [\"disused:shop:={shop}\", \"shop=\"]\n}\n```",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"neededChangesets": {
|
||||
"description": "*\nBy default, the contributor needs 20 previous changesets to delete points edited by others.\nFor 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": "number"
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
91
Docs/Schemas/DeleteConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
export default {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"extraDeleteReasons": {
|
||||
"description": "*\nBy default, three reasons to delete a point are shown:\n\n- The point does not exist anymore\n- The point was a testing point\n- THe point could not be found\n\nHowever, for some layers, there might be different or more specific reasons for deletion which can be user friendly to set, e.g.:\n\n- the shop has closed\n- the climbing route has been closed of for nature conservation reasons\n- ...\n\nThese reasons can be stated here and will be shown in the list of options the user can choose from",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"explanation": {
|
||||
"description": "The text that will be shown to the user - translatable"
|
||||
},
|
||||
"changesetMessage": {
|
||||
"description": "The text that will be uploaded into the changeset or will be used in the fixme in case of a soft deletion\nShould be a few words, in english",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changesetMessage",
|
||||
"explanation"
|
||||
]
|
||||
}
|
||||
},
|
||||
"nonDeleteMappings": {
|
||||
"description": "In some cases, a (starting) contributor might wish to delete a feature even though deletion is not appropriate.\n(The most relevant case are small paths running over private property. These should be marked as 'private' instead of deleted, as the community might trace the path again from aerial imagery, gettting us back to the original situation).\n\nBy adding a 'nonDeleteMapping', an option can be added into the list which will retag the feature.\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
"then": {}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"softDeletionTags": {
|
||||
"description": "In some cases, the contributor is not allowed to delete the current feature (e.g. because it isn't a point, the point is referenced by a relation or the user isn't experienced enough).\nTo still offer the user a 'delete'-option, the feature is retagged with these tags. This is a soft deletion, as the point isn't actually removed from OSM but rather marked as 'disused'\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!\n\nExample (note that \"amenity=\" erases the 'amenity'-key alltogether):\n```\n{\n \"and\": [\"disussed:amenity=public_bookcase\", \"amenity=\"]\n}\n```\n\nor (notice the use of the ':='-tag to copy the old value of 'shop=*' into 'disused:shop='):\n```\n{\n \"and\": [\"disused:shop:={shop}\", \"shop=\"]\n}\n```",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"neededChangesets": {
|
||||
"description": "*\nBy default, the contributor needs 20 previous changesets to delete points edited by others.\nFor 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": "number"
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
72
Docs/Schemas/FilterConfigJson.schema.json
Normal file
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "An id/name for this filter, used to set the URL parameters",
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": {},
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"question"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"options"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
70
Docs/Schemas/FilterConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
export default {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "An id/name for this filter, used to set the URL parameters",
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": {},
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"question"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"options"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
900
Docs/Schemas/LayerConfigJson.schema.json
Normal file
|
@ -0,0 +1,900 @@
|
|||
{
|
||||
"description": "Configuration for a single layer",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of this layer.\nThis should be a simple, lowercase, human readable string that is used to identify the layer.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "The name of this layer\nUsed in the layer control panel and the 'Personal theme'.\n\nIf not given, will be hidden (and thus not toggable) in the layer control"
|
||||
},
|
||||
"description": {
|
||||
"description": "A description for this layer.\nShown in the layer selections and in the personel theme"
|
||||
},
|
||||
"source": {
|
||||
"description": "This determines where the data for the layer is fetched.\nThere are some options:\n\n# Query OSM directly\nsource: {osmTags: \"key=value\"}\n will fetch all objects with given tags from OSM.\n Currently, this will create a query to overpass and fetch the data - in the future this might fetch from the OSM API\n\n# Query OSM Via the overpass API with a custom script\nsource: {overpassScript: \"<custom overpass tags>\"} when you want to do special things. _This should be really rare_.\n This means that the data will be pulled from overpass with this script, and will ignore the osmTags for the query\n However, for the rest of the pipeline, the OsmTags will _still_ be used. This is important to enable layers etc...\n\n\n# A single geojson-file\nsource: {geoJson: \"https://my.source.net/some-geo-data.geojson\"}\n fetches a geojson from a third party source\n\n# A tiled geojson source\nsource: {geoJson: \"https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson\", geoJsonZoomLevel: 14}\n 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\n\nSome API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max}\nSome API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this\n\nNote that both geojson-options might set a flag 'isOsmCache' indicating that the data originally comes from OSM too\n\n\nNOTE: the previous format was 'overpassTags: AndOrTagConfigJson | string', which is interpreted as a shorthand for source: {osmTags: \"key=value\"}\n While still supported, this is considered deprecated",
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"overpassScript": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"osmTags"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"maxCacheAge": {
|
||||
"description": "The maximum amount of seconds that a tile is allowed to linger in the cache",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"geoJson": {
|
||||
"type": "string"
|
||||
},
|
||||
"geoJsonZoomLevel": {
|
||||
"type": "number"
|
||||
},
|
||||
"isOsmCache": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"mercatorCrs": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"geoJson",
|
||||
"osmTags"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"maxCacheAge": {
|
||||
"description": "The maximum amount of seconds that a tile is allowed to linger in the cache",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"calculatedTags": {
|
||||
"description": "A list of extra tags to calculate, specified as \"keyToAssignTo=javascript-expression\".\nThere are a few extra functions available. Refer to <a>Docs/CalculatedTags.md</a> for more information\nThe functions will be run in order, e.g.\n[\n \"_max_overlap_m2=Math.max(...feat.overlapsWith(\"someOtherLayer\").map(o => o.overlap))\n \"_max_overlap_ratio=Number(feat._max_overlap_m2)/feat.area\n]",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"doNotDownload": {
|
||||
"description": "If set, this layer will not query overpass; but it'll still match the tags above which are by chance returned by other layers.\nWorks well together with 'passAllFeatures', to add decoration",
|
||||
"type": "boolean"
|
||||
},
|
||||
"isShown": {
|
||||
"description": "This tag rendering should either be 'yes' or 'no'. If 'no' is returned, then the feature will be hidden from view.\nThis is useful to hide certain features from view.\n\nImportant: hiding features does not work dynamically, but is only calculated when the data is first renders.\nThis implies that it is not possible to hide a feature after a tagging change\n\nThe default value is 'yes'",
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
"minzoom": {
|
||||
"description": "The minimum needed zoomlevel required before loading of the data start\nDefault: 0",
|
||||
"type": "number"
|
||||
},
|
||||
"minzoomVisible": {
|
||||
"description": "The zoom level at which point the data is hidden again\nDefault: 100 (thus: always visible",
|
||||
"type": "number"
|
||||
},
|
||||
"title": {
|
||||
"description": "The title shown in a popup for elements of this layer.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"titleIcons": {
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"mapRendering": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/default_3"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/default_4"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"passAllFeatures": {
|
||||
"description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directionss on cameras",
|
||||
"type": "boolean"
|
||||
},
|
||||
"presets": {
|
||||
"description": "Presets for this layer.\nA preset shows up when clicking the map on a without data (or when right-clicking/long-pressing);\nit will prompt the user to add a new point.\n\nThe most important aspect are the tags, which define which tags the new point will have;\nThe title is shown in the dialog, along with the first sentence of the description.\n\nUpon confirmation, the full description is shown beneath the buttons - perfect to add pictures and examples.\n\nNote: the icon of the preset is determined automatically based on the tags and the icon above. Don't worry about that!\nNB: if no presets are defined, the popup to add new points doesn't show up at all",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"description": "The title - shown on the 'add-new'-button."
|
||||
},
|
||||
"tags": {
|
||||
"description": "The tags to add. It determines the icon too",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"description": "The _first sentence_ of the description is shown on the button of the `add` menu.\nThe full description is shown in the confirmation dialog.\n\n(The first sentence is until the first '.'-character in the description)"
|
||||
},
|
||||
"preciseInput": {
|
||||
"description": "If set, the user will prompted to confirm the location before actually adding the data.\nThis will be with a 'drag crosshair'-method.\n\nIf 'preferredBackgroundCategory' is set, the element will attempt to pick a background layer of that category.",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"preferredBackground": {
|
||||
"description": "The type of background picture",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"snapToLayer": {
|
||||
"description": "If specified, these layers will be shown to and the new point will be snapped towards it",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"maxSnapDistance": {
|
||||
"description": "If specified, a new point will only be snapped if it is within this range.\nDistance in meter\n\nDefault: 10",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredBackground"
|
||||
]
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
true
|
||||
],
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"tags",
|
||||
"title"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"description": "All the tag renderings.\nA tag rendering is a block that either shows the known value or asks a question.\n\nRefer to the class `TagRenderingConfigJson` to see the possibilities.\n\nNote that we can also use a string here - where the string refers to a tag rendering defined in `assets/questions/questions.json`,\nwhere a few very general questions are defined e.g. website, phone number, ...\n\nA 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.\n\nAt last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings.\nThis is mainly create questions for a 'left' and a 'right' side of the road.\nThese will be grouped and questions will be asked together",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"builtin": {
|
||||
"type": "string"
|
||||
},
|
||||
"override": {}
|
||||
},
|
||||
"required": [
|
||||
"builtin",
|
||||
"override"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"rewrite": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sourceString": {
|
||||
"type": "string"
|
||||
},
|
||||
"into": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"into",
|
||||
"sourceString"
|
||||
]
|
||||
}
|
||||
},
|
||||
"renderings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"builtin": {
|
||||
"type": "string"
|
||||
},
|
||||
"override": {}
|
||||
},
|
||||
"required": [
|
||||
"builtin",
|
||||
"override"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"renderings",
|
||||
"rewrite"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"filter": {
|
||||
"description": "All the extra questions for filtering",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/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\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 from OSM 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.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DeleteConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"allowMove": {
|
||||
"description": "Indicates if a point can be moved and configures the modalities.\n\nA feature can be moved by MapComplete if:\n\n- It is a point\n- The point is _not_ part of a way or a a relation.\n\nOff by default. Can be enabled by setting this flag or by configuring.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/default_2"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"allowSplit": {
|
||||
"description": "IF set, a 'split this road' button is shown",
|
||||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/default_1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"mapRendering",
|
||||
"source"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default_3": {
|
||||
"description": "The PointRenderingConfig gives all details onto how to render a single point of a feature.\n\nThis can be used if:\n\n- The feature is a point\n- To render something at the centroid of an area, or at the start, end or projected centroid of a way",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"description": "All the locations that this point should be rendered at.\nUsing `location: [\"point\", \"centroid\"] will always render centerpoint",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [
|
||||
"centroid",
|
||||
"end",
|
||||
"point",
|
||||
"start"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"icon": {
|
||||
"description": "The icon for an element.\nNote that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.\n\nThe result of the icon is rendered as follows:\nthe resulting string is interpreted as a _list_ of items, separated by \";\". The bottommost layer is the first layer.\nAs a result, on could use a generic pin, then overlay it with a specific icon.\nTo make things even more practical, one can use all SVG's from the folder \"assets/svg\" and _substitute the color_ in it.\nE.g. to draw a red pin, use \"pin:#f00\", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconBadges": {
|
||||
"description": "A list of extra badges to show next to the icon as small badge\nThey will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.\n\nNote: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"iconSize": {
|
||||
"description": "A string containing \"width,height\" or \"width,height,anchorpoint\" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...\nDefault is '40,40,center'",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rotation": {
|
||||
"description": "The rotation of an icon, useful for e.g. directions.\nUsage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"label": {
|
||||
"description": "A HTML-fragment that is shown below the icon, for example:\n<div style=\"background: white; display: block\">{name}</div>\n\nIf the icon is undefined, then the label is shown in the center of the feature.\nNote that, if the wayhandling hides the icon then no label is shown as well.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default_4": {
|
||||
"description": "The LineRenderingConfig gives all details onto how to render a single line of a feature.\n\nThis can be used if:\n\n- The feature is a line\n- The feature is an area",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"description": "The color for way-elements and SVG-elements.\nIf the value starts with \"--\", the style of the body element will be queried for the corresponding variable instead",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"width": {
|
||||
"description": "The stroke-width for way-elements",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dashArray": {
|
||||
"description": "A dasharray, e.g. \"5 6\"\nThe dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap',\nDefault value: \"\" (empty string == full line)",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lineCap": {
|
||||
"description": "The form at the end of a line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fill": {
|
||||
"description": "Wehter or not to fill polygons",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"no",
|
||||
"yes"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fillColor": {
|
||||
"description": "The color to fill a polygon with.\nIf undefined, this will be slightly more opaque version of the stroke line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": {
|
||||
"description": "The number of pixels this line should be moved.\nUse a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line).\n\nIMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')\nThis simplifies programming. Refer to the CalculatedTags.md-documentation for more details",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "An id/name for this filter, used to set the URL parameters",
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": {},
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"question"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DeleteConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"extraDeleteReasons": {
|
||||
"description": "*\nBy default, three reasons to delete a point are shown:\n\n- The point does not exist anymore\n- The point was a testing point\n- THe point could not be found\n\nHowever, for some layers, there might be different or more specific reasons for deletion which can be user friendly to set, e.g.:\n\n- the shop has closed\n- the climbing route has been closed of for nature conservation reasons\n- ...\n\nThese reasons can be stated here and will be shown in the list of options the user can choose from",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"explanation": {
|
||||
"description": "The text that will be shown to the user - translatable"
|
||||
},
|
||||
"changesetMessage": {
|
||||
"description": "The text that will be uploaded into the changeset or will be used in the fixme in case of a soft deletion\nShould be a few words, in english",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changesetMessage",
|
||||
"explanation"
|
||||
]
|
||||
}
|
||||
},
|
||||
"nonDeleteMappings": {
|
||||
"description": "In some cases, a (starting) contributor might wish to delete a feature even though deletion is not appropriate.\n(The most relevant case are small paths running over private property. These should be marked as 'private' instead of deleted, as the community might trace the path again from aerial imagery, gettting us back to the original situation).\n\nBy adding a 'nonDeleteMapping', an option can be added into the list which will retag the feature.\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
"then": {}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"softDeletionTags": {
|
||||
"description": "In some cases, the contributor is not allowed to delete the current feature (e.g. because it isn't a point, the point is referenced by a relation or the user isn't experienced enough).\nTo still offer the user a 'delete'-option, the feature is retagged with these tags. This is a soft deletion, as the point isn't actually removed from OSM but rather marked as 'disused'\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!\n\nExample (note that \"amenity=\" erases the 'amenity'-key alltogether):\n```\n{\n \"and\": [\"disussed:amenity=public_bookcase\", \"amenity=\"]\n}\n```\n\nor (notice the use of the ':='-tag to copy the old value of 'shop=*' into 'disused:shop='):\n```\n{\n \"and\": [\"disused:shop:={shop}\", \"shop=\"]\n}\n```",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"neededChangesets": {
|
||||
"description": "*\nBy default, the contributor needs 20 previous changesets to delete points edited by others.\nFor 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": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default_2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enableImproveAccuracy": {
|
||||
"description": "One default reason to move a point is to improve accuracy.\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableRelocation": {
|
||||
"description": "One default reason to move a point is because it has relocated\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default_1": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
"description": "Every key from this list will be normalized",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"eraseInvalidValues": {
|
||||
"description": "If set, invalid values will be erased in the MC application (but not in OSM of course!)\nBe careful with setting this",
|
||||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApplicableUnitJson"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"applicableUnits",
|
||||
"appliesToKey"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
890
Docs/Schemas/LayerConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,890 @@
|
|||
export default {
|
||||
"description": "Configuration for a single layer",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of this layer.\nThis should be a simple, lowercase, human readable string that is used to identify the layer.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "The name of this layer\nUsed in the layer control panel and the 'Personal theme'.\n\nIf not given, will be hidden (and thus not toggable) in the layer control"
|
||||
},
|
||||
"description": {
|
||||
"description": "A description for this layer.\nShown in the layer selections and in the personel theme"
|
||||
},
|
||||
"source": {
|
||||
"description": "This determines where the data for the layer is fetched.\nThere are some options:\n\n# Query OSM directly\nsource: {osmTags: \"key=value\"}\n will fetch all objects with given tags from OSM.\n Currently, this will create a query to overpass and fetch the data - in the future this might fetch from the OSM API\n\n# Query OSM Via the overpass API with a custom script\nsource: {overpassScript: \"<custom overpass tags>\"} when you want to do special things. _This should be really rare_.\n This means that the data will be pulled from overpass with this script, and will ignore the osmTags for the query\n However, for the rest of the pipeline, the OsmTags will _still_ be used. This is important to enable layers etc...\n\n\n# A single geojson-file\nsource: {geoJson: \"https://my.source.net/some-geo-data.geojson\"}\n fetches a geojson from a third party source\n\n# A tiled geojson source\nsource: {geoJson: \"https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson\", geoJsonZoomLevel: 14}\n 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\n\nSome API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max}\nSome API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this\n\nNote that both geojson-options might set a flag 'isOsmCache' indicating that the data originally comes from OSM too\n\n\nNOTE: the previous format was 'overpassTags: AndOrTagConfigJson | string', which is interpreted as a shorthand for source: {osmTags: \"key=value\"}\n While still supported, this is considered deprecated",
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"overpassScript": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"osmTags"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"maxCacheAge": {
|
||||
"description": "The maximum amount of seconds that a tile is allowed to linger in the cache",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"geoJson": {
|
||||
"type": "string"
|
||||
},
|
||||
"geoJsonZoomLevel": {
|
||||
"type": "number"
|
||||
},
|
||||
"isOsmCache": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"mercatorCrs": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"geoJson",
|
||||
"osmTags"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"maxCacheAge": {
|
||||
"description": "The maximum amount of seconds that a tile is allowed to linger in the cache",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"calculatedTags": {
|
||||
"description": "A list of extra tags to calculate, specified as \"keyToAssignTo=javascript-expression\".\nThere are a few extra functions available. Refer to <a>Docs/CalculatedTags.md</a> for more information\nThe functions will be run in order, e.g.\n[\n \"_max_overlap_m2=Math.max(...feat.overlapsWith(\"someOtherLayer\").map(o => o.overlap))\n \"_max_overlap_ratio=Number(feat._max_overlap_m2)/feat.area\n]",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"doNotDownload": {
|
||||
"description": "If set, this layer will not query overpass; but it'll still match the tags above which are by chance returned by other layers.\nWorks well together with 'passAllFeatures', to add decoration",
|
||||
"type": "boolean"
|
||||
},
|
||||
"isShown": {
|
||||
"description": "This tag rendering should either be 'yes' or 'no'. If 'no' is returned, then the feature will be hidden from view.\nThis is useful to hide certain features from view.\n\nImportant: hiding features does not work dynamically, but is only calculated when the data is first renders.\nThis implies that it is not possible to hide a feature after a tagging change\n\nThe default value is 'yes'",
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
"minzoom": {
|
||||
"description": "The minimum needed zoomlevel required before loading of the data start\nDefault: 0",
|
||||
"type": "number"
|
||||
},
|
||||
"minzoomVisible": {
|
||||
"description": "The zoom level at which point the data is hidden again\nDefault: 100 (thus: always visible",
|
||||
"type": "number"
|
||||
},
|
||||
"title": {
|
||||
"description": "The title shown in a popup for elements of this layer.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"titleIcons": {
|
||||
"description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"mapRendering": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/default_3"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/default_4"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"passAllFeatures": {
|
||||
"description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directionss on cameras",
|
||||
"type": "boolean"
|
||||
},
|
||||
"presets": {
|
||||
"description": "Presets for this layer.\nA preset shows up when clicking the map on a without data (or when right-clicking/long-pressing);\nit will prompt the user to add a new point.\n\nThe most important aspect are the tags, which define which tags the new point will have;\nThe title is shown in the dialog, along with the first sentence of the description.\n\nUpon confirmation, the full description is shown beneath the buttons - perfect to add pictures and examples.\n\nNote: the icon of the preset is determined automatically based on the tags and the icon above. Don't worry about that!\nNB: if no presets are defined, the popup to add new points doesn't show up at all",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"description": "The title - shown on the 'add-new'-button."
|
||||
},
|
||||
"tags": {
|
||||
"description": "The tags to add. It determines the icon too",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"description": "The _first sentence_ of the description is shown on the button of the `add` menu.\nThe full description is shown in the confirmation dialog.\n\n(The first sentence is until the first '.'-character in the description)"
|
||||
},
|
||||
"preciseInput": {
|
||||
"description": "If set, the user will prompted to confirm the location before actually adding the data.\nThis will be with a 'drag crosshair'-method.\n\nIf 'preferredBackgroundCategory' is set, the element will attempt to pick a background layer of that category.",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"preferredBackground": {
|
||||
"description": "The type of background picture",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"snapToLayer": {
|
||||
"description": "If specified, these layers will be shown to and the new point will be snapped towards it",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"maxSnapDistance": {
|
||||
"description": "If specified, a new point will only be snapped if it is within this range.\nDistance in meter\n\nDefault: 10",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"preferredBackground"
|
||||
]
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
true
|
||||
],
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"tags",
|
||||
"title"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"description": "All the tag renderings.\nA tag rendering is a block that either shows the known value or asks a question.\n\nRefer to the class `TagRenderingConfigJson` to see the possibilities.\n\nNote that we can also use a string here - where the string refers to a tag rendering defined in `assets/questions/questions.json`,\nwhere a few very general questions are defined e.g. website, phone number, ...\n\nA 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.\n\nAt last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings.\nThis is mainly create questions for a 'left' and a 'right' side of the road.\nThese will be grouped and questions will be asked together",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"builtin": {
|
||||
"type": "string"
|
||||
},
|
||||
"override": {}
|
||||
},
|
||||
"required": [
|
||||
"builtin",
|
||||
"override"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"rewrite": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sourceString": {
|
||||
"type": "string"
|
||||
},
|
||||
"into": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"into",
|
||||
"sourceString"
|
||||
]
|
||||
}
|
||||
},
|
||||
"renderings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"builtin": {
|
||||
"type": "string"
|
||||
},
|
||||
"override": {}
|
||||
},
|
||||
"required": [
|
||||
"builtin",
|
||||
"override"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"renderings",
|
||||
"rewrite"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"filter": {
|
||||
"description": "All the extra questions for filtering",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/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\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 from OSM 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.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DeleteConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"allowMove": {
|
||||
"description": "Indicates if a point can be moved and configures the modalities.\n\nA feature can be moved by MapComplete if:\n\n- It is a point\n- The point is _not_ part of a way or a a relation.\n\nOff by default. Can be enabled by setting this flag or by configuring.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/default_2"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"allowSplit": {
|
||||
"description": "IF set, a 'split this road' button is shown",
|
||||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/default_1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"mapRendering",
|
||||
"source"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default_3": {
|
||||
"description": "The PointRenderingConfig gives all details onto how to render a single point of a feature.\n\nThis can be used if:\n\n- The feature is a point\n- To render something at the centroid of an area, or at the start, end or projected centroid of a way",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"description": "All the locations that this point should be rendered at.\nUsing `location: [\"point\", \"centroid\"] will always render centerpoint",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [
|
||||
"centroid",
|
||||
"end",
|
||||
"point",
|
||||
"start"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"icon": {
|
||||
"description": "The icon for an element.\nNote that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.\n\nThe result of the icon is rendered as follows:\nthe resulting string is interpreted as a _list_ of items, separated by \";\". The bottommost layer is the first layer.\nAs a result, on could use a generic pin, then overlay it with a specific icon.\nTo make things even more practical, one can use all SVG's from the folder \"assets/svg\" and _substitute the color_ in it.\nE.g. to draw a red pin, use \"pin:#f00\", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconBadges": {
|
||||
"description": "A list of extra badges to show next to the icon as small badge\nThey will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.\n\nNote: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"iconSize": {
|
||||
"description": "A string containing \"width,height\" or \"width,height,anchorpoint\" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...\nDefault is '40,40,center'",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rotation": {
|
||||
"description": "The rotation of an icon, useful for e.g. directions.\nUsage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"label": {
|
||||
"description": "A HTML-fragment that is shown below the icon, for example:\n<div style=\"background: white; display: block\">{name}</div>\n\nIf the icon is undefined, then the label is shown in the center of the feature.\nNote that, if the wayhandling hides the icon then no label is shown as well.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
]
|
||||
},
|
||||
"default_4": {
|
||||
"description": "The LineRenderingConfig gives all details onto how to render a single line of a feature.\n\nThis can be used if:\n\n- The feature is a line\n- The feature is an area",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"description": "The color for way-elements and SVG-elements.\nIf the value starts with \"--\", the style of the body element will be queried for the corresponding variable instead",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"width": {
|
||||
"description": "The stroke-width for way-elements",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dashArray": {
|
||||
"description": "A dasharray, e.g. \"5 6\"\nThe dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap',\nDefault value: \"\" (empty string == full line)",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lineCap": {
|
||||
"description": "The form at the end of a line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fill": {
|
||||
"description": "Wehter or not to fill polygons",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"no",
|
||||
"yes"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fillColor": {
|
||||
"description": "The color to fill a polygon with.\nIf undefined, this will be slightly more opaque version of the stroke line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": {
|
||||
"description": "The number of pixels this line should be moved.\nUse a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line).\n\nIMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')\nThis simplifies programming. Refer to the CalculatedTags.md-documentation for more details",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "An id/name for this filter, used to set the URL parameters",
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": {},
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"question"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"options"
|
||||
]
|
||||
},
|
||||
"DeleteConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"extraDeleteReasons": {
|
||||
"description": "*\nBy default, three reasons to delete a point are shown:\n\n- The point does not exist anymore\n- The point was a testing point\n- THe point could not be found\n\nHowever, for some layers, there might be different or more specific reasons for deletion which can be user friendly to set, e.g.:\n\n- the shop has closed\n- the climbing route has been closed of for nature conservation reasons\n- ...\n\nThese reasons can be stated here and will be shown in the list of options the user can choose from",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"explanation": {
|
||||
"description": "The text that will be shown to the user - translatable"
|
||||
},
|
||||
"changesetMessage": {
|
||||
"description": "The text that will be uploaded into the changeset or will be used in the fixme in case of a soft deletion\nShould be a few words, in english",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changesetMessage",
|
||||
"explanation"
|
||||
]
|
||||
}
|
||||
},
|
||||
"nonDeleteMappings": {
|
||||
"description": "In some cases, a (starting) contributor might wish to delete a feature even though deletion is not appropriate.\n(The most relevant case are small paths running over private property. These should be marked as 'private' instead of deleted, as the community might trace the path again from aerial imagery, gettting us back to the original situation).\n\nBy adding a 'nonDeleteMapping', an option can be added into the list which will retag the feature.\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
"then": {}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"softDeletionTags": {
|
||||
"description": "In some cases, the contributor is not allowed to delete the current feature (e.g. because it isn't a point, the point is referenced by a relation or the user isn't experienced enough).\nTo still offer the user a 'delete'-option, the feature is retagged with these tags. This is a soft deletion, as the point isn't actually removed from OSM but rather marked as 'disused'\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!\n\nExample (note that \"amenity=\" erases the 'amenity'-key alltogether):\n```\n{\n \"and\": [\"disussed:amenity=public_bookcase\", \"amenity=\"]\n}\n```\n\nor (notice the use of the ':='-tag to copy the old value of 'shop=*' into 'disused:shop='):\n```\n{\n \"and\": [\"disused:shop:={shop}\", \"shop=\"]\n}\n```",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"neededChangesets": {
|
||||
"description": "*\nBy default, the contributor needs 20 previous changesets to delete points edited by others.\nFor 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": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"default_2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enableImproveAccuracy": {
|
||||
"description": "One default reason to move a point is to improve accuracy.\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableRelocation": {
|
||||
"description": "One default reason to move a point is because it has relocated\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"default_1": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
"description": "Every key from this list will be normalized",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"eraseInvalidValues": {
|
||||
"description": "If set, invalid values will be erased in the MC application (but not in OSM of course!)\nBe careful with setting this",
|
||||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApplicableUnitJson"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"applicableUnits",
|
||||
"appliesToKey"
|
||||
]
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
1215
Docs/Schemas/LayoutConfigJson.schema.json
Normal file
1203
Docs/Schemas/LayoutConfigJsonJSC.ts
Normal file
293
Docs/Schemas/LineRenderingConfigJson.schema.json
Normal file
|
@ -0,0 +1,293 @@
|
|||
{
|
||||
"description": "The LineRenderingConfig gives all details onto how to render a single line of a feature.\n\nThis can be used if:\n\n- The feature is a line\n- The feature is an area",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"description": "The color for way-elements and SVG-elements.\nIf the value starts with \"--\", the style of the body element will be queried for the corresponding variable instead",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"width": {
|
||||
"description": "The stroke-width for way-elements",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dashArray": {
|
||||
"description": "A dasharray, e.g. \"5 6\"\nThe dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap',\nDefault value: \"\" (empty string == full line)",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lineCap": {
|
||||
"description": "The form at the end of a line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fill": {
|
||||
"description": "Wehter or not to fill polygons",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"no",
|
||||
"yes"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fillColor": {
|
||||
"description": "The color to fill a polygon with.\nIf undefined, this will be slightly more opaque version of the stroke line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": {
|
||||
"description": "The number of pixels this line should be moved.\nUse a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line).\n\nIMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')\nThis simplifies programming. Refer to the CalculatedTags.md-documentation for more details",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
289
Docs/Schemas/LineRenderingConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,289 @@
|
|||
export default {
|
||||
"description": "The LineRenderingConfig gives all details onto how to render a single line of a feature.\n\nThis can be used if:\n\n- The feature is a line\n- The feature is an area",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"description": "The color for way-elements and SVG-elements.\nIf the value starts with \"--\", the style of the body element will be queried for the corresponding variable instead",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"width": {
|
||||
"description": "The stroke-width for way-elements",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dashArray": {
|
||||
"description": "A dasharray, e.g. \"5 6\"\nThe dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap',\nDefault value: \"\" (empty string == full line)",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lineCap": {
|
||||
"description": "The form at the end of a line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fill": {
|
||||
"description": "Wehter or not to fill polygons",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"no",
|
||||
"yes"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fillColor": {
|
||||
"description": "The color to fill a polygon with.\nIf undefined, this will be slightly more opaque version of the stroke line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": {
|
||||
"description": "The number of pixels this line should be moved.\nUse a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line).\n\nIMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')\nThis simplifies programming. Refer to the CalculatedTags.md-documentation for more details",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
87
Docs/Schemas/MoveConfigJson.schema.json
Normal file
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enableImproveAccuracy": {
|
||||
"description": "One default reason to move a point is to improve accuracy.\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableRelocation": {
|
||||
"description": "One default reason to move a point is because it has relocated\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
84
Docs/Schemas/MoveConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
export default {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enableImproveAccuracy": {
|
||||
"description": "One default reason to move a point is to improve accuracy.\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableRelocation": {
|
||||
"description": "One default reason to move a point is because it has relocated\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
]
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
305
Docs/Schemas/PointRenderingConfigJson.schema.json
Normal file
|
@ -0,0 +1,305 @@
|
|||
{
|
||||
"description": "The PointRenderingConfig gives all details onto how to render a single point of a feature.\n\nThis can be used if:\n\n- The feature is a point\n- To render something at the centroid of an area, or at the start, end or projected centroid of a way",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"description": "All the locations that this point should be rendered at.\nUsing `location: [\"point\", \"centroid\"] will always render centerpoint",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [
|
||||
"centroid",
|
||||
"end",
|
||||
"point",
|
||||
"start"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"icon": {
|
||||
"description": "The icon for an element.\nNote that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.\n\nThe result of the icon is rendered as follows:\nthe resulting string is interpreted as a _list_ of items, separated by \";\". The bottommost layer is the first layer.\nAs a result, on could use a generic pin, then overlay it with a specific icon.\nTo make things even more practical, one can use all SVG's from the folder \"assets/svg\" and _substitute the color_ in it.\nE.g. to draw a red pin, use \"pin:#f00\", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconBadges": {
|
||||
"description": "A list of extra badges to show next to the icon as small badge\nThey will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.\n\nNote: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"iconSize": {
|
||||
"description": "A string containing \"width,height\" or \"width,height,anchorpoint\" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...\nDefault is '40,40,center'",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rotation": {
|
||||
"description": "The rotation of an icon, useful for e.g. directions.\nUsage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"label": {
|
||||
"description": "A HTML-fragment that is shown below the icon, for example:\n<div style=\"background: white; display: block\">{name}</div>\n\nIf the icon is undefined, then the label is shown in the center of the feature.\nNote that, if the wayhandling hides the icon then no label is shown as well.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
301
Docs/Schemas/PointRenderingConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,301 @@
|
|||
export default {
|
||||
"description": "The PointRenderingConfig gives all details onto how to render a single point of a feature.\n\nThis can be used if:\n\n- The feature is a point\n- To render something at the centroid of an area, or at the start, end or projected centroid of a way",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"description": "All the locations that this point should be rendered at.\nUsing `location: [\"point\", \"centroid\"] will always render centerpoint",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [
|
||||
"centroid",
|
||||
"end",
|
||||
"point",
|
||||
"start"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"icon": {
|
||||
"description": "The icon for an element.\nNote that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.\n\nThe result of the icon is rendered as follows:\nthe resulting string is interpreted as a _list_ of items, separated by \";\". The bottommost layer is the first layer.\nAs a result, on could use a generic pin, then overlay it with a specific icon.\nTo make things even more practical, one can use all SVG's from the folder \"assets/svg\" and _substitute the color_ in it.\nE.g. to draw a red pin, use \"pin:#f00\", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconBadges": {
|
||||
"description": "A list of extra badges to show next to the icon as small badge\nThey will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.\n\nNote: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"iconSize": {
|
||||
"description": "A string containing \"width,height\" or \"width,height,anchorpoint\" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...\nDefault is '40,40,center'",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rotation": {
|
||||
"description": "The rotation of an icon, useful for e.g. directions.\nUsage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"label": {
|
||||
"description": "A HTML-fragment that is shown below the icon, for example:\n<div style=\"background: white; display: block\">{name}</div>\n\nIf the icon is undefined, then the label is shown in the center of the feature.\nNote that, if the wayhandling hides the icon then no label is shown as well.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
167
Docs/Schemas/TagRenderingConfigJson.schema.json
Normal file
|
@ -0,0 +1,167 @@
|
|||
{
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
165
Docs/Schemas/TagRenderingConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,165 @@
|
|||
export default {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
569
Docs/Schemas/TilesourceConfigJson.schema.json
Normal file
|
@ -0,0 +1,569 @@
|
|||
{
|
||||
"description": "Configuration for a tilesource config",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Id of this overlay, used in the URL-parameters to set the state",
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"description": "The path, where {x}, {y} and {z} will be substituted",
|
||||
"type": "string"
|
||||
},
|
||||
"isOverlay": {
|
||||
"description": "Wether or not this is an overlay. Default: true",
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"description": "How this will be shown in the selection menu.\nMake undefined if this may not be toggled"
|
||||
},
|
||||
"minZoom": {
|
||||
"description": "Only visible at this or a higher zoom level",
|
||||
"type": "number"
|
||||
},
|
||||
"maxZoom": {
|
||||
"description": "Only visible at this or a lower zoom level",
|
||||
"type": "number"
|
||||
},
|
||||
"defaultState": {
|
||||
"description": "The default state, set to false to hide by default",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"defaultState",
|
||||
"id",
|
||||
"source"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default_3": {
|
||||
"description": "The PointRenderingConfig gives all details onto how to render a single point of a feature.\n\nThis can be used if:\n\n- The feature is a point\n- To render something at the centroid of an area, or at the start, end or projected centroid of a way",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"description": "All the locations that this point should be rendered at.\nUsing `location: [\"point\", \"centroid\"] will always render centerpoint",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [
|
||||
"centroid",
|
||||
"end",
|
||||
"point",
|
||||
"start"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"icon": {
|
||||
"description": "The icon for an element.\nNote that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.\n\nThe result of the icon is rendered as follows:\nthe resulting string is interpreted as a _list_ of items, separated by \";\". The bottommost layer is the first layer.\nAs a result, on could use a generic pin, then overlay it with a specific icon.\nTo make things even more practical, one can use all SVG's from the folder \"assets/svg\" and _substitute the color_ in it.\nE.g. to draw a red pin, use \"pin:#f00\", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconBadges": {
|
||||
"description": "A list of extra badges to show next to the icon as small badge\nThey will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.\n\nNote: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"iconSize": {
|
||||
"description": "A string containing \"width,height\" or \"width,height,anchorpoint\" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...\nDefault is '40,40,center'",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rotation": {
|
||||
"description": "The rotation of an icon, useful for e.g. directions.\nUsage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"label": {
|
||||
"description": "A HTML-fragment that is shown below the icon, for example:\n<div style=\"background: white; display: block\">{name}</div>\n\nIf the icon is undefined, then the label is shown in the center of the feature.\nNote that, if the wayhandling hides the icon then no label is shown as well.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default_4": {
|
||||
"description": "The LineRenderingConfig gives all details onto how to render a single line of a feature.\n\nThis can be used if:\n\n- The feature is a line\n- The feature is an area",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"description": "The color for way-elements and SVG-elements.\nIf the value starts with \"--\", the style of the body element will be queried for the corresponding variable instead",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"width": {
|
||||
"description": "The stroke-width for way-elements",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dashArray": {
|
||||
"description": "A dasharray, e.g. \"5 6\"\nThe dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap',\nDefault value: \"\" (empty string == full line)",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lineCap": {
|
||||
"description": "The form at the end of a line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fill": {
|
||||
"description": "Wehter or not to fill polygons",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"no",
|
||||
"yes"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fillColor": {
|
||||
"description": "The color to fill a polygon with.\nIf undefined, this will be slightly more opaque version of the stroke line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": {
|
||||
"description": "The number of pixels this line should be moved.\nUse a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line).\n\nIMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')\nThis simplifies programming. Refer to the CalculatedTags.md-documentation for more details",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "An id/name for this filter, used to set the URL parameters",
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": {},
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"question"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DeleteConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"extraDeleteReasons": {
|
||||
"description": "*\nBy default, three reasons to delete a point are shown:\n\n- The point does not exist anymore\n- The point was a testing point\n- THe point could not be found\n\nHowever, for some layers, there might be different or more specific reasons for deletion which can be user friendly to set, e.g.:\n\n- the shop has closed\n- the climbing route has been closed of for nature conservation reasons\n- ...\n\nThese reasons can be stated here and will be shown in the list of options the user can choose from",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"explanation": {
|
||||
"description": "The text that will be shown to the user - translatable"
|
||||
},
|
||||
"changesetMessage": {
|
||||
"description": "The text that will be uploaded into the changeset or will be used in the fixme in case of a soft deletion\nShould be a few words, in english",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changesetMessage",
|
||||
"explanation"
|
||||
]
|
||||
}
|
||||
},
|
||||
"nonDeleteMappings": {
|
||||
"description": "In some cases, a (starting) contributor might wish to delete a feature even though deletion is not appropriate.\n(The most relevant case are small paths running over private property. These should be marked as 'private' instead of deleted, as the community might trace the path again from aerial imagery, gettting us back to the original situation).\n\nBy adding a 'nonDeleteMapping', an option can be added into the list which will retag the feature.\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
"then": {}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"softDeletionTags": {
|
||||
"description": "In some cases, the contributor is not allowed to delete the current feature (e.g. because it isn't a point, the point is referenced by a relation or the user isn't experienced enough).\nTo still offer the user a 'delete'-option, the feature is retagged with these tags. This is a soft deletion, as the point isn't actually removed from OSM but rather marked as 'disused'\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!\n\nExample (note that \"amenity=\" erases the 'amenity'-key alltogether):\n```\n{\n \"and\": [\"disussed:amenity=public_bookcase\", \"amenity=\"]\n}\n```\n\nor (notice the use of the ':='-tag to copy the old value of 'shop=*' into 'disused:shop='):\n```\n{\n \"and\": [\"disused:shop:={shop}\", \"shop=\"]\n}\n```",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"neededChangesets": {
|
||||
"description": "*\nBy default, the contributor needs 20 previous changesets to delete points edited by others.\nFor 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": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default_2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enableImproveAccuracy": {
|
||||
"description": "One default reason to move a point is to improve accuracy.\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableRelocation": {
|
||||
"description": "One default reason to move a point is because it has relocated\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default_1": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
"description": "Every key from this list will be normalized",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"eraseInvalidValues": {
|
||||
"description": "If set, invalid values will be erased in the MC application (but not in OSM of course!)\nBe careful with setting this",
|
||||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApplicableUnitJson"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"applicableUnits",
|
||||
"appliesToKey"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
559
Docs/Schemas/TilesourceConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,559 @@
|
|||
export default {
|
||||
"description": "Configuration for a tilesource config",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Id of this overlay, used in the URL-parameters to set the state",
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"description": "The path, where {x}, {y} and {z} will be substituted",
|
||||
"type": "string"
|
||||
},
|
||||
"isOverlay": {
|
||||
"description": "Wether or not this is an overlay. Default: true",
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"description": "How this will be shown in the selection menu.\nMake undefined if this may not be toggled"
|
||||
},
|
||||
"minZoom": {
|
||||
"description": "Only visible at this or a higher zoom level",
|
||||
"type": "number"
|
||||
},
|
||||
"maxZoom": {
|
||||
"description": "Only visible at this or a lower zoom level",
|
||||
"type": "number"
|
||||
},
|
||||
"defaultState": {
|
||||
"description": "The default state, set to false to hide by default",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"defaultState",
|
||||
"id",
|
||||
"source"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
]
|
||||
},
|
||||
"TagRenderingConfigJson": {
|
||||
"description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The id of the tagrendering, should be an unique string.\nUsed to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise",
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well.\nThe first tagRendering of a group will always be a sticky element.",
|
||||
"type": "string"
|
||||
},
|
||||
"render": {
|
||||
"description": "Renders this value. Note that \"{key}\"-parts are substituted by the corresponding values of the element.\nIf neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`"
|
||||
},
|
||||
"question": {
|
||||
"description": "If it turns out that this tagRendering doesn't match _any_ value, then we show this question.\nIf undefined, the question is never asked and this tagrendering is read-only"
|
||||
},
|
||||
"condition": {
|
||||
"description": "Only show this question if the object also matches the following tags.\n\nThis is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables...",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"freeform": {
|
||||
"description": "Allow freeform text input from the user",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"description": "If this key is present, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values",
|
||||
"type": "string"
|
||||
},
|
||||
"helperArgs": {
|
||||
"description": "Extra parameters to initialize the input helper arguments.\nFor semantics, see the 'SpecialInputElements.md'",
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set, influences the way a question is asked.\nInstead of showing a full-widht 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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "default value to enter if no previous tagging is present.\nNormally undefined (aka do not enter anything)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
},
|
||||
"multiAnswer": {
|
||||
"description": "If true, use checkboxes instead of radio buttons when asking the question",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mappings": {
|
||||
"description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"description": "If 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}'}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option"
|
||||
},
|
||||
"hideInAnswer": {
|
||||
"description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"string",
|
||||
"boolean"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ifnot": {
|
||||
"description": "Only applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addExtraTags": {
|
||||
"description": "If chosen as answer, these tags will be applied as well onto the object.\nNot compatible with multiAnswer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default_3": {
|
||||
"description": "The PointRenderingConfig gives all details onto how to render a single point of a feature.\n\nThis can be used if:\n\n- The feature is a point\n- To render something at the centroid of an area, or at the start, end or projected centroid of a way",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"description": "All the locations that this point should be rendered at.\nUsing `location: [\"point\", \"centroid\"] will always render centerpoint",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [
|
||||
"centroid",
|
||||
"end",
|
||||
"point",
|
||||
"start"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"icon": {
|
||||
"description": "The icon for an element.\nNote that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.\n\nThe result of the icon is rendered as follows:\nthe resulting string is interpreted as a _list_ of items, separated by \";\". The bottommost layer is the first layer.\nAs a result, on could use a generic pin, then overlay it with a specific icon.\nTo make things even more practical, one can use all SVG's from the folder \"assets/svg\" and _substitute the color_ in it.\nE.g. to draw a red pin, use \"pin:#f00\", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconBadges": {
|
||||
"description": "A list of extra badges to show next to the icon as small badge\nThey will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.\n\nNote: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"iconSize": {
|
||||
"description": "A string containing \"width,height\" or \"width,height,anchorpoint\" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...\nDefault is '40,40,center'",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rotation": {
|
||||
"description": "The rotation of an icon, useful for e.g. directions.\nUsage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"label": {
|
||||
"description": "A HTML-fragment that is shown below the icon, for example:\n<div style=\"background: white; display: block\">{name}</div>\n\nIf the icon is undefined, then the label is shown in the center of the feature.\nNote that, if the wayhandling hides the icon then no label is shown as well.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
]
|
||||
},
|
||||
"default_4": {
|
||||
"description": "The LineRenderingConfig gives all details onto how to render a single line of a feature.\n\nThis can be used if:\n\n- The feature is a line\n- The feature is an area",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"description": "The color for way-elements and SVG-elements.\nIf the value starts with \"--\", the style of the body element will be queried for the corresponding variable instead",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"width": {
|
||||
"description": "The stroke-width for way-elements",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dashArray": {
|
||||
"description": "A dasharray, e.g. \"5 6\"\nThe dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap',\nDefault value: \"\" (empty string == full line)",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lineCap": {
|
||||
"description": "The form at the end of a line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fill": {
|
||||
"description": "Wehter or not to fill polygons",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"no",
|
||||
"yes"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fillColor": {
|
||||
"description": "The color to fill a polygon with.\nIf undefined, this will be slightly more opaque version of the stroke line",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": {
|
||||
"description": "The number of pixels this line should be moved.\nUse a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line).\n\nIMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')\nThis simplifies programming. Refer to the CalculatedTags.md-documentation for more details",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TagRenderingConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "An id/name for this filter, used to set the URL parameters",
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": {},
|
||||
"osmTags": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"question"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"options"
|
||||
]
|
||||
},
|
||||
"DeleteConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"extraDeleteReasons": {
|
||||
"description": "*\nBy default, three reasons to delete a point are shown:\n\n- The point does not exist anymore\n- The point was a testing point\n- THe point could not be found\n\nHowever, for some layers, there might be different or more specific reasons for deletion which can be user friendly to set, e.g.:\n\n- the shop has closed\n- the climbing route has been closed of for nature conservation reasons\n- ...\n\nThese reasons can be stated here and will be shown in the list of options the user can choose from",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"explanation": {
|
||||
"description": "The text that will be shown to the user - translatable"
|
||||
},
|
||||
"changesetMessage": {
|
||||
"description": "The text that will be uploaded into the changeset or will be used in the fixme in case of a soft deletion\nShould be a few words, in english",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"changesetMessage",
|
||||
"explanation"
|
||||
]
|
||||
}
|
||||
},
|
||||
"nonDeleteMappings": {
|
||||
"description": "In some cases, a (starting) contributor might wish to delete a feature even though deletion is not appropriate.\n(The most relevant case are small paths running over private property. These should be marked as 'private' instead of deleted, as the community might trace the path again from aerial imagery, gettting us back to the original situation).\n\nBy adding a 'nonDeleteMapping', an option can be added into the list which will retag the feature.\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"if": {
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
"then": {}
|
||||
},
|
||||
"required": [
|
||||
"if",
|
||||
"then"
|
||||
]
|
||||
}
|
||||
},
|
||||
"softDeletionTags": {
|
||||
"description": "In some cases, the contributor is not allowed to delete the current feature (e.g. because it isn't a point, the point is referenced by a relation or the user isn't experienced enough).\nTo still offer the user a 'delete'-option, the feature is retagged with these tags. This is a soft deletion, as the point isn't actually removed from OSM but rather marked as 'disused'\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!\n\nExample (note that \"amenity=\" erases the 'amenity'-key alltogether):\n```\n{\n \"and\": [\"disussed:amenity=public_bookcase\", \"amenity=\"]\n}\n```\n\nor (notice the use of the ':='-tag to copy the old value of 'shop=*' into 'disused:shop='):\n```\n{\n \"and\": [\"disused:shop:={shop}\", \"shop=\"]\n}\n```",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"neededChangesets": {
|
||||
"description": "*\nBy default, the contributor needs 20 previous changesets to delete points edited by others.\nFor 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": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"default_2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enableImproveAccuracy": {
|
||||
"description": "One default reason to move a point is to improve accuracy.\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableRelocation": {
|
||||
"description": "One default reason to move a point is because it has relocated\nSet to false to disable this reason",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"default_1": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
"description": "Every key from this list will be normalized",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"eraseInvalidValues": {
|
||||
"description": "If set, invalid values will be erased in the MC application (but not in OSM of course!)\nBe careful with setting this",
|
||||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApplicableUnitJson"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"applicableUnits",
|
||||
"appliesToKey"
|
||||
]
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
101
Docs/Schemas/UnitConfigJson.schema.json
Normal file
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
"description": "Every key from this list will be normalized",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"eraseInvalidValues": {
|
||||
"description": "If set, invalid values will be erased in the MC application (but not in OSM of course!)\nBe careful with setting this",
|
||||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApplicableUnitJson"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"applicableUnits",
|
||||
"appliesToKey"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false
|
||||
}
|
98
Docs/Schemas/UnitConfigJsonJSC.ts
Normal file
|
@ -0,0 +1,98 @@
|
|||
export default {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appliesToKey": {
|
||||
"description": "Every key from this list will be normalized",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"eraseInvalidValues": {
|
||||
"description": "If set, invalid values will be erased in the MC application (but not in OSM of course!)\nBe careful with setting this",
|
||||
"type": "boolean"
|
||||
},
|
||||
"applicableUnits": {
|
||||
"description": "The possible denominations",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ApplicableUnitJson"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"applicableUnits",
|
||||
"appliesToKey"
|
||||
],
|
||||
"definitions": {
|
||||
"AndOrTagConfigJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"and": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AndOrTagConfigJson"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApplicableUnitJson": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canonicalDenomination": {
|
||||
"description": "The canonical value which will be added to the text.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'",
|
||||
"type": "string"
|
||||
},
|
||||
"canonicalDenominationSingular": {
|
||||
"description": "The canonical denomination in the case that the unit is precisely '1'",
|
||||
"type": "string"
|
||||
},
|
||||
"alternativeDenomination": {
|
||||
"description": "A list of alternative values which can occur in the OSM database - used for parsing.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}"
|
||||
},
|
||||
"humanSingular": {
|
||||
"description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"x²\n}"
|
||||
},
|
||||
"prefix": {
|
||||
"description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field",
|
||||
"type": "boolean"
|
||||
},
|
||||
"default": {
|
||||
"description": "The default interpretation - only one can be set.\nIf none is set, the first unit will be considered the default interpretation of a value without a unit",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"canonicalDenomination"
|
||||
]
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
|
||||
Available types for text fields
|
||||
=================================
|
||||
|
||||
|
@ -12,7 +13,7 @@ A basic string
|
|||
|
||||
## text
|
||||
|
||||
A string, but allows input of longer strings more comfortably (a text area)
|
||||
A string, but allows input of longer strings more comfortably and supports newlines (a text area)
|
||||
|
||||
## date
|
||||
|
||||
|
@ -29,6 +30,7 @@ A geographical length in meters (rounded at two points). Will give an extra mini
|
|||
## wikidata
|
||||
|
||||
A wikidata identifier, e.g. Q42.
|
||||
|
||||
### Helper arguments
|
||||
|
||||
|
||||
|
@ -44,6 +46,7 @@ removePrefixes | remove these snippets of text from the start of the passed stri
|
|||
removePostfixes | remove these snippets of text from the end of the passed string to search
|
||||
|
||||
|
||||
|
||||
### Example usage
|
||||
|
||||
The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name
|
||||
|
@ -102,6 +105,7 @@ A phone number
|
|||
## opening_hours
|
||||
|
||||
Has extra elements to easily input when a POI is opened.
|
||||
|
||||
### Helper arguments
|
||||
|
||||
|
||||
|
@ -116,6 +120,7 @@ prefix | Piece of text that will always be added to the front of the generated o
|
|||
postfix | Piece of text that will always be added to the end of the generated opening hours
|
||||
|
||||
|
||||
|
||||
### Example usage
|
||||
|
||||
To add a conditional (based on time) access restriction:
|
||||
|
@ -138,4 +143,6 @@ postfix | Piece of text that will always be added to the end of the generated op
|
|||
|
||||
## color
|
||||
|
||||
Shows a color picker Generated from ValidatedTextField.ts
|
||||
Shows a color picker
|
||||
|
||||
This document is autogenerated from ValidatedTextField.ts
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
|
||||
### Special tag renderings
|
||||
|
||||
|
||||
|
@ -27,14 +28,17 @@ General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_nam
|
|||
|
||||
|
||||
|
||||
|
||||
### all_tags
|
||||
|
||||
Prints all key-value pairs of the object - used for debugging
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{all_tags()}`
|
||||
|
||||
|
||||
|
||||
### image_carousel
|
||||
|
||||
Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links)
|
||||
|
@ -43,11 +47,13 @@ name | default | description
|
|||
------ | --------- | -------------
|
||||
image key/prefix (multiple values allowed if comma-seperated) | image,mapillary,image,wikidata,wikimedia_commons,image,image | The keys given to the images, e.g. if <span class='literal-code'>image</span> is given, the first picture URL will be added as <span class='literal-code'>image</span>, the second as <span class='literal-code'>image:0</span>, the third as <span class='literal-code'>image:1</span>, etc...
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{image_carousel(image,mapillary,image,wikidata,wikimedia_commons,image,image)}`
|
||||
|
||||
|
||||
|
||||
### image_upload
|
||||
|
||||
Creates a button where a user can upload an image to IMGUR
|
||||
|
@ -57,11 +63,13 @@ name | default | description
|
|||
image-key | image | Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added)
|
||||
label | Add image | The text to show on the button
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{image_upload(image,Add image)}`
|
||||
|
||||
|
||||
|
||||
### wikipedia
|
||||
|
||||
A box showing the corresponding wikipedia article - based on the wikidata tag
|
||||
|
@ -70,11 +78,13 @@ name | default | description
|
|||
------ | --------- | -------------
|
||||
keyToShowWikipediaFor | wikidata | Use the wikidata entry from this key to show the wikipedia article for
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height
|
||||
|
||||
|
||||
|
||||
### minimap
|
||||
|
||||
A small map showing the selected feature.
|
||||
|
@ -84,11 +94,13 @@ name | default | description
|
|||
zoomlevel | 18 | The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close
|
||||
idKey | id | (Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap.
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`
|
||||
|
||||
|
||||
|
||||
### sided_minimap
|
||||
|
||||
A small map showing _only one side_ the selected feature. *This features requires to have linerenderings with offset* as only linerenderings with a postive or negative offset will be shown. Note: in most cases, this map will be automatically introduced
|
||||
|
@ -97,11 +109,13 @@ name | default | description
|
|||
------ | --------- | -------------
|
||||
side | _undefined_ | The side to show, either `left` or `right`
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{sided_minimap(left)}`
|
||||
|
||||
|
||||
|
||||
### reviews
|
||||
|
||||
Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten
|
||||
|
@ -111,11 +125,13 @@ name | default | description
|
|||
subjectKey | name | The key to use to determine the subject. If specified, the subject will be <b>tags[subjectKey]</b>
|
||||
fallback | _undefined_ | The identifier to use, if <i>tags[subjectKey]</i> as specified above is not available. This is effectively a fallback value
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{reviews()}` for a vanilla review, `{reviews(name, play_forest)}` to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used
|
||||
|
||||
|
||||
|
||||
### opening_hours_table
|
||||
|
||||
Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag 'opening_hours'.
|
||||
|
@ -126,11 +142,13 @@ key | opening_hours | The tagkey from which the table is constructed.
|
|||
prefix | _empty string_ | Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__
|
||||
postfix | _empty string_ | Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}`
|
||||
|
||||
|
||||
|
||||
### live
|
||||
|
||||
Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)}
|
||||
|
@ -141,11 +159,13 @@ Url | _undefined_ | The URL to load
|
|||
Shorthands | _undefined_ | A list of shorthands, of the format 'shorthandname:path.path.path'. separated by ;
|
||||
path | _undefined_ | The path (or shorthand) that should be returned
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
{live({url},{url:format},hour)} {live(https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CB2105,hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt,hour)}
|
||||
|
||||
|
||||
|
||||
### histogram
|
||||
|
||||
Create a histogram for a list of given values, read from the properties.
|
||||
|
@ -157,11 +177,13 @@ title | _empty string_ | The text to put above the given values column
|
|||
countHeader | _empty string_ | The text to put above the counts
|
||||
colors* | _undefined_ | (Matches all resting arguments - optional) Matches a regex onto a color value, e.g. `3[a-zA-Z+-]*:#33cc33`
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{histogram('some_key')}` with properties being `{some_key: ['a','b','a','c']} to create a histogram
|
||||
|
||||
|
||||
|
||||
### share_link
|
||||
|
||||
Creates a link that (attempts to) open the native 'share'-screen
|
||||
|
@ -170,11 +192,13 @@ name | default | description
|
|||
------ | --------- | -------------
|
||||
url | _undefined_ | The url to share (default: current URL)
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
{share_link()} to share the current page, {share_link(<some_url>)} to share the given url
|
||||
|
||||
|
||||
|
||||
### canonical
|
||||
|
||||
Converts a short, canonical value into the long, translated text
|
||||
|
@ -183,11 +207,13 @@ name | default | description
|
|||
------ | --------- | -------------
|
||||
key | _undefined_ | The key of the tag to give the canonical text for
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
{canonical(length)} will give 42 metre (in french)
|
||||
|
||||
|
||||
|
||||
### import_button
|
||||
|
||||
This button will copy the data from an external dataset into OpenStreetMap. It is only functional in official themes but can be tested in unofficial themes.
|
||||
|
@ -247,11 +273,13 @@ Snap onto layer(s)/replace geometry with this other way | _undefined_ | - If th
|
|||
- If a way of the given layer(s) is closeby, will snap the new point onto this way (similar as preset might snap). To show multiple layers to snap onto, use a `;`-seperated list
|
||||
snap max distance | 5 | The maximum distance that this point will move to snap onto a layer (in meters)
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{import_button(,,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,18,,5)}`
|
||||
|
||||
|
||||
|
||||
### multi_apply
|
||||
|
||||
A button to apply the tagging of this object onto a list of other features. This is an advanced feature for which you'll need calculatedTags
|
||||
|
@ -264,11 +292,13 @@ text | _undefined_ | The text to show on the button
|
|||
autoapply | _undefined_ | A boolean indicating wether this tagging should be applied automatically if the relevant tags on this object are changed. A visual element indicating the multi_apply is still shown
|
||||
overwrite | _undefined_ | If set to 'true', the tags on the other objects will always be overwritten. The default behaviour will be to only change the tags on other objects if they are either undefined or had the same value before the change
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
{multi_apply(_features_with_the_same_name_within_100m, name:etymology:wikidata;name:etymology, Apply etymology information on all nearby objects with the same name)}
|
||||
|
||||
|
||||
|
||||
### tag_apply
|
||||
|
||||
Shows a big button; clicking this button will apply certain tags onto the feature.
|
||||
|
@ -293,6 +323,9 @@ message | _undefined_ | The text to show to the contributor
|
|||
image | _undefined_ | An image to show to the contributor on the button
|
||||
id_of_object_to_apply_this_one | _undefined_ | If specified, applies the the tags onto _another_ object. The id will be read from properties[id_of_object_to_apply_this_one] of the selected object. The tags are still calculated based on the tags of the _selected_ element
|
||||
|
||||
|
||||
#### Example usage
|
||||
|
||||
`{tag_apply(survey_date:=$_now:date, Surveyed today!)}` Generated from UI/SpecialVisualisations.ts
|
||||
`{tag_apply(survey_date:=$_now:date, Surveyed today!)}`
|
||||
|
||||
This document is autogenerated from UI/SpecialVisualisations.ts
|
|
@ -121,6 +121,26 @@
|
|||
"description": "Layer 'Cafés and pubs' shows wheelchair=no with a fixed text, namely 'This place is not reachable with a wheelchair' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cafés and pubs')",
|
||||
"value": "no"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Cafés and pubs' shows service:electricity=yes with a fixed text, namely 'There are plenty of domestic sockets available to customers seated indoors, where they can charge their electronics' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cafés and pubs')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Cafés and pubs' shows service:electricity=limited with a fixed text, namely 'There are a few domestic sockets available to customers seated indoors, where they can charge their electronics' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cafés and pubs')",
|
||||
"value": "limited"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Cafés and pubs' shows service:electricity=ask with a fixed text, namely 'There are no sockets available indoors to customers, but charging might be possible if the staff is asked' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cafés and pubs')",
|
||||
"value": "ask"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Cafés and pubs' shows service:electricity=no with a fixed text, namely 'There are a no domestic sockets available to customers seated indoors' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cafés and pubs')",
|
||||
"value": "no"
|
||||
},
|
||||
{
|
||||
"key": "dog",
|
||||
"description": "Layer 'Cafés and pubs' shows dog=yes with a fixed text, namely 'Dogs are allowed' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cafés and pubs')",
|
||||
|
|
|
@ -543,129 +543,129 @@
|
|||
},
|
||||
{
|
||||
"key": "planned:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "construction:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "disused:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "operational_status",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"value": "charging_station"
|
||||
},
|
||||
{
|
||||
"key": "planned:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "construction:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "disused:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "operational_status",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"value": "broken"
|
||||
},
|
||||
{
|
||||
"key": "amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=broken&amenity=charging_station with a fixed text, namely 'This charging station works' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"value": "charging_station"
|
||||
},
|
||||
{
|
||||
"key": "planned:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"value": "charging_station"
|
||||
},
|
||||
{
|
||||
"key": "construction:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "disused:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "operational_status",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=charging_station&construction:amenity=&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'This charging station is broken' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "planned:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "construction:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"value": "charging_station"
|
||||
},
|
||||
{
|
||||
"key": "disused:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "operational_status",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=charging_station&disused:amenity=&operational_status=&amenity= with a fixed text, namely 'A charging station is planned here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "planned:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "construction:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "disused:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"value": "charging_station"
|
||||
},
|
||||
{
|
||||
"key": "operational_status",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=charging_station&operational_status=&amenity= with a fixed text, namely 'A charging station is constructed here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "planned:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key planned:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "construction:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key construction:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "disused:amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key disused:amenity.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "operational_status",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations') Picking this answer will delete the key operational_status.",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"key": "amenity",
|
||||
"description": "Layer 'Charging stations' shows planned:amenity=&construction:amenity=&disused:amenity=&operational_status=&amenity=charging_station with a fixed text, namely 'This charging station has beed permanently disabled and is not in use anymore but is still visible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
"value": "charging_station"
|
||||
},
|
||||
{
|
||||
"key": "parking:fee",
|
||||
"description": "Layer 'Charging stations' shows parking:fee=no with a fixed text, namely 'No additional parking cost while charging' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Charging stations')",
|
||||
|
|
|
@ -600,7 +600,7 @@
|
|||
},
|
||||
{
|
||||
"key": "cycle_barrier:type",
|
||||
"description": "Layer 'Barriers' shows cycle_barrier:type=double with a fixed text, namely 'Double, two barriers behind each other <img src='./assets/themes/cycle_infra/Cycle_barrier_double.png' style='width:8em'>' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Bicycle infrastructure')",
|
||||
"description": "Layer 'Barriers' shows cycle_barrier:type=double with a fixed text, namely 'Double, two barriers behind each other <img src='./assets/themes/cycle_infra/Cycle_barrier_double.svg' style='width:8em'>' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Bicycle infrastructure')",
|
||||
"value": "double"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -966,13 +966,13 @@
|
|||
},
|
||||
{
|
||||
"key": "location",
|
||||
"description": "Layer 'Bike parking' shows location=underground with a fixed text, namely 'Underground parking' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')",
|
||||
"value": "underground"
|
||||
"description": "Layer 'Bike parking' shows location=surface with a fixed text, namely 'Surface level parking' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')",
|
||||
"value": "surface"
|
||||
},
|
||||
{
|
||||
"key": "location",
|
||||
"description": "Layer 'Bike parking' shows location=surface with a fixed text, namely 'Surface level parking' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')",
|
||||
"value": "surface"
|
||||
"description": "Layer 'Bike parking' shows location=rooftop with a fixed text, namely 'Rooftop parking' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclofix - an open map for cyclists')",
|
||||
"value": "rooftop"
|
||||
},
|
||||
{
|
||||
"key": "location",
|
||||
|
|
|
@ -76,16 +76,6 @@
|
|||
"description": "Layer 'Restaurants and fast food' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "payment:app",
|
||||
"description": "Layer 'Restaurants and fast food' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "payment:membership_card",
|
||||
"description": "Layer 'Restaurants and fast food' shows payment:membership_card=yes with a fixed text, namely 'Payment is done using a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "wheelchair",
|
||||
"description": "Layer 'Restaurants and fast food' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
|
@ -315,6 +305,26 @@
|
|||
"description": "Layer 'Restaurants and fast food' shows reusable_packaging:accept=only with a fixed text, namely 'You <b>must</b> bring your own container to order here.' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
"value": "only"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Restaurants and fast food' shows service:electricity=yes with a fixed text, namely 'There are plenty of domestic sockets available to customers seated indoors, where they can charge their electronics' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Restaurants and fast food' shows service:electricity=limited with a fixed text, namely 'There are a few domestic sockets available to customers seated indoors, where they can charge their electronics' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
"value": "limited"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Restaurants and fast food' shows service:electricity=ask with a fixed text, namely 'There are no sockets available indoors to customers, but charging might be possible if the staff is asked' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
"value": "ask"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Restaurants and fast food' shows service:electricity=no with a fixed text, namely 'There are a no domestic sockets available to customers seated indoors' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
"value": "no"
|
||||
},
|
||||
{
|
||||
"key": "dog",
|
||||
"description": "Layer 'Restaurants and fast food' shows dog=yes with a fixed text, namely 'Dogs are allowed' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Restaurants and fast food')",
|
||||
|
|
|
@ -81,16 +81,6 @@
|
|||
"description": "Layer 'Fries shop' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "payment:app",
|
||||
"description": "Layer 'Fries shop' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "payment:membership_card",
|
||||
"description": "Layer 'Fries shop' shows payment:membership_card=yes with a fixed text, namely 'Payment is done using a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "wheelchair",
|
||||
"description": "Layer 'Fries shop' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
|
@ -320,6 +310,26 @@
|
|||
"description": "Layer 'Fries shop' shows reusable_packaging:accept=only with a fixed text, namely 'You <b>must</b> bring your own container to order here.' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "only"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Fries shop' shows service:electricity=yes with a fixed text, namely 'There are plenty of domestic sockets available to customers seated indoors, where they can charge their electronics' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Fries shop' shows service:electricity=limited with a fixed text, namely 'There are a few domestic sockets available to customers seated indoors, where they can charge their electronics' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "limited"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Fries shop' shows service:electricity=ask with a fixed text, namely 'There are no sockets available indoors to customers, but charging might be possible if the staff is asked' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "ask"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Fries shop' shows service:electricity=no with a fixed text, namely 'There are a no domestic sockets available to customers seated indoors' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "no"
|
||||
},
|
||||
{
|
||||
"key": "dog",
|
||||
"description": "Layer 'Fries shop' shows dog=yes with a fixed text, namely 'Dogs are allowed' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
|
@ -406,16 +416,6 @@
|
|||
"description": "Layer 'Restaurants and fast food' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "payment:app",
|
||||
"description": "Layer 'Restaurants and fast food' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "payment:membership_card",
|
||||
"description": "Layer 'Restaurants and fast food' shows payment:membership_card=yes with a fixed text, namely 'Payment is done using a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "wheelchair",
|
||||
"description": "Layer 'Restaurants and fast food' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
|
@ -645,6 +645,26 @@
|
|||
"description": "Layer 'Restaurants and fast food' shows reusable_packaging:accept=only with a fixed text, namely 'You <b>must</b> bring your own container to order here.' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "only"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Restaurants and fast food' shows service:electricity=yes with a fixed text, namely 'There are plenty of domestic sockets available to customers seated indoors, where they can charge their electronics' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Restaurants and fast food' shows service:electricity=limited with a fixed text, namely 'There are a few domestic sockets available to customers seated indoors, where they can charge their electronics' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "limited"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Restaurants and fast food' shows service:electricity=ask with a fixed text, namely 'There are no sockets available indoors to customers, but charging might be possible if the staff is asked' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "ask"
|
||||
},
|
||||
{
|
||||
"key": "service:electricity",
|
||||
"description": "Layer 'Restaurants and fast food' shows service:electricity=no with a fixed text, namely 'There are a no domestic sockets available to customers seated indoors' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
"value": "no"
|
||||
},
|
||||
{
|
||||
"key": "dog",
|
||||
"description": "Layer 'Restaurants and fast food' shows dog=yes with a fixed text, namely 'Dogs are allowed' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Friturenkaart')",
|
||||
|
|
|
@ -76,16 +76,6 @@
|
|||
"description": "Layer 'Observation towers' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "payment:app",
|
||||
"description": "Layer 'Observation towers' shows payment:app=yes with a fixed text, namely 'Payment is done using a dedicated app' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "payment:membership_card",
|
||||
"description": "Layer 'Observation towers' shows payment:membership_card=yes with a fixed text, namely 'Payment is done using a membership card' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "wheelchair",
|
||||
"description": "Layer 'Observation towers' shows wheelchair=designated with a fixed text, namely 'This place is specially adapted for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Observation towers')",
|
||||
|
|
|
@ -74,6 +74,25 @@
|
|||
"key": "charge",
|
||||
"description": "Layer 'Toilets' shows and asks freeform values for key 'charge' (in the MapComplete.osm.be theme 'Open Toilet Map')"
|
||||
},
|
||||
{
|
||||
"key": "payment:cash",
|
||||
"description": "Layer 'Toilets' shows payment:cash=yes with a fixed text, namely 'Cash is accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "payment:cards",
|
||||
"description": "Layer 'Toilets' shows payment:cards=yes with a fixed text, namely 'Payment cards are accepted here' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
"key": "opening_hours",
|
||||
"description": "Layer 'Toilets' shows and asks freeform values for key 'opening_hours' (in the MapComplete.osm.be theme 'Open Toilet Map')"
|
||||
},
|
||||
{
|
||||
"key": "opening_hours",
|
||||
"description": "Layer 'Toilets' shows opening_hours=24/7 with a fixed text, namely 'Opened 24/7' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')",
|
||||
"value": "24/7"
|
||||
},
|
||||
{
|
||||
"key": "wheelchair",
|
||||
"description": "Layer 'Toilets' shows wheelchair=yes with a fixed text, namely 'There is a dedicated toilet for wheelchair users' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')",
|
||||
|
@ -150,7 +169,7 @@
|
|||
},
|
||||
{
|
||||
"key": "toilets:paper_supplied",
|
||||
"description": "Layer 'Toilets' shows toilets:paper_supplied=yes with a fixed text, namely 'Toilet paper is equipped with toilet paper' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')",
|
||||
"description": "Layer 'Toilets' shows toilets:paper_supplied=yes with a fixed text, namely 'This toilet is equipped with toilet paper' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')",
|
||||
"value": "yes"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import datetime
|
||||
from matplotlib import pyplot
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from matplotlib import pyplot
|
||||
|
||||
|
||||
def pyplot_init():
|
||||
|
@ -14,20 +14,22 @@ def pyplot_init():
|
|||
def genKeys(data, type):
|
||||
keys = map(lambda kv: kv["key"], data)
|
||||
if type == "date":
|
||||
keys = map(lambda key : datetime.strptime(key, "%Y-%m-%dT%H:%M:%S.000Z"), keys)
|
||||
keys = map(lambda key: datetime.strptime(key, "%Y-%m-%dT%H:%M:%S.000Z"), keys)
|
||||
return list(keys)
|
||||
|
||||
|
||||
def createPie(options):
|
||||
data = options["plot"]["count"]
|
||||
keys = genKeys(data, options["interpetKeysAs"])
|
||||
values = list(map(lambda kv: kv["value"], data))
|
||||
|
||||
total = sum(map(lambda kv : kv["value"], data))
|
||||
total = sum(map(lambda kv: kv["value"], data))
|
||||
first_pct = data[0]["value"] / total
|
||||
|
||||
pyplot_init()
|
||||
pyplot.pie(values, labels=keys, startangle=(90 - 360 * first_pct / 2))
|
||||
|
||||
|
||||
def createBar(options):
|
||||
data = options["plot"]["count"]
|
||||
keys = genKeys(data, options["interpetKeysAs"])
|
||||
|
@ -37,26 +39,25 @@ def createBar(options):
|
|||
pyplot.legend()
|
||||
|
||||
|
||||
|
||||
pyplot_init()
|
||||
title = sys.argv[1]
|
||||
pyplot.title = title
|
||||
names = []
|
||||
while(True):
|
||||
while (True):
|
||||
line = sys.stdin.readline()
|
||||
if line == "" or line == "\n":
|
||||
if(len(names) > 1):
|
||||
pyplot.legend(loc="upper left", ncol=3)
|
||||
pyplot.savefig(title+".png", dpi=400, facecolor='w', edgecolor='w',
|
||||
if (len(names) > 1):
|
||||
pyplot.legend(loc="upper left", ncol=3)
|
||||
pyplot.savefig(title + ".png", dpi=400, facecolor='w', edgecolor='w',
|
||||
bbox_inches='tight')
|
||||
break
|
||||
|
||||
options = json.loads(line)
|
||||
print("Creating "+options["plot"]["type"]+" '"+options["name"]+"'")
|
||||
print("Creating " + options["plot"]["type"] + " '" + options["name"] + "'")
|
||||
names.append(options["name"])
|
||||
if(options["plot"]["type"] == "pie"):
|
||||
if (options["plot"]["type"] == "pie"):
|
||||
createPie(options)
|
||||
elif(options["plot"]["type"] == "bar"):
|
||||
elif (options["plot"]["type"] == "bar"):
|
||||
createBar(options)
|
||||
else:
|
||||
print("Unkown type: "+options.type)
|
||||
print("Unkown type: " + options.type)
|
||||
|
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 273 KiB |
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 231 KiB |
Before Width: | Height: | Size: 507 KiB After Width: | Height: | Size: 518 KiB |
Before Width: | Height: | Size: 536 KiB After Width: | Height: | Size: 546 KiB |
Before Width: | Height: | Size: 689 KiB After Width: | Height: | Size: 707 KiB |
Before Width: | Height: | Size: 737 KiB After Width: | Height: | Size: 731 KiB |
Before Width: | Height: | Size: 439 KiB After Width: | Height: | Size: 445 KiB |
Before Width: | Height: | Size: 469 KiB After Width: | Height: | Size: 468 KiB |
Before Width: | Height: | Size: 448 KiB After Width: | Height: | Size: 481 KiB |
Before Width: | Height: | Size: 512 KiB After Width: | Height: | Size: 533 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 147 KiB |
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
7103
Docs/Tools/stats/stats.2021-11.json
Normal file
|
@ -20,139 +20,164 @@ the URL-parameters are stated in the part between the `?` and the `#`. There are
|
|||
Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case.
|
||||
|
||||
|
||||
|
||||
fs-userbadge
|
||||
--------------
|
||||
|
||||
Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode. The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-search
|
||||
-----------
|
||||
|
||||
Disables/Enables the search bar The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-background
|
||||
---------------
|
||||
|
||||
Disables/Enables the background layer control The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-filter
|
||||
-----------
|
||||
|
||||
Disables/Enables the filter The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-add-new
|
||||
------------
|
||||
|
||||
Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-welcome-message
|
||||
--------------------
|
||||
|
||||
Disables/enables the help menu or welcome message The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-iframe-popout
|
||||
------------------
|
||||
|
||||
Disables/Enables the iframe-popout button. If in iframe mode and the welcome message is hidden, a popout button to the full mapcomplete instance is shown instead (unless disabled with this switch) The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-more-quests
|
||||
----------------
|
||||
|
||||
Disables/Enables the 'More Quests'-tab in the welcome message The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-share-screen
|
||||
-----------------
|
||||
|
||||
Disables/Enables the 'Share-screen'-tab in the welcome message The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-geolocation
|
||||
----------------
|
||||
|
||||
Disables/Enables the geolocation button The default value is _true_
|
||||
|
||||
|
||||
|
||||
fs-all-questions
|
||||
------------------
|
||||
|
||||
Always show all questions The default value is _false_
|
||||
|
||||
|
||||
|
||||
fs-export
|
||||
-----------
|
||||
|
||||
Enable the export as GeoJSON and CSV button The default value is _false_
|
||||
|
||||
|
||||
|
||||
fs-pdf
|
||||
--------
|
||||
|
||||
Enable the PDF download button The default value is _false_
|
||||
|
||||
|
||||
|
||||
backend
|
||||
---------
|
||||
|
||||
The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_
|
||||
|
||||
|
||||
|
||||
test
|
||||
------
|
||||
|
||||
If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org The default value is _false_
|
||||
|
||||
|
||||
|
||||
debug
|
||||
-------
|
||||
|
||||
If true, shows some extra debugging help such as all the available tags on every object The default value is _false_
|
||||
|
||||
|
||||
|
||||
fake-user
|
||||
-----------
|
||||
|
||||
If true, 'dryrun' mode is activated and a fake user account is loaded The default value is _false_
|
||||
|
||||
|
||||
|
||||
overpassUrl
|
||||
-------------
|
||||
|
||||
Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter The default value is _https://overpass-api.de/api/interpreter,https://overpass.kumi.systems/api/interpreter,https://overpass.openstreetmap.ru/cgi/interpreter_
|
||||
|
||||
|
||||
|
||||
overpassTimeout
|
||||
-----------------
|
||||
|
||||
Set a different timeout (in seconds) for queries in overpass The default value is _30_
|
||||
|
||||
|
||||
|
||||
overpassMaxZoom
|
||||
-----------------
|
||||
|
||||
point to switch between OSM-api and overpass The default value is _17_
|
||||
|
||||
|
||||
|
||||
osmApiTileSize
|
||||
----------------
|
||||
|
||||
Tilesize when the OSM-API is used to fetch data within a BBOX The default value is _18_
|
||||
|
||||
|
||||
|
||||
background
|
||||
------------
|
||||
|
||||
The id of the background layer to start with The default value is _osm_
|
||||
|
||||
|
||||
|
||||
layer-<layer-id>
|
||||
------------------
|
||||
|
||||
Wether or not the layer with id <layer-id> is shown The default value is _true_ Generated from QueryParameters
|
||||
Wether or not the layer with id <layer-id> is shown The default value is _true_
|
||||
|
||||
This document is autogenerated from QueryParameters
|
|
@ -5,8 +5,10 @@ import Loc from "../../Models/Loc";
|
|||
export interface AvailableBaseLayersObj {
|
||||
readonly osmCarto: BaseLayer;
|
||||
layerOverview: BaseLayer[];
|
||||
|
||||
AvailableLayersAt(location: UIEventSource<Loc>): UIEventSource<BaseLayer[]>
|
||||
SelectBestLayerAccordingTo(location: UIEventSource<Loc>, preferedCategory: UIEventSource<string | string[]>): UIEventSource<BaseLayer> ;
|
||||
|
||||
SelectBestLayerAccordingTo(location: UIEventSource<Loc>, preferedCategory: UIEventSource<string | string[]>): UIEventSource<BaseLayer>;
|
||||
|
||||
}
|
||||
|
||||
|
@ -31,7 +33,7 @@ export default class AvailableBaseLayers {
|
|||
|
||||
}
|
||||
|
||||
public static implement(backend: AvailableBaseLayersObj){
|
||||
public static implement(backend: AvailableBaseLayersObj) {
|
||||
AvailableBaseLayers.layerOverview = backend.layerOverview
|
||||
AvailableBaseLayers.osmCarto = backend.osmCarto
|
||||
AvailableBaseLayers.implementation = backend
|
||||
|
|
|
@ -3,13 +3,13 @@ import {UIEventSource} from "../UIEventSource";
|
|||
import Loc from "../../Models/Loc";
|
||||
import {GeoOperations} from "../GeoOperations";
|
||||
import * as editorlayerindex from "../../assets/editor-layer-index.json";
|
||||
import * as L from "leaflet";
|
||||
import {TileLayer} from "leaflet";
|
||||
import * as X from "leaflet-providers";
|
||||
import * as L from "leaflet";
|
||||
import {Utils} from "../../Utils";
|
||||
import {AvailableBaseLayersObj} from "./AvailableBaseLayers";
|
||||
|
||||
export default class AvailableBaseLayersImplementation implements AvailableBaseLayersObj{
|
||||
export default class AvailableBaseLayersImplementation implements AvailableBaseLayersObj {
|
||||
|
||||
public readonly osmCarto: BaseLayer =
|
||||
{
|
||||
|
@ -28,102 +28,6 @@ export default class AvailableBaseLayersImplementation implements AvailableBaseL
|
|||
|
||||
public layerOverview = AvailableBaseLayersImplementation.LoadRasterIndex().concat(AvailableBaseLayersImplementation.LoadProviderIndex());
|
||||
|
||||
public AvailableLayersAt(location: UIEventSource<Loc>): UIEventSource<BaseLayer[]> {
|
||||
const source = location.map(
|
||||
(currentLocation) => {
|
||||
|
||||
if (currentLocation === undefined) {
|
||||
return this.layerOverview;
|
||||
}
|
||||
|
||||
const currentLayers = source?.data; // A bit unorthodox - I know
|
||||
const newLayers = this.CalculateAvailableLayersAt(currentLocation?.lon, currentLocation?.lat);
|
||||
|
||||
if (currentLayers === undefined) {
|
||||
return newLayers;
|
||||
}
|
||||
if (newLayers.length !== currentLayers.length) {
|
||||
return newLayers;
|
||||
}
|
||||
for (let i = 0; i < newLayers.length; i++) {
|
||||
if (newLayers[i].name !== currentLayers[i].name) {
|
||||
return newLayers;
|
||||
}
|
||||
}
|
||||
|
||||
return currentLayers;
|
||||
});
|
||||
return source;
|
||||
}
|
||||
|
||||
public SelectBestLayerAccordingTo(location: UIEventSource<Loc>, preferedCategory: UIEventSource<string | string[]>): UIEventSource<BaseLayer> {
|
||||
return this.AvailableLayersAt(location).map(available => {
|
||||
// First float all 'best layers' to the top
|
||||
available.sort((a, b) => {
|
||||
if (a.isBest && b.isBest) {
|
||||
return 0;
|
||||
}
|
||||
if (!a.isBest) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
)
|
||||
|
||||
if (preferedCategory.data === undefined) {
|
||||
return available[0]
|
||||
}
|
||||
|
||||
let prefered: string []
|
||||
if (typeof preferedCategory.data === "string") {
|
||||
prefered = [preferedCategory.data]
|
||||
} else {
|
||||
prefered = preferedCategory.data;
|
||||
}
|
||||
|
||||
prefered.reverse();
|
||||
for (const category of prefered) {
|
||||
//Then sort all 'photo'-layers to the top. Stability of the sorting will force a 'best' photo layer on top
|
||||
available.sort((a, b) => {
|
||||
if (a.category === category && b.category === category) {
|
||||
return 0;
|
||||
}
|
||||
if (a.category !== category) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
)
|
||||
}
|
||||
return available[0]
|
||||
})
|
||||
}
|
||||
|
||||
private CalculateAvailableLayersAt(lon: number, lat: number): BaseLayer[] {
|
||||
const availableLayers = [this.osmCarto]
|
||||
const globalLayers = [];
|
||||
for (const layerOverviewItem of this.layerOverview) {
|
||||
const layer = layerOverviewItem;
|
||||
|
||||
if (layer.feature?.geometry === undefined || layer.feature?.geometry === null) {
|
||||
globalLayers.push(layer);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lon === undefined || lat === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GeoOperations.inside([lon, lat], layer.feature)) {
|
||||
availableLayers.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
return availableLayers.concat(globalLayers);
|
||||
}
|
||||
|
||||
private static LoadRasterIndex(): BaseLayer[] {
|
||||
const layers: BaseLayer[] = []
|
||||
// @ts-ignore
|
||||
|
@ -289,4 +193,100 @@ export default class AvailableBaseLayersImplementation implements AvailableBaseL
|
|||
subdomains: domains
|
||||
});
|
||||
}
|
||||
|
||||
public AvailableLayersAt(location: UIEventSource<Loc>): UIEventSource<BaseLayer[]> {
|
||||
const source = location.map(
|
||||
(currentLocation) => {
|
||||
|
||||
if (currentLocation === undefined) {
|
||||
return this.layerOverview;
|
||||
}
|
||||
|
||||
const currentLayers = source?.data; // A bit unorthodox - I know
|
||||
const newLayers = this.CalculateAvailableLayersAt(currentLocation?.lon, currentLocation?.lat);
|
||||
|
||||
if (currentLayers === undefined) {
|
||||
return newLayers;
|
||||
}
|
||||
if (newLayers.length !== currentLayers.length) {
|
||||
return newLayers;
|
||||
}
|
||||
for (let i = 0; i < newLayers.length; i++) {
|
||||
if (newLayers[i].name !== currentLayers[i].name) {
|
||||
return newLayers;
|
||||
}
|
||||
}
|
||||
|
||||
return currentLayers;
|
||||
});
|
||||
return source;
|
||||
}
|
||||
|
||||
public SelectBestLayerAccordingTo(location: UIEventSource<Loc>, preferedCategory: UIEventSource<string | string[]>): UIEventSource<BaseLayer> {
|
||||
return this.AvailableLayersAt(location).map(available => {
|
||||
// First float all 'best layers' to the top
|
||||
available.sort((a, b) => {
|
||||
if (a.isBest && b.isBest) {
|
||||
return 0;
|
||||
}
|
||||
if (!a.isBest) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
)
|
||||
|
||||
if (preferedCategory.data === undefined) {
|
||||
return available[0]
|
||||
}
|
||||
|
||||
let prefered: string []
|
||||
if (typeof preferedCategory.data === "string") {
|
||||
prefered = [preferedCategory.data]
|
||||
} else {
|
||||
prefered = preferedCategory.data;
|
||||
}
|
||||
|
||||
prefered.reverse();
|
||||
for (const category of prefered) {
|
||||
//Then sort all 'photo'-layers to the top. Stability of the sorting will force a 'best' photo layer on top
|
||||
available.sort((a, b) => {
|
||||
if (a.category === category && b.category === category) {
|
||||
return 0;
|
||||
}
|
||||
if (a.category !== category) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
)
|
||||
}
|
||||
return available[0]
|
||||
})
|
||||
}
|
||||
|
||||
private CalculateAvailableLayersAt(lon: number, lat: number): BaseLayer[] {
|
||||
const availableLayers = [this.osmCarto]
|
||||
const globalLayers = [];
|
||||
for (const layerOverviewItem of this.layerOverview) {
|
||||
const layer = layerOverviewItem;
|
||||
|
||||
if (layer.feature?.geometry === undefined || layer.feature?.geometry === null) {
|
||||
globalLayers.push(layer);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lon === undefined || lat === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GeoOperations.inside([lon, lat], layer.feature)) {
|
||||
availableLayers.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
return availableLayers.concat(globalLayers);
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ export default class BackgroundLayerResetter {
|
|||
availableLayers: UIEventSource<BaseLayer[]>,
|
||||
defaultLayerId: string = undefined) {
|
||||
|
||||
if(Utils.runningFromConsole){
|
||||
if (Utils.runningFromConsole) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,22 @@ import {VariableUiElement} from "../../UI/Base/VariableUIElement";
|
|||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||
import {QueryParameters} from "../Web/QueryParameters";
|
||||
import FeatureSource from "../FeatureSource/FeatureSource";
|
||||
import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource";
|
||||
|
||||
export interface GeoLocationPointProperties {
|
||||
id: "gps",
|
||||
"user:location": "yes",
|
||||
"date": string,
|
||||
"latitude": number
|
||||
"longitude":number,
|
||||
"speed": number,
|
||||
"accuracy": number
|
||||
"heading": number
|
||||
"altitude":number
|
||||
}
|
||||
|
||||
export default class GeoLocationHandler extends VariableUiElement {
|
||||
|
||||
public readonly currentLocation : FeatureSource
|
||||
private readonly currentLocation: FeatureSource
|
||||
|
||||
/**
|
||||
* Wether or not the geolocation is active, aka the user requested the current location
|
||||
|
@ -59,13 +70,13 @@ export default class GeoLocationHandler extends VariableUiElement {
|
|||
|
||||
constructor(
|
||||
state: {
|
||||
currentGPSLocation: UIEventSource<Coordinates>,
|
||||
currentUserLocation: FeatureSource,
|
||||
leafletMap: UIEventSource<any>,
|
||||
layoutToUse: LayoutConfig,
|
||||
featureSwitchGeolocation: UIEventSource<boolean>
|
||||
}
|
||||
) {
|
||||
const currentGPSLocation = state.currentGPSLocation
|
||||
const currentGPSLocation = new UIEventSource<Coordinates>(undefined, "GPS-coordinate")
|
||||
const leafletMap = state.leafletMap
|
||||
const hasLocation = currentGPSLocation.map(
|
||||
(location) => location !== undefined
|
||||
|
@ -182,19 +193,24 @@ export default class GeoLocationHandler extends VariableUiElement {
|
|||
}
|
||||
})
|
||||
|
||||
this.currentLocation = new StaticFeatureSource([], false)
|
||||
this.currentLocation = state.currentUserLocation
|
||||
this._currentGPSLocation.addCallback((location) => {
|
||||
self._previousLocationGrant.setData("granted");
|
||||
|
||||
const feature = {
|
||||
"type": "Feature",
|
||||
properties: {
|
||||
"user:location":"yes",
|
||||
"accuracy":location.accuracy,
|
||||
"speed":location.speed,
|
||||
properties: <GeoLocationPointProperties>{
|
||||
id: "gps",
|
||||
"user:location": "yes",
|
||||
"date": new Date().toISOString(),
|
||||
"latitude": location.latitude,
|
||||
"longitude": location.longitude,
|
||||
"speed": location.speed,
|
||||
"accuracy": location.accuracy,
|
||||
"heading": location.heading,
|
||||
"altitude": location.altitude
|
||||
},
|
||||
geometry:{
|
||||
type:"Point",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [location.longitude, location.latitude],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,7 +115,6 @@ export default class OverpassFeatureSource implements FeatureSource {
|
|||
let lastUsed = 0;
|
||||
|
||||
|
||||
|
||||
const layersToDownload = []
|
||||
for (const layer of this.state.layoutToUse.layers) {
|
||||
|
||||
|
@ -137,7 +136,7 @@ export default class OverpassFeatureSource implements FeatureSource {
|
|||
|
||||
const self = this;
|
||||
const overpassUrls = self.state.overpassUrl.data
|
||||
let bounds : BBox
|
||||
let bounds: BBox
|
||||
do {
|
||||
try {
|
||||
|
||||
|
@ -182,7 +181,7 @@ export default class OverpassFeatureSource implements FeatureSource {
|
|||
|
||||
|
||||
try {
|
||||
if(data === undefined){
|
||||
if (data === undefined) {
|
||||
return undefined
|
||||
}
|
||||
data.features.forEach(feature => SimpleMetaTagger.objectMetaInfo.applyMetaTagsOnFeature(feature, date, undefined));
|
||||
|
|
|
@ -31,7 +31,7 @@ export default class PendingChangesUploader {
|
|||
}
|
||||
});
|
||||
|
||||
if(Utils.runningFromConsole){
|
||||
if (Utils.runningFromConsole) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
|||
* Makes sure the hash shows the selected element and vice-versa.
|
||||
*/
|
||||
export default class SelectedFeatureHandler {
|
||||
private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "filters","", undefined])
|
||||
private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "filters", "location_track", "", undefined])
|
||||
private readonly hash: UIEventSource<string>;
|
||||
private readonly state: {
|
||||
selectedElement: UIEventSource<any>,
|
||||
|
|
|
@ -81,5 +81,4 @@ export default class StrayClickHandler {
|
|||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@ import {ElementStorage} from "../ElementStorage";
|
|||
import {Utils} from "../../Utils";
|
||||
|
||||
export default class TitleHandler {
|
||||
constructor(state : {
|
||||
constructor(state: {
|
||||
selectedElement: UIEventSource<any>,
|
||||
layoutToUse: LayoutConfig,
|
||||
allElements: ElementStorage
|
||||
|
@ -28,7 +28,7 @@ export default class TitleHandler {
|
|||
continue;
|
||||
}
|
||||
if (layer.source.osmTags.matchesProperties(tags)) {
|
||||
const tagsSource = state.allElements.getEventSourceById(tags.id)
|
||||
const tagsSource = state.allElements.getEventSourceById(tags.id) ?? new UIEventSource<any>(tags)
|
||||
const title = new TagRenderingAnswer(tagsSource, layer.title)
|
||||
return new Combine([defaultTitle, " | ", title]).ConstructElement()?.innerText ?? defaultTitle;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ export default class TitleHandler {
|
|||
|
||||
|
||||
currentTitle.addCallbackAndRunD(title => {
|
||||
if(Utils.runningFromConsole){
|
||||
if (Utils.runningFromConsole) {
|
||||
return
|
||||
}
|
||||
document.title = title
|
||||
|
|
|
@ -4,11 +4,11 @@ import {GeoOperations} from "./GeoOperations";
|
|||
|
||||
export class BBox {
|
||||
|
||||
static global: BBox = new BBox([[-180, -90], [180, 90]]);
|
||||
readonly maxLat: number;
|
||||
readonly maxLon: number;
|
||||
readonly minLat: number;
|
||||
readonly minLon: number;
|
||||
static global: BBox = new BBox([[-180, -90], [180, 90]]);
|
||||
|
||||
constructor(coordinates) {
|
||||
this.maxLat = -90;
|
||||
|
@ -45,6 +45,17 @@ export class BBox {
|
|||
return feature.bbox;
|
||||
}
|
||||
|
||||
static fromTile(z: number, x: number, y: number): BBox {
|
||||
return new BBox(Tiles.tile_bounds_lon_lat(z, x, y))
|
||||
}
|
||||
|
||||
static fromTileIndex(i: number): BBox {
|
||||
if (i === 0) {
|
||||
return BBox.global
|
||||
}
|
||||
return BBox.fromTile(...Tiles.tile_from_index(i))
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a tilerange which fully contains this bbox (thus might be a bit larger)
|
||||
* @param zoomlevel
|
||||
|
@ -83,24 +94,6 @@ export class BBox {
|
|||
return true;
|
||||
}
|
||||
|
||||
private check() {
|
||||
if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) {
|
||||
console.log(this);
|
||||
throw "BBOX has NAN";
|
||||
}
|
||||
}
|
||||
|
||||
static fromTile(z: number, x: number, y: number): BBox {
|
||||
return new BBox(Tiles.tile_bounds_lon_lat(z, x, y))
|
||||
}
|
||||
|
||||
static fromTileIndex(i: number): BBox {
|
||||
if (i === 0) {
|
||||
return BBox.global
|
||||
}
|
||||
return BBox.fromTile(...Tiles.tile_from_index(i))
|
||||
}
|
||||
|
||||
getEast() {
|
||||
return this.maxLon
|
||||
}
|
||||
|
@ -117,9 +110,9 @@ export class BBox {
|
|||
return this.minLat
|
||||
}
|
||||
|
||||
contains(lonLat: [number, number]){
|
||||
contains(lonLat: [number, number]) {
|
||||
return this.minLat <= lonLat[1] && lonLat[1] <= this.maxLat
|
||||
&& this.minLon<= lonLat[0] && lonLat[0] <= this.maxLon
|
||||
&& this.minLon <= lonLat[0] && lonLat[0] <= this.maxLon
|
||||
}
|
||||
|
||||
pad(factor: number, maxIncrease = 2): BBox {
|
||||
|
@ -179,4 +172,11 @@ export class BBox {
|
|||
|
||||
|
||||
}
|
||||
|
||||
private check() {
|
||||
if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) {
|
||||
console.log(this);
|
||||
throw "BBOX has NAN";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ export default class ContributorCount {
|
|||
|
||||
public readonly Contributors: UIEventSource<Map<string, number>> = new UIEventSource<Map<string, number>>(new Map<string, number>());
|
||||
private readonly state: { featurePipeline: FeaturePipeline, currentBounds: UIEventSource<BBox>, locationControl: UIEventSource<Loc> };
|
||||
private lastUpdate: Date = undefined;
|
||||
|
||||
constructor(state: { featurePipeline: FeaturePipeline, currentBounds: UIEventSource<BBox>, locationControl: UIEventSource<Loc> }) {
|
||||
this.state = state;
|
||||
|
@ -16,15 +17,13 @@ export default class ContributorCount {
|
|||
self.update(bbox)
|
||||
})
|
||||
state.featurePipeline.runningQuery.addCallbackAndRun(
|
||||
_ => self.update(state.currentBounds.data)
|
||||
_ => self.update(state.currentBounds.data)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private lastUpdate: Date = undefined;
|
||||
|
||||
private update(bbox: BBox) {
|
||||
if(bbox === undefined){
|
||||
if (bbox === undefined) {
|
||||
return;
|
||||
}
|
||||
const now = new Date();
|
||||
|
|
|
@ -65,39 +65,6 @@ export default class DetermineLayout {
|
|||
return [layoutToUse, undefined]
|
||||
}
|
||||
|
||||
private static async LoadRemoteTheme(link: string): Promise<LayoutConfig | null> {
|
||||
console.log("Downloading map theme from ", link);
|
||||
|
||||
new FixedUiElement(`Downloading the theme from the <a href="${link}">link</a>...`)
|
||||
.AttachTo("centermessage");
|
||||
|
||||
try {
|
||||
|
||||
const parsed = await Utils.downloadJson(link)
|
||||
console.log("Got ", parsed)
|
||||
LegacyJsonConvert.fixThemeConfig(parsed)
|
||||
try {
|
||||
parsed.id = link;
|
||||
return new LayoutConfig(parsed, false).patchImages(link, JSON.stringify(parsed));
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
DetermineLayout.ShowErrorOnCustomTheme(
|
||||
`<a href="${link}">${link}</a> is invalid:`,
|
||||
new FixedUiElement(e)
|
||||
)
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
DetermineLayout.ShowErrorOnCustomTheme(
|
||||
`<a href="${link}">${link}</a> is invalid - probably not found or invalid JSON:`,
|
||||
new FixedUiElement(e)
|
||||
)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static LoadLayoutFromHash(
|
||||
userLayoutParam: UIEventSource<string>
|
||||
): [LayoutConfig, string] | null {
|
||||
|
@ -166,4 +133,37 @@ export default class DetermineLayout {
|
|||
.AttachTo("centermessage");
|
||||
}
|
||||
|
||||
private static async LoadRemoteTheme(link: string): Promise<LayoutConfig | null> {
|
||||
console.log("Downloading map theme from ", link);
|
||||
|
||||
new FixedUiElement(`Downloading the theme from the <a href="${link}">link</a>...`)
|
||||
.AttachTo("centermessage");
|
||||
|
||||
try {
|
||||
|
||||
const parsed = await Utils.downloadJson(link)
|
||||
console.log("Got ", parsed)
|
||||
LegacyJsonConvert.fixThemeConfig(parsed)
|
||||
try {
|
||||
parsed.id = link;
|
||||
return new LayoutConfig(parsed, false).patchImages(link, JSON.stringify(parsed));
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
DetermineLayout.ShowErrorOnCustomTheme(
|
||||
`<a href="${link}">${link}</a> is invalid:`,
|
||||
new FixedUiElement(e)
|
||||
)
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
DetermineLayout.ShowErrorOnCustomTheme(
|
||||
`<a href="${link}">${link}</a> is invalid - probably not found or invalid JSON:`,
|
||||
new FixedUiElement(e)
|
||||
)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -39,10 +39,10 @@ export class ElementStorage {
|
|||
}
|
||||
|
||||
getEventSourceById(elementId): UIEventSource<any> {
|
||||
if(elementId === undefined){
|
||||
if (elementId === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return this._elements.get(elementId);
|
||||
return this._elements.get(elementId);
|
||||
}
|
||||
|
||||
has(id) {
|
||||
|
|
|
@ -64,7 +64,7 @@ export class ExtraFunction {
|
|||
},
|
||||
(params, feat) => {
|
||||
return (...layerIds: string[]) => {
|
||||
const result : {feat:any, overlap: number}[]= []
|
||||
const result: { feat: any, overlap: number }[] = []
|
||||
|
||||
const bbox = BBox.get(feat)
|
||||
|
||||
|
@ -90,7 +90,7 @@ export class ExtraFunction {
|
|||
private static readonly DistanceToFunc = new ExtraFunction(
|
||||
{
|
||||
name: "distanceTo",
|
||||
doc: "Calculates the distance between the feature and a specified point in kilometer. The input should either be a pair of coordinates, a geojson feature or the ID of an object",
|
||||
doc: "Calculates the distance between the feature and a specified point in meter. The input should either be a pair of coordinates, a geojson feature or the ID of an object",
|
||||
args: ["feature OR featureID OR longitude", "undefined OR latitude"]
|
||||
},
|
||||
(featuresPerLayer, feature) => {
|
||||
|
@ -181,7 +181,7 @@ export class ExtraFunction {
|
|||
}
|
||||
try {
|
||||
const parsed = JSON.parse(value)
|
||||
if(parsed === null){
|
||||
if (parsed === null) {
|
||||
return undefined;
|
||||
}
|
||||
return parsed;
|
||||
|
|
|
@ -39,7 +39,7 @@ export default class SaveTileToLocalStorageActor {
|
|||
}
|
||||
}
|
||||
|
||||
public static poison(layers: string[], lon: number, lat: number) {
|
||||
public static poison(layers: string[], lon: number, lat: number) {
|
||||
for (let z = 0; z < 25; z++) {
|
||||
|
||||
const {x, y} = Tiles.embedded_tile(lat, lon, z)
|
||||
|
|
|
@ -5,11 +5,9 @@ import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from
|
|||
import TiledFeatureSource from "./TiledFeatureSource/TiledFeatureSource";
|
||||
import {UIEventSource} from "../UIEventSource";
|
||||
import {TileHierarchyTools} from "./TiledFeatureSource/TileHierarchy";
|
||||
import FilteredLayer from "../../Models/FilteredLayer";
|
||||
import MetaTagging from "../MetaTagging";
|
||||
import RememberingSource from "./Sources/RememberingSource";
|
||||
import OverpassFeatureSource from "../Actors/OverpassFeatureSource";
|
||||
import {Changes} from "../Osm/Changes";
|
||||
import GeoJsonSource from "./Sources/GeoJsonSource";
|
||||
import Loc from "../../Models/Loc";
|
||||
import RegisteringAllFromFeatureSourceActor from "./Actors/RegisteringAllFromFeatureSourceActor";
|
||||
|
@ -22,11 +20,10 @@ import {NewGeometryFromChangesFeatureSource} from "./Sources/NewGeometryFromChan
|
|||
import ChangeGeometryApplicator from "./Sources/ChangeGeometryApplicator";
|
||||
import {BBox} from "../BBox";
|
||||
import OsmFeatureSource from "./TiledFeatureSource/OsmFeatureSource";
|
||||
import {OsmConnection} from "../Osm/OsmConnection";
|
||||
import {Tiles} from "../../Models/TileRange";
|
||||
import TileFreshnessCalculator from "./TileFreshnessCalculator";
|
||||
import {ElementStorage} from "../ElementStorage";
|
||||
import FullNodeDatabaseSource from "./TiledFeatureSource/FullNodeDatabaseSource";
|
||||
import MapState from "../State/MapState";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -51,19 +48,7 @@ export default class FeaturePipeline {
|
|||
public readonly newDataLoadedSignal: UIEventSource<FeatureSource> = new UIEventSource<FeatureSource>(undefined)
|
||||
|
||||
private readonly overpassUpdater: OverpassFeatureSource
|
||||
private state: {
|
||||
readonly filteredLayers: UIEventSource<FilteredLayer[]>,
|
||||
readonly locationControl: UIEventSource<Loc>,
|
||||
readonly selectedElement: UIEventSource<any>,
|
||||
readonly changes: Changes,
|
||||
readonly layoutToUse: LayoutConfig,
|
||||
readonly leafletMap: any,
|
||||
readonly overpassUrl: UIEventSource<string[]>;
|
||||
readonly overpassTimeout: UIEventSource<number>;
|
||||
readonly overpassMaxZoom: UIEventSource<number>;
|
||||
readonly osmConnection: OsmConnection
|
||||
readonly currentBounds: UIEventSource<BBox>
|
||||
};
|
||||
private state: MapState;
|
||||
private readonly relationTracker: RelationsTracker
|
||||
private readonly perLayerHierarchy: Map<string, TileHierarchyMerger>;
|
||||
|
||||
|
@ -74,21 +59,7 @@ export default class FeaturePipeline {
|
|||
|
||||
constructor(
|
||||
handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void,
|
||||
state: {
|
||||
readonly filteredLayers: UIEventSource<FilteredLayer[]>,
|
||||
readonly locationControl: UIEventSource<Loc>,
|
||||
readonly selectedElement: UIEventSource<any>,
|
||||
readonly changes: Changes,
|
||||
readonly layoutToUse: LayoutConfig,
|
||||
readonly leafletMap: any,
|
||||
readonly overpassUrl: UIEventSource<string[]>;
|
||||
readonly overpassTimeout: UIEventSource<number>;
|
||||
readonly overpassMaxZoom: UIEventSource<number>;
|
||||
readonly osmConnection: OsmConnection
|
||||
readonly currentBounds: UIEventSource<BBox>,
|
||||
readonly osmApiTileSize: UIEventSource<number>,
|
||||
readonly allElements: ElementStorage
|
||||
}) {
|
||||
state: MapState) {
|
||||
this.state = state;
|
||||
|
||||
const self = this
|
||||
|
@ -125,17 +96,25 @@ export default class FeaturePipeline {
|
|||
const perLayerHierarchy = new Map<string, TileHierarchyMerger>()
|
||||
this.perLayerHierarchy = perLayerHierarchy
|
||||
|
||||
const patchedHandleFeatureSource = function (src: FeatureSourceForLayer & IndexedFeatureSource & Tiled) {
|
||||
function patchedHandleFeatureSource (src: FeatureSourceForLayer & IndexedFeatureSource & Tiled) {
|
||||
// This will already contain the merged features for this tile. In other words, this will only be triggered once for every tile
|
||||
const srcFiltered =
|
||||
new FilteringFeatureSource(state, src.tileIndex,
|
||||
new ChangeGeometryApplicator(src, state.changes)
|
||||
new ChangeGeometryApplicator(src, state.changes)
|
||||
)
|
||||
|
||||
handleFeatureSource(srcFiltered)
|
||||
self.somethingLoaded.setData(true)
|
||||
// We do not mark as visited here, this is the responsability of the code near the actual loader (e.g. overpassLoader and OSMApiFeatureLoader)
|
||||
};
|
||||
}
|
||||
|
||||
function handlePriviligedFeatureSource(src: FeatureSourceForLayer & Tiled){
|
||||
// Passthrough to passed function, except that it registers as well
|
||||
handleFeatureSource(src)
|
||||
src.features.addCallbackAndRunD(fs => {
|
||||
fs.forEach(ff => state.allElements.addOrGetElement(ff.feature))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
for (const filteredLayer of state.filteredLayers.data) {
|
||||
|
@ -147,11 +126,31 @@ export default class FeaturePipeline {
|
|||
|
||||
this.freshnesses.set(id, new TileFreshnessCalculator())
|
||||
|
||||
if(id === "type_node"){
|
||||
if (id === "type_node") {
|
||||
// Handles by the 'FullNodeDatabaseSource'
|
||||
continue;
|
||||
}
|
||||
|
||||
if (id === "gps_location") {
|
||||
handlePriviligedFeatureSource(state.currentUserLocation)
|
||||
continue
|
||||
}
|
||||
|
||||
if (id === "gps_location_history") {
|
||||
handlePriviligedFeatureSource(state.historicalUserLocations)
|
||||
continue
|
||||
}
|
||||
|
||||
if (id === "gps_track") {
|
||||
handlePriviligedFeatureSource(state.historicalUserLocationsTrack)
|
||||
continue
|
||||
}
|
||||
|
||||
if (id === "home_location") {
|
||||
handlePriviligedFeatureSource(state.homeLocation)
|
||||
continue
|
||||
}
|
||||
|
||||
if (source.geojsonSource === undefined) {
|
||||
// This is an OSM layer
|
||||
// We load the cached values and register them
|
||||
|
@ -227,14 +226,14 @@ export default class FeaturePipeline {
|
|||
})
|
||||
})
|
||||
|
||||
if(state.layoutToUse.trackAllNodes){
|
||||
const fullNodeDb = new FullNodeDatabaseSource(
|
||||
state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0],
|
||||
tile => {
|
||||
new RegisteringAllFromFeatureSourceActor(tile)
|
||||
perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile)
|
||||
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
|
||||
})
|
||||
if (state.layoutToUse.trackAllNodes) {
|
||||
const fullNodeDb = new FullNodeDatabaseSource(
|
||||
state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0],
|
||||
tile => {
|
||||
new RegisteringAllFromFeatureSourceActor(tile)
|
||||
perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile)
|
||||
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
|
||||
})
|
||||
|
||||
osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => fullNodeDb.handleOsmJson(osmJson, tileId))
|
||||
}
|
||||
|
@ -299,6 +298,34 @@ export default class FeaturePipeline {
|
|||
|
||||
}
|
||||
|
||||
public GetAllFeaturesWithin(bbox: BBox): any[][] {
|
||||
const self = this
|
||||
const tiles = []
|
||||
Array.from(this.perLayerHierarchy.keys())
|
||||
.forEach(key => tiles.push(...self.GetFeaturesWithin(key, bbox)))
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] {
|
||||
if (layerId === "*") {
|
||||
return this.GetAllFeaturesWithin(bbox)
|
||||
}
|
||||
const requestedHierarchy = this.perLayerHierarchy.get(layerId)
|
||||
if (requestedHierarchy === undefined) {
|
||||
console.warn("Layer ", layerId, "is not defined. Try one of ", Array.from(this.perLayerHierarchy.keys()))
|
||||
return undefined;
|
||||
}
|
||||
return TileHierarchyTools.getTiles(requestedHierarchy, bbox)
|
||||
.filter(featureSource => featureSource.features?.data !== undefined)
|
||||
.map(featureSource => featureSource.features.data.map(fs => fs.feature))
|
||||
}
|
||||
|
||||
public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void) {
|
||||
Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => {
|
||||
TileHierarchyTools.getTiles(hierarchy, bbox).forEach(handleTile)
|
||||
})
|
||||
}
|
||||
|
||||
private freshnessForVisibleLayers(z: number, x: number, y: number): Date {
|
||||
let oldestDate = undefined;
|
||||
for (const flayer of this.state.filteredLayers.data) {
|
||||
|
@ -438,32 +465,4 @@ export default class FeaturePipeline {
|
|||
|
||||
}
|
||||
|
||||
public GetAllFeaturesWithin(bbox: BBox): any[][] {
|
||||
const self = this
|
||||
const tiles = []
|
||||
Array.from(this.perLayerHierarchy.keys())
|
||||
.forEach(key => tiles.push(...self.GetFeaturesWithin(key, bbox)))
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] {
|
||||
if (layerId === "*") {
|
||||
return this.GetAllFeaturesWithin(bbox)
|
||||
}
|
||||
const requestedHierarchy = this.perLayerHierarchy.get(layerId)
|
||||
if (requestedHierarchy === undefined) {
|
||||
console.warn("Layer ", layerId, "is not defined. Try one of ", Array.from(this.perLayerHierarchy.keys()))
|
||||
return undefined;
|
||||
}
|
||||
return TileHierarchyTools.getTiles(requestedHierarchy, bbox)
|
||||
.filter(featureSource => featureSource.features?.data !== undefined)
|
||||
.map(featureSource => featureSource.features.data.map(fs => fs.feature))
|
||||
}
|
||||
|
||||
public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void) {
|
||||
Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => {
|
||||
TileHierarchyTools.getTiles(hierarchy, bbox).forEach(handleTile)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import {UIEventSource} from "../UIEventSource";
|
||||
import {Utils} from "../../Utils";
|
||||
import FilteredLayer from "../../Models/FilteredLayer";
|
||||
import {BBox} from "../BBox";
|
||||
|
||||
|
@ -19,7 +18,7 @@ export interface Tiled {
|
|||
/**
|
||||
* A feature source which only contains features for the defined layer
|
||||
*/
|
||||
export interface FeatureSourceForLayer extends FeatureSource{
|
||||
export interface FeatureSourceForLayer extends FeatureSource {
|
||||
readonly layer: FilteredLayer
|
||||
}
|
||||
|
||||
|
|
|
@ -14,15 +14,15 @@ export default class PerLayerFeatureSourceSplitter {
|
|||
constructor(layers: UIEventSource<FilteredLayer[]>,
|
||||
handleLayerData: (source: FeatureSourceForLayer & Tiled) => void,
|
||||
upstream: FeatureSource,
|
||||
options?:{
|
||||
tileIndex?: number,
|
||||
handleLeftovers?: (featuresWithoutLayer: any[]) => void
|
||||
options?: {
|
||||
tileIndex?: number,
|
||||
handleLeftovers?: (featuresWithoutLayer: any[]) => void
|
||||
}) {
|
||||
|
||||
const knownLayers = new Map<string, FeatureSourceForLayer & Tiled>()
|
||||
|
||||
function update() {
|
||||
const features = upstream.features.data;
|
||||
const features = upstream.features?.data;
|
||||
if (features === undefined) {
|
||||
return;
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ export default class PerLayerFeatureSourceSplitter {
|
|||
|
||||
const featuresPerLayer = new Map<string, { feature, freshness } []>();
|
||||
const noLayerFound = []
|
||||
|
||||
function addTo(layer: FilteredLayer, feature: { feature, freshness }) {
|
||||
const id = layer.layerDef.id
|
||||
const list = featuresPerLayer.get(id)
|
||||
|
@ -82,7 +83,7 @@ export default class PerLayerFeatureSourceSplitter {
|
|||
}
|
||||
|
||||
// AT last, the leftovers are handled
|
||||
if(options?.handleLeftovers !== undefined && noLayerFound.length > 0){
|
||||
if (options?.handleLeftovers !== undefined && noLayerFound.length > 0) {
|
||||
options.handleLeftovers(noLayerFound)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@ import {ChangeDescription, ChangeDescriptionTools} from "../../Osm/Actions/Chang
|
|||
export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
|
||||
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
|
||||
public readonly name: string;
|
||||
public readonly layer: FilteredLayer
|
||||
private readonly source: IndexedFeatureSource;
|
||||
private readonly changes: Changes;
|
||||
public readonly layer: FilteredLayer
|
||||
|
||||
constructor(source: (IndexedFeatureSource & FeatureSourceForLayer), changes: Changes) {
|
||||
this.source = source;
|
||||
|
@ -52,9 +52,9 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer {
|
|||
const changesPerId = new Map<string, ChangeDescription[]>()
|
||||
for (const ch of changesToApply) {
|
||||
const key = ch.type + "/" + ch.id
|
||||
if(changesPerId.has(key)){
|
||||
if (changesPerId.has(key)) {
|
||||
changesPerId.get(key).push(ch)
|
||||
}else{
|
||||
} else {
|
||||
changesPerId.set(key, [ch])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import {UIEventSource} from "../../UIEventSource";
|
||||
import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource";
|
||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||
import {Utils} from "../../../Utils";
|
||||
import {Tiles} from "../../../Models/TileRange";
|
||||
import {BBox} from "../../BBox";
|
||||
|
||||
|
@ -14,17 +13,17 @@ export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled
|
|||
public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
|
||||
public readonly name;
|
||||
public readonly layer: FilteredLayer
|
||||
private readonly _sources: UIEventSource<FeatureSource[]>;
|
||||
public readonly tileIndex: number;
|
||||
public readonly bbox: BBox;
|
||||
public readonly containedIds: UIEventSource<Set<string>> = new UIEventSource<Set<string>>(new Set())
|
||||
private readonly _sources: UIEventSource<FeatureSource[]>;
|
||||
|
||||
constructor(layer: FilteredLayer, tileIndex: number, bbox: BBox, sources: UIEventSource<FeatureSource[]>) {
|
||||
this.tileIndex = tileIndex;
|
||||
this.bbox = bbox;
|
||||
this._sources = sources;
|
||||
this.layer = layer;
|
||||
this.name = "FeatureSourceMerger("+layer.layerDef.id+", "+Tiles.tile_from_index(tileIndex).join(",")+")"
|
||||
this.name = "FeatureSourceMerger(" + layer.layerDef.id + ", " + Tiles.tile_from_index(tileIndex).join(",") + ")"
|
||||
const self = this;
|
||||
|
||||
const handledSources = new Set<FeatureSource>();
|
||||
|
|
|
@ -18,6 +18,8 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
locationControl: UIEventSource<{ zoom: number }>; selectedElement: UIEventSource<any>,
|
||||
allElements: ElementStorage
|
||||
};
|
||||
private readonly _alreadyRegistered = new Set<UIEventSource<any>>();
|
||||
private readonly _is_dirty = new UIEventSource(false)
|
||||
|
||||
constructor(
|
||||
state: {
|
||||
|
@ -55,24 +57,6 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
this.update();
|
||||
}
|
||||
|
||||
private readonly _alreadyRegistered = new Set<UIEventSource<any>>();
|
||||
private readonly _is_dirty = new UIEventSource(false)
|
||||
|
||||
private registerCallback(feature: any, layer: LayerConfig) {
|
||||
const src = this.state.allElements.addOrGetElement(feature)
|
||||
if (this._alreadyRegistered.has(src)) {
|
||||
return
|
||||
}
|
||||
this._alreadyRegistered.add(src)
|
||||
if (layer.isShown !== undefined) {
|
||||
|
||||
const self = this;
|
||||
src.map(tags => layer.isShown?.GetRenderValue(tags, "yes").txt).addCallbackAndRunD(isShown => {
|
||||
self._is_dirty.setData(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public update() {
|
||||
const self = this;
|
||||
const layer = this.upstream.layer;
|
||||
|
@ -116,4 +100,19 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
|||
this._is_dirty.setData(false)
|
||||
}
|
||||
|
||||
private registerCallback(feature: any, layer: LayerConfig) {
|
||||
const src = this.state.allElements.addOrGetElement(feature)
|
||||
if (this._alreadyRegistered.has(src)) {
|
||||
return
|
||||
}
|
||||
this._alreadyRegistered.add(src)
|
||||
if (layer.isShown !== undefined) {
|
||||
|
||||
const self = this;
|
||||
src.map(tags => layer.isShown?.GetRenderValue(tags, "yes").txt).addCallbackAndRunD(isShown => {
|
||||
self._is_dirty.setData(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,12 +15,10 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>;
|
||||
public readonly name;
|
||||
public readonly isOsmCache: boolean
|
||||
private readonly seenids: Set<string> = new Set<string>()
|
||||
public readonly layer: FilteredLayer;
|
||||
|
||||
public readonly tileIndex
|
||||
public readonly bbox;
|
||||
|
||||
private readonly seenids: Set<string> = new Set<string>()
|
||||
/**
|
||||
* Only used if the actual source is a tiled geojson.
|
||||
* A big feature might be contained in multiple tiles.
|
||||
|
@ -32,7 +30,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
public constructor(flayer: FilteredLayer,
|
||||
zxy?: [number, number, number],
|
||||
options?: {
|
||||
featureIdBlacklist?: UIEventSource<Set<string>>
|
||||
featureIdBlacklist?: UIEventSource<Set<string>>
|
||||
}) {
|
||||
|
||||
if (flayer.layerDef.source.geojsonZoomLevel !== undefined && zxy === undefined) {
|
||||
|
@ -45,18 +43,18 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
if (zxy !== undefined) {
|
||||
const [z, x, y] = zxy;
|
||||
let tile_bbox = BBox.fromTile(z, x, y)
|
||||
let bounds : { minLat: number, maxLat: number, minLon: number, maxLon: number } = tile_bbox
|
||||
if(this.layer.layerDef.source.mercatorCrs){
|
||||
let bounds: { minLat: number, maxLat: number, minLon: number, maxLon: number } = tile_bbox
|
||||
if (this.layer.layerDef.source.mercatorCrs) {
|
||||
bounds = tile_bbox.toMercator()
|
||||
}
|
||||
url = url
|
||||
.replace('{z}', "" + z)
|
||||
.replace('{x}', "" + x)
|
||||
.replace('{y}', "" + y)
|
||||
.replace('{y_min}',""+bounds.minLat)
|
||||
.replace('{y_max}',""+bounds.maxLat)
|
||||
.replace('{x_min}',""+bounds.minLon)
|
||||
.replace('{x_max}',""+bounds.maxLon)
|
||||
.replace('{y_min}', "" + bounds.minLat)
|
||||
.replace('{y_max}', "" + bounds.maxLat)
|
||||
.replace('{x_min}', "" + bounds.minLon)
|
||||
.replace('{x_max}', "" + bounds.maxLon)
|
||||
|
||||
this.tileIndex = Tiles.tile_index(z, x, y)
|
||||
this.bbox = BBox.fromTile(z, x, y)
|
||||
|
@ -78,11 +76,11 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
const self = this;
|
||||
Utils.downloadJson(url)
|
||||
.then(json => {
|
||||
if(json.features === undefined || json.features === null){
|
||||
if (json.features === undefined || json.features === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(self.layer.layerDef.source.mercatorCrs){
|
||||
if (self.layer.layerDef.source.mercatorCrs) {
|
||||
json = GeoOperations.GeoJsonToWGS84(json)
|
||||
}
|
||||
|
||||
|
@ -110,7 +108,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
}
|
||||
self.seenids.add(props.id)
|
||||
|
||||
if(self.featureIdBlacklist?.data?.has(props.id)){
|
||||
if (self.featureIdBlacklist?.data?.has(props.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -122,7 +120,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
newFeatures.push({feature: feature, freshness: freshness})
|
||||
}
|
||||
|
||||
if ( newFeatures.length == 0) {
|
||||
if (newFeatures.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ export class NewGeometryFromChangesFeatureSource implements FeatureSource {
|
|||
for (const kv of change.tags) {
|
||||
tags[kv.k] = kv.v
|
||||
}
|
||||
tags["id"] = change.type+"/"+change.id
|
||||
tags["id"] = change.type + "/" + change.id
|
||||
|
||||
tags["_backend"] = State.state.osmConnection._oauth_config.url
|
||||
|
||||
|
|
|
@ -6,17 +6,17 @@ import FeatureSource, {Tiled} from "../FeatureSource";
|
|||
import {UIEventSource} from "../../UIEventSource";
|
||||
import {BBox} from "../../BBox";
|
||||
|
||||
export default class RememberingSource implements FeatureSource , Tiled{
|
||||
export default class RememberingSource implements FeatureSource, Tiled {
|
||||
|
||||
public readonly features: UIEventSource<{ feature: any, freshness: Date }[]>;
|
||||
public readonly name;
|
||||
public readonly tileIndex : number
|
||||
public readonly bbox : BBox
|
||||
public readonly tileIndex: number
|
||||
public readonly bbox: BBox
|
||||
|
||||
constructor(source: FeatureSource & Tiled) {
|
||||
const self = this;
|
||||
this.name = "RememberingSource of " + source.name;
|
||||
this.tileIndex= source.tileIndex
|
||||
this.tileIndex = source.tileIndex
|
||||
this.bbox = source.bbox;
|
||||
|
||||
const empty = [];
|
||||
|
|
|
@ -7,7 +7,6 @@ import FeatureSource from "../FeatureSource";
|
|||
import PointRenderingConfig from "../../../Models/ThemeConfig/PointRenderingConfig";
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
|
||||
|
||||
export default class RenderingMultiPlexerFeatureSource {
|
||||
public readonly features: UIEventSource<(any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[]>;
|
||||
|
||||
|
@ -29,10 +28,10 @@ export default class RenderingMultiPlexerFeatureSource {
|
|||
|
||||
const lineRenderObjects = layer.lineRendering
|
||||
|
||||
const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined })[] = [];
|
||||
const withIndex: (any & { pointRenderingIndex: number | undefined, lineRenderingIndex: number | undefined, multiLineStringIndex: number | undefined })[] = [];
|
||||
|
||||
|
||||
function addAsPoint(feat, rendering, coordinate) {
|
||||
function addAsPoint(feat, rendering, coordinate) {
|
||||
const patched = {
|
||||
...feat,
|
||||
pointRenderingIndex: rendering.index
|
||||
|
@ -55,12 +54,14 @@ export default class RenderingMultiPlexerFeatureSource {
|
|||
})
|
||||
}
|
||||
} else {
|
||||
// This is a a line
|
||||
// This is a a line: add the centroids
|
||||
for (const rendering of centroidRenderings) {
|
||||
addAsPoint(feat, rendering, GeoOperations.centerpointCoordinates(feat))
|
||||
}
|
||||
|
||||
if (feat.geometry.type === "LineString") {
|
||||
|
||||
// Add start- and endpoints
|
||||
const coordinates = feat.geometry.coordinates
|
||||
for (const rendering of startRenderings) {
|
||||
addAsPoint(feat, rendering, coordinates[0])
|
||||
|
@ -69,33 +70,45 @@ export default class RenderingMultiPlexerFeatureSource {
|
|||
const coordinate = coordinates[coordinates.length - 1]
|
||||
addAsPoint(feat, rendering, coordinate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (feat.geometry.type === "MultiLineString") {
|
||||
const lineList = feat.geometry.coordinates
|
||||
for (const coordinates of lineList) {
|
||||
|
||||
for (const rendering of startRenderings) {
|
||||
const coordinate = coordinates[0]
|
||||
addAsPoint(feat, rendering, coordinate)
|
||||
}
|
||||
for (const rendering of endRenderings) {
|
||||
const coordinate = coordinates[coordinates.length - 1]
|
||||
addAsPoint(feat, rendering, coordinate)
|
||||
if (feat.geometry.type === "MultiLineString") {
|
||||
// Multilinestrings get a special treatment: we split them into their subparts before rendering
|
||||
const lineList: [number, number][][] = feat.geometry.coordinates
|
||||
|
||||
for (let i1 = 0; i1 < lineList.length; i1++) {
|
||||
const coordinates = lineList[i1];
|
||||
|
||||
for (let i = 0; i < lineRenderObjects.length; i++) {
|
||||
const orig = {
|
||||
...feat,
|
||||
lineRenderingIndex: i,
|
||||
multiLineStringIndex: i1
|
||||
}
|
||||
orig.geometry.coordinates = coordinates
|
||||
orig.geometry.type = "LineString"
|
||||
withIndex.push(orig)
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
// AT last, add it 'as is' to what we should render
|
||||
for (let i = 0; i < lineRenderObjects.length; i++) {
|
||||
withIndex.push({
|
||||
...feat,
|
||||
lineRenderingIndex: i
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < lineRenderObjects.length; i++) {
|
||||
withIndex.push({
|
||||
...feat,
|
||||
lineRenderingIndex: i
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return withIndex;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -4,17 +4,18 @@ import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
|||
import {BBox} from "../../BBox";
|
||||
|
||||
export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled {
|
||||
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]);
|
||||
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>;
|
||||
public readonly name: string = "SimpleFeatureSource";
|
||||
public readonly layer: FilteredLayer;
|
||||
public readonly bbox: BBox = BBox.global;
|
||||
public readonly tileIndex: number;
|
||||
|
||||
constructor(layer: FilteredLayer, tileIndex: number) {
|
||||
constructor(layer: FilteredLayer, tileIndex: number, featureSource?: UIEventSource<{ feature:any; freshness: Date }[]>) {
|
||||
this.name = "SimpleFeatureSource(" + layer.layerDef.id + ")"
|
||||
this.layer = layer
|
||||
this.tileIndex = tileIndex ?? 0;
|
||||
this.bbox = BBox.fromTileIndex(this.tileIndex)
|
||||
this.features = featureSource ?? new UIEventSource<{ feature: any; freshness: Date }[]>([]);
|
||||
}
|
||||
|
||||
}
|
|
@ -13,35 +13,35 @@ export default class TileFreshnessCalculator {
|
|||
* @param tileId
|
||||
* @param freshness
|
||||
*/
|
||||
public addTileLoad(tileId: number, freshness: Date){
|
||||
public addTileLoad(tileId: number, freshness: Date) {
|
||||
const existingFreshness = this.freshnessFor(...Tiles.tile_from_index(tileId))
|
||||
if(existingFreshness >= freshness){
|
||||
if (existingFreshness >= freshness) {
|
||||
return;
|
||||
}
|
||||
this.freshnesses.set(tileId, freshness)
|
||||
|
||||
// Do we have freshness for the neighbouring tiles? If so, we can mark the tile above as loaded too!
|
||||
let [z, x, y] = Tiles.tile_from_index(tileId)
|
||||
if(z === 0){
|
||||
if (z === 0) {
|
||||
return;
|
||||
}
|
||||
x = x - (x % 2) // Make the tiles always even
|
||||
y = y - (y % 2)
|
||||
|
||||
const ul = this.freshnessFor(z, x, y)?.getTime()
|
||||
if(ul === undefined){
|
||||
if (ul === undefined) {
|
||||
return
|
||||
}
|
||||
const ur = this.freshnessFor(z, x + 1, y)?.getTime()
|
||||
if(ur === undefined){
|
||||
if (ur === undefined) {
|
||||
return
|
||||
}
|
||||
const ll = this.freshnessFor(z, x, y + 1)?.getTime()
|
||||
if(ll === undefined){
|
||||
if (ll === undefined) {
|
||||
return
|
||||
}
|
||||
const lr = this.freshnessFor(z, x + 1, y + 1)?.getTime()
|
||||
if(lr === undefined){
|
||||
if (lr === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -50,21 +50,21 @@ export default class TileFreshnessCalculator {
|
|||
date.setTime(leastFresh)
|
||||
this.addTileLoad(
|
||||
Tiles.tile_index(z - 1, Math.floor(x / 2), Math.floor(y / 2)),
|
||||
date
|
||||
date
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
public freshnessFor(z: number, x: number, y:number): Date {
|
||||
if(z < 0){
|
||||
public freshnessFor(z: number, x: number, y: number): Date {
|
||||
if (z < 0) {
|
||||
return undefined
|
||||
}
|
||||
const tileId = Tiles.tile_index(z, x, y)
|
||||
if(this.freshnesses.has(tileId)) {
|
||||
if (this.freshnesses.has(tileId)) {
|
||||
return this.freshnesses.get(tileId)
|
||||
}
|
||||
// recurse up
|
||||
return this.freshnessFor(z - 1, Math.floor(x /2), Math.floor(y / 2))
|
||||
return this.freshnessFor(z - 1, Math.floor(x / 2), Math.floor(y / 2))
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,8 @@ import {Tiles} from "../../../Models/TileRange";
|
|||
* A tiled source which dynamically loads the required tiles at a fixed zoom level
|
||||
*/
|
||||
export default class DynamicTileSource implements TileHierarchy<FeatureSourceForLayer & Tiled> {
|
||||
private readonly _loadedTiles = new Set<number>();
|
||||
|
||||
public readonly loadedTiles: Map<number, FeatureSourceForLayer & Tiled>;
|
||||
private readonly _loadedTiles = new Set<number>();
|
||||
|
||||
constructor(
|
||||
layer: FilteredLayer,
|
||||
|
@ -24,7 +23,7 @@ export default class DynamicTileSource implements TileHierarchy<FeatureSourceFor
|
|||
) {
|
||||
const self = this;
|
||||
|
||||
this.loadedTiles = new Map<number,FeatureSourceForLayer & Tiled>()
|
||||
this.loadedTiles = new Map<number, FeatureSourceForLayer & Tiled>()
|
||||
const neededTiles = state.locationControl.map(
|
||||
location => {
|
||||
if (!layer.isDisplayed.data) {
|
||||
|
@ -54,14 +53,14 @@ export default class DynamicTileSource implements TileHierarchy<FeatureSourceFor
|
|||
, [layer.isDisplayed, state.leafletMap]).stabilized(250);
|
||||
|
||||
neededTiles.addCallbackAndRunD(neededIndexes => {
|
||||
console.log("Tiled geojson source ",layer.layerDef.id," needs", neededIndexes)
|
||||
console.log("Tiled geojson source ", layer.layerDef.id, " needs", neededIndexes)
|
||||
if (neededIndexes === undefined) {
|
||||
return;
|
||||
}
|
||||
for (const neededIndex of neededIndexes) {
|
||||
self._loadedTiles.add(neededIndex)
|
||||
const src = constructTile(Tiles.tile_from_index(neededIndex))
|
||||
if(src !== undefined){
|
||||
if (src !== undefined) {
|
||||
self.loadedTiles.set(neededIndex, src)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,10 @@ import {Or} from "../../Tags/Or";
|
|||
import {TagsFilter} from "../../Tags/TagsFilter";
|
||||
|
||||
export default class OsmFeatureSource {
|
||||
private readonly _backend: string;
|
||||
|
||||
public readonly isRunning: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
||||
public readonly downloadedTiles = new Set<number>()
|
||||
public rawDataHandlers: ((osmJson: any, tileId: number) => void)[] = []
|
||||
private readonly _backend: string;
|
||||
private readonly filteredLayers: UIEventSource<FilteredLayer[]>;
|
||||
private readonly handleTile: (fs: (FeatureSourceForLayer & Tiled)) => void;
|
||||
private isActive: UIEventSource<boolean>;
|
||||
|
@ -28,11 +29,8 @@ export default class OsmFeatureSource {
|
|||
},
|
||||
markTileVisited?: (tileId: number) => void
|
||||
};
|
||||
public readonly downloadedTiles = new Set<number>()
|
||||
private readonly allowedTags: TagsFilter;
|
||||
|
||||
public rawDataHandlers: ((osmJson: any, tileId: number) => void)[] = []
|
||||
|
||||
constructor(options: {
|
||||
handleTile: (tile: FeatureSourceForLayer & Tiled) => void;
|
||||
isActive: UIEventSource<boolean>,
|
||||
|
@ -57,7 +55,7 @@ export default class OsmFeatureSource {
|
|||
|
||||
neededTiles = neededTiles.filter(tile => !self.downloadedTiles.has(tile))
|
||||
|
||||
if(neededTiles.length == 0){
|
||||
if (neededTiles.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -73,7 +71,7 @@ export default class OsmFeatureSource {
|
|||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}finally {
|
||||
} finally {
|
||||
console.log("Done")
|
||||
self.isRunning.setData(false)
|
||||
}
|
||||
|
|
|
@ -11,17 +11,14 @@ Currently, they are:
|
|||
When the data enters from Overpass or from the OSM-API, they are first distributed per layer:
|
||||
|
||||
OVERPASS | ---PerLayerFeatureSource---> FeatureSourceForLayer[]
|
||||
OSM |
|
||||
OSM |
|
||||
|
||||
The GeoJSon files (not tiled) are then added to this list
|
||||
|
||||
A single FeatureSourcePerLayer is then further handled by splitting it into a tile hierarchy.
|
||||
|
||||
|
||||
|
||||
In order to keep thins snappy, they are distributed over a tiled database per layer.
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
`cached-featuresbookcases` is the old key used `cahced-features{themeid}` and should be cleaned up
|
|
@ -8,9 +8,8 @@ import {BBox} from "../../BBox";
|
|||
|
||||
export class TileHierarchyMerger implements TileHierarchy<FeatureSourceForLayer & Tiled> {
|
||||
public readonly loadedTiles: Map<number, FeatureSourceForLayer & Tiled> = new Map<number, FeatureSourceForLayer & Tiled>();
|
||||
private readonly sources: Map<number, UIEventSource<FeatureSource[]>> = new Map<number, UIEventSource<FeatureSource[]>>();
|
||||
|
||||
public readonly layer: FilteredLayer;
|
||||
private readonly sources: Map<number, UIEventSource<FeatureSource[]>> = new Map<number, UIEventSource<FeatureSource[]>>();
|
||||
private _handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource, index: number) => void;
|
||||
|
||||
constructor(layer: FilteredLayer, handleTile: (src: FeatureSourceForLayer & IndexedFeatureSource & Tiled, index: number) => void) {
|
||||
|
@ -24,7 +23,7 @@ export class TileHierarchyMerger implements TileHierarchy<FeatureSourceForLayer
|
|||
* @param src
|
||||
* @param index
|
||||
*/
|
||||
public registerTile(src: FeatureSource & Tiled) {
|
||||
public registerTile(src: FeatureSource & Tiled) {
|
||||
|
||||
const index = src.tileIndex
|
||||
if (this.sources.has(index)) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from "../FeatureSource";
|
||||
import {UIEventSource} from "../../UIEventSource";
|
||||
import {Utils} from "../../../Utils";
|
||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||
import TileHierarchy from "./TileHierarchy";
|
||||
import {Tiles} from "../../../Models/TileRange";
|
||||
|
@ -28,13 +27,13 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource,
|
|||
public readonly containedIds: UIEventSource<Set<string>>
|
||||
|
||||
public readonly bbox: BBox;
|
||||
public readonly tileIndex: number;
|
||||
private upper_left: TiledFeatureSource
|
||||
private upper_right: TiledFeatureSource
|
||||
private lower_left: TiledFeatureSource
|
||||
private lower_right: TiledFeatureSource
|
||||
private readonly maxzoom: number;
|
||||
private readonly options: TiledFeatureSourceOptions
|
||||
public readonly tileIndex: number;
|
||||
|
||||
private constructor(z: number, x: number, y: number, parent: TiledFeatureSource, options?: TiledFeatureSourceOptions) {
|
||||
this.z = z;
|
||||
|
@ -92,16 +91,16 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource,
|
|||
return root;
|
||||
}
|
||||
|
||||
private isSplitNeeded(featureCount: number){
|
||||
if(this.upper_left !== undefined){
|
||||
private isSplitNeeded(featureCount: number) {
|
||||
if (this.upper_left !== undefined) {
|
||||
// This tile has been split previously, so we keep on splitting
|
||||
return true;
|
||||
}
|
||||
if(this.z >= this.maxzoom){
|
||||
if (this.z >= this.maxzoom) {
|
||||
// We are not allowed to split any further
|
||||
return false
|
||||
}
|
||||
if(this.options.minZoomLevel !== undefined && this.z < this.options.minZoomLevel){
|
||||
if (this.options.minZoomLevel !== undefined && this.z < this.options.minZoomLevel) {
|
||||
// We must have at least this zoom level before we are allowed to start splitting
|
||||
return true
|
||||
}
|
||||
|
@ -155,7 +154,7 @@ export default class TiledFeatureSource implements Tiled, IndexedFeatureSource,
|
|||
} else {
|
||||
overlapsboundary.push(feature)
|
||||
}
|
||||
}else if (this.options.minZoomLevel === undefined) {
|
||||
} else if (this.options.minZoomLevel === undefined) {
|
||||
if (bbox.isContainedIn(this.upper_left.bbox)) {
|
||||
ulf.push(feature)
|
||||
} else if (bbox.isContainedIn(this.upper_right.bbox)) {
|
||||
|
|
|
@ -13,44 +13,6 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
|
|||
private readonly handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void;
|
||||
private readonly undefinedTiles: Set<number>;
|
||||
|
||||
public static GetFreshnesses(layerId: string): Map<number, Date> {
|
||||
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layerId + "-"
|
||||
const freshnesses = new Map<number, Date>()
|
||||
for (const key of Object.keys(localStorage)) {
|
||||
if (!(key.startsWith(prefix) && key.endsWith("-time"))) {
|
||||
continue
|
||||
}
|
||||
const index = Number(key.substring(prefix.length, key.length - "-time".length))
|
||||
const time = Number(localStorage.getItem(key))
|
||||
const freshness = new Date()
|
||||
freshness.setTime(time)
|
||||
freshnesses.set(index, freshness)
|
||||
}
|
||||
return freshnesses
|
||||
}
|
||||
|
||||
|
||||
static cleanCacheForLayer(layer: LayerConfig) {
|
||||
const now = new Date()
|
||||
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.id + "-"
|
||||
console.log("Cleaning tiles of ", prefix, "with max age",layer.maxAgeOfCache)
|
||||
for (const key of Object.keys(localStorage)) {
|
||||
if (!(key.startsWith(prefix) && key.endsWith("-time"))) {
|
||||
continue
|
||||
}
|
||||
const index = Number(key.substring(prefix.length, key.length - "-time".length))
|
||||
const time = Number(localStorage.getItem(key))
|
||||
const timeDiff = (now.getTime() - time) / 1000
|
||||
|
||||
if(timeDiff >= layer.maxAgeOfCache){
|
||||
const k = prefix+index;
|
||||
localStorage.removeItem(k)
|
||||
localStorage.removeItem(k+"-format")
|
||||
localStorage.removeItem(k+"-time")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(layer: FilteredLayer,
|
||||
handleFeatureSource: (src: FeatureSourceForLayer & Tiled, index: number) => void,
|
||||
state: {
|
||||
|
@ -110,6 +72,43 @@ export default class TiledFromLocalStorageSource implements TileHierarchy<Featur
|
|||
|
||||
}
|
||||
|
||||
public static GetFreshnesses(layerId: string): Map<number, Date> {
|
||||
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layerId + "-"
|
||||
const freshnesses = new Map<number, Date>()
|
||||
for (const key of Object.keys(localStorage)) {
|
||||
if (!(key.startsWith(prefix) && key.endsWith("-time"))) {
|
||||
continue
|
||||
}
|
||||
const index = Number(key.substring(prefix.length, key.length - "-time".length))
|
||||
const time = Number(localStorage.getItem(key))
|
||||
const freshness = new Date()
|
||||
freshness.setTime(time)
|
||||
freshnesses.set(index, freshness)
|
||||
}
|
||||
return freshnesses
|
||||
}
|
||||
|
||||
static cleanCacheForLayer(layer: LayerConfig) {
|
||||
const now = new Date()
|
||||
const prefix = SaveTileToLocalStorageActor.storageKey + "-" + layer.id + "-"
|
||||
console.log("Cleaning tiles of ", prefix, "with max age", layer.maxAgeOfCache)
|
||||
for (const key of Object.keys(localStorage)) {
|
||||
if (!(key.startsWith(prefix) && key.endsWith("-time"))) {
|
||||
continue
|
||||
}
|
||||
const index = Number(key.substring(prefix.length, key.length - "-time".length))
|
||||
const time = Number(localStorage.getItem(key))
|
||||
const timeDiff = (now.getTime() - time) / 1000
|
||||
|
||||
if (timeDiff >= layer.maxAgeOfCache) {
|
||||
const k = prefix + index;
|
||||
localStorage.removeItem(k)
|
||||
localStorage.removeItem(k + "-format")
|
||||
localStorage.removeItem(k + "-time")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private loadTile(neededIndex: number) {
|
||||
try {
|
||||
const key = SaveTileToLocalStorageActor.storageKey + "-" + this.layer.layerDef.id + "-" + neededIndex
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
import * as turf from '@turf/turf'
|
||||
import {BBox} from "./BBox";
|
||||
import togpx from "togpx"
|
||||
import Constants from "../Models/Constants";
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||
|
||||
export class GeoOperations {
|
||||
|
||||
private static readonly _earthRadius = 6378137;
|
||||
private static readonly _originShift = 2 * Math.PI * GeoOperations._earthRadius / 2;
|
||||
|
||||
static surfaceAreaInSqMeters(feature: any) {
|
||||
return turf.area(feature);
|
||||
}
|
||||
|
@ -24,12 +30,12 @@ export class GeoOperations {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the distance between the two points in kilometers
|
||||
* Returns the distance between the two points in meters
|
||||
* @param lonlat0
|
||||
* @param lonlat1
|
||||
*/
|
||||
static distanceBetween(lonlat0: [number, number], lonlat1: [number, number]) {
|
||||
return turf.distance(lonlat0, lonlat1)
|
||||
return turf.distance(lonlat0, lonlat1) * 1000
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -237,7 +243,7 @@ export class GeoOperations {
|
|||
* @param point Point defined as [lon, lat]
|
||||
*/
|
||||
public static nearestPoint(way, point: [number, number]) {
|
||||
if(way.geometry.type === "Polygon"){
|
||||
if (way.geometry.type === "Polygon") {
|
||||
way = {...way}
|
||||
way.geometry = {...way.geometry}
|
||||
way.geometry.type = "LineString"
|
||||
|
@ -292,10 +298,6 @@ export class GeoOperations {
|
|||
return headerValuesOrdered.map(v => JSON.stringify(v)).join(",") + "\n" + lines.join("\n")
|
||||
}
|
||||
|
||||
|
||||
private static readonly _earthRadius = 6378137;
|
||||
private static readonly _originShift = 2 * Math.PI * GeoOperations._earthRadius / 2;
|
||||
|
||||
//Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:900913
|
||||
public static ConvertWgs84To900913(lonLat: [number, number]): [number, number] {
|
||||
const lon = lonLat[0];
|
||||
|
@ -316,10 +318,35 @@ export class GeoOperations {
|
|||
return [x, y];
|
||||
}
|
||||
|
||||
public static GeoJsonToWGS84(geojson){
|
||||
public static GeoJsonToWGS84(geojson) {
|
||||
return turf.toWgs84(geojson)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to remove points which do not contribute much to the general outline.
|
||||
* Points for which the angle is ~ 180° are removed
|
||||
* @param coordinates
|
||||
* @constructor
|
||||
*/
|
||||
public static SimplifyCoordinates(coordinates: [number, number][]) {
|
||||
const newCoordinates = []
|
||||
for (let i = 1; i < coordinates.length - 1; i++) {
|
||||
const coordinate = coordinates[i];
|
||||
const prev = coordinates[i - 1]
|
||||
const next = coordinates[i + 1]
|
||||
const b0 = turf.bearing(prev, coordinate, {final: true})
|
||||
const b1 = turf.bearing(coordinate, next)
|
||||
|
||||
const diff = Math.abs(b1 - b0)
|
||||
if (diff < 2) {
|
||||
continue
|
||||
}
|
||||
newCoordinates.push(coordinate)
|
||||
}
|
||||
return newCoordinates
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the intersection between two features.
|
||||
* Returns the length if intersecting a linestring and a (multi)polygon (in meters), returns a surface area (in m²) if intersecting two (multi)polygons
|
||||
|
@ -412,31 +439,176 @@ export class GeoOperations {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to remove points which do not contribute much to the general outline.
|
||||
* Points for which the angle is ~ 180° are removed
|
||||
* @param coordinates
|
||||
* @constructor
|
||||
*/
|
||||
public static SimplifyCoordinates(coordinates: [number, number][]){
|
||||
const newCoordinates = []
|
||||
for (let i = 1; i < coordinates.length - 1; i++){
|
||||
const coordinate = coordinates[i];
|
||||
const prev = coordinates[i - 1]
|
||||
const next = coordinates[i + 1]
|
||||
const b0 = turf.bearing(prev, coordinate, {final: true})
|
||||
const b1 = turf.bearing(coordinate, next)
|
||||
public static AsGpx(feature, generatedWithLayer?: LayerConfig){
|
||||
|
||||
const diff = Math.abs(b1 - b0)
|
||||
if(diff < 2){
|
||||
continue
|
||||
const metadata = {}
|
||||
const tags = feature.properties
|
||||
|
||||
if(generatedWithLayer !== undefined){
|
||||
|
||||
metadata["name"] = generatedWithLayer.title?.GetRenderValue(tags)?.Subs(tags)?.txt
|
||||
metadata["desc"] = "Generated with MapComplete layer "+generatedWithLayer.id
|
||||
if(tags._backend?.contains("openstreetmap")){
|
||||
metadata["copyright"]= "Data copyrighted by OpenStreetMap-contributors, freely available under ODbL. See https://www.openstreetmap.org/copyright"
|
||||
metadata["author"] = tags["_last_edit:contributor"]
|
||||
metadata["link"]= "https://www.openstreetmap.org/"+tags.id
|
||||
metadata["time"] = tags["_last_edit:timestamp"]
|
||||
}else{
|
||||
metadata["time"] = new Date().toISOString()
|
||||
}
|
||||
newCoordinates.push(coordinate)
|
||||
}
|
||||
return newCoordinates
|
||||
|
||||
return togpx(feature, {
|
||||
creator: "MapComplete "+Constants.vNumber,
|
||||
metadata
|
||||
})
|
||||
}
|
||||
|
||||
public static IdentifieCommonSegments(coordinatess: [number,number][][] ): {
|
||||
originalIndex: number,
|
||||
segmentShardWith: number[],
|
||||
coordinates: []
|
||||
}[]{
|
||||
|
||||
// An edge. Note that the edge might be reversed to fix the sorting condition: start[0] < end[0] && (start[0] != end[0] || start[0] < end[1])
|
||||
type edge = {start: [number, number], end: [number, number], intermediate: [number,number][], members: {index:number, isReversed: boolean}[]}
|
||||
|
||||
// The strategy:
|
||||
// 1. Index _all_ edges from _every_ linestring. Index them by starting key, gather which relations run over them
|
||||
// 2. Join these edges back together - as long as their membership groups are the same
|
||||
// 3. Convert to results
|
||||
|
||||
const allEdgesByKey = new Map<string, edge>()
|
||||
|
||||
for (let index = 0; index < coordinatess.length; index++){
|
||||
const coordinates = coordinatess[index];
|
||||
for (let i = 0; i < coordinates.length - 1; i++){
|
||||
|
||||
const c0 = coordinates[i];
|
||||
const c1 = coordinates[i + 1]
|
||||
const isReversed = (c0[0] > c1[0]) || (c0[0] == c1[0] && c0[1] > c1[1])
|
||||
|
||||
let key : string
|
||||
if(isReversed){
|
||||
key = ""+c1+";"+c0
|
||||
}else{
|
||||
key = ""+c0+";"+c1
|
||||
}
|
||||
const member = {index, isReversed}
|
||||
if(allEdgesByKey.has(key)){
|
||||
allEdgesByKey.get(key).members.push(member)
|
||||
continue
|
||||
}
|
||||
|
||||
let edge : edge;
|
||||
if(!isReversed){
|
||||
edge = {
|
||||
start : c0,
|
||||
end: c1,
|
||||
members: [member],
|
||||
intermediate: []
|
||||
}
|
||||
}else{
|
||||
edge = {
|
||||
start : c1,
|
||||
end: c0,
|
||||
members: [member],
|
||||
intermediate: []
|
||||
}
|
||||
}
|
||||
allEdgesByKey.set(key, edge)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Lets merge them back together!
|
||||
|
||||
let didMergeSomething = false;
|
||||
let allMergedEdges = Array.from(allEdgesByKey.values())
|
||||
const allEdgesByStartPoint = new Map<string, edge[]>()
|
||||
for (const edge of allMergedEdges) {
|
||||
|
||||
edge.members.sort((m0, m1) => m0.index - m1.index)
|
||||
|
||||
const kstart = edge.start+""
|
||||
if(!allEdgesByStartPoint.has(kstart)){
|
||||
allEdgesByStartPoint.set(kstart, [])
|
||||
}
|
||||
allEdgesByStartPoint.get(kstart).push(edge)
|
||||
}
|
||||
|
||||
|
||||
function membersAreCompatible(first:edge, second:edge): boolean{
|
||||
// There must be an exact match between the members
|
||||
if(first.members === second.members){
|
||||
return true
|
||||
}
|
||||
|
||||
if(first.members.length !== second.members.length){
|
||||
return false
|
||||
}
|
||||
|
||||
// Members are sorted and have the same length, so we can check quickly
|
||||
for (let i = 0; i < first.members.length; i++) {
|
||||
const m0 = first.members[i]
|
||||
const m1 = second.members[i]
|
||||
if(m0.index !== m1.index || m0.isReversed !== m1.isReversed){
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Allrigth, they are the same, lets mark this permanently
|
||||
second.members = first.members
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
do{
|
||||
didMergeSomething = false
|
||||
// We use 'allMergedEdges' as our running list
|
||||
const consumed = new Set<edge>()
|
||||
for (const edge of allMergedEdges) {
|
||||
// Can we make this edge longer at the end?
|
||||
if(consumed.has(edge)){
|
||||
continue
|
||||
}
|
||||
|
||||
console.log("Considering edge", edge)
|
||||
const matchingEndEdges = allEdgesByStartPoint.get(edge.end+"")
|
||||
console.log("Matchign endpoints:", matchingEndEdges)
|
||||
if(matchingEndEdges === undefined){
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < matchingEndEdges.length; i++){
|
||||
const endEdge = matchingEndEdges[i];
|
||||
|
||||
if(consumed.has(endEdge)){
|
||||
continue
|
||||
}
|
||||
|
||||
if(!membersAreCompatible(edge, endEdge)){
|
||||
continue
|
||||
}
|
||||
|
||||
// We can make the segment longer!
|
||||
didMergeSomething = true
|
||||
console.log("Merging ", edge, "with ", endEdge)
|
||||
edge.intermediate.push(edge.end)
|
||||
edge.end = endEdge.end
|
||||
consumed.add(endEdge)
|
||||
matchingEndEdges.splice(i, 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
allMergedEdges = allMergedEdges.filter(edge => !consumed.has(edge));
|
||||
|
||||
}while(didMergeSomething)
|
||||
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ export default class AllImageProviders {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const cacheKey = tags.data.id+tagKey
|
||||
const cacheKey = tags.data.id + tagKey
|
||||
const cached = this._cache.get(cacheKey)
|
||||
if (cached !== undefined) {
|
||||
return cached
|
||||
|
@ -45,7 +45,7 @@ export default class AllImageProviders {
|
|||
for (const imageProvider of AllImageProviders.ImageAttributionSource) {
|
||||
|
||||
let prefixes = imageProvider.defaultKeyPrefixes
|
||||
if(tagKey !== undefined){
|
||||
if (tagKey !== undefined) {
|
||||
prefixes = tagKey
|
||||
}
|
||||
|
||||
|
@ -54,11 +54,11 @@ export default class AllImageProviders {
|
|||
})
|
||||
allSources.push(singleSource)
|
||||
singleSource.addCallbackAndRunD(_ => {
|
||||
const all : ProvidedImage[] = [].concat(...allSources.map(source => source.data))
|
||||
const all: ProvidedImage[] = [].concat(...allSources.map(source => source.data))
|
||||
const uniq = []
|
||||
const seen = new Set<string>()
|
||||
for (const img of all) {
|
||||
if(seen.has(img.url)){
|
||||
if (seen.has(img.url)) {
|
||||
continue
|
||||
}
|
||||
seen.add(img.url)
|
||||
|
|
|
@ -10,20 +10,15 @@ export default class GenericImageProvider extends ImageProvider {
|
|||
this._valuePrefixBlacklist = valuePrefixBlacklist;
|
||||
}
|
||||
|
||||
|
||||
protected DownloadAttribution(url: string) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
||||
|
||||
if (this._valuePrefixBlacklist.some(prefix => value.startsWith(prefix))) {
|
||||
return []
|
||||
}
|
||||
|
||||
try{
|
||||
try {
|
||||
new URL(value)
|
||||
}catch (_){
|
||||
} catch (_) {
|
||||
// Not a valid URL
|
||||
return []
|
||||
}
|
||||
|
@ -39,5 +34,9 @@ export default class GenericImageProvider extends ImageProvider {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
protected DownloadAttribution(url: string) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -27,8 +27,6 @@ export default abstract class ImageProvider {
|
|||
|
||||
public abstract SourceIcon(backlinkSource?: string): BaseUIElement;
|
||||
|
||||
protected abstract DownloadAttribution(url: string): Promise<LicenseInfo>;
|
||||
|
||||
/**
|
||||
* Given a properies object, maps it onto _all_ the available pictures for this imageProvider
|
||||
*/
|
||||
|
@ -77,4 +75,6 @@ export default abstract class ImageProvider {
|
|||
|
||||
public abstract ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]>;
|
||||
|
||||
protected abstract DownloadAttribution(url: string): Promise<LicenseInfo>;
|
||||
|
||||
}
|