forked from MapComplete/MapComplete
		
	Add level selector and global filters
This commit is contained in:
		
							parent
							
								
									5504d49d59
								
							
						
					
					
						commit
						7fd7a3722e
					
				
					 19 changed files with 401 additions and 253 deletions
				
			
		|  | @ -1,8 +1,7 @@ | |||
| import { FeatureSource, FeatureSourceForLayer } from "./FeatureSource" | ||||
| import { FeatureSource, IndexedFeatureSource } from "./FeatureSource" | ||||
| import FilteredLayer from "../../Models/FilteredLayer" | ||||
| import SimpleFeatureSource from "./Sources/SimpleFeatureSource" | ||||
| import { Feature } from "geojson" | ||||
| import { Utils } from "../../Utils" | ||||
| import { UIEventSource } from "../UIEventSource" | ||||
| 
 | ||||
| /** | ||||
|  | @ -10,9 +9,7 @@ import { UIEventSource } from "../UIEventSource" | |||
|  * If this is the case, multiple objects with a different _matching_layer_id are generated. | ||||
|  * In any case, this featureSource marks the objects with _matching_layer_id | ||||
|  */ | ||||
| export default class PerLayerFeatureSourceSplitter< | ||||
|     T extends FeatureSourceForLayer = SimpleFeatureSource | ||||
| > { | ||||
| export default class PerLayerFeatureSourceSplitter<T extends FeatureSource = FeatureSource> { | ||||
|     public readonly perLayer: ReadonlyMap<string, T> | ||||
|     constructor( | ||||
|         layers: FilteredLayer[], | ||||
|  | @ -23,6 +20,11 @@ export default class PerLayerFeatureSourceSplitter< | |||
|         } | ||||
|     ) { | ||||
|         const knownLayers = new Map<string, T>() | ||||
|         /** | ||||
|          * Keeps track of the ids that are included per layer. | ||||
|          * Used to know if the downstream feature source needs to be pinged | ||||
|          */ | ||||
|         let layerIndexes: ReadonlySet<string>[] = layers.map((_) => new Set<string>()) | ||||
|         this.perLayer = knownLayers | ||||
|         const layerSources = new Map<string, UIEventSource<Feature[]>>() | ||||
|         const constructStore = | ||||
|  | @ -41,6 +43,12 @@ export default class PerLayerFeatureSourceSplitter< | |||
|             // We try to figure out (for each feature) in which feature store it should be saved.
 | ||||
| 
 | ||||
|             const featuresPerLayer = new Map<string, Feature[]>() | ||||
|             /** | ||||
|              * Indexed on layer-position | ||||
|              * Will be true if a new id pops up | ||||
|              */ | ||||
|             const hasChanged: boolean[] = layers.map((_) => false) | ||||
|             const newIndices: Set<string>[] = layers.map((_) => new Set<string>()) | ||||
|             const noLayerFound: Feature[] = [] | ||||
| 
 | ||||
|             for (const layer of layers) { | ||||
|  | @ -49,9 +57,14 @@ export default class PerLayerFeatureSourceSplitter< | |||
| 
 | ||||
|             for (const f of features) { | ||||
|                 let foundALayer = false | ||||
|                 for (const layer of layers) { | ||||
|                 for (let i = 0; i < layers.length; i++) { | ||||
|                     const layer = layers[i] | ||||
|                     if (layer.layerDef.source.osmTags.matchesProperties(f.properties)) { | ||||
|                         const id = f.properties.id | ||||
|                         // We have found our matching layer!
 | ||||
|                         const previousIndex = layerIndexes[i] | ||||
|                         hasChanged[i] = hasChanged[i] || !previousIndex.has(id) | ||||
|                         newIndices[i].add(id) | ||||
|                         featuresPerLayer.get(layer.layerDef.id).push(f) | ||||
|                         foundALayer = true | ||||
|                         if (!layer.layerDef.passAllFeatures) { | ||||
|  | @ -67,7 +80,8 @@ export default class PerLayerFeatureSourceSplitter< | |||
| 
 | ||||
|             // At this point, we have our features per layer as a list
 | ||||
|             // We assign them to the correct featureSources
 | ||||
|             for (const layer of layers) { | ||||
|             for (let i = 0; i < layers.length; i++) { | ||||
|                 const layer = layers[i] | ||||
|                 const id = layer.layerDef.id | ||||
|                 const features = featuresPerLayer.get(id) | ||||
|                 if (features === undefined) { | ||||
|  | @ -75,14 +89,17 @@ export default class PerLayerFeatureSourceSplitter< | |||
|                     continue | ||||
|                 } | ||||
| 
 | ||||
|                 const src = layerSources.get(id) | ||||
| 
 | ||||
|                 if (Utils.sameList(src.data, features)) { | ||||
|                 if (!hasChanged[i] && layerIndexes[i].size === newIndices[i].size) { | ||||
|                     // No new id has been added and the sizes are the same (thus: nothing has been removed as well)
 | ||||
|                     // We can safely assume that no changes were made
 | ||||
|                     continue | ||||
|                 } | ||||
|                 src.setData(features) | ||||
| 
 | ||||
|                 layerSources.get(id).setData(features) | ||||
|             } | ||||
| 
 | ||||
|             layerIndexes = newIndices | ||||
| 
 | ||||
|             // AT last, the leftovers are handled
 | ||||
|             if (options?.handleLeftovers !== undefined && noLayerFound.length > 0) { | ||||
|                 options.handleLeftovers(noLayerFound) | ||||
|  | @ -90,7 +107,7 @@ export default class PerLayerFeatureSourceSplitter< | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     public forEach(f: (featureSource: FeatureSourceForLayer) => void) { | ||||
|     public forEach(f: (featureSource: FeatureSource) => void) { | ||||
|         for (const fs of this.perLayer.values()) { | ||||
|             f(fs) | ||||
|         } | ||||
|  |  | |||
|  | @ -122,7 +122,7 @@ export default class OsmFeatureSource extends FeatureSourceMerger { | |||
|             throw "This is an absurd high zoom level" | ||||
|         } | ||||
| 
 | ||||
|         if (z < 14) { | ||||
|         if (z < 15) { | ||||
|             throw `Zoom ${z} is too much for OSM to handle! Use a higher zoom level!` | ||||
|         } | ||||
|         const index = Tiles.tile_index(z, x, y) | ||||
|  |  | |||
|  | @ -35,7 +35,18 @@ export default class MetaTagging { | |||
|                 continue | ||||
|             } | ||||
|             const featureSource = state.perLayer.get(layer.id) | ||||
|             featureSource.features?.addCallbackAndRunD((features) => { | ||||
|             featureSource.features?.stabilized(1000)?.addCallbackAndRunD((features) => { | ||||
|                 if (!(features?.length > 0)) { | ||||
|                     // No features to handle
 | ||||
|                     return | ||||
|                 } | ||||
|                 console.trace( | ||||
|                     "Recalculating metatags for layer ", | ||||
|                     layer.id, | ||||
|                     "due to a change in the upstream features. Contains ", | ||||
|                     features.length, | ||||
|                     "items" | ||||
|                 ) | ||||
|                 MetaTagging.addMetatags( | ||||
|                     features, | ||||
|                     params, | ||||
|  | @ -71,7 +82,6 @@ export default class MetaTagging { | |||
|             return | ||||
|         } | ||||
| 
 | ||||
|         console.debug("Recalculating metatags...") | ||||
|         const metatagsToApply: SimpleMetaTagger[] = [] | ||||
|         for (const metatag of SimpleMetaTaggers.metatags) { | ||||
|             if (metatag.includesDates) { | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ import { GlobalFilter } from "../../Models/GlobalFilter" | |||
| import FilteredLayer from "../../Models/FilteredLayer" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import { OsmConnection } from "../Osm/OsmConnection" | ||||
| import { Tag } from "../Tags/Tag" | ||||
| import Translations from "../../UI/i18n/Translations" | ||||
| 
 | ||||
| /** | ||||
|  * The layer state keeps track of: | ||||
|  | @ -41,6 +43,45 @@ export default class LayerState { | |||
|         } | ||||
|         this.filteredLayers = filteredLayers | ||||
|         layers.forEach((l) => LayerState.linkFilterStates(l, filteredLayers)) | ||||
| 
 | ||||
|         this.globalFilters.data.push({ | ||||
|             id: "level", | ||||
|             osmTags: undefined, | ||||
|             state: undefined, | ||||
|             onNewPoint: undefined, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the global filter which looks to the 'level'-tag. | ||||
|      * Only features with the given 'level' will be shown. | ||||
|      * | ||||
|      * If undefined is passed, _all_ levels will be shown | ||||
|      * @param level | ||||
|      */ | ||||
|     public setLevelFilter(level?: string) { | ||||
|         // Remove all previous
 | ||||
|         const l = this.globalFilters.data.length | ||||
|         this.globalFilters.data = this.globalFilters.data.filter((f) => f.id !== "level") | ||||
|         if (!level) { | ||||
|             if (l !== this.globalFilters.data.length) { | ||||
|                 this.globalFilters.ping() | ||||
|             } | ||||
|             return | ||||
|         } | ||||
|         const t = Translations.t.general.levelSelection | ||||
|         this.globalFilters.data.push({ | ||||
|             id: "level", | ||||
|             state: level, | ||||
|             osmTags: new Tag("level", level), | ||||
|             onNewPoint: { | ||||
|                 tags: [new Tag("level", level)], | ||||
|                 icon: "./assets/svg/elevator.svg", | ||||
|                 confirmAddNew: t.confirmLevel.PartialSubs({ level }), | ||||
|                 safetyCheck: t.addNewOnLevel.Subs({ level }), | ||||
|             }, | ||||
|         }) | ||||
|         this.globalFilters.ping() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue