Fix deployment, fix documentation generation, add a small markdown generator
This commit is contained in:
parent
e480c97676
commit
8e72b70742
27 changed files with 478 additions and 399 deletions
|
@ -1,5 +1,8 @@
|
||||||
Metatags
|
|
||||||
--------
|
Metatags
|
||||||
|
==========
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Metatags are extra tags available, in order to display more data or to give better questions.
|
Metatags are extra tags available, in order to display more data or to give better questions.
|
||||||
|
|
||||||
|
@ -7,85 +10,154 @@ 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
|
**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
|
||||||
|
|
||||||
### \_lat, \_lon
|
|
||||||
|
Metatags calculated by MapComplete
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The following values are always calculated, by default, by MapComplete and are available automatically on all elements in every theme
|
||||||
|
|
||||||
|
|
||||||
|
### _lat, _lon
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The latitude and longitude of the point (or centerpoint in the case of a way/area)
|
The latitude and longitude of the point (or centerpoint in the case of a way/area)
|
||||||
|
|
||||||
### \_surface, \_surface:ha
|
|
||||||
|
### _surface, _surface:ha
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The surface area of the feature, in square meters and in hectare. Not set on points and ways
|
The surface area of the feature, in square meters and in hectare. Not set on points and ways
|
||||||
|
|
||||||
### \_length, \_length:km
|
|
||||||
|
|
||||||
The total length of a feature in meters (and in kilometers, rounded to one decimal for '\_length:km'). For a surface, the length of the perimeter
|
### _length, _length:km
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The total length of a feature in meters (and in kilometers, rounded to one decimal for '_length:km'). For a surface, the length of the perimeter
|
||||||
|
|
||||||
|
|
||||||
|
### _country
|
||||||
|
|
||||||
|
|
||||||
### \_country
|
|
||||||
|
|
||||||
The country code of the property (with latlon2country)
|
The country code of the property (with latlon2country)
|
||||||
|
|
||||||
### \_isOpen, \_isOpen:description
|
|
||||||
|
|
||||||
If 'opening\_hours' is present, it will add the current state of the feature (being 'yes' or 'no')
|
### _isOpen, _isOpen:description
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')
|
||||||
|
|
||||||
|
|
||||||
|
### _width:needed, _width:needed:no_pedestrians, _width:difference
|
||||||
|
|
||||||
|
|
||||||
### \_width:needed, \_width:needed:no\_pedestrians, \_width:difference
|
|
||||||
|
|
||||||
Legacy for a specific project calculating the needed width for safe traffic on a road. Only activated if 'width:carriageway' is present
|
Legacy for a specific project calculating the needed width for safe traffic on a road. Only activated if 'width:carriageway' is present
|
||||||
|
|
||||||
### \_direction:numerical, \_direction:leftright
|
|
||||||
|
|
||||||
\_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). \_direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map
|
### _direction:numerical, _direction:leftright
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_direction:numerical is a normalized, numerical direction based on 'camera:direction' or on 'direction'; it is only present if a valid direction is found (e.g. 38.5 or NE). _direction:leftright is either 'left' or 'right', which is left-looking on the map or 'right-looking' on the map
|
||||||
|
|
||||||
|
|
||||||
|
### _now:date, _now:datetime, _loaded:date, _loaded:_datetime
|
||||||
|
|
||||||
|
|
||||||
### \_now:date, \_now:datetime, \_loaded:date, \_loaded:\_datetime
|
|
||||||
|
|
||||||
Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely
|
Adds the time that the data got loaded - pretty much the time of downloading from overpass. The format is YYYY-MM-DD hh:mm, aka 'sortable' aka ISO-8601-but-not-entirely
|
||||||
|
|
||||||
### \_last\_edit:contributor, \_last\_edit:contributor:uid, \_last\_edit:changeset, \_last\_edit:timestamp, \_version\_number
|
|
||||||
|
### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Information about the last edit of this object.
|
Information about the last edit of this object.
|
||||||
|
|
||||||
Calculating tags with Javascript
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by default (e.g. **lat**, **lon**, **\_country**), as detailed above.
|
Calculating tags with Javascript
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by default (e.g. `lat`, `lon`, `_country`), as detailed above.
|
||||||
|
|
||||||
It is also possible to calculate your own tags - but this requires some javascript knowledge.
|
It is also possible to calculate your own tags - but this requires some javascript knowledge.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Before proceeding, some warnings:
|
Before proceeding, some warnings:
|
||||||
|
|
||||||
* DO NOT DO THIS AS BEGINNER
|
|
||||||
* **Only do this if all other techniques fail**. This should _not_ be done to create a rendering effect, only to calculate a specific value
|
|
||||||
* **THIS MIGHT BE DISABLED WITHOUT ANY NOTICE ON UNOFFICIAL THEMES**. As unofficial themes might be loaded from the internet, this is the equivalent of injecting arbitrary code into the client. It'll be disabled if abuse occurs.
|
|
||||||
|
|
||||||
In the layer object, add a field **calculatedTags**, e.g.:
|
|
||||||
|
|
||||||
"calculatedTags": \[ "\_someKey=javascript-expression", "name=feat.properties.name ?? feat.properties.ref ?? feat.properties.operator", "\_distanceCloserThen3Km=feat.distanceTo( some\_lon, some\_lat) < 3 ? 'yes' : 'no'" \]
|
- DO NOT DO THIS AS BEGINNER
|
||||||
|
- **Only do this if all other techniques fail** This should _not_ be done to create a rendering effect, only to calculate a specific value
|
||||||
|
- **THIS MIGHT BE DISABLED WITHOUT ANY NOTICE ON UNOFFICIAL THEMES** As unofficial themes might be loaded from the internet, this is the equivalent of injecting arbitrary code into the client. It'll be disabled if abuse occurs.
|
||||||
|
|
||||||
The above code will be executed for every feature in the layer. The feature is accessible as **feat** and is an amended geojson object: - **area** contains the surface area (in square meters) of the object - **lat** and **lon** contain the latitude and longitude Some advanced functions are available on **feat** as well:
|
|
||||||
|
|
||||||
* distanceTo
|
To enable this feature, add a field `calculatedTags` in the layer object, e.g.:
|
||||||
* overlapWith
|
|
||||||
* closest
|
|
||||||
* memberships
|
|
||||||
|
|
||||||
### 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
|
"calculatedTags": [
|
||||||
|
|
||||||
* longitude
|
"_someKey=javascript-expression",
|
||||||
* latitude
|
|
||||||
|
|
||||||
### overlapWith
|
"name=feat.properties.name ?? feat.properties.ref ?? feat.properties.operator",
|
||||||
|
|
||||||
Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point
|
"_distanceCloserThen3Km=feat.distanceTo( some_lon, some_lat) < 3 ? 'yes' : 'no'"
|
||||||
|
|
||||||
* ...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.
|
|
||||||
|
|
||||||
* list of features
|
|
||||||
|
|
||||||
### memberships
|
The above code will be executed for every feature in the layer. The feature is accessible as `feat` and is an amended geojson object:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- `area` contains the surface area (in square meters) of the object
|
||||||
|
- `lat` and `lon` contain the latitude and longitude
|
||||||
|
|
||||||
|
|
||||||
|
Some advanced functions are available on **feat** as well:
|
||||||
|
|
||||||
|
- distanceTo
|
||||||
|
- overlapWith
|
||||||
|
- closest
|
||||||
|
- memberships
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
0. longitude
|
||||||
|
1. latitude
|
||||||
|
|
||||||
|
### overlapWith
|
||||||
|
|
||||||
|
Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
0. list of features
|
||||||
|
|
||||||
|
### memberships
|
||||||
|
|
||||||
|
Gives a list of `{role: string, relation: Relation}`-objects, containing all the relations that this feature is part of.
|
||||||
|
|
||||||
|
For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')`
|
||||||
|
|
||||||
|
|
||||||
Gives a list of `{role: string, relation: Relation}`\-objects, containing all the relations that this feature is part of. For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')`
|
|
|
@ -1,61 +1 @@
|
||||||
### Special tag renderings
|
<h3>Special tag renderings</h3> In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's. General usage is <b>{func_name()}</b> or <b>{func_name(arg, someotherarg)}</b>. Note that you <i>do not</i> need to use quotes around your arguments, the comma is enough to seperate them. This also implies you cannot use a comma in your args <h3>all_tags</h3> Prints all key-value pairs of the object - used for debugging <ol> </ol> <b>Example usage: </b> {all_tags()} <h3>image_carousel</h3> 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) <ol> <li> <b>image key/prefix</b>: 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... Default: <span class='literal-code'>image</span> </li> <li> <b>smart search</b>: Also include images given via 'Wikidata', 'wikimedia_commons' and 'mapillary Default: <span class='literal-code'>true</span> </li> </ol> <b>Example usage: </b> {image_carousel(image,true)} <h3>image_upload</h3> Creates a button where a user can upload an image to IMGUR <ol> <li> <b>image-key</b>: Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added) Default: <span class='literal-code'>image</span> </li> </ol> <b>Example usage: </b> {image_upload(image)} <h3>reviews</h3> 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 <ol> <li> <b>subjectKey</b>: The key to use to determine the subject. If specified, the subject will be <b>tags[subjectKey]</b> Default: <span class='literal-code'>name</span> </li> <li> <b>fallback</b>: The identifier to use, if <i>tags[subjectKey]</i> as specified above is not available. This is effectively a fallback value </li> </ol> <b>Example usage: </b> <b>{reviews()}<b> for a vanilla review, <b>{reviews(name, play_forest)}</b> to review a play forest. If a name is known, the name will be used as identifier, otherwise 'play_forest' is used <h3>opening_hours_table</h3> Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag 'opening_hours'. <ol> <li> <b>key</b>: The tagkey from which the table is constructed. Default: <span class='literal-code'>opening_hours</span> </li> </ol> <b>Example usage: </b> {opening_hours_table(opening_hours)} <h3>live</h3> 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)} <ol> <li> <b>Url</b>: The URL to load </li> <li> <b>Shorthands</b>: A list of shorthands, of the format 'shorthandname:path.path.path'. Seperated by ; </li> <li> <b>path</b>: The path (or shorthand) that should be returned </li> </ol> <b>Example usage: </b> {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)} <h3>share_link</h3> Creates a link that (attempts to) open the native 'share'-screen <ol> <li> <b>url</b>: The url to share (default: current URL) </li> </ol> <b>Example usage: </b> {share_link()} to share the current page, {share_link(<some_url>)} to share the given url
|
||||||
|
|
||||||
In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.General usage is **{func\_name()}** or **{func\_name(arg, someotherarg)}**. Note that you _do not_ need to use quotes around your arguments, the comma is enough to seperate them. This also implies you cannot use a comma in your args
|
|
||||||
|
|
||||||
### 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)
|
|
||||||
|
|
||||||
1. **image key/prefix**: The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... Default: image
|
|
||||||
2. **smart search**: Also include images given via 'Wikidata', 'wikimedia\_commons' and 'mapillary Default: true
|
|
||||||
|
|
||||||
**Example usage:** {image\_carousel(image,true)}
|
|
||||||
|
|
||||||
### image\_upload
|
|
||||||
|
|
||||||
Creates a button where a user can upload an image to IMGUR
|
|
||||||
|
|
||||||
1. **image-key**: Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added) Default: image
|
|
||||||
|
|
||||||
**Example usage:** {image\_upload(image)}
|
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
1. **subjectKey**: The key to use to determine the subject. If specified, the subject will be **tags\[subjectKey\]** Default: name
|
|
||||||
2. **fallback**: The identifier to use, if _tags\[subjectKey\]_ 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'.
|
|
||||||
|
|
||||||
1. **key**: The tagkey from which the table is constructed. Default: opening\_hours
|
|
||||||
|
|
||||||
**Example usage:** {opening\_hours\_table(opening\_hours)}
|
|
||||||
|
|
||||||
### 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)}
|
|
||||||
|
|
||||||
1. **Url**: The URL to load
|
|
||||||
2. **Shorthands**: A list of shorthands, of the format 'shorthandname:path.path.path'. Seperated by ;
|
|
||||||
3. **path**: 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)}
|
|
||||||
|
|
||||||
### share\_link
|
|
||||||
|
|
||||||
Creates a link that (attempts to) open the native 'share'-screen
|
|
||||||
|
|
||||||
1. **url**: The url to share (default: current URL)
|
|
||||||
|
|
||||||
**Example usage:** {share\_link()} to share the current page, {share\_link()} to share the given url****
|
|
|
@ -33,22 +33,22 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "cyclestreet",
|
"key": "cyclestreet",
|
||||||
"description": "Layer 'Cyclestreets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
"description": "Layer 'Cyclestreets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
||||||
"value": "yes"
|
"value": "yes"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "maxspeed",
|
"key": "maxspeed",
|
||||||
"description": "Layer 'Cyclestreets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
"description": "Layer 'Cyclestreets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
||||||
"value": "30"
|
"value": "30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "overtaking:motor_vehicle",
|
"key": "overtaking:motor_vehicle",
|
||||||
"description": "Layer 'Cyclestreets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
"description": "Layer 'Cyclestreets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
||||||
"value": "no"
|
"value": "no"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "proposed:cyclestreet",
|
"key": "proposed:cyclestreet",
|
||||||
"description": "Layer 'Cyclestreets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets') Picking this answer will delete the key proposed:cyclestreet.",
|
"description": "Layer 'Cyclestreets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets') Picking this answer will delete the key proposed:cyclestreet.",
|
||||||
"value": ""
|
"value": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -113,22 +113,22 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "cyclestreet",
|
"key": "cyclestreet",
|
||||||
"description": "Layer 'Future cyclestreet' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
"description": "Layer 'Future cyclestreet' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
||||||
"value": "yes"
|
"value": "yes"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "maxspeed",
|
"key": "maxspeed",
|
||||||
"description": "Layer 'Future cyclestreet' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
"description": "Layer 'Future cyclestreet' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
||||||
"value": "30"
|
"value": "30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "overtaking:motor_vehicle",
|
"key": "overtaking:motor_vehicle",
|
||||||
"description": "Layer 'Future cyclestreet' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
"description": "Layer 'Future cyclestreet' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
||||||
"value": "no"
|
"value": "no"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "proposed:cyclestreet",
|
"key": "proposed:cyclestreet",
|
||||||
"description": "Layer 'Future cyclestreet' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets') Picking this answer will delete the key proposed:cyclestreet.",
|
"description": "Layer 'Future cyclestreet' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets') Picking this answer will delete the key proposed:cyclestreet.",
|
||||||
"value": ""
|
"value": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -203,22 +203,22 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "cyclestreet",
|
"key": "cyclestreet",
|
||||||
"description": "Layer 'All streets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
"description": "Layer 'All streets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
||||||
"value": "yes"
|
"value": "yes"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "maxspeed",
|
"key": "maxspeed",
|
||||||
"description": "Layer 'All streets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
"description": "Layer 'All streets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
||||||
"value": "30"
|
"value": "30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "overtaking:motor_vehicle",
|
"key": "overtaking:motor_vehicle",
|
||||||
"description": "Layer 'All streets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
"description": "Layer 'All streets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets')",
|
||||||
"value": "no"
|
"value": "no"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "proposed:cyclestreet",
|
"key": "proposed:cyclestreet",
|
||||||
"description": "Layer 'All streets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a maxspeeld of 30km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets') Picking this answer will delete the key proposed:cyclestreet.",
|
"description": "Layer 'All streets' shows cyclestreet=yes&maxspeed=30&overtaking:motor_vehicle=no&proposed:cyclestreet= with a fixed text, namely 'This street is a cyclestreet (and has a speed limit of 30 km/h)' and allows to pick this as a default answer (in the MapComplete.osm.be theme 'Cyclestreets') Picking this answer will delete the key proposed:cyclestreet.",
|
||||||
"value": ""
|
"value": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
URL-parameters and URL-hash
|
URL-parameters and URL-hash
|
||||||
============================
|
============================
|
||||||
|
|
||||||
|
@ -18,125 +19,128 @@ 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.
|
Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case.
|
||||||
|
|
||||||
custom-css (broken)
|
|
||||||
------------
|
|
||||||
If specified, the custom css from the given link will be loaded additionaly
|
|
||||||
|
|
||||||
|
layer-control-toggle
|
||||||
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_
|
|
||||||
|
|
||||||
layout
|
|
||||||
--------
|
|
||||||
The layout to load into MapComplete
|
|
||||||
|
|
||||||
userlayout
|
|
||||||
------------
|
|
||||||
If not 'false', a custom (non-official) theme is loaded. This custom layout can be done in multiple ways:
|
|
||||||
|
|
||||||
- The hash of the URL contains a base64-encoded .json-file containing the theme definition
|
|
||||||
- The hash of the URL contains a lz-compressed .json-file, as generated by the custom theme generator
|
|
||||||
- The parameter itself is an URL, in which case that URL will be downloaded. It should point to a .json of a theme
|
|
||||||
The default value is _false_
|
|
||||||
|
|
||||||
layer-control-toggle
|
|
||||||
----------------------
|
----------------------
|
||||||
Whether or not the layer control is shown
|
|
||||||
The default value is _false_
|
|
||||||
|
|
||||||
tab
|
Whether or not the layer control is shown The default value is _false_
|
||||||
|
|
||||||
|
|
||||||
|
tab
|
||||||
-----
|
-----
|
||||||
The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets)
|
|
||||||
The default value is _0_
|
|
||||||
|
|
||||||
z
|
The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets) The default value is _0_
|
||||||
|
|
||||||
|
|
||||||
|
z
|
||||||
---
|
---
|
||||||
The initial/current zoom level
|
|
||||||
The default value is set by the theme
|
|
||||||
|
|
||||||
lat
|
The initial/current zoom level The default value is _0_
|
||||||
|
|
||||||
|
|
||||||
|
lat
|
||||||
-----
|
-----
|
||||||
The initial/current latitude
|
|
||||||
The default value is set by the theme
|
|
||||||
|
|
||||||
lon
|
The initial/current latitude The default value is _0_
|
||||||
|
|
||||||
|
|
||||||
|
lon
|
||||||
-----
|
-----
|
||||||
The initial/current longitude of the app
|
|
||||||
The default value is set by the theme
|
|
||||||
|
|
||||||
fs-userbadge
|
The initial/current longitude of the app The default value is _0_
|
||||||
|
|
||||||
|
|
||||||
|
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 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-layers
|
Disables/Enables the search bar The default value is _true_
|
||||||
|
|
||||||
|
|
||||||
|
fs-layers
|
||||||
-----------
|
-----------
|
||||||
Disables/Enables the layer control
|
|
||||||
The default value is _true_
|
|
||||||
|
|
||||||
fs-add-new
|
Disables/Enables the layer control 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 '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
|
Disables/enables the help menu or welcome message The default value is _true_
|
||||||
|
|
||||||
|
|
||||||
|
fs-iframe
|
||||||
-----------
|
-----------
|
||||||
Disables/Enables the iframe-popup
|
|
||||||
The default value is _false_
|
|
||||||
|
|
||||||
fs-more-quests
|
Disables/Enables the iframe-popup The default value is _false_
|
||||||
|
|
||||||
|
|
||||||
|
fs-more-quests
|
||||||
----------------
|
----------------
|
||||||
Disables/Enables the 'More Quests'-tab in the welcome message
|
|
||||||
The default value is _true_
|
|
||||||
|
|
||||||
fs-share-screen
|
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 '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
|
Disables/Enables the geolocation button The default value is _true_
|
||||||
|
|
||||||
|
|
||||||
|
fs-all-questions
|
||||||
------------------
|
------------------
|
||||||
Always show all questions
|
|
||||||
The default value is _false_
|
|
||||||
|
|
||||||
debug
|
Always show all questions The default value is _false_
|
||||||
|
|
||||||
|
|
||||||
|
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_
|
|
||||||
|
|
||||||
backend
|
If true, shows some extra debugging help such as all the available tags on every object 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_
|
|
||||||
|
|
||||||
oauth_token
|
The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_
|
||||||
-------------
|
|
||||||
Used to complete the login
|
|
||||||
No default value set
|
|
||||||
|
|
||||||
background
|
|
||||||
|
custom-css
|
||||||
------------
|
------------
|
||||||
The id of the background layer to start with
|
|
||||||
The default value is _OSM_ (overridden by the theme)
|
|
||||||
|
|
||||||
layer-<layerid>
|
If specified, the custom css from the given link will be loaded additionaly The default value is __
|
||||||
--------------
|
|
||||||
Wether or not layer with layer-id is shown
|
|
||||||
The default value is _true_
|
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_
|
|
@ -21,7 +21,6 @@ import * as L from "leaflet";
|
||||||
import Img from "./UI/Base/Img";
|
import Img from "./UI/Base/Img";
|
||||||
import UserDetails from "./Logic/Osm/OsmConnection";
|
import UserDetails from "./Logic/Osm/OsmConnection";
|
||||||
import Attribution from "./UI/BigComponents/Attribution";
|
import Attribution from "./UI/BigComponents/Attribution";
|
||||||
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
|
|
||||||
import LayerResetter from "./Logic/Actors/LayerResetter";
|
import LayerResetter from "./Logic/Actors/LayerResetter";
|
||||||
import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs";
|
import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs";
|
||||||
import LayerControlPanel from "./UI/BigComponents/LayerControlPanel";
|
import LayerControlPanel from "./UI/BigComponents/LayerControlPanel";
|
||||||
|
@ -40,6 +39,7 @@ import ContributorCount from "./Logic/ContributorCount";
|
||||||
import FeatureSource from "./Logic/FeatureSource/FeatureSource";
|
import FeatureSource from "./Logic/FeatureSource/FeatureSource";
|
||||||
import AllKnownLayers from "./Customizations/AllKnownLayers";
|
import AllKnownLayers from "./Customizations/AllKnownLayers";
|
||||||
import LayerConfig from "./Customizations/JSON/LayerConfig";
|
import LayerConfig from "./Customizations/JSON/LayerConfig";
|
||||||
|
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
|
||||||
|
|
||||||
export class InitUiElements {
|
export class InitUiElements {
|
||||||
|
|
||||||
|
@ -348,9 +348,8 @@ export class InitUiElements {
|
||||||
private static InitBaseMap() {
|
private static InitBaseMap() {
|
||||||
|
|
||||||
State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state.locationControl).availableEditorLayers;
|
State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state.locationControl).availableEditorLayers;
|
||||||
State.state.backgroundLayer = QueryParameters.GetQueryParameter("background",
|
|
||||||
State.state.layoutToUse.data.defaultBackgroundId ?? AvailableBaseLayers.osmCarto.id,
|
State.state.backgroundLayer = State.state.backgroundLayerId
|
||||||
"The id of the background layer to start with")
|
|
||||||
.map((selectedId: string) => {
|
.map((selectedId: string) => {
|
||||||
const available = State.state.availableBackgroundLayers.data;
|
const available = State.state.availableBackgroundLayers.data;
|
||||||
for (const layer of available) {
|
for (const layer of available) {
|
||||||
|
@ -359,9 +358,8 @@ export class InitUiElements {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return AvailableBaseLayers.osmCarto;
|
return AvailableBaseLayers.osmCarto;
|
||||||
}, [], layer => layer.id);
|
}, [State.state.availableBackgroundLayers], layer => layer.id);
|
||||||
|
|
||||||
|
|
||||||
new LayerResetter(
|
new LayerResetter(
|
||||||
State.state.backgroundLayer, State.state.locationControl,
|
State.state.backgroundLayer, State.state.locationControl,
|
||||||
State.state.availableBackgroundLayers, State.state.layoutToUse.map((layout: LayoutConfig) => layout.defaultBackgroundId));
|
State.state.availableBackgroundLayers, State.state.layoutToUse.map((layout: LayoutConfig) => layout.defaultBackgroundId));
|
||||||
|
|
|
@ -76,7 +76,6 @@ export default class GeoLocationHandler extends UIElement {
|
||||||
}, [this._hasLocation])
|
}, [this._hasLocation])
|
||||||
currentPointer.addCallbackAndRun(pointerClass => {
|
currentPointer.addCallbackAndRun(pointerClass => {
|
||||||
self.SetClass(pointerClass);
|
self.SetClass(pointerClass);
|
||||||
self.Update()
|
|
||||||
})
|
})
|
||||||
this._element = new VariableUiElement(
|
this._element = new VariableUiElement(
|
||||||
this._hasLocation.map(hasLocation => {
|
this._hasLocation.map(hasLocation => {
|
||||||
|
|
|
@ -1,46 +1,48 @@
|
||||||
import {GeoOperations} from "./GeoOperations";
|
import {GeoOperations} from "./GeoOperations";
|
||||||
import {UIElement} from "../UI/UIElement";
|
|
||||||
import Combine from "../UI/Base/Combine";
|
import Combine from "../UI/Base/Combine";
|
||||||
import {Relation} from "./Osm/ExtractRelations";
|
import {Relation} from "./Osm/ExtractRelations";
|
||||||
import State from "../State";
|
import State from "../State";
|
||||||
import {Utils} from "../Utils";
|
import {Utils} from "../Utils";
|
||||||
|
import BaseUIElement from "../UI/BaseUIElement";
|
||||||
|
import List from "../UI/Base/List";
|
||||||
|
import Title from "../UI/Base/Title";
|
||||||
|
|
||||||
export class ExtraFunction {
|
export class ExtraFunction {
|
||||||
|
|
||||||
|
|
||||||
static readonly intro = `<h2>Calculating tags with Javascript</h2>
|
static readonly intro = new Combine([
|
||||||
|
new Title("Calculating tags with Javascript", 2),
|
||||||
|
"In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by default (e.g. `lat`, `lon`, `_country`), as detailed above.",
|
||||||
|
"It is also possible to calculate your own tags - but this requires some javascript knowledge.",
|
||||||
|
"",
|
||||||
|
"Before proceeding, some warnings:",
|
||||||
|
new List([
|
||||||
|
"DO NOT DO THIS AS BEGINNER",
|
||||||
|
"**Only do this if all other techniques fail** This should _not_ be done to create a rendering effect, only to calculate a specific value",
|
||||||
|
"**THIS MIGHT BE DISABLED WITHOUT ANY NOTICE ON UNOFFICIAL THEMES** As unofficial themes might be loaded from the internet, this is the equivalent of injecting arbitrary code into the client. It'll be disabled if abuse occurs."
|
||||||
|
]),
|
||||||
|
"To enable this feature, add a field `calculatedTags` in the layer object, e.g.:",
|
||||||
|
"````",
|
||||||
|
"\"calculatedTags\": [",
|
||||||
|
" \"_someKey=javascript-expression\",",
|
||||||
|
" \"name=feat.properties.name ?? feat.properties.ref ?? feat.properties.operator\",",
|
||||||
|
" \"_distanceCloserThen3Km=feat.distanceTo( some_lon, some_lat) < 3 ? 'yes' : 'no'\" ",
|
||||||
|
" ]",
|
||||||
|
"````",
|
||||||
|
"",
|
||||||
|
"The above code will be executed for every feature in the layer. The feature is accessible as `feat` and is an amended geojson object:",
|
||||||
|
|
||||||
<p>In some cases, it is useful to have some tags calculated based on other properties. Some useful tags are available by default (e.g. <b>lat</b>, <b>lon</b>, <b>_country</b>), as detailed above.</p>
|
new List([
|
||||||
|
"`area` contains the surface area (in square meters) of the object",
|
||||||
|
"`lat` and `lon` contain the latitude and longitude"
|
||||||
|
]),
|
||||||
|
"Some advanced functions are available on **feat** as well:"
|
||||||
|
]).SetClass("flex-col").AsMarkdown();
|
||||||
|
|
||||||
<p>It is also possible to calculate your own tags - but this requires some javascript knowledge. </p>
|
|
||||||
|
|
||||||
Before proceeding, some warnings:
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> DO NOT DO THIS AS BEGINNER</li>
|
|
||||||
<li> <b>Only do this if all other techniques fail</b>. This should <i>not</i> be done to create a rendering effect, only to calculate a specific value</li>
|
|
||||||
<li> <b>THIS MIGHT BE DISABLED WITHOUT ANY NOTICE ON UNOFFICIAL THEMES</b>. As unofficial themes might be loaded from the internet, this is the equivalent of injecting arbitrary code into the client. It'll be disabled if abuse occurs.</li>
|
|
||||||
</ul>
|
|
||||||
In the layer object, add a field <b>calculatedTags</b>, e.g.:
|
|
||||||
|
|
||||||
<div class="code">
|
|
||||||
"calculatedTags": [
|
|
||||||
"_someKey=javascript-expression",
|
|
||||||
"name=feat.properties.name ?? feat.properties.ref ?? feat.properties.operator",
|
|
||||||
"_distanceCloserThen3Km=feat.distanceTo( some_lon, some_lat) < 3 ? 'yes' : 'no'"
|
|
||||||
]
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The above code will be executed for every feature in the layer. The feature is accessible as <b>feat</b> and is an amended geojson object:
|
|
||||||
- <b>area</b> contains the surface area (in square meters) of the object
|
|
||||||
- <b>lat</b> and <b>lon</b> contain the latitude and longitude
|
|
||||||
|
|
||||||
Some advanced functions are available on <b>feat</b> as well:
|
|
||||||
|
|
||||||
`
|
|
||||||
private static readonly OverlapFunc = new ExtraFunction(
|
private static readonly OverlapFunc = new ExtraFunction(
|
||||||
"overlapWith",
|
"overlapWith",
|
||||||
"Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is <code>{ feat: GeoJSONFeature, overlap: number}[]</code> where <code>overlap</code> is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or <code>undefined</code> if the current feature is a point",
|
"Gives a list of features from the specified layer which this feature (partly) overlaps with. If the current feature is a point, all features that embed the point are given. The returned value is `{ feat: GeoJSONFeature, overlap: number}[]` where `overlap` is the overlapping surface are (in m²) for areas, the overlapping length (in meter) if the current feature is a line or `undefined` if the current feature is a point",
|
||||||
["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"],
|
["...layerIds - one or more layer ids of the layer from which every feature is checked for overlap)"],
|
||||||
(params, feat) => {
|
(params, feat) => {
|
||||||
return (...layerIds: string[]) => {
|
return (...layerIds: string[]) => {
|
||||||
|
@ -72,7 +74,7 @@ Some advanced functions are available on <b>feat</b> as well:
|
||||||
if (typeof arg0 === "string") {
|
if (typeof arg0 === "string") {
|
||||||
// This is an identifier
|
// This is an identifier
|
||||||
const feature = State.state.allElements.ContainingFeatures.get(arg0);
|
const feature = State.state.allElements.ContainingFeatures.get(arg0);
|
||||||
if(feature === undefined){
|
if (feature === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
arg0 = feature;
|
arg0 = feature;
|
||||||
|
@ -138,9 +140,9 @@ Some advanced functions are available on <b>feat</b> as well:
|
||||||
|
|
||||||
private static readonly Memberships = new ExtraFunction(
|
private static readonly Memberships = new ExtraFunction(
|
||||||
"memberships",
|
"memberships",
|
||||||
"Gives a list of <code>{role: string, relation: Relation}</code>-objects, containing all the relations that this feature is part of. " +
|
"Gives a list of `{role: string, relation: Relation}`-objects, containing all the relations that this feature is part of. " +
|
||||||
"\n\n" +
|
"\n\n" +
|
||||||
"For example: <code>_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')</code>",
|
"For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')`",
|
||||||
[],
|
[],
|
||||||
(params, _) => {
|
(params, _) => {
|
||||||
return () => params.relations ?? [];
|
return () => params.relations ?? [];
|
||||||
|
@ -167,25 +169,19 @@ Some advanced functions are available on <b>feat</b> as well:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HelpText(): UIElement {
|
public static HelpText(): BaseUIElement {
|
||||||
|
|
||||||
|
const elems = []
|
||||||
|
for (const func of ExtraFunction.allFuncs) {
|
||||||
|
elems.push(new Title(func._name, 3),
|
||||||
|
func._doc,
|
||||||
|
new List(func._args, true))
|
||||||
|
}
|
||||||
|
|
||||||
return new Combine([
|
return new Combine([
|
||||||
ExtraFunction.intro,
|
ExtraFunction.intro,
|
||||||
"<ul>",
|
new List(ExtraFunction.allFuncs.map(func => func._name)),
|
||||||
...ExtraFunction.allFuncs.map(func =>
|
...elems
|
||||||
new Combine([
|
|
||||||
"<li>", func._name, "</li>"
|
|
||||||
])
|
|
||||||
),
|
|
||||||
"</ul>",
|
|
||||||
...ExtraFunction.allFuncs.map(func =>
|
|
||||||
new Combine([
|
|
||||||
"<h3>" + func._name + "</h3>",
|
|
||||||
func._doc,
|
|
||||||
"<ul>",
|
|
||||||
...func._args.map(arg => "<li>" + arg + "</li>"),
|
|
||||||
"</ul>"
|
|
||||||
])
|
|
||||||
)
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,15 @@ import {Tag} from "./Tags/Tag";
|
||||||
import {Or} from "./Tags/Or";
|
import {Or} from "./Tags/Or";
|
||||||
import {Utils} from "../Utils";
|
import {Utils} from "../Utils";
|
||||||
import opening_hours from "opening_hours";
|
import opening_hours from "opening_hours";
|
||||||
import {UIElement} from "../UI/UIElement";
|
|
||||||
import Combine from "../UI/Base/Combine";
|
import Combine from "../UI/Base/Combine";
|
||||||
|
import BaseUIElement from "../UI/BaseUIElement";
|
||||||
|
import Title from "../UI/Base/Title";
|
||||||
|
import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
||||||
|
|
||||||
|
|
||||||
const cardinalDirections = {
|
const cardinalDirections = {
|
||||||
N: 0, NNE: 22.5, NE: 45, ENE: 67.5,
|
N: 0, NNE: 22.5, NE: 45, ENE: 67.5,
|
||||||
E: 90, ESE: 112.5, SE: 135, SSE: 157.5,
|
E: 90, ESE: 112.5, SE: 135, SSE: 157.5,
|
||||||
S: 180, SSW: 202.5, SW: 225, WSW: 247.5,
|
S: 180, SSW: 202.5, SW: 225, WSW: 247.5,
|
||||||
W: 270, WNW: 292.5, NW: 315, NNW: 337.5
|
W: 270, WNW: 292.5, NW: 315, NNW: 337.5
|
||||||
}
|
}
|
||||||
|
@ -31,20 +33,20 @@ export default class SimpleMetaTagger {
|
||||||
(feature) => {/*Note: also handled by 'UpdateTagsFromOsmAPI'*/
|
(feature) => {/*Note: also handled by 'UpdateTagsFromOsmAPI'*/
|
||||||
|
|
||||||
const tgs = feature.properties;
|
const tgs = feature.properties;
|
||||||
|
|
||||||
function move(src: string, target: string){
|
function move(src: string, target: string) {
|
||||||
if(tgs[src] === undefined){
|
if (tgs[src] === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tgs[target] = tgs[src]
|
tgs[target] = tgs[src]
|
||||||
delete tgs[src]
|
delete tgs[src]
|
||||||
}
|
}
|
||||||
|
|
||||||
move("user","_last_edit:contributor")
|
move("user", "_last_edit:contributor")
|
||||||
move("uid","_last_edit:contributor:uid")
|
move("uid", "_last_edit:contributor:uid")
|
||||||
move("changeset","_last_edit:changeset")
|
move("changeset", "_last_edit:changeset")
|
||||||
move("timestamp","_last_edit:timestamp")
|
move("timestamp", "_last_edit:timestamp")
|
||||||
move("version","_version_number")
|
move("version", "_version_number")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
private static latlon = new SimpleMetaTagger({
|
private static latlon = new SimpleMetaTagger({
|
||||||
|
@ -375,28 +377,27 @@ export default class SimpleMetaTagger {
|
||||||
SimpleMetaTagger.coder?.GetCountryCodeFor(lon, lat, callback)
|
SimpleMetaTagger.coder?.GetCountryCodeFor(lon, lat, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
static HelpText(): UIElement {
|
static HelpText(): BaseUIElement {
|
||||||
const subElements: UIElement[] = [
|
const subElements: (string | BaseUIElement)[] = [
|
||||||
new Combine([
|
new Combine([
|
||||||
"<h2>Metatags</h2>",
|
new Title("Metatags", 1),
|
||||||
"<p>Metatags are extra tags available, in order to display more data or to give better questions.</p>",
|
"Metatags are extra tags available, in order to display more data or to give better questions.",
|
||||||
"<p>The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.</p>",
|
"The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.",
|
||||||
"<p><b>Hint:</b> when using metatags, add the <a href='URL_Parameters.md'>query parameter</a> <code>debug=true</code> to the URL. This will include a box in the popup for features which shows all the properties of the object</p>"
|
"**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"
|
||||||
])
|
]).SetClass("flex-col")
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
subElements.push(new Title("Metatags calculated by MapComplete", 2))
|
||||||
|
subElements.push(new FixedUiElement("The following values are always calculated, by default, by MapComplete and are available automatically on all elements in every theme"))
|
||||||
for (const metatag of SimpleMetaTagger.metatags) {
|
for (const metatag of SimpleMetaTagger.metatags) {
|
||||||
subElements.push(
|
subElements.push(
|
||||||
new Combine([
|
new Title(metatag.keys.join(", "), 3),
|
||||||
"<h3>", metatag.keys.join(", "), "</h3>",
|
metatag.doc
|
||||||
metatag.doc]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Combine(subElements)
|
return new Combine(subElements).SetClass("flex-col")
|
||||||
}
|
}
|
||||||
|
|
||||||
addMetaTags(features: { feature: any, freshness: Date }[]) {
|
addMetaTags(features: { feature: any, freshness: Date }[]) {
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
*/
|
*/
|
||||||
import {UIEventSource} from "../UIEventSource";
|
import {UIEventSource} from "../UIEventSource";
|
||||||
import Hash from "./Hash";
|
import Hash from "./Hash";
|
||||||
|
import {Utils} from "../../Utils";
|
||||||
|
import Title from "../../UI/Base/Title";
|
||||||
|
import Combine from "../../UI/Base/Combine";
|
||||||
|
|
||||||
export class QueryParameters {
|
export class QueryParameters {
|
||||||
|
|
||||||
|
@ -12,6 +15,58 @@ export class QueryParameters {
|
||||||
private static defaults = {}
|
private static defaults = {}
|
||||||
|
|
||||||
private static documentation = {}
|
private static documentation = {}
|
||||||
|
private static QueryParamDocsIntro = "\n" +
|
||||||
|
"URL-parameters and URL-hash\n" +
|
||||||
|
"============================\n" +
|
||||||
|
"\n" +
|
||||||
|
"This document gives an overview of which URL-parameters can be used to influence MapComplete.\n" +
|
||||||
|
"\n" +
|
||||||
|
"What is a URL parameter?\n" +
|
||||||
|
"------------------------\n" +
|
||||||
|
"\n" +
|
||||||
|
"URL-parameters are extra parts of the URL used to set the state.\n" +
|
||||||
|
"\n" +
|
||||||
|
"For example, if the url is `https://mapcomplete.osm.be/cyclofix?lat=51.0&lon=4.3&z=5&test=true#node/1234`,\n" +
|
||||||
|
"the URL-parameters are stated in the part between the `?` and the `#`. There are multiple, all seperated by `&`, namely:\n" +
|
||||||
|
"\n" +
|
||||||
|
"- The url-parameter `lat` is `51.0` in this instance\n" +
|
||||||
|
"- The url-parameter `lon` is `4.3` in this instance\n" +
|
||||||
|
"- The url-parameter `z` is `5` in this instance\n" +
|
||||||
|
"- The url-parameter `test` is `true` in this instance\n" +
|
||||||
|
"\n" +
|
||||||
|
"Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case."
|
||||||
|
|
||||||
|
public static GetQueryParameter(key: string, deflt: string, documentation?: string): UIEventSource<string> {
|
||||||
|
if (!this.initialized) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
QueryParameters.documentation[key] = documentation;
|
||||||
|
if (deflt !== undefined) {
|
||||||
|
QueryParameters.defaults[key] = deflt;
|
||||||
|
}
|
||||||
|
if (QueryParameters.knownSources[key] !== undefined) {
|
||||||
|
return QueryParameters.knownSources[key];
|
||||||
|
}
|
||||||
|
QueryParameters.addOrder(key);
|
||||||
|
const source = new UIEventSource<string>(deflt, "&" + key);
|
||||||
|
QueryParameters.knownSources[key] = source;
|
||||||
|
source.addCallback(() => QueryParameters.Serialize())
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GenerateQueryParameterDocs(): string {
|
||||||
|
const docs = [QueryParameters.QueryParamDocsIntro];
|
||||||
|
for (const key in QueryParameters.documentation) {
|
||||||
|
const c = new Combine([
|
||||||
|
new Title(key, 2),
|
||||||
|
QueryParameters.documentation[key],
|
||||||
|
QueryParameters.defaults[key] === undefined ? "No default value set" : `The default value is _${QueryParameters.defaults[key]}_`
|
||||||
|
|
||||||
|
])
|
||||||
|
docs.push(c.AsMarkdown())
|
||||||
|
}
|
||||||
|
return docs.join("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
private static addOrder(key) {
|
private static addOrder(key) {
|
||||||
if (this.order.indexOf(key) < 0) {
|
if (this.order.indexOf(key) < 0) {
|
||||||
|
@ -25,7 +80,11 @@ export class QueryParameters {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
|
||||||
|
if (Utils.runningFromConsole) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (window?.location?.search) {
|
if (window?.location?.search) {
|
||||||
const params = window.location.search.substr(1).split("&");
|
const params = window.location.search.substr(1).split("&");
|
||||||
for (const param of params) {
|
for (const param of params) {
|
||||||
|
@ -38,7 +97,7 @@ export class QueryParameters {
|
||||||
QueryParameters.knownSources[key] = source;
|
QueryParameters.knownSources[key] = source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window["mapcomplete_query_parameter_overview"] = () => {
|
window["mapcomplete_query_parameter_overview"] = () => {
|
||||||
console.log(QueryParameters.GenerateQueryParameterDocs())
|
console.log(QueryParameters.GenerateQueryParameterDocs())
|
||||||
}
|
}
|
||||||
|
@ -50,7 +109,7 @@ export class QueryParameters {
|
||||||
if (QueryParameters.knownSources[key]?.data === undefined) {
|
if (QueryParameters.knownSources[key]?.data === undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QueryParameters.knownSources[key].data === "undefined") {
|
if (QueryParameters.knownSources[key].data === "undefined") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -62,41 +121,8 @@ export class QueryParameters {
|
||||||
parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(QueryParameters.knownSources[key].data))
|
parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(QueryParameters.knownSources[key].data))
|
||||||
}
|
}
|
||||||
// Don't pollute the history every time a parameter changes
|
// Don't pollute the history every time a parameter changes
|
||||||
|
|
||||||
history.replaceState(null, "", "?" + parts.join("&") + Hash.Current());
|
history.replaceState(null, "", "?" + parts.join("&") + Hash.Current());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GetQueryParameter(key: string, deflt: string, documentation?: string): UIEventSource<string> {
|
|
||||||
if(!this.initialized){
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
QueryParameters.documentation[key] = documentation;
|
|
||||||
if (deflt !== undefined) {
|
|
||||||
QueryParameters.defaults[key] = deflt;
|
|
||||||
}
|
|
||||||
if (QueryParameters.knownSources[key] !== undefined) {
|
|
||||||
return QueryParameters.knownSources[key];
|
|
||||||
}
|
|
||||||
QueryParameters.addOrder(key);
|
|
||||||
const source = new UIEventSource<string>(deflt, "&"+key);
|
|
||||||
QueryParameters.knownSources[key] = source;
|
|
||||||
source.addCallback(() => QueryParameters.Serialize())
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GenerateQueryParameterDocs(): string {
|
|
||||||
const docs = [];
|
|
||||||
for (const key in QueryParameters.documentation) {
|
|
||||||
docs.push([
|
|
||||||
" "+key+" ",
|
|
||||||
"-".repeat(key.length + 2),
|
|
||||||
QueryParameters.documentation[key],
|
|
||||||
QueryParameters.defaults[key] === undefined ? "No default value set" : `The default value is _${QueryParameters.defaults[key]}_`
|
|
||||||
|
|
||||||
].join("\n"))
|
|
||||||
}
|
|
||||||
return docs.join("\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
21
State.ts
21
State.ts
|
@ -102,6 +102,8 @@ export default class State {
|
||||||
*/
|
*/
|
||||||
public readonly locationControl = new UIEventSource<Loc>(undefined);
|
public readonly locationControl = new UIEventSource<Loc>(undefined);
|
||||||
public backgroundLayer;
|
public backgroundLayer;
|
||||||
|
public readonly backgroundLayerId: UIEventSource<string>;
|
||||||
|
|
||||||
/* Last location where a click was registered
|
/* Last location where a click was registered
|
||||||
*/
|
*/
|
||||||
public readonly LastClickLocation: UIEventSource<{ lat: number, lon: number }> = new UIEventSource<{ lat: number, lon: number }>(undefined)
|
public readonly LastClickLocation: UIEventSource<{ lat: number, lon: number }> = new UIEventSource<{ lat: number, lon: number }>(undefined)
|
||||||
|
@ -123,7 +125,7 @@ export default class State {
|
||||||
public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter("tab", "0", `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)`).map<number>(
|
public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter("tab", "0", `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)`).map<number>(
|
||||||
str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n
|
str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n
|
||||||
);
|
);
|
||||||
|
|
||||||
constructor(layoutToUse: LayoutConfig) {
|
constructor(layoutToUse: LayoutConfig) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
|
@ -210,8 +212,25 @@ export default class State {
|
||||||
"The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test'")
|
"The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test'")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// Some other feature switches
|
||||||
|
const customCssQP = QueryParameters.GetQueryParameter("custom-css", "", "If specified, the custom css from the given link will be loaded additionaly");
|
||||||
|
if (customCssQP.data !== undefined && customCssQP.data !== "") {
|
||||||
|
Utils.LoadCustomCss(customCssQP.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.backgroundLayerId = QueryParameters.GetQueryParameter("background",
|
||||||
|
layoutToUse.defaultBackgroundId ?? "osm",
|
||||||
|
"The id of the background layer to start with")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(Utils.runningFromConsole){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.osmConnection = new OsmConnection(
|
this.osmConnection = new OsmConnection(
|
||||||
this.featureSwitchIsTesting.data,
|
this.featureSwitchIsTesting.data,
|
||||||
QueryParameters.GetQueryParameter("oauth_token", undefined,
|
QueryParameters.GetQueryParameter("oauth_token", undefined,
|
||||||
|
|
|
@ -32,4 +32,8 @@ export default class Combine extends BaseUIElement {
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AsMarkdown(): string {
|
||||||
|
return this.uiElements.map(el => el.AsMarkdown()).join(this.HasClass("flex-col") ? "\n\n" : " ");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -17,5 +17,9 @@ export class FixedUiElement extends BaseUIElement {
|
||||||
e.innerHTML = this._html
|
e.innerHTML = this._html
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AsMarkdown(): string {
|
||||||
|
return this._html;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -30,5 +30,14 @@ export default class List extends BaseUIElement {
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AsMarkdown(): string {
|
||||||
|
if(this._ordered){
|
||||||
|
return "\n\n"+this.uiElements.map((el, i) => " "+i+". "+el.AsMarkdown().replace(/\n/g, ' \n') ).join("\n") + "\n"
|
||||||
|
}else{
|
||||||
|
return "\n\n"+this.uiElements.map(el => " - "+el.AsMarkdown().replace(/\n/g, ' \n') ).join("\n")+"\n"
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
37
UI/Base/Title.ts
Normal file
37
UI/Base/Title.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import {UIElement} from "../UIElement";
|
||||||
|
import BaseUIElement from "../BaseUIElement";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
|
||||||
|
export default class Title extends BaseUIElement{
|
||||||
|
private readonly _embedded: BaseUIElement;
|
||||||
|
private readonly _level: number;
|
||||||
|
constructor(embedded: string | BaseUIElement, level: number =3 ) {
|
||||||
|
super()
|
||||||
|
this._embedded = Translations.W(embedded);
|
||||||
|
this._level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InnerConstructElement(): HTMLElement {
|
||||||
|
const el = this._embedded.ConstructElement()
|
||||||
|
if(el === undefined){
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const h = document.createElement("h"+this._level)
|
||||||
|
h.appendChild(el)
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsMarkdown(): string {
|
||||||
|
const embedded = " " +this._embedded.AsMarkdown()+" ";
|
||||||
|
|
||||||
|
if(this._level == 1){
|
||||||
|
return "\n"+embedded+"\n"+"=".repeat(embedded.length)+"\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._level == 2){
|
||||||
|
return "\n"+embedded+"\n"+"-".repeat(embedded.length)+"\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "\n"+"#".repeat( this._level)+embedded +"\n\n";
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,17 +26,7 @@ export default abstract class BaseUIElement {
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IsHovered(): UIEventSource<boolean> {
|
|
||||||
if (this._onHover !== undefined) {
|
|
||||||
return this._onHover;
|
|
||||||
}
|
|
||||||
// Note: we just save it. 'Update' will register that an eventsource exist and install the necessary hooks
|
|
||||||
this._onHover = new UIEventSource<boolean>(false);
|
|
||||||
return this._onHover;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
AttachTo(divId: string) {
|
AttachTo(divId: string) {
|
||||||
let element = document.getElementById(divId);
|
let element = document.getElementById(divId);
|
||||||
if (element === null) {
|
if (element === null) {
|
||||||
|
@ -84,6 +74,10 @@ export default abstract class BaseUIElement {
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HasClass(clss: string): boolean{
|
||||||
|
return this.clss.has(clss)
|
||||||
|
}
|
||||||
|
|
||||||
public SetStyle(style: string): BaseUIElement {
|
public SetStyle(style: string): BaseUIElement {
|
||||||
this.style = style;
|
this.style = style;
|
||||||
|
@ -156,4 +150,8 @@ export default abstract class BaseUIElement {
|
||||||
|
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AsMarkdown(): string{
|
||||||
|
throw "AsMarkdown is not implemented by "+this.constructor.name
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -23,7 +23,6 @@ export default class PersonalLayersPanel extends UIElement {
|
||||||
const self = this;
|
const self = this;
|
||||||
State.state.installedThemes.addCallback(extraThemes => {
|
State.state.installedThemes.addCallback(extraThemes => {
|
||||||
self.UpdateView(extraThemes.map(layout => layout.layout));
|
self.UpdateView(extraThemes.map(layout => layout.layout));
|
||||||
self.Update();
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -216,7 +216,6 @@ export default class SpecialVisualizations {
|
||||||
"In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.",
|
"In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.",
|
||||||
"General usage is <b>{func_name()}</b> or <b>{func_name(arg, someotherarg)}</b>. Note that you <i>do not</i> need to use quotes around your arguments, the comma is enough to seperate them. This also implies you cannot use a comma in your args",
|
"General usage is <b>{func_name()}</b> or <b>{func_name(arg, someotherarg)}</b>. Note that you <i>do not</i> need to use quotes around your arguments, the comma is enough to seperate them. This also implies you cannot use a comma in your args",
|
||||||
...helpTexts
|
...helpTexts
|
||||||
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,27 +33,6 @@ export abstract class UIElement extends BaseUIElement{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Update(): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Render(): string {
|
|
||||||
return this.InnerRenderAsString()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public InnerRenderAsString(): string {
|
|
||||||
let rendered = this.InnerRender();
|
|
||||||
if (typeof rendered !== "string") {
|
|
||||||
let html = rendered.ConstructElement()
|
|
||||||
return html.innerHTML
|
|
||||||
}
|
|
||||||
return rendered
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be overridden for specific HTML functionality
|
* Should be overridden for specific HTML functionality
|
||||||
*/
|
*/
|
||||||
|
|
1
Utils.ts
1
Utils.ts
|
@ -159,7 +159,6 @@ export class Utils {
|
||||||
return txt;
|
return txt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date will be undefined on failure
|
|
||||||
public static LoadCustomCss(location: string) {
|
public static LoadCustomCss(location: string) {
|
||||||
const head = document.getElementsByTagName('head')[0];
|
const head = document.getElementsByTagName('head')[0];
|
||||||
const link = document.createElement('link');
|
const link = document.createElement('link');
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"contributors":[{"contributor":"Pieter Vander Vennet", "commits":714},{"contributor":"pietervdvn", "commits":650},{"contributor":"Tobias", "commits":35},{"contributor":"Christian Neumann", "commits":33},{"contributor":"Win Olario", "commits":31},{"contributor":"Pieter Fiers", "commits":31},{"contributor":"Sebastian Kürten", "commits":16},{"contributor":"ToastHawaii", "commits":15},{"contributor":"Weblate", "commits":14},{"contributor":"Marco", "commits":14},{"contributor":"Bavo Vanderghote", "commits":12},{"contributor":"Joost", "commits":11},{"contributor":"Midgard", "commits":8},{"contributor":"Jacque Fresco", "commits":8},{"contributor":"Artem", "commits":8},{"contributor":"yopaseopor", "commits":7},{"contributor":"Flo Edelmann", "commits":7},{"contributor":"Binnette", "commits":7},{"contributor":"pelderson", "commits":6},{"contributor":"Mateusz Konieczny", "commits":6},{"contributor":"lvgx", "commits":6},{"contributor":"dependabot[bot]", "commits":6},{"contributor":"Alexey Shabanov", "commits":6},{"contributor":"SiegbjornSitumeang", "commits":4},{"contributor":"Polgár Sándor", "commits":4},{"contributor":"Léo Villeveygoux", "commits":3},{"contributor":"Hosted Weblate", "commits":3},{"contributor":"David Haberthür", "commits":3},{"contributor":"Wiktor Przybylski", "commits":2},{"contributor":"Stanislas Gueniffey", "commits":2},{"contributor":"Robin van der Linde", "commits":2},{"contributor":"riiga", "commits":2},{"contributor":"pbarban", "commits":2},{"contributor":"Leo Alcaraz", "commits":2},{"contributor":"Jan Zabel", "commits":2},{"contributor":"graveelius", "commits":2},{"contributor":"Vinicius", "commits":1},{"contributor":"Tomas Fiers", "commits":1},{"contributor":"Thibault Molleman", "commits":1},{"contributor":"tbowdecl97", "commits":1},{"contributor":"Schouppe Joost", "commits":1},{"contributor":"Noémie", "commits":1},{"contributor":"mozita", "commits":1},{"contributor":"Carlos Ramos Carreño", "commits":1},{"contributor":"Beardhatcode", "commits":1}]}
|
{"contributors":[{"contributor":"Pieter Vander Vennet", "commits":738},{"contributor":"pietervdvn", "commits":718},{"contributor":"Weblate", "commits":35},{"contributor":"Tobias", "commits":35},{"contributor":"Christian Neumann", "commits":33},{"contributor":"Win Olario", "commits":31},{"contributor":"Pieter Fiers", "commits":31},{"contributor":"Sebastian Kürten", "commits":16},{"contributor":"Marco", "commits":16},{"contributor":"Joost", "commits":16},{"contributor":"ToastHawaii", "commits":15},{"contributor":"J. Lavoie", "commits":14},{"contributor":"Bavo Vanderghote", "commits":12},{"contributor":"Artem", "commits":12},{"contributor":"Supaplex", "commits":9},{"contributor":"Jacque Fresco", "commits":9},{"contributor":"Midgard", "commits":8},{"contributor":"Mateusz Konieczny", "commits":8},{"contributor":"yopaseopor", "commits":7},{"contributor":"Flo Edelmann", "commits":7},{"contributor":"Binnette", "commits":7},{"contributor":"Allan Nordhøy", "commits":7},{"contributor":"pelderson", "commits":6},{"contributor":"lvgx", "commits":6},{"contributor":"dependabot[bot]", "commits":6},{"contributor":"Alexey Shabanov", "commits":6},{"contributor":"SiegbjornSitumeang", "commits":4},{"contributor":"Polgár Sándor", "commits":4},{"contributor":"Hiroshi Miura", "commits":4},{"contributor":"vankos", "commits":3},{"contributor":"Léo Villeveygoux", "commits":3},{"contributor":"JCGF-OSM", "commits":3},{"contributor":"Jan Zabel", "commits":3},{"contributor":"Hosted Weblate", "commits":3},{"contributor":"David Haberthür", "commits":3},{"contributor":"快乐的老鼠宝宝", "commits":2},{"contributor":"Wiktor Przybylski", "commits":2},{"contributor":"Vinicius", "commits":2},{"contributor":"Stanislas Gueniffey", "commits":2},{"contributor":"Robin van der Linde", "commits":2},{"contributor":"riiga", "commits":2},{"contributor":"pbarban", "commits":2},{"contributor":"mic140", "commits":2},{"contributor":"Leo Alcaraz", "commits":2},{"contributor":"Jose Luis Infante", "commits":2},{"contributor":"Heiko", "commits":2},{"contributor":"graveelius", "commits":2},{"contributor":"Tomas Fiers", "commits":1},{"contributor":"Thibault Molleman", "commits":1},{"contributor":"tbowdecl97", "commits":1},{"contributor":"Sebastian", "commits":1},{"contributor":"Sean Young", "commits":1},{"contributor":"Schouppe Joost", "commits":1},{"contributor":"Noémie", "commits":1},{"contributor":"mozita", "commits":1},{"contributor":"Michał Targoński", "commits":1},{"contributor":"Iváns", "commits":1},{"contributor":"Eric Armijo", "commits":1},{"contributor":"Damian Pułka", "commits":1},{"contributor":"Carlos Ramos Carreño", "commits":1},{"contributor":"Beardhatcode", "commits":1}]}
|
|
@ -1,23 +0,0 @@
|
||||||
.image-upload-flow-button span {
|
|
||||||
width: max-content;
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: 4px;
|
|
||||||
padding-top: 4px;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
padding-left: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-upload-flow-button {
|
|
||||||
display: flex;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0.5em;
|
|
||||||
border-radius: 1em;
|
|
||||||
border: 3px solid var(--foreground-color);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-upload-flow svg {
|
|
||||||
fill: var(--foreground-color);
|
|
||||||
stroke: var(--foreground-color);
|
|
||||||
}
|
|
|
@ -12,7 +12,6 @@
|
||||||
<link rel="stylesheet" href="./css/mobile.css"/>
|
<link rel="stylesheet" href="./css/mobile.css"/>
|
||||||
<link rel="stylesheet" href="./css/openinghourstable.css"/>
|
<link rel="stylesheet" href="./css/openinghourstable.css"/>
|
||||||
<link rel="stylesheet" href="./css/tagrendering.css"/>
|
<link rel="stylesheet" href="./css/tagrendering.css"/>
|
||||||
<link rel="stylesheet" href="./css/imageUploadFlow.css"/>
|
|
||||||
<link rel="stylesheet" type="text/css" href="node_modules/slick-carousel/slick/slick.css"/>
|
<link rel="stylesheet" type="text/css" href="node_modules/slick-carousel/slick/slick.css"/>
|
||||||
<link rel="stylesheet" type="text/css" href="node_modules/slick-carousel/slick/slick-theme.css"/>
|
<link rel="stylesheet" type="text/css" href="node_modules/slick-carousel/slick/slick-theme.css"/>
|
||||||
<link rel="stylesheet" href="./css/slideshow.css"/>
|
<link rel="stylesheet" href="./css/slideshow.css"/>
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"short_name": "MapComplete",
|
|
||||||
"start_url": "index.html",
|
"start_url": "index.html",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#fff",
|
"background_color": "#fff",
|
||||||
"description": "A thematic map viewer and editor based on OpenStreetMap",
|
|
||||||
"orientation": "portrait-primary, landscape-primary",
|
"orientation": "portrait-primary, landscape-primary",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
|
|
5
index.ts
5
index.ts
|
@ -33,10 +33,6 @@ if (location.href.indexOf("buurtnatuur.be") >= 0) {
|
||||||
defaultLayout = "buurtnatuur"
|
defaultLayout = "buurtnatuur"
|
||||||
}
|
}
|
||||||
|
|
||||||
const customCssQP = QueryParameters.GetQueryParameter("custom-css", "", "If specified, the custom css from the given link will be loaded additionaly");
|
|
||||||
if (customCssQP.data !== undefined && customCssQP.data !== "") {
|
|
||||||
Utils.LoadCustomCss(customCssQP.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let testing: UIEventSource<string>;
|
let testing: UIEventSource<string>;
|
||||||
|
@ -87,7 +83,6 @@ if (layoutToUse?.id === "cyclofix") {
|
||||||
|
|
||||||
|
|
||||||
const layoutFromBase64 = decodeURIComponent(userLayoutParam.data);
|
const layoutFromBase64 = decodeURIComponent(userLayoutParam.data);
|
||||||
document.getElementById('centermessage').innerText = 'Initilai';
|
|
||||||
|
|
||||||
new Combine(["Initializing... <br/>",
|
new Combine(["Initializing... <br/>",
|
||||||
new FixedUiElement("<a>If this message persist, something went wrong - click here to try again</a>")
|
new FixedUiElement("<a>If this message persist, something went wrong - click here to try again</a>")
|
||||||
|
|
|
@ -95,7 +95,6 @@
|
||||||
"ts-node": "^9.0.0",
|
"ts-node": "^9.0.0",
|
||||||
"ts-node-dev": "^1.0.0-pre.63",
|
"ts-node-dev": "^1.0.0-pre.63",
|
||||||
"tslint-no-circular-imports": "^0.7.0",
|
"tslint-no-circular-imports": "^0.7.0",
|
||||||
"turndown": "^7.0.0",
|
|
||||||
"typescript": "^3.9.7",
|
"typescript": "^3.9.7",
|
||||||
"write-file": "^1.0.0"
|
"write-file": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,53 @@
|
||||||
import {Utils} from "../Utils";
|
import {Utils} from "../Utils";
|
||||||
|
|
||||||
Utils.runningFromConsole = true;
|
Utils.runningFromConsole = true;
|
||||||
import SpecialVisualizations from "../UI/SpecialVisualizations";
|
import SpecialVisualizations from "../UI/SpecialVisualizations";
|
||||||
import {writeFileSync} from "fs";
|
|
||||||
import {UIElement} from "../UI/UIElement";
|
|
||||||
import SimpleMetaTagger from "../Logic/SimpleMetaTagger";
|
import SimpleMetaTagger from "../Logic/SimpleMetaTagger";
|
||||||
import Combine from "../UI/Base/Combine";
|
import Combine from "../UI/Base/Combine";
|
||||||
import {ExtraFunction} from "../Logic/ExtraFunction";
|
import {ExtraFunction} from "../Logic/ExtraFunction";
|
||||||
import ValidatedTextField from "../UI/Input/ValidatedTextField";
|
import ValidatedTextField from "../UI/Input/ValidatedTextField";
|
||||||
|
import BaseUIElement from "../UI/BaseUIElement";
|
||||||
|
import Translations from "../UI/i18n/Translations";
|
||||||
|
import {writeFileSync} from "fs";
|
||||||
|
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
|
||||||
|
import State from "../State";
|
||||||
|
import {QueryParameters} from "../Logic/Web/QueryParameters";
|
||||||
|
|
||||||
|
|
||||||
|
function WriteFile(filename, html: string | BaseUIElement): void {
|
||||||
|
writeFileSync(filename, Translations.W(html).AsMarkdown());
|
||||||
const TurndownService = require('turndown')
|
|
||||||
|
|
||||||
function WriteFile(filename, html: UIElement) : void {
|
|
||||||
const md = new TurndownService().turndown(html.InnerRenderAsString());
|
|
||||||
writeFileSync(filename, md);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage)
|
WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage)
|
||||||
WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()]))
|
WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()]).SetClass("flex-col"))
|
||||||
writeFileSync("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText());
|
WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText());
|
||||||
|
|
||||||
|
|
||||||
|
new State(new LayoutConfig({
|
||||||
|
language: ["en"],
|
||||||
|
id: "<theme>",
|
||||||
|
maintainer: "pietervdvn",
|
||||||
|
version: "0",
|
||||||
|
title: "<theme>",
|
||||||
|
description: "A theme to generate docs with",
|
||||||
|
startLat: 0,
|
||||||
|
startLon: 0,
|
||||||
|
startZoom: 0,
|
||||||
|
icon: undefined,
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
name: "<layer>",
|
||||||
|
id: "<layer>",
|
||||||
|
source: {
|
||||||
|
osmTags: "id~*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}))
|
||||||
|
QueryParameters.GetQueryParameter("layer-<layer-id>", "true", "Wether or not the layer with id <layer-id> is shown")
|
||||||
|
|
||||||
|
WriteFile("./Docs/URL_Parameters.md", QueryParameters.GenerateQueryParameterDocs())
|
||||||
|
|
||||||
console.log("Generated docs")
|
console.log("Generated docs")
|
||||||
|
|
||||||
|
|
|
@ -88,8 +88,8 @@ async function createManifest(layout: LayoutConfig) {
|
||||||
console.log(icon)
|
console.log(icon)
|
||||||
throw "Icon is not an svg for " + layout.id
|
throw "Icon is not an svg for " + layout.id
|
||||||
}
|
}
|
||||||
const ogTitle = Translations.W(layout.title).InnerRenderAsString();
|
const ogTitle = Translations.W(layout.title).ConstructElement()?.innerText;
|
||||||
const ogDescr = Translations.W(layout.description ?? "").InnerRenderAsString();
|
const ogDescr = Translations.W(layout.description ?? "").ConstructElement()?.innerText;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -109,8 +109,8 @@ async function createLandingPage(layout: LayoutConfig, manifest) {
|
||||||
|
|
||||||
Locale.language.setData(layout.language[0]);
|
Locale.language.setData(layout.language[0]);
|
||||||
|
|
||||||
const ogTitle = Translations.W(layout.title)?.InnerRenderAsString();
|
const ogTitle = Translations.W(layout.title)?.ConstructElement()?.innerText;
|
||||||
const ogDescr = Translations.W(layout.shortDescription ?? "Easily add and edit geodata with OpenStreetMap")?.InnerRenderAsString();
|
const ogDescr = Translations.W(layout.shortDescription ?? "Easily add and edit geodata with OpenStreetMap")?.ConstructElement()?.innerText;
|
||||||
const ogImage = layout.socialImage;
|
const ogImage = layout.socialImage;
|
||||||
|
|
||||||
let customCss = "";
|
let customCss = "";
|
||||||
|
|
Loading…
Reference in a new issue