Feature: second iteration of clustering

This commit is contained in:
Pieter Vander Vennet 2025-07-21 22:02:04 +02:00
parent 8360ab9a8b
commit 5bc8f11d24
3 changed files with 25 additions and 5 deletions

View file

@ -8,7 +8,8 @@ import { Utils } from "../../../Utils"
import { TagsFilter } from "../../Tags/TagsFilter" import { TagsFilter } from "../../Tags/TagsFilter"
import { BBox } from "../../BBox" import { BBox } from "../../BBox"
import { OsmTags } from "../../../Models/OsmFeature" import { OsmTags } from "../../../Models/OsmFeature"
;("use strict")
("use strict")
/** /**
* A wrapper around the 'Overpass'-object. * A wrapper around the 'Overpass'-object.
@ -138,7 +139,6 @@ export default class OverpassFeatureSource implements UpdatableFeatureSource {
return undefined return undefined
} }
this.runningQuery.setData(true) this.runningQuery.setData(true)
console.trace("Overpass feature source: querying geojson")
data = (await overpass.queryGeoJson(bounds))[0] data = (await overpass.queryGeoJson(bounds))[0]
} catch (e) { } catch (e) {
this.retries.data++ this.retries.data++

View file

@ -15,12 +15,15 @@ export interface ClusteringOptions {
* drop those features and emit a summary tile instead * drop those features and emit a summary tile instead
*/ */
cutoff?: 20 | number cutoff?: 20 | number
showSummaryAt?: "tilecenter" | "average"
} }
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 public readonly summaryPoints: FeatureSource
private readonly id: string private readonly id: string
private readonly showSummaryAt: "tilecenter" | "average"
features: Store<T[]> features: Store<T[]>
/** /**
@ -35,6 +38,7 @@ export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>>
id: string, id: string,
options?: ClusteringOptions) { options?: ClusteringOptions) {
this.id = id this.id = id
this.showSummaryAt = options?.showSummaryAt ?? "average"
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
@ -42,6 +46,7 @@ export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>>
currentZoomlevel = currentZoomlevel.stabilized(500) currentZoomlevel = currentZoomlevel.stabilized(500)
this.summaryPoints = new StaticFeatureSource(summaryPoints) 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
@ -69,8 +74,23 @@ export class ClusteringFeatureSource<T extends Feature<Point> = Feature<Point>>
private createSummaryFeature(features: Feature<Point>[], tileId: number): Feature<Point> { private createSummaryFeature(features: Feature<Point>[], tileId: number): Feature<Point> {
let lon: number
let lat: number
const [z, x, y] = Tiles.tile_from_index(tileId) const [z, x, y] = Tiles.tile_from_index(tileId)
const [lon, lat] = Tiles.centerPointOf(z, x, y) if (this.showSummaryAt === "tilecenter") {
[lon, lat] = Tiles.centerPointOf(z, x, y)
} else {
let lonSum = 0
let latSum = 0
for (const feature of features) {
const [lon, lat] = feature.geometry.coordinates
lonSum += lon
latSum += lat
}
lon = lonSum / features.length
lat = latSum / features.length
}
return <Feature<Point>>{ return <Feature<Point>>{
type: "Feature", type: "Feature",
geometry: { geometry: {

View file

@ -351,7 +351,6 @@ export default class ShowDataLayer {
drawLines?: true | boolean drawLines?: true | boolean
} }
) { ) {
console.trace("Creating a data layer for", options.layer.id)
this._options = options this._options = options
this.onDestroy.push(map.addCallbackAndRunD((map) => this.initDrawFeatures(map))) this.onDestroy.push(map.addCallbackAndRunD((map) => this.initDrawFeatures(map)))
} }
@ -397,7 +396,8 @@ 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: 5 cutoff: 2,
showSummaryAt: "tilecenter"
}) })
new ShowDataLayer(mlmap, { new ShowDataLayer(mlmap, {
features: clustering.summaryPoints, features: clustering.summaryPoints,