forked from MapComplete/MapComplete
Refactoring: split 'ThemeViewState' into many classes
This commit is contained in:
parent
2b858bd2aa
commit
dbcbf2787d
34 changed files with 1503 additions and 1227 deletions
292
src/Models/ThemeViewState/UserMapFeatureswitchState.ts
Normal file
292
src/Models/ThemeViewState/UserMapFeatureswitchState.ts
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
import ThemeConfig from "../ThemeConfig/ThemeConfig"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { Map as MlMap } from "maplibre-gl"
|
||||
import { GeoLocationState } from "../../Logic/State/GeoLocationState"
|
||||
import InitialMapPositioning from "../../Logic/Actors/InitialMapPositioning"
|
||||
import { MapLibreAdaptor } from "../../UI/Map/MapLibreAdaptor"
|
||||
import { ExportableMap, MapProperties } from "../MapProperties"
|
||||
import { LastClickFeatureSource } from "../../Logic/FeatureSource/Sources/LastClickFeatureSource"
|
||||
import { PreferredRasterLayerSelector } from "../../Logic/Actors/PreferredRasterLayerSelector"
|
||||
import { AvailableRasterLayers, RasterLayerPolygon, RasterLayerUtils } from "../RasterLayers"
|
||||
import BackgroundLayerResetter from "../../Logic/Actors/BackgroundLayerResetter"
|
||||
import Hotkeys from "../../UI/Base/Hotkeys"
|
||||
import Translations from "../../UI/i18n/Translations"
|
||||
import { EliCategory } from "../RasterLayerProperties"
|
||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||
import { Feature, Point, Polygon } from "geojson"
|
||||
import { FeatureSource, WritableFeatureSource } from "../../Logic/FeatureSource/FeatureSource"
|
||||
import FullNodeDatabaseSource from "../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import { WithUserRelatedState } from "./WithUserRelatedState"
|
||||
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler"
|
||||
import { GeolocationControlState } from "../../UI/BigComponents/GeolocationControl"
|
||||
import ShowOverlayRasterLayer from "../../UI/Map/ShowOverlayRasterLayer"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import ShowDataLayer from "../../UI/Map/ShowDataLayer"
|
||||
|
||||
/**
|
||||
* The first core of the state management; everything related to:
|
||||
* - the OSM connection
|
||||
* - setting up the basemap
|
||||
* - the feature switches
|
||||
* - the GPS-location
|
||||
*
|
||||
* Anything that handles editable elements is _not_ done on this level.
|
||||
* Anything that handles the UI is not done on this level
|
||||
*/
|
||||
|
||||
export class UserMapFeatureswitchState extends WithUserRelatedState {
|
||||
|
||||
readonly map: UIEventSource<MlMap>
|
||||
|
||||
|
||||
readonly mapProperties: MapLibreAdaptor & MapProperties & ExportableMap
|
||||
readonly lastClickObject: LastClickFeatureSource
|
||||
|
||||
readonly geolocationState: GeoLocationState
|
||||
readonly geolocation: GeoLocationHandler
|
||||
readonly geolocationControl: GeolocationControlState
|
||||
readonly historicalUserLocations: WritableFeatureSource<Feature<Point>>
|
||||
|
||||
|
||||
readonly availableLayers: { store: Store<RasterLayerPolygon[]> }
|
||||
readonly currentView: FeatureSource<Feature<Polygon>>
|
||||
readonly fullNodeDatabase?: FullNodeDatabaseSource
|
||||
|
||||
|
||||
constructor(theme: ThemeConfig, selectedElement: Store<object>) {
|
||||
const rasterLayer: UIEventSource<RasterLayerPolygon> = new UIEventSource<RasterLayerPolygon>(undefined)
|
||||
super(theme, rasterLayer)
|
||||
this.geolocationState = new GeoLocationState()
|
||||
const initial = new InitialMapPositioning(theme, this.geolocationState, this.osmConnection)
|
||||
this.map = new UIEventSource<MlMap>(undefined)
|
||||
this.mapProperties = new MapLibreAdaptor(this.map, { rasterLayer, ...initial }, { correctClick: 20 })
|
||||
|
||||
this.geolocation = new GeoLocationHandler(
|
||||
this.geolocationState,
|
||||
selectedElement,
|
||||
this.mapProperties,
|
||||
this.userRelatedState.gpsLocationHistoryRetentionTime
|
||||
)
|
||||
this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties)
|
||||
this.historicalUserLocations = this.geolocation.historicalUserLocations
|
||||
|
||||
|
||||
this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => {
|
||||
this.mapProperties.allowRotating.setData(fixated !== "yes")
|
||||
})
|
||||
|
||||
this.availableLayers = AvailableRasterLayers.layersAvailableAt(
|
||||
this.mapProperties.location,
|
||||
this.osmConnection.isLoggedIn
|
||||
)
|
||||
|
||||
this.userRelatedState.markLayoutAsVisited(this.theme)
|
||||
|
||||
this.lastClickObject = new LastClickFeatureSource(
|
||||
this.theme,
|
||||
this.mapProperties.lastClickLocation,
|
||||
this.userRelatedState.addNewFeatureMode
|
||||
)
|
||||
{
|
||||
let currentViewIndex = 0
|
||||
const empty = []
|
||||
this.currentView = new StaticFeatureSource(
|
||||
this.mapProperties.bounds.map((bbox) => {
|
||||
if (!bbox) {
|
||||
return empty
|
||||
}
|
||||
currentViewIndex++
|
||||
return <Feature[]>[
|
||||
bbox.asGeoJson({
|
||||
zoom: this.mapProperties.zoom.data,
|
||||
...this.mapProperties.location.data,
|
||||
id: "current_view_" + currentViewIndex
|
||||
})
|
||||
]
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (this.theme.layers.some((l) => l._needsFullNodeDatabase)) {
|
||||
this.fullNodeDatabase = new FullNodeDatabaseSource()
|
||||
}
|
||||
|
||||
|
||||
///////// Actors ///////////////
|
||||
|
||||
new BackgroundLayerResetter(this.mapProperties.rasterLayer, this.availableLayers)
|
||||
|
||||
|
||||
this.userRelatedState.showScale.addCallbackAndRun((showScale) => {
|
||||
this.mapProperties.showScale.set(showScale)
|
||||
})
|
||||
new PreferredRasterLayerSelector(
|
||||
this.mapProperties.rasterLayer,
|
||||
this.availableLayers,
|
||||
this.featureSwitches.backgroundLayerId,
|
||||
this.userRelatedState.preferredBackgroundLayer
|
||||
)
|
||||
|
||||
|
||||
this.initHotkeys()
|
||||
this.drawOverlayLayers()
|
||||
this.drawLock()
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the map is locked to a certain area _and_ we are in test mode, draw this on the map
|
||||
* @private
|
||||
*/
|
||||
private drawLock() {
|
||||
if (!this.theme?.lockLocation) {
|
||||
return
|
||||
}
|
||||
const bbox = new BBox(<[[number, number], [number, number]]>this.theme.lockLocation)
|
||||
this.mapProperties.maxbounds.setData(bbox)
|
||||
ShowDataLayer.showRange(
|
||||
this.map,
|
||||
new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]),
|
||||
this.featureSwitches.featureSwitchIsTesting
|
||||
)
|
||||
}
|
||||
|
||||
private drawOverlayLayers() {
|
||||
for (const rasterInfo of this.theme.tileLayerSources) {
|
||||
const state = this.overlayLayerStates.get(rasterInfo.id)
|
||||
new ShowOverlayRasterLayer(rasterInfo, this.map, this.mapProperties, state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* By focussing on the map, the keyboard panning and zoom with '+' and '+' works */
|
||||
public focusOnMap() {
|
||||
if (this.map.data) {
|
||||
this.map.data.getCanvas().focus()
|
||||
return
|
||||
}
|
||||
this.map.addCallbackAndRunD((map) => {
|
||||
map.on("load", () => {
|
||||
map.getCanvas().focus()
|
||||
})
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
private initHotkeys() {
|
||||
const docs = Translations.t.hotkeyDocumentation
|
||||
|
||||
this.featureSwitches.featureSwitchBackgroundSelection.addCallbackAndRun((enable) => {
|
||||
if (!enable) {
|
||||
return
|
||||
}
|
||||
|
||||
const setLayerCategory = (category: EliCategory, skipLayers: number = 0) => {
|
||||
const timeOfCall = new Date()
|
||||
this.availableLayers.store.addCallbackAndRunD((available) => {
|
||||
const now = new Date()
|
||||
const timeDiff = (now.getTime() - timeOfCall.getTime()) / 1000
|
||||
if (timeDiff > 3) {
|
||||
return true // unregister
|
||||
}
|
||||
const current = this.mapProperties.rasterLayer
|
||||
const best = RasterLayerUtils.SelectBestLayerAccordingTo(
|
||||
available,
|
||||
category,
|
||||
current.data,
|
||||
skipLayers
|
||||
)
|
||||
if (!best) {
|
||||
return
|
||||
}
|
||||
console.log("Best layer for category", category, "is", best?.properties?.id)
|
||||
current.setData(best)
|
||||
})
|
||||
}
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "O" },
|
||||
docs.selectOsmbasedmap,
|
||||
() => setLayerCategory("osmbasedmap")
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "M" },
|
||||
docs.selectMap,
|
||||
() => setLayerCategory("map")
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "P" },
|
||||
docs.selectAerial,
|
||||
() => setLayerCategory("photo")
|
||||
)
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ shift: "O" },
|
||||
docs.selectOsmbasedmap,
|
||||
() => setLayerCategory("osmbasedmap", 2)
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ shift: "M" },
|
||||
docs.selectMap,
|
||||
() => setLayerCategory("map", 2)
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ shift: "P" },
|
||||
docs.selectAerial,
|
||||
() => setLayerCategory("photo", 2)
|
||||
)
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "L" },
|
||||
Translations.t.hotkeyDocumentation.geolocate,
|
||||
() => {
|
||||
this.geolocationControl.handleClick()
|
||||
}
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{
|
||||
shift: "T"
|
||||
},
|
||||
docs.translationMode,
|
||||
() => {
|
||||
const tm = this.userRelatedState.translationMode
|
||||
if (tm.data === "false") {
|
||||
tm.setData("true")
|
||||
} else {
|
||||
tm.setData("false")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the current GPS-location marker on the given map.
|
||||
* This is used to show the location on _other_ maps, e.g. on the map to add a new feature.
|
||||
*
|
||||
* This is _NOT_ to be used on the main map!
|
||||
*/
|
||||
public showCurrentLocationOn(map: Store<MlMap>) {
|
||||
const id = "gps_location"
|
||||
const layer = this.theme.getLayer(id)
|
||||
if (layer === undefined) {
|
||||
return
|
||||
}
|
||||
if (map === this.map) {
|
||||
throw "Invalid use of showCurrentLocationOn"
|
||||
}
|
||||
const features = this.geolocation.currentUserLocation
|
||||
return new ShowDataLayer(map, {
|
||||
features,
|
||||
layer,
|
||||
metaTags: this.userRelatedState.preferencesAsTags
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue