Add more guidelines on themes

This commit is contained in:
pietervdvn 2022-04-19 00:57:18 +02:00
parent b4ade5de1d
commit b1e304e05c
3 changed files with 210 additions and 8 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 MiB

BIN
Docs/FilteredByDepth.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

View file

@ -4,6 +4,11 @@ Making your own theme
In MapComplete, it is relatively simple to make your own theme. This guide will give some information on how you can do
this.
Table of contents:
1. [Requirements](#requirements) which lists what you should know before starting to create a theme
2. [What is a good theme?](#what-is-a-good-theme)
Requirements
------------
@ -15,10 +20,213 @@ Before you start, you should have the following qualifications:
- You are in contact with your local OpenStreetMap community and do know some other members to discuss tagging and to
help testing
If you do not have those qualifications, reach out to the MapComplete community channel
Please, do reach out to the MapComplete community channel
on [Telegram](https://t.me/MapComplete)
or [Matrix](https://app.element.io/#/room/#MapComplete:matrix.org).
What is a good theme?
---------------------
A **theme** (or _layout_) is a single map showing one or more layers.
The layers should work together in such a way that they serve a certain **audience**.
You should be able to state in a few sentences whom would be the user of such a map, e.g.
- a cyclist searching for bike repair
- a thirsty person who needs water
- someone who wants to know what their street is named after
- ...
Some layers will be useful for many themes (e.g. _drinking water_, _toilets_, _shops_, ...). Due to this, MapComplete supports to reuse already existing official layers into a theme.
To include an already existing layer, simply type the layer id, e.g.:
```json
{
"id": "my-theme",
"title": "My theme for xyz",
"...": "...",
"layers": [
{
"id": "my super-awesome new layer"
},
"bench",
"shops",
"drinking_water",
"toilet"
]
}
```
Note that it is good practice to use an existing layer and to tweak it:
```json
{
"id": "my super awesome theme",
"...": "...",
"layers": [
{
"builtin": [
"toilet",
"bench"
],
"override": {
"#": "Override is a section which copies all the keys here and 'pastes' them into the existing layers. For example, the 'minzoom' defined here will redifine the minzoom of 'toilet' and 'bench'",
"minzoom": 17,
"#0": "Appending to lists is supported to, e.g. to add an extra question",
"tagRenderings+": [
{
"id": "new-question",
"question": "What is <some property>?",
"render": "{property}",
"...": "..."
}
],
"#1": "Note that paths will be followed: the below block will add/change the icon of the layer, without changing the other properties of the first tag rendering. (Assumption: the first mapRendering is the icon rendering)",
"mapRendering": [
{
"icon": {
"render": "new-icon.svg"
}
}
]
}
}
]
}
```
### What is a good layer?
A good layer is layer which shows **all** objects of a certain type, e.g. **all** shops, **all** restaurants, ...
It asks some relevant questions, with the most important and easiests questions first.
#### Don't: use a layer to filter
**Do not define a layer which filters on an attribute**, such as <del>all restaurants with a vegetarian diet</del>, <del>all shops which accept bitcoin</del>.
This makes _addition_ of new points difficult as information might not yet be known. Conser the following situation:
1. A theme defines a layer `vegetarian restaurants`, which matches `amenity=restaurant` & `diet:vegetarian=yes`.
2. An object exists in OSM with `amenity=restaurant`;`name=Fancy Food`;`diet:vegan=yes`;`phone=...`;...
3. A contributor visits the themes and will notice that _Fancy Food_ is missing
4. The contributor will add _Fancy Food_
5. There are now **two** Fancy Food objects in OSM.
Instead, use the filter functionality instead. This can be used from the layer to hide some objects based on their properties.
When the contributor wants to add a new point, they'll be notified that some features might be hidden and only be allowed to add a new point when the points are shown.
![](./FilterFunctionality.gif)
```json
{
"id": "my awesome layer",
"tagRenderings": "... some relevant attributes and questions ...",
"mapRenderings": "... display on the map ... ",
"filter": [
{
"id": "vegetarian",
"options": [
{
"question": {
"en": "Has a vegetarian menu"
},
"osmTags": {
"or": [
"diet:vegetarian=yes",
"diet:vegetarian=only",
"diet:vegan=yes",
"diet:vegan=only"
]
}
}
]
}
]
}
```
If you want to show only features of a certain type, there is a workaround.
For example, the [fritures map](https://mapcomplete.osm.be/fritures.html?z=1&welcome-control-toggle=true) will show french fries shop, aka every `amenity~fast_food|restaurant` with `cuisine=friture`.
However, quite a few fritures are already mapped as fastfood but have their `cuisine`-tag missing (or misspelled).
There is a workaround for this: show **all** food related items at zoomlevel 19 (or higher), and only show the fritures when zoomed out.
In order to achieve this:
1. The layer 'food' is defined in a separate file and reused
2. The layer food is imported in the theme 'fritures'. With 'override', some properties are changed, namely:
- The `osmTags` are overwritten: `cuisine=friture` is now required
- The presets are overwritten and _disabled_
- The _id_ and _name_ of the layer are changed
3. The layer `food` is imported _a second time_, but now the minzoom is set to `19`. This will show _all_ restaurants.
In case of a friture which is already added as fastfood, they'll see the fastfood popup instead of adding a new item:
![](./FilteredByDepth.gif)
```json
{
"layers": [
{
"builtin": "food",
"override": {
"id": "friture",
"name": {
"en": "Fries shop"
},
"=presets": [],
"source": {
"=osmTags": {
"and": [
"cuisine=friture",
{
"or": [
"amenity=fast_food",
"amenity=restaurant"
]
}
]
}
}
}
},
{
"builtin": "food",
"override": {
"minzoom": 19,
"filter": null,
"name": null
}
}
]
}
```
### What is a good question and tagrendering?
A tagrendering maps an attribute onto a piece of human readable text.
These should be **full sentences**, e.g. `"render": "The maximum speed of this road is {maxspeed} km/h"`
In some cases, there might be some predifined special values as mappings, such as `"mappings": [{"if": "maxspeed=30", "then": "The maxspeed is 30km/h"}]`
The question then follows logically: `{"question": "What is the maximum allowed speed for this road, in km/h?"}`
At last, you'll also want to say that the user can type an answer too and that it has to be a number: `"freeform":{"key": "maxspeed","type":"pnat"}`.
The entire tagRendering will thus be:
```json
{
"question": "What is the maximum allowed speed for this road, in km/h?",
"render": "The maximum speed of this road is {maxspeed} km/h",
"freeform":{"key": "maxspeed","type":"pnat"},
"mappings": [{"if": "maxspeed=30", "then": "The maxspeed is 30km/h"}]
}
```
The template
------------
@ -229,16 +437,10 @@ disregarding other properties.
One should not make one layer for benches with a backrest and one layer for benches without. This is confusing for users
and poses problems: what if the backrest status is unknown? What if it is some weird value? Also, it isn't possible to '
move' an attribute to another layer.
move' a feature to another layer.
Instead, make one layer for one kind of object and change the icon based on attributes.
### Using layers as filters
Using layers as filters - this doesn't work!
Use the `filter`-functionality instead.
### Not reading the theme JSON specs
There are a few advanced features to do fancy stuff available, which are documented only in the spec above - for