forked from MapComplete/MapComplete
Merge branch 'develop'
This commit is contained in:
commit
a6102aa3e0
6 changed files with 159 additions and 16 deletions
129
Docs/Integrating_Maproulette.md
Normal file
129
Docs/Integrating_Maproulette.md
Normal file
|
@ -0,0 +1,129 @@
|
|||
# Integrating MapRoulette
|
||||
|
||||
[MapRoulette](https://www.maproulette.org/) is a website which has challenges. A challenge is a collection of _microtasks_, i.e. mapping
|
||||
tasks which can be solved in a few minutes.
|
||||
|
||||
A perfect example of this is to setup such a challenge to e.g. import new points. [Important: always follow the import guidelines if you want to import data.](https://wiki.openstreetmap.org/wiki/Import/Guidelines)
|
||||
(Another approach to set up a guided import is to create a map note for every point with the [import helper](https://mapcomplete.osm.be/import_helper). This however litters the map and will upset mappers if used with to much points.)
|
||||
|
||||
## The API
|
||||
|
||||
**Most of the heavy lifting is done in layer `maproulette`. Extend this layer with your needs**
|
||||
The API is shortly discussed here for future reference only.
|
||||
|
||||
There is an API-endpoint at `https://maproulette.org/api/v2/tasks/box/{x_min}/{y_min}/{x_max}/{y_max}` which can be used
|
||||
to query _all_ tasks in a bbox and returns this as geojson. Hint:
|
||||
use [the maproulette theme in debug mode](https://mapcomplete.osm.be/maproulette?debug=true) to inspect all properties.
|
||||
|
||||
To view the overview a single challenge, visit `https://maproulette.org/browse/challenges/<challenge-id>` with your
|
||||
browser.
|
||||
The API endpoint for a single challenge is `https://maproulette.org/api/v2/challenge/view/<challenge-id>` which returns a
|
||||
geojson.
|
||||
|
||||
## Displaying MapRoulette data in MapComplete
|
||||
|
||||
As you'll probably want to link MapComplete to your challenge, reuse [maproulette_challenge](Docs/Layers/maproulette_challenge.md).
|
||||
It has a basic framework already to load the tags.
|
||||
|
||||
Of course, interacting with the tasks is the next step.
|
||||
|
||||
### Detecting nearby features
|
||||
|
||||
You can use [`calculatedTags`](./Docs/CalculatedTags.md) to add a small piece of code to e.g. detect nearby entities.
|
||||
|
||||
The following example is to match hotels:
|
||||
|
||||
```
|
||||
"calculatedTags": [
|
||||
"_closest_osm_hotel=feat.closest('hotel')?.properties?.id",
|
||||
"_closest_osm_hotel_distance=feat.distanceTo(feat.properties._closest_osm_hotel)",
|
||||
"_has_closeby_feature=Number(feat.properties._closest_osm_hotel_distance) < 50 ? 'yes' : 'no'"
|
||||
],
|
||||
```
|
||||
|
||||
This can be used to decide if tags should be applied on an existing object or a new point should be created.
|
||||
|
||||
|
||||
### Creating a new point based on a maproulette challenge (Guided import)
|
||||
|
||||
**Requirement**: the MapRoulette task should have `tags` added.
|
||||
|
||||
One can add `import`-button in the featureInfoBox ([documentation here](./Docs/SpecialRenderings.md#importbutton)).
|
||||
Note that the import button has support for MapRoulette and is able to close the task if the property containing the maproulette-id is given:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "import-button",
|
||||
"render": {
|
||||
"special": {
|
||||
"type": "import_button",
|
||||
"targetLayer": "<the layer in which the point should appear afterwards>",
|
||||
"tags": "tags", -- should stay 'tags'
|
||||
"maproulette_id": "mr_taskId", -- important to get the task closed
|
||||
"text": {
|
||||
"en": "Import this point" -- or a nice message
|
||||
},
|
||||
"icon": "./assets/svg/addSmall.svg", -- optional, feel free to change
|
||||
"location_picker": "photo", -- optional, background layer to pinpoint the hotel
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Applying tags to already existing features
|
||||
|
||||
For this, [the `tag_apply`-button can be used](./Docs/SpecialRenderings.md#tagapply).
|
||||
|
||||
The following example uses the calculated tags `_has_closeby_feature` and `_closest_osm_hotel`. These were added by a small extra script using `calculatedTagss`.
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
"id": "tag-apply-button",
|
||||
"condition": "_has_closeby_feature=yes", -- don't show if no feature to add to
|
||||
"render": {
|
||||
"special": {
|
||||
"type": "tag_apply",
|
||||
"tags_to_apply": "$tags", -- note the '$', property containing the tags
|
||||
"id_of_object_to_apply_this_one": "_closest_osm_hotel" -- id of the feature to add those tags to
|
||||
"message": {
|
||||
"en": "Add all the suggested tags"
|
||||
},
|
||||
"image": "./assets/svg/addSmall.svg",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Creating a maproulette challenge
|
||||
|
||||
A challenge can be created on https://maproulette.org/admin/projects
|
||||
|
||||
This can be done with a geojson-file (or by other means).
|
||||
|
||||
To create an import dataset, make a geojson file where every feature has a `tags`-field with ';'-seperated tags to add.
|
||||
Furthermore, setting the property `blurb` can be useful.
|
||||
|
||||
|
||||
(The following example is not tested and might be wrong.)
|
||||
|
||||
```
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {"type": "Point", "coordinates": [1.234, 5.678]},
|
||||
"properties": {
|
||||
"tags": "foo=bar;name=xyz",
|
||||
"blurb": "Please review this item and add it..."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
```
|
|
@ -14,6 +14,9 @@ export class FixedUiElement extends BaseUIElement {
|
|||
|
||||
AsMarkdown(): string {
|
||||
if (this.HasClass("code")) {
|
||||
if (this.content.indexOf("\n") > 0 || this.HasClass("block")) {
|
||||
return "\n```\n" + this.content + "\n```\n"
|
||||
}
|
||||
return "`" + this.content + "`"
|
||||
}
|
||||
if (this.HasClass("font-bold")) {
|
||||
|
|
|
@ -644,7 +644,7 @@ export class ImportPointButton extends AbstractImportButton {
|
|||
},
|
||||
{
|
||||
name: "maproulette_id",
|
||||
doc: "If given, the maproulette challenge will be marked as fixed",
|
||||
doc: "The property name of the maproulette_id - this is probably `mr_taskId`. If given, the maproulette challenge will be marked as fixed. Only use this if part of a maproulette-layer.",
|
||||
},
|
||||
],
|
||||
{ showRemovedTags: false }
|
||||
|
|
|
@ -30,7 +30,6 @@ import WikipediaBox from "./Wikipedia/WikipediaBox"
|
|||
import Wikidata, { WikidataResponse } from "../Logic/Web/Wikidata"
|
||||
import { Translation } from "./i18n/Translation"
|
||||
import Translations from "./i18n/Translations"
|
||||
import MangroveReviews from "../Logic/Web/MangroveReviews"
|
||||
import ReviewForm from "./Reviews/ReviewForm"
|
||||
import ReviewElement from "./Reviews/ReviewElement"
|
||||
import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"
|
||||
|
@ -480,6 +479,10 @@ export default class SpecialVisualizations {
|
|||
args: [],
|
||||
constr(state, tagSource, argument, guistate) {
|
||||
let parentId = tagSource.data.mr_challengeId
|
||||
if (parentId === undefined) {
|
||||
console.warn("Element ", tagSource.data.id, " has no mr_challengeId")
|
||||
return undefined
|
||||
}
|
||||
let challenge = Stores.FromPromise(
|
||||
Utils.downloadJsonCached(
|
||||
`https://maproulette.org/api/v2/challenge/${parentId}`,
|
||||
|
@ -512,7 +515,7 @@ export default class SpecialVisualizations {
|
|||
})
|
||||
)
|
||||
},
|
||||
docs: "Show details of a MapRoulette task",
|
||||
docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign.",
|
||||
},
|
||||
{
|
||||
funcName: "statistics",
|
||||
|
@ -725,13 +728,6 @@ export default class SpecialVisualizations {
|
|||
render: {
|
||||
special: {
|
||||
type: "some_special_visualisation",
|
||||
before: {
|
||||
en: "Some text to prefix before the special element (e.g. a title)",
|
||||
nl: "Een tekst om voor het element te zetten (bv. een titel)",
|
||||
},
|
||||
after: {
|
||||
en: "Some text to put after the element, e.g. a footer",
|
||||
},
|
||||
argname: "some_arg",
|
||||
message: {
|
||||
en: "some other really long message",
|
||||
|
@ -739,12 +735,20 @@ export default class SpecialVisualizations {
|
|||
},
|
||||
other_arg_name: "more args",
|
||||
},
|
||||
before: {
|
||||
en: "Some text to prefix before the special element (e.g. a title)",
|
||||
nl: "Een tekst om voor het element te zetten (bv. een titel)",
|
||||
},
|
||||
after: {
|
||||
en: "Some text to put after the element, e.g. a footer",
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
" "
|
||||
)
|
||||
).SetClass("code"),
|
||||
'In other words: use `{ "before": ..., "after": ..., "special": {"type": ..., "argname": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)',
|
||||
]).SetClass("flex flex-col"),
|
||||
...helpTexts,
|
||||
]).SetClass("flex flex-col")
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
"geoJsonZoomLevel": 16,
|
||||
"osmTags": "title~*"
|
||||
},
|
||||
"description": {
|
||||
"en": "Layer showing all tasks in MapRoulette",
|
||||
"de": "Ebene, die alle MapRoulette-Aufgaben zeigt",
|
||||
"nl": "Laag met alle taken uit MapRoulette"
|
||||
},
|
||||
"mapRendering": [
|
||||
{
|
||||
"location": [
|
||||
|
@ -128,11 +133,6 @@
|
|||
"render": "{blurb}"
|
||||
}
|
||||
],
|
||||
"description": {
|
||||
"en": "Layer showing all tasks in MapRoulette",
|
||||
"de": "Ebene, die alle MapRoulette-Aufgaben zeigt",
|
||||
"nl": "Laag met alle taken uit MapRoulette"
|
||||
},
|
||||
"minzoom": 15,
|
||||
"name": {
|
||||
"en": "MapRoulette Tasks",
|
||||
|
|
|
@ -64,7 +64,14 @@ function WriteFile(
|
|||
|
||||
md.replace(/\n\n\n+/g, "\n\n")
|
||||
|
||||
writeFileSync(filename, md)
|
||||
if (!md.endsWith("\n")) {
|
||||
md += "\n"
|
||||
}
|
||||
|
||||
const warnAutomated =
|
||||
"[//]: # (WARNING: this file is automatically generated. Please find the sources at the bottom and edit those sources)"
|
||||
|
||||
writeFileSync(filename, warnAutomated + md)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue