mirror of
				https://github.com/gpxstudio/gpx.studio.git
				synced 2025-11-04 05:21:09 +00:00 
			
		
		
		
	add highway info to elevation profile, closes #65
This commit is contained in:
		@@ -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<string, string>) {
 | 
			
		||||
        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<string, string> {
 | 
			
		||||
        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<string, number>,
 | 
			
		||||
        extensions: Record<string, Record<string, number>>,
 | 
			
		||||
    };
 | 
			
		||||
    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];
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -92,9 +92,7 @@ export type TrackPointExtension = {
 | 
			
		||||
    'gpxtpx:atemp'?: number;
 | 
			
		||||
    'gpxtpx:hr'?: number;
 | 
			
		||||
    'gpxtpx:cad'?: number;
 | 
			
		||||
    'gpxtpx:Extensions'?: {
 | 
			
		||||
        surface?: string;
 | 
			
		||||
    };
 | 
			
		||||
    'gpxtpx:Extensions'?: Record<string, string>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type PowerExtension = {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										64
									
								
								website/src/lib/assets/colors.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								website/src/lib/assets/colors.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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"
 | 
			
		||||
};  
 | 
			
		||||
@@ -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',
 | 
			
		||||
}
 | 
			
		||||
@@ -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<GPXStatistics>;
 | 
			
		||||
	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 @@
 | 
			
		||||
						<ChartNoAxesColumn size="18" />
 | 
			
		||||
					</ButtonWithTooltip>
 | 
			
		||||
				</Popover.Trigger>
 | 
			
		||||
				<Popover.Content class="w-fit p-0 flex flex-col divide-y" side="left" sideOffset={-32}>
 | 
			
		||||
				<Popover.Content class="w-fit p-0 flex flex-col divide-y" side="top" sideOffset={-32}>
 | 
			
		||||
					<ToggleGroup.Root
 | 
			
		||||
						class="flex flex-col items-start gap-0 p-1"
 | 
			
		||||
						type="single"
 | 
			
		||||
