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(),
|
|
|
|
|
};
|
|
|
|
|
}
|