forked from MapComplete/MapComplete
		
	Fix import flow, add typing
This commit is contained in:
		
							parent
							
								
									97334fc369
								
							
						
					
					
						commit
						4246221e8e
					
				
					 29 changed files with 396 additions and 309 deletions
				
			
		|  | @ -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) | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue