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
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
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 * 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) {

View file

@ -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

View file

@ -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"]

View file

@ -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"
]
}
]
}

View file

@ -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 @@
]
}
]
}
}

View file

@ -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
}
]
}

View file

@ -24,7 +24,9 @@
"=presets": [],
"source": {
"osmTags": {
"and+": ["crossing:marking=rainbow"]
"and+": [
"crossing:marking=rainbow"
]
}
}
}
@ -38,6 +40,4 @@
}
}
]
}
}

View file

@ -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

View file

@ -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": {

View file

@ -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",