forked from MapComplete/MapComplete
Allow closing Maproulette tasks
This commit is contained in:
parent
a1bffc7b7f
commit
65997291bb
12 changed files with 309 additions and 165 deletions
|
@ -347,11 +347,12 @@ snap_onto_layers | _undefined_ | If a way of the given layer(s) is closeby, will
|
|||
max_snap_distance | 5 | The maximum distance that the imported point will be moved to snap onto a way in an already existing layer (in meters). This is previewed to the contributor, similar to the 'add new point'-action of MapComplete
|
||||
note_id | _undefined_ | If given, this key will be read. The corresponding note on OSM will be closed, stating 'imported'
|
||||
location_picker | photo | Chooses the background for the precise location picker, options are 'map', 'photo' or 'osmbasedmap' or 'none' if the precise input picker should be disabled
|
||||
maproulette_id | _undefined_ | If given, the maproulette challenge will be marked as fixed
|
||||
|
||||
|
||||
#### Example usage of import_button
|
||||
|
||||
`{import_button(,,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,,5,,photo)}`
|
||||
`{import_button(,,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,,5,,photo,)}`
|
||||
|
||||
|
||||
|
||||
|
|
39
Logic/Maproulette.ts
Normal file
39
Logic/Maproulette.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import Constants from "../Models/Constants";
|
||||
|
||||
export default class Maproulette {
|
||||
/**
|
||||
* The API endpoint to use
|
||||
*/
|
||||
endpoint: string;
|
||||
|
||||
/**
|
||||
* The API key to use for all requests
|
||||
*/
|
||||
private apiKey: string;
|
||||
|
||||
/**
|
||||
* Creates a new Maproulette instance
|
||||
* @param endpoint The API endpoint to use
|
||||
*/
|
||||
constructor(endpoint: string = "https://maproulette.org/api/v2") {
|
||||
this.endpoint = endpoint;
|
||||
this.apiKey = Constants.MaprouletteApiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a task
|
||||
* @param taskId The task to close
|
||||
*/
|
||||
async closeTask(taskId: number): Promise<void> {
|
||||
const response = await fetch(`${this.endpoint}/task/${taskId}/1`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"apiKey": this.apiKey,
|
||||
},
|
||||
});
|
||||
if (response.status !== 304) {
|
||||
console.log(`Failed to close task: ${response.status}`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import ChangeToElementsActor from "../Actors/ChangeToElementsActor";
|
|||
import PendingChangesUploader from "../Actors/PendingChangesUploader";
|
||||
import * as translators from "../../assets/translators.json"
|
||||
import {post} from "jquery";
|
||||
import Maproulette from "../Maproulette";
|
||||
|
||||
/**
|
||||
* The part of the state which keeps track of user-related stuff, e.g. the OSM-connection,
|
||||
|
@ -34,6 +35,11 @@ export default class UserRelatedState extends ElementsState {
|
|||
*/
|
||||
public mangroveIdentity: MangroveIdentity;
|
||||
|
||||
/**
|
||||
* Maproulette connection
|
||||
*/
|
||||
public maprouletteConnection: Maproulette;
|
||||
|
||||
public readonly isTranslator : Store<boolean>;
|
||||
|
||||
public readonly installedUserThemes: Store<string[]>
|
||||
|
@ -80,6 +86,8 @@ export default class UserRelatedState extends ElementsState {
|
|||
this.osmConnection.GetLongPreference("identity", "mangrove")
|
||||
);
|
||||
|
||||
this.maprouletteConnection = new Maproulette();
|
||||
|
||||
if (layoutToUse?.hideFromOverview) {
|
||||
this.osmConnection.isLoggedIn.addCallbackAndRunD(loggedIn => {
|
||||
if (loggedIn) {
|
||||
|
|
|
@ -6,6 +6,8 @@ export default class Constants {
|
|||
|
||||
public static ImgurApiKey = '7070e7167f0a25a'
|
||||
public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85"
|
||||
// Currently there is no user-friendly way to get the user's API key. See https://github.com/maproulette/maproulette2/issues/476 for more information.
|
||||
public static readonly MaprouletteApiKey = "";
|
||||
|
||||
public static defaultOverpassUrls = [
|
||||
// The official instance, 10000 queries per day per project allowed
|
||||
|
|
|
@ -550,15 +550,21 @@ export class ImportPointButton extends AbstractImportButton {
|
|||
name: "note_id",
|
||||
doc: "If given, this key will be read. The corresponding note on OSM will be closed, stating 'imported'"
|
||||
},
|
||||
{name:"location_picker",
|
||||
{
|
||||
name:"location_picker",
|
||||
defaultValue: "photo",
|
||||
doc: "Chooses the background for the precise location picker, options are 'map', 'photo' or 'osmbasedmap' or 'none' if the precise input picker should be disabled"}],
|
||||
doc: "Chooses the background for the precise location picker, options are 'map', 'photo' or 'osmbasedmap' or 'none' if the precise input picker should be disabled"
|
||||
},
|
||||
{
|
||||
name: "maproulette_id",
|
||||
doc: "If given, the maproulette challenge will be marked as fixed"
|
||||
}],
|
||||
{ showRemovedTags: false}
|
||||
)
|
||||
}
|
||||
|
||||
private static createConfirmPanelForPoint(
|
||||
args: { max_snap_distance: string, snap_onto_layers: string, icon: string, text: string, newTags: UIEventSource<any>, targetLayer: string, note_id: string },
|
||||
args: { max_snap_distance: string, snap_onto_layers: string, icon: string, text: string, newTags: UIEventSource<any>, targetLayer: string, note_id: string, maproulette_id: string },
|
||||
state: FeaturePipelineState,
|
||||
guiState: DefaultGuiState,
|
||||
originalFeatureTags: UIEventSource<any>,
|
||||
|
@ -600,6 +606,14 @@ export class ImportPointButton extends AbstractImportButton {
|
|||
originalFeatureTags.data["closed_at"] = new Date().toISOString()
|
||||
originalFeatureTags.ping()
|
||||
}
|
||||
|
||||
let maproulette_id = originalFeatureTags.data[args.maproulette_id];
|
||||
console.log("Checking if we need to mark a maproulette challenge as fixed (" + maproulette_id + ")")
|
||||
if (maproulette_id !== undefined) {
|
||||
// Fetch MapRoulette API key, then use it to mark the challenge as fixed
|
||||
console.log("Marking maproulette challenge as fixed")
|
||||
state.maprouletteConnection.closeTask(Number(maproulette_id));
|
||||
}
|
||||
}
|
||||
|
||||
let preciseInputOption = args["location_picker"]
|
||||
|
|
|
@ -1,147 +1,147 @@
|
|||
{
|
||||
"id": "doctors",
|
||||
"name": {
|
||||
"en": "doctors"
|
||||
"id": "doctors",
|
||||
"name": {
|
||||
"en": "doctors"
|
||||
},
|
||||
"description": {
|
||||
"en": "This layer shows doctor offices, dentists and other healthcare facilities"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"or": [
|
||||
"amenity=doctors",
|
||||
"amenity=dentist",
|
||||
"healthcare=physiotherapist"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Doctors Office {name}"
|
||||
},
|
||||
"description": {
|
||||
"en": "This layer shows doctor offices, dentists and other healthcare facilities"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"or": [
|
||||
"amenity=doctors",
|
||||
"amenity=dentist",
|
||||
"healthcare=physiotherapist"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Doctors Office {name}"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "amenity=doctors",
|
||||
"then": "Doctors Office {name}"
|
||||
},
|
||||
{
|
||||
"if": "amenity=dentist",
|
||||
"then": "Dentists office {name}"
|
||||
},
|
||||
{
|
||||
"if": "healthcare=physiotherapist",
|
||||
"then": "Physiotherapists office {name}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"minzoom": 13,
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
"opening_hours",
|
||||
"phone",
|
||||
"email",
|
||||
"website",
|
||||
{
|
||||
"question": {
|
||||
"en": "What is the name of this doctors place?"
|
||||
},
|
||||
"render": {
|
||||
"en": "This doctors place is called {name}"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "name"
|
||||
},
|
||||
"id": "name"
|
||||
},
|
||||
{
|
||||
"condition": "amenity=doctors",
|
||||
"id": "specialty",
|
||||
"render": {
|
||||
"en": "This doctor is specialized in {healthcare:speciality}"
|
||||
},
|
||||
"question": {
|
||||
"en": "What is this doctor specialized in?"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "healthcare:speciality"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "healthcare:speciality=general",
|
||||
"then": {
|
||||
"en": "This is a general practitioner"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "healthcare:speciality=gynaecology",
|
||||
"then": {
|
||||
"en": "This is a gynaecologist"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "healthcare:speciality=psychiatry",
|
||||
"then": {
|
||||
"en": "This is a psychiatrist"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "healthcare:speciality=paediatrics",
|
||||
"then": {
|
||||
"en": "This is a paediatrician"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "a doctors office"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=doctors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": {
|
||||
"en": "a dentists office"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=dentist"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": {
|
||||
"en": "a physiotherapists office"
|
||||
},
|
||||
"tags": [
|
||||
"healthcare=physiotherapist"
|
||||
]
|
||||
}
|
||||
],
|
||||
"filter": [
|
||||
{
|
||||
"id": "opened-now",
|
||||
"options": [
|
||||
{
|
||||
"question": {
|
||||
"en": "Opened now"
|
||||
},
|
||||
"osmTags": "_isOpen=yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"mapRendering": [
|
||||
{
|
||||
"icon": {
|
||||
"render": "circle:white;./assets/layers/doctors/doctors.svg"
|
||||
},
|
||||
"iconSize": "40,40,center",
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
]
|
||||
}
|
||||
"mappings": [
|
||||
{
|
||||
"if": "amenity=doctors",
|
||||
"then": "Doctors Office {name}"
|
||||
},
|
||||
{
|
||||
"if": "amenity=dentist",
|
||||
"then": "Dentists office {name}"
|
||||
},
|
||||
{
|
||||
"if": "healthcare=physiotherapist",
|
||||
"then": "Physiotherapists office {name}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"minzoom": 13,
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
"opening_hours",
|
||||
"phone",
|
||||
"email",
|
||||
"website",
|
||||
{
|
||||
"question": {
|
||||
"en": "What is the name of this doctors place?"
|
||||
},
|
||||
"render": {
|
||||
"en": "This doctors place is called {name}"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "name"
|
||||
},
|
||||
"id": "name"
|
||||
},
|
||||
{
|
||||
"condition": "amenity=doctors",
|
||||
"id": "specialty",
|
||||
"render": {
|
||||
"en": "This doctor is specialized in {healthcare:speciality}"
|
||||
},
|
||||
"question": {
|
||||
"en": "What is this doctor specialized in?"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "healthcare:speciality"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "healthcare:speciality=general",
|
||||
"then": {
|
||||
"en": "This is a general practitioner"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "healthcare:speciality=gynaecology",
|
||||
"then": {
|
||||
"en": "This is a gynaecologist"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "healthcare:speciality=psychiatry",
|
||||
"then": {
|
||||
"en": "This is a psychiatrist"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "healthcare:speciality=paediatrics",
|
||||
"then": {
|
||||
"en": "This is a paediatrician"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "a doctors office"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=doctors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": {
|
||||
"en": "a dentists office"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=dentist"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": {
|
||||
"en": "a physiotherapists office"
|
||||
},
|
||||
"tags": [
|
||||
"healthcare=physiotherapist"
|
||||
]
|
||||
}
|
||||
],
|
||||
"filter": [
|
||||
{
|
||||
"id": "opened-now",
|
||||
"options": [
|
||||
{
|
||||
"question": {
|
||||
"en": "Opened now"
|
||||
},
|
||||
"osmTags": "_isOpen=yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"mapRendering": [
|
||||
{
|
||||
"icon": {
|
||||
"render": "circle:white;./assets/layers/doctors/doctors.svg"
|
||||
},
|
||||
"iconSize": "40,40,center",
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -7,8 +7,7 @@
|
|||
"en": "A layer showing pedestrian crossings with rainbow paintings"
|
||||
},
|
||||
"source": {
|
||||
"osmTags":
|
||||
"highway=crossing"
|
||||
"osmTags": "highway=crossing"
|
||||
},
|
||||
"minzoom": 17,
|
||||
"title": {
|
||||
|
@ -38,7 +37,7 @@
|
|||
],
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
{
|
||||
{
|
||||
"id": "crossing-with-rainbow",
|
||||
"question": {
|
||||
"en": "Does this crossing has rainbow paintings?"
|
||||
|
@ -77,10 +76,12 @@
|
|||
{
|
||||
"icon": {
|
||||
"render": "./assets/themes/rainbow_crossings/crossing.svg",
|
||||
"mappings": [{
|
||||
"if": "crossing:marking=rainbow",
|
||||
"then": "./assets/themes/rainbow_crossings/logo.svg"
|
||||
}]
|
||||
"mappings": [
|
||||
{
|
||||
"if": "crossing:marking=rainbow",
|
||||
"then": "./assets/themes/rainbow_crossings/logo.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconSize": "40,40,center",
|
||||
"location": [
|
||||
|
@ -89,4 +90,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"id": "onwheels",
|
||||
"title": {
|
||||
"en": "OnWheels"
|
||||
"en": "OnWheels"
|
||||
},
|
||||
"description": {
|
||||
"en": "On this map, publicly weelchair accessible places are shown and can be easily added"
|
||||
"en": "On this map, publicly weelchair accessible places are shown and can be easily added"
|
||||
},
|
||||
"maintainer": "MapComplete",
|
||||
"icon": "./assets/themes/onwheels/crest.svg",
|
||||
|
@ -29,11 +29,11 @@
|
|||
"viewpoint",
|
||||
"doctors"
|
||||
],
|
||||
"overrideAll" : {
|
||||
"minzoom" : "15",
|
||||
"mapRendering" : [
|
||||
"overrideAll": {
|
||||
"minzoom": "15",
|
||||
"mapRendering": [
|
||||
{
|
||||
"label" : null
|
||||
"label": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
"=presets": [],
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and+": ["crossing:marking=rainbow"]
|
||||
"and+": [
|
||||
"crossing:marking=rainbow"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +40,4 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -51,6 +51,43 @@
|
|||
"tagRenderings": [
|
||||
"all_tags"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "maproulette",
|
||||
"name": "Maproulette Tasks",
|
||||
"source": {
|
||||
"osmTags": "id~*",
|
||||
"geoJson": "https://maproulette.org/api/v2/challenge/view/27971",
|
||||
"isOsmCache": false
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_closest_osm_street_lamp=feat.closest('street_lamps')?.properties?.id",
|
||||
"_closest_osm_street_lamp_distance=feat.distanceTo(feat.properties._closest_osm_street_lamp)",
|
||||
"_has_closeby_feature=Number(feat.properties._closest_osm_street_lamp_distance) < 5 ? 'yes' : 'no'"
|
||||
],
|
||||
"title": "Straatlantaarn in Maproulette",
|
||||
"mapRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"icon": "circle:black",
|
||||
"iconSize": "20,20,center"
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"all_tags",
|
||||
{
|
||||
"id": "link",
|
||||
"render": "<a href='https://maproulette.org/challenge/{mr_challengeId}/task/{mr_taskId}'>View this task</a>"
|
||||
},
|
||||
{
|
||||
"id": "import",
|
||||
|
||||
"render": "{import_button(street_lamps,tags,Import,./assets/svg/addSmall.svg,,,,photo,mr_taskId)}"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"hideFromOverview": true
|
||||
|
|
|
@ -3256,6 +3256,7 @@
|
|||
"name": "Direction visualization"
|
||||
},
|
||||
"doctors": {
|
||||
"description": "This layer shows doctor offices, dentists and other healthcare facilities",
|
||||
"filter": {
|
||||
"0": {
|
||||
"options": {
|
||||
|
@ -3278,6 +3279,10 @@
|
|||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"name": {
|
||||
"question": "What is the name of this doctors place?",
|
||||
"render": "This doctors place is called {name}"
|
||||
},
|
||||
"specialty": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
|
@ -5074,6 +5079,35 @@
|
|||
"render": "Bookcase"
|
||||
}
|
||||
},
|
||||
"rainbow_crossings": {
|
||||
"description": "A layer showing pedestrian crossings with rainbow paintings",
|
||||
"name": "Crossings with rainbow paintings",
|
||||
"presets": {
|
||||
"0": {
|
||||
"description": "Pedestrian crossing",
|
||||
"title": "a crossing"
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"crossing-with-rainbow": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This crossing has rainbow paintings"
|
||||
},
|
||||
"1": {
|
||||
"then": "No rainbow paintings here"
|
||||
},
|
||||
"2": {
|
||||
"then": "No rainbow paintings here"
|
||||
}
|
||||
},
|
||||
"question": "Does this crossing has rainbow paintings?"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Crossing"
|
||||
}
|
||||
},
|
||||
"recycling": {
|
||||
"description": "A layer with recycling containers and centres",
|
||||
"filter": {
|
||||
|
|
|
@ -745,6 +745,10 @@
|
|||
"shortDescription": "Publicly accessible towers to enjoy the view",
|
||||
"title": "Observation towers"
|
||||
},
|
||||
"onwheels": {
|
||||
"description": "On this map, publicly weelchair accessible places are shown and can be easily added",
|
||||
"title": "OnWheels"
|
||||
},
|
||||
"openwindpowermap": {
|
||||
"description": "A map for showing and editing wind turbines.",
|
||||
"title": "OpenWindPowerMap"
|
||||
|
@ -867,6 +871,10 @@
|
|||
"shortDescription": "A map showing postboxes and post offices",
|
||||
"title": "Postbox and Post Office Map"
|
||||
},
|
||||
"rainbow_crossings": {
|
||||
"description": "On this map, rainbow-painted pedestrian crossings are shown and can be easily added",
|
||||
"title": "Rainbow pedestrian crossings"
|
||||
},
|
||||
"shops": {
|
||||
"description": "On this map, one can mark basic information about shops, add opening hours and phone numbers",
|
||||
"shortDescription": "An editable map with basic shop information",
|
||||
|
|
Loading…
Reference in a new issue