MapComplete/src/UI/Popup/AutoApplyButton.svelte

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

166 lines
5.9 KiB
Svelte
Raw Normal View History

<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-04 00:21:28 +02:00
let buttonState: UIEventSource<"idle" | "running" | "done" | { error: string }> =
new UIEventSource<"idle" | "running" | "done" | { error: string }>("idle")
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,
})
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))
)
new ShowDataLayer(mlmap, {
features: StaticFeatureSource.fromGeojson(features),
zoomToFeatures: true,
2025-06-04 00:21:28 +02:00
layer: layer.layerDef,
})
2025-06-04 00:21:28 +02:00
features.addCallbackAndRunD((f) => console.log("Features are now", f))
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
const specialRenderings = Lists.noNull(SpecialVisualizations.constructSpecification(rendering)).filter((v) => typeof v !== "string" && v.func["supportsAutoAction"] === true)
if (specialRenderings.length == 0) {
console.warn(
"AutoApply: feature " +
2025-06-04 00:21:28 +02:00
targetFeatureId +
" got a rendering without supported auto actions:",
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)
}
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>
<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()
}}
>
<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}