refactoring(maplibre): WIP

This commit is contained in:
Pieter Vander Vennet 2023-03-24 19:21:15 +01:00
parent 231d67361e
commit 4d48b1cf2b
89 changed files with 1166 additions and 3973 deletions

View file

@ -1,51 +1,31 @@
import UserRelatedState from "./UserRelatedState"
import { Store, Stores, UIEventSource } from "../UIEventSource"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import { Store, UIEventSource } from "../UIEventSource"
import Attribution from "../../UI/BigComponents/Attribution"
import Minimap, { MinimapObj } from "../../UI/Base/Minimap"
import { Tiles } from "../../Models/TileRange"
import BaseUIElement from "../../UI/BaseUIElement"
import FilteredLayer, { FilterState } from "../../Models/FilteredLayer"
import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig"
import { QueryParameters } from "../Web/QueryParameters"
import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer"
import { FeatureSourceForLayer, Tiled } from "../FeatureSource/FeatureSource"
import SimpleFeatureSource from "../FeatureSource/Sources/SimpleFeatureSource"
import { LocalStorageSource } from "../Web/LocalStorageSource"
import { GeoOperations } from "../GeoOperations"
import TitleHandler from "../Actors/TitleHandler"
import { BBox } from "../BBox"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { TiledStaticFeatureSource } from "../FeatureSource/Sources/StaticFeatureSource"
import { Translation, TypedTranslation } from "../../UI/i18n/Translation"
import { Tag } from "../Tags/Tag"
import StaticFeatureSource, {
TiledStaticFeatureSource,
} from "../FeatureSource/Sources/StaticFeatureSource"
import { OsmConnection } from "../Osm/OsmConnection"
import { Feature, LineString } from "geojson"
import { OsmTags } from "../../Models/OsmFeature"
export interface GlobalFilter {
filter: FilterState
id: string
onNewPoint: {
safetyCheck: Translation
confirmAddNew: TypedTranslation<{ preset: Translation }>
tags: Tag[]
}
}
import { Feature } from "geojson"
import { Map as MlMap } from "maplibre-gl"
import { GlobalFilter } from "../../Models/GlobalFilter"
import { MapProperties } from "../../Models/MapProperties"
import ShowDataLayer from "../../UI/Map/ShowDataLayer"
/**
* Contains all the leaflet-map related state
*/
export default class MapState extends UserRelatedState {
/**
The leaflet instance of the big basemap
*/
public leafletMap = new UIEventSource<any /*L.Map*/>(undefined, "leafletmap")
export default class MapState {
/**
* The current background layer
*/
public backgroundLayer: UIEventSource<BaseLayer>
/**
* Last location where a click was registered
*/
@ -58,34 +38,6 @@ export default class MapState extends UserRelatedState {
* The bounds of the current map view
*/
public currentView: FeatureSourceForLayer & Tiled
/**
* The location as delivered by the GPS
*/
public currentUserLocation: SimpleFeatureSource
/**
* All previously visited points, with their metadata
*/
public historicalUserLocations: SimpleFeatureSource
/**
* The number of seconds that the GPS-locations are stored in memory.
* Time in seconds
*/
public gpsLocationHistoryRetentionTime = new UIEventSource(
7 * 24 * 60 * 60,
"gps_location_retention"
)
/**
* A featureSource containing a single linestring which has the GPS-history of the user.
* However, metadata (such as when every single point was visited) is lost here (but is kept in `historicalUserLocations`.
* Note that this featureSource is _derived_ from 'historicalUserLocations'
*/
public historicalUserLocationsTrack: FeatureSourceForLayer & Tiled
/**
* A feature source containing the current home location of the user
*/
public homeLocation: FeatureSourceForLayer & Tiled
/**
* A builtin layer which contains the selected element.
@ -94,7 +46,7 @@ export default class MapState extends UserRelatedState {
*/
public selectedElementsLayer: FeatureSourceForLayer & Tiled
public readonly mainMapObject: BaseUIElement & MinimapObj
public readonly mainMapObject: BaseUIElement
/**
* Which layers are enabled in the current theme and what filters are applied onto them
@ -114,9 +66,7 @@ export default class MapState extends UserRelatedState {
*/
public overlayToggles: { config: TilesourceConfig; isDisplayed: UIEventSource<boolean> }[]
constructor(layoutToUse: LayoutConfig, options?: { attemptLogin: true | boolean }) {
super(layoutToUse, options)
constructor() {
this.availableBackgroundLayers = AvailableBaseLayers.AvailableLayersAt(this.locationControl)
let defaultLayer = AvailableBaseLayers.osmCarto
@ -130,13 +80,6 @@ export default class MapState extends UserRelatedState {
this.backgroundLayer = new UIEventSource<BaseLayer>(defaultLayer)
this.backgroundLayer.addCallbackAndRunD((layer) => self.backgroundLayerId.setData(layer.id))
const attr = new Attribution(
this.locationControl,
this.osmConnection.userDetails,
this.layoutToUse,
this.currentBounds
)
// Will write into this.leafletMap
this.mainMapObject = Minimap.createMiniMap({
background: this.backgroundLayer,
@ -162,12 +105,8 @@ export default class MapState extends UserRelatedState {
MapState.InitializeFilteredLayers(this.layoutToUse, this.osmConnection)
)
this.lockBounds()
this.AddAllOverlaysToMap(this.leafletMap)
this.initHomeLocation()
this.initGpsLocation()
this.initUserLocationTrail()
this.initCurrentView()
this.initSelectedElement()
@ -189,17 +128,6 @@ export default class MapState extends UserRelatedState {
}
}
private lockBounds() {
const layout = this.layoutToUse
if (!layout?.lockLocation) {
return
}
console.warn("Locking the bounds to ", layout.lockLocation)
this.mainMapObject.installBounds(
new BBox(layout.lockLocation),
this.featureSwitchIsTesting.data
)
}
private initCurrentView() {
let currentViewLayer: FilteredLayer = this.filteredLayers.data.filter(
@ -244,17 +172,6 @@ export default class MapState extends UserRelatedState {
this.currentView = new TiledStaticFeatureSource(features, currentViewLayer)
}
private initGpsLocation() {
// Initialize the gps layer data. This is emtpy for now, the actual writing happens in the Geolocationhandler
const gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(
(l) => l.layerDef.id === "gps_location"
)[0]
if (gpsLayerDef === undefined) {
return
}
this.currentUserLocation = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0))
}
private initSelectedElement() {
const layerDef: FilteredLayer = this.filteredLayers.data.filter(
(l) => l.layerDef.id === "selected_element"
@ -281,145 +198,6 @@ export default class MapState extends UserRelatedState {
this.selectedElementsLayer = new TiledStaticFeatureSource(store, layerDef)
}
private initUserLocationTrail() {
const features = LocalStorageSource.GetParsed<{ feature: any; freshness: Date }[]>(
"gps_location_history",
[]
)
const now = new Date().getTime()
features.data = features.data
.map((ff) => ({ feature: ff.feature, freshness: new Date(ff.freshness) }))
.filter(
(ff) =>
now - ff.freshness.getTime() < 1000 * this.gpsLocationHistoryRetentionTime.data
)
features.ping()
const self = this
let i = 0
this.currentUserLocation?.features?.addCallbackAndRunD(([location]) => {
if (location === undefined) {
return
}
const previousLocation = features.data[features.data.length - 1]
if (previousLocation !== undefined) {
const d = GeoOperations.distanceBetween(
previousLocation.feature.geometry.coordinates,
location.feature.geometry.coordinates
)
let timeDiff = Number.MAX_VALUE // in seconds
const olderLocation = features.data[features.data.length - 2]
if (olderLocation !== undefined) {
timeDiff =
(new Date(previousLocation.freshness).getTime() -
new Date(olderLocation.freshness).getTime()) /
1000
}
if (d < 20 && timeDiff < 60) {
// Do not append changes less then 20m - it's probably noise anyway
return
}
}
const feature = JSON.parse(JSON.stringify(location.feature))
feature.properties.id = "gps/" + features.data.length
i++
features.data.push({ feature, freshness: new Date() })
features.ping()
})
let gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(
(l) => l.layerDef.id === "gps_location_history"
)[0]
if (gpsLayerDef !== undefined) {
this.historicalUserLocations = new SimpleFeatureSource(
gpsLayerDef,
Tiles.tile_index(0, 0, 0),
features
)
this.changes.setHistoricalUserLocations(this.historicalUserLocations)
}
const asLine = features.map((allPoints) => {
if (allPoints === undefined || allPoints.length < 2) {
return []
}
const feature: Feature<LineString, OsmTags> = {
type: "Feature",
properties: {
id: "location_track",
"_date:now": new Date().toISOString(),
},
geometry: {
type: "LineString",
coordinates: allPoints.map((ff) => ff.feature.geometry.coordinates),
},
}
self.allElements.ContainingFeatures.set(feature.properties.id, feature)
return [
{
feature,
freshness: new Date(),
},
]
})
let gpsLineLayerDef: FilteredLayer = this.filteredLayers.data.filter(
(l) => l.layerDef.id === "gps_track"
)[0]
if (gpsLineLayerDef !== undefined) {
this.historicalUserLocationsTrack = new TiledStaticFeatureSource(
asLine,
gpsLineLayerDef
)
}
}
private initHomeLocation() {
const empty = []
const feature = Stores.ListStabilized(
this.osmConnection.userDetails.map((userDetails) => {
if (userDetails === undefined) {
return undefined
}
const home = userDetails.home
if (home === undefined) {
return undefined
}
return [home.lon, home.lat]
})
).map((homeLonLat) => {
if (homeLonLat === undefined) {
return empty
}
return [
{
feature: {
type: "Feature",
properties: {
id: "home",
"user:home": "yes",
_lon: homeLonLat[0],
_lat: homeLonLat[1],
},
geometry: {
type: "Point",
coordinates: homeLonLat,
},
},
freshness: new Date(),
},
]
})
const flayer = this.filteredLayers.data.filter((l) => l.layerDef.id === "home_location")[0]
if (flayer !== undefined) {
this.homeLocation = new TiledStaticFeatureSource(feature, flayer)
}
}
private static getPref(
osmConnection: OsmConnection,
key: string,