diff --git a/leaflet/src/bbox-handler.ts b/leaflet/src/bbox-handler.ts index 4d0f91a0..edab69e9 100644 --- a/leaflet/src/bbox-handler.ts +++ b/leaflet/src/bbox-handler.ts @@ -1,7 +1,7 @@ import type Client from "facilmap-client"; import type { ClientEvents } from "facilmap-client"; import type { EventHandler } from "facilmap-types"; -import { Handler, LatLng, LatLngBounds, Map } from "leaflet"; +import { Bounds, Handler, LatLng, LatLngBounds, Map } from "leaflet"; import { leafletToFmBbox } from "./utils/leaflet"; const flyToBkp = Map.prototype.flyTo; @@ -11,9 +11,23 @@ Map.prototype.flyTo = function(...args) { return flyToBkp.apply(this, args); }; +function extendBounds(bounds: Bounds, pixels: number): Bounds { + return new Bounds( + [bounds.getTopLeft().x - pixels, bounds.getTopLeft().y - pixels], + [bounds.getBottomRight().x + pixels, bounds.getBottomRight().y + pixels] + ); +} + export default class BboxHandler extends Handler { - margin = 50; + /** + * The min and max values how much wider the map pixel bounds should be used than what they actually are. + * By default, the max value is used. If the map is panned and the last bounds are still within the range + * of the min value, no update is triggered. This avoids an update every second when the locate control + * adjusts the map position very slightly. + */ + margin = [50, 400]; + lastBounds: { bounds: Bounds; zoom: number } | undefined = undefined; client: Client; @@ -26,17 +40,23 @@ export default class BboxHandler extends Handler { if (!this._map._loaded && (center == null || zoom == null)) return; - const pixelBounds = this._map.getPixelBounds(center, zoom); - pixelBounds.min!.x -= this.margin; - pixelBounds.min!.y -= this.margin; - pixelBounds.max!.x += this.margin; - pixelBounds.max!.y += this.margin; + const resolvedZoom = zoom ?? this._map.getZoom(); + const pixelBounds = this._map.getPixelBounds(center, resolvedZoom); + + if (this.lastBounds && this.lastBounds.zoom === resolvedZoom) { + const minBounds = extendBounds(pixelBounds, this.margin[0]); + if (this.lastBounds.bounds.contains(minBounds)) { + return; + } + } + const maxBounds = extendBounds(pixelBounds, this.margin[1]); const bounds = new LatLngBounds( - this._map.unproject(pixelBounds.getBottomLeft(), zoom), - this._map.unproject(pixelBounds.getTopRight(), zoom) + this._map.unproject(maxBounds.getBottomLeft(), resolvedZoom), + this._map.unproject(maxBounds.getTopRight(), resolvedZoom) ); - void this.client.updateBbox(leafletToFmBbox(bounds ?? this._map.getBounds(), zoom ?? this._map.getZoom())); + void this.client.updateBbox(leafletToFmBbox(bounds, resolvedZoom)); + this.lastBounds = { bounds: maxBounds, zoom: resolvedZoom }; } shouldUpdateBbox(): boolean { @@ -72,6 +92,10 @@ export default class BboxHandler extends Handler { this._map.on("viewreset", this.handleViewReset); this._map.on("fmFlyTo", this.handleFlyTo); this.client.on("emitResolve", this.handleEmitResolve); + + if (this.shouldUpdateBbox()) { + this.updateBbox(); + } } removeHooks(): void { @@ -79,5 +103,6 @@ export default class BboxHandler extends Handler { this._map.off("viewreset", this.handleViewReset); this._map.off("fmFlyTo", this.handleFlyTo); this.client.removeListener("emitResolve", this.handleEmitResolve); + this.lastBounds = undefined; } } \ No newline at end of file