refactoring: split all the states
This commit is contained in:
parent
4d48b1cf2b
commit
8e2f04c0d0
32 changed files with 411 additions and 395 deletions
|
@ -148,7 +148,7 @@ export default class OverpassFeatureSource implements FeatureSource {
|
||||||
if (typeof layer === "string") {
|
if (typeof layer === "string") {
|
||||||
throw "A layer was not expanded!"
|
throw "A layer was not expanded!"
|
||||||
}
|
}
|
||||||
if (Constants.priviliged_layers.indexOf(layer.id) >= 0) {
|
if (layer.source === undefined) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (this.state.locationControl.data.zoom < layer.minzoom) {
|
if (this.state.locationControl.data.zoom < layer.minzoom) {
|
||||||
|
|
|
@ -7,14 +7,9 @@ import { ElementStorage } from "../ElementStorage"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
|
|
||||||
export default class TitleHandler {
|
export default class TitleHandler {
|
||||||
constructor(state: {
|
constructor(selectedElement: Store<any>, layout: LayoutConfig, allElements: ElementStorage) {
|
||||||
selectedElement: Store<any>
|
const currentTitle: Store<string> = selectedElement.map(
|
||||||
layoutToUse: LayoutConfig
|
|
||||||
allElements: ElementStorage
|
|
||||||
}) {
|
|
||||||
const currentTitle: Store<string> = state.selectedElement.map(
|
|
||||||
(selected) => {
|
(selected) => {
|
||||||
const layout = state.layoutToUse
|
|
||||||
const defaultTitle = layout?.title?.txt ?? "MapComplete"
|
const defaultTitle = layout?.title?.txt ?? "MapComplete"
|
||||||
|
|
||||||
if (selected === undefined) {
|
if (selected === undefined) {
|
||||||
|
@ -28,8 +23,7 @@ export default class TitleHandler {
|
||||||
}
|
}
|
||||||
if (layer.source.osmTags.matchesProperties(tags)) {
|
if (layer.source.osmTags.matchesProperties(tags)) {
|
||||||
const tagsSource =
|
const tagsSource =
|
||||||
state.allElements.getEventSourceById(tags.id) ??
|
allElements.getEventSourceById(tags.id) ?? new UIEventSource<any>(tags)
|
||||||
new UIEventSource<any>(tags)
|
|
||||||
const title = new TagRenderingAnswer(tagsSource, layer.title, {})
|
const title = new TagRenderingAnswer(tagsSource, layer.title, {})
|
||||||
return (
|
return (
|
||||||
new Combine([defaultTitle, " | ", title]).ConstructElement()
|
new Combine([defaultTitle, " | ", title]).ConstructElement()
|
||||||
|
|
|
@ -189,7 +189,7 @@ export class BBox {
|
||||||
public asGeoJson<T>(properties: T): Feature<Polygon, T> {
|
public asGeoJson<T>(properties: T): Feature<Polygon, T> {
|
||||||
return {
|
return {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties: properties,
|
properties,
|
||||||
geometry: this.asGeometry(),
|
geometry: this.asGeometry(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default class FeaturePipeline {
|
||||||
public readonly relationTracker: RelationsTracker
|
public readonly relationTracker: RelationsTracker
|
||||||
/**
|
/**
|
||||||
* Keeps track of all raw OSM-nodes.
|
* Keeps track of all raw OSM-nodes.
|
||||||
* Only initialized if 'type_node' is defined as layer
|
* Only initialized if `ReplaceGeometryAction` is needed somewhere
|
||||||
*/
|
*/
|
||||||
public readonly fullNodeDatabase?: FullNodeDatabaseSource
|
public readonly fullNodeDatabase?: FullNodeDatabaseSource
|
||||||
private readonly overpassUpdater: OverpassFeatureSource
|
private readonly overpassUpdater: OverpassFeatureSource
|
||||||
|
@ -132,14 +132,6 @@ export default class FeaturePipeline {
|
||||||
// We do not mark as visited here, this is the responsability of the code near the actual loader (e.g. overpassLoader and OSMApiFeatureLoader)
|
// We do not mark as visited here, this is the responsability of the code near the actual loader (e.g. overpassLoader and OSMApiFeatureLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePriviligedFeatureSource(src: FeatureSourceForLayer & Tiled) {
|
|
||||||
// Passthrough to passed function, except that it registers as well
|
|
||||||
handleFeatureSource(src)
|
|
||||||
src.features.addCallbackAndRunD((fs) => {
|
|
||||||
fs.forEach((ff) => state.allElements.addOrGetElement(<any>ff))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const filteredLayer of state.filteredLayers.data) {
|
for (const filteredLayer of state.filteredLayers.data) {
|
||||||
const id = filteredLayer.layerDef.id
|
const id = filteredLayer.layerDef.id
|
||||||
const source = filteredLayer.layerDef.source
|
const source = filteredLayer.layerDef.source
|
||||||
|
@ -160,36 +152,6 @@ export default class FeaturePipeline {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === "selected_element") {
|
|
||||||
handlePriviligedFeatureSource(state.selectedElementsLayer)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id === "gps_location") {
|
|
||||||
handlePriviligedFeatureSource(state.currentUserLocation)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id === "gps_location_history") {
|
|
||||||
handlePriviligedFeatureSource(state.historicalUserLocations)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id === "gps_track") {
|
|
||||||
handlePriviligedFeatureSource(state.historicalUserLocationsTrack)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id === "home_location") {
|
|
||||||
handlePriviligedFeatureSource(state.homeLocation)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id === "current_view") {
|
|
||||||
handlePriviligedFeatureSource(state.currentView)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const localTileSaver = new SaveTileToLocalStorageActor(filteredLayer)
|
const localTileSaver = new SaveTileToLocalStorageActor(filteredLayer)
|
||||||
this.localStorageSavers.set(filteredLayer.layerDef.id, localTileSaver)
|
this.localStorageSavers.set(filteredLayer.layerDef.id, localTileSaver)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import ScrollableFullScreen from "../../UI/Base/ScrollableFullScreen"
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
import ShowDataLayer from "../../UI/Map/ShowDataLayer"
|
import ShowDataLayer from "../../UI/Map/ShowDataLayer"
|
||||||
|
|
||||||
export default class FeaturePipelineState extends MapState {
|
export default class FeaturePipelineState {
|
||||||
/**
|
/**
|
||||||
* The piece of code which fetches data from various sources and shows it on the background map
|
* The piece of code which fetches data from various sources and shows it on the background map
|
||||||
*/
|
*/
|
||||||
|
@ -27,8 +27,6 @@ export default class FeaturePipelineState extends MapState {
|
||||||
>()
|
>()
|
||||||
|
|
||||||
constructor(layoutToUse: LayoutConfig) {
|
constructor(layoutToUse: LayoutConfig) {
|
||||||
super(layoutToUse)
|
|
||||||
|
|
||||||
const clustering = layoutToUse?.clustering
|
const clustering = layoutToUse?.clustering
|
||||||
this.featureAggregator = TileHierarchyAggregator.createHierarchy(this)
|
this.featureAggregator = TileHierarchyAggregator.createHierarchy(this)
|
||||||
const clusterCounter = this.featureAggregator
|
const clusterCounter = this.featureAggregator
|
||||||
|
|
145
Logic/State/LayerState.ts
Normal file
145
Logic/State/LayerState.ts
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import { UIEventSource } from "../UIEventSource"
|
||||||
|
import { GlobalFilter } from "../../Models/GlobalFilter"
|
||||||
|
import FilteredLayer, { FilterState } from "../../Models/FilteredLayer"
|
||||||
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
|
import { OsmConnection } from "../Osm/OsmConnection"
|
||||||
|
import { LocalStorageSource } from "../Web/LocalStorageSource"
|
||||||
|
import { QueryParameters } from "../Web/QueryParameters"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The layer state keeps track of:
|
||||||
|
* - Which layers are enabled
|
||||||
|
* - Which filters are used, including 'global' filters
|
||||||
|
*/
|
||||||
|
export default class LayerState {
|
||||||
|
/**
|
||||||
|
* Filters which apply onto all layers
|
||||||
|
*/
|
||||||
|
public readonly globalFilters: UIEventSource<GlobalFilter[]> = new UIEventSource(
|
||||||
|
[],
|
||||||
|
"globalFilters"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which layers are enabled in the current theme and what filters are applied onto them
|
||||||
|
*/
|
||||||
|
public readonly filteredLayers: Map<string, FilteredLayer>
|
||||||
|
private readonly osmConnection: OsmConnection
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param osmConnection
|
||||||
|
* @param layers
|
||||||
|
* @param context: the context, probably the name of the theme. Used to disambiguate the upstream user preference
|
||||||
|
*/
|
||||||
|
constructor(osmConnection: OsmConnection, layers: LayerConfig[], context: string) {
|
||||||
|
this.osmConnection = osmConnection
|
||||||
|
this.filteredLayers = new Map()
|
||||||
|
for (const layer of layers) {
|
||||||
|
this.filteredLayers.set(layer.id, this.initFilteredLayer(layer, context))
|
||||||
|
}
|
||||||
|
layers.forEach((l) => this.linkFilterStates(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getPref(
|
||||||
|
osmConnection: OsmConnection,
|
||||||
|
key: string,
|
||||||
|
layer: LayerConfig
|
||||||
|
): UIEventSource<boolean> {
|
||||||
|
return osmConnection.GetPreference(key, layer.shownByDefault + "").sync(
|
||||||
|
(v) => {
|
||||||
|
if (v === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return v === "true"
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
(b) => {
|
||||||
|
if (b === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return "" + b
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* INitializes a filtered layer for the given layer.
|
||||||
|
* @param layer
|
||||||
|
* @param context: probably the theme-name. This is used to disambiguate the user settings; e.g. when using the same layer in different contexts
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private initFilteredLayer(layer: LayerConfig, context: string): FilteredLayer | undefined {
|
||||||
|
let isDisplayed: UIEventSource<boolean>
|
||||||
|
const osmConnection = this.osmConnection
|
||||||
|
if (layer.syncSelection === "local") {
|
||||||
|
isDisplayed = LocalStorageSource.GetParsed(
|
||||||
|
context + "-layer-" + layer.id + "-enabled",
|
||||||
|
layer.shownByDefault
|
||||||
|
)
|
||||||
|
} else if (layer.syncSelection === "theme-only") {
|
||||||
|
isDisplayed = LayerState.getPref(
|
||||||
|
osmConnection,
|
||||||
|
context + "-layer-" + layer.id + "-enabled",
|
||||||
|
layer
|
||||||
|
)
|
||||||
|
} else if (layer.syncSelection === "global") {
|
||||||
|
isDisplayed = LayerState.getPref(osmConnection, "layer-" + layer.id + "-enabled", layer)
|
||||||
|
} else {
|
||||||
|
isDisplayed = QueryParameters.GetBooleanQueryParameter(
|
||||||
|
"layer-" + layer.id,
|
||||||
|
layer.shownByDefault,
|
||||||
|
"Wether or not layer " + layer.id + " is shown"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const flayer: FilteredLayer = {
|
||||||
|
isDisplayed,
|
||||||
|
layerDef: layer,
|
||||||
|
appliedFilters: new UIEventSource<Map<string, FilterState>>(
|
||||||
|
new Map<string, FilterState>()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
layer.filters?.forEach((filterConfig) => {
|
||||||
|
const stateSrc = filterConfig.initState()
|
||||||
|
|
||||||
|
stateSrc.addCallbackAndRun((state) =>
|
||||||
|
flayer.appliedFilters.data.set(filterConfig.id, state)
|
||||||
|
)
|
||||||
|
flayer.appliedFilters
|
||||||
|
.map((dict) => dict.get(filterConfig.id))
|
||||||
|
.addCallback((state) => stateSrc.setData(state))
|
||||||
|
})
|
||||||
|
|
||||||
|
return flayer
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some layers copy the filter state of another layer - this is quite often the case for 'sibling'-layers,
|
||||||
|
* (where two variations of the same layer are used, e.g. a specific type of shop on all zoom levels and all shops on high zoom).
|
||||||
|
*
|
||||||
|
* This methods links those states for the given layer
|
||||||
|
*/
|
||||||
|
private linkFilterStates(layer: LayerConfig) {
|
||||||
|
if (layer.filterIsSameAs === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const toReuse = this.filteredLayers.get(layer.filterIsSameAs)
|
||||||
|
if (toReuse === undefined) {
|
||||||
|
throw (
|
||||||
|
"Error in layer " +
|
||||||
|
layer.id +
|
||||||
|
": it defines that it should be use the filters of " +
|
||||||
|
layer.filterIsSameAs +
|
||||||
|
", but this layer was not loaded"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
console.warn(
|
||||||
|
"Linking filter and isDisplayed-states of " + layer.id + " and " + layer.filterIsSameAs
|
||||||
|
)
|
||||||
|
this.filteredLayers.set(layer.id, {
|
||||||
|
isDisplayed: toReuse.isDisplayed,
|
||||||
|
layerDef: layer,
|
||||||
|
appliedFilters: toReuse.appliedFilters,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +1,19 @@
|
||||||
import { Store, UIEventSource } from "../UIEventSource"
|
import { Store, UIEventSource } from "../UIEventSource"
|
||||||
import Attribution from "../../UI/BigComponents/Attribution"
|
import FilteredLayer from "../../Models/FilteredLayer"
|
||||||
import BaseUIElement from "../../UI/BaseUIElement"
|
|
||||||
import FilteredLayer, { FilterState } from "../../Models/FilteredLayer"
|
|
||||||
import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig"
|
import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig"
|
||||||
import { QueryParameters } from "../Web/QueryParameters"
|
import { QueryParameters } from "../Web/QueryParameters"
|
||||||
import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer"
|
import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer"
|
||||||
import { FeatureSourceForLayer, Tiled } from "../FeatureSource/FeatureSource"
|
import FeatureSource, { FeatureSourceForLayer, Tiled } from "../FeatureSource/FeatureSource"
|
||||||
import { LocalStorageSource } from "../Web/LocalStorageSource"
|
|
||||||
import TitleHandler from "../Actors/TitleHandler"
|
|
||||||
import { BBox } from "../BBox"
|
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
|
||||||
import StaticFeatureSource, {
|
import StaticFeatureSource, {
|
||||||
TiledStaticFeatureSource,
|
TiledStaticFeatureSource,
|
||||||
} from "../FeatureSource/Sources/StaticFeatureSource"
|
} from "../FeatureSource/Sources/StaticFeatureSource"
|
||||||
import { OsmConnection } from "../Osm/OsmConnection"
|
|
||||||
import { Feature } from "geojson"
|
import { Feature } from "geojson"
|
||||||
import { Map as MlMap } from "maplibre-gl"
|
|
||||||
import { GlobalFilter } from "../../Models/GlobalFilter"
|
|
||||||
import { MapProperties } from "../../Models/MapProperties"
|
import { MapProperties } from "../../Models/MapProperties"
|
||||||
import ShowDataLayer from "../../UI/Map/ShowDataLayer"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all the leaflet-map related state
|
* Contains all the leaflet-map related state
|
||||||
*/
|
*/
|
||||||
export default class MapState {
|
export default class MapState {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last location where a click was registered
|
* Last location where a click was registered
|
||||||
*/
|
*/
|
||||||
|
@ -46,21 +34,6 @@ export default class MapState {
|
||||||
*/
|
*/
|
||||||
public selectedElementsLayer: FeatureSourceForLayer & Tiled
|
public selectedElementsLayer: FeatureSourceForLayer & Tiled
|
||||||
|
|
||||||
public readonly mainMapObject: BaseUIElement
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Which layers are enabled in the current theme and what filters are applied onto them
|
|
||||||
*/
|
|
||||||
public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>(
|
|
||||||
[],
|
|
||||||
"filteredLayers"
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters which apply onto all layers
|
|
||||||
*/
|
|
||||||
public globalFilters: UIEventSource<GlobalFilter[]> = new UIEventSource([], "globalFilters")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Which overlays are shown
|
* Which overlays are shown
|
||||||
*/
|
*/
|
||||||
|
@ -80,16 +53,6 @@ export default class MapState {
|
||||||
this.backgroundLayer = new UIEventSource<BaseLayer>(defaultLayer)
|
this.backgroundLayer = new UIEventSource<BaseLayer>(defaultLayer)
|
||||||
this.backgroundLayer.addCallbackAndRunD((layer) => self.backgroundLayerId.setData(layer.id))
|
this.backgroundLayer.addCallbackAndRunD((layer) => self.backgroundLayerId.setData(layer.id))
|
||||||
|
|
||||||
// Will write into this.leafletMap
|
|
||||||
this.mainMapObject = Minimap.createMiniMap({
|
|
||||||
background: this.backgroundLayer,
|
|
||||||
location: this.locationControl,
|
|
||||||
leafletMap: this.leafletMap,
|
|
||||||
bounds: this.currentBounds,
|
|
||||||
attribution: attr,
|
|
||||||
lastClickLocation: this.LastClickLocation,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.overlayToggles =
|
this.overlayToggles =
|
||||||
this.layoutToUse?.tileLayerSources
|
this.layoutToUse?.tileLayerSources
|
||||||
?.filter((c) => c.name !== undefined)
|
?.filter((c) => c.name !== undefined)
|
||||||
|
@ -101,16 +64,11 @@ export default class MapState {
|
||||||
"Wether or not the overlay " + c.id + " is shown"
|
"Wether or not the overlay " + c.id + " is shown"
|
||||||
),
|
),
|
||||||
})) ?? []
|
})) ?? []
|
||||||
this.filteredLayers = new UIEventSource<FilteredLayer[]>(
|
|
||||||
MapState.InitializeFilteredLayers(this.layoutToUse, this.osmConnection)
|
|
||||||
)
|
|
||||||
|
|
||||||
this.AddAllOverlaysToMap(this.leafletMap)
|
this.AddAllOverlaysToMap(this.leafletMap)
|
||||||
|
|
||||||
this.initCurrentView()
|
this.initCurrentView()
|
||||||
this.initSelectedElement()
|
this.initSelectedElement()
|
||||||
|
|
||||||
new TitleHandler(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddAllOverlaysToMap(leafletMap: UIEventSource<any>) {
|
public AddAllOverlaysToMap(leafletMap: UIEventSource<any>) {
|
||||||
|
@ -128,48 +86,23 @@ export default class MapState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static initCurrentView(mapproperties: MapProperties): FeatureSource {
|
||||||
private initCurrentView() {
|
|
||||||
let currentViewLayer: FilteredLayer = this.filteredLayers.data.filter(
|
|
||||||
(l) => l.layerDef.id === "current_view"
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
if (currentViewLayer === undefined) {
|
|
||||||
// This layer is not needed by the theme and thus unloaded
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let i = 0
|
let i = 0
|
||||||
const self = this
|
const features: Store<Feature[]> = mapproperties.bounds.map((bounds) => {
|
||||||
const features: Store<Feature[]> = this.currentBounds.map((bounds) => {
|
|
||||||
if (bounds === undefined) {
|
if (bounds === undefined) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
const feature = {
|
return [
|
||||||
type: "Feature",
|
bounds.asGeoJson({
|
||||||
properties: {
|
|
||||||
id: "current_view-" + i,
|
id: "current_view-" + i,
|
||||||
current_view: "yes",
|
current_view: "yes",
|
||||||
zoom: "" + self.locationControl.data.zoom,
|
zoom: "" + mapproperties.zoom.data,
|
||||||
},
|
}),
|
||||||
geometry: {
|
]
|
||||||
type: "Polygon",
|
|
||||||
coordinates: [
|
|
||||||
[
|
|
||||||
[bounds.maxLon, bounds.maxLat],
|
|
||||||
[bounds.minLon, bounds.maxLat],
|
|
||||||
[bounds.minLon, bounds.minLat],
|
|
||||||
[bounds.maxLon, bounds.minLat],
|
|
||||||
[bounds.maxLon, bounds.maxLat],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return [feature]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.currentView = new TiledStaticFeatureSource(features, currentViewLayer)
|
return new StaticFeatureSource(features)
|
||||||
}
|
}
|
||||||
|
|
||||||
private initSelectedElement() {
|
private initSelectedElement() {
|
||||||
|
@ -197,113 +130,4 @@ export default class MapState {
|
||||||
})
|
})
|
||||||
this.selectedElementsLayer = new TiledStaticFeatureSource(store, layerDef)
|
this.selectedElementsLayer = new TiledStaticFeatureSource(store, layerDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getPref(
|
|
||||||
osmConnection: OsmConnection,
|
|
||||||
key: string,
|
|
||||||
layer: LayerConfig
|
|
||||||
): UIEventSource<boolean> {
|
|
||||||
return osmConnection.GetPreference(key, layer.shownByDefault + "").sync(
|
|
||||||
(v) => {
|
|
||||||
if (v === undefined) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
return v === "true"
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
(b) => {
|
|
||||||
if (b === undefined) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
return "" + b
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InitializeFilteredLayers(
|
|
||||||
layoutToUse: { layers: LayerConfig[]; id: string },
|
|
||||||
osmConnection: OsmConnection
|
|
||||||
): FilteredLayer[] {
|
|
||||||
if (layoutToUse === undefined) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
const flayers: FilteredLayer[] = []
|
|
||||||
for (const layer of layoutToUse.layers) {
|
|
||||||
let isDisplayed: UIEventSource<boolean>
|
|
||||||
if (layer.syncSelection === "local") {
|
|
||||||
isDisplayed = LocalStorageSource.GetParsed(
|
|
||||||
layoutToUse.id + "-layer-" + layer.id + "-enabled",
|
|
||||||
layer.shownByDefault
|
|
||||||
)
|
|
||||||
} else if (layer.syncSelection === "theme-only") {
|
|
||||||
isDisplayed = MapState.getPref(
|
|
||||||
osmConnection,
|
|
||||||
layoutToUse.id + "-layer-" + layer.id + "-enabled",
|
|
||||||
layer
|
|
||||||
)
|
|
||||||
} else if (layer.syncSelection === "global") {
|
|
||||||
isDisplayed = MapState.getPref(
|
|
||||||
osmConnection,
|
|
||||||
"layer-" + layer.id + "-enabled",
|
|
||||||
layer
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
isDisplayed = QueryParameters.GetBooleanQueryParameter(
|
|
||||||
"layer-" + layer.id,
|
|
||||||
layer.shownByDefault,
|
|
||||||
"Wether or not layer " + layer.id + " is shown"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const flayer: FilteredLayer = {
|
|
||||||
isDisplayed,
|
|
||||||
layerDef: layer,
|
|
||||||
appliedFilters: new UIEventSource<Map<string, FilterState>>(
|
|
||||||
new Map<string, FilterState>()
|
|
||||||
),
|
|
||||||
}
|
|
||||||
layer.filters.forEach((filterConfig) => {
|
|
||||||
const stateSrc = filterConfig.initState()
|
|
||||||
|
|
||||||
stateSrc.addCallbackAndRun((state) =>
|
|
||||||
flayer.appliedFilters.data.set(filterConfig.id, state)
|
|
||||||
)
|
|
||||||
flayer.appliedFilters
|
|
||||||
.map((dict) => dict.get(filterConfig.id))
|
|
||||||
.addCallback((state) => stateSrc.setData(state))
|
|
||||||
})
|
|
||||||
|
|
||||||
flayers.push(flayer)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const layer of layoutToUse.layers) {
|
|
||||||
if (layer.filterIsSameAs === undefined) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
const toReuse = flayers.find((l) => l.layerDef.id === layer.filterIsSameAs)
|
|
||||||
if (toReuse === undefined) {
|
|
||||||
throw (
|
|
||||||
"Error in layer " +
|
|
||||||
layer.id +
|
|
||||||
": it defines that it should be use the filters of " +
|
|
||||||
layer.filterIsSameAs +
|
|
||||||
", but this layer was not loaded"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
console.warn(
|
|
||||||
"Linking filter and isDisplayed-states of " +
|
|
||||||
layer.id +
|
|
||||||
" and " +
|
|
||||||
layer.filterIsSameAs
|
|
||||||
)
|
|
||||||
const selfLayer = flayers.findIndex((l) => l.layerDef.id === layer.id)
|
|
||||||
flayers[selfLayer] = {
|
|
||||||
isDisplayed: toReuse.isDisplayed,
|
|
||||||
layerDef: layer,
|
|
||||||
appliedFilters: toReuse.appliedFilters,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return flayers
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Locale from "../../UI/i18n/Locale"
|
||||||
import { Changes } from "../Osm/Changes"
|
import { Changes } from "../Osm/Changes"
|
||||||
import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource"
|
import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource"
|
||||||
import FeatureSource from "../FeatureSource/FeatureSource"
|
import FeatureSource from "../FeatureSource/FeatureSource"
|
||||||
|
import { Feature } from "geojson"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The part of the state which keeps track of user-related stuff, e.g. the OSM-connection,
|
* The part of the state which keeps track of user-related stuff, e.g. the OSM-connection,
|
||||||
|
@ -182,7 +183,7 @@ export default class UserRelatedState {
|
||||||
|
|
||||||
private initHomeLocation(): FeatureSource {
|
private initHomeLocation(): FeatureSource {
|
||||||
const empty = []
|
const empty = []
|
||||||
const feature = Stores.ListStabilized(
|
const feature: Store<Feature[]> = Stores.ListStabilized(
|
||||||
this.osmConnection.userDetails.map((userDetails) => {
|
this.osmConnection.userDetails.map((userDetails) => {
|
||||||
if (userDetails === undefined) {
|
if (userDetails === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -198,21 +199,18 @@ export default class UserRelatedState {
|
||||||
return empty
|
return empty
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
{
|
<Feature>{
|
||||||
feature: {
|
type: "Feature",
|
||||||
type: "Feature",
|
properties: {
|
||||||
properties: {
|
id: "home",
|
||||||
id: "home",
|
"user:home": "yes",
|
||||||
"user:home": "yes",
|
_lon: homeLonLat[0],
|
||||||
_lon: homeLonLat[0],
|
_lat: homeLonLat[1],
|
||||||
_lat: homeLonLat[1],
|
},
|
||||||
},
|
geometry: {
|
||||||
geometry: {
|
type: "Point",
|
||||||
type: "Point",
|
coordinates: homeLonLat,
|
||||||
coordinates: homeLonLat,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
freshness: new Date(),
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,31 +26,30 @@ export default class Constants {
|
||||||
// Doesn't support nwr: "https://overpass.openstreetmap.fr/api/interpreter"
|
// Doesn't support nwr: "https://overpass.openstreetmap.fr/api/interpreter"
|
||||||
]
|
]
|
||||||
|
|
||||||
public static readonly added_by_default: string[] = [
|
public static readonly added_by_default = [
|
||||||
"selected_element",
|
"selected_element",
|
||||||
"gps_location",
|
"gps_location",
|
||||||
"gps_location_history",
|
"gps_location_history",
|
||||||
"home_location",
|
"home_location",
|
||||||
"gps_track",
|
"gps_track",
|
||||||
]
|
"range",
|
||||||
public static readonly no_include: string[] = [
|
] as const
|
||||||
|
/**
|
||||||
|
* Special layers which are not included in a theme by default
|
||||||
|
*/
|
||||||
|
public static readonly no_include = [
|
||||||
"conflation",
|
"conflation",
|
||||||
"left_right_style",
|
|
||||||
"split_point",
|
"split_point",
|
||||||
"current_view",
|
"current_view",
|
||||||
"matchpoint",
|
"matchpoint",
|
||||||
]
|
] as const
|
||||||
/**
|
/**
|
||||||
* Layer IDs of layers which have special properties through built-in hooks
|
* Layer IDs of layers which have special properties through built-in hooks
|
||||||
*/
|
*/
|
||||||
public static readonly priviliged_layers: string[] = [
|
public static readonly priviliged_layers = [
|
||||||
...Constants.added_by_default,
|
...Constants.added_by_default,
|
||||||
"type_node",
|
|
||||||
"note",
|
|
||||||
"import_candidate",
|
|
||||||
"direction",
|
|
||||||
...Constants.no_include,
|
...Constants.no_include,
|
||||||
]
|
] as const
|
||||||
|
|
||||||
// The user journey states thresholds when a new feature gets unlocked
|
// The user journey states thresholds when a new feature gets unlocked
|
||||||
public static userJourney = {
|
public static userJourney = {
|
||||||
|
|
|
@ -255,7 +255,7 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
|
||||||
const creator = new CreateNoteImportLayer()
|
const creator = new CreateNoteImportLayer()
|
||||||
for (let i1 = 0; i1 < allLayers.length; i1++) {
|
for (let i1 = 0; i1 < allLayers.length; i1++) {
|
||||||
const layer = allLayers[i1]
|
const layer = allLayers[i1]
|
||||||
if (Constants.priviliged_layers.indexOf(layer.id) >= 0) {
|
if (layer.source === undefined) {
|
||||||
// Priviliged layers are skipped
|
// Priviliged layers are skipped
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -600,7 +600,7 @@ class PreparePersonalTheme extends DesugaringStep<LayoutConfigJson> {
|
||||||
// All other preparations are done by the 'override-all'-block in personal.json
|
// All other preparations are done by the 'override-all'-block in personal.json
|
||||||
|
|
||||||
json.layers = Array.from(this._state.sharedLayers.keys())
|
json.layers = Array.from(this._state.sharedLayers.keys())
|
||||||
.filter((l) => Constants.priviliged_layers.indexOf(l) < 0)
|
.filter((l) => this._state.sharedLayers.get(l).source !== null)
|
||||||
.filter((l) => this._state.publicLayers.has(l))
|
.filter((l) => this._state.publicLayers.has(l))
|
||||||
return {
|
return {
|
||||||
result: json,
|
result: json,
|
||||||
|
|
|
@ -845,7 +845,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.description === undefined) {
|
if (json.description === undefined) {
|
||||||
if (Constants.priviliged_layers.indexOf(json.id) >= 0) {
|
if (typeof json.source === null) {
|
||||||
errors.push(context + ": A priviliged layer must have a description")
|
errors.push(context + ": A priviliged layer must have a description")
|
||||||
} else {
|
} else {
|
||||||
warnings.push(context + ": A builtin layer should have a description")
|
warnings.push(context + ": A builtin layer should have a description")
|
||||||
|
@ -882,6 +882,9 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.presets !== undefined) {
|
if (json.presets !== undefined) {
|
||||||
|
if (typeof json.source === "string") {
|
||||||
|
throw "A special layer cannot have presets"
|
||||||
|
}
|
||||||
// Check that a preset will be picked up by the layer itself
|
// Check that a preset will be picked up by the layer itself
|
||||||
const baseTags = TagUtils.Tag(json.source.osmTags)
|
const baseTags = TagUtils.Tag(json.source.osmTags)
|
||||||
for (let i = 0; i < json.presets.length; i++) {
|
for (let i = 0; i < json.presets.length; i++) {
|
||||||
|
|
|
@ -77,4 +77,15 @@ export default interface PointRenderingConfigJson {
|
||||||
* A snippet of css-classes. They can be space-separated
|
* A snippet of css-classes. They can be space-separated
|
||||||
*/
|
*/
|
||||||
cssClasses?: string | TagRenderingConfigJson
|
cssClasses?: string | TagRenderingConfigJson
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the map is pitched, the marker will stay parallel to the screen.
|
||||||
|
* Set to 'map' if you want to put it flattened on the map
|
||||||
|
*/
|
||||||
|
pitchAlignment?: "canvas" | "map" | TagRenderingConfigJson
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the map is rotated, the icon will still point to the north if no rotation was applied
|
||||||
|
*/
|
||||||
|
rotationAlignment?: "map" | "canvas" | TagRenderingConfigJson
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,9 +86,9 @@ export default class LayerConfig extends WithContextLoader {
|
||||||
throw "Layer " + this.id + " does not define a source section (" + context + ")"
|
throw "Layer " + this.id + " does not define a source section (" + context + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
if(json.source === "special" || json.source === "special:library"){
|
if (json.source === "special" || json.source === "special:library") {
|
||||||
this.source = null
|
this.source = null
|
||||||
}else if (json.source.osmTags === undefined) {
|
} else if (json.source.osmTags === undefined) {
|
||||||
throw (
|
throw (
|
||||||
"Layer " +
|
"Layer " +
|
||||||
this.id +
|
this.id +
|
||||||
|
@ -105,7 +105,6 @@ export default class LayerConfig extends WithContextLoader {
|
||||||
throw `${context}: The id of a layer should match [a-z0-9-_]*: ${json.id}`
|
throw `${context}: The id of a layer should match [a-z0-9-_]*: ${json.id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
this.maxAgeOfCache = json.source.maxCacheAge ?? 24 * 60 * 60 * 30
|
|
||||||
if (
|
if (
|
||||||
json.syncSelection !== undefined &&
|
json.syncSelection !== undefined &&
|
||||||
LayerConfig.syncSelectionAllowed.indexOf(json.syncSelection) < 0
|
LayerConfig.syncSelectionAllowed.indexOf(json.syncSelection) < 0
|
||||||
|
@ -120,13 +119,28 @@ export default class LayerConfig extends WithContextLoader {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
this.syncSelection = json.syncSelection ?? "no"
|
this.syncSelection = json.syncSelection ?? "no"
|
||||||
const osmTags = TagUtils.Tag(json.source.osmTags, context + "source.osmTags")
|
if (typeof json.source !== "string") {
|
||||||
|
this.maxAgeOfCache = json.source.maxCacheAge ?? 24 * 60 * 60 * 30
|
||||||
|
const osmTags = TagUtils.Tag(json.source.osmTags, context + "source.osmTags")
|
||||||
|
if (osmTags.isNegative()) {
|
||||||
|
throw (
|
||||||
|
context +
|
||||||
|
"The source states tags which give a very wide selection: it only uses negative expressions, which will result in too much and unexpected data. Add at least one required tag. The tags are:\n\t" +
|
||||||
|
osmTags.asHumanString(false, false, {})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (Constants.priviliged_layers.indexOf(this.id) < 0 && osmTags.isNegative()) {
|
this.source = new SourceConfig(
|
||||||
throw (
|
{
|
||||||
context +
|
osmTags: osmTags,
|
||||||
"The source states tags which give a very wide selection: it only uses negative expressions, which will result in too much and unexpected data. Add at least one required tag. The tags are:\n\t" +
|
geojsonSource: json.source["geoJson"],
|
||||||
osmTags.asHumanString(false, false, {})
|
geojsonSourceLevel: json.source["geoJsonZoomLevel"],
|
||||||
|
overpassScript: json.source["overpassScript"],
|
||||||
|
isOsmCache: json.source["isOsmCache"],
|
||||||
|
mercatorCrs: json.source["mercatorCrs"],
|
||||||
|
idKey: json.source["idKey"],
|
||||||
|
},
|
||||||
|
json.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,20 +152,6 @@ export default class LayerConfig extends WithContextLoader {
|
||||||
throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)"
|
throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)"
|
||||||
}
|
}
|
||||||
|
|
||||||
this.source = new SourceConfig(
|
|
||||||
{
|
|
||||||
osmTags: osmTags,
|
|
||||||
geojsonSource: json.source["geoJson"],
|
|
||||||
geojsonSourceLevel: json.source["geoJsonZoomLevel"],
|
|
||||||
overpassScript: json.source["overpassScript"],
|
|
||||||
isOsmCache: json.source["isOsmCache"],
|
|
||||||
mercatorCrs: json.source["mercatorCrs"],
|
|
||||||
idKey: json.source["idKey"],
|
|
||||||
},
|
|
||||||
Constants.priviliged_layers.indexOf(this.id) > 0,
|
|
||||||
json.id
|
|
||||||
)
|
|
||||||
|
|
||||||
this.allowSplit = json.allowSplit ?? false
|
this.allowSplit = json.allowSplit ?? false
|
||||||
this.name = Translations.T(json.name, translationContext + ".name")
|
this.name = Translations.T(json.name, translationContext + ".name")
|
||||||
if (json.units !== undefined && !Array.isArray(json.units)) {
|
if (json.units !== undefined && !Array.isArray(json.units)) {
|
||||||
|
@ -250,7 +250,7 @@ export default class LayerConfig extends WithContextLoader {
|
||||||
| "osmbasedmap"
|
| "osmbasedmap"
|
||||||
| "historicphoto"
|
| "historicphoto"
|
||||||
| string
|
| string
|
||||||
)[]
|
)[]
|
||||||
if (typeof pr.preciseInput.preferredBackground === "string") {
|
if (typeof pr.preciseInput.preferredBackground === "string") {
|
||||||
preferredBackground = [pr.preciseInput.preferredBackground]
|
preferredBackground = [pr.preciseInput.preferredBackground]
|
||||||
} else {
|
} else {
|
||||||
|
@ -597,7 +597,7 @@ export default class LayerConfig extends WithContextLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
let overpassLink: BaseUIElement = undefined
|
let overpassLink: BaseUIElement = undefined
|
||||||
if (Constants.priviliged_layers.indexOf(this.id) < 0) {
|
if (this.source !== undefined) {
|
||||||
try {
|
try {
|
||||||
overpassLink = new Link(
|
overpassLink = new Link(
|
||||||
"Execute on overpass",
|
"Execute on overpass",
|
||||||
|
|
|
@ -12,6 +12,7 @@ import Img from "../../UI/Base/Img"
|
||||||
import Combine from "../../UI/Base/Combine"
|
import Combine from "../../UI/Base/Combine"
|
||||||
import { VariableUiElement } from "../../UI/Base/VariableUIElement"
|
import { VariableUiElement } from "../../UI/Base/VariableUIElement"
|
||||||
import { OsmTags } from "../OsmFeature"
|
import { OsmTags } from "../OsmFeature"
|
||||||
|
import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson"
|
||||||
|
|
||||||
export default class PointRenderingConfig extends WithContextLoader {
|
export default class PointRenderingConfig extends WithContextLoader {
|
||||||
private static readonly allowed_location_codes = new Set<string>([
|
private static readonly allowed_location_codes = new Set<string>([
|
||||||
|
@ -32,6 +33,8 @@ export default class PointRenderingConfig extends WithContextLoader {
|
||||||
public readonly rotation: TagRenderingConfig
|
public readonly rotation: TagRenderingConfig
|
||||||
public readonly cssDef: TagRenderingConfig
|
public readonly cssDef: TagRenderingConfig
|
||||||
public readonly cssClasses?: TagRenderingConfig
|
public readonly cssClasses?: TagRenderingConfig
|
||||||
|
public readonly pitchAlignment?: TagRenderingConfig
|
||||||
|
public readonly rotationAlignment?: TagRenderingConfig
|
||||||
|
|
||||||
constructor(json: PointRenderingConfigJson, context: string) {
|
constructor(json: PointRenderingConfigJson, context: string) {
|
||||||
super(json, context)
|
super(json, context)
|
||||||
|
@ -88,6 +91,14 @@ export default class PointRenderingConfig extends WithContextLoader {
|
||||||
this.iconSize = this.tr("iconSize", "40,40,center")
|
this.iconSize = this.tr("iconSize", "40,40,center")
|
||||||
this.label = this.tr("label", undefined)
|
this.label = this.tr("label", undefined)
|
||||||
this.rotation = this.tr("rotation", "0")
|
this.rotation = this.tr("rotation", "0")
|
||||||
|
if (json.pitchAlignment) {
|
||||||
|
console.log("Got a pitch alignment!", json.pitchAlignment)
|
||||||
|
}
|
||||||
|
this.pitchAlignment = this.tr("pitchAlignment", "canvas")
|
||||||
|
this.rotationAlignment = this.tr(
|
||||||
|
"rotationAlignment",
|
||||||
|
json.pitchAlignment === "map" ? "map" : "canvas"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,7 +20,6 @@ export default class SourceConfig {
|
||||||
geojsonSourceLevel?: number
|
geojsonSourceLevel?: number
|
||||||
idKey?: string
|
idKey?: string
|
||||||
},
|
},
|
||||||
isSpecialLayer: boolean,
|
|
||||||
context?: string
|
context?: string
|
||||||
) {
|
) {
|
||||||
let defined = 0
|
let defined = 0
|
||||||
|
@ -51,7 +50,7 @@ export default class SourceConfig {
|
||||||
throw `Source defines a geojson-zoomLevel, but does not specify {x} nor {y} (or equivalent), this is probably a bug (in context ${context})`
|
throw `Source defines a geojson-zoomLevel, but does not specify {x} nor {y} (or equivalent), this is probably a bug (in context ${context})`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (params.osmTags !== undefined && !isSpecialLayer) {
|
if (params.osmTags !== undefined) {
|
||||||
const optimized = params.osmTags.optimize()
|
const optimized = params.osmTags.optimize()
|
||||||
if (optimized === false) {
|
if (optimized === false) {
|
||||||
throw (
|
throw (
|
||||||
|
|
|
@ -228,7 +228,7 @@ export class DownloadPanel extends Toggle {
|
||||||
new Set(neededLayers)
|
new Set(neededLayers)
|
||||||
)
|
)
|
||||||
for (const tile of featureList) {
|
for (const tile of featureList) {
|
||||||
if (Constants.priviliged_layers.indexOf(tile.layer) >= 0) {
|
if (tile.layer !== undefined) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ export class GeolocationControl extends VariableUiElement {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const timeDiff = (new Date().getTime() - date.getTime()) / 1000
|
const timeDiff = (new Date().getTime() - date.getTime()) / 1000
|
||||||
console.log("Timediff", timeDiff)
|
|
||||||
return timeDiff <= Constants.zoomToLocationTimeout
|
return timeDiff <= Constants.zoomToLocationTimeout
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class MapPreview
|
||||||
}
|
}
|
||||||
|
|
||||||
const availableLayers = AllKnownLayouts.AllPublicLayers().filter(
|
const availableLayers = AllKnownLayouts.AllPublicLayers().filter(
|
||||||
(l) => l.name !== undefined && Constants.priviliged_layers.indexOf(l.id) < 0
|
(l) => l.name !== undefined && l.source !== undefined
|
||||||
)
|
)
|
||||||
const layerPicker = new DropDown(
|
const layerPicker = new DropDown(
|
||||||
t.selectLayer,
|
t.selectLayer,
|
||||||
|
|
|
@ -14,12 +14,12 @@ import Constants from "../../Models/Constants"
|
||||||
*/
|
*/
|
||||||
export class MapLibreAdaptor implements MapProperties {
|
export class MapLibreAdaptor implements MapProperties {
|
||||||
private static maplibre_control_handlers = [
|
private static maplibre_control_handlers = [
|
||||||
"scrollZoom",
|
// "scrollZoom",
|
||||||
"boxZoom",
|
// "boxZoom",
|
||||||
|
// "doubleClickZoom",
|
||||||
"dragRotate",
|
"dragRotate",
|
||||||
"dragPan",
|
"dragPan",
|
||||||
"keyboard",
|
"keyboard",
|
||||||
"doubleClickZoom",
|
|
||||||
"touchZoomRotate",
|
"touchZoomRotate",
|
||||||
]
|
]
|
||||||
readonly location: UIEventSource<{ lon: number; lat: number }>
|
readonly location: UIEventSource<{ lon: number; lat: number }>
|
||||||
|
|
|
@ -8,12 +8,13 @@ import PointRenderingConfig from "../../Models/ThemeConfig/PointRenderingConfig"
|
||||||
import { OsmTags } from "../../Models/OsmFeature"
|
import { OsmTags } from "../../Models/OsmFeature"
|
||||||
import FeatureSource from "../../Logic/FeatureSource/FeatureSource"
|
import FeatureSource from "../../Logic/FeatureSource/FeatureSource"
|
||||||
import { BBox } from "../../Logic/BBox"
|
import { BBox } from "../../Logic/BBox"
|
||||||
import { Feature, LineString } from "geojson"
|
import { Feature } from "geojson"
|
||||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
||||||
import LineRenderingConfig from "../../Models/ThemeConfig/LineRenderingConfig"
|
import LineRenderingConfig from "../../Models/ThemeConfig/LineRenderingConfig"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import * as range_layer from "../../assets/layers/range/range.json"
|
import * as range_layer from "../../assets/layers/range/range.json"
|
||||||
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||||
|
|
||||||
class PointRenderingLayer {
|
class PointRenderingLayer {
|
||||||
private readonly _config: PointRenderingConfig
|
private readonly _config: PointRenderingConfig
|
||||||
private readonly _fetchStore?: (id: string) => Store<OsmTags>
|
private readonly _fetchStore?: (id: string) => Store<OsmTags>
|
||||||
|
@ -44,6 +45,16 @@ class PointRenderingLayer {
|
||||||
const unseenKeys = new Set(cache.keys())
|
const unseenKeys = new Set(cache.keys())
|
||||||
for (const location of this._config.location) {
|
for (const location of this._config.location) {
|
||||||
for (const feature of features) {
|
for (const feature of features) {
|
||||||
|
if (feature?.geometry === undefined) {
|
||||||
|
console.warn(
|
||||||
|
"Got an invalid feature:",
|
||||||
|
features,
|
||||||
|
" while rendering",
|
||||||
|
location,
|
||||||
|
"of",
|
||||||
|
this._config
|
||||||
|
)
|
||||||
|
}
|
||||||
const loc = GeoOperations.featureToCoordinateWithRenderingType(
|
const loc = GeoOperations.featureToCoordinateWithRenderingType(
|
||||||
<any>feature,
|
<any>feature,
|
||||||
location
|
location
|
||||||
|
@ -102,7 +113,14 @@ class PointRenderingLayer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Marker(el).setLngLat(loc).setOffset(iconAnchor).addTo(this._map)
|
const marker = new Marker(el).setLngLat(loc).setOffset(iconAnchor).addTo(this._map)
|
||||||
|
store
|
||||||
|
.map((tags) => this._config.pitchAlignment.GetRenderValue(tags).Subs(tags).txt)
|
||||||
|
.addCallbackAndRun((pitchAligment) => marker.setPitchAlignment(pitchAligment))
|
||||||
|
store
|
||||||
|
.map((tags) => this._config.rotationAlignment.GetRenderValue(tags).Subs(tags).txt)
|
||||||
|
.addCallbackAndRun((pitchAligment) => marker.setRotationAlignment(pitchAligment))
|
||||||
|
return marker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,13 +136,17 @@ class LineRenderingLayer {
|
||||||
"offset",
|
"offset",
|
||||||
"fill",
|
"fill",
|
||||||
"fillColor",
|
"fillColor",
|
||||||
]
|
] as const
|
||||||
|
|
||||||
|
private static readonly lineConfigKeysColor = ["color", "fillColor"] as const
|
||||||
|
private static readonly lineConfigKeysNumber = ["width", "offset"] as const
|
||||||
private readonly _map: MlMap
|
private readonly _map: MlMap
|
||||||
private readonly _config: LineRenderingConfig
|
private readonly _config: LineRenderingConfig
|
||||||
private readonly _visibility?: Store<boolean>
|
private readonly _visibility?: Store<boolean>
|
||||||
private readonly _fetchStore?: (id: string) => Store<OsmTags>
|
private readonly _fetchStore?: (id: string) => Store<OsmTags>
|
||||||
private readonly _onClick?: (id: string) => void
|
private readonly _onClick?: (id: string) => void
|
||||||
private readonly _layername: string
|
private readonly _layername: string
|
||||||
|
private readonly _listenerInstalledOn: Set<string> = new Set<string>()
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
map: MlMap,
|
map: MlMap,
|
||||||
|
@ -145,6 +167,39 @@ class LineRenderingLayer {
|
||||||
features.features.addCallbackAndRunD((features) => self.update(features))
|
features.features.addCallbackAndRunD((features) => self.update(features))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private calculatePropsFor(
|
||||||
|
properties: Record<string, string>
|
||||||
|
): Partial<Record<typeof LineRenderingLayer.lineConfigKeys[number], string>> {
|
||||||
|
const calculatedProps = {}
|
||||||
|
const config = this._config
|
||||||
|
|
||||||
|
for (const key of LineRenderingLayer.lineConfigKeys) {
|
||||||
|
const v = config[key]?.GetRenderValue(properties)?.Subs(properties).txt
|
||||||
|
calculatedProps[key] = v
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
for (const key of LineRenderingLayer.lineConfigKeysNumber) {
|
||||||
|
const v = config[key]?.GetRenderValue(properties)?.Subs(properties).txt
|
||||||
|
calculatedProps[key] = Number(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Calculated props:", calculatedProps, "for", properties.id)
|
||||||
|
return calculatedProps
|
||||||
|
}
|
||||||
|
|
||||||
private async update(features: Feature[]) {
|
private async update(features: Feature[]) {
|
||||||
const map = this._map
|
const map = this._map
|
||||||
while (!map.isStyleLoaded()) {
|
while (!map.isStyleLoaded()) {
|
||||||
|
@ -158,31 +213,14 @@ class LineRenderingLayer {
|
||||||
},
|
},
|
||||||
promoteId: "id",
|
promoteId: "id",
|
||||||
})
|
})
|
||||||
for (let i = 0; i < features.length; i++) {
|
|
||||||
const feature = features[i]
|
|
||||||
const id = feature.properties.id ?? "" + i
|
|
||||||
const tags = this._fetchStore(id)
|
|
||||||
tags.addCallbackAndRunD((properties) => {
|
|
||||||
const config = this._config
|
|
||||||
|
|
||||||
const calculatedProps = {}
|
|
||||||
for (const key of LineRenderingLayer.lineConfigKeys) {
|
|
||||||
const v = config[key]?.GetRenderValue(properties)?.Subs(properties).txt
|
|
||||||
calculatedProps[key] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
map.setFeatureState({ source: this._layername, id }, calculatedProps)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
map.addLayer({
|
map.addLayer({
|
||||||
source: this._layername,
|
source: this._layername,
|
||||||
id: this._layername + "_line",
|
id: this._layername + "_line",
|
||||||
type: "line",
|
type: "line",
|
||||||
filter: ["in", ["geometry-type"], ["literal", ["LineString", "MultiLineString"]]],
|
|
||||||
layout: {},
|
|
||||||
paint: {
|
paint: {
|
||||||
"line-color": ["feature-state", "color"],
|
"line-color": ["feature-state", "color"],
|
||||||
|
"line-opacity": ["feature-state", "color-opacity"],
|
||||||
"line-width": ["feature-state", "width"],
|
"line-width": ["feature-state", "width"],
|
||||||
"line-offset": ["feature-state", "offset"],
|
"line-offset": ["feature-state", "offset"],
|
||||||
},
|
},
|
||||||
|
@ -205,12 +243,49 @@ class LineRenderingLayer {
|
||||||
layout: {},
|
layout: {},
|
||||||
paint: {
|
paint: {
|
||||||
"fill-color": ["feature-state", "fillColor"],
|
"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:",
|
||||||
|
feature,
|
||||||
|
"from"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (this._listenerInstalledOn.has(id)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (this._fetchStore === undefined) {
|
||||||
|
map.setFeatureState(
|
||||||
|
{ source: this._layername, id },
|
||||||
|
this.calculatePropsFor(feature.properties)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const tags = this._fetchStore(id)
|
||||||
|
this._listenerInstalledOn.add(id)
|
||||||
|
tags.addCallbackAndRunD((properties) => {
|
||||||
|
map.setFeatureState(
|
||||||
|
{ source: this._layername, id },
|
||||||
|
this.calculatePropsFor(properties)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ShowDataLayer {
|
export default class ShowDataLayer {
|
||||||
|
private static rangeLayer = new LayerConfig(
|
||||||
|
<LayerConfigJson>range_layer,
|
||||||
|
"ShowDataLayer.ts:range.json"
|
||||||
|
)
|
||||||
private readonly _map: Store<MlMap>
|
private readonly _map: Store<MlMap>
|
||||||
private readonly _options: ShowDataLayerOptions & { layer: LayerConfig }
|
private readonly _options: ShowDataLayerOptions & { layer: LayerConfig }
|
||||||
private readonly _popupCache: Map<string, ScrollableFullScreen>
|
private readonly _popupCache: Map<string, ScrollableFullScreen>
|
||||||
|
@ -223,11 +298,6 @@ export default class ShowDataLayer {
|
||||||
map.addCallbackAndRunD((map) => self.initDrawFeatures(map))
|
map.addCallbackAndRunD((map) => self.initDrawFeatures(map))
|
||||||
}
|
}
|
||||||
|
|
||||||
private static rangeLayer = new LayerConfig(
|
|
||||||
<LayerConfigJson>range_layer,
|
|
||||||
"ShowDataLayer.ts:range.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
public static showRange(
|
public static showRange(
|
||||||
map: Store<MlMap>,
|
map: Store<MlMap>,
|
||||||
features: FeatureSource,
|
features: FeatureSource,
|
||||||
|
@ -241,6 +311,9 @@ export default class ShowDataLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private openOrReusePopup(id: string): void {
|
private openOrReusePopup(id: string): void {
|
||||||
|
if (!this._popupCache || !this._options.fetchStore) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (this._popupCache.has(id)) {
|
if (this._popupCache.has(id)) {
|
||||||
this._popupCache.get(id).Activate()
|
this._popupCache.get(id).Activate()
|
||||||
return
|
return
|
||||||
|
@ -267,11 +340,12 @@ export default class ShowDataLayer {
|
||||||
private initDrawFeatures(map: MlMap) {
|
private initDrawFeatures(map: MlMap) {
|
||||||
const { features, doShowLayer, fetchStore, buildPopup } = this._options
|
const { features, doShowLayer, fetchStore, buildPopup } = this._options
|
||||||
const onClick = buildPopup === undefined ? undefined : (id) => this.openOrReusePopup(id)
|
const onClick = buildPopup === undefined ? undefined : (id) => this.openOrReusePopup(id)
|
||||||
for (const lineRenderingConfig of this._options.layer.lineRendering) {
|
for (let i = 0; i < this._options.layer.lineRendering.length; i++) {
|
||||||
|
const lineRenderingConfig = this._options.layer.lineRendering[i]
|
||||||
new LineRenderingLayer(
|
new LineRenderingLayer(
|
||||||
map,
|
map,
|
||||||
features,
|
features,
|
||||||
"test",
|
this._options.layer.id + "_linerendering_" + i,
|
||||||
lineRenderingConfig,
|
lineRenderingConfig,
|
||||||
doShowLayer,
|
doShowLayer,
|
||||||
fetchStore,
|
fetchStore,
|
||||||
|
|
|
@ -21,11 +21,13 @@
|
||||||
import Svg from "../Svg";
|
import Svg from "../Svg";
|
||||||
import If from "./Base/If.svelte";
|
import If from "./Base/If.svelte";
|
||||||
import { GeolocationControl } from "./BigComponents/GeolocationControl.js";
|
import { GeolocationControl } from "./BigComponents/GeolocationControl.js";
|
||||||
import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline";
|
|
||||||
import { BBox } from "../Logic/BBox";
|
import { BBox } from "../Logic/BBox";
|
||||||
import ShowDataLayer from "./Map/ShowDataLayer";
|
import ShowDataLayer from "./Map/ShowDataLayer";
|
||||||
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource";
|
import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||||
|
import type FeatureSource from "../Logic/FeatureSource/FeatureSource";
|
||||||
|
import LayerState from "../Logic/State/LayerState";
|
||||||
|
import Constants from "../Models/Constants";
|
||||||
|
import type { Feature } from "geojson";
|
||||||
export let layout: LayoutConfig;
|
export let layout: LayoutConfig;
|
||||||
|
|
||||||
const maplibremap: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
|
const maplibremap: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
|
||||||
|
@ -46,7 +48,7 @@
|
||||||
osmConfiguration: <"osm" | "osm-test">featureSwitches.featureSwitchApiURL.data
|
osmConfiguration: <"osm" | "osm-test">featureSwitches.featureSwitchApiURL.data
|
||||||
});
|
});
|
||||||
const userRelatedState = new UserRelatedState(osmConnection, layout?.language);
|
const userRelatedState = new UserRelatedState(osmConnection, layout?.language);
|
||||||
const selectedElement = new UIEventSource<any>(undefined, "Selected element");
|
const selectedElement = new UIEventSource<Feature | undefined>(undefined, "Selected element");
|
||||||
const geolocation = new GeoLocationHandler(geolocationState, selectedElement, mapproperties, userRelatedState.gpsLocationHistoryRetentionTime);
|
const geolocation = new GeoLocationHandler(geolocationState, selectedElement, mapproperties, userRelatedState.gpsLocationHistoryRetentionTime);
|
||||||
|
|
||||||
const allElements = new ElementStorage();
|
const allElements = new ElementStorage();
|
||||||
|
@ -55,16 +57,19 @@
|
||||||
osmConnection,
|
osmConnection,
|
||||||
historicalUserLocations: geolocation.historicalUserLocations
|
historicalUserLocations: geolocation.historicalUserLocations
|
||||||
}, layout?.isLeftRightSensitive() ?? false);
|
}, layout?.isLeftRightSensitive() ?? false);
|
||||||
|
console.log("Setting up layerstate...")
|
||||||
Map
|
const layerState = new LayerState(osmConnection, layout.layers, layout.id)
|
||||||
|
|
||||||
{
|
{
|
||||||
// Various actors that we don't need to reference
|
// Various actors that we don't need to reference
|
||||||
|
// TODO enable new TitleHandler(selectedElement,layout,allElements)
|
||||||
new ChangeToElementsActor(changes, allElements);
|
new ChangeToElementsActor(changes, allElements);
|
||||||
new PendingChangesUploader(changes, selectedElement);
|
new PendingChangesUploader(changes, selectedElement);
|
||||||
new SelectedElementTagsUpdater({
|
new SelectedElementTagsUpdater({
|
||||||
allElements, changes, selectedElement, layoutToUse: layout, osmConnection
|
allElements, changes, selectedElement, layoutToUse: layout, osmConnection
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Various initial setup
|
// Various initial setup
|
||||||
userRelatedState.markLayoutAsVisited(layout);
|
userRelatedState.markLayoutAsVisited(layout);
|
||||||
if(layout?.lockLocation){
|
if(layout?.lockLocation){
|
||||||
|
@ -76,7 +81,37 @@
|
||||||
featureSwitches.featureSwitchIsTesting
|
featureSwitches.featureSwitchIsTesting
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type AddedByDefaultTypes = typeof Constants.added_by_default[number]
|
||||||
|
/**
|
||||||
|
* A listing which maps the layerId onto the featureSource
|
||||||
|
*/
|
||||||
|
const empty = []
|
||||||
|
const specialLayers : Record<AddedByDefaultTypes | "current_view", FeatureSource> = {
|
||||||
|
"home_location": userRelatedState.homeLocation,
|
||||||
|
gps_location: geolocation.currentUserLocation,
|
||||||
|
gps_location_history: geolocation.historicalUserLocations,
|
||||||
|
gps_track: geolocation.historicalUserLocationsTrack,
|
||||||
|
selected_element: new StaticFeatureSource(selectedElement.map(f => f === undefined ? empty : [f])),
|
||||||
|
range: new StaticFeatureSource(mapproperties.maxbounds.map(bbox => bbox === undefined ? empty : <Feature[]> [bbox.asGeoJson({id:"range"})])) ,
|
||||||
|
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]
|
||||||
|
if(features === undefined){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
new ShowDataLayer(maplibremap, {
|
||||||
|
features,
|
||||||
|
doShowLayer: flayer.isDisplayed,
|
||||||
|
layer: flayer.layerDef,
|
||||||
|
selectedElement
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -93,15 +128,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="absolute bottom-0 right-0 mb-4 mr-4">
|
<div class="absolute bottom-0 right-0 mb-4 mr-4">
|
||||||
|
<MapControlButton on:click={() => mapproperties.zoom.update(z => z+1)}>
|
||||||
<If condition={mapproperties.allowMoving}>
|
<ToSvelte class="w-7 h-7 block" construct={Svg.plus_ui}></ToSvelte>
|
||||||
<MapControlButton on:click={() => mapproperties.zoom.update(z => z+1)}>
|
</MapControlButton>
|
||||||
<ToSvelte class="w-7 h-7 block" construct={Svg.plus_ui}></ToSvelte>
|
<MapControlButton on:click={() => mapproperties.zoom.update(z => z-1)}>
|
||||||
</MapControlButton>
|
<ToSvelte class="w-7 h-7 block" construct={Svg.min_ui}></ToSvelte>
|
||||||
<MapControlButton on:click={() => mapproperties.zoom.update(z => z-1)}>
|
</MapControlButton>
|
||||||
<ToSvelte class="w-7 h-7 block" construct={Svg.min_ui}></ToSvelte>
|
|
||||||
</MapControlButton>
|
|
||||||
</If>
|
|
||||||
<If condition={featureSwitches.featureSwitchGeolocation}>
|
<If condition={featureSwitches.featureSwitchGeolocation}>
|
||||||
<MapControlButton>
|
<MapControlButton>
|
||||||
<ToSvelte construct={() => new GeolocationControl(geolocation, mapproperties).SetClass("block w-8 h-8")}></ToSvelte>
|
<ToSvelte construct={() => new GeolocationControl(geolocation, mapproperties).SetClass("block w-8 h-8")}></ToSvelte>
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"iconSize": "40,40,center",
|
"iconSize": "40,40,center",
|
||||||
|
"pitchAlignment": "map",
|
||||||
"rotation": {
|
"rotation": {
|
||||||
"render": "0deg",
|
"render": "0deg",
|
||||||
"mappings": [
|
"mappings": [
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "home_location",
|
"id": "home_location",
|
||||||
"description": "Meta layer showing the home location of the user. The home location can be set in the [profile settings](https://www.openstreetmap.org/profile/edit) of OpenStreetMap.",
|
"description": "Meta layer showing the home location of the user. The home location can be set in the [profile settings](https://www.openstreetmap.org/profile/edit) of OpenStreetMap.",
|
||||||
"minzoom": 0,
|
"minzoom": 0,
|
||||||
"source":"special",
|
"source": "special",
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
"icon": {
|
"icon": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"id": "import_candidate",
|
"id": "import_candidate",
|
||||||
"description": "Layer used in the importHelper",
|
"description": "Layer used as template in the importHelper",
|
||||||
"source":"special",
|
"source":"special",
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
"name": null,
|
"name": null,
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
{
|
{
|
||||||
"width": 4,
|
"width": 3,
|
||||||
"fill": "no",
|
"fill": "no",
|
||||||
"color": "#ff000088"
|
"color": "#cc00cc"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
"id": "split_point",
|
"id": "split_point",
|
||||||
"description": "Layer rendering the little scissors for the minimap in the 'splitRoadWizard'",
|
"description": "Layer rendering the little scissors for the minimap in the 'splitRoadWizard'",
|
||||||
"minzoom": 1,
|
"minzoom": 1,
|
||||||
"source": {
|
"source": "special",
|
||||||
"osmTags": "_split_point=yes"
|
|
||||||
},
|
|
||||||
"name": "Split point",
|
"name": "Split point",
|
||||||
"title": "Split point",
|
"title": "Split point",
|
||||||
"mapRendering": [
|
"mapRendering": [
|
||||||
|
@ -17,4 +15,4 @@
|
||||||
"iconSize": "30,30,center"
|
"iconSize": "30,30,center"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"id": "type_node",
|
|
||||||
"description": "This is a priviliged meta_layer which exports _every_ point in OSM. This only works if zoomed below the point that the full tile is loaded (and not loaded via Overpass). Note that this point will also contain a property `parent_ways` which contains all the ways this node is part of as a list. This is mainly used for extremely specialized themes, which do advanced conflations. Expert use only.",
|
|
||||||
"minzoom": 18,
|
|
||||||
"source": "special",
|
|
||||||
"mapRendering": null,
|
|
||||||
"name": "All OSM Nodes",
|
|
||||||
"title": "OSM node {id}",
|
|
||||||
"tagRendering": []
|
|
||||||
}
|
|
|
@ -28,29 +28,6 @@
|
||||||
"minzoom": 19
|
"minzoom": 19
|
||||||
},
|
},
|
||||||
"layers": [
|
"layers": [
|
||||||
{
|
|
||||||
"builtin": "type_node",
|
|
||||||
"override": {
|
|
||||||
"calculatedTags": [
|
|
||||||
"_is_part_of_building=feat.get('parent_ways')?.some(p => p.building !== undefined && p.building !== '') ?? false",
|
|
||||||
"_is_part_of_grb_building=feat.get('parent_ways')?.some(p => p['source:geometry:ref'] !== undefined) ?? false",
|
|
||||||
"_is_part_of_building_passage=feat.get('parent_ways')?.some(p => p.tunnel === 'building_passage') ?? false",
|
|
||||||
"_is_part_of_highway=!feat.get('is_part_of_building_passage') && (feat.get('parent_ways')?.some(p => p.highway !== undefined && p.highway !== '') ?? false)",
|
|
||||||
"_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false",
|
|
||||||
"_moveable=feat.get('_is_part_of_building') && !feat.get('_is_part_of_grb_building')"
|
|
||||||
],
|
|
||||||
"mapRendering": [
|
|
||||||
{
|
|
||||||
"icon": "square:#cc0",
|
|
||||||
"iconSize": "5,5,center",
|
|
||||||
"location": [
|
|
||||||
"point"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"passAllFeatures": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "osm-buildings",
|
"id": "osm-buildings",
|
||||||
"name": "All OSM-buildings",
|
"name": "All OSM-buildings",
|
||||||
|
@ -771,4 +748,4 @@
|
||||||
"overpassMaxZoom": 17,
|
"overpassMaxZoom": 17,
|
||||||
"osmApiTileSize": 17,
|
"osmApiTileSize": 17,
|
||||||
"credits": "Pieter Vander Vennet"
|
"credits": "Pieter Vander Vennet"
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ function GenLayerOverviewText(): BaseUIElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
const allLayers: LayerConfig[] = Array.from(AllSharedLayers.sharedLayers.values()).filter(
|
const allLayers: LayerConfig[] = Array.from(AllSharedLayers.sharedLayers.values()).filter(
|
||||||
(layer) => Constants.priviliged_layers.indexOf(layer.id) < 0
|
(layer) => layer.source === null
|
||||||
)
|
)
|
||||||
|
|
||||||
const builtinLayerIds: Set<string> = new Set<string>()
|
const builtinLayerIds: Set<string> = new Set<string>()
|
||||||
|
@ -183,7 +183,7 @@ function GenOverviewsForSingleLayer(
|
||||||
callback: (layer: LayerConfig, element: BaseUIElement, inlineSource: string) => void
|
callback: (layer: LayerConfig, element: BaseUIElement, inlineSource: string) => void
|
||||||
): void {
|
): void {
|
||||||
const allLayers: LayerConfig[] = Array.from(AllSharedLayers.sharedLayers.values()).filter(
|
const allLayers: LayerConfig[] = Array.from(AllSharedLayers.sharedLayers.values()).filter(
|
||||||
(layer) => Constants.priviliged_layers.indexOf(layer.id) < 0
|
(layer) => layer.source !== null
|
||||||
)
|
)
|
||||||
const builtinLayerIds: Set<string> = new Set<string>()
|
const builtinLayerIds: Set<string> = new Set<string>()
|
||||||
allLayers.forEach((l) => builtinLayerIds.add(l.id))
|
allLayers.forEach((l) => builtinLayerIds.add(l.id))
|
||||||
|
@ -195,7 +195,7 @@ function GenOverviewsForSingleLayer(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const layer of layout.layers) {
|
for (const layer of layout.layers) {
|
||||||
if (Constants.priviliged_layers.indexOf(layer.id) >= 0) {
|
if (layer.source === null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (builtinLayerIds.has(layer.id)) {
|
if (builtinLayerIds.has(layer.id)) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ async function main(includeTags = true) {
|
||||||
if (layer.source["geoJson"] !== undefined && !layer.source["isOsmCache"]) {
|
if (layer.source["geoJson"] !== undefined && !layer.source["isOsmCache"]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (Constants.priviliged_layers.indexOf(layer.id) >= 0) {
|
if (layer.source == null || typeof layer.source === "string") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ function generateLayerUsage(layer: LayerConfig, layout: LayoutConfig): any[] {
|
||||||
function generateTagInfoEntry(layout: LayoutConfig): any {
|
function generateTagInfoEntry(layout: LayoutConfig): any {
|
||||||
const usedTags = []
|
const usedTags = []
|
||||||
for (const layer of layout.layers) {
|
for (const layer of layout.layers) {
|
||||||
if (Constants.priviliged_layers.indexOf(layer.id) >= 0) {
|
if (layer.source === null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (layer.source.geojsonSource !== undefined && layer.source.isOsmCacheLayer !== true) {
|
if (layer.source.geojsonSource !== undefined && layer.source.isOsmCacheLayer !== true) {
|
||||||
|
|
7
test.ts
7
test.ts
|
@ -3,11 +3,12 @@ import ThemeViewGUI from "./UI/ThemeViewGUI.svelte"
|
||||||
import { FixedUiElement } from "./UI/Base/FixedUiElement"
|
import { FixedUiElement } from "./UI/Base/FixedUiElement"
|
||||||
import { QueryParameters } from "./Logic/Web/QueryParameters"
|
import { QueryParameters } from "./Logic/Web/QueryParameters"
|
||||||
import { AllKnownLayoutsLazy } from "./Customizations/AllKnownLayouts"
|
import { AllKnownLayoutsLazy } from "./Customizations/AllKnownLayouts"
|
||||||
|
import LayoutConfig from "./Models/ThemeConfig/LayoutConfig"
|
||||||
|
import * as benches from "./assets/generated/themes/benches.json"
|
||||||
async function main() {
|
async function main() {
|
||||||
new FixedUiElement("Determining layout...").AttachTo("maindiv")
|
new FixedUiElement("Determining layout...").AttachTo("maindiv")
|
||||||
const qp = QueryParameters.GetQueryParameter("layout", "benches")
|
const qp = QueryParameters.GetQueryParameter("layout", "")
|
||||||
const layout = new AllKnownLayoutsLazy().get(qp.data)
|
const layout = new LayoutConfig(<any>benches, true) // qp.data === "" ? : new AllKnownLayoutsLazy().get(qp.data)
|
||||||
console.log("Using layout", layout.id)
|
console.log("Using layout", layout.id)
|
||||||
new SvelteUIElement(ThemeViewGUI, { layout }).AttachTo("maindiv")
|
new SvelteUIElement(ThemeViewGUI, { layout }).AttachTo("maindiv")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue