Fix import flow, add typing

This commit is contained in:
Pieter Vander Vennet 2022-07-08 03:14:55 +02:00
parent 97334fc369
commit 4246221e8e
29 changed files with 396 additions and 309 deletions

View file

@ -22,12 +22,17 @@ import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource";
import ValidatedTextField from "../Input/ValidatedTextField";
import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource";
import * as currentview from "../../assets/layers/current_view/current_view.json"
import * as import_candidate from "../../assets/layers/import_candidate/import_candidate.json"
import {GeoOperations} from "../../Logic/GeoOperations";
import FeatureInfoBox from "../Popup/FeatureInfoBox";
import {ImportUtils} from "./ImportUtils";
import Translations from "../i18n/Translations";
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer";
import FilteredLayer, {FilterState} from "../../Models/FilteredLayer";
import {Feature, FeatureCollection} from "@turf/turf";
import * as currentview from "../../assets/layers/current_view/current_view.json"
import {CheckBox} from "../Input/Checkboxes";
import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch";
/**
* Given the data to import, the bbox and the layer, will query overpass for similar items
@ -36,20 +41,21 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
public readonly IsValid
public readonly Value: Store<{ features: any[], theme: string }>
constructor(
state,
params: { bbox: BBox, layer: LayerConfig, theme: string, features: any[] }) {
const t = Translations.t.importHelper.conflationChecker
const bbox = params.bbox.padAbsolute(0.0001)
const layer = params.layer;
const toImport: {features: any[]} = params;
const toImport: { features: any[] } = params;
let overpassStatus = new UIEventSource<{ error: string } | "running" | "success" | "idle" | "cached">("idle")
const cacheAge = new UIEventSource<number>(undefined);
function loadDataFromOverpass(){
function loadDataFromOverpass() {
// Load the data!
const url = Constants.defaultOverpassUrls[1]
const relationTracker = new RelationsTracker()
@ -66,42 +72,49 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
overpassStatus.setData({error})
})
}
const fromLocalStorage = IdbLocalStorage.Get<[any, Date]>("importer-overpass-cache-" + layer.id, {
whenLoaded: (v) => {
if (v !== undefined && v !== null) {
console.log("Loaded from local storage:", v)
const [geojson, date] = v;
const timeDiff = (new Date().getTime() - date.getTime()) / 1000;
console.log("Loaded ", geojson.features.length, " features; cache is ", timeDiff, "seconds old")
cacheAge.setData(timeDiff)
if (timeDiff < 24 * 60 * 60) {
// Recently cached!
overpassStatus.setData("cached")
return;
}
cacheAge.setData(-1)
overpassStatus.setData("cached")
}
loadDataFromOverpass()
}
});
const cacheAge = fromLocalStorage.map(d => {
if(d === undefined || d[1] === undefined){
return undefined
}
const [_, loadedDate] = d
return (new Date().getTime() - loadedDate.getTime()) / 1000;
})
cacheAge.addCallbackD(timeDiff => {
if (timeDiff < 24 * 60 * 60) {
// Recently cached!
overpassStatus.setData("cached")
return;
} else {
loadDataFromOverpass()
}
})
const geojson: Store<any> = fromLocalStorage.map(d => {
const geojson: Store<FeatureCollection> = fromLocalStorage.map(d => {
if (d === undefined) {
return undefined
}
return d[0]
})
const background = new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
const location = new UIEventSource<Loc>({lat: 0, lon: 0, zoom: 1})
const currentBounds = new UIEventSource<BBox>(undefined)
const zoomLevel = ValidatedTextField.ForType("pnat").ConstructInputElement()
const zoomLevel = ValidatedTextField.ForType("pnat").ConstructInputElement({
value: LocalStorageSource.GetParsed<string>("importer-zoom-level", "0")
})
zoomLevel.SetClass("ml-1 border border-black")
zoomLevel.GetValue().syncWith(LocalStorageSource.Get("importer-zoom-level", "14"), true)
const osmLiveData = Minimap.createMiniMap({
allowMoving: true,
location,
@ -110,18 +123,24 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
attribution: new Attribution(location, state.osmConnection.userDetails, undefined, currentBounds)
})
osmLiveData.SetClass("w-full").SetStyle("height: 500px")
const preview = new StaticFeatureSource(geojson.map(geojson => {
const geojsonFeatures : Store<Feature[]> = geojson.map(geojson => {
if (geojson?.features === undefined) {
return []
}
const zoomedEnough: boolean = osmLiveData.location.data.zoom >= Number(zoomLevel.GetValue().data)
if (!zoomedEnough) {
const currentZoom = zoomLevel.GetValue().data
const zoomedEnough: boolean = osmLiveData.location.data.zoom >= Number(currentZoom)
if (currentZoom !== undefined && !zoomedEnough) {
return []
}
const bounds = osmLiveData.bounds.data
if(bounds === undefined){
return geojson.features;
}
return geojson.features.filter(f => BBox.get(f).overlapsWith(bounds))
}, [osmLiveData.bounds, zoomLevel.GetValue()]));
}, [osmLiveData.bounds, zoomLevel.GetValue()])
const preview = StaticFeatureSource.fromGeojsonStore(geojsonFeatures)
new ShowDataLayer({
layerToShow: new LayerConfig(currentview),
@ -134,12 +153,16 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
])
})
new ShowDataLayer({
layerToShow: layer,
new ShowDataMultiLayer({
//layerToShow: layer,
layers: new UIEventSource<FilteredLayer[]>([{
layerDef: layer,
isDisplayed: new UIEventSource<boolean>(true),
appliedFilters: new UIEventSource<Map<string, FilterState>>(undefined)
}]),
state,
leafletMap: osmLiveData.leafletMap,
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, {setHash: false}),
zoomToFeatures: false,
features: preview
})
@ -148,7 +171,7 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
layerToShow: new LayerConfig(import_candidate),
state,
leafletMap: osmLiveData.leafletMap,
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, {setHash: false}),
zoomToFeatures: false,
features: StaticFeatureSource.fromGeojson(toImport.features)
})
@ -164,7 +187,7 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
matchedFeaturesMap.SetClass("w-full").SetStyle("height: 500px")
// Featuresource showing OSM-features which are nearby a toImport-feature
const nearbyFeatures = new StaticFeatureSource(geojson.map(osmData => {
const geojsonMapped: Store<Feature[]> = geojson.map(osmData => {
if (osmData?.features === undefined) {
return []
}
@ -172,32 +195,37 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
return osmData.features.filter(f =>
toImport.features.some(imp =>
maxDist >= GeoOperations.distanceBetween(imp.geometry.coordinates, GeoOperations.centerpointCoordinates(f))))
}, [nearbyCutoff.GetValue().stabilized(500)]));
}, [nearbyCutoff.GetValue().stabilized(500)])
const nearbyFeatures = StaticFeatureSource.fromGeojsonStore(geojsonMapped);
const paritionedImport = ImportUtils.partitionFeaturesIfNearby(toImport, geojson, nearbyCutoff.GetValue().map(Number));
// Featuresource showing OSM-features which are nearby a toImport-feature
const toImportWithNearby = new StaticFeatureSource(paritionedImport.map(els => els?.hasNearby ?? []));
new ShowDataLayer({
layerToShow: layer,
state,
leafletMap: matchedFeaturesMap.leafletMap,
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
zoomToFeatures: true,
features: nearbyFeatures
})
const toImportWithNearby = StaticFeatureSource.fromGeojsonStore(paritionedImport.map(els => els?.hasNearby ?? []));
toImportWithNearby.features.addCallback(nearby => console.log("The following features are near an already existing object:", nearby))
new ShowDataLayer({
layerToShow: new LayerConfig(import_candidate),
state,
leafletMap: matchedFeaturesMap.leafletMap,
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, {setHash: false}),
zoomToFeatures: false,
features: toImportWithNearby
})
const showOsmLayer = new CheckBox(t.showOsmLayerInConflationMap, true)
new ShowDataLayer({
layerToShow: layer,
state,
leafletMap: matchedFeaturesMap.leafletMap,
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, {setHash: false}),
zoomToFeatures: true,
features: nearbyFeatures,
doShowLayer: showOsmLayer.GetValue()
})
const t = Translations.t.importHelper.conflationChecker
const conflationMaps = new Combine([
new VariableUiElement(
geojson.map(geojson => {
@ -218,34 +246,44 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
return t.cacheExpired
}
return new Combine([t.loadedDataAge.Subs({age: Utils.toHumanTime(age)}),
new SubtleButton(Svg.reload_svg().SetClass("h-8"), t.reloadTheCache)
.onClick(loadDataFromOverpass)
.SetClass("h-12")
new SubtleButton(Svg.reload_svg().SetClass("h-8"), t.reloadTheCache)
.onClick(loadDataFromOverpass)
.SetClass("h-12")
])
})),
new Title(t.titleLive),
t.importCandidatesCount.Subs({count:toImport.features.length }),
new VariableUiElement(geojson.map(geojson => {
if(geojson?.features?.length === undefined || geojson?.features?.length === 0){
t.importCandidatesCount.Subs({count: toImport.features.length}),
new VariableUiElement(geojson.map(geojson => {
if (geojson?.features?.length === undefined || geojson?.features?.length === 0) {
return t.nothingLoaded.Subs(layer).SetClass("alert")
}
return new Combine([
}
return new Combine([
t.osmLoaded.Subs({count: geojson.features.length, name: layer.name}),
])
})),
])
})),
osmLiveData,
new VariableUiElement(osmLiveData.location.map(location => {
return t.zoomIn.Subs({needed:zoomLevel, current: location.zoom })
} )),
new Combine([
t.zoomLevelSelection,
zoomLevel,
new VariableUiElement(osmLiveData.location.map(location => {
return t.zoomIn.Subs(<any>{current: location.zoom})
})),
]).SetClass("flex"),
new Title(t.titleNearby),
new Combine([t.mapShowingNearbyIntro, nearbyCutoff]).SetClass("flex"),
new VariableUiElement(toImportWithNearby.features.map(feats =>
new VariableUiElement(toImportWithNearby.features.map(feats =>
t.nearbyWarn.Subs({count: feats.length}).SetClass("alert"))),
t.setRangeToZero,
matchedFeaturesMap]).SetClass("flex flex-col")
matchedFeaturesMap,
new Combine([
new BackgroundMapSwitch({backgroundLayer: background, locationControl: matchedFeaturesMap.location}, background),
showOsmLayer,
]).SetClass("flex")
]).SetClass("flex flex-col")
super([
new Title(t.title),
new VariableUiElement(overpassStatus.map(d => {
@ -270,7 +308,11 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
])
this.Value = paritionedImport.map(feats => ({theme: params.theme, features: feats?.noNearby, layer: params.layer}))
this.Value = paritionedImport.map(feats => ({
theme: params.theme,
features: feats?.noNearby,
layer: params.layer
}))
this.IsValid = this.Value.map(v => v?.features !== undefined && v.features.length > 0)
}