Allow closing Maproulette tasks

This commit is contained in:
Robin van der Linde 2022-07-13 08:03:09 +00:00 committed by GitHub
parent a1bffc7b7f
commit 65997291bb
12 changed files with 309 additions and 165 deletions

View file

@ -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 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' 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 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 #### 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
View 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}`);
}
}
}

View file

@ -13,6 +13,7 @@ import ChangeToElementsActor from "../Actors/ChangeToElementsActor";
import PendingChangesUploader from "../Actors/PendingChangesUploader"; import PendingChangesUploader from "../Actors/PendingChangesUploader";
import * as translators from "../../assets/translators.json" import * as translators from "../../assets/translators.json"
import {post} from "jquery"; 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, * 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; public mangroveIdentity: MangroveIdentity;
/**
* Maproulette connection
*/
public maprouletteConnection: Maproulette;
public readonly isTranslator : Store<boolean>; public readonly isTranslator : Store<boolean>;
public readonly installedUserThemes: Store<string[]> public readonly installedUserThemes: Store<string[]>
@ -80,6 +86,8 @@ export default class UserRelatedState extends ElementsState {
this.osmConnection.GetLongPreference("identity", "mangrove") this.osmConnection.GetLongPreference("identity", "mangrove")
); );
this.maprouletteConnection = new Maproulette();
if (layoutToUse?.hideFromOverview) { if (layoutToUse?.hideFromOverview) {
this.osmConnection.isLoggedIn.addCallbackAndRunD(loggedIn => { this.osmConnection.isLoggedIn.addCallbackAndRunD(loggedIn => {
if (loggedIn) { if (loggedIn) {

View file

@ -6,6 +6,8 @@ export default class Constants {
public static ImgurApiKey = '7070e7167f0a25a' public static ImgurApiKey = '7070e7167f0a25a'
public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" 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 = [ public static defaultOverpassUrls = [
// The official instance, 10000 queries per day per project allowed // The official instance, 10000 queries per day per project allowed

View file

@ -550,15 +550,21 @@ export class ImportPointButton extends AbstractImportButton {
name: "note_id", name: "note_id",
doc: "If given, this key will be read. The corresponding note on OSM will be closed, stating 'imported'" 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", 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} { showRemovedTags: false}
) )
} }
private static createConfirmPanelForPoint( 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, state: FeaturePipelineState,
guiState: DefaultGuiState, guiState: DefaultGuiState,
originalFeatureTags: UIEventSource<any>, originalFeatureTags: UIEventSource<any>,
@ -600,6 +606,14 @@ export class ImportPointButton extends AbstractImportButton {
originalFeatureTags.data["closed_at"] = new Date().toISOString() originalFeatureTags.data["closed_at"] = new Date().toISOString()
originalFeatureTags.ping() 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"] let preciseInputOption = args["location_picker"]

View file

@ -1,147 +1,147 @@
{ {
"id": "doctors", "id": "doctors",
"name": { "name": {
"en": "doctors" "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": { "mappings": [
"en": "This layer shows doctor offices, dentists and other healthcare facilities" {
}, "if": "amenity=doctors",
"source": { "then": "Doctors Office {name}"
"osmTags": { },
"or": [ {
"amenity=doctors", "if": "amenity=dentist",
"amenity=dentist", "then": "Dentists office {name}"
"healthcare=physiotherapist" },
] {
} "if": "healthcare=physiotherapist",
}, "then": "Physiotherapists office {name}"
"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"
]
}
] ]
},
"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"
]
}
]
} }

View file

@ -7,8 +7,7 @@
"en": "A layer showing pedestrian crossings with rainbow paintings" "en": "A layer showing pedestrian crossings with rainbow paintings"
}, },
"source": { "source": {
"osmTags": "osmTags": "highway=crossing"
"highway=crossing"
}, },
"minzoom": 17, "minzoom": 17,
"title": { "title": {
@ -77,10 +76,12 @@
{ {
"icon": { "icon": {
"render": "./assets/themes/rainbow_crossings/crossing.svg", "render": "./assets/themes/rainbow_crossings/crossing.svg",
"mappings": [{ "mappings": [
"if": "crossing:marking=rainbow", {
"then": "./assets/themes/rainbow_crossings/logo.svg" "if": "crossing:marking=rainbow",
}] "then": "./assets/themes/rainbow_crossings/logo.svg"
}
]
}, },
"iconSize": "40,40,center", "iconSize": "40,40,center",
"location": [ "location": [

View file

@ -1,10 +1,10 @@
{ {
"id": "onwheels", "id": "onwheels",
"title": { "title": {
"en": "OnWheels" "en": "OnWheels"
}, },
"description": { "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", "maintainer": "MapComplete",
"icon": "./assets/themes/onwheels/crest.svg", "icon": "./assets/themes/onwheels/crest.svg",
@ -29,11 +29,11 @@
"viewpoint", "viewpoint",
"doctors" "doctors"
], ],
"overrideAll" : { "overrideAll": {
"minzoom" : "15", "minzoom": "15",
"mapRendering" : [ "mapRendering": [
{ {
"label" : null "label": null
} }
] ]
} }

View file

@ -24,7 +24,9 @@
"=presets": [], "=presets": [],
"source": { "source": {
"osmTags": { "osmTags": {
"and+": ["crossing:marking=rainbow"] "and+": [
"crossing:marking=rainbow"
]
} }
} }
} }
@ -39,5 +41,3 @@
} }
] ]
} }

View file

@ -51,6 +51,43 @@
"tagRenderings": [ "tagRenderings": [
"all_tags" "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 "hideFromOverview": true

View file

@ -3256,6 +3256,7 @@
"name": "Direction visualization" "name": "Direction visualization"
}, },
"doctors": { "doctors": {
"description": "This layer shows doctor offices, dentists and other healthcare facilities",
"filter": { "filter": {
"0": { "0": {
"options": { "options": {
@ -3278,6 +3279,10 @@
} }
}, },
"tagRenderings": { "tagRenderings": {
"name": {
"question": "What is the name of this doctors place?",
"render": "This doctors place is called {name}"
},
"specialty": { "specialty": {
"mappings": { "mappings": {
"0": { "0": {
@ -5074,6 +5079,35 @@
"render": "Bookcase" "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": { "recycling": {
"description": "A layer with recycling containers and centres", "description": "A layer with recycling containers and centres",
"filter": { "filter": {

View file

@ -745,6 +745,10 @@
"shortDescription": "Publicly accessible towers to enjoy the view", "shortDescription": "Publicly accessible towers to enjoy the view",
"title": "Observation towers" "title": "Observation towers"
}, },
"onwheels": {
"description": "On this map, publicly weelchair accessible places are shown and can be easily added",
"title": "OnWheels"
},
"openwindpowermap": { "openwindpowermap": {
"description": "A map for showing and editing wind turbines.", "description": "A map for showing and editing wind turbines.",
"title": "OpenWindPowerMap" "title": "OpenWindPowerMap"
@ -867,6 +871,10 @@
"shortDescription": "A map showing postboxes and post offices", "shortDescription": "A map showing postboxes and post offices",
"title": "Postbox and Post Office Map" "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": { "shops": {
"description": "On this map, one can mark basic information about shops, add opening hours and phone numbers", "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", "shortDescription": "An editable map with basic shop information",