import and export of routes, ref #192

This commit is contained in:
vcoppe committed 2026-10-09 09:26:38 +02:00
1 parent da9abbae5e
commit dc36de2a48
10 files changed
+620 -62

No files matched your search

@@ -21,6 +21,7 @@
Orbit,
Thermometer,
SquareActivity,
Route,
} from '@lucide/svelte';
import { i18n } from '$lib/i18n.svelte';
import { engine, type ExportOptions } from '$lib/engine';
@@ -37,6 +38,7 @@
atemp: true,
power: true,
osm: false,
asRoute: false,
});
// the files that are exported
let fileIds: string[] = $derived.by(() => {
@@ -61,6 +63,8 @@
atemp: !available.atemp,
power: !available.power,
osm: !available.osm,
// always possible
asRoute: false,
};
});
@@ -113,6 +117,7 @@
atemp: exportOptions.atemp && !hide.atemp,
power: exportOptions.power && !hide.power,
osm: exportOptions.osm && !hide.osm,
asRoute: exportOptions.asRoute,
};
if (exportState.current === ExportState.SELECTION) {
exportSelectedFiles(options);
@@ -192,6 +197,13 @@
{i18n._('quantities.osm_extensions')}
</Label>
</div>
<div class="flex flex-row items-center gap-1.5">
<Checkbox id="export-as-route" bind:checked={exportOptions.asRoute} />
<Label for="export-as-route" class="flex flex-row items-center gap-1">
<Route size="16" />
{i18n._('menu.export_as_route', 'Export as route')}
</Label>
</div>
</div>
</div>
</Dialog.Content>
+51 -1
View File
@@ -5,7 +5,14 @@ import { readFileSync } from 'node:fs';
import { beforeEach, describe, it } from 'node:test';
import { get } from 'svelte/store';
import { isHidden } from '$lib/file-visibility';
import { engine, FileStateCollectionObserver, NO_TIME, type FileState } from '$lib/engine';
import {
ALL_EXPORT_OPTIONS,
engine,
FileStateCollectionObserver,
NO_TIME,
type ExportOptions,
type FileState,
} from '$lib/engine';
function fixture(name: string): Uint8Array {
return new Uint8Array(
@@ -387,6 +394,49 @@ describe('observer of the files', () => {
});
});
describe('export', () => {
const text = (id: string, options: Partial<ExportOptions> = {}) =>
new TextDecoder().decode(engine.exportFile(id, { ...ALL_EXPORT_OPTIONS, ...options }));
it('writes tracks, or routes when asked to', async () => {
await load('with_tracks_and_segments');
const file = fileStates()[0];
const segments = file.structure.tracks.reduce(
(sum, track) => sum + track.segments.length,
0
);
const id = file.structure.id;
const tracks = text(id);
assert.ok(tracks.includes('<trk>') && !tracks.includes('<rte>'));
assert.equal(tracks.match(/<trkseg>/g)?.length, segments);
const routes = text(id, { asRoute: true });
assert.ok(routes.includes('<rte>') && !routes.includes('<trk>'));
assert.equal(routes.match(/<rte>/g)?.length, segments);
assert.ok(routes.includes('<rtept'));
});
it('does not count the way a file is written among its data', async () => {
await load('simple');
const data = engine.exportableData(get(engine.order));
assert.equal(data.asRoute, false);
});
it('can read back the routes it writes', async () => {
await load('with_tracks_and_segments');
const id = get(engine.order)[0];
const before = fileStates()[0].segments.features.length;
const routes = engine.exportFile(id, { ...ALL_EXPORT_OPTIONS, asRoute: true })!;
await engine.loadFiles([{ data: routes, name: 'routes' }]);
const [, loaded] = get(engine.order);
const state = get(get(engine.files).get(loaded)!);
// one route per segment, each read as a track of one segment
assert.equal(state.structure.tracks.length, before);
assert.equal(state.segments.features.length, before);
});
});
describe('visibility', () => {
it('hides the selection and shows it again', async () => {
await load('simple');
+4 -1
View File
@@ -509,7 +509,8 @@ class Engine {
options.cad,
options.atemp,
options.power,
options.osm
options.osm,
options.asRoute
) as Uint8Array<ArrayBuffer> | undefined;
}
@@ -523,6 +524,8 @@ class Engine {
atemp: (bits & 8) !== 0,
power: (bits & 16) !== 0,
osm: (bits & 32) !== 0,
// not a data
asRoute: false,
};
}
+6
View File
@@ -117,6 +117,11 @@ export type ExportOptions = {
power: boolean;
/** The OpenStreetMap data: surface, highway, SAC scale and MTB scale. */
osm: boolean;
/**
* Writes the segments as routes instead of tracks: the anchors are the route points, and the
* trackpoints between them the points of their path (which only keep their position).
*/
asRoute: boolean;
};
export const ALL_EXPORT_OPTIONS: ExportOptions = {
@@ -126,6 +131,7 @@ export const ALL_EXPORT_OPTIONS: ExportOptions = {
atemp: true,
power: true,
osm: true,
asRoute: false,
};
/**
+1
View File
@@ -20,6 +20,7 @@
"export": "Export...",
"export_all": "Export all...",
"export_options": "Export options",
"export_as_route": "Export as route",
"support_message": "The tool is free to use, but not free to run. Please consider supporting the website if you use it frequently. Thank you!",
"support_button": "Help keep the website free",
"download_file": "Download file",