mapcomplete/UI/ImportFlow/CompareToAlreadyExistingNotes.ts

171 lines
No EOL
7.4 KiB
TypeScript

import Combine from "../Base/Combine";
import {FlowStep} from "./FlowStep";
import {BBox} from "../../Logic/BBox";
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
import {Store, UIEventSource} from "../../Logic/UIEventSource";
import CreateNoteImportLayer from "../../Models/ThemeConfig/Conversion/CreateNoteImportLayer";
import FilteredLayer, {FilterState} from "../../Models/FilteredLayer";
import GeoJsonSource from "../../Logic/FeatureSource/Sources/GeoJsonSource";
import MetaTagging from "../../Logic/MetaTagging";
import RelationsTracker from "../../Logic/Osm/RelationsTracker";
import FilteringFeatureSource from "../../Logic/FeatureSource/Sources/FilteringFeatureSource";
import Minimap from "../Base/Minimap";
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
import FeatureInfoBox from "../Popup/FeatureInfoBox";
import {ImportUtils} from "./ImportUtils";
import * as import_candidate from "../../assets/layers/import_candidate/import_candidate.json";
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource";
import Title from "../Base/Title";
import Loading from "../Base/Loading";
import {VariableUiElement} from "../Base/VariableUIElement";
import * as known_layers from "../../assets/generated/known_layers.json"
import {LayerConfigJson} from "../../Models/ThemeConfig/Json/LayerConfigJson";
import Translations from "../i18n/Translations";
/**
* Filters out points for which the import-note already exists, to prevent duplicates
*/
export class CompareToAlreadyExistingNotes extends Combine implements FlowStep<{ bbox: BBox, layer: LayerConfig, features: any[], theme: string }> {
public IsValid: Store<boolean>
public Value: Store<{ bbox: BBox, layer: LayerConfig, features: any[], theme: string }>
constructor(state, params: { bbox: BBox, layer: LayerConfig, features: any[], theme: string }) {
const t = Translations.t.importHelper.compareToAlreadyExistingNotes
const layerConfig = known_layers.layers.filter(l => l.id === params.layer.id)[0]
if (layerConfig === undefined) {
console.error("WEIRD: layer not found in the builtin layer overview")
}
const importLayerJson = new CreateNoteImportLayer(150).convertStrict(<LayerConfigJson>layerConfig, "CompareToAlreadyExistingNotes")
const importLayer = new LayerConfig(importLayerJson, "import-layer-dynamic")
const flayer: FilteredLayer = {
appliedFilters: new UIEventSource<Map<string, FilterState>>(new Map<string, FilterState>()),
isDisplayed: new UIEventSource<boolean>(true),
layerDef: importLayer
}
const allNotesWithinBbox = new GeoJsonSource(flayer, params.bbox.padAbsolute(0.0001))
allNotesWithinBbox.features.map(f => MetaTagging.addMetatags(
f,
{
memberships: new RelationsTracker(),
getFeaturesWithin: () => [],
getFeatureById: () => undefined
},
importLayer,
state,
{
includeDates: true,
// We assume that the non-dated metatags are already set by the cache generator
includeNonDates: true
}
)
)
const alreadyOpenImportNotes = new FilteringFeatureSource(state, undefined, allNotesWithinBbox)
const map = Minimap.createMiniMap()
map.SetClass("w-full").SetStyle("height: 500px")
const comparisonMap = Minimap.createMiniMap({
location: map.location,
})
comparisonMap.SetClass("w-full").SetStyle("height: 500px")
new ShowDataLayer({
layerToShow: importLayer,
state,
zoomToFeatures: true,
leafletMap: map.leafletMap,
features: alreadyOpenImportNotes,
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state)
})
const maxDistance = new UIEventSource<number>(10)
const partitionedImportPoints = ImportUtils.partitionFeaturesIfNearby(params, alreadyOpenImportNotes.features
.map(ff => ({features: ff.map(ff => ff.feature)})), maxDistance)
new ShowDataLayer({
layerToShow: new LayerConfig(import_candidate),
state,
zoomToFeatures: true,
leafletMap: comparisonMap.leafletMap,
features: StaticFeatureSource.fromGeojsonStore(partitionedImportPoints.map(p => p.hasNearby)),
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state)
})
super([
new Title(t.titleLong),
new VariableUiElement(
alreadyOpenImportNotes.features.map(notesWithImport => {
if (allNotesWithinBbox.state.data !== undefined && allNotesWithinBbox.state.data["error"] !== undefined) {
const error = allNotesWithinBbox.state.data["error"]
t.loadingFailed.Subs({error})
}
if (allNotesWithinBbox.features.data === undefined || allNotesWithinBbox.features.data.length === 0) {
return new Loading(t.loading)
}
if (notesWithImport.length === 0) {
return t.noPreviousNotesFound.SetClass("thanks")
}
return new Combine([
t.mapExplanation.Subs(params.features),
map,
new VariableUiElement(partitionedImportPoints.map(({noNearby, hasNearby}) => {
if (noNearby.length === 0) {
// Nothing can be imported
return t.completelyImported.SetClass("alert w-full block").SetStyle("padding: 0.5rem")
}
if (hasNearby.length === 0) {
// All points can be imported
return t.nothingNearby.SetClass("thanks w-full block").SetStyle("padding: 0.5rem")
}
return new Combine([
t.someNearby.Subs({
hasNearby: hasNearby.length,
distance: maxDistance.data
}).SetClass("alert"),
t.wontBeImported,
comparisonMap.SetClass("w-full")
]).SetClass("w-full")
}))
]).SetClass("flex flex-col")
}, [allNotesWithinBbox.features, allNotesWithinBbox.state])
),
]);
this.SetClass("flex flex-col")
this.Value = partitionedImportPoints.map(({noNearby}) => ({
features: noNearby,
bbox: params.bbox,
layer: params.layer,
theme: params.theme
}))
this.IsValid = alreadyOpenImportNotes.features.map(ff => {
if (allNotesWithinBbox.features.data.length === 0) {
// Not yet loaded
return false
}
if (ff.length == 0) {
// No import notes at all
return true;
}
return partitionedImportPoints.data.noNearby.length > 0; // at least _something_ can be imported
}, [partitionedImportPoints, allNotesWithinBbox.features])
}
}