further improve event listener performance

This commit is contained in:
vcoppe
2026-02-01 15:57:18 +01:00
parent 0ab3b77db8
commit 9895c3c304
5 changed files with 114 additions and 59 deletions

View File

@@ -227,6 +227,56 @@ export class GPXLayer {
layerEventManager.on('mouseleave', this.fileId, this.layerOnMouseLeaveBinded); layerEventManager.on('mouseleave', this.fileId, this.layerOnMouseLeaveBinded);
layerEventManager.on('mousemove', this.fileId, this.layerOnMouseMoveBinded); layerEventManager.on('mousemove', this.fileId, this.layerOnMouseMoveBinded);
} }
let visibleTrackSegmentIds: string[] = [];
file.forEachSegment((segment, trackIndex, segmentIndex) => {
if (!segment._data.hidden) {
visibleTrackSegmentIds.push(`${trackIndex}-${segmentIndex}`);
}
});
const segmentFilter: FilterSpecification = [
'in',
['get', 'trackSegmentId'],
['literal', visibleTrackSegmentIds],
];
_map.setFilter(this.fileId, segmentFilter, { validate: false });
if (get(directionMarkers)) {
if (!_map.getLayer(this.fileId + '-direction')) {
_map.addLayer(
{
id: this.fileId + '-direction',
type: 'symbol',
source: this.fileId,
layout: {
'text-field': '»',
'text-offset': [0, -0.1],
'text-keep-upright': false,
'text-max-angle': 361,
'text-allow-overlap': true,
'text-font': ['Open Sans Bold'],
'symbol-placement': 'line',
'symbol-spacing': 20,
},
paint: {
'text-color': 'white',
'text-opacity': 0.7,
'text-halo-width': 0.2,
'text-halo-color': 'white',
},
},
ANCHOR_LAYER_KEY.directionMarkers
);
}
_map.setFilter(this.fileId + '-direction', segmentFilter, { validate: false });
} else {
if (_map.getLayer(this.fileId + '-direction')) {
_map.removeLayer(this.fileId + '-direction');
}
}
let waypointSource = _map.getSource(this.fileId + '-waypoints') as let waypointSource = _map.getSource(this.fileId + '-waypoints') as
| GeoJSONSource | GeoJSONSource
| undefined; | undefined;
@@ -284,57 +334,6 @@ export class GPXLayer {
); );
} }
if (get(directionMarkers)) {
if (!_map.getLayer(this.fileId + '-direction')) {
_map.addLayer(
{
id: this.fileId + '-direction',
type: 'symbol',
source: this.fileId,
layout: {
'text-field': '»',
'text-offset': [0, -0.1],
'text-keep-upright': false,
'text-max-angle': 361,
'text-allow-overlap': true,
'text-font': ['Open Sans Bold'],
'symbol-placement': 'line',
'symbol-spacing': 20,
},
paint: {
'text-color': 'white',
'text-opacity': 0.7,
'text-halo-width': 0.2,
'text-halo-color': 'white',
},
},
ANCHOR_LAYER_KEY.directionMarkers
);
}
} else {
if (_map.getLayer(this.fileId + '-direction')) {
_map.removeLayer(this.fileId + '-direction');
}
}
let visibleTrackSegmentIds: string[] = [];
file.forEachSegment((segment, trackIndex, segmentIndex) => {
if (!segment._data.hidden) {
visibleTrackSegmentIds.push(`${trackIndex}-${segmentIndex}`);
}
});
const segmentFilter: FilterSpecification = [
'in',
['get', 'trackSegmentId'],
['literal', visibleTrackSegmentIds],
];
_map.setFilter(this.fileId, segmentFilter, { validate: false });
if (_map.getLayer(this.fileId + '-direction')) {
_map.setFilter(this.fileId + '-direction', segmentFilter, { validate: false });
}
let visibleWaypoints: number[] = []; let visibleWaypoints: number[] = [];
file.wpt.forEach((waypoint, waypointIndex) => { file.wpt.forEach((waypoint, waypointIndex) => {
if (!waypoint._data.hidden) { if (!waypoint._data.hidden) {

View File

@@ -76,7 +76,14 @@ export class OverpassLayer {
update() { update() {
this.loadIcons(); this.loadIcons();
let d = get(data); const fullData = get(data);
const queries = getCurrentQueries();
const d: GeoJSON.FeatureCollection = {
type: 'FeatureCollection',
features: fullData.features.filter((feature) =>
queries.includes(feature.properties!.query)
),
};
try { try {
let source = this.map.getSource('overpass') as GeoJSONSource | undefined; let source = this.map.getSource('overpass') as GeoJSONSource | undefined;
@@ -108,10 +115,6 @@ export class OverpassLayer {
this.layerEventManager.on('mouseenter', 'overpass', this.onHoverBinded); this.layerEventManager.on('mouseenter', 'overpass', this.onHoverBinded);
this.layerEventManager.on('click', 'overpass', this.onHoverBinded); this.layerEventManager.on('click', 'overpass', this.onHoverBinded);
} }
this.map.setFilter('overpass', ['in', 'query', ...getCurrentQueries()], {
validate: false,
});
} catch (e) { } catch (e) {
// No reliable way to check if the map is ready to add sources and layers // No reliable way to check if the map is ready to add sources and layers
} }

View File

@@ -1,3 +1,4 @@
import { fileStateCollection } from '$lib/logic/file-state';
import maplibregl from 'maplibre-gl'; import maplibregl from 'maplibre-gl';
type MapLayerMouseEventListener = (e: maplibregl.MapLayerMouseEvent) => void; type MapLayerMouseEventListener = (e: maplibregl.MapLayerMouseEvent) => void;
@@ -141,7 +142,10 @@ export class MapLayerEventManager {
} }
private _handleMouseMove(e: maplibregl.MapMouseEvent) { private _handleMouseMove(e: maplibregl.MapMouseEvent) {
const layerIds = Object.keys(this._listeners); const layerIds = this._filterLayersContainingCoordinate(
Object.keys(this._listeners),
e.lngLat
);
const features = const features =
layerIds.length > 0 layerIds.length > 0
? this._map.queryRenderedFeatures(e.point, { layers: layerIds }) ? this._map.queryRenderedFeatures(e.point, { layers: layerIds })
@@ -224,8 +228,11 @@ export class MapLayerEventManager {
} }
private _handleTouchStart(e: maplibregl.MapTouchEvent) { private _handleTouchStart(e: maplibregl.MapTouchEvent) {
const layerIds = Object.keys(this._listeners).filter( const layerIds = this._filterLayersContainingCoordinate(
Object.keys(this._listeners).filter(
(layerId) => this._listeners[layerId].touchstarts.length > 0 (layerId) => this._listeners[layerId].touchstarts.length > 0
),
e.lngLat
); );
if (layerIds.length === 0) return; if (layerIds.length === 0) return;
const features = this._map.queryRenderedFeatures(e.points[0], { layers: layerIds }); const features = this._map.queryRenderedFeatures(e.points[0], { layers: layerIds });
@@ -250,4 +257,19 @@ export class MapLayerEventManager {
} }
}); });
} }
private _filterLayersContainingCoordinate(
layerIds: string[],
lngLat: maplibregl.LngLat
): string[] {
let result = layerIds.filter((layerId) => {
const fileId = layerId.replace('-waypoints', '');
if (fileId === layerId) {
return fileStateCollection.getStatistics(fileId)?.inBBox(lngLat) ?? true;
} else {
return fileStateCollection.getStatistics(fileId)?.inWaypointBBox(lngLat) ?? true;
}
});
return result;
}
} }

View File

@@ -157,6 +157,9 @@ export class StyleManager {
const terrain = this.getCurrentTerrain(); const terrain = this.getCurrentTerrain();
if (JSON.stringify(mapTerrain) !== JSON.stringify(terrain)) { if (JSON.stringify(mapTerrain) !== JSON.stringify(terrain)) {
if (terrain.exaggeration > 0) { if (terrain.exaggeration > 0) {
if (!map_.getSource(terrain.source)) {
map_.addSource(terrain.source, terrainSources[terrain.source]);
}
map_.setTerrain(terrain); map_.setTerrain(terrain);
} else { } else {
map_.setTerrain(null); map_.setTerrain(null);

View File

@@ -1,18 +1,24 @@
import { ListItem, ListLevel } from '$lib/components/file-list/file-list'; import { ListItem, ListLevel } from '$lib/components/file-list/file-list';
import { GPXFile, GPXStatistics, GPXStatisticsGroup, type Track } from 'gpx'; import { GPXFile, GPXStatistics, GPXStatisticsGroup, type Track } from 'gpx';
import maplibregl from 'maplibre-gl';
export class GPXStatisticsTree { export class GPXStatisticsTree {
level: ListLevel; level: ListLevel;
statistics: { statistics: {
[key: string]: GPXStatisticsTree | GPXStatistics; [key: string]: GPXStatisticsTree | GPXStatistics;
} = {}; } = {};
wptBounds: maplibregl.LngLatBounds;
constructor(element: GPXFile | Track) { constructor(element: GPXFile | Track) {
this.wptBounds = new maplibregl.LngLatBounds();
if (element instanceof GPXFile) { if (element instanceof GPXFile) {
this.level = ListLevel.FILE; this.level = ListLevel.FILE;
element.children.forEach((child, index) => { element.children.forEach((child, index) => {
this.statistics[index] = new GPXStatisticsTree(child); this.statistics[index] = new GPXStatisticsTree(child);
}); });
element.wpt.forEach((wpt) => {
this.wptBounds.extend(wpt.getCoordinates());
});
} else { } else {
this.level = ListLevel.TRACK; this.level = ListLevel.TRACK;
element.children.forEach((child, index) => { element.children.forEach((child, index) => {
@@ -42,5 +48,27 @@ export class GPXStatisticsTree {
} }
return statistics; return statistics;
} }
inBBox(coordinates: { lat: number; lng: number }): boolean {
for (let key in this.statistics) {
const stats = this.statistics[key];
if (stats instanceof GPXStatistics) {
const bbox = new maplibregl.LngLatBounds(
stats.global.bounds.southWest,
stats.global.bounds.northEast
);
if (!bbox.isEmpty() && bbox.contains(coordinates)) {
return true;
}
} else if (stats.inBBox(coordinates)) {
return true;
}
}
return false;
}
inWaypointBBox(coordinates: { lat: number; lng: number }): boolean {
return !this.wptBounds.isEmpty() && this.wptBounds.contains(coordinates);
}
} }
export type GPXFileWithStatistics = { file: GPXFile; statistics: GPXStatisticsTree }; export type GPXFileWithStatistics = { file: GPXFile; statistics: GPXStatisticsTree };