refactoring: more state splitting, basic layoutFeatureSource

This commit is contained in:
Pieter Vander Vennet 2023-03-26 05:58:28 +02:00
parent 8e2f04c0d0
commit b94a8f5745
54 changed files with 1067 additions and 1969 deletions

View file

@ -5,28 +5,32 @@ import MoreScreen from "./BigComponents/MoreScreen"
import Translations from "./i18n/Translations"
import Constants from "../Models/Constants"
import { Utils } from "../Utils"
import LanguagePicker1 from "./LanguagePicker"
import LanguagePicker from "./LanguagePicker"
import IndexText from "./BigComponents/IndexText"
import FeaturedMessage from "./BigComponents/FeaturedMessage"
import { ImportViewerLinks } from "./BigComponents/UserInformation"
import { LoginToggle } from "./Popup/LoginButton"
import { ImmutableStore } from "../Logic/UIEventSource"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
export default class AllThemesGui {
setup() {
try {
new FixedUiElement("").AttachTo("centermessage")
const state = new UserRelatedState(undefined)
const osmConnection = new OsmConnection()
const state = new UserRelatedState(osmConnection)
const intro = new Combine([
new LanguagePicker1(Translations.t.index.title.SupportedLanguages(), "").SetClass(
new LanguagePicker(Translations.t.index.title.SupportedLanguages(), "").SetClass(
"flex absolute top-2 right-3"
),
new IndexText(),
])
new Combine([
intro,
new FeaturedMessage().SetClass("mb-4 block"),
new MoreScreen(state, true),
new LoginToggle(undefined, Translations.t.index.logIn, state),
new LoginToggle(undefined, Translations.t.index.logIn, {
osmConnection,
featureSwitchUserbadge: new ImmutableStore(true),
}),
new ImportViewerLinks(state.osmConnection),
Translations.t.general.aboutMapcomplete
.Subs({ osmcha_link: Utils.OsmChaLinkFor(7) })

View file

@ -1,103 +0,0 @@
import Combine from "../Base/Combine"
import welcome_messages from "../../assets/welcome_message.json"
import BaseUIElement from "../BaseUIElement"
import { FixedUiElement } from "../Base/FixedUiElement"
import MoreScreen from "./MoreScreen"
import themeOverview from "../../assets/generated/theme_overview.json"
import Translations from "../i18n/Translations"
import Title from "../Base/Title"
export default class FeaturedMessage extends Combine {
constructor() {
const now = new Date()
let welcome_message = undefined
for (const wm of FeaturedMessage.WelcomeMessages()) {
if (wm.start_date >= now) {
continue
}
if (wm.end_date <= now) {
continue
}
if (welcome_message !== undefined) {
console.warn("Multiple applicable messages today:", welcome_message.featured_theme)
}
welcome_message = wm
}
welcome_message = welcome_message ?? undefined
super([FeaturedMessage.CreateFeaturedBox(welcome_message)])
}
public static WelcomeMessages(): {
start_date: Date
end_date: Date
message: string
featured_theme?: string
}[] {
const all_messages: {
start_date: Date
end_date: Date
message: string
featured_theme?: string
}[] = []
const themesById = new Map<string, { id: string; title: any; shortDescription: any }>()
for (const theme of themeOverview) {
themesById.set(theme.id, theme)
}
for (const i in welcome_messages) {
if (isNaN(Number(i))) {
continue
}
const wm = welcome_messages[i]
if (wm === null) {
continue
}
if (themesById.get(wm.featured_theme) === undefined) {
console.log("THEMES BY ID:", themesById)
console.error("Unkown featured theme for ", wm)
continue
}
if (!wm.message) {
console.error("Featured message is missing for", wm)
continue
}
all_messages.push({
start_date: new Date(wm.start_date),
end_date: new Date(wm.end_date),
message: wm.message,
featured_theme: wm.featured_theme,
})
}
return all_messages
}
public static CreateFeaturedBox(welcome_message: {
message: string
featured_theme?: string
}): BaseUIElement {
const els: BaseUIElement[] = []
if (welcome_message === undefined) {
return undefined
}
const title = new Title(Translations.t.index.featuredThemeTitle.Clone())
const msg = new FixedUiElement(welcome_message.message).SetClass("link-underline font-lg")
els.push(new Combine([title, msg]).SetClass("m-4"))
if (welcome_message.featured_theme !== undefined) {
const theme = themeOverview.filter((th) => th.id === welcome_message.featured_theme)[0]
els.push(
MoreScreen.createLinkButton({}, theme)
.SetClass("m-4 self-center md:w-160")
.SetStyle("height: min-content;")
)
}
return new Combine(els).SetClass(
"border-2 border-grey-400 rounded-xl flex flex-col md:flex-row"
)
}
}

View file

@ -7,7 +7,6 @@ import CreateNoteImportLayer from "../../Models/ThemeConfig/Conversion/CreateNot
import FilteredLayer, { FilterState } from "../../Models/FilteredLayer"
import GeoJsonSource from "../../Logic/FeatureSource/Sources/GeoJsonSource"
import MetaTagging from "../../Logic/MetaTagging"
import RelationsTracker from "../../Logic/Osm/RelationsTracker"
import FilteringFeatureSource from "../../Logic/FeatureSource/Sources/FilteringFeatureSource"
import Minimap from "../Base/Minimap"
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
@ -58,7 +57,6 @@ export class CompareToAlreadyExistingNotes
MetaTagging.addMetatags(
f,
{
memberships: new RelationsTracker(),
getFeaturesWithin: () => [],
getFeatureById: () => undefined,
},

View file

@ -7,7 +7,6 @@ import { BBox } from "../../Logic/BBox"
import { MapProperties } from "../../Models/MapProperties"
import SvelteUIElement from "../Base/SvelteUIElement"
import MaplibreMap from "./MaplibreMap.svelte"
import Constants from "../../Models/Constants"
/**
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`
@ -51,7 +50,7 @@ export class MapLibreAdaptor implements MapProperties {
})
this.maxbounds = state?.maxbounds ?? new UIEventSource(undefined)
this.allowMoving = state?.allowMoving ?? new UIEventSource(true)
this._bounds = new UIEventSource(BBox.global)
this._bounds = new UIEventSource(undefined)
this.bounds = this._bounds
this.rasterLayer =
state?.rasterLayer ?? new UIEventSource<RasterLayerPolygon | undefined>(undefined)
@ -75,6 +74,12 @@ export class MapLibreAdaptor implements MapProperties {
dt.lat = map.getCenter().lat
this.location.ping()
this.zoom.setData(Math.round(map.getZoom() * 10) / 10)
const bounds = map.getBounds()
const bbox = new BBox([
[bounds.getEast(), bounds.getNorth()],
[bounds.getWest(), bounds.getSouth()],
])
self._bounds.setData(bbox)
})
})

View file

@ -1,6 +1,6 @@
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
import type { Map as MlMap } from "maplibre-gl"
import { Marker } from "maplibre-gl"
import { GeoJSONSource, Marker } from "maplibre-gl"
import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
import { GeoOperations } from "../../Logic/GeoOperations"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
@ -19,7 +19,7 @@ class PointRenderingLayer {
private readonly _config: PointRenderingConfig
private readonly _fetchStore?: (id: string) => Store<OsmTags>
private readonly _map: MlMap
private readonly _onClick: (id: string) => void
private readonly _onClick: (feature: Feature) => void
private readonly _allMarkers: Map<string, Marker> = new Map<string, Marker>()
constructor(
@ -28,7 +28,7 @@ class PointRenderingLayer {
config: PointRenderingConfig,
visibility?: Store<boolean>,
fetchStore?: (id: string) => Store<OsmTags>,
onClick?: (id: string) => void
onClick?: (feature: Feature) => void
) {
this._config = config
this._map = map
@ -109,7 +109,7 @@ class PointRenderingLayer {
if (this._onClick) {
const self = this
el.addEventListener("click", function () {
self._onClick(feature.properties.id)
self._onClick(feature)
})
}
@ -144,7 +144,7 @@ class LineRenderingLayer {
private readonly _config: LineRenderingConfig
private readonly _visibility?: Store<boolean>
private readonly _fetchStore?: (id: string) => Store<OsmTags>
private readonly _onClick?: (id: string) => void
private readonly _onClick?: (feature: Feature) => void
private readonly _layername: string
private readonly _listenerInstalledOn: Set<string> = new Set<string>()
@ -155,7 +155,7 @@ class LineRenderingLayer {
config: LineRenderingConfig,
visibility?: Store<boolean>,
fetchStore?: (id: string) => Store<OsmTags>,
onClick?: (id: string) => void
onClick?: (feature: Feature) => void
) {
this._layername = layername
this._map = map
@ -174,20 +174,17 @@ class LineRenderingLayer {
const config = this._config
for (const key of LineRenderingLayer.lineConfigKeys) {
const v = config[key]?.GetRenderValue(properties)?.Subs(properties).txt
calculatedProps[key] = v
calculatedProps[key] = config[key]?.GetRenderValue(properties)?.Subs(properties).txt
}
for (const key of LineRenderingLayer.lineConfigKeysColor) {
let v = config[key]?.GetRenderValue(properties)?.Subs(properties).txt
if (v === undefined) {
continue
}
console.log("Color", v)
if (v.length == 9 && v.startsWith("#")) {
// This includes opacity
calculatedProps[key + "-opacity"] = parseInt(v.substring(7), 16) / 256
v = v.substring(0, 7)
console.log("Color >", v, calculatedProps[key + "-opacity"])
}
calculatedProps[key] = v
}
@ -196,7 +193,6 @@ class LineRenderingLayer {
calculatedProps[key] = Number(v)
}
console.log("Calculated props:", calculatedProps, "for", properties.id)
return calculatedProps
}
@ -205,52 +201,53 @@ class LineRenderingLayer {
while (!map.isStyleLoaded()) {
await Utils.waitFor(100)
}
map.addSource(this._layername, {
type: "geojson",
data: {
const src = <GeoJSONSource>map.getSource(this._layername)
if (src === undefined) {
map.addSource(this._layername, {
type: "geojson",
data: {
type: "FeatureCollection",
features,
},
promoteId: "id",
})
// @ts-ignore
map.addLayer({
source: this._layername,
id: this._layername + "_line",
type: "line",
paint: {
"line-color": ["feature-state", "color"],
"line-opacity": ["feature-state", "color-opacity"],
"line-width": ["feature-state", "width"],
"line-offset": ["feature-state", "offset"],
},
layout: {
"line-cap": "round",
},
})
map.addLayer({
source: this._layername,
id: this._layername + "_polygon",
type: "fill",
filter: ["in", ["geometry-type"], ["literal", ["Polygon", "MultiPolygon"]]],
layout: {},
paint: {
"fill-color": ["feature-state", "fillColor"],
"fill-opacity": 0.1,
},
})
} else {
src.setData({
type: "FeatureCollection",
features,
},
promoteId: "id",
})
map.addLayer({
source: this._layername,
id: this._layername + "_line",
type: "line",
paint: {
"line-color": ["feature-state", "color"],
"line-opacity": ["feature-state", "color-opacity"],
"line-width": ["feature-state", "width"],
"line-offset": ["feature-state", "offset"],
},
})
/*[
"color",
"width",
"dashArray",
"lineCap",
"offset",
"fill",
"fillColor",
]*/
map.addLayer({
source: this._layername,
id: this._layername + "_polygon",
type: "fill",
filter: ["in", ["geometry-type"], ["literal", ["Polygon", "MultiPolygon"]]],
layout: {},
paint: {
"fill-color": ["feature-state", "fillColor"],
"fill-opacity": 0.1,
},
})
})
}
for (let i = 0; i < features.length; i++) {
const feature = features[i]
const id = feature.properties.id ?? feature.id
console.log("ID is", id)
if (id === undefined) {
console.trace(
"Got a feature without ID; this causes rendering bugs:",
@ -310,23 +307,6 @@ export default class ShowDataLayer {
})
}
private openOrReusePopup(id: string): void {
if (!this._popupCache || !this._options.fetchStore) {
return
}
if (this._popupCache.has(id)) {
this._popupCache.get(id).Activate()
return
}
const tags = this._options.fetchStore(id)
if (!tags) {
return
}
const popup = this._options.buildPopup(tags, this._options.layer)
this._popupCache.set(id, popup)
popup.Activate()
}
private zoomToCurrentFeatures(map: MlMap) {
if (this._options.zoomToFeatures) {
const features = this._options.features.features.data
@ -338,8 +318,8 @@ export default class ShowDataLayer {
}
private initDrawFeatures(map: MlMap) {
const { features, doShowLayer, fetchStore, buildPopup } = this._options
const onClick = buildPopup === undefined ? undefined : (id) => this.openOrReusePopup(id)
const { features, doShowLayer, fetchStore, selectedElement } = this._options
const onClick = (feature: Feature) => selectedElement?.setData(feature)
for (let i = 0; i < this._options.layer.lineRendering.length; i++) {
const lineRenderingConfig = this._options.layer.lineRendering[i]
new LineRenderingLayer(

View file

@ -1,8 +1,5 @@
import FeatureSource from "../../Logic/FeatureSource/FeatureSource"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { ElementStorage } from "../../Logic/ElementStorage"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
import { OsmTags } from "../../Models/OsmFeature"
export interface ShowDataLayerOptions {
@ -11,15 +8,10 @@ export interface ShowDataLayerOptions {
*/
features: FeatureSource
/**
* Indication of the current selected element; overrides some filters
* Indication of the current selected element; overrides some filters.
* When a feature is tapped, the feature will be put in there
*/
selectedElement?: UIEventSource<any>
/**
* What popup to build when a feature is selected
*/
buildPopup?:
| undefined
| ((tags: UIEventSource<any>, layer: LayerConfig) => ScrollableFullScreen)
/**
* If set, zoom to the features when initially loaded and when they are changed
@ -31,7 +23,8 @@ export interface ShowDataLayerOptions {
doShowLayer?: Store<true | boolean>
/**
* Function which fetches the relevant store
* Function which fetches the relevant store.
* If given, the map will update when a property is changed
*/
fetchStore?: (id: string) => UIEventSource<OsmTags>
}

View file

@ -1,24 +1,35 @@
/**
* SHows geojson on the given leaflet map, but attempts to figure out the correct layer first
*/
import { Store } from "../../Logic/UIEventSource"
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
import ShowDataLayer from "./ShowDataLayer"
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
import FilteredLayer from "../../Models/FilteredLayer"
import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
import { Map as MlMap } from "maplibre-gl"
import FilteringFeatureSource from "../../Logic/FeatureSource/Sources/FilteringFeatureSource"
import { GlobalFilter } from "../../Models/GlobalFilter"
export default class ShowDataMultiLayer {
constructor(
map: Store<MlMap>,
options: ShowDataLayerOptions & { layers: Store<FilteredLayer[]> }
options: ShowDataLayerOptions & {
layers: FilteredLayer[]
globalFilters?: Store<GlobalFilter[]>
}
) {
new PerLayerFeatureSourceSplitter(
options.layers,
(perLayer) => {
new ImmutableStore(options.layers),
(features, layer) => {
const newOptions = {
...options,
layer: perLayer.layer.layerDef,
features: perLayer,
layer: layer.layerDef,
features: new FilteringFeatureSource(
layer,
features,
options.fetchStore,
options.globalFilters
),
}
new ShowDataLayer(map, newOptions)
},

View file

@ -1,21 +0,0 @@
import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig"
import { UIEventSource } from "../../Logic/UIEventSource"
export default class ShowOverlayLayer {
public static implementation: (
config: TilesourceConfig,
leafletMap: UIEventSource<any>,
isShown?: UIEventSource<boolean>
) => void
constructor(
config: TilesourceConfig,
leafletMap: UIEventSource<any>,
isShown: UIEventSource<boolean> = undefined
) {
if (ShowOverlayLayer.implementation === undefined) {
throw "Call ShowOverlayLayerImplemenation.initialize() first before using this"
}
ShowOverlayLayer.implementation(config, leafletMap, isShown)
}
}

View file

@ -3,6 +3,7 @@ import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig"
import { UIEventSource } from "../../Logic/UIEventSource"
import ShowOverlayLayer from "./ShowOverlayLayer"
// TODO port this to maplibre!
export default class ShowOverlayLayerImplementation {
public static Implement() {
ShowOverlayLayer.implementation = ShowOverlayLayerImplementation.AddToMap

View file

@ -1,257 +0,0 @@
import FeatureSource, {
FeatureSourceForLayer,
Tiled,
} from "../../Logic/FeatureSource/FeatureSource"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { UIEventSource } from "../../Logic/UIEventSource"
import { Tiles } from "../../Models/TileRange"
import { BBox } from "../../Logic/BBox"
import FilteredLayer from "../../Models/FilteredLayer"
import { Feature } from "geojson"
/**
* A feature source containing but a single feature, which keeps stats about a tile
*/
export class TileHierarchyAggregator implements FeatureSource {
private static readonly empty = []
public totalValue: number = 0
public showCount: number = 0
public hiddenCount: number = 0
public readonly features = new UIEventSource<Feature[]>(TileHierarchyAggregator.empty)
public readonly name
private _parent: TileHierarchyAggregator
private _root: TileHierarchyAggregator
private readonly _z: number
private readonly _x: number
private readonly _y: number
private readonly _tileIndex: number
private _counter: SingleTileCounter
private _subtiles: [
TileHierarchyAggregator,
TileHierarchyAggregator,
TileHierarchyAggregator,
TileHierarchyAggregator
] = [undefined, undefined, undefined, undefined]
private readonly featuresStatic = []
private readonly featureProperties: {
count: string
kilocount: string
tileId: string
id: string
showCount: string
totalCount: string
}
private readonly _state: { filteredLayers: UIEventSource<FilteredLayer[]> }
private readonly updateSignal = new UIEventSource<any>(undefined)
private constructor(
parent: TileHierarchyAggregator,
state: {
filteredLayers: UIEventSource<FilteredLayer[]>
},
z: number,
x: number,
y: number
) {
this._parent = parent
this._state = state
this._root = parent?._root ?? this
this._z = z
this._x = x
this._y = y
this._tileIndex = Tiles.tile_index(z, x, y)
this.name = "Count(" + this._tileIndex + ")"
const totals = {
id: "" + this._tileIndex,
tileId: "" + this._tileIndex,
count: `0`,
kilocount: "0",
showCount: "0",
totalCount: "0",
}
this.featureProperties = totals
const now = new Date()
const feature = {
type: "Feature",
properties: totals,
geometry: {
type: "Point",
coordinates: Tiles.centerPointOf(z, x, y),
},
}
this.featuresStatic.push({ feature: feature, freshness: now })
const bbox = BBox.fromTile(z, x, y)
const box = {
type: "Feature",
properties: totals,
geometry: {
type: "Polygon",
coordinates: [
[
[bbox.minLon, bbox.minLat],
[bbox.minLon, bbox.maxLat],
[bbox.maxLon, bbox.maxLat],
[bbox.maxLon, bbox.minLat],
[bbox.minLon, bbox.minLat],
],
],
},
}
this.featuresStatic.push({ feature: box, freshness: now })
}
public static createHierarchy(state: { filteredLayers: UIEventSource<FilteredLayer[]> }) {
return new TileHierarchyAggregator(undefined, state, 0, 0, 0)
}
public getTile(tileIndex): TileHierarchyAggregator {
if (tileIndex === this._tileIndex) {
return this
}
let [tileZ, tileX, tileY] = Tiles.tile_from_index(tileIndex)
while (tileZ - 1 > this._z) {
tileX = Math.floor(tileX / 2)
tileY = Math.floor(tileY / 2)
tileZ--
}
const xDiff = tileX - 2 * this._x
const yDiff = tileY - 2 * this._y
const subtileIndex = yDiff * 2 + xDiff
return this._subtiles[subtileIndex]?.getTile(tileIndex)
}
public addTile(source: FeatureSourceForLayer & Tiled) {
const self = this
if (source.tileIndex === this._tileIndex) {
if (this._counter === undefined) {
this._counter = new SingleTileCounter(this._tileIndex)
this._counter.countsPerLayer.addCallbackAndRun((_) => self.update())
}
this._counter.addTileCount(source)
} else {
// We have to give it to one of the subtiles
let [tileZ, tileX, tileY] = Tiles.tile_from_index(source.tileIndex)
while (tileZ - 1 > this._z) {
tileX = Math.floor(tileX / 2)
tileY = Math.floor(tileY / 2)
tileZ--
}
const xDiff = tileX - 2 * this._x
const yDiff = tileY - 2 * this._y
const subtileIndex = yDiff * 2 + xDiff
if (this._subtiles[subtileIndex] === undefined) {
this._subtiles[subtileIndex] = new TileHierarchyAggregator(
this,
this._state,
tileZ,
tileX,
tileY
)
}
this._subtiles[subtileIndex].addTile(source)
}
this.updateSignal.setData(source)
}
private update() {
const newMap = new Map<string, number>()
let total = 0
let hiddenCount = 0
let showCount = 0
let isShown: Map<string, FilteredLayer> = new Map<string, FilteredLayer>()
for (const filteredLayer of this._state.filteredLayers.data) {
isShown.set(filteredLayer.layerDef.id, filteredLayer)
}
this?._counter?.countsPerLayer?.data?.forEach((count, layerId) => {
newMap.set("layer:" + layerId, count)
total += count
this.featureProperties["direct_layer:" + layerId] = count
const flayer = isShown.get(layerId)
if (flayer.isDisplayed.data && this._z >= flayer.layerDef.minzoom) {
showCount += count
} else {
hiddenCount += count
}
})
for (const tile of this._subtiles) {
if (tile === undefined) {
continue
}
total += tile.totalValue
showCount += tile.showCount
hiddenCount += tile.hiddenCount
for (const key in tile.featureProperties) {
if (key.startsWith("layer:")) {
newMap.set(
key,
(newMap.get(key) ?? 0) + Number(tile.featureProperties[key] ?? 0)
)
}
}
}
this.totalValue = total
this.showCount = showCount
this.hiddenCount = hiddenCount
this._parent?.update()
if (total === 0) {
this.features.setData(TileHierarchyAggregator.empty)
} else {
this.featureProperties.count = "" + total
this.featureProperties.kilocount = "" + Math.floor(total / 1000)
this.featureProperties.showCount = "" + showCount
this.featureProperties.totalCount = "" + total
newMap.forEach((value, key) => {
this.featureProperties[key] = "" + value
})
this.features.data = this.featuresStatic
this.features.ping()
}
}
}
/**
* Keeps track of a single tile
*/
class SingleTileCounter implements Tiled {
public readonly bbox: BBox
public readonly tileIndex: number
public readonly countsPerLayer: UIEventSource<Map<string, number>> = new UIEventSource<
Map<string, number>
>(new Map<string, number>())
public readonly z: number
public readonly x: number
public readonly y: number
private readonly registeredLayers: Map<string, LayerConfig> = new Map<string, LayerConfig>()
constructor(tileIndex: number) {
this.tileIndex = tileIndex
this.bbox = BBox.fromTileIndex(tileIndex)
const [z, x, y] = Tiles.tile_from_index(tileIndex)
this.z = z
this.x = x
this.y = y
}
public addTileCount(source: FeatureSourceForLayer) {
const layer = source.layer.layerDef
this.registeredLayers.set(layer.id, layer)
const self = this
source.features.map(
(f) => {
const isDisplayed = source.layer.isDisplayed.data
self.countsPerLayer.data.set(layer.id, isDisplayed ? f.length : 0)
self.countsPerLayer.ping()
},
[source.layer.isDisplayed]
)
}
}

View file

@ -11,7 +11,6 @@
import { QueryParameters } from "../Logic/Web/QueryParameters";
import UserRelatedState from "../Logic/State/UserRelatedState";
import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler";
import { ElementStorage } from "../Logic/ElementStorage";
import { Changes } from "../Logic/Osm/Changes";
import ChangeToElementsActor from "../Logic/Actors/ChangeToElementsActor";
import PendingChangesUploader from "../Logic/Actors/PendingChangesUploader";
@ -28,6 +27,12 @@
import LayerState from "../Logic/State/LayerState";
import Constants from "../Models/Constants";
import type { Feature } from "geojson";
import FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropertiesStore";
import ShowDataMultiLayer from "./Map/ShowDataMultiLayer";
import { Or } from "../Logic/Tags/Or";
import LayoutSource from "../Logic/FeatureSource/LayoutSource";
import { type OsmTags } from "../Models/OsmFeature";
export let layout: LayoutConfig;
const maplibremap: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
@ -49,16 +54,34 @@
});
const userRelatedState = new UserRelatedState(osmConnection, layout?.language);
const selectedElement = new UIEventSource<Feature | undefined>(undefined, "Selected element");
selectedElement.addCallbackAndRunD(s => console.log("Selected element:", s))
const geolocation = new GeoLocationHandler(geolocationState, selectedElement, mapproperties, userRelatedState.gpsLocationHistoryRetentionTime);
const allElements = new ElementStorage();
const tags = new Or(layout.layers.filter(l => l.source !== null&& Constants.priviliged_layers.indexOf(l.id) < 0 && l.source.geojsonSource === undefined).map(l => l.source.osmTags ))
const layerState = new LayerState(osmConnection, layout.layers, layout.id)
const indexedElements = new LayoutSource(layout.layers, featureSwitches, new StaticFeatureSource([]), mapproperties, osmConnection.Backend(),
(id) => layerState.filteredLayers.get(id).isDisplayed
)
const allElements = new FeaturePropertiesStore(indexedElements)
const changes = new Changes({
allElements,
dryRun: featureSwitches.featureSwitchIsTesting,
allElements: indexedElements,
featurePropertiesStore: allElements,
osmConnection,
historicalUserLocations: geolocation.historicalUserLocations
}, layout?.isLeftRightSensitive() ?? false);
console.log("Setting up layerstate...")
const layerState = new LayerState(osmConnection, layout.layers, layout.id)
new ShowDataMultiLayer(maplibremap, {
layers: Array.from(layerState.filteredLayers.values()),
features: indexedElements,
fetchStore: id => <UIEventSource<OsmTags>> allElements.getStore(id),
selectedElement,
globalFilters: layerState.globalFilters
})
{
// Various actors that we don't need to reference
// TODO enable new TitleHandler(selectedElement,layout,allElements)
@ -98,7 +121,7 @@
current_view: new StaticFeatureSource(mapproperties.bounds.map(bbox => bbox === undefined ? empty : <Feature[]> [bbox.asGeoJson({id:"current_view"})])),
}
layerState.filteredLayers.get("range")?.isDisplayed?.syncWith(featureSwitches.featureSwitchIsTesting, true)
console.log("RAnge fs", specialLayers.range)
specialLayers.range.features.addCallbackAndRun(fs => console.log("Range.features:", JSON.stringify(fs)))
layerState.filteredLayers.forEach((flayer) => {
const features = specialLayers[flayer.layerDef.id]
@ -116,7 +139,8 @@ console.log("RAnge fs", specialLayers.range)
</script>
<div class="h-screen w-screen absolute top-0 left-0 border-3 border-red-500">
<div class="h-screen w-screen absolute top-0 left-0 flex">
<div id="fullscreen" class="transition-all transition-duration-500" style="border: 2px solid red">Hello world</div>
<MaplibreMap class="w-full h-full border border-black" map={maplibremap}></MaplibreMap>
</div>