forked from MapComplete/MapComplete
refactoring
This commit is contained in:
parent
b94a8f5745
commit
5d0fe31c41
114 changed files with 2412 additions and 2958 deletions
|
@ -21,12 +21,20 @@ export class MapLibreAdaptor implements MapProperties {
|
|||
"keyboard",
|
||||
"touchZoomRotate",
|
||||
]
|
||||
private static maplibre_zoom_handlers = [
|
||||
"scrollZoom",
|
||||
"boxZoom",
|
||||
"doubleClickZoom",
|
||||
"touchZoomRotate",
|
||||
]
|
||||
readonly location: UIEventSource<{ lon: number; lat: number }>
|
||||
readonly zoom: UIEventSource<number>
|
||||
readonly bounds: Store<BBox>
|
||||
readonly bounds: UIEventSource<BBox>
|
||||
readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined>
|
||||
readonly maxbounds: UIEventSource<BBox | undefined>
|
||||
readonly allowMoving: UIEventSource<true | boolean | undefined>
|
||||
readonly allowZooming: UIEventSource<true | boolean | undefined>
|
||||
readonly lastClickLocation: Store<undefined | { lon: number; lat: number }>
|
||||
private readonly _maplibreMap: Store<MLMap>
|
||||
private readonly _bounds: UIEventSource<BBox>
|
||||
/**
|
||||
|
@ -50,11 +58,14 @@ export class MapLibreAdaptor implements MapProperties {
|
|||
})
|
||||
this.maxbounds = state?.maxbounds ?? new UIEventSource(undefined)
|
||||
this.allowMoving = state?.allowMoving ?? new UIEventSource(true)
|
||||
this.allowZooming = state?.allowZooming ?? new UIEventSource(true)
|
||||
this._bounds = new UIEventSource(undefined)
|
||||
this.bounds = this._bounds
|
||||
this.rasterLayer =
|
||||
state?.rasterLayer ?? new UIEventSource<RasterLayerPolygon | undefined>(undefined)
|
||||
|
||||
const lastClickLocation = new UIEventSource<{ lon: number; lat: number }>(undefined)
|
||||
this.lastClickLocation = lastClickLocation
|
||||
const self = this
|
||||
maplibreMap.addCallbackAndRunD((map) => {
|
||||
map.on("load", () => {
|
||||
|
@ -63,11 +74,13 @@ export class MapLibreAdaptor implements MapProperties {
|
|||
self.SetZoom(self.zoom.data)
|
||||
self.setMaxBounds(self.maxbounds.data)
|
||||
self.setAllowMoving(self.allowMoving.data)
|
||||
self.setAllowZooming(self.allowZooming.data)
|
||||
})
|
||||
self.MoveMapToCurrentLoc(self.location.data)
|
||||
self.SetZoom(self.zoom.data)
|
||||
self.setMaxBounds(self.maxbounds.data)
|
||||
self.setAllowMoving(self.allowMoving.data)
|
||||
self.setAllowZooming(self.allowZooming.data)
|
||||
map.on("moveend", () => {
|
||||
const dt = this.location.data
|
||||
dt.lon = map.getCenter().lng
|
||||
|
@ -81,6 +94,11 @@ export class MapLibreAdaptor implements MapProperties {
|
|||
])
|
||||
self._bounds.setData(bbox)
|
||||
})
|
||||
map.on("click", (e) => {
|
||||
const lon = e.lngLat.lng
|
||||
const lat = e.lngLat.lat
|
||||
lastClickLocation.setData({ lon, lat })
|
||||
})
|
||||
})
|
||||
|
||||
this.rasterLayer.addCallback((_) =>
|
||||
|
@ -95,6 +113,8 @@ export class MapLibreAdaptor implements MapProperties {
|
|||
this.zoom.addCallbackAndRunD((z) => self.SetZoom(z))
|
||||
this.maxbounds.addCallbackAndRun((bbox) => self.setMaxBounds(bbox))
|
||||
this.allowMoving.addCallbackAndRun((allowMoving) => self.setAllowMoving(allowMoving))
|
||||
this.allowZooming.addCallbackAndRun((allowZooming) => self.setAllowZooming(allowZooming))
|
||||
this.bounds.addCallbackAndRunD((bounds) => self.setBounds(bounds))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,7 +225,7 @@ export class MapLibreAdaptor implements MapProperties {
|
|||
// already the correct background layer, nothing to do
|
||||
return
|
||||
}
|
||||
if (background === undefined) {
|
||||
if (!background?.url) {
|
||||
// no background to set
|
||||
this.removeCurrentLayer(map)
|
||||
this._currentRasterLayer = undefined
|
||||
|
@ -266,4 +286,38 @@ export class MapLibreAdaptor implements MapProperties {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setAllowZooming(allow: true | boolean | undefined) {
|
||||
const map = this._maplibreMap.data
|
||||
if (map === undefined) {
|
||||
return
|
||||
}
|
||||
if (allow === false) {
|
||||
for (const id of MapLibreAdaptor.maplibre_zoom_handlers) {
|
||||
map[id].disable()
|
||||
}
|
||||
} else {
|
||||
for (const id of MapLibreAdaptor.maplibre_zoom_handlers) {
|
||||
map[id].enable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setBounds(bounds: BBox) {
|
||||
const map = this._maplibreMap.data
|
||||
if (map === undefined) {
|
||||
return
|
||||
}
|
||||
const oldBounds = map.getBounds()
|
||||
const e = 0.0000001
|
||||
const hasDiff =
|
||||
Math.abs(oldBounds.getWest() - bounds.getWest()) > e &&
|
||||
Math.abs(oldBounds.getEast() - bounds.getEast()) > e &&
|
||||
Math.abs(oldBounds.getNorth() - bounds.getNorth()) > e &&
|
||||
Math.abs(oldBounds.getSouth() - bounds.getSouth()) > e
|
||||
if (!hasDiff) {
|
||||
return
|
||||
}
|
||||
map.fitBounds(bounds.toLngLat())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { Map as MlMap } from "maplibre-gl"
|
||||
import { GeoJSONSource, Marker } from "maplibre-gl"
|
||||
import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
|
||||
|
@ -14,10 +14,13 @@ import LineRenderingConfig from "../../Models/ThemeConfig/LineRenderingConfig"
|
|||
import { Utils } from "../../Utils"
|
||||
import * as range_layer from "../../assets/layers/range/range.json"
|
||||
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
|
||||
import FilteredLayer from "../../Models/FilteredLayer"
|
||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||
|
||||
class PointRenderingLayer {
|
||||
private readonly _config: PointRenderingConfig
|
||||
private readonly _fetchStore?: (id: string) => Store<OsmTags>
|
||||
private readonly _fetchStore?: (id: string) => Store<Record<string, string>>
|
||||
private readonly _map: MlMap
|
||||
private readonly _onClick: (feature: Feature) => void
|
||||
private readonly _allMarkers: Map<string, Marker> = new Map<string, Marker>()
|
||||
|
@ -27,7 +30,7 @@ class PointRenderingLayer {
|
|||
features: FeatureSource,
|
||||
config: PointRenderingConfig,
|
||||
visibility?: Store<boolean>,
|
||||
fetchStore?: (id: string) => Store<OsmTags>,
|
||||
fetchStore?: (id: string) => Store<Record<string, string>>,
|
||||
onClick?: (feature: Feature) => void
|
||||
) {
|
||||
this._config = config
|
||||
|
@ -96,7 +99,7 @@ class PointRenderingLayer {
|
|||
}
|
||||
|
||||
private addPoint(feature: Feature, loc: [number, number]): Marker {
|
||||
let store: Store<OsmTags>
|
||||
let store: Store<Record<string, string>>
|
||||
if (this._fetchStore) {
|
||||
store = this._fetchStore(feature.properties.id)
|
||||
} else {
|
||||
|
@ -143,7 +146,7 @@ class LineRenderingLayer {
|
|||
private readonly _map: MlMap
|
||||
private readonly _config: LineRenderingConfig
|
||||
private readonly _visibility?: Store<boolean>
|
||||
private readonly _fetchStore?: (id: string) => Store<OsmTags>
|
||||
private readonly _fetchStore?: (id: string) => Store<Record<string, string>>
|
||||
private readonly _onClick?: (feature: Feature) => void
|
||||
private readonly _layername: string
|
||||
private readonly _listenerInstalledOn: Set<string> = new Set<string>()
|
||||
|
@ -154,7 +157,7 @@ class LineRenderingLayer {
|
|||
layername: string,
|
||||
config: LineRenderingConfig,
|
||||
visibility?: Store<boolean>,
|
||||
fetchStore?: (id: string) => Store<OsmTags>,
|
||||
fetchStore?: (id: string) => Store<Record<string, string>>,
|
||||
onClick?: (feature: Feature) => void
|
||||
) {
|
||||
this._layername = layername
|
||||
|
@ -212,9 +215,10 @@ class LineRenderingLayer {
|
|||
promoteId: "id",
|
||||
})
|
||||
// @ts-ignore
|
||||
const linelayer = this._layername + "_line"
|
||||
map.addLayer({
|
||||
source: this._layername,
|
||||
id: this._layername + "_line",
|
||||
id: linelayer,
|
||||
type: "line",
|
||||
paint: {
|
||||
"line-color": ["feature-state", "color"],
|
||||
|
@ -227,9 +231,10 @@ class LineRenderingLayer {
|
|||
},
|
||||
})
|
||||
|
||||
const polylayer = this._layername + "_polygon"
|
||||
map.addLayer({
|
||||
source: this._layername,
|
||||
id: this._layername + "_polygon",
|
||||
id: polylayer,
|
||||
type: "fill",
|
||||
filter: ["in", ["geometry-type"], ["literal", ["Polygon", "MultiPolygon"]]],
|
||||
layout: {},
|
||||
|
@ -238,6 +243,11 @@ class LineRenderingLayer {
|
|||
"fill-opacity": 0.1,
|
||||
},
|
||||
})
|
||||
|
||||
this._visibility.addCallbackAndRunD((visible) => {
|
||||
map.setLayoutProperty(linelayer, "visibility", visible ? "visible" : "none")
|
||||
map.setLayoutProperty(polylayer, "visibility", visible ? "visible" : "none")
|
||||
})
|
||||
} else {
|
||||
src.setData({
|
||||
type: "FeatureCollection",
|
||||
|
@ -295,6 +305,24 @@ export default class ShowDataLayer {
|
|||
map.addCallbackAndRunD((map) => self.initDrawFeatures(map))
|
||||
}
|
||||
|
||||
public static showMultipleLayers(
|
||||
mlmap: UIEventSource<MlMap>,
|
||||
features: FeatureSource,
|
||||
layers: LayerConfig[],
|
||||
options?: Partial<ShowDataLayerOptions>
|
||||
) {
|
||||
const perLayer = new PerLayerFeatureSourceSplitter(
|
||||
layers.map((l) => new FilteredLayer(l)),
|
||||
new StaticFeatureSource(features)
|
||||
)
|
||||
perLayer.forEach((fs) => {
|
||||
new ShowDataLayer(mlmap, {
|
||||
layer: fs.layer.layerDef,
|
||||
features: fs,
|
||||
...(options ?? {}),
|
||||
})
|
||||
})
|
||||
}
|
||||
public static showRange(
|
||||
map: Store<MlMap>,
|
||||
features: FeatureSource,
|
||||
|
@ -318,8 +346,11 @@ export default class ShowDataLayer {
|
|||
}
|
||||
|
||||
private initDrawFeatures(map: MlMap) {
|
||||
const { features, doShowLayer, fetchStore, selectedElement } = this._options
|
||||
const onClick = (feature: Feature) => selectedElement?.setData(feature)
|
||||
let { features, doShowLayer, fetchStore, selectedElement, selectedLayer } = this._options
|
||||
const onClick = (feature: Feature) => {
|
||||
selectedElement?.setData(feature)
|
||||
selectedLayer?.setData(this._options.layer)
|
||||
}
|
||||
for (let i = 0; i < this._options.layer.lineRendering.length; i++) {
|
||||
const lineRenderingConfig = this._options.layer.lineRendering[i]
|
||||
new LineRenderingLayer(
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import FeatureSource from "../../Logic/FeatureSource/FeatureSource"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { OsmTags } from "../../Models/OsmFeature"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import { Feature } from "geojson"
|
||||
|
||||
export interface ShowDataLayerOptions {
|
||||
/**
|
||||
|
@ -11,7 +13,12 @@ export interface ShowDataLayerOptions {
|
|||
* Indication of the current selected element; overrides some filters.
|
||||
* When a feature is tapped, the feature will be put in there
|
||||
*/
|
||||
selectedElement?: UIEventSource<any>
|
||||
selectedElement?: UIEventSource<Feature>
|
||||
|
||||
/**
|
||||
* When a feature of this layer is tapped, the layer will be marked
|
||||
*/
|
||||
selectedLayer?: UIEventSource<LayerConfig>
|
||||
|
||||
/**
|
||||
* If set, zoom to the features when initially loaded and when they are changed
|
||||
|
@ -26,5 +33,5 @@ export interface ShowDataLayerOptions {
|
|||
* Function which fetches the relevant store.
|
||||
* If given, the map will update when a property is changed
|
||||
*/
|
||||
fetchStore?: (id: string) => UIEventSource<OsmTags>
|
||||
fetchStore?: (id: string) => Store<Record<string, string>>
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/**
|
||||
* SHows geojson on the given leaflet map, but attempts to figure out the correct layer first
|
||||
*/
|
||||
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: FilteredLayer[]
|
||||
globalFilters?: Store<GlobalFilter[]>
|
||||
}
|
||||
) {
|
||||
new PerLayerFeatureSourceSplitter(
|
||||
new ImmutableStore(options.layers),
|
||||
(features, layer) => {
|
||||
const newOptions = {
|
||||
...options,
|
||||
layer: layer.layerDef,
|
||||
features: new FilteringFeatureSource(
|
||||
layer,
|
||||
features,
|
||||
options.fetchStore,
|
||||
options.globalFilters
|
||||
),
|
||||
}
|
||||
new ShowDataLayer(map, newOptions)
|
||||
},
|
||||
options.features
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue