2025-06-02 17:03:07 +02:00
|
|
|
<script lang="ts">
|
|
|
|
import Loading from "../Base/Loading.svelte"
|
|
|
|
import MaplibreMap from "../Map/MaplibreMap.svelte"
|
|
|
|
import { Utils } from "../../Utils"
|
|
|
|
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
|
|
|
import FilteredLayer from "../../Models/FilteredLayer"
|
|
|
|
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
|
|
|
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
|
|
|
import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
|
|
|
|
import ShowDataLayer from "../Map/ShowDataLayer"
|
|
|
|
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
|
|
|
import SpecialVisualizations from "../SpecialVisualizations"
|
|
|
|
import type { AutoAction } from "./AutoApplyButtonVis"
|
|
|
|
import Tr from "../Base/Tr.svelte"
|
|
|
|
import Translations from "../i18n/Translations"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The ids to handle. Might be data from an external dataset, we cannot assume an OSM-id
|
|
|
|
*/
|
|
|
|
export let ids: Store<string[] | undefined>
|
|
|
|
export let state: SpecialVisualizationState
|
|
|
|
export let options: {
|
2025-06-04 00:21:28 +02:00
|
|
|
target_layer_id: string
|
|
|
|
targetTagRendering: string
|
|
|
|
text: string
|
|
|
|
icon: string
|
2025-06-02 17:03:07 +02:00
|
|
|
}
|
2025-06-04 00:21:28 +02:00
|
|
|
let buttonState: UIEventSource<"idle" | "running" | "done" | { error: string }> =
|
|
|
|
new UIEventSource<"idle" | "running" | "done" | { error: string }>("idle")
|
2025-06-02 17:03:07 +02:00
|
|
|
let tagRenderingConfig: TagRenderingConfig
|
|
|
|
let appliedNumberOfFeatures = new UIEventSource<number>(0)
|
|
|
|
|
|
|
|
let layer: FilteredLayer = state.layerState.filteredLayers.get(options.target_layer_id)
|
|
|
|
tagRenderingConfig = layer.layerDef.tagRenderings.find(
|
|
|
|
(tr) => tr.id === options.targetTagRendering
|
|
|
|
)
|
|
|
|
|
|
|
|
const mlmap = new UIEventSource(undefined)
|
|
|
|
const mla = new MapLibreAdaptor(mlmap, {
|
2025-06-04 00:21:28 +02:00
|
|
|
rasterLayer: state.mapProperties.rasterLayer,
|
2025-06-02 17:03:07 +02:00
|
|
|
})
|
|
|
|
mla.allowZooming.setData(false)
|
|
|
|
mla.allowMoving.setData(false)
|
|
|
|
|
2025-06-04 00:21:28 +02:00
|
|
|
const features = ids.mapD((ids) =>
|
|
|
|
ids.map((id) => state.indexedFeatures.featuresById.data.get(id))
|
|
|
|
)
|
2025-06-02 17:03:07 +02:00
|
|
|
|
|
|
|
new ShowDataLayer(mlmap, {
|
|
|
|
features: StaticFeatureSource.fromGeojson(features),
|
|
|
|
zoomToFeatures: true,
|
2025-06-04 00:21:28 +02:00
|
|
|
layer: layer.layerDef,
|
2025-06-02 17:03:07 +02:00
|
|
|
})
|
|
|
|
|
2025-06-04 00:21:28 +02:00
|
|
|
features.addCallbackAndRunD((f) => console.log("Features are now", f))
|
2025-06-02 17:03:07 +02:00
|
|
|
|
|
|
|
async function applyAllChanges() {
|
|
|
|
buttonState.set("running")
|
|
|
|
try {
|
|
|
|
const target_feature_ids = ids.data
|
|
|
|
console.log("Applying auto-action on " + target_feature_ids.length + " features")
|
|
|
|
const appliedOn: string[] = []
|
|
|
|
for (let i = 0; i < target_feature_ids.length; i++) {
|
|
|
|
const targetFeatureId = target_feature_ids[i]
|
|
|
|
console.log("Handling", targetFeatureId)
|
|
|
|
const feature = state.indexedFeatures.featuresById.data.get(targetFeatureId)
|
|
|
|
const featureTags = state.featureProperties.getStore(targetFeatureId)
|
|
|
|
const rendering = tagRenderingConfig.GetRenderValue(featureTags.data).txt
|
2025-08-01 04:02:09 +02:00
|
|
|
const specialRenderings = Lists.noNull(SpecialVisualizations.constructSpecification(rendering)).filter((v) => typeof v !== "string" && v.func["supportsAutoAction"] === true)
|
2025-06-02 17:03:07 +02:00
|
|
|
|
|
|
|
if (specialRenderings.length == 0) {
|
|
|
|
console.warn(
|
|
|
|
"AutoApply: feature " +
|
2025-06-04 00:21:28 +02:00
|
|
|
targetFeatureId +
|
|
|
|
" got a rendering without supported auto actions:",
|
2025-06-02 17:03:07 +02:00
|
|
|
rendering
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const specialRendering of specialRenderings) {
|
|
|
|
if (typeof specialRendering === "string") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
const action = <AutoAction>specialRendering.func
|
2025-06-04 00:21:28 +02:00
|
|
|
await action.applyActionOn(feature, state, featureTags, specialRendering.args)
|
2025-06-02 17:03:07 +02:00
|
|
|
}
|
|
|
|
appliedOn.push(targetFeatureId)
|
|
|
|
if (i % 50 === 0) {
|
|
|
|
await state.changes.flushChanges("Auto button: intermediate save")
|
|
|
|
}
|
|
|
|
appliedNumberOfFeatures.setData(i + 1)
|
|
|
|
}
|
|
|
|
console.log("Flushing changes...")
|
|
|
|
await state.changes.flushChanges("Auto button: done")
|
|
|
|
buttonState.setData("done")
|
|
|
|
console.log(
|
|
|
|
"Applied changes onto",
|
|
|
|
appliedOn.length,
|
|
|
|
"items, unique IDs:",
|
|
|
|
new Set(appliedOn).size
|
|
|
|
)
|
|
|
|
} catch (e) {
|
|
|
|
console.error("Error while running autoApply: ", e)
|
|
|
|
buttonState.setData({ error: e })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const t = Translations.t.general.add.import
|
|
|
|
</script>
|
|
|
|
|
|
|
|
{#if !state.theme.official && !state.featureSwitchIsTesting.data}
|
2025-06-04 00:21:28 +02:00
|
|
|
<div class="alert">
|
|
|
|
The auto-apply button is only available in official themes (or in testing mode)
|
|
|
|
</div>
|
2025-06-02 17:03:07 +02:00
|
|
|
<Tr t={t.howToTest} />
|
|
|
|
{:else if ids === undefined}
|
|
|
|
<Loading>Gathering which elements support auto-apply...</Loading>
|
|
|
|
{:else if tagRenderingConfig === undefined}
|
|
|
|
<div class="alert">Target tagrendering {options.targetTagRendering} not found"</div>
|
|
|
|
{:else if $ids.length === 0}
|
|
|
|
<div>No elements found to perform action</div>
|
|
|
|
{:else if $buttonState.error !== undefined}
|
|
|
|
<div class="flex flex-col">
|
|
|
|
<div class="alert">Something went wrong</div>
|
|
|
|
<div>{$buttonState.error}</div>
|
|
|
|
</div>
|
|
|
|
{:else if $buttonState === "done"}
|
|
|
|
<div class="thanks">All done!</div>
|
|
|
|
{:else if $buttonState === "running"}
|
|
|
|
<Loading>
|
|
|
|
Applying changes, currently at {$appliedNumberOfFeatures} / {$ids.length}
|
|
|
|
</Loading>
|
|
|
|
{:else if $buttonState === "idle"}
|
|
|
|
<div class="flex flex-col">
|
2025-06-04 00:21:28 +02:00
|
|
|
<button
|
|
|
|
on:click={() => {
|
|
|
|
applyAllChanges()
|
|
|
|
}}
|
|
|
|
>
|
2025-06-02 17:03:07 +02:00
|
|
|
<img class="h-8 w-8" alt="" src={options.icon} />
|
|
|
|
{options.text}
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<div class="h-48 w-full">
|
|
|
|
<MaplibreMap mapProperties={mla} map={mlmap} />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="subtle link-underline">
|
|
|
|
The following objects will be updated:
|
|
|
|
<div class="flex flex-wrap gap-x-2">
|
|
|
|
{#each $ids as featId}
|
|
|
|
{#if layer.layerDef.source.geojsonSource === undefined}
|
|
|
|
<a href={"https://openstreetmap.org/" + featId} target="_blank">{featId}</a>
|
|
|
|
{:else}
|
|
|
|
<div>
|
|
|
|
{featId}
|
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{:else}
|
|
|
|
<div>Not supposed to show this... AutoApplyButton has invalid buttonstate: {$buttonState}</div>
|
|
|
|
{/if}
|