diff --git a/Customizations/AllKnownLayers.ts b/Customizations/AllKnownLayers.ts index 2335fe710..002b8b5f4 100644 --- a/Customizations/AllKnownLayers.ts +++ b/Customizations/AllKnownLayers.ts @@ -1,6 +1,13 @@ import * as known_layers from "../assets/generated/known_layers_and_themes.json" import {Utils} from "../Utils"; 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"; +import {AllKnownLayouts} from "./AllKnownLayouts"; +import {isNullOrUndefined} from "util"; +import {Layer} from "leaflet"; export default class AllKnownLayers { @@ -9,6 +16,16 @@ export default class AllKnownLayers { public static sharedLayers: Map<string, LayerConfig> = AllKnownLayers.getSharedLayers(); public static sharedLayersJson: Map<string, any> = AllKnownLayers.getSharedLayersJson(); + + public static added_by_default: string[] = ["gps_location", "home_location"] + 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 +33,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) @@ -57,5 +73,4 @@ export default class AllKnownLayers { return sharedLayers; } - } diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index 0281a7649..763b81505 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -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 { @@ -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 = [] diff --git a/Docs/BuiltinLayers.md b/Docs/BuiltinLayers.md new file mode 100644 index 000000000..653546195 --- /dev/null +++ b/Docs/BuiltinLayers.md @@ -0,0 +1,145 @@ + + + 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) + - [type_node](#type_node) + - [conflation](#conflation) + - [left_right_style](#left_right_style) + + +### gps_location + + **This layer is included automatically in every theme. This layer might contain no points** [Go to the source code](../assets/layers/gps_location/gps_location.json) 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. + + - This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data. + + +### home_location + + **This layer is included automatically in every theme. This layer might contain no points** [Go to the source code](../assets/layers/home_location/home_location.json) 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. + + - This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data. + + +### type_node + + [Go to the source code](../assets/layers/type_node/type_node.json) 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. + + - This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data. + + +### conflation + + [Go to the source code](../assets/layers/conflation/conflation.json) 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. + + + + +### left_right_style + + [Go to the source code](../assets/layers/left_right_style/left_right_style.json) 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 + + - 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` + - Not rendered on the map by default. If you want to rendering this on the map, override `mapRenderings` + + + 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 + + [Go to the source code](../assets/layers/bicycle_library/bicycle_library.json) A facility where bicycles can be lent for longer period of times + + + + +#### 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 + + [Go to the source code](../assets/layers/map/map.json) A map, meant for tourists which is permanently installed in the public space + + + + +#### 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 \ No newline at end of file diff --git a/Docs/CalculatedTags.md b/Docs/CalculatedTags.md index 07f334db6..91b0dd568 100644 --- a/Docs/CalculatedTags.md +++ b/Docs/CalculatedTags.md @@ -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 \ No newline at end of file + + +This document is autogenerated from SimpleMetaTagger, ExtraFunction \ No newline at end of file diff --git a/Docs/SpecialInputElements.md b/Docs/SpecialInputElements.md index d6648fa3b..138054b30 100644 --- a/Docs/SpecialInputElements.md +++ b/Docs/SpecialInputElements.md @@ -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 \ No newline at end of file +Shows a color picker + +This document is autogenerated from ValidatedTextField.ts \ No newline at end of file diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index 35d03abc0..717e75e86 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -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 \ No newline at end of file + `{tag_apply(survey_date:=$_now:date, Surveyed today!)}` + +This document is autogenerated from UI/SpecialVisualisations.ts \ No newline at end of file diff --git a/Docs/TagInfo/mapcomplete_cafes_and_pubs.json b/Docs/TagInfo/mapcomplete_cafes_and_pubs.json index 0e002e665..a72df9b19 100644 --- a/Docs/TagInfo/mapcomplete_cafes_and_pubs.json +++ b/Docs/TagInfo/mapcomplete_cafes_and_pubs.json @@ -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')", diff --git a/Docs/TagInfo/mapcomplete_cyclofix.json b/Docs/TagInfo/mapcomplete_cyclofix.json index a21fd8ffa..f9a4eef42 100644 --- a/Docs/TagInfo/mapcomplete_cyclofix.json +++ b/Docs/TagInfo/mapcomplete_cyclofix.json @@ -966,13 +966,13 @@ }, { "key": "location", - "description": "Layer 'Bike parking' shows location=underground 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": "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 'Rooftop 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", diff --git a/Docs/TagInfo/mapcomplete_food.json b/Docs/TagInfo/mapcomplete_food.json index 4fde737a4..36d13bdce 100644 --- a/Docs/TagInfo/mapcomplete_food.json +++ b/Docs/TagInfo/mapcomplete_food.json @@ -305,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')", diff --git a/Docs/TagInfo/mapcomplete_fritures.json b/Docs/TagInfo/mapcomplete_fritures.json index fe6f8ab3c..42cf12774 100644 --- a/Docs/TagInfo/mapcomplete_fritures.json +++ b/Docs/TagInfo/mapcomplete_fritures.json @@ -310,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')", @@ -625,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')", diff --git a/Docs/TagInfo/mapcomplete_toilets.json b/Docs/TagInfo/mapcomplete_toilets.json index a02a8fd58..57fed0e1d 100644 --- a/Docs/TagInfo/mapcomplete_toilets.json +++ b/Docs/TagInfo/mapcomplete_toilets.json @@ -89,6 +89,64 @@ "description": "Layer 'Toilets' shows access=public with a fixed text, namely 'Public access' (in the MapComplete.osm.be theme 'Open Toilet Map')", "value": "public" }, + { + "key": "access", + "description": "Layer 'Toilets' shows and asks freeform values for key 'access' (in the MapComplete.osm.be theme 'Open Toilet Map')" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=yes with a fixed text, namely 'Public access' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "yes" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=customers with a fixed text, namely 'Only access to customers' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "customers" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=no with a fixed text, namely 'Not accessible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "no" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=key with a fixed text, namely 'Accessible, but one has to ask a key to enter' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "key" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=public with a fixed text, namely 'Public access' (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "public" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows and asks freeform values for key 'access' (in the MapComplete.osm.be theme 'Open Toilet Map')" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=yes with a fixed text, namely 'Public access' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "yes" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=customers with a fixed text, namely 'Only access to customers' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "customers" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=no with a fixed text, namely 'Not accessible' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "no" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=key with a fixed text, namely 'Accessible, but one has to ask a key to enter' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "key" + }, + { + "key": "access", + "description": "Layer 'Toilets' shows access=public with a fixed text, namely 'Public access' (in the MapComplete.osm.be theme 'Open Toilet Map')", + "value": "public" + }, { "key": "fee", "description": "Layer 'Toilets' shows fee=yes with a fixed text, namely 'These are paid toilets' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Open Toilet Map')", diff --git a/Docs/URL_Parameters.md b/Docs/URL_Parameters.md index a27eadc97..47e54a29b 100644 --- a/Docs/URL_Parameters.md +++ b/Docs/URL_Parameters.md @@ -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 \ No newline at end of file + Wether or not the layer with id <layer-id> is shown The default value is _true_ + +This document is autogenerated from QueryParameters \ No newline at end of file diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index e997afec5..efa32b31a 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -5,11 +5,10 @@ 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 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 +58,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,16 +181,16 @@ 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: { + id: "gps", "user:location": "yes", - "accuracy": location.accuracy, - "speed": location.speed, + ...location }, geometry: { type: "Point", diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 17c753ccf..0b2140b1b 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -75,6 +75,8 @@ export default class FeaturePipeline { constructor( handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void, state: { + readonly homeLocation: FeatureSourceForLayer & Tiled; + readonly currentUserLocation: FeatureSourceForLayer & Tiled; readonly filteredLayers: UIEventSource<FilteredLayer[]>, readonly locationControl: UIEventSource<Loc>, readonly selectedElement: UIEventSource<any>, @@ -152,6 +154,16 @@ export default class FeaturePipeline { continue; } + if (id === "gps_location") { + hierarchy.registerTile(state.currentUserLocation) + continue + } + + if (id === "home_location") { + hierarchy.registerTile(state.homeLocation) + continue + } + if (source.geojsonSource === undefined) { // This is an OSM layer // We load the cached values and register them diff --git a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts index fd98ad92e..138a3465e 100644 --- a/Logic/FeatureSource/Sources/SimpleFeatureSource.ts +++ b/Logic/FeatureSource/Sources/SimpleFeatureSource.ts @@ -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 }[]>([]); } } \ No newline at end of file diff --git a/Logic/Osm/ChangesetHandler.ts b/Logic/Osm/ChangesetHandler.ts index 26ccafd1a..08884e6d8 100644 --- a/Logic/Osm/ChangesetHandler.ts +++ b/Logic/Osm/ChangesetHandler.ts @@ -286,7 +286,7 @@ export class ChangesetHandler { ["language", Locale.language.data], ["host", window.location.host], ["path", path], - ["source", State.state.currentGPSLocation.data !== undefined ? "survey" : undefined], + ["source", State.state.currentUserLocation.features.data.length > 0 ? "survey" : undefined], ["imagery", State.state.backgroundLayer.data.id], ...changesetTags.map(cstag => [cstag.key, cstag.value]) ] diff --git a/Logic/State/MapState.ts b/Logic/State/MapState.ts index a4141f33c..44525dbf8 100644 --- a/Logic/State/MapState.ts +++ b/Logic/State/MapState.ts @@ -14,6 +14,8 @@ import {QueryParameters} from "../Web/QueryParameters"; import * as personal from "../../assets/themes/personal/personal.json"; import FilterConfig from "../../Models/ThemeConfig/FilterConfig"; import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer"; +import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource/FeatureSource"; +import SimpleFeatureSource from "../FeatureSource/Sources/SimpleFeatureSource"; /** * Contains all the leaflet-map related state @@ -44,7 +46,17 @@ export default class MapState extends UserRelatedState { /** * The location as delivered by the GPS */ - public currentGPSLocation: UIEventSource<Coordinates> = new UIEventSource<Coordinates>(undefined); + public currentUserLocation: FeatureSourceForLayer & Tiled; + + /** + * All previously visited points + */ + public historicalUserLocations: FeatureSourceForLayer & Tiled; + + /** + * A feature source containing the current home location of the user + */ + public homeLocation: FeatureSourceForLayer & Tiled public readonly mainMapObject: BaseUIElement & MinimapObj; @@ -120,6 +132,9 @@ export default class MapState extends UserRelatedState { this.lockBounds() this.AddAllOverlaysToMap(this.leafletMap) + + this.initHomeLocation() + this.initGpsLocation() } public AddAllOverlaysToMap(leafletMap: UIEventSource<any>) { @@ -164,6 +179,50 @@ export default class MapState extends UserRelatedState { }) } } + + private initGpsLocation(){ + // Initialize the gps layer data. This is emtpy for now, the actual writing happens in the Geolocationhandler + let gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "gps_location")[0] + this.currentUserLocation = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0)); + } + + private initHomeLocation() { + const empty = [] + const feature = UIEventSource.ListStabilized(this.osmConnection.userDetails.map(userDetails => { + + if (userDetails === undefined) { + return undefined; + } + const home = userDetails.home; + if (home === undefined) { + return undefined; + } + return [home.lon, home.lat] + })).map(homeLonLat => { + if (homeLonLat === undefined) { + return empty + } + return [{ + feature: { + "type": "Feature", + "properties": { + "id":"home", + "user:home": "yes", + "_lon": homeLonLat[0], + "_lat": homeLonLat[1] + }, + "geometry": { + "type": "Point", + "coordinates": homeLonLat + } + }, freshness: new Date() + }] + }) + + const flayer = this.filteredLayers.data.filter(l => l.layerDef.id === "home_location")[0] + this.homeLocation = new SimpleFeatureSource(flayer, Tiles.tile_index(0, 0, 0), feature) + + } private InitializeFilteredLayers() { // Initialize the filtered layers state diff --git a/Logic/State/UserRelatedState.ts b/Logic/State/UserRelatedState.ts index b31cd55b1..e75db0e42 100644 --- a/Logic/State/UserRelatedState.ts +++ b/Logic/State/UserRelatedState.ts @@ -36,10 +36,7 @@ export default class UserRelatedState extends ElementsState { * WHich other themes the user previously visited */ public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>; - /** - * A feature source containing the current home location of the user - */ - public homeLocation: FeatureSource + constructor(layoutToUse: LayoutConfig) { super(layoutToUse); @@ -88,7 +85,6 @@ export default class UserRelatedState extends ElementsState { this.InitializeLanguage(); - this.initHomeLocation() new SelectedElementTagsUpdater(this) } @@ -116,37 +112,4 @@ export default class UserRelatedState extends ElementsState { .ping(); } - private initHomeLocation() { - const empty = [] - const feature = UIEventSource.ListStabilized(this.osmConnection.userDetails.map(userDetails => { - - if (userDetails === undefined) { - return undefined; - } - const home = userDetails.home; - if (home === undefined) { - return undefined; - } - return [home.lon, home.lat] - })).map(homeLonLat => { - if (homeLonLat === undefined) { - return empty - } - return [{ - "type": "Feature", - "properties": { - "user:home": "yes", - "_lon": homeLonLat[0], - "_lat": homeLonLat[1] - }, - "geometry": { - "type": "Point", - "coordinates": homeLonLat - } - }] - }) - - this.homeLocation = new StaticFeatureSource(feature, false) - } - } \ No newline at end of file diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index a7e3e992a..34918c7dd 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -17,6 +17,10 @@ import LineRenderingConfigJson from "./Json/LineRenderingConfigJson"; import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson"; import {UIEventSource} from "../../Logic/UIEventSource"; import BaseUIElement from "../../UI/BaseUIElement"; +import Combine from "../../UI/Base/Combine"; +import Title from "../../UI/Base/Title"; +import List from "../../UI/Base/List"; +import Link from "../../UI/Base/Link"; export default class LayerConfig extends WithContextLoader { @@ -369,6 +373,41 @@ export default class LayerConfig extends WithContextLoader { } + public GenerateDocumentation(usedInThemes: string[], addedByDefault = false, canBeIncluded = true): BaseUIElement { + const extraProps = [] + + if (canBeIncluded) { + if (this.title === undefined) { + extraProps.push("Not clickable by default. If you import this layer in your theme, override `title` to make this clickable") + } + if (this.name === undefined) { + extraProps.push("Not visible in the layer selection by default. If you want to make this layer toggable, override `name`") + } + if (this.mapRendering.length === 0) { + extraProps.push("Not rendered on the map by default. If you want to rendering this on the map, override `mapRenderings`") + } + } else { + extraProps.push("This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data.") + } + + + let usingLayer: BaseUIElement[] = [] + if (usedInThemes?.length > 0 && !addedByDefault) { + usingLayer = [new Title("Themes using this layer", 4), + new List((usedInThemes ?? []).map(id => new Link(id, "https://mapcomplete.osm.be/" + id))) + ] + } + + return new Combine([ + new Title(this.id, 3), + addedByDefault ? "**This layer is included automatically in every theme. This layer might contain no points**" : undefined, + new Link("Go to the source code", `../assets/layers/${this.id}/${this.id}.json`), + this.description, + new List(extraProps), + ...usingLayer + ]) + } + public CustomCodeSnippets(): string[] { if (this.calculatedTags === undefined) { return []; diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index bfa9ceaf6..b1f8caae7 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -160,12 +160,16 @@ export default class LayoutConfig { if (typeof layer === "string") { if (AllKnownLayers.sharedLayersJson.get(layer) !== undefined) { if (json.overrideAll !== undefined) { - let lyr = JSON.parse(JSON.stringify(AllKnownLayers.sharedLayersJson[layer])); + let lyr = JSON.parse(JSON.stringify(AllKnownLayers.sharedLayersJson.get(layer))); const newLayer = new LayerConfig(Utils.Merge(json.overrideAll, lyr), `${json.id}+overrideAll.layers[${i}]`, official) result.push(newLayer) return } else { - result.push(AllKnownLayers.sharedLayers[layer]) + const shared = AllKnownLayers.sharedLayers.get(layer) + if(shared === undefined){ + throw `Shared layer ${layer} not found (at ${context}.layers[${i}])` + } + result.push(shared) return } } else { @@ -179,8 +183,7 @@ export default class LayoutConfig { layer = Utils.Merge(json.overrideAll, layer); } // @ts-ignore - const newLayer = new LayerConfig(layer, `${json.id}.layers[${i}]`, official) - result.push(newLayer) + result.push(new LayerConfig(layer, `${json.id}.layers[${i}]`, official)) return } @@ -204,13 +207,19 @@ export default class LayoutConfig { if (json.overrideAll !== undefined) { newLayer = Utils.Merge(json.overrideAll, newLayer); } - // @ts-ignore - const layerConfig = new LayerConfig(newLayer, `${json.id}.layers[${i}]`, official) - result.push(layerConfig) + result.push(new LayerConfig(newLayer, `${json.id}.layers[${i}]`, official)) return }) }); + + // Some special layers which are always included by default + for (const defaultLayer of AllKnownLayers.added_by_default) { + if(result.some(l => l?.id === defaultLayer)){ + continue; // Already added + } + result.push(AllKnownLayers.sharedLayers.get(defaultLayer)) + } return {layers: result, extractAllNodes: exportAllNodes} } diff --git a/UI/Base/Title.ts b/UI/Base/Title.ts index bc02d0c28..adf4b2dd7 100644 --- a/UI/Base/Title.ts +++ b/UI/Base/Title.ts @@ -19,14 +19,14 @@ export default class Title extends BaseUIElement { const embedded = " " + this._embedded.AsMarkdown() + " "; if (this._level == 1) { - return "\n" + embedded + "\n" + "=".repeat(embedded.length) + "\n\n" + return "\n\n" + embedded + "\n" + "=".repeat(embedded.length) + "\n\n" } if (this._level == 2) { - return "\n" + embedded + "\n" + "-".repeat(embedded.length) + "\n\n" + return "\n\n" + embedded + "\n" + "-".repeat(embedded.length) + "\n\n" } - return "\n" + "#".repeat(this._level) + embedded + "\n\n"; + return "\n\n" + "#".repeat(this._level) + embedded + "\n\n"; } protected InnerConstructElement(): HTMLElement { diff --git a/UI/BaseUIElement.ts b/UI/BaseUIElement.ts index 6e96e2616..b9c085fa5 100644 --- a/UI/BaseUIElement.ts +++ b/UI/BaseUIElement.ts @@ -161,7 +161,7 @@ export default abstract class BaseUIElement { } public AsMarkdown(): string { - throw "AsMarkdown is not implemented by " + this.constructor.name + throw "AsMarkdown is not implemented by " + this.constructor.name+"; implement it in the subclass" } protected abstract InnerConstructElement(): HTMLElement; diff --git a/UI/BigComponents/RightControls.ts b/UI/BigComponents/RightControls.ts index 9dbcd08a4..470774786 100644 --- a/UI/BigComponents/RightControls.ts +++ b/UI/BigComponents/RightControls.ts @@ -15,13 +15,6 @@ export default class RightControls extends Combine { state ) - new ShowDataLayer({ - layerToShow: AllKnownLayers.sharedLayers.get("gps_location"), - leafletMap: state.leafletMap, - enablePopups: true, - features: geolocatioHandler.currentLocation - }) - const geolocationButton = new Toggle( new MapControlButton( geolocatioHandler diff --git a/UI/SubstitutedTranslation.ts b/UI/SubstitutedTranslation.ts index 076073f75..69f57683f 100644 --- a/UI/SubstitutedTranslation.ts +++ b/UI/SubstitutedTranslation.ts @@ -63,7 +63,6 @@ export class SubstitutedTranslation extends VariableUiElement { this.SetClass("w-full") } - public static ExtractSpecialComponents(template: string, extraMappings: SpecialVisualization[] = []): { fixed?: string, special?: { diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 60c04ee9e..f3966508f 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -227,4 +227,8 @@ export class Translation extends BaseUIElement { } return allIcons.filter(icon => icon != undefined) } + + AsMarkdown(): string { + return this.txt + } } \ No newline at end of file diff --git a/assets/layers/conflation/conflation.json b/assets/layers/conflation/conflation.json index fe866afba..75220077e 100644 --- a/assets/layers/conflation/conflation.json +++ b/assets/layers/conflation/conflation.json @@ -1,6 +1,6 @@ { "id": "conflation", - "description": "This is a special meta_layer which render geometry-changes for inspection", + "description": "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.", "minzoom": 1, "source": { "osmTags": { diff --git a/assets/layers/gps_location/gps_location.json b/assets/layers/gps_location/gps_location.json index 59c2c4ef0..06f8323cc 100644 --- a/assets/layers/gps_location/gps_location.json +++ b/assets/layers/gps_location/gps_location.json @@ -1,6 +1,6 @@ { "id": "gps_location", - "description": "Meta layer showing the current location of the user", + "description": "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.", "minzoom": 0, "source": { "osmTags": "user:location=yes" diff --git a/assets/layers/home_location/home_location.json b/assets/layers/home_location/home_location.json index c2073d3b8..687d6e622 100644 --- a/assets/layers/home_location/home_location.json +++ b/assets/layers/home_location/home_location.json @@ -1,6 +1,6 @@ { "id": "home_location", - "description": "Meta layer showing the home location of the user", + "description": "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.", "minzoom": 0, "source": { "osmTags": "user:home=yes" diff --git a/assets/layers/left_right_style/left_right_style.json b/assets/layers/left_right_style/left_right_style.json index 1817702f4..9b8f82d13 100644 --- a/assets/layers/left_right_style/left_right_style.json +++ b/assets/layers/left_right_style/left_right_style.json @@ -1,6 +1,6 @@ { "id": "left_right_style", - "description": "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", + "description": "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", "source": { "osmTags": { "or": [ diff --git a/assets/layers/type_node/type_node.json b/assets/layers/type_node/type_node.json index 8a2a7823c..e7e3ba5f4 100644 --- a/assets/layers/type_node/type_node.json +++ b/assets/layers/type_node/type_node.json @@ -1,6 +1,6 @@ { "id": "type_node", - "description": "This is a special 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", + "description": "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.", "minzoom": 18, "source": { "osmTags": "id~node/.*" diff --git a/scripts/generateDocs.ts b/scripts/generateDocs.ts index af34c38e1..7061896be 100644 --- a/scripts/generateDocs.ts +++ b/scripts/generateDocs.ts @@ -11,13 +11,15 @@ import {QueryParameters} from "../Logic/Web/QueryParameters"; import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; import Minimap from "../UI/Base/Minimap"; import FeatureSwitchState from "../Logic/State/FeatureSwitchState"; +import AllKnownLayers from "../Customizations/AllKnownLayers"; +import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; Utils.runningFromConsole = true; function WriteFile(filename, html: string | BaseUIElement, autogenSource: string[]): void { writeFileSync(filename, new Combine([Translations.W(html), - "Generated from " + autogenSource.join(", ") + "\n\nThis document is autogenerated from " + autogenSource.join(", ") ]).AsMarkdown()); } @@ -25,7 +27,7 @@ WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage(), [" WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()]).SetClass("flex-col"), ["SimpleMetaTagger", "ExtraFunction"]) WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(), ["ValidatedTextField.ts"]); - +WriteFile("./Docs/BuiltinLayers.md", AllKnownLayouts.GenLayerOverviewText(), ["AllKnownLayers.ts"]) Minimap.createMiniMap = _ => { console.log("Not creating a minimap, it is disabled"); return undefined diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index 9d076dd84..9d4ee262b 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -196,7 +196,7 @@ class LayerOverviewUtils { } themeConfigs.push(theme) } catch (e) { - themeErrorCount.push("Could not parse theme " + themeFile["id"] + "due to", e) + themeErrorCount.push("Could not parse theme " + themeFile["id"] + " due to", e) } }