forked from MapComplete/MapComplete
Feature: more or less working version of clustering, clusters of multiple layers are joined
This commit is contained in:
parent
5bc8f11d24
commit
0048c091d0
9 changed files with 143 additions and 65 deletions
|
@ -1,25 +1,27 @@
|
||||||
import { Store, UIEventSource } from "../UIEventSource"
|
import { Store, UIEventSource } from "../UIEventSource"
|
||||||
import FilteredLayer from "../../Models/FilteredLayer"
|
import FilteredLayer from "../../Models/FilteredLayer"
|
||||||
import { Feature } from "geojson"
|
import { Feature, Geometry } from "geojson"
|
||||||
|
import { OsmTags } from "../../Models/OsmFeature"
|
||||||
|
|
||||||
export interface FeatureSource<T extends Feature = Feature> {
|
export interface FeatureSource<T extends Feature = Feature<Geometry, OsmTags>> {
|
||||||
features: Store<T[]>
|
features: Store<T[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdatableFeatureSource<T extends Feature = Feature> extends FeatureSource<T> {
|
export interface UpdatableFeatureSource<T extends Feature = Feature<Geometry, OsmTags>> extends FeatureSource<T> {
|
||||||
/**
|
/**
|
||||||
* Forces an update and downloads the data, even if the feature source is supposed to be active
|
* Forces an update and downloads the data, even if the feature source is supposed to be active
|
||||||
*/
|
*/
|
||||||
updateAsync()
|
updateAsync(): void
|
||||||
}
|
}
|
||||||
export interface WritableFeatureSource<T extends Feature = Feature> extends FeatureSource<T> {
|
|
||||||
|
export interface WritableFeatureSource<T extends Feature = Feature<Geometry, OsmTags>> extends FeatureSource<T> {
|
||||||
features: UIEventSource<T[]>
|
features: UIEventSource<T[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A feature source which only contains features for the defined layer
|
* A feature source which only contains features for the defined layer
|
||||||
*/
|
*/
|
||||||
export interface FeatureSourceForLayer<T extends Feature = Feature> extends FeatureSource<T> {
|
export interface FeatureSourceForLayer<T extends Feature = Feature<Geometry, OsmTags>> extends FeatureSource<T> {
|
||||||
readonly layer: FilteredLayer
|
readonly layer: FilteredLayer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,17 +11,17 @@ import { UIEventSource } from "../UIEventSource"
|
||||||
* If this is the case, multiple objects with a different _matching_layer_id are generated.
|
* If this is the case, multiple objects with a different _matching_layer_id are generated.
|
||||||
* In any case, this featureSource marks the objects with _matching_layer_id
|
* In any case, this featureSource marks the objects with _matching_layer_id
|
||||||
*/
|
*/
|
||||||
export default class PerLayerFeatureSourceSplitter<T extends FeatureSource = FeatureSource> {
|
export default class PerLayerFeatureSourceSplitter<T extends Feature, SRC extends FeatureSource<T>> {
|
||||||
public readonly perLayer: ReadonlyMap<string, T>
|
public readonly perLayer: ReadonlyMap<string, SRC>
|
||||||
constructor(
|
constructor(
|
||||||
layers: FilteredLayer[],
|
layers: FilteredLayer[],
|
||||||
upstream: FeatureSource,
|
upstream: FeatureSource<T>,
|
||||||
options?: {
|
options?: {
|
||||||
constructStore?: (features: UIEventSource<Feature[]>, layer: FilteredLayer) => T
|
constructStore?: (features: UIEventSource<T[]>, layer: FilteredLayer) => SRC
|
||||||
handleLeftovers?: (featuresWithoutLayer: Feature[]) => void
|
handleLeftovers?: (featuresWithoutLayer: T[]) => void
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const knownLayers = new Map<string, T>()
|
const knownLayers = new Map<string, SRC>()
|
||||||
/**
|
/**
|
||||||
* Keeps track of the ids that are included per layer.
|
* Keeps track of the ids that are included per layer.
|
||||||
* Used to know if the downstream feature source needs to be pinged
|
* Used to know if the downstream feature source needs to be pinged
|
||||||
|
@ -32,12 +32,12 @@ export default class PerLayerFeatureSourceSplitter<T extends FeatureSource = Fea
|
||||||
const constructStore =
|
const constructStore =
|
||||||
options?.constructStore ?? ((store, layer) => new SimpleFeatureSource(layer, store))
|
options?.constructStore ?? ((store, layer) => new SimpleFeatureSource(layer, store))
|
||||||
for (const layer of layers) {
|
for (const layer of layers) {
|
||||||
const src = new UIEventSource<Feature[]>([])
|
const src = new UIEventSource<T[]>([])
|
||||||
layerSources.set(layer.layerDef.id, src)
|
layerSources.set(layer.layerDef.id, src)
|
||||||
knownLayers.set(layer.layerDef.id, <T>constructStore(src, layer))
|
knownLayers.set(layer.layerDef.id, <SRC>constructStore(src, layer))
|
||||||
}
|
}
|
||||||
|
|
||||||
upstream.features.addCallbackAndRunD((features) => {
|
upstream.features.addCallbackAndRunD((features: T[]) => {
|
||||||
if (layers === undefined) {
|
if (layers === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ export default class PerLayerFeatureSourceSplitter<T extends FeatureSource = Fea
|
||||||
*/
|
*/
|
||||||
const hasChanged: boolean[] = layers.map(() => false)
|
const hasChanged: boolean[] = layers.map(() => false)
|
||||||
const newIndices: Set<string>[] = layers.map(() => new Set<string>())
|
const newIndices: Set<string>[] = layers.map(() => new Set<string>())
|
||||||
const noLayerFound: Feature[] = []
|
const noLayerFound: T[] = []
|
||||||
|
|
||||||
for (const layer of layers) {
|
for (const layer of layers) {
|
||||||
featuresPerLayer.set(layer.layerDef.id, [])
|
featuresPerLayer.set(layer.layerDef.id, [])
|
||||||
|
@ -115,7 +115,7 @@ export default class PerLayerFeatureSourceSplitter<T extends FeatureSource = Fea
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public forEach(f: (featureSource: T) => void) {
|
public forEach(f: ((src: SRC) => void)) {
|
||||||
for (const fs of this.perLayer.values()) {
|
for (const fs of this.perLayer.values()) {
|
||||||
f(fs)
|
f(fs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
import { Feature as GeojsonFeature, Geometry } from "geojson"
|
import { Feature, Feature as GeojsonFeature, Geometry } from "geojson"
|
||||||
|
|
||||||
import { Store, UIEventSource } from "../../UIEventSource"
|
import { Store, UIEventSource } from "../../UIEventSource"
|
||||||
import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
|
import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
|
||||||
import { MvtToGeojson } from "mvt-to-geojson"
|
import { MvtToGeojson } from "mvt-to-geojson"
|
||||||
|
import { OsmTags } from "../../../Models/OsmFeature"
|
||||||
|
|
||||||
export default class MvtSource implements FeatureSourceForTile, UpdatableFeatureSource {
|
export default class MvtSource implements FeatureSourceForTile, UpdatableFeatureSource {
|
||||||
public readonly features: Store<GeojsonFeature<Geometry, { [name: string]: any }>[]>
|
public readonly features: Store<GeojsonFeature<Geometry, OsmTags>[]>
|
||||||
public readonly x: number
|
public readonly x: number
|
||||||
public readonly y: number
|
public readonly y: number
|
||||||
public readonly z: number
|
public readonly z: number
|
||||||
private readonly _url: string
|
private readonly _url: string
|
||||||
private readonly _layerName: string
|
|
||||||
private readonly _features: UIEventSource<
|
private readonly _features: UIEventSource<
|
||||||
GeojsonFeature<
|
GeojsonFeature<Geometry, OsmTags>[]
|
||||||
Geometry,
|
> = new UIEventSource<GeojsonFeature<Geometry, OsmTags>[]>([])
|
||||||
{
|
|
||||||
[name: string]: any
|
|
||||||
}
|
|
||||||
>[]
|
|
||||||
> = new UIEventSource<GeojsonFeature<Geometry, { [p: string]: any }>[]>([])
|
|
||||||
private currentlyRunning: Promise<any>
|
private currentlyRunning: Promise<any>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -26,11 +21,9 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
z: number,
|
z: number,
|
||||||
layerName?: string,
|
|
||||||
isActive?: Store<boolean>
|
isActive?: Store<boolean>
|
||||||
) {
|
) {
|
||||||
this._url = url
|
this._url = url
|
||||||
this._layerName = layerName
|
|
||||||
this.x = x
|
this.x = x
|
||||||
this.y = y
|
this.y = y
|
||||||
this.z = z
|
this.z = z
|
||||||
|
@ -61,7 +54,7 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const buffer = await result.arrayBuffer()
|
const buffer = await result.arrayBuffer()
|
||||||
const features = MvtToGeojson.fromBuffer(buffer, this.x, this.y, this.z)
|
const features = <Feature<Geometry, OsmTags>[]>MvtToGeojson.fromBuffer(buffer, this.x, this.y, this.z)
|
||||||
for (const feature of features) {
|
for (const feature of features) {
|
||||||
const properties = feature.properties
|
const properties = feature.properties
|
||||||
if (!properties["osm_type"]) {
|
if (!properties["osm_type"]) {
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { UIEventSource } from "../../UIEventSource"
|
import { UIEventSource } from "../../UIEventSource"
|
||||||
import FilteredLayer from "../../../Models/FilteredLayer"
|
import FilteredLayer from "../../../Models/FilteredLayer"
|
||||||
import { FeatureSourceForLayer } from "../FeatureSource"
|
import { FeatureSourceForLayer } from "../FeatureSource"
|
||||||
import { Feature } from "geojson"
|
import { Feature, Geometry } from "geojson"
|
||||||
|
import { OsmTags } from "../../../Models/OsmFeature"
|
||||||
|
|
||||||
export default class SimpleFeatureSource implements FeatureSourceForLayer {
|
export default class SimpleFeatureSource<T extends Feature = Feature<Geometry, OsmTags>> implements FeatureSourceForLayer<T> {
|
||||||
public readonly features: UIEventSource<Feature[]>
|
public readonly features: UIEventSource<T[]>
|
||||||
public readonly layer: FilteredLayer
|
public readonly layer: FilteredLayer
|
||||||
|
|
||||||
constructor(layer: FilteredLayer, featureSource?: UIEventSource<Feature[]>) {
|
constructor(layer: FilteredLayer, featureSource?: UIEventSource<T[]>) {
|
||||||
this.layer = layer
|
this.layer = layer
|
||||||
this.features = featureSource ?? new UIEventSource<Feature[]>([])
|
this.features = featureSource ?? new UIEventSource<T[]>([])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { FeatureSource } from "../FeatureSource"
|
||||||
import { Feature, Point } from "geojson"
|
import { Feature, Point } from "geojson"
|
||||||
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
|
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
|
||||||
import { GeoOperations } from "../../GeoOperations"
|
import { GeoOperations } from "../../GeoOperations"
|
||||||
import StaticFeatureSource from "../Sources/StaticFeatureSource"
|
|
||||||
import { Tiles } from "../../../Models/TileRange"
|
import { Tiles } from "../../../Models/TileRange"
|
||||||
|
|
||||||
export interface ClusteringOptions {
|
export interface ClusteringOptions {
|
||||||
|
@ -19,9 +18,14 @@ export interface ClusteringOptions {
|
||||||
showSummaryAt?: "tilecenter" | "average"
|
showSummaryAt?: "tilecenter" | "average"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SummaryProperties {
|
||||||
|
id: string,
|
||||||
|
total: number,
|
||||||
|
tile_id: number
|
||||||
|
}
|
||||||
|
|
||||||
export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>> implements FeatureSource<T> {
|
export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>> implements FeatureSource<T> {
|
||||||
|
|
||||||
public readonly summaryPoints: FeatureSource
|
|
||||||
private readonly id: string
|
private readonly id: string
|
||||||
private readonly showSummaryAt: "tilecenter" | "average"
|
private readonly showSummaryAt: "tilecenter" | "average"
|
||||||
features: Store<T[]>
|
features: Store<T[]>
|
||||||
|
@ -42,11 +46,9 @@ export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>>
|
||||||
const clusterCutoff = options?.dontClusterAboveZoom ?? 17
|
const clusterCutoff = options?.dontClusterAboveZoom ?? 17
|
||||||
const doCluster = options?.dontClusterAboveZoom === undefined ? new ImmutableStore(true) : currentZoomlevel.map(zoom => zoom <= clusterCutoff)
|
const doCluster = options?.dontClusterAboveZoom === undefined ? new ImmutableStore(true) : currentZoomlevel.map(zoom => zoom <= clusterCutoff)
|
||||||
const cutoff = options?.cutoff ?? 20
|
const cutoff = options?.cutoff ?? 20
|
||||||
const summaryPoints = new UIEventSource<Feature<Point>[]>([])
|
const summaryPoints = new UIEventSource<Feature<Point, SummaryProperties>[]>([])
|
||||||
currentZoomlevel = currentZoomlevel.stabilized(500)
|
currentZoomlevel = currentZoomlevel.stabilized(500).map(z => Math.floor(z))
|
||||||
this.summaryPoints = new StaticFeatureSource(summaryPoints)
|
|
||||||
this.features = (upstream.features.map(features => {
|
this.features = (upstream.features.map(features => {
|
||||||
console.log(">>> Updating features in clusters ", this.id, ":", features)
|
|
||||||
if (!doCluster.data) {
|
if (!doCluster.data) {
|
||||||
summaryPoints.set([])
|
summaryPoints.set([])
|
||||||
return features
|
return features
|
||||||
|
@ -55,7 +57,7 @@ export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>>
|
||||||
const z = currentZoomlevel.data
|
const z = currentZoomlevel.data
|
||||||
const perTile = GeoOperations.spreadIntoBboxes(features, z)
|
const perTile = GeoOperations.spreadIntoBboxes(features, z)
|
||||||
const resultingFeatures = []
|
const resultingFeatures = []
|
||||||
const summary: Feature<Point>[] = []
|
const summary: Feature<Point, SummaryProperties>[] = []
|
||||||
for (const tileIndex of perTile.keys()) {
|
for (const tileIndex of perTile.keys()) {
|
||||||
const tileFeatures: Feature<Point>[] = perTile.get(tileIndex)
|
const tileFeatures: Feature<Point>[] = perTile.get(tileIndex)
|
||||||
if (tileFeatures.length > cutoff) {
|
if (tileFeatures.length > cutoff) {
|
||||||
|
@ -69,11 +71,12 @@ export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>>
|
||||||
|
|
||||||
}, [doCluster, currentZoomlevel]))
|
}, [doCluster, currentZoomlevel]))
|
||||||
|
|
||||||
|
ClusterGrouping.singleton.registerSource(summaryPoints)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private createSummaryFeature(features: Feature<Point>[], tileId: number): Feature<Point> {
|
private createSummaryFeature(features: Feature<Point>[], tileId: number): Feature<Point, SummaryProperties> {
|
||||||
|
|
||||||
let lon: number
|
let lon: number
|
||||||
let lat: number
|
let lat: number
|
||||||
|
@ -91,7 +94,7 @@ export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>>
|
||||||
lon = lonSum / features.length
|
lon = lonSum / features.length
|
||||||
lat = latSum / features.length
|
lat = latSum / features.length
|
||||||
}
|
}
|
||||||
return <Feature<Point>>{
|
return {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
geometry: {
|
geometry: {
|
||||||
type: "Point",
|
type: "Point",
|
||||||
|
@ -99,9 +102,68 @@ export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>>
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
id: "summary_" + this.id + "_" + tileId,
|
id: "summary_" + this.id + "_" + tileId,
|
||||||
z,
|
tile_id: tileId,
|
||||||
total_metric: "" + features.length
|
total: features.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups multiple summaries together
|
||||||
|
*/
|
||||||
|
export class ClusterGrouping implements FeatureSource<Feature<Point, { total_metric: string }>> {
|
||||||
|
private readonly _features: UIEventSource<Feature<Point, { total_metric: string }>[]> = new UIEventSource([])
|
||||||
|
public readonly features: Store<Feature<Point, { total_metric: string }>[]> = this._features
|
||||||
|
|
||||||
|
public static readonly singleton = new ClusterGrouping()
|
||||||
|
|
||||||
|
public readonly isDirty = new UIEventSource(false)
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
this.isDirty.stabilized(200).addCallback(dirty => {
|
||||||
|
if (dirty) {
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private allSource: Store<Feature<Point, { total: number, tile_id: number }>[]>[] = []
|
||||||
|
|
||||||
|
private update() {
|
||||||
|
const countPerTile = new Map<number, number>()
|
||||||
|
for (const source of this.allSource) {
|
||||||
|
for (const f of source.data) {
|
||||||
|
const id = f.properties.tile_id
|
||||||
|
const count = f.properties.total + (countPerTile.get(id) ?? 0)
|
||||||
|
countPerTile.set(id, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const features: Feature<Point, { total_metric: string, id: string }>[] = []
|
||||||
|
for (const tileId of countPerTile.keys()) {
|
||||||
|
const coordinates = Tiles.centerPointOf(tileId)
|
||||||
|
features.push({
|
||||||
|
type: "Feature",
|
||||||
|
properties: {
|
||||||
|
total_metric: "" + countPerTile.get(tileId),
|
||||||
|
id: "clustered_all_" + tileId
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
type: "Point",
|
||||||
|
coordinates
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this._features.set(features)
|
||||||
|
this.isDirty.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerSource(features: Store<Feature<Point, SummaryProperties>[]>) {
|
||||||
|
this.allSource.push(features)
|
||||||
|
features.addCallbackAndRun(() => {
|
||||||
|
//this.isDirty.set(true)
|
||||||
|
this.update()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -18,9 +18,10 @@ import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import NearbyFeatureSource from "../../Logic/FeatureSource/Sources/NearbyFeatureSource"
|
import NearbyFeatureSource from "../../Logic/FeatureSource/Sources/NearbyFeatureSource"
|
||||||
import {
|
import {
|
||||||
SummaryTileSource,
|
SummaryTileSource,
|
||||||
SummaryTileSourceRewriter,
|
SummaryTileSourceRewriter
|
||||||
} from "../../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource"
|
} from "../../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource"
|
||||||
import { ShowDataLayerOptions } from "../../UI/Map/ShowDataLayerOptions"
|
import { ShowDataLayerOptions } from "../../UI/Map/ShowDataLayerOptions"
|
||||||
|
import { ClusterGrouping } from "../../Logic/FeatureSource/TiledFeatureSource/ClusteringFeatureSource"
|
||||||
|
|
||||||
export class WithSpecialLayers extends WithChangesState {
|
export class WithSpecialLayers extends WithChangesState {
|
||||||
readonly favourites: FavouritesFeatureSource
|
readonly favourites: FavouritesFeatureSource
|
||||||
|
@ -61,6 +62,7 @@ export class WithSpecialLayers extends WithChangesState {
|
||||||
this.closestFeatures.registerSource(this.favourites, "favourite")
|
this.closestFeatures.registerSource(this.favourites, "favourite")
|
||||||
|
|
||||||
this.featureSummary = this.setupSummaryLayer()
|
this.featureSummary = this.setupSummaryLayer()
|
||||||
|
this.setupClusterLayer()
|
||||||
this.initActorsSpecialLayers()
|
this.initActorsSpecialLayers()
|
||||||
this.drawSelectedElement()
|
this.drawSelectedElement()
|
||||||
this.drawSpecialLayers()
|
this.drawSpecialLayers()
|
||||||
|
@ -128,6 +130,18 @@ export class WithSpecialLayers extends WithChangesState {
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On high zoom levels, the clusters will be gathered in the GroupClustering.
|
||||||
|
* This method is responsible for drawing it
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private setupClusterLayer(): void {
|
||||||
|
new ShowDataLayer(this.map, {
|
||||||
|
features: ClusterGrouping.singleton,
|
||||||
|
layer: new LayerConfig(<LayerConfigJson>(<unknown>summaryLayer), "summaryLayer")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
protected registerSpecialLayer(flayer: FilteredLayer, source: FeatureSource) {
|
protected registerSpecialLayer(flayer: FilteredLayer, source: FeatureSource) {
|
||||||
if (!source?.features) {
|
if (!source?.features) {
|
||||||
return
|
return
|
||||||
|
|
|
@ -53,11 +53,20 @@ export class Tiles {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the centerpoint [lon, lat] of the specified tile
|
* Returns the centerpoint [lon, lat] of the specified tile
|
||||||
* @param z
|
* @param z OR tileId
|
||||||
* @param x
|
* @param x
|
||||||
* @param y
|
* @param y
|
||||||
*/
|
*/
|
||||||
static centerPointOf(z: number, x: number, y: number): [number, number] {
|
static centerPointOf(z: number, x: number, y: number): [number, number] ;
|
||||||
|
static centerPointOf(tileId: number): [number, number] ;
|
||||||
|
|
||||||
|
static centerPointOf(zOrId: number, x?: number, y?: number): [number, number] {
|
||||||
|
let z: number
|
||||||
|
if (x === undefined) {
|
||||||
|
[z, x, y] = Tiles.tile_from_index(zOrId)
|
||||||
|
} else {
|
||||||
|
z = zOrId
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
(Tiles.tile2long(x, z) + Tiles.tile2long(x + 1, z)) / 2,
|
(Tiles.tile2long(x, z) + Tiles.tile2long(x + 1, z)) / 2,
|
||||||
(Tiles.tile2lat(y, z) + Tiles.tile2lat(y + 1, z)) / 2,
|
(Tiles.tile2lat(y, z) + Tiles.tile2lat(y + 1, z)) / 2,
|
||||||
|
|
|
@ -3,9 +3,9 @@ import type { AddLayerObject, Map as MlMap } from "maplibre-gl"
|
||||||
import { GeoJSONSource } from "maplibre-gl"
|
import { GeoJSONSource } from "maplibre-gl"
|
||||||
import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
|
import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
import { FeatureSource, FeatureSourceForLayer } from "../../Logic/FeatureSource/FeatureSource"
|
import { FeatureSource } from "../../Logic/FeatureSource/FeatureSource"
|
||||||
import { BBox } from "../../Logic/BBox"
|
import { BBox } from "../../Logic/BBox"
|
||||||
import { Feature } from "geojson"
|
import { Feature, Geometry } from "geojson"
|
||||||
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"
|
||||||
|
@ -16,7 +16,7 @@ import { TagsFilter } from "../../Logic/Tags/TagsFilter"
|
||||||
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||||
import { PointRenderingLayer } from "./PointRenderingLayer"
|
import { PointRenderingLayer } from "./PointRenderingLayer"
|
||||||
import { ClusteringFeatureSource } from "../../Logic/FeatureSource/TiledFeatureSource/ClusteringFeatureSource"
|
import { ClusteringFeatureSource } from "../../Logic/FeatureSource/TiledFeatureSource/ClusteringFeatureSource"
|
||||||
import summaryLayer from "../../../public/assets/generated/layers/summary.json"
|
import { OsmTags } from "../../Models/OsmFeature"
|
||||||
|
|
||||||
class LineRenderingLayer {
|
class LineRenderingLayer {
|
||||||
/**
|
/**
|
||||||
|
@ -361,7 +361,7 @@ export default class ShowDataLayer {
|
||||||
layers: LayerConfig[],
|
layers: LayerConfig[],
|
||||||
options?: Partial<Omit<ShowDataLayerOptions, "features" | "layer">>
|
options?: Partial<Omit<ShowDataLayerOptions, "features" | "layer">>
|
||||||
) {
|
) {
|
||||||
const perLayer: PerLayerFeatureSourceSplitter<FeatureSourceForLayer> =
|
const perLayer =
|
||||||
new PerLayerFeatureSourceSplitter(
|
new PerLayerFeatureSourceSplitter(
|
||||||
layers.filter((l) => l.source !== null).map((l) => new FilteredLayer(l)),
|
layers.filter((l) => l.source !== null).map((l) => new FilteredLayer(l)),
|
||||||
features,
|
features,
|
||||||
|
@ -379,10 +379,10 @@ export default class ShowDataLayer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
perLayer.forEach((fs) => {
|
perLayer.forEach((features) => {
|
||||||
new ShowDataLayer(mlmap, {
|
new ShowDataLayer(mlmap, {
|
||||||
layer: fs.layer.layerDef,
|
layer: features.layer.layerDef,
|
||||||
features: fs,
|
features,
|
||||||
...(options ?? {}),
|
...(options ?? {}),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -396,14 +396,10 @@ export default class ShowDataLayer {
|
||||||
const clustering = new ClusteringFeatureSource(feats, state.mapProperties.zoom.map(z => z + 2),
|
const clustering = new ClusteringFeatureSource(feats, state.mapProperties.zoom.map(z => z + 2),
|
||||||
options.layer.id,
|
options.layer.id,
|
||||||
{
|
{
|
||||||
cutoff: 2,
|
cutoff: 7,
|
||||||
showSummaryAt: "tilecenter"
|
showSummaryAt: "tilecenter"
|
||||||
})
|
})
|
||||||
new ShowDataLayer(mlmap, {
|
|
||||||
features: clustering.summaryPoints,
|
|
||||||
layer: new LayerConfig(<LayerConfigJson>(<unknown>summaryLayer), "summaryLayer")
|
|
||||||
// doShowLayer: this.mapProperties.zoom.map((z) => z < maxzoom),
|
|
||||||
})
|
|
||||||
return clustering
|
return clustering
|
||||||
}
|
}
|
||||||
new ShowDataLayer(mlmap, options)
|
new ShowDataLayer(mlmap, options)
|
||||||
|
@ -411,7 +407,7 @@ export default class ShowDataLayer {
|
||||||
|
|
||||||
public static showRange(
|
public static showRange(
|
||||||
map: Store<MlMap>,
|
map: Store<MlMap>,
|
||||||
features: FeatureSource,
|
features: FeatureSource<Feature<Geometry, OsmTags>>,
|
||||||
doShowLayer?: Store<boolean>
|
doShowLayer?: Store<boolean>
|
||||||
): ShowDataLayer {
|
): ShowDataLayer {
|
||||||
return new ShowDataLayer(map, {
|
return new ShowDataLayer(map, {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { FeatureSource } from "../../Logic/FeatureSource/FeatureSource"
|
import { FeatureSource } from "../../Logic/FeatureSource/FeatureSource"
|
||||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import { Feature, Point } from "geojson"
|
import { Feature, Geometry, Point } from "geojson"
|
||||||
|
import { OsmTags } from "../../Models/OsmFeature"
|
||||||
|
|
||||||
export interface ShowDataLayerOptions {
|
export interface ShowDataLayerOptions {
|
||||||
/**
|
/**
|
||||||
* Features to show
|
* Features to show
|
||||||
*/
|
*/
|
||||||
features: FeatureSource
|
features: FeatureSource<Feature<Geometry, OsmTags>>
|
||||||
/**
|
/**
|
||||||
* 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
|
* When a feature is tapped, the feature will be put in there
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue