diff --git a/assets/themes/velopark/velopark.json b/assets/themes/velopark/velopark.json
index 428ea0b51..9466b0f79 100644
--- a/assets/themes/velopark/velopark.json
+++ b/assets/themes/velopark/velopark.json
@@ -17,7 +17,7 @@
"nl": "Een hulpmiddel om data van velopark.be in OpenStreetMap in te laden"
},
"descriptionTail": {
- "*": "
Maintainer tools "
+ "*": "Maintainer tools "
},
"icon": "./assets/themes/velopark/velopark.svg",
"startZoom": 18,
@@ -31,7 +31,7 @@
"description": "Maproulette challenge containing velopark data",
"source": {
"osmTags": "mr_taskId~*",
- "geoJson": "https://maproulette.org/api/v2/challenge/view/43282",
+ "geoJson": "https://maproulette.org/api/v2/challenge/view/50552",
"idKey": "mr_taskId"
},
"title": {
@@ -161,6 +161,7 @@
"en": "Create a new bicycle parking in OSM. This parking will have the link, you'll be able to copy the attributes in the next step",
"nl": "Maak een nieuwe parking aan in OSM. Deze parking zal gelinkt zijn met Velopark en je kan in de volgende stap de attributen overzetten"
},
+ "to_point": "yes",
"maproulette_id": "mr_taskId"
}
}
@@ -238,7 +239,12 @@
}
}
],
- "lineRendering": [],
+ "lineRendering": [
+ {
+ "color": "#bb9922",
+ "lineWidth": 2
+ }
+ ],
"filter": [
{
"id": "created-only",
diff --git a/scripts/velopark/veloParkToGeojson.ts b/scripts/velopark/veloParkToGeojson.ts
index c5dc7fb9d..9634df49a 100644
--- a/scripts/velopark/veloParkToGeojson.ts
+++ b/scripts/velopark/veloParkToGeojson.ts
@@ -2,17 +2,18 @@ import Script from "../Script"
import fs from "fs"
import LinkedDataLoader from "../../src/Logic/Web/LinkedDataLoader"
import { Utils } from "../../src/Utils"
-import { Feature } from "geojson"
+import { Feature, FeatureCollection, Point } from "geojson"
import { BBox } from "../../src/Logic/BBox"
import { Overpass } from "../../src/Logic/Osm/Overpass"
import { RegexTag } from "../../src/Logic/Tags/RegexTag"
import { ImmutableStore } from "../../src/Logic/UIEventSource"
import Constants from "../../src/Models/Constants"
+import { MaprouletteStatus } from "../../src/Logic/Maproulette"
class VeloParkToGeojson extends Script {
constructor() {
super(
- "Downloads the latest Velopark data and converts it to a geojson, which will be saved at the current directory"
+ "Downloads the latest Velopark data and converts it to a geojson, which will be saved at the current directory",
)
}
@@ -23,13 +24,13 @@ class VeloParkToGeojson extends Script {
JSON.stringify(
extension === ".geojson"
? {
- type: "FeatureCollection",
- features,
- }
+ type: "FeatureCollection",
+ features,
+ }
: features,
null,
- " "
- )
+ " ",
+ ),
)
console.log("Written", file, "(" + features.length, " features)")
}
@@ -44,12 +45,15 @@ class VeloParkToGeojson extends Script {
const linkedData = await LinkedDataLoader.fetchVeloparkEntry(url)
const allVelopark: Feature[] = []
+ if (linkedData.length > 1) {
+ console.log("Detected multiple sections in:", url)
+ }
for (const sectionId in linkedData) {
const sectionInfo = linkedData[sectionId]
if (Object.keys(sectionInfo).length === 0) {
console.warn("No result for", url)
}
- if (!sectionInfo.geometry?.coordinates) {
+ if (!sectionInfo.geometry?.["coordinates"]) {
throw "Invalid properties!"
}
allVelopark.push(sectionInfo)
@@ -62,8 +66,8 @@ class VeloParkToGeojson extends Script {
console.log("Downloading velopark data")
// Download data for NIS-code 1000. 1000 means: all of belgium
const url = "https://www.velopark.be/api/parkings/1000"
- const allVeloparkRaw: { url: string }[] = <{ url: string }[]>await Utils.downloadJson(url)
-
+ const allVeloparkRaw= (await Utils.downloadJson<{ url: string }[]>(url))
+ // Example multi-entry: https://data.velopark.be/data/Stad-Izegem_IZE_015
let failed = 0
console.log("Got", allVeloparkRaw.length, "items")
const allVelopark: Feature[] = []
@@ -82,14 +86,15 @@ class VeloParkToGeojson extends Script {
console.error("Loading ", f.url, " failed due to", e)
failed++
}
- })
+ }),
)
+ console.log("Batch complete:", i)
}
console.log(
"Fetching data done, got ",
allVelopark.length + "/" + allVeloparkRaw.length,
"failed:",
- failed
+ failed,
)
VeloParkToGeojson.exportGeojsonTo("velopark_all", allVelopark)
@@ -135,7 +140,37 @@ class VeloParkToGeojson extends Script {
}
}
+ private static async fetchMapRouletteClosedItems() {
+ const challenges = ["https://maproulette.org/api/v2/challenge/view/43282"]
+ const solvedRefs: Set = new Set();
+ for (const url of challenges) {
+ const data = await Utils.downloadJson>(url)
+ for (const challenge of data.features) {
+ const status = challenge.properties.mr_taskStatus
+ const isClosed = status === "Fixed" || status === "False_positive" || status === "Already fixed" || status === "Too_Hard" || status === "Deleted"
+ if(isClosed){
+ const ref = challenge.properties["ref:velopark"]
+ solvedRefs .add(ref)
+ }
+ }
+ }
+ console.log("Detected", solvedRefs,"as closed on mapRoulette")
+ return solvedRefs
+ }
+
+ /**
+ * Creates an extra version where all bicycle parkings which are already linked are removed.
+ * Fetches the latest OSM-data from overpass
+ * @param allVelopark
+ * @private
+ */
private static async createDiff(allVelopark: Feature[]) {
+ console.log("Creating diff...")
const bboxBelgium = new BBox([
[2.51357303225, 49.5294835476],
[6.15665815596, 51.4750237087],
@@ -146,42 +181,71 @@ class VeloParkToGeojson extends Script {
[],
Constants.defaultOverpassUrls[0],
new ImmutableStore(60 * 5),
- false
+ false,
)
const alreadyLinkedFeatures = (await alreadyLinkedQuery.queryGeoJson(bboxBelgium))[0]
const seenIds = new Set(
- alreadyLinkedFeatures.features.map((f) => f.properties?.["ref:velopark"])
+ alreadyLinkedFeatures.features.map((f) => f.properties?.["ref:velopark"]),
)
this.exportGeojsonTo("osm_with_velopark_link", alreadyLinkedFeatures.features)
console.log("OpenStreetMap contains", seenIds.size, "bicycle parkings with a velopark ref")
const features: Feature[] = allVelopark.filter(
- (f) => !seenIds.has(f.properties["ref:velopark"])
+ (f) => !seenIds.has(f.properties["ref:velopark"]),
)
VeloParkToGeojson.exportGeojsonTo("velopark_nonsynced", features)
+ const synced =await this.fetchMapRouletteClosedItems()
+ const featuresMoreFiltered = features.filter(
+ (f) => !synced.has(f.properties["ref:velopark"])
+ )
+ VeloParkToGeojson.exportGeojsonTo("velopark_nonsynced_nonclosed", featuresMoreFiltered)
+
+
+
+ const featuresMoreFilteredFailed = features.filter(
+ (f) => synced.has(f.properties["ref:velopark"])
+ )
+ VeloParkToGeojson.exportGeojsonTo("velopark_nonsynced_human_import_failed", featuresMoreFilteredFailed)
+
const allProperties = new Set()
- for (const feature of features) {
- Object.keys(feature).forEach((k) => allProperties.add(k))
+ for (const feature of featuresMoreFiltered) {
+ Object.keys(feature.properties).forEach((k) => allProperties.add(k))
}
allProperties.delete("ref:velopark")
- for (const feature of features) {
+ for (const feature of featuresMoreFiltered) {
allProperties.forEach((k) => {
- delete feature[k]
+ if(k === "ref:velopark"){
+ return
+ }
+ delete feature.properties[k]
})
}
- this.exportGeojsonTo("velopark_nonsynced_id_only", features)
+ this.exportGeojsonTo("velopark_nonsynced_nonclosed_id_only", featuresMoreFiltered)
+ }
+
+ public static async findMultiSection(): Promise {
+ const url = "https://www.velopark.be/api/parkings/1000"
+ const raw = await Utils.downloadJson<{"@graph": {}[], url: string}[]>(url)
+ const multiEntries: string[] = []
+ for (const entry of raw) {
+ if(entry["@graph"].length > 1){
+ multiEntries.push(entry.url)
+ }
+ }
+ return multiEntries
}
async main(): Promise {
+ // const multiEntries = new Set(await VeloParkToGeojson.findMultiSection())
const allVelopark =
VeloParkToGeojson.loadFromFile() ?? (await VeloParkToGeojson.downloadData())
console.log("Got", allVelopark.length, " items")
VeloParkToGeojson.exportExtraAmenities(allVelopark)
await VeloParkToGeojson.createDiff(allVelopark)
console.log(
- "Use vite-node scripts/velopark/compare.ts to compare the results and generate a diff file"
+ "Use vite-node scripts/velopark/compare.ts to compare the results and generate a diff file",
)
}
}
diff --git a/src/Logic/Maproulette.ts b/src/Logic/Maproulette.ts
index fd561ede7..51f45cd52 100644
--- a/src/Logic/Maproulette.ts
+++ b/src/Logic/Maproulette.ts
@@ -6,6 +6,17 @@ export interface MaprouletteTask {
description: string
instruction: string
}
+export const maprouletteStatus = ["Open",
+ "Fixed",
+ "False_positive",
+ "Skipped",
+ "Deleted",
+ "Already fixed",
+ "Too_Hard",
+ "Disabled",
+] as const
+
+export type MaprouletteStatus = typeof maprouletteStatus[number]
export default class Maproulette {
public static readonly defaultEndpoint = "https://maproulette.org/api/v2"
@@ -19,16 +30,7 @@ export default class Maproulette {
public static readonly STATUS_TOO_HARD = 6
public static readonly STATUS_DISABLED = 9
- public static readonly STATUS_MEANING = {
- 0: "Open",
- 1: "Fixed",
- 2: "False_positive",
- 3: "Skipped",
- 4: "Deleted",
- 5: "Already fixed",
- 6: "Too_Hard",
- 9: "Disabled"
- }
+
public static singleton = new Maproulette()
/*
* The API endpoint to use
@@ -62,12 +64,11 @@ export default class Maproulette {
if (code === "Created") {
return Maproulette.STATUS_OPEN
}
- for (let i = 0; i < 9; i++) {
- if (Maproulette.STATUS_MEANING["" + i] === code) {
- return i
- }
+ const i = maprouletteStatus.findIndex( code)
+ if(i < 0){
+ return undefined
}
- return undefined
+ return i
}
/**
@@ -87,7 +88,7 @@ export default class Maproulette {
tags?: string
requestReview?: boolean
completionResponses?: Record
- }
+ },
): Promise {
console.log("Maproulette: setting", `${this.endpoint}/task/${taskId}/${status}`, options)
options ??= {}
@@ -97,9 +98,9 @@ export default class Maproulette {
method: "PUT",
headers: {
"Content-Type": "application/json",
- apiKey: this.apiKey
+ apiKey: this.apiKey,
},
- body: JSON.stringify(options)
+ body: JSON.stringify(options),
})
if (response.status !== 204) {
console.log(`Failed to close task: ${response.status}`)
diff --git a/src/Logic/State/UserSettingsMetaTagging.ts b/src/Logic/State/UserSettingsMetaTagging.ts
index 6e568c5c3..33a5ae85b 100644
--- a/src/Logic/State/UserSettingsMetaTagging.ts
+++ b/src/Logic/State/UserSettingsMetaTagging.ts
@@ -1,42 +1,14 @@
import { Utils } from "../../Utils"
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
export class ThemeMetaTagging {
- public static readonly themeName = "usersettings"
+ public static readonly themeName = "usersettings"
- public metaTaggging_for_usersettings(feat: { properties: Record }) {
- Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
- feat.properties._description
- .match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
- ?.at(1)
- )
- Utils.AddLazyProperty(
- feat.properties,
- "_d",
- () => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? ""
- )
- Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
- ((feat) => {
- const e = document.createElement("div")
- e.innerHTML = feat.properties._d
- return Array.from(e.getElementsByTagName("a")).filter(
- (a) => a.href.match(/mastodon|en.osm.town/) !== null
- )[0]?.href
- })(feat)
- )
- Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
- ((feat) => {
- const e = document.createElement("div")
- e.innerHTML = feat.properties._d
- return Array.from(e.getElementsByTagName("a")).filter(
- (a) => a.getAttribute("rel")?.indexOf("me") >= 0
- )[0]?.href
- })(feat)
- )
- Utils.AddLazyProperty(
- feat.properties,
- "_mastodon_candidate",
- () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
- )
- feat.properties["__current_backgroun"] = "initial_value"
- }
-}
+ public metaTaggging_for_usersettings(feat: {properties: Record}) {
+ Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
+ Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )
+ Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
+ Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
+ Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
+ feat.properties['__current_backgroun'] = 'initial_value'
+ }
+}
\ No newline at end of file
diff --git a/src/Logic/Web/LinkedDataLoader.ts b/src/Logic/Web/LinkedDataLoader.ts
index 268fd4865..21a8ab650 100644
--- a/src/Logic/Web/LinkedDataLoader.ts
+++ b/src/Logic/Web/LinkedDataLoader.ts
@@ -68,7 +68,7 @@ export default class LinkedDataLoader {
coors
.trim()
.split(" ")
- .map((n) => Number(n))
+ .map((n) => Number(n)),
),
],
}
@@ -156,7 +156,7 @@ export default class LinkedDataLoader {
}
const compacted = await jsonld.compact(
openingHoursSpecification,
- LinkedDataLoader.COMPACTING_CONTEXT_OH
+ LinkedDataLoader.COMPACTING_CONTEXT_OH,
)
const spec: object = compacted["@graph"]
if (!spec) {
@@ -190,12 +190,12 @@ export default class LinkedDataLoader {
const compacted = await jsonld.compact(data, LinkedDataLoader.COMPACTING_CONTEXT)
compacted["opening_hours"] = await LinkedDataLoader.ohToOsmFormat(
- compacted["opening_hours"]
+ compacted["opening_hours"],
)
if (compacted["openingHours"]) {
const ohspec: string[] = compacted["openingHours"]
compacted["opening_hours"] = OH.simplify(
- ohspec.map((r) => LinkedDataLoader.ohStringToOsmFormat(r)).join("; ")
+ ohspec.map((r) => LinkedDataLoader.ohStringToOsmFormat(r)).join("; "),
)
delete compacted["openingHours"]
}
@@ -236,7 +236,7 @@ export default class LinkedDataLoader {
static async fetchJsonLd(
url: string,
options?: JsonLdLoaderOptions,
- mode?: "fetch-lod" | "fetch-raw" | "proxy"
+ mode?: "fetch-lod" | "fetch-raw" | "proxy",
): Promise {
mode ??= "fetch-lod"
if (mode === "proxy") {
@@ -251,7 +251,7 @@ export default class LinkedDataLoader {
const div = document.createElement("div")
div.innerHTML = htmlContent
const script = Array.from(div.getElementsByTagName("script")).find(
- (script) => script.type === "application/ld+json"
+ (script) => script.type === "application/ld+json",
)
const snippet = JSON.parse(script.textContent)
@@ -266,7 +266,7 @@ export default class LinkedDataLoader {
*/
static removeDuplicateData(
externalData: Record,
- currentData: Record
+ currentData: Record,
): Record {
const d = { ...externalData }
delete d["@context"]
@@ -332,7 +332,7 @@ export default class LinkedDataLoader {
}
private static patchVeloparkProperties(
- input: Record>
+ input: Record>,
): Record {
const output: Record = {}
for (const k in input) {
@@ -472,7 +472,7 @@ export default class LinkedDataLoader {
audience,
"for",
input["ref:velopark"],
- " assuming yes"
+ " assuming yes",
)
return "yes"
})
@@ -516,8 +516,11 @@ export default class LinkedDataLoader {
private static async fetchVeloparkProperty(
url: string,
property: string,
- variable?: string
+ variable?: string,
): Promise> {
+ if(property === "schema:photos"){
+ console.log(">> Getting photos")
+ }
const results = await new TypedSparql().typedSparql(
{
schema: "http://schema.org/",
@@ -529,17 +532,25 @@ export default class LinkedDataLoader {
[url],
undefined,
" ?parking a ",
- "?parking " + property + " " + (variable ?? "")
+ "?parking " + property + " " + (variable ?? ""),
)
+
return results
}
+ /**
+ *
+ * @param url
+ * @param property
+ * @param subExpr
+ * @private
+ */
private static async fetchVeloparkGraphProperty(
url: string,
property: string,
- subExpr?: string
+ subExpr?: string,
): Promise> {
- return await new TypedSparql().typedSparql(
+ const result = await new TypedSparql().typedSparql(
{
schema: "http://schema.org/",
mv: "http://schema.mobivoc.org/",
@@ -551,8 +562,10 @@ export default class LinkedDataLoader {
"g",
" ?parking a ",
- S.graph("g", "?section " + property + " " + (subExpr ?? ""), "?section a ?type")
+ S.graph("g", "?section " + property + " " + (subExpr ?? ""), "?section a ?type", "BIND(STR(?section) AS ?id)"),
)
+
+ return result
}
/**
@@ -569,26 +582,67 @@ export default class LinkedDataLoader {
continue
}
for (const sectionKey in subResult) {
- if (!r[sectionKey]) {
- r[sectionKey] = {}
- }
- const section = subResult[sectionKey]
- for (const key in section) {
- r[sectionKey][key] ??= section[key]
+ if (sectionKey === "default") {
+ r["default"] ??= {}
+ const section = subResult["default"]
+ for (const key in section) {
+ r["default"][key] ??= section[key]
+ }
+ } else {
+ const section = subResult[sectionKey]
+ const actualId = Array.from(section["id"] ?? [])[0] ?? sectionKey
+ r[actualId] ??= {}
+ for (const key in section) {
+ r[actualId][key] ??= section[key]
+ }
}
}
}
- if (r["default"] !== undefined && Object.keys(r).length > 1) {
+ /**
+ * Copy all values from the section with name "key" into the other sections,
+ * remove section "key" afterwards
+ * @param key
+ */
+ function spreadSection(key: string){
for (const section in r) {
- if (section === "default") {
+ if (section === key) {
continue
}
- for (const k in r.default) {
- r[section][k] ??= r.default[k]
+ for (const k in r[key]) {
+ r[section][k] ??= r[key][k]
}
}
- delete r.default
+ delete r[key]
+ }
+
+ // The "default" part of the result contains all general info
+ // The other 'sections' need to get those copied! Then, we delete the "default"-section
+ if (r["default"] !== undefined && Object.keys(r).length > 1) {
+ spreadSection("default")
+
+ }
+ if (Object.keys(r).length > 1) {
+ // This result has multiple sections
+ // We should check that the naked URL got distributed and scrapped
+ const keys = Object.keys(r)
+ if (Object.keys(r).length > 2) {
+ console.log("Multiple sections detected: ", JSON.stringify(keys))
+ }
+ const shortestKeyLength: number = Math.min(...keys.map(k => k.length))
+ const key = keys.find(k => k.length === shortestKeyLength)
+ if (keys.some(k => !k.startsWith(key))) {
+ throw "Invalid multi-object: the shortest key is not the start of all the others: " + JSON.stringify(keys)
+ }
+ spreadSection(key)
+ }
+ if (Object.keys(r).length == 1) {
+ const key = Object.keys(r)[0]
+ if(key.indexOf("#")>0){
+ const newKey = key.split("#")[0]
+ r[newKey] = r[key]
+ delete r[key]
+ }
}
return r
}
@@ -597,7 +651,7 @@ export default class LinkedDataLoader {
directUrl: string,
propertiesWithoutGraph: PropertiesSpec,
propertiesInGraph: PropertiesSpec,
- extra?: string[]
+ extra?: string[],
): Promise> {
const allPartialResults: SparqlResult[] = []
for (const propertyName in propertiesWithoutGraph) {
@@ -607,7 +661,7 @@ export default class LinkedDataLoader {
const result = await this.fetchVeloparkProperty(
directUrl,
propertyName,
- "?" + variableName
+ "?" + variableName,
)
allPartialResults.push(result)
} else {
@@ -616,7 +670,7 @@ export default class LinkedDataLoader {
const result = await this.fetchVeloparkProperty(
directUrl,
propertyName,
- `[${subProperty} ?${variableName}] `
+ `[${subProperty} ?${variableName}] `,
)
allPartialResults.push(result)
}
@@ -634,7 +688,7 @@ export default class LinkedDataLoader {
const result = await this.fetchVeloparkGraphProperty(
directUrl,
propertyName,
- variableName
+ variableName,
)
allPartialResults.push(result)
}
@@ -646,7 +700,7 @@ export default class LinkedDataLoader {
const result = await this.fetchVeloparkGraphProperty(
directUrl,
propertyName,
- variableName
+ variableName,
)
allPartialResults.push(result)
} else {
@@ -655,7 +709,7 @@ export default class LinkedDataLoader {
const result = await this.fetchVeloparkGraphProperty(
directUrl,
propertyName,
- `[${subProperty} ?${variableName}] `
+ `[${subProperty} ?${variableName}] `,
)
allPartialResults.push(result)
}
@@ -675,16 +729,18 @@ export default class LinkedDataLoader {
/**
* Fetches all data relevant to velopark.
* The id will be saved as `ref:velopark`
+ * If the entry has multiple sections, this will return multiple items
* @param url
*/
public static async fetchVeloparkEntry(
url: string,
- includeExtras: boolean = false
+ includeExtras: boolean = false,
): Promise {
const cacheKey = includeExtras + url
if (this.veloparkCache[cacheKey]) {
return this.veloparkCache[cacheKey]
}
+ // Note: the proxy doesn't make any changes in this case
const withProxyUrl = Constants.linkedDataProxy.replace("{url}", encodeURIComponent(url))
const optionalPaths: Record> = {
"schema:interactionService": {
@@ -697,6 +753,7 @@ export default class LinkedDataLoader {
"schema:email": "email",
"schema:telephone": "phone",
},
+ // "schema:photos": "images",
"schema:dateModified": "_last_edit_timestamp",
}
if (includeExtras) {
@@ -738,8 +795,16 @@ export default class LinkedDataLoader {
withProxyUrl,
optionalPaths,
graphOptionalPaths,
- extra
+ extra,
)
+ for (const unpatchedKey in unpatched) {
+ // Dirty hack
+ const rawData = await Utils.downloadJsonCached(url, 1000*60*60)
+ const images = rawData["photos"].map(ph => ph.image)
+ unpatched[unpatchedKey].images = new Set(images)
+ }
+
+ console.log("Got unpatched:", unpatched)
const patched: Feature[] = []
for (const section in unpatched) {
const p = LinkedDataLoader.patchVeloparkProperties(unpatched[section])
diff --git a/src/Logic/Web/TypedSparql.ts b/src/Logic/Web/TypedSparql.ts
index c87991205..4cda88ce4 100644
--- a/src/Logic/Web/TypedSparql.ts
+++ b/src/Logic/Web/TypedSparql.ts
@@ -67,13 +67,11 @@ export default class TypedSparql {
bindings.forEach((item) => {
const result = >>{}
item.forEach((value, key) => {
- if (!result[key.value]) {
- result[key.value] = new Set()
- }
+ result[key.value] ??= new Set()
result[key.value].add(value.value)
})
if (graphVariable && result[graphVariable]?.size > 0) {
- const id = Array.from(result[graphVariable])?.[0] ?? "default"
+ const id: string = ( Array.from(result["id"] ?? [])?.[0] ?? Array.from(result[graphVariable] ?? [])?.[0]) ?? "default"
resultAllGraphs[id] = result
} else {
resultAllGraphs["default"] = result
diff --git a/src/UI/MapRoulette/MaprouletteSetStatus.svelte b/src/UI/MapRoulette/MaprouletteSetStatus.svelte
index 3ea6056e2..56619f6b7 100644
--- a/src/UI/MapRoulette/MaprouletteSetStatus.svelte
+++ b/src/UI/MapRoulette/MaprouletteSetStatus.svelte
@@ -5,7 +5,7 @@
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
import Icon from "../Map/Icon.svelte"
- import Maproulette from "../../Logic/Maproulette"
+ import Maproulette, { maprouletteStatus } from "../../Logic/Maproulette"
import LoginToggle from "../Base/LoginToggle.svelte"
/**
@@ -38,10 +38,11 @@
async function apply() {
const maproulette_id = tags.data[maproulette_id_key] ?? tags.data.mr_taskId ?? tags.data.id
try {
- await Maproulette.singleton.closeTask(Number(maproulette_id), Number(statusToSet), state, {
+ const statusIndex = Maproulette.codeToIndex(statusToSet) ?? Number(statusToSet)
+ await Maproulette.singleton.closeTask(Number(maproulette_id), statusIndex, state, {
comment: feedback,
})
- tags.data["mr_taskStatus"] = Maproulette.STATUS_MEANING[Number(statusToSet)]
+ tags.data["mr_taskStatus"] = maprouletteStatus[statusIndex]
tags.data.status = statusToSet
tags.ping()
} catch (e) {
diff --git a/src/UI/Popup/ImportButtons/PointImportButtonViz.ts b/src/UI/Popup/ImportButtons/PointImportButtonViz.ts
index e5addb9e6..8be4e3f46 100644
--- a/src/UI/Popup/ImportButtons/PointImportButtonViz.ts
+++ b/src/UI/Popup/ImportButtons/PointImportButtonViz.ts
@@ -9,6 +9,7 @@ import { PointImportFlowArguments, PointImportFlowState } from "./PointImportFlo
import { Utils } from "../../../Utils"
import { ImportFlowUtils } from "./ImportFlow"
import Translations from "../../i18n/Translations"
+import { GeoOperations } from "../../../Logic/GeoOperations"
/**
* The wrapper to make the special visualisation for the PointImportFlow
@@ -44,6 +45,10 @@ export class PointImportButtonViz implements SpecialVisualization {
name: "maproulette_id",
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.",
},
+ {
+ name: "to_point",
+ doc: "If set, a feature will be converted to a centerpoint",
+ },
]
}
@@ -51,10 +56,17 @@ export class PointImportButtonViz implements SpecialVisualization {
state: SpecialVisualizationState,
tagSource: UIEventSource>,
argument: string[],
- feature: Feature
+ feature: Feature,
): BaseUIElement {
+
+ const to_point_index = this.args.findIndex(arg => arg.name === "to_point")
+ const summarizePointArg = argument[to_point_index].toLowerCase()
if (feature.geometry.type !== "Point") {
- return Translations.t.general.add.import.wrongType.SetClass("alert")
+ if (summarizePointArg !== "no" && summarizePointArg !== "false") {
+ feature = GeoOperations.centerpoint(feature)
+ } else {
+ return Translations.t.general.add.import.wrongType.SetClass("alert")
+ }
}
const baseArgs: PointImportFlowArguments = Utils.ParseVisArgs(this.args, argument)
const tagsToApply = ImportFlowUtils.getTagsToApply(tagSource, baseArgs)
@@ -63,7 +75,7 @@ export class PointImportButtonViz implements SpecialVisualization {
>feature,
baseArgs,
tagsToApply,
- tagSource
+ tagSource,
)
return new SvelteUIElement(PointImportFlow, {