diff --git a/gpx/src/gpx.ts b/gpx/src/gpx.ts index a5b5df29..121fd120 100644 --- a/gpx/src/gpx.ts +++ b/gpx/src/gpx.ts @@ -775,12 +775,16 @@ export class TrackSegment extends GPXTreeLeaf { } } - if (i > 0 && points[i - 1].extensions && points[i - 1].extensions["gpxtpx:TrackPointExtension"] && points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] && points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface) { - let surface = points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface; - if (statistics.global.surface[surface] === undefined) { - statistics.global.surface[surface] = 0; - } - statistics.global.surface[surface] += dist; + if (i > 0 && points[i - 1].extensions && points[i - 1].extensions["gpxtpx:TrackPointExtension"] && points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]) { + Object.entries(points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]).forEach(([key, value]) => { + if (statistics.global.extensions[key] === undefined) { + statistics.global.extensions[key] = {}; + } + if (statistics.global.extensions[key][value] === undefined) { + statistics.global.extensions[key][value] = 0; + } + statistics.global.extensions[key][value] += dist; + }); } } @@ -1075,11 +1079,10 @@ export class TrackPoint { return this.extensions && this.extensions["gpxpx:PowerExtension"] && this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] ? this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] : undefined; } - getSurface(): string { - return this.extensions && this.extensions["gpxtpx:TrackPointExtension"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface ? this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface : undefined; - } - - setSurface(surface: string): void { + setExtensions(extensions: Record) { + if (Object.keys(extensions).length === 0) { + return; + } if (!this.extensions) { this.extensions = {}; } @@ -1089,7 +1092,13 @@ export class TrackPoint { if (!this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]) { this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] = {}; } - this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]["surface"] = surface; + Object.entries(extensions).forEach(([key, value]) => { + this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"][key] = value; + }); + } + + getExtensions(): Record { + return this.extensions && this.extensions["gpxtpx:TrackPointExtension"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] ? this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] : {}; } toTrackPointType(exclude: string[] = []): TrackPointType { @@ -1119,8 +1128,11 @@ export class TrackPoint { if (this.extensions["gpxpx:PowerExtension"] && this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] && !exclude.includes('power')) { trkpt.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] = this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"]; } - if (this.extensions["gpxtpx:TrackPointExtension"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface && !exclude.includes('surface')) { - trkpt.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] = { surface: this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface }; + if (this.extensions["gpxtpx:TrackPointExtension"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] && !exclude.includes('extensions')) { + trkpt.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] = {}; + Object.entries(this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]).forEach(([key, value]) => { + trkpt.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"][key] = value; + }); } } return trkpt; @@ -1270,7 +1282,7 @@ export class GPXStatistics { avg: number, count: number, }, - surface: Record, + extensions: Record>, }; local: { points: TrackPoint[], @@ -1341,7 +1353,7 @@ export class GPXStatistics { avg: 0, count: 0, }, - surface: {}, + extensions: {}, }; this.local = { points: [], @@ -1411,11 +1423,16 @@ export class GPXStatistics { this.global.cad.count += other.global.cad.count; this.global.power.avg = (this.global.power.count * this.global.power.avg + other.global.power.count * other.global.power.avg) / Math.max(1, this.global.power.count + other.global.power.count); this.global.power.count += other.global.power.count; - Object.keys(other.global.surface).forEach((surface) => { - if (this.global.surface[surface] === undefined) { - this.global.surface[surface] = 0; + Object.keys(other.global.extensions).forEach((extension) => { + if (this.global.extensions[extension] === undefined) { + this.global.extensions[extension] = {}; } - this.global.surface[surface] += other.global.surface[surface]; + Object.keys(other.global.extensions[extension]).forEach((value) => { + if (this.global.extensions[extension][value] === undefined) { + this.global.extensions[extension][value] = 0; + } + this.global.extensions[extension][value] += other.global.extensions[extension][value]; + }); }); } diff --git a/gpx/src/types.ts b/gpx/src/types.ts index c845a3f8..88f9fab1 100644 --- a/gpx/src/types.ts +++ b/gpx/src/types.ts @@ -92,9 +92,7 @@ export type TrackPointExtension = { 'gpxtpx:atemp'?: number; 'gpxtpx:hr'?: number; 'gpxtpx:cad'?: number; - 'gpxtpx:Extensions'?: { - surface?: string; - }; + 'gpxtpx:Extensions'?: Record; } export type PowerExtension = { diff --git a/website/src/lib/assets/colors.ts b/website/src/lib/assets/colors.ts new file mode 100644 index 00000000..3a4254d3 --- /dev/null +++ b/website/src/lib/assets/colors.ts @@ -0,0 +1,64 @@ +export const surfaceColors: { [key: string]: string } = { + "missing": "#d1d1d1", + "paved": "#8c8c8c", + "unpaved": "#6b443a", + "asphalt": "#8c8c8c", + "concrete": "#8c8c8c", + "cobblestone": "#ffd991", + "paving_stones": "#8c8c8c", + "sett": "#ffd991", + "metal": "#8c8c8c", + "wood": "#6b443a", + "compacted": "#ffffa8", + "fine_gravel": "#ffffa8", + "gravel": "#ffffa8", + "pebblestone": "#ffffa8", + "rock": "#ffd991", + "dirt": "#ffffa8", + "ground": "#6b443a", + "earth": "#6b443a", + "mud": "#6b443a", + "sand": "#ffffc4", + "grass": "#61b55c", + "grass_paver": "#61b55c", + "clay": "#6b443a", + "stone": "#ffd991", +}; + +export const highwayColors: { [key: string]: string } = { + "missing": "#d1d1d1", + "residential": "#73b2ff", + "service": "#b3b3cc", + "track": "#946f43", + "unclassified": "#e0e0e0", + "footway": "#a3c989", + "tertiary": "#ffdd7f", + "path": "#a3c989", + "secondary": "#ffd75f", + "primary": "#ff6e5c", + "cycleway": "#ffbb6e", + "trunk": "#ff5e4d", + "living_street": "#9de2ff", + "motorway": "#ff4d33", + "motorway_link": "#ff947f", + "steps": "#8d91a2", + "road": "#e0e0e0", + "pedestrian": "#c1c8e4", + "trunk_link": "#ff947f", + "primary_link": "#ff8d7b", + "secondary_link": "#ffcb66", + "tertiary_link": "#ffdd8b", + "construction": "#e09a4a", + "bridleway": "#a3c989", + "platform": "#c4c4c4", + "proposed": "#e0e0e0", + "raceway": "#ff0000", + "rest_area": "#73b2ff", + "abandoned": "#e0e0e0", + "services": "#ffe066", + "corridor": "#c4c4c4", + "bus_stop": "#ffe6e6", + "busway": "#ffe6e6", + "elevator": "#c4c4c4", + "via_ferrata": "#8d91a2" +}; diff --git a/website/src/lib/assets/surfaces.ts b/website/src/lib/assets/surfaces.ts deleted file mode 100644 index c823d654..00000000 --- a/website/src/lib/assets/surfaces.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const surfaceColors: { [key: string]: string } = { - 'missing': '#d1d1d1', - 'paved': '#8c8c8c', - 'unpaved': '#6b443a', - 'asphalt': '#8c8c8c', - 'concrete': '#8c8c8c', - 'cobblestone': '#ffd991', - 'paving_stones': '#8c8c8c', - 'sett': '#ffd991', - 'metal': '#8c8c8c', - 'wood': '#6b443a', - 'compacted': '#ffffa8', - 'fine_gravel': '#ffffa8', - 'gravel': '#ffffa8', - 'pebblestone': '#ffffa8', - 'rock': '#ffd991', - 'dirt': '#ffffa8', - 'ground': '#6b443a', - 'earth': '#6b443a', - 'mud': '#6b443a', - 'sand': '#ffffc4', - 'grass': '#61b55c', - 'grass_paver': '#61b55c', - 'clay': '#6b443a', - 'stone': '#ffd991', -} \ No newline at end of file diff --git a/website/src/lib/components/ElevationProfile.svelte b/website/src/lib/components/ElevationProfile.svelte index 8c5efa05..d0b04966 100644 --- a/website/src/lib/components/ElevationProfile.svelte +++ b/website/src/lib/components/ElevationProfile.svelte @@ -16,9 +16,10 @@ Zap, Circle, Check, - ChartNoAxesColumn + ChartNoAxesColumn, + Construction } from 'lucide-svelte'; - import { surfaceColors } from '$lib/assets/surfaces'; + import { surfaceColors, highwayColors } from '$lib/assets/colors'; import { _, locale } from 'svelte-i18n'; import { getCadenceWithUnits, @@ -43,7 +44,7 @@ export let gpxStatistics: Writable; export let slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>; export let additionalDatasets: string[]; - export let elevationFill: 'slope' | 'surface' | undefined; + export let elevationFill: 'slope' | 'surface' | 'highway' | undefined; export let showControls: boolean = true; const { distanceUnits, velocityUnits, temperatureUnits } = settings; @@ -151,7 +152,10 @@ segment: point.slope.segment.toFixed(1), length: getDistanceWithUnits(point.slope.length) }; - let surface = point.surface ? point.surface : 'unknown'; + let surface = point.extensions.surface ? point.extensions.surface : 'unknown'; + let highway = point.extensions.highway ? point.extensions.highway : 'unknown'; + let sacScale = point.extensions.sac_scale; + let mtbScale = point.extensions['mtb:scale']; let labels = [ ` ${$_('quantities.distance')}: ${getDistanceWithUnits(point.x, false)}`, @@ -164,6 +168,17 @@ ); } + if (elevationFill === 'highway') { + labels.push( + ` ${$_('quantities.highway')}: ${$_(`toolbar.routing.highway.${highway}`)}${ + sacScale ? ` (${$_(`toolbar.routing.sac_scale.${sacScale}`)})` : '' + }` + ); + if (mtbScale) { + labels.push(` ${$_('toolbar.routing.mtb_scale')}: ${mtbScale}`); + } + } + if (point.time) { labels.push(` ${$_('quantities.time')}: ${df.format(point.time)}`); } @@ -353,7 +368,7 @@ segment: data.local.slope.segment[index], length: data.local.slope.length[index] }, - surface: point.getSurface(), + extensions: point.getExtensions(), coordinates: point.getCoordinates(), index: index }; @@ -448,10 +463,15 @@ } function surfaceFillCallback(context) { - let surface = context.p0.raw.surface; + let surface = context.p0.raw.extensions.surface; return surfaceColors[surface] ? surfaceColors[surface] : surfaceColors.missing; } + function highwayFillCallback(context) { + let highway = context.p0.raw.extensions.highway; + return highwayColors[highway] ? highwayColors[highway] : highwayColors.missing; + } + $: if (chart) { if (elevationFill === 'slope') { chart.data.datasets[0]['segment'] = { @@ -461,6 +481,10 @@ chart.data.datasets[0]['segment'] = { backgroundColor: surfaceFillCallback }; + } else if (elevationFill === 'highway') { + chart.data.datasets[0]['segment'] = { + backgroundColor: highwayFillCallback + }; } else { chart.data.datasets[0]['segment'] = {}; } @@ -551,7 +575,7 @@ - + {$_('quantities.surface')} + +
+ {#if elevationFill === 'highway'} + + {/if} +
+ + {$_('quantities.highway')} +
= { time: true, - surface: true, hr: true, cad: true, atemp: true, - power: true + power: true, + extensions: true }; let hide: Record = { time: false, - surface: false, hr: false, cad: false, atemp: false, - power: false + power: false, + extensions: false }; $: if ($exportState !== ExportState.NONE) { @@ -63,11 +63,11 @@ } hide.time = statistics.global.time.total === 0; - hide.surface = !Object.keys(statistics.global.surface).some((key) => key !== 'unknown'); hide.hr = statistics.global.hr.count === 0; hide.cad = statistics.global.cad.count === 0; hide.atemp = statistics.global.atemp.count === 0; hide.power = statistics.global.power.count === 0; + hide.extensions = Object.keys(statistics.global.extensions).length === 0; } $: exclude = Object.keys(exportOptions).filter((key) => !exportOptions[key]); @@ -144,11 +144,11 @@ {$_('quantities.time')} -
- -