forked from MapComplete/MapComplete
		
	
		
			
	
	
		
			140 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			140 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 
								 | 
							
								import FloorLevelInputElement from "../Input/FloorLevelInputElement";
							 | 
						||
| 
								 | 
							
								import MapState, {GlobalFilter} from "../../Logic/State/MapState";
							 | 
						||
| 
								 | 
							
								import {TagsFilter} from "../../Logic/Tags/TagsFilter";
							 | 
						||
| 
								 | 
							
								import {RegexTag} from "../../Logic/Tags/RegexTag";
							 | 
						||
| 
								 | 
							
								import {Or} from "../../Logic/Tags/Or";
							 | 
						||
| 
								 | 
							
								import {Tag} from "../../Logic/Tags/Tag";
							 | 
						||
| 
								 | 
							
								import Translations from "../i18n/Translations";
							 | 
						||
| 
								 | 
							
								import Combine from "../Base/Combine";
							 | 
						||
| 
								 | 
							
								import {OsmFeature} from "../../Models/OsmFeature";
							 | 
						||
| 
								 | 
							
								import {BBox} from "../../Logic/BBox";
							 | 
						||
| 
								 | 
							
								import {TagUtils} from "../../Logic/Tags/TagUtils";
							 | 
						||
| 
								 | 
							
								import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline";
							 | 
						||
| 
								 | 
							
								import {Store} from "../../Logic/UIEventSource";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/***
							 | 
						||
| 
								 | 
							
								 * The element responsible for the level input element and picking the right level, showing and hiding at the right time, ...
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								export default class LevelSelector extends Combine {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    constructor(state: MapState & { featurePipeline: FeaturePipeline }) {
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        const levelsInView : Store< Record<string, number>> = state.currentBounds.map(bbox => {
							 | 
						||
| 
								 | 
							
								            if (bbox === undefined) {
							 | 
						||
| 
								 | 
							
								                return {}
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            const allElementsUnfiltered: OsmFeature[] = [].concat(...state.featurePipeline.GetAllFeaturesAndMetaWithin(bbox).map(ff => ff.features))
							 | 
						||
| 
								 | 
							
								            const allElements = allElementsUnfiltered.filter(f => BBox.get(f).overlapsWith(bbox))
							 | 
						||
| 
								 | 
							
								            const allLevelsRaw: string[] = allElements.map(f => f.properties["level"])
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            const levels : Record<string, number> = {"0": 0}
							 | 
						||
| 
								 | 
							
								            for (const levelDescription of allLevelsRaw) {
							 | 
						||
| 
								 | 
							
								                if(levelDescription === undefined){
							 | 
						||
| 
								 | 
							
								                    levels["0"] ++
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                for (const level of TagUtils.LevelsParser(levelDescription)) {
							 | 
						||
| 
								 | 
							
								                    levels[level] = (levels[level] ?? 0) + 1
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return levels
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const levelSelect = new FloorLevelInputElement(levelsInView)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        state.globalFilters.data.push({
							 | 
						||
| 
								 | 
							
								            filter: {
							 | 
						||
| 
								 | 
							
								                currentFilter: undefined,
							 | 
						||
| 
								 | 
							
								                state: undefined,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            id: "level",
							 | 
						||
| 
								 | 
							
								            onNewPoint: undefined
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								        const isShown = levelsInView.map(levelsInView => {
							 | 
						||
| 
								 | 
							
								                if (state.locationControl.data.zoom <= 16) {
							 | 
						||
| 
								 | 
							
								                    return false;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                if (Object.keys(levelsInView).length == 1) {
							 | 
						||
| 
								 | 
							
								                    return false;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								     
							 | 
						||
| 
								 | 
							
								                return true;
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            [state.locationControl])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        function setLevelFilter() {
							 | 
						||
| 
								 | 
							
								            console.log("Updating levels filter to ", levelSelect.GetValue().data, " is shown:", isShown.data)
							 | 
						||
| 
								 | 
							
								            const filter: GlobalFilter = state.globalFilters.data.find(gf => gf.id === "level")
							 | 
						||
| 
								 | 
							
								            if (!isShown.data) {
							 | 
						||
| 
								 | 
							
								                filter.filter = {
							 | 
						||
| 
								 | 
							
								                    state: "*",
							 | 
						||
| 
								 | 
							
								                    currentFilter: undefined,
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                filter.onNewPoint = undefined
							 | 
						||
| 
								 | 
							
								                state.globalFilters.ping();
							 | 
						||
| 
								 | 
							
								                return
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            const l = levelSelect.GetValue().data
							 | 
						||
| 
								 | 
							
								            if(l === undefined){
							 | 
						||
| 
								 | 
							
								                return
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            let neededLevel: TagsFilter = new RegexTag("level", new RegExp("(^|;)" + l + "(;|$)"));
							 | 
						||
| 
								 | 
							
								            if (l === "0") {
							 | 
						||
| 
								 | 
							
								                neededLevel = new Or([neededLevel, new Tag("level", "")])
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            filter.filter = {
							 | 
						||
| 
								 | 
							
								                state: l,
							 | 
						||
| 
								 | 
							
								                currentFilter: neededLevel
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            const t = Translations.t.general.levelSelection
							 | 
						||
| 
								 | 
							
								            filter.onNewPoint = {
							 | 
						||
| 
								 | 
							
								                confirmAddNew: t.confirmLevel.PartialSubs({level: l}),
							 | 
						||
| 
								 | 
							
								                safetyCheck: t.addNewOnLevel.Subs({level: l}),
							 | 
						||
| 
								 | 
							
								                tags: [new Tag("level", l)]
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            state.globalFilters.ping();
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        isShown.addCallbackAndRun(shown => {
							 | 
						||
| 
								 | 
							
								            console.log("Is level selector shown?", shown)
							 | 
						||
| 
								 | 
							
								            setLevelFilter()
							 | 
						||
| 
								 | 
							
								            if (shown) {
							 | 
						||
| 
								 | 
							
								                levelSelect.RemoveClass("invisible")
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                levelSelect.SetClass("invisible")
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        levelsInView.addCallbackAndRun(levels => {
							 | 
						||
| 
								 | 
							
								            if(!isShown.data){
							 | 
						||
| 
								 | 
							
								                return
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            const value = levelSelect.GetValue()
							 | 
						||
| 
								 | 
							
								            if (!(levels[value.data] === undefined || levels[value.data] === 0)) {
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // Nothing in view. Lets switch to a different level (the level with the most features)
							 | 
						||
| 
								 | 
							
								            let mostElements = 0
							 | 
						||
| 
								 | 
							
								            let mostElementsLevel = undefined
							 | 
						||
| 
								 | 
							
								            for (const level in levels) {
							 | 
						||
| 
								 | 
							
								                const count = levels[level]
							 | 
						||
| 
								 | 
							
								                if(mostElementsLevel === undefined || mostElements < count){
							 | 
						||
| 
								 | 
							
								                    mostElementsLevel = level
							 | 
						||
| 
								 | 
							
								                    mostElements = count
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            console.log("Force switching to a different level:", mostElementsLevel,"as it has",mostElements,"elements on that floor",levels,"(old level: "+value.data+")")
							 | 
						||
| 
								 | 
							
								            value.setData(mostElementsLevel )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								        levelSelect.GetValue().addCallback(_ => setLevelFilter())
							 | 
						||
| 
								 | 
							
								        super([levelSelect])
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 |