Files
gpx.studio/website/src/lib/engine/file-state.ts
T

107 lines
4.0 KiB
TypeScript
Raw Normal View History

2026-10-09 08:58:00 +02:00
import type { Feature, LineString } from 'geojson';
2026-10-11 11:18:36 +02:00
import { normalizeColor, type FileColorAllocator } from '$lib/engine/file-colors';
import type { Visibility } from '$lib/engine/file-visibility';
2026-10-14 08:51:38 +02:00
import type { Backend } from './backend';
import { toPositions } from './convert';
2026-10-09 08:58:00 +02:00
import { EMPTY_STATISTICS, type FileState, type SegmentProperties } from './types';
/** Reads the state of a file, reusing from `previous` what did not change. */
2026-10-14 08:51:38 +02:00
export async function readFileState(
backend: Backend,
2026-10-09 08:58:00 +02:00
id: string,
colors: FileColorAllocator,
visibility: ReadonlyMap<string, Visibility>,
previous?: FileState
2026-10-14 08:51:38 +02:00
): Promise<FileState | null> {
const structure = await backend.file_structure(id);
2026-10-09 08:58:00 +02:00
if (!structure) {
return null;
}
const color = colors.resolve(
id,
structure.tracks.map((track) => track.color)
);
const previousSegments = new Map(
previous?.segments.features.map((f) => [f.properties.segmentId, f]) ?? []
);
2026-10-14 08:51:38 +02:00
// the coordinates of the segments that changed, read together
const segments: (
| Feature<LineString, SegmentProperties>
| Promise<Feature<LineString, SegmentProperties>>
)[] = [];
2026-10-09 08:58:00 +02:00
structure.tracks.forEach((track, trackIndex) => {
track.segments.forEach((segment, segmentIndex) => {
const properties: SegmentProperties = {
fileId: id,
trackId: track.id,
segmentId: segment.id,
trackIndex,
segmentIndex,
rev: segment.rev,
color: track.color !== undefined ? normalizeColor(track.color) : color,
opacity: track.opacity,
width: track.width,
};
const old = previousSegments.get(segment.id);
const unchanged =
old !== undefined &&
(Object.keys(properties) as (keyof SegmentProperties)[]).every(
(key) => old.properties[key] === properties[key]
);
segments.push(
unchanged
? old
2026-10-14 08:51:38 +02:00
: backend.segment_coordinates(segment.id).then((coordinates) => ({
2026-10-09 08:58:00 +02:00
type: 'Feature',
geometry: {
type: 'LineString',
2026-10-14 08:51:38 +02:00
coordinates: toPositions(coordinates),
2026-10-09 08:58:00 +02:00
},
properties,
2026-10-14 08:51:38 +02:00
}))
2026-10-09 08:58:00 +02:00
);
});
});
2026-10-14 08:51:38 +02:00
const segmentFeatures = await Promise.all(segments);
2026-10-09 08:58:00 +02:00
const previousStructure = previous?.structure;
const waypointsUnchanged =
previousStructure !== undefined &&
previousStructure.waypointsRev === structure.waypointsRev &&
previousStructure.waypoints.length === structure.waypoints.length &&
previousStructure.waypoints.every(
(w, i) =>
w.id === structure.waypoints[i].id &&
w.name === structure.waypoints[i].name &&
w.sym === structure.waypoints[i].sym
);
let waypoints = previous?.waypoints;
if (!waypoints || !waypointsUnchanged) {
2026-10-14 08:51:38 +02:00
const coordinates = toPositions(await backend.waypoint_coordinates(id));
2026-10-09 08:58:00 +02:00
waypoints = {
type: 'FeatureCollection',
features: structure.waypoints.map((waypoint, index) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: coordinates[index] },
properties: {
fileId: id,
waypointId: waypoint.id,
index,
name: waypoint.name,
sym: waypoint.sym,
},
})),
};
}
return {
structure,
color,
2026-10-14 08:51:38 +02:00
statistics: (await backend.file_statistics(id)) ?? EMPTY_STATISTICS,
segments: { type: 'FeatureCollection', features: segmentFeatures },
2026-10-09 08:58:00 +02:00
waypoints,
visibility: visibility.get(id) ?? new Map(),
};
}