Refactoring: move special visualisations into multiple files

This commit is contained in:
Pieter Vander Vennet 2025-01-29 20:37:04 +01:00
parent f057b0f358
commit b9bc3f5980
14 changed files with 839 additions and 801 deletions

View file

@ -1,14 +1,33 @@
import { SpecialVisualizationSvelte } from "../SpecialVisualization"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import Maproulette from "../../Logic/Maproulette"
import SvelteUIElement from "../Base/SvelteUIElement"
import MaprouletteSetStatus from "../MapRoulette/MaprouletteSetStatus.svelte"
import TagApplyButton from "../Popup/TagApplyButton"
import { PointImportButtonViz } from "../Popup/ImportButtons/PointImportButtonViz"
import WayImportButtonViz from "../Popup/ImportButtons/WayImportButtonViz"
import ConflateImportButtonViz from "../Popup/ImportButtons/ConflateImportButtonViz"
import { PlantNetDetectionViz } from "../Popup/PlantNetDetectionViz"
import Constants from "../../Models/Constants"
import { Store, Stores, UIEventSource } from "../../Logic/UIEventSource"
import { Feature, GeoJsonProperties } from "geojson"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import BaseUIElement from "../BaseUIElement"
import LinkedDataLoader from "../../Logic/Web/LinkedDataLoader"
import Toggle from "../Input/Toggle"
import ComparisonTool from "../Comparison/ComparisonTool.svelte"
import { Utils } from "../../Utils"
export class MapRouletteSpecialVisualisations {
public static initList(): SpecialVisualizationSvelte[] {
export class DataImportSpecialVisualisations {
public static initList(): (SpecialVisualization & { group })[] {
return [
{
new TagApplyButton(),
new PointImportButtonViz(),
new WayImportButtonViz(),
new ConflateImportButtonViz(),
new PlantNetDetectionViz(),
{
funcName: "maproulette_set_status",
group: "maproulette",
group: "data_import",
docs: "Change the status of the given MapRoulette task",
needsUrls: [Maproulette.defaultEndpoint],
example:
@ -87,6 +106,186 @@ export class MapRouletteSpecialVisualisations {
askFeedback
})
}
}]
},
{
funcName: "linked_data_from_website",
group: "data_import",
docs: "Attempts to load (via a proxy) the specified website and parsed ld+json from there. Suitable data will be offered to import into OSM. Note: this element is added by default",
args: [
{
name: "key",
defaultValue: "website",
doc: "Attempt to load ld+json from the specified URL. This can be in an embedded <script type='ld+json'>"
},
{
name: "useProxy",
defaultValue: "yes",
doc: "If 'yes', uses the provided proxy server. This proxy server will scrape HTML and search for a script with `lang='ld+json'`. If `no`, the data will be downloaded and expects a linked-data-json directly"
},
{
name: "host",
doc: "If not using a proxy, define what host the website is allowed to connect to"
},
{
name: "mode",
doc: "If `display`, only show the data in tabular and readonly form, ignoring already existing tags. This is used to explicitly show all the tags. If unset or anything else, allow to apply/import on OSM"
},
{
name: "collapsed",
defaultValue: "yes",
doc: "If the containing accordion should be closed"
}
],
needsUrls: [Constants.linkedDataProxy, "http://www.schema.org"],
constr(
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
): BaseUIElement {
const key = argument[0] ?? "website"
const useProxy = argument[1] !== "no"
const readonly = argument[3] === "readonly"
const isClosed = (argument[4] ?? "yes") === "yes"
const countryStore: Store<string | undefined> = tags.mapD(
(tags) => tags._country
)
const sourceUrl: Store<string | undefined> = tags.mapD((tags) => {
if (!tags[key] || tags[key] === "undefined") {
return null
}
return tags[key]
})
const externalData: Store<{ success: GeoJsonProperties } | { error }> =
sourceUrl.bindD(
(url) => {
const country = countryStore.data
if (url.startsWith("https://data.velopark.be/")) {
return Stores.FromPromiseWithErr(
(async () => {
try {
const loadAll =
layer.id.toLowerCase().indexOf("maproulette") >=
0 // Dirty hack
const features =
await LinkedDataLoader.fetchVeloparkEntry(
url,
loadAll
)
const feature =
features.find(
(f) => f.properties["ref:velopark"] === url
) ?? features[0]
const properties = feature.properties
properties["ref:velopark"] = url
console.log(
"Got properties from velopark:",
properties
)
return properties
} catch (e) {
console.error(e)
throw e
}
})()
)
}
if (country === undefined) {
return undefined
}
return Stores.FromPromiseWithErr(
(async () => {
try {
return await LinkedDataLoader.fetchJsonLd(
url,
{ country },
useProxy ? "proxy" : "fetch-lod"
)
} catch (e) {
console.log(
"Could not get with proxy/download LOD, attempting to download directly. Error for ",
url,
"is",
e
)
return await LinkedDataLoader.fetchJsonLd(
url,
{ country },
"fetch-raw"
)
}
})()
)
},
[countryStore]
)
externalData.addCallbackAndRunD((lod) =>
console.log("linked_data_from_website received the following data:", lod)
)
return new Toggle(
new SvelteUIElement(ComparisonTool, {
feature,
state,
tags,
layer,
externalData,
sourceUrl,
readonly,
collapsed: isClosed
}),
undefined,
sourceUrl.map((url) => !!url)
)
}
},
{
funcName: "compare_data",
group: "data_import",
needsUrls: (args) => args[1].split(";"),
args: [
{
name: "url",
required: true,
doc: "The attribute containing the url where to fetch more data"
},
{
name: "host",
required: true,
doc: "The domain name(s) where data might be fetched from - this is needed to set the CSP. A domain must include 'https', e.g. 'https://example.com'. For multiple domains, separate them with ';'. If you don't know the possible domains, use '*'. "
},
{
name: "readonly",
required: false,
doc: "If 'yes', will not show 'apply'-buttons"
}
],
docs: "Gives an interactive element which shows a tag comparison between the OSM-object and the upstream object. This allows to copy some or all tags into OSM",
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
): BaseUIElement {
const url = args[0]
const readonly = args[3] === "yes"
const externalData = Stores.FromPromiseWithErr(Utils.downloadJson(url))
return new SvelteUIElement(ComparisonTool, {
url,
state,
tags: tagSource,
layer,
feature,
readonly,
externalData
})
}
}
]
}
}