mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-02-06 00:13:09 +00:00
further improve event listener performance
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
(layerId) => this._listeners[layerId].touchstarts.length > 0
|
Object.keys(this._listeners).filter(
|
||||||
|
(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
Reference in New Issue
Block a user