mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-20 13:51:45 +00:00
feat(import): selective GPX/KML element import and performance improvements
Add type-selector UI in the file import modal letting users choose which GPX elements (waypoints, routes, tracks) or KML/KMZ elements (points, paths) to import. KML LineString placemarks are now imported as path places with route_geometry. Performance improvements: - Extract MemoPlaceRow with React.memo and contentVisibility:auto to cut unnecessary re-renders in PlacesSidebar - Add weatherQueue to cap concurrent weather fetches at 3 - Replace sequential per-place deletes with a single bulkDelete API call (new DELETE /places/bulk endpoint + deletePlacesMany service) - Memoize atlas/photo/weather service calls to avoid redundant requests - Add multi-select mode to PlacesSidebar for bulk operations Add large GPX/KML/KMZ fixtures for integration/perf testing and two profiler analysis scripts under scripts/.
This commit is contained in:
@@ -6,6 +6,7 @@ export interface ParsedKmlPlacemark {
|
||||
lat: number | null;
|
||||
lng: number | null;
|
||||
folderName: string | null;
|
||||
routeGeometry: string | null;
|
||||
}
|
||||
|
||||
export interface KmlPlacemarkNode {
|
||||
@@ -97,6 +98,26 @@ export function sanitizeKmlDescription(value: unknown): string | null {
|
||||
return decoded || null;
|
||||
}
|
||||
|
||||
export function parseKmlLineStringCoordinates(value: unknown): Array<{ lat: number; lng: number; ele: number | null }> | null {
|
||||
const coordinates = asTrimmedString(value);
|
||||
if (!coordinates) return null;
|
||||
|
||||
const points = coordinates
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.map(coord => {
|
||||
const parts = coord.split(',');
|
||||
const lng = Number.parseFloat(parts[0] ?? '');
|
||||
const lat = Number.parseFloat(parts[1] ?? '');
|
||||
const eleRaw = parts[2] != null ? Number.parseFloat(parts[2]) : NaN;
|
||||
if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null;
|
||||
return { lat, lng, ele: Number.isFinite(eleRaw) ? eleRaw : null };
|
||||
})
|
||||
.filter((p): p is { lat: number; lng: number; ele: number | null } => p !== null);
|
||||
|
||||
return points.length >= 2 ? points : null;
|
||||
}
|
||||
|
||||
export function parseKmlPointCoordinates(value: unknown): { lat: number; lng: number } | null {
|
||||
const coordinates = asTrimmedString(value);
|
||||
if (!coordinates) return null;
|
||||
@@ -167,13 +188,25 @@ export function extractKmlPlacemarkNodes(kmlRoot: any): KmlPlacemarkNode[] {
|
||||
}
|
||||
|
||||
export function parsePlacemarkNode(node: KmlPlacemarkNode): ParsedKmlPlacemark {
|
||||
const coordinates = parseKmlPointCoordinates(node.placemark?.Point?.coordinates);
|
||||
const pointCoords = parseKmlPointCoordinates(node.placemark?.Point?.coordinates);
|
||||
|
||||
let routeGeometry: string | null = null;
|
||||
let pathFirstPt: { lat: number; lng: number } | null = null;
|
||||
if (!pointCoords) {
|
||||
const linePts = parseKmlLineStringCoordinates(node.placemark?.LineString?.coordinates);
|
||||
if (linePts) {
|
||||
pathFirstPt = { lat: linePts[0].lat, lng: linePts[0].lng };
|
||||
const hasAllEle = linePts.every(p => p.ele !== null);
|
||||
routeGeometry = JSON.stringify(linePts.map(p => hasAllEle ? [p.lat, p.lng, p.ele] : [p.lat, p.lng]));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: asTrimmedString(node.placemark?.name),
|
||||
description: sanitizeKmlDescription(node.placemark?.description),
|
||||
lat: coordinates?.lat ?? null,
|
||||
lng: coordinates?.lng ?? null,
|
||||
lat: pointCoords?.lat ?? pathFirstPt?.lat ?? null,
|
||||
lng: pointCoords?.lng ?? pathFirstPt?.lng ?? null,
|
||||
folderName: node.folderName,
|
||||
routeGeometry,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user