@@ -582,6 +606,19 @@
 | 
			
		||||
							<BrickWall size="15" class="mr-1" />
 | 
			
		||||
							{$_('quantities.surface')}
 | 
			
		||||
						</ToggleGroup.Item>
 | 
			
		||||
						<ToggleGroup.Item
 | 
			
		||||
							class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
 | 
			
		||||
							value="highway"
 | 
			
		||||
							variant="outline"
 | 
			
		||||
						>
 | 
			
		||||
							<div class="w-6 flex justify-center items-center">
 | 
			
		||||
								{#if elevationFill === 'highway'}
 | 
			
		||||
									<Circle class="h-1.5 w-1.5 fill-current text-current" />
 | 
			
		||||
								{/if}
 | 
			
		||||
							</div>
 | 
			
		||||
							<Construction size="15" class="mr-1" />
 | 
			
		||||
							{$_('quantities.highway')}
 | 
			
		||||
						</ToggleGroup.Item>
 | 
			
		||||
					</ToggleGroup.Root>
 | 
			
		||||
					<ToggleGroup.Root
 | 
			
		||||
						class="flex flex-col items-start gap-0 p-1"
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
	import {
 | 
			
		||||
		Download,
 | 
			
		||||
		Zap,
 | 
			
		||||
		BrickWall,
 | 
			
		||||
		Earth,
 | 
			
		||||
		HeartPulse,
 | 
			
		||||
		Orbit,
 | 
			
		||||
		Thermometer,
 | 
			
		||||
@@ -31,19 +31,19 @@
 | 
			
		||||
	let open = false;
 | 
			
		||||
	let exportOptions: Record<string, boolean> = {
 | 
			
		||||
		time: true,
 | 
			
		||||
		surface: true,
 | 
			
		||||
		hr: true,
 | 
			
		||||
		cad: true,
 | 
			
		||||
		atemp: true,
 | 
			
		||||
		power: true
 | 
			
		||||
		power: true,
 | 
			
		||||
		extensions: true
 | 
			
		||||
	};
 | 
			
		||||
	let hide: Record<string, boolean> = {
 | 
			
		||||
		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')}
 | 
			
		||||
						</Label>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="flex flex-row items-center gap-1.5 {hide.surface ? 'hidden' : ''}">
 | 
			
		||||
						<Checkbox id="export-surface" bind:checked={exportOptions.surface} />
 | 
			
		||||
						<Label for="export-surface" class="flex flex-row items-center gap-1">
 | 
			
		||||
							<BrickWall size="16" />
 | 
			
		||||
							{$_('quantities.surface')}
 | 
			
		||||
					<div class="flex flex-row items-center gap-1.5 {hide.extensions ? 'hidden' : ''}">
 | 
			
		||||
						<Checkbox id="export-extensions" bind:checked={exportOptions.extensions} />
 | 
			
		||||
						<Label for="export-extensions" class="flex flex-row items-center gap-1">
 | 
			
		||||
							<Earth size="16" />
 | 
			
		||||
							{$_('quantities.osm_extensions')}
 | 
			
		||||
						</Label>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="flex flex-row items-center gap-1.5 {hide.hr ? 'hidden' : ''}">
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ export type EmbeddingOptions = {
 | 
			
		||||
        show: boolean;
 | 
			
		||||
        height: number;
 | 
			
		||||
        controls: boolean;
 | 
			
		||||
        fill: 'slope' | 'surface' | undefined;
 | 
			
		||||
        fill: 'slope' | 'surface' | 'highway' | undefined;
 | 
			
		||||
        speed: boolean;
 | 
			
		||||
        hr: boolean;
 | 
			
		||||
        cad: boolean;
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,7 @@
 | 
			
		||||
								let value = selected?.value;
 | 
			
		||||
								if (value === 'none') {
 | 
			
		||||
									options.elevation.fill = undefined;
 | 
			
		||||
								} else if (value === 'slope' || value === 'surface') {
 | 
			
		||||
								} else if (value === 'slope' || value === 'surface' || value === 'highway') {
 | 
			
		||||
									options.elevation.fill = value;
 | 
			
		||||
								}
 | 
			
		||||
							}}
 | 
			
		||||
@@ -153,6 +153,7 @@
 | 
			
		||||
							<Select.Content>
 | 
			
		||||
								<Select.Item value="slope">{$_('quantities.slope')}</Select.Item>
 | 
			
		||||
								<Select.Item value="surface">{$_('quantities.surface')}</Select.Item>
 | 
			
		||||
								<Select.Item value="highway">{$_('quantities.highway')}</Select.Item>
 | 
			
		||||
								<Select.Item value="none">{$_('embedding.none')}</Select.Item>
 | 
			
		||||
							</Select.Content>
 | 
			
		||||
						</Select.Root>
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ async function getRoute(points: Coordinates[], brouterProfile: string, privateRo
 | 
			
		||||
    const latIdx = messages[0].indexOf("Latitude");
 | 
			
		||||
    const tagIdx = messages[0].indexOf("WayTags");
 | 
			
		||||
    let messageIdx = 1;
 | 
			
		||||
    let surface = messageIdx < messages.length ? getSurface(messages[messageIdx][tagIdx]) : undefined;
 | 
			
		||||
    let tags = messageIdx < messages.length ? getTags(messages[messageIdx][tagIdx]) : {};
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < coordinates.length; i++) {
 | 
			
		||||
        let coord = coordinates[i];
 | 
			
		||||
@@ -82,25 +82,25 @@ async function getRoute(points: Coordinates[], brouterProfile: string, privateRo
 | 
			
		||||
            coordinates[i][1] == Number(messages[messageIdx][latIdx]) / 1000000) {
 | 
			
		||||
            messageIdx++;
 | 
			
		||||
 | 
			
		||||
            if (messageIdx == messages.length) surface = undefined;
 | 
			
		||||
            else surface = getSurface(messages[messageIdx][tagIdx]);
 | 
			
		||||
            if (messageIdx == messages.length) tags = {};
 | 
			
		||||
            else tags = getTags(messages[messageIdx][tagIdx]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (surface) {
 | 
			
		||||
            route[route.length - 1].setSurface(surface);
 | 
			
		||||
        }
 | 
			
		||||
        route[route.length - 1].setExtensions(tags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return route;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getSurface(message: string): string | undefined {
 | 
			
		||||
function getTags(message: string): { [key: string]: string } {
 | 
			
		||||
    const fields = message.split(" ");
 | 
			
		||||
    for (let i = 0; i < fields.length; i++) if (fields[i].startsWith("surface=")) {
 | 
			
		||||
        return fields[i].substring(8);
 | 
			
		||||
    let tags: { [key: string]: string } = {};
 | 
			
		||||
    for (let i = 0; i < fields.length; i++) {
 | 
			
		||||
        let tag = fields[i].split("=");
 | 
			
		||||
        tags[tag[0]] = tag[1];
 | 
			
		||||
    }
 | 
			
		||||
    return undefined;
 | 
			
		||||
};
 | 
			
		||||
    return tags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getIntermediatePoints(points: Coordinates[]): Promise<TrackPoint[]> {
 | 
			
		||||
    let route: TrackPoint[] = [];
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ title: Files and statistics
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
    import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
 | 
			
		||||
    import { ChartNoAxesColumn } from 'lucide-svelte';
 | 
			
		||||
    import DocsNote from '$lib/components/docs/DocsNote.svelte';
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
@@ -73,9 +73,9 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
 | 
			
		||||
 | 
			
		||||
### Additional data
 | 
			
		||||
 | 
			
		||||
Using the button at the bottom-right of the elevation profile, you can optionally color the elevation profile by:
 | 
			
		||||
- **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
 | 
			
		||||
- **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
 | 
			
		||||
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by:
 | 
			
		||||
- **slope** information computed from the elevation data, or
 | 
			
		||||
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags.
 | 
			
		||||
This is only available for files created with **gpx.studio**.
 | 
			
		||||
 | 
			
		||||
If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.
 | 
			
		||||
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile.
 | 
			
		||||
 
 | 
			
		||||
@@ -139,6 +139,52 @@
 | 
			
		||||
                "clay": "Clay",
 | 
			
		||||
                "stone": "Stone"
 | 
			
		||||
            },
 | 
			
		||||
            "highway": {
 | 
			
		||||
                "unknown": "Unknown",
 | 
			
		||||
                "residential": "Residential",
 | 
			
		||||
                "service": "Service",
 | 
			
		||||
                "track": "Track",
 | 
			
		||||
                "unclassified": "Unclassified",
 | 
			
		||||
                "footway": "Footway",
 | 
			
		||||
                "tertiary": "Tertiary",
 | 
			
		||||
                "path": "Path",
 | 
			
		||||
                "secondary": "Secondary",
 | 
			
		||||
                "primary": "Primary",
 | 
			
		||||
                "cycleway": "Cycleway",
 | 
			
		||||
                "trunk": "Trunk",
 | 
			
		||||
                "living_street": "Living Street",
 | 
			
		||||
                "motorway": "Motorway",
 | 
			
		||||
                "motorway_link": "Motorway Link",
 | 
			
		||||
                "steps": "Steps",
 | 
			
		||||
                "road": "Road",
 | 
			
		||||
                "pedestrian": "Pedestrian",
 | 
			
		||||
                "trunk_link": "Trunk Link",
 | 
			
		||||
                "primary_link": "Primary Link",
 | 
			
		||||
                "secondary_link": "Secondary Link",
 | 
			
		||||
                "tertiary_link": "Tertiary Link",
 | 
			
		||||
                "construction": "Construction",
 | 
			
		||||
                "bridleway": "Bridleway",
 | 
			
		||||
                "platform": "Platform",
 | 
			
		||||
                "proposed": "Proposed",
 | 
			
		||||
                "raceway": "Raceway",
 | 
			
		||||
                "rest_area": "Rest Area",
 | 
			
		||||
                "abandoned": "Abandoned",
 | 
			
		||||
                "services": "Services",
 | 
			
		||||
                "corridor": "Corridor",
 | 
			
		||||
                "bus_stop": "Bus Stop",
 | 
			
		||||
                "busway": "Busway",
 | 
			
		||||
                "elevator": "Elevator",
 | 
			
		||||
                "via_ferrata": "Via Ferrata"
 | 
			
		||||
            },
 | 
			
		||||
            "sac_scale": {
 | 
			
		||||
                "hiking": "Hiking",
 | 
			
		||||
                "mountain_hiking": "Mountain hiking",
 | 
			
		||||
                "demanding_mountain_hiking": "Demanding mountain hiking",
 | 
			
		||||
                "alpine_hiking": "Alpine hiking",
 | 
			
		||||
                "demanding_alpine_hiking": "Demanding alpine hiking",
 | 
			
		||||
                "difficult_alpine_hiking": "Difficult alpine hiking"
 | 
			
		||||
            },
 | 
			
		||||
            "mtb_scale": "MTB scale",
 | 
			
		||||
            "error": {
 | 
			
		||||
                "from": "The start point is too far from the nearest road",
 | 
			
		||||
                "via": "The via point is too far from the nearest road",
 | 
			
		||||
@@ -355,9 +401,11 @@
 | 
			
		||||
        "power": "Power",
 | 
			
		||||
        "slope": "Slope",
 | 
			
		||||
        "surface": "Surface",
 | 
			
		||||
        "highway": "Category",
 | 
			
		||||
        "time": "Time",
 | 
			
		||||
        "moving": "Moving",
 | 
			
		||||
        "total": "Total"
 | 
			
		||||
        "total": "Total",
 | 
			
		||||
        "osm_extensions": "OpenStreetMap data"
 | 
			
		||||
    },
 | 
			
		||||
    "units": {
 | 
			
		||||
        "meters": "m",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user