forked from MapComplete/MapComplete
Fix(linkeddata): velopark deals with sections, fix image loading
This commit is contained in:
parent
7a06bb9930
commit
ef1d2c9f56
8 changed files with 242 additions and 123 deletions
|
@ -17,7 +17,7 @@
|
||||||
"nl": "Een hulpmiddel om data van velopark.be in OpenStreetMap in te laden"
|
"nl": "Een hulpmiddel om data van velopark.be in OpenStreetMap in te laden"
|
||||||
},
|
},
|
||||||
"descriptionTail": {
|
"descriptionTail": {
|
||||||
"*": "<h3>Maintainer tools</h3><ul class='link-underline'><li><a target='_blank' href='https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/velopark.md'>See documentation and links to Overpass</a></li><li><a href='https://maproulette.org/api/v2/challenge/view/43282' download='Velopark_sync_2024-01-15.geojson'>Download the first sync results</a></li><li><a href='http://overpass-turbo.eu/?Q=%5Bout%3Ajson%5D%5Btimeout%3A90%5D%3B%28%20%20%20%20nwr%5B%22amenity%22%3D%22bicycle_parking%22%5D%5B%22ref%3Avelopark%22%5D%28%7B%7Bbbox%7D%7D%29%3B%0A%29%3Bout%20body%3B%3E%3Bout%20skel%20qt%3B' target='_blank'>See all bicycle parkings with a velopark ref</a>To export: visit this link, click 'run' and then 'export'; 'export as geojson'</ul>"
|
"*": "<h3>Maintainer tools</h3><ul class='link-underline'><li><a target='_blank' href='https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/velopark.md'>See documentation and links to Overpass</a></li><li><a href='https://maproulette.org/api/v2/challenge/view/43282' download='Velopark_sync_2024-01-15.geojson'>Download the first batch results</a></li><li><a href='https://maproulette.org/api/v2/challenge/view/50552'>Download the second batch results</a></li><li><a href='http://overpass-turbo.eu/?Q=%5Bout%3Ajson%5D%5Btimeout%3A90%5D%3B%28%20%20%20%20nwr%5B%22amenity%22%3D%22bicycle_parking%22%5D%5B%22ref%3Avelopark%22%5D%28%7B%7Bbbox%7D%7D%29%3B%0A%29%3Bout%20body%3B%3E%3Bout%20skel%20qt%3B' target='_blank'>See all bicycle parkings with a velopark ref</a>To export: visit this link, click 'run' and then 'export'; 'export as geojson'</ul>"
|
||||||
},
|
},
|
||||||
"icon": "./assets/themes/velopark/velopark.svg",
|
"icon": "./assets/themes/velopark/velopark.svg",
|
||||||
"startZoom": 18,
|
"startZoom": 18,
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
"description": "Maproulette challenge containing velopark data",
|
"description": "Maproulette challenge containing velopark data",
|
||||||
"source": {
|
"source": {
|
||||||
"osmTags": "mr_taskId~*",
|
"osmTags": "mr_taskId~*",
|
||||||
"geoJson": "https://maproulette.org/api/v2/challenge/view/43282",
|
"geoJson": "https://maproulette.org/api/v2/challenge/view/50552",
|
||||||
"idKey": "mr_taskId"
|
"idKey": "mr_taskId"
|
||||||
},
|
},
|
||||||
"title": {
|
"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",
|
"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"
|
"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"
|
"maproulette_id": "mr_taskId"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,7 +239,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"lineRendering": [],
|
"lineRendering": [
|
||||||
|
{
|
||||||
|
"color": "#bb9922",
|
||||||
|
"lineWidth": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
"filter": [
|
"filter": [
|
||||||
{
|
{
|
||||||
"id": "created-only",
|
"id": "created-only",
|
||||||
|
|
|
@ -2,17 +2,18 @@ import Script from "../Script"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import LinkedDataLoader from "../../src/Logic/Web/LinkedDataLoader"
|
import LinkedDataLoader from "../../src/Logic/Web/LinkedDataLoader"
|
||||||
import { Utils } from "../../src/Utils"
|
import { Utils } from "../../src/Utils"
|
||||||
import { Feature } from "geojson"
|
import { Feature, FeatureCollection, Point } from "geojson"
|
||||||
import { BBox } from "../../src/Logic/BBox"
|
import { BBox } from "../../src/Logic/BBox"
|
||||||
import { Overpass } from "../../src/Logic/Osm/Overpass"
|
import { Overpass } from "../../src/Logic/Osm/Overpass"
|
||||||
import { RegexTag } from "../../src/Logic/Tags/RegexTag"
|
import { RegexTag } from "../../src/Logic/Tags/RegexTag"
|
||||||
import { ImmutableStore } from "../../src/Logic/UIEventSource"
|
import { ImmutableStore } from "../../src/Logic/UIEventSource"
|
||||||
import Constants from "../../src/Models/Constants"
|
import Constants from "../../src/Models/Constants"
|
||||||
|
import { MaprouletteStatus } from "../../src/Logic/Maproulette"
|
||||||
|
|
||||||
class VeloParkToGeojson extends Script {
|
class VeloParkToGeojson extends Script {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(
|
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(
|
JSON.stringify(
|
||||||
extension === ".geojson"
|
extension === ".geojson"
|
||||||
? {
|
? {
|
||||||
type: "FeatureCollection",
|
type: "FeatureCollection",
|
||||||
features,
|
features,
|
||||||
}
|
}
|
||||||
: features,
|
: features,
|
||||||
null,
|
null,
|
||||||
" "
|
" ",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
console.log("Written", file, "(" + features.length, " features)")
|
console.log("Written", file, "(" + features.length, " features)")
|
||||||
}
|
}
|
||||||
|
@ -44,12 +45,15 @@ class VeloParkToGeojson extends Script {
|
||||||
|
|
||||||
const linkedData = await LinkedDataLoader.fetchVeloparkEntry(url)
|
const linkedData = await LinkedDataLoader.fetchVeloparkEntry(url)
|
||||||
const allVelopark: Feature[] = []
|
const allVelopark: Feature[] = []
|
||||||
|
if (linkedData.length > 1) {
|
||||||
|
console.log("Detected multiple sections in:", url)
|
||||||
|
}
|
||||||
for (const sectionId in linkedData) {
|
for (const sectionId in linkedData) {
|
||||||
const sectionInfo = linkedData[sectionId]
|
const sectionInfo = linkedData[sectionId]
|
||||||
if (Object.keys(sectionInfo).length === 0) {
|
if (Object.keys(sectionInfo).length === 0) {
|
||||||
console.warn("No result for", url)
|
console.warn("No result for", url)
|
||||||
}
|
}
|
||||||
if (!sectionInfo.geometry?.coordinates) {
|
if (!sectionInfo.geometry?.["coordinates"]) {
|
||||||
throw "Invalid properties!"
|
throw "Invalid properties!"
|
||||||
}
|
}
|
||||||
allVelopark.push(sectionInfo)
|
allVelopark.push(sectionInfo)
|
||||||
|
@ -62,8 +66,8 @@ class VeloParkToGeojson extends Script {
|
||||||
console.log("Downloading velopark data")
|
console.log("Downloading velopark data")
|
||||||
// Download data for NIS-code 1000. 1000 means: all of belgium
|
// Download data for NIS-code 1000. 1000 means: all of belgium
|
||||||
const url = "https://www.velopark.be/api/parkings/1000"
|
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
|
let failed = 0
|
||||||
console.log("Got", allVeloparkRaw.length, "items")
|
console.log("Got", allVeloparkRaw.length, "items")
|
||||||
const allVelopark: Feature[] = []
|
const allVelopark: Feature[] = []
|
||||||
|
@ -82,14 +86,15 @@ class VeloParkToGeojson extends Script {
|
||||||
console.error("Loading ", f.url, " failed due to", e)
|
console.error("Loading ", f.url, " failed due to", e)
|
||||||
failed++
|
failed++
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
|
console.log("Batch complete:", i)
|
||||||
}
|
}
|
||||||
console.log(
|
console.log(
|
||||||
"Fetching data done, got ",
|
"Fetching data done, got ",
|
||||||
allVelopark.length + "/" + allVeloparkRaw.length,
|
allVelopark.length + "/" + allVeloparkRaw.length,
|
||||||
"failed:",
|
"failed:",
|
||||||
failed
|
failed,
|
||||||
)
|
)
|
||||||
VeloParkToGeojson.exportGeojsonTo("velopark_all", allVelopark)
|
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<string> = new Set<string>();
|
||||||
|
for (const url of challenges) {
|
||||||
|
const data = await Utils.downloadJson<FeatureCollection<Point, {
|
||||||
|
"mr_taskId": string,
|
||||||
|
"ref:velopark": string,
|
||||||
|
mr_taskStatus: MaprouletteStatus,
|
||||||
|
mr_responses: string | undefined
|
||||||
|
}>>(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[]) {
|
private static async createDiff(allVelopark: Feature[]) {
|
||||||
|
console.log("Creating diff...")
|
||||||
const bboxBelgium = new BBox([
|
const bboxBelgium = new BBox([
|
||||||
[2.51357303225, 49.5294835476],
|
[2.51357303225, 49.5294835476],
|
||||||
[6.15665815596, 51.4750237087],
|
[6.15665815596, 51.4750237087],
|
||||||
|
@ -146,42 +181,71 @@ class VeloParkToGeojson extends Script {
|
||||||
[],
|
[],
|
||||||
Constants.defaultOverpassUrls[0],
|
Constants.defaultOverpassUrls[0],
|
||||||
new ImmutableStore(60 * 5),
|
new ImmutableStore(60 * 5),
|
||||||
false
|
false,
|
||||||
)
|
)
|
||||||
const alreadyLinkedFeatures = (await alreadyLinkedQuery.queryGeoJson(bboxBelgium))[0]
|
const alreadyLinkedFeatures = (await alreadyLinkedQuery.queryGeoJson(bboxBelgium))[0]
|
||||||
const seenIds = new Set<string>(
|
const seenIds = new Set<string>(
|
||||||
alreadyLinkedFeatures.features.map((f) => f.properties?.["ref:velopark"])
|
alreadyLinkedFeatures.features.map((f) => f.properties?.["ref:velopark"]),
|
||||||
)
|
)
|
||||||
this.exportGeojsonTo("osm_with_velopark_link", <Feature[]>alreadyLinkedFeatures.features)
|
this.exportGeojsonTo("osm_with_velopark_link", <Feature[]>alreadyLinkedFeatures.features)
|
||||||
console.log("OpenStreetMap contains", seenIds.size, "bicycle parkings with a velopark ref")
|
console.log("OpenStreetMap contains", seenIds.size, "bicycle parkings with a velopark ref")
|
||||||
|
|
||||||
const features: Feature[] = allVelopark.filter(
|
const features: Feature[] = allVelopark.filter(
|
||||||
(f) => !seenIds.has(f.properties["ref:velopark"])
|
(f) => !seenIds.has(f.properties["ref:velopark"]),
|
||||||
)
|
)
|
||||||
VeloParkToGeojson.exportGeojsonTo("velopark_nonsynced", features)
|
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<string>()
|
const allProperties = new Set<string>()
|
||||||
for (const feature of features) {
|
for (const feature of featuresMoreFiltered) {
|
||||||
Object.keys(feature).forEach((k) => allProperties.add(k))
|
Object.keys(feature.properties).forEach((k) => allProperties.add(k))
|
||||||
}
|
}
|
||||||
allProperties.delete("ref:velopark")
|
allProperties.delete("ref:velopark")
|
||||||
for (const feature of features) {
|
for (const feature of featuresMoreFiltered) {
|
||||||
allProperties.forEach((k) => {
|
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<string[]> {
|
||||||
|
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<void> {
|
async main(): Promise<void> {
|
||||||
|
// const multiEntries = new Set(await VeloParkToGeojson.findMultiSection())
|
||||||
const allVelopark =
|
const allVelopark =
|
||||||
VeloParkToGeojson.loadFromFile() ?? (await VeloParkToGeojson.downloadData())
|
VeloParkToGeojson.loadFromFile() ?? (await VeloParkToGeojson.downloadData())
|
||||||
console.log("Got", allVelopark.length, " items")
|
console.log("Got", allVelopark.length, " items")
|
||||||
VeloParkToGeojson.exportExtraAmenities(allVelopark)
|
VeloParkToGeojson.exportExtraAmenities(allVelopark)
|
||||||
await VeloParkToGeojson.createDiff(allVelopark)
|
await VeloParkToGeojson.createDiff(allVelopark)
|
||||||
console.log(
|
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",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,17 @@ export interface MaprouletteTask {
|
||||||
description: string
|
description: string
|
||||||
instruction: 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 {
|
export default class Maproulette {
|
||||||
public static readonly defaultEndpoint = "https://maproulette.org/api/v2"
|
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_TOO_HARD = 6
|
||||||
public static readonly STATUS_DISABLED = 9
|
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()
|
public static singleton = new Maproulette()
|
||||||
/*
|
/*
|
||||||
* The API endpoint to use
|
* The API endpoint to use
|
||||||
|
@ -62,12 +64,11 @@ export default class Maproulette {
|
||||||
if (code === "Created") {
|
if (code === "Created") {
|
||||||
return Maproulette.STATUS_OPEN
|
return Maproulette.STATUS_OPEN
|
||||||
}
|
}
|
||||||
for (let i = 0; i < 9; i++) {
|
const i = maprouletteStatus.findIndex(<any> code)
|
||||||
if (Maproulette.STATUS_MEANING["" + i] === code) {
|
if(i < 0){
|
||||||
return i
|
return undefined
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return undefined
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,7 +88,7 @@ export default class Maproulette {
|
||||||
tags?: string
|
tags?: string
|
||||||
requestReview?: boolean
|
requestReview?: boolean
|
||||||
completionResponses?: Record<string, string>
|
completionResponses?: Record<string, string>
|
||||||
}
|
},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log("Maproulette: setting", `${this.endpoint}/task/${taskId}/${status}`, options)
|
console.log("Maproulette: setting", `${this.endpoint}/task/${taskId}/${status}`, options)
|
||||||
options ??= {}
|
options ??= {}
|
||||||
|
@ -97,9 +98,9 @@ export default class Maproulette {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
apiKey: this.apiKey
|
apiKey: this.apiKey,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(options)
|
body: JSON.stringify(options),
|
||||||
})
|
})
|
||||||
if (response.status !== 204) {
|
if (response.status !== 204) {
|
||||||
console.log(`Failed to close task: ${response.status}`)
|
console.log(`Failed to close task: ${response.status}`)
|
||||||
|
|
|
@ -1,42 +1,14 @@
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
|
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
|
||||||
export class ThemeMetaTagging {
|
export class ThemeMetaTagging {
|
||||||
public static readonly themeName = "usersettings"
|
public static readonly themeName = "usersettings"
|
||||||
|
|
||||||
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
|
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
|
||||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
|
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
|
||||||
feat.properties._description
|
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )
|
||||||
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
|
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) )
|
||||||
?.at(1)
|
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 )
|
||||||
Utils.AddLazyProperty(
|
feat.properties['__current_backgroun'] = 'initial_value'
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -68,7 +68,7 @@ export default class LinkedDataLoader {
|
||||||
coors
|
coors
|
||||||
.trim()
|
.trim()
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map((n) => Number(n))
|
.map((n) => Number(n)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ export default class LinkedDataLoader {
|
||||||
}
|
}
|
||||||
const compacted = await jsonld.compact(
|
const compacted = await jsonld.compact(
|
||||||
openingHoursSpecification,
|
openingHoursSpecification,
|
||||||
<any>LinkedDataLoader.COMPACTING_CONTEXT_OH
|
<any>LinkedDataLoader.COMPACTING_CONTEXT_OH,
|
||||||
)
|
)
|
||||||
const spec: object = compacted["@graph"]
|
const spec: object = compacted["@graph"]
|
||||||
if (!spec) {
|
if (!spec) {
|
||||||
|
@ -190,12 +190,12 @@ export default class LinkedDataLoader {
|
||||||
const compacted = await jsonld.compact(data, <any>LinkedDataLoader.COMPACTING_CONTEXT)
|
const compacted = await jsonld.compact(data, <any>LinkedDataLoader.COMPACTING_CONTEXT)
|
||||||
|
|
||||||
compacted["opening_hours"] = await LinkedDataLoader.ohToOsmFormat(
|
compacted["opening_hours"] = await LinkedDataLoader.ohToOsmFormat(
|
||||||
compacted["opening_hours"]
|
compacted["opening_hours"],
|
||||||
)
|
)
|
||||||
if (compacted["openingHours"]) {
|
if (compacted["openingHours"]) {
|
||||||
const ohspec: string[] = <any>compacted["openingHours"]
|
const ohspec: string[] = <any>compacted["openingHours"]
|
||||||
compacted["opening_hours"] = OH.simplify(
|
compacted["opening_hours"] = OH.simplify(
|
||||||
ohspec.map((r) => LinkedDataLoader.ohStringToOsmFormat(r)).join("; ")
|
ohspec.map((r) => LinkedDataLoader.ohStringToOsmFormat(r)).join("; "),
|
||||||
)
|
)
|
||||||
delete compacted["openingHours"]
|
delete compacted["openingHours"]
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ export default class LinkedDataLoader {
|
||||||
static async fetchJsonLd(
|
static async fetchJsonLd(
|
||||||
url: string,
|
url: string,
|
||||||
options?: JsonLdLoaderOptions,
|
options?: JsonLdLoaderOptions,
|
||||||
mode?: "fetch-lod" | "fetch-raw" | "proxy"
|
mode?: "fetch-lod" | "fetch-raw" | "proxy",
|
||||||
): Promise<object> {
|
): Promise<object> {
|
||||||
mode ??= "fetch-lod"
|
mode ??= "fetch-lod"
|
||||||
if (mode === "proxy") {
|
if (mode === "proxy") {
|
||||||
|
@ -251,7 +251,7 @@ export default class LinkedDataLoader {
|
||||||
const div = document.createElement("div")
|
const div = document.createElement("div")
|
||||||
div.innerHTML = htmlContent
|
div.innerHTML = htmlContent
|
||||||
const script = Array.from(div.getElementsByTagName("script")).find(
|
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)
|
const snippet = JSON.parse(script.textContent)
|
||||||
|
@ -266,7 +266,7 @@ export default class LinkedDataLoader {
|
||||||
*/
|
*/
|
||||||
static removeDuplicateData(
|
static removeDuplicateData(
|
||||||
externalData: Record<string, string>,
|
externalData: Record<string, string>,
|
||||||
currentData: Record<string, string>
|
currentData: Record<string, string>,
|
||||||
): Record<string, string> {
|
): Record<string, string> {
|
||||||
const d = { ...externalData }
|
const d = { ...externalData }
|
||||||
delete d["@context"]
|
delete d["@context"]
|
||||||
|
@ -332,7 +332,7 @@ export default class LinkedDataLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static patchVeloparkProperties(
|
private static patchVeloparkProperties(
|
||||||
input: Record<string, Set<string>>
|
input: Record<string, Set<string>>,
|
||||||
): Record<string, string[]> {
|
): Record<string, string[]> {
|
||||||
const output: Record<string, string[]> = {}
|
const output: Record<string, string[]> = {}
|
||||||
for (const k in input) {
|
for (const k in input) {
|
||||||
|
@ -472,7 +472,7 @@ export default class LinkedDataLoader {
|
||||||
audience,
|
audience,
|
||||||
"for",
|
"for",
|
||||||
input["ref:velopark"],
|
input["ref:velopark"],
|
||||||
" assuming yes"
|
" assuming yes",
|
||||||
)
|
)
|
||||||
return "yes"
|
return "yes"
|
||||||
})
|
})
|
||||||
|
@ -516,8 +516,11 @@ export default class LinkedDataLoader {
|
||||||
private static async fetchVeloparkProperty<T extends string, G extends T>(
|
private static async fetchVeloparkProperty<T extends string, G extends T>(
|
||||||
url: string,
|
url: string,
|
||||||
property: string,
|
property: string,
|
||||||
variable?: string
|
variable?: string,
|
||||||
): Promise<SparqlResult<T, G>> {
|
): Promise<SparqlResult<T, G>> {
|
||||||
|
if(property === "schema:photos"){
|
||||||
|
console.log(">> Getting photos")
|
||||||
|
}
|
||||||
const results = await new TypedSparql().typedSparql<T, G>(
|
const results = await new TypedSparql().typedSparql<T, G>(
|
||||||
{
|
{
|
||||||
schema: "http://schema.org/",
|
schema: "http://schema.org/",
|
||||||
|
@ -529,17 +532,25 @@ export default class LinkedDataLoader {
|
||||||
[url],
|
[url],
|
||||||
undefined,
|
undefined,
|
||||||
" ?parking a <http://schema.mobivoc.org/BicycleParkingStation>",
|
" ?parking a <http://schema.mobivoc.org/BicycleParkingStation>",
|
||||||
"?parking " + property + " " + (variable ?? "")
|
"?parking " + property + " " + (variable ?? ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param property
|
||||||
|
* @param subExpr
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
private static async fetchVeloparkGraphProperty<T extends string>(
|
private static async fetchVeloparkGraphProperty<T extends string>(
|
||||||
url: string,
|
url: string,
|
||||||
property: string,
|
property: string,
|
||||||
subExpr?: string
|
subExpr?: string,
|
||||||
): Promise<SparqlResult<T, "g">> {
|
): Promise<SparqlResult<T, "g">> {
|
||||||
return await new TypedSparql().typedSparql<T, "g">(
|
const result = await new TypedSparql().typedSparql<T, "g">(
|
||||||
{
|
{
|
||||||
schema: "http://schema.org/",
|
schema: "http://schema.org/",
|
||||||
mv: "http://schema.mobivoc.org/",
|
mv: "http://schema.mobivoc.org/",
|
||||||
|
@ -551,8 +562,10 @@ export default class LinkedDataLoader {
|
||||||
"g",
|
"g",
|
||||||
" ?parking a <http://schema.mobivoc.org/BicycleParkingStation>",
|
" ?parking a <http://schema.mobivoc.org/BicycleParkingStation>",
|
||||||
|
|
||||||
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
|
continue
|
||||||
}
|
}
|
||||||
for (const sectionKey in subResult) {
|
for (const sectionKey in subResult) {
|
||||||
if (!r[sectionKey]) {
|
if (sectionKey === "default") {
|
||||||
r[sectionKey] = {}
|
r["default"] ??= {}
|
||||||
}
|
const section = subResult["default"]
|
||||||
const section = subResult[sectionKey]
|
for (const key in section) {
|
||||||
for (const key in section) {
|
r["default"][key] ??= section[key]
|
||||||
r[sectionKey][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) {
|
for (const section in r) {
|
||||||
if (section === "default") {
|
if (section === key) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (const k in r.default) {
|
for (const k in r[key]) {
|
||||||
r[section][k] ??= r.default[k]
|
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
|
return r
|
||||||
}
|
}
|
||||||
|
@ -597,7 +651,7 @@ export default class LinkedDataLoader {
|
||||||
directUrl: string,
|
directUrl: string,
|
||||||
propertiesWithoutGraph: PropertiesSpec<T>,
|
propertiesWithoutGraph: PropertiesSpec<T>,
|
||||||
propertiesInGraph: PropertiesSpec<T>,
|
propertiesInGraph: PropertiesSpec<T>,
|
||||||
extra?: string[]
|
extra?: string[],
|
||||||
): Promise<SparqlResult<T, string>> {
|
): Promise<SparqlResult<T, string>> {
|
||||||
const allPartialResults: SparqlResult<T, string>[] = []
|
const allPartialResults: SparqlResult<T, string>[] = []
|
||||||
for (const propertyName in propertiesWithoutGraph) {
|
for (const propertyName in propertiesWithoutGraph) {
|
||||||
|
@ -607,7 +661,7 @@ export default class LinkedDataLoader {
|
||||||
const result = await this.fetchVeloparkProperty(
|
const result = await this.fetchVeloparkProperty(
|
||||||
directUrl,
|
directUrl,
|
||||||
propertyName,
|
propertyName,
|
||||||
"?" + variableName
|
"?" + variableName,
|
||||||
)
|
)
|
||||||
allPartialResults.push(result)
|
allPartialResults.push(result)
|
||||||
} else {
|
} else {
|
||||||
|
@ -616,7 +670,7 @@ export default class LinkedDataLoader {
|
||||||
const result = await this.fetchVeloparkProperty(
|
const result = await this.fetchVeloparkProperty(
|
||||||
directUrl,
|
directUrl,
|
||||||
propertyName,
|
propertyName,
|
||||||
`[${subProperty} ?${variableName}] `
|
`[${subProperty} ?${variableName}] `,
|
||||||
)
|
)
|
||||||
allPartialResults.push(result)
|
allPartialResults.push(result)
|
||||||
}
|
}
|
||||||
|
@ -634,7 +688,7 @@ export default class LinkedDataLoader {
|
||||||
const result = await this.fetchVeloparkGraphProperty(
|
const result = await this.fetchVeloparkGraphProperty(
|
||||||
directUrl,
|
directUrl,
|
||||||
propertyName,
|
propertyName,
|
||||||
variableName
|
variableName,
|
||||||
)
|
)
|
||||||
allPartialResults.push(result)
|
allPartialResults.push(result)
|
||||||
}
|
}
|
||||||
|
@ -646,7 +700,7 @@ export default class LinkedDataLoader {
|
||||||
const result = await this.fetchVeloparkGraphProperty(
|
const result = await this.fetchVeloparkGraphProperty(
|
||||||
directUrl,
|
directUrl,
|
||||||
propertyName,
|
propertyName,
|
||||||
variableName
|
variableName,
|
||||||
)
|
)
|
||||||
allPartialResults.push(result)
|
allPartialResults.push(result)
|
||||||
} else {
|
} else {
|
||||||
|
@ -655,7 +709,7 @@ export default class LinkedDataLoader {
|
||||||
const result = await this.fetchVeloparkGraphProperty(
|
const result = await this.fetchVeloparkGraphProperty(
|
||||||
directUrl,
|
directUrl,
|
||||||
propertyName,
|
propertyName,
|
||||||
`[${subProperty} ?${variableName}] `
|
`[${subProperty} ?${variableName}] `,
|
||||||
)
|
)
|
||||||
allPartialResults.push(result)
|
allPartialResults.push(result)
|
||||||
}
|
}
|
||||||
|
@ -675,16 +729,18 @@ export default class LinkedDataLoader {
|
||||||
/**
|
/**
|
||||||
* Fetches all data relevant to velopark.
|
* Fetches all data relevant to velopark.
|
||||||
* The id will be saved as `ref:velopark`
|
* The id will be saved as `ref:velopark`
|
||||||
|
* If the entry has multiple sections, this will return multiple items
|
||||||
* @param url
|
* @param url
|
||||||
*/
|
*/
|
||||||
public static async fetchVeloparkEntry(
|
public static async fetchVeloparkEntry(
|
||||||
url: string,
|
url: string,
|
||||||
includeExtras: boolean = false
|
includeExtras: boolean = false,
|
||||||
): Promise<Feature[]> {
|
): Promise<Feature[]> {
|
||||||
const cacheKey = includeExtras + url
|
const cacheKey = includeExtras + url
|
||||||
if (this.veloparkCache[cacheKey]) {
|
if (this.veloparkCache[cacheKey]) {
|
||||||
return 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 withProxyUrl = Constants.linkedDataProxy.replace("{url}", encodeURIComponent(url))
|
||||||
const optionalPaths: Record<string, string | Record<string, string>> = {
|
const optionalPaths: Record<string, string | Record<string, string>> = {
|
||||||
"schema:interactionService": {
|
"schema:interactionService": {
|
||||||
|
@ -697,6 +753,7 @@ export default class LinkedDataLoader {
|
||||||
"schema:email": "email",
|
"schema:email": "email",
|
||||||
"schema:telephone": "phone",
|
"schema:telephone": "phone",
|
||||||
},
|
},
|
||||||
|
// "schema:photos": "images",
|
||||||
"schema:dateModified": "_last_edit_timestamp",
|
"schema:dateModified": "_last_edit_timestamp",
|
||||||
}
|
}
|
||||||
if (includeExtras) {
|
if (includeExtras) {
|
||||||
|
@ -738,8 +795,16 @@ export default class LinkedDataLoader {
|
||||||
withProxyUrl,
|
withProxyUrl,
|
||||||
optionalPaths,
|
optionalPaths,
|
||||||
graphOptionalPaths,
|
graphOptionalPaths,
|
||||||
extra
|
extra,
|
||||||
)
|
)
|
||||||
|
for (const unpatchedKey in unpatched) {
|
||||||
|
// Dirty hack
|
||||||
|
const rawData = await Utils.downloadJsonCached<object>(url, 1000*60*60)
|
||||||
|
const images = rawData["photos"].map(ph => <string> ph.image)
|
||||||
|
unpatched[unpatchedKey].images = new Set<string>(images)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Got unpatched:", unpatched)
|
||||||
const patched: Feature[] = []
|
const patched: Feature[] = []
|
||||||
for (const section in unpatched) {
|
for (const section in unpatched) {
|
||||||
const p = LinkedDataLoader.patchVeloparkProperties(unpatched[section])
|
const p = LinkedDataLoader.patchVeloparkProperties(unpatched[section])
|
||||||
|
|
|
@ -67,13 +67,11 @@ export default class TypedSparql {
|
||||||
bindings.forEach((item) => {
|
bindings.forEach((item) => {
|
||||||
const result = <Record<VARS | G, Set<string>>>{}
|
const result = <Record<VARS | G, Set<string>>>{}
|
||||||
item.forEach((value, key) => {
|
item.forEach((value, key) => {
|
||||||
if (!result[key.value]) {
|
result[key.value] ??= new Set()
|
||||||
result[key.value] = new Set()
|
|
||||||
}
|
|
||||||
result[key.value].add(value.value)
|
result[key.value].add(value.value)
|
||||||
})
|
})
|
||||||
if (graphVariable && result[graphVariable]?.size > 0) {
|
if (graphVariable && result[graphVariable]?.size > 0) {
|
||||||
const id = Array.from(result[graphVariable])?.[0] ?? "default"
|
const id: string = (<string> Array.from(result["id"] ?? [])?.[0] ?? Array.from(result[graphVariable] ?? [])?.[0]) ?? "default"
|
||||||
resultAllGraphs[id] = result
|
resultAllGraphs[id] = result
|
||||||
} else {
|
} else {
|
||||||
resultAllGraphs["default"] = result
|
resultAllGraphs["default"] = result
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import Tr from "../Base/Tr.svelte"
|
import Tr from "../Base/Tr.svelte"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
import Icon from "../Map/Icon.svelte"
|
import Icon from "../Map/Icon.svelte"
|
||||||
import Maproulette from "../../Logic/Maproulette"
|
import Maproulette, { maprouletteStatus } from "../../Logic/Maproulette"
|
||||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,10 +38,11 @@
|
||||||
async function apply() {
|
async function apply() {
|
||||||
const maproulette_id = tags.data[maproulette_id_key] ?? tags.data.mr_taskId ?? tags.data.id
|
const maproulette_id = tags.data[maproulette_id_key] ?? tags.data.mr_taskId ?? tags.data.id
|
||||||
try {
|
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,
|
comment: feedback,
|
||||||
})
|
})
|
||||||
tags.data["mr_taskStatus"] = Maproulette.STATUS_MEANING[Number(statusToSet)]
|
tags.data["mr_taskStatus"] = maprouletteStatus[statusIndex]
|
||||||
tags.data.status = statusToSet
|
tags.data.status = statusToSet
|
||||||
tags.ping()
|
tags.ping()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { PointImportFlowArguments, PointImportFlowState } from "./PointImportFlo
|
||||||
import { Utils } from "../../../Utils"
|
import { Utils } from "../../../Utils"
|
||||||
import { ImportFlowUtils } from "./ImportFlow"
|
import { ImportFlowUtils } from "./ImportFlow"
|
||||||
import Translations from "../../i18n/Translations"
|
import Translations from "../../i18n/Translations"
|
||||||
|
import { GeoOperations } from "../../../Logic/GeoOperations"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The wrapper to make the special visualisation for the PointImportFlow
|
* The wrapper to make the special visualisation for the PointImportFlow
|
||||||
|
@ -44,6 +45,10 @@ export class PointImportButtonViz implements SpecialVisualization {
|
||||||
name: "maproulette_id",
|
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.",
|
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,
|
state: SpecialVisualizationState,
|
||||||
tagSource: UIEventSource<Record<string, string>>,
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
argument: string[],
|
argument: string[],
|
||||||
feature: Feature
|
feature: Feature,
|
||||||
): BaseUIElement {
|
): 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") {
|
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 = <any>Utils.ParseVisArgs(this.args, argument)
|
const baseArgs: PointImportFlowArguments = <any>Utils.ParseVisArgs(this.args, argument)
|
||||||
const tagsToApply = ImportFlowUtils.getTagsToApply(tagSource, baseArgs)
|
const tagsToApply = ImportFlowUtils.getTagsToApply(tagSource, baseArgs)
|
||||||
|
@ -63,7 +75,7 @@ export class PointImportButtonViz implements SpecialVisualization {
|
||||||
<Feature<Point>>feature,
|
<Feature<Point>>feature,
|
||||||
baseArgs,
|
baseArgs,
|
||||||
tagsToApply,
|
tagsToApply,
|
||||||
tagSource
|
tagSource,
|
||||||
)
|
)
|
||||||
|
|
||||||
return new SvelteUIElement(PointImportFlow, {
|
return new SvelteUIElement(PointImportFlow, {
|
||||||
|
|
Loading…
Reference in a new issue