mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-11-04 13:31:13 +00:00
fix elevation profile
This commit is contained in:
@@ -3,8 +3,6 @@
|
|||||||
import * as Popover from '$lib/components/ui/popover/index.js';
|
import * as Popover from '$lib/components/ui/popover/index.js';
|
||||||
import * as ToggleGroup from '$lib/components/ui/toggle-group/index.js';
|
import * as ToggleGroup from '$lib/components/ui/toggle-group/index.js';
|
||||||
import Separator from '$lib/components/ui/separator/separator.svelte';
|
import Separator from '$lib/components/ui/separator/separator.svelte';
|
||||||
import Chart from 'chart.js/auto';
|
|
||||||
import mapboxgl from 'mapbox-gl';
|
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import {
|
import {
|
||||||
BrickWall,
|
BrickWall,
|
||||||
@@ -19,552 +17,45 @@
|
|||||||
ChartNoAxesColumn,
|
ChartNoAxesColumn,
|
||||||
Construction,
|
Construction,
|
||||||
} from '@lucide/svelte';
|
} from '@lucide/svelte';
|
||||||
import { getSlopeColor, getSurfaceColor, getHighwayColor } from '$lib/assets/colors';
|
|
||||||
import {
|
|
||||||
getCadenceWithUnits,
|
|
||||||
getConvertedDistance,
|
|
||||||
getConvertedElevation,
|
|
||||||
getConvertedTemperature,
|
|
||||||
getConvertedVelocity,
|
|
||||||
getDistanceUnits,
|
|
||||||
getDistanceWithUnits,
|
|
||||||
getElevationWithUnits,
|
|
||||||
getHeartRateWithUnits,
|
|
||||||
getPowerWithUnits,
|
|
||||||
getTemperatureWithUnits,
|
|
||||||
getVelocityWithUnits,
|
|
||||||
} from '$lib/units';
|
|
||||||
import type { Readable, Writable } from 'svelte/store';
|
import type { Readable, Writable } from 'svelte/store';
|
||||||
import type { GPXStatistics } from 'gpx';
|
import type { GPXStatistics } from 'gpx';
|
||||||
import { mode } from 'mode-watcher';
|
|
||||||
import { settings } from '$lib/logic/settings';
|
import { settings } from '$lib/logic/settings';
|
||||||
import { map } from '$lib/components/map/map';
|
|
||||||
import { i18n } from '$lib/i18n.svelte';
|
import { i18n } from '$lib/i18n.svelte';
|
||||||
|
import { ElevationProfile } from '$lib/components/elevation-profile/elevation-profile';
|
||||||
|
|
||||||
const { distanceUnits, velocityUnits, temperatureUnits } = settings;
|
const { velocityUnits } = settings;
|
||||||
|
|
||||||
let {
|
let {
|
||||||
gpxStatistics,
|
gpxStatistics,
|
||||||
slicedGPXStatistics,
|
slicedGPXStatistics,
|
||||||
additionalDatasets = $bindable(),
|
additionalDatasets,
|
||||||
elevationFill = $bindable(),
|
elevationFill,
|
||||||
showControls = true,
|
showControls = true,
|
||||||
}: {
|
}: {
|
||||||
gpxStatistics: Readable<GPXStatistics>;
|
gpxStatistics: Readable<GPXStatistics>;
|
||||||
slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>;
|
slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>;
|
||||||
additionalDatasets: string[];
|
additionalDatasets: Writable<string[]>;
|
||||||
elevationFill: 'slope' | 'surface' | 'highway' | undefined;
|
elevationFill: Writable<'slope' | 'surface' | 'highway' | undefined>;
|
||||||
showControls?: boolean;
|
showControls?: boolean;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
let canvas: HTMLCanvasElement;
|
let canvas: HTMLCanvasElement;
|
||||||
let overlay: HTMLCanvasElement;
|
let overlay: HTMLCanvasElement;
|
||||||
let chart: Chart;
|
let elevationProfile: ElevationProfile;
|
||||||
|
|
||||||
Chart.defaults.font.family =
|
onMount(() => {
|
||||||
'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'; // Tailwind CSS font
|
elevationProfile = new ElevationProfile(
|
||||||
|
gpxStatistics,
|
||||||
let marker: mapboxgl.Marker | null = null;
|
slicedGPXStatistics,
|
||||||
let dragging = false;
|
additionalDatasets,
|
||||||
let panning = false;
|
elevationFill,
|
||||||
|
canvas,
|
||||||
let options = {
|
overlay
|
||||||
animation: false,
|
|
||||||
parsing: false,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
type: 'linear',
|
|
||||||
ticks: {
|
|
||||||
callback: function (value: number) {
|
|
||||||
return `${value.toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`;
|
|
||||||
},
|
|
||||||
align: 'inner',
|
|
||||||
maxRotation: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
type: 'linear',
|
|
||||||
ticks: {
|
|
||||||
callback: function (value: number) {
|
|
||||||
return getElevationWithUnits(value, false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
datasets: {
|
|
||||||
line: {
|
|
||||||
pointRadius: 0,
|
|
||||||
tension: 0.4,
|
|
||||||
borderWidth: 2,
|
|
||||||
cubicInterpolationMode: 'monotone',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
mode: 'nearest',
|
|
||||||
axis: 'x',
|
|
||||||
intersect: false,
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
decimation: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
enabled: () => !dragging && !panning,
|
|
||||||
callbacks: {
|
|
||||||
title: function () {
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
label: function (context: Chart.TooltipContext) {
|
|
||||||
let point = context.raw;
|
|
||||||
if (context.datasetIndex === 0) {
|
|
||||||
if ($map && marker) {
|
|
||||||
if (dragging) {
|
|
||||||
marker.remove();
|
|
||||||
} else {
|
|
||||||
marker.setLngLat(point.coordinates);
|
|
||||||
marker.addTo($map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return `${i18n._('quantities.elevation')}: ${getElevationWithUnits(point.y, false)}`;
|
|
||||||
} else if (context.datasetIndex === 1) {
|
|
||||||
return `${$velocityUnits === 'speed' ? i18n._('quantities.speed') : i18n._('quantities.pace')}: ${getVelocityWithUnits(point.y, false)}`;
|
|
||||||
} else if (context.datasetIndex === 2) {
|
|
||||||
return `${i18n._('quantities.heartrate')}: ${getHeartRateWithUnits(point.y)}`;
|
|
||||||
} else if (context.datasetIndex === 3) {
|
|
||||||
return `${i18n._('quantities.cadence')}: ${getCadenceWithUnits(point.y)}`;
|
|
||||||
} else if (context.datasetIndex === 4) {
|
|
||||||
return `${i18n._('quantities.temperature')}: ${getTemperatureWithUnits(point.y, false)}`;
|
|
||||||
} else if (context.datasetIndex === 5) {
|
|
||||||
return `${i18n._('quantities.power')}: ${getPowerWithUnits(point.y)}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
afterBody: function (contexts: Chart.TooltipContext[]) {
|
|
||||||
let context = contexts.filter((context) => context.datasetIndex === 0);
|
|
||||||
if (context.length === 0) return;
|
|
||||||
let point = context[0].raw;
|
|
||||||
let slope = {
|
|
||||||
at: point.slope.at.toFixed(1),
|
|
||||||
segment: point.slope.segment.toFixed(1),
|
|
||||||
length: getDistanceWithUnits(point.slope.length),
|
|
||||||
};
|
|
||||||
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 = [
|
|
||||||
` ${i18n._('quantities.distance')}: ${getDistanceWithUnits(point.x, false)}`,
|
|
||||||
` ${i18n._('quantities.slope')}: ${slope.at} %${elevationFill === 'slope' ? ` (${slope.length} @${slope.segment} %)` : ''}`,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (elevationFill === 'surface') {
|
|
||||||
labels.push(
|
|
||||||
` ${i18n._('quantities.surface')}: ${i18n._(`toolbar.routing.surface.${surface}`)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elevationFill === 'highway') {
|
|
||||||
labels.push(
|
|
||||||
` ${i18n._('quantities.highway')}: ${i18n._(`toolbar.routing.highway.${highway}`)}${
|
|
||||||
sacScale
|
|
||||||
? ` (${i18n._(`toolbar.routing.sac_scale.${sacScale}`)})`
|
|
||||||
: ''
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
if (mtbScale) {
|
|
||||||
labels.push(
|
|
||||||
` ${i18n._('toolbar.routing.mtb_scale')}: ${mtbScale}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (point.time) {
|
|
||||||
labels.push(
|
|
||||||
` ${i18n._('quantities.time')}: ${i18n.df.format(point.time)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return labels;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
zoom: {
|
|
||||||
pan: {
|
|
||||||
enabled: true,
|
|
||||||
mode: 'x',
|
|
||||||
modifierKey: 'shift',
|
|
||||||
onPanStart: function () {
|
|
||||||
// hide tooltip
|
|
||||||
panning = true;
|
|
||||||
$slicedGPXStatistics = undefined;
|
|
||||||
},
|
|
||||||
onPanComplete: function () {
|
|
||||||
panning = false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
zoom: {
|
|
||||||
wheel: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
mode: 'x',
|
|
||||||
onZoomStart: function ({ chart, event }: { chart: Chart; event: any }) {
|
|
||||||
if (
|
|
||||||
event.deltaY < 0 &&
|
|
||||||
Math.abs(
|
|
||||||
chart.getInitialScaleBounds().x.max /
|
|
||||||
chart.options.plugins.zoom.limits.x.minRange -
|
|
||||||
chart.getZoomLevel()
|
|
||||||
) < 0.01
|
|
||||||
) {
|
|
||||||
// Disable wheel pan if zoomed in to the max, and zooming in
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$slicedGPXStatistics = undefined;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
limits: {
|
|
||||||
x: {
|
|
||||||
min: 'original',
|
|
||||||
max: 'original',
|
|
||||||
minRange: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
stacked: false,
|
|
||||||
onResize: function () {
|
|
||||||
updateOverlay();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let datasets: string[] = ['speed', 'hr', 'cad', 'atemp', 'power'];
|
|
||||||
datasets.forEach((id) => {
|
|
||||||
options.scales[`y${id}`] = {
|
|
||||||
type: 'linear',
|
|
||||||
position: 'right',
|
|
||||||
grid: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
reverse: () => id === 'speed' && $velocityUnits === 'pace',
|
|
||||||
display: false,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
Chart.register((await import('chartjs-plugin-zoom')).default); // dynamic import to avoid SSR and 'window is not defined' error
|
|
||||||
|
|
||||||
chart = new Chart(canvas, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
datasets: [],
|
|
||||||
},
|
|
||||||
options,
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
id: 'toggleMarker',
|
|
||||||
events: ['mouseout'],
|
|
||||||
afterEvent: function (chart: Chart, args: { event: Chart.ChartEvent }) {
|
|
||||||
if (args.event.type === 'mouseout') {
|
|
||||||
if ($map && marker) {
|
|
||||||
marker.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Map marker to show on hover
|
|
||||||
let element = document.createElement('div');
|
|
||||||
element.className = 'h-4 w-4 rounded-full bg-cyan-500 border-2 border-white';
|
|
||||||
marker = new mapboxgl.Marker({
|
|
||||||
element,
|
|
||||||
});
|
|
||||||
|
|
||||||
let startIndex = 0;
|
|
||||||
let endIndex = 0;
|
|
||||||
function getIndex(evt) {
|
|
||||||
const points = chart.getElementsAtEventForMode(
|
|
||||||
evt,
|
|
||||||
'x',
|
|
||||||
{
|
|
||||||
intersect: false,
|
|
||||||
},
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
if (points.length === 0) {
|
|
||||||
const rect = canvas.getBoundingClientRect();
|
|
||||||
if (evt.x - rect.left <= chart.chartArea.left) {
|
|
||||||
return 0;
|
|
||||||
} else if (evt.x - rect.left >= chart.chartArea.right) {
|
|
||||||
return $gpxStatistics.local.points.length - 1;
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let point = points.find((point) => point.element.raw);
|
|
||||||
if (point) {
|
|
||||||
return point.element.raw.index;
|
|
||||||
} else {
|
|
||||||
return points[0].index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let dragStarted = false;
|
|
||||||
function onMouseDown(evt) {
|
|
||||||
if (evt.shiftKey) {
|
|
||||||
// Panning interaction
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dragStarted = true;
|
|
||||||
canvas.style.cursor = 'col-resize';
|
|
||||||
startIndex = getIndex(evt);
|
|
||||||
}
|
|
||||||
function onMouseMove(evt) {
|
|
||||||
if (dragStarted) {
|
|
||||||
dragging = true;
|
|
||||||
endIndex = getIndex(evt);
|
|
||||||
if (endIndex !== undefined) {
|
|
||||||
if (startIndex === undefined) {
|
|
||||||
startIndex = endIndex;
|
|
||||||
} else if (startIndex !== endIndex) {
|
|
||||||
$slicedGPXStatistics = [
|
|
||||||
$gpxStatistics.slice(
|
|
||||||
Math.min(startIndex, endIndex),
|
|
||||||
Math.max(startIndex, endIndex)
|
|
||||||
),
|
|
||||||
Math.min(startIndex, endIndex),
|
|
||||||
Math.max(startIndex, endIndex),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onMouseUp(evt) {
|
|
||||||
dragStarted = false;
|
|
||||||
dragging = false;
|
|
||||||
canvas.style.cursor = '';
|
|
||||||
endIndex = getIndex(evt);
|
|
||||||
if (startIndex === endIndex) {
|
|
||||||
$slicedGPXStatistics = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
canvas.addEventListener('pointerdown', onMouseDown);
|
|
||||||
canvas.addEventListener('pointermove', onMouseMove);
|
|
||||||
canvas.addEventListener('pointerup', onMouseUp);
|
|
||||||
});
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
let data = $gpxStatistics;
|
|
||||||
if (chart && $distanceUnits && $velocityUnits && $temperatureUnits) {
|
|
||||||
// update data
|
|
||||||
chart.data.datasets[0] = {
|
|
||||||
label: i18n._('quantities.elevation'),
|
|
||||||
data: data.local.points.map((point, index) => {
|
|
||||||
return {
|
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
|
||||||
y: point.ele ? getConvertedElevation(point.ele) : 0,
|
|
||||||
time: point.time,
|
|
||||||
slope: {
|
|
||||||
at: data.local.slope.at[index],
|
|
||||||
segment: data.local.slope.segment[index],
|
|
||||||
length: data.local.slope.length[index],
|
|
||||||
},
|
|
||||||
extensions: point.getExtensions(),
|
|
||||||
coordinates: point.getCoordinates(),
|
|
||||||
index: index,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
normalized: true,
|
|
||||||
fill: 'start',
|
|
||||||
order: 1,
|
|
||||||
};
|
|
||||||
chart.data.datasets[1] = {
|
|
||||||
data: data.local.points.map((point, index) => {
|
|
||||||
return {
|
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
|
||||||
y: getConvertedVelocity(data.local.speed[index]),
|
|
||||||
index: index,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
normalized: true,
|
|
||||||
yAxisID: 'yspeed',
|
|
||||||
hidden: true,
|
|
||||||
};
|
|
||||||
chart.data.datasets[2] = {
|
|
||||||
data: data.local.points.map((point, index) => {
|
|
||||||
return {
|
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
|
||||||
y: point.getHeartRate(),
|
|
||||||
index: index,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
normalized: true,
|
|
||||||
yAxisID: 'yhr',
|
|
||||||
hidden: true,
|
|
||||||
};
|
|
||||||
chart.data.datasets[3] = {
|
|
||||||
data: data.local.points.map((point, index) => {
|
|
||||||
return {
|
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
|
||||||
y: point.getCadence(),
|
|
||||||
index: index,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
normalized: true,
|
|
||||||
yAxisID: 'ycad',
|
|
||||||
hidden: true,
|
|
||||||
};
|
|
||||||
chart.data.datasets[4] = {
|
|
||||||
data: data.local.points.map((point, index) => {
|
|
||||||
return {
|
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
|
||||||
y: getConvertedTemperature(point.getTemperature()),
|
|
||||||
index: index,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
normalized: true,
|
|
||||||
yAxisID: 'yatemp',
|
|
||||||
hidden: true,
|
|
||||||
};
|
|
||||||
chart.data.datasets[5] = {
|
|
||||||
data: data.local.points.map((point, index) => {
|
|
||||||
return {
|
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
|
||||||
y: point.getPower(),
|
|
||||||
index: index,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
normalized: true,
|
|
||||||
yAxisID: 'ypower',
|
|
||||||
hidden: true,
|
|
||||||
};
|
|
||||||
chart.options.scales.x['min'] = 0;
|
|
||||||
chart.options.scales.x['max'] = getConvertedDistance(data.global.distance.total);
|
|
||||||
|
|
||||||
chart.update();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function slopeFillCallback(context) {
|
|
||||||
return getSlopeColor(context.p0.raw.slope.segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
function surfaceFillCallback(context) {
|
|
||||||
return getSurfaceColor(context.p0.raw.extensions.surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
function highwayFillCallback(context) {
|
|
||||||
return getHighwayColor(
|
|
||||||
context.p0.raw.extensions.highway,
|
|
||||||
context.p0.raw.extensions.sac_scale,
|
|
||||||
context.p0.raw.extensions.mtb_scale
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (elevationFill && chart) {
|
|
||||||
if (elevationFill === 'slope') {
|
|
||||||
chart.data.datasets[0]['segment'] = {
|
|
||||||
backgroundColor: slopeFillCallback,
|
|
||||||
};
|
|
||||||
} else if (elevationFill === 'surface') {
|
|
||||||
chart.data.datasets[0]['segment'] = {
|
|
||||||
backgroundColor: surfaceFillCallback,
|
|
||||||
};
|
|
||||||
} else if (elevationFill === 'highway') {
|
|
||||||
chart.data.datasets[0]['segment'] = {
|
|
||||||
backgroundColor: highwayFillCallback,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
chart.data.datasets[0]['segment'] = {};
|
|
||||||
}
|
|
||||||
chart.update();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (additionalDatasets && chart) {
|
|
||||||
let includeSpeed = additionalDatasets.includes('speed');
|
|
||||||
let includeHeartRate = additionalDatasets.includes('hr');
|
|
||||||
let includeCadence = additionalDatasets.includes('cad');
|
|
||||||
let includeTemperature = additionalDatasets.includes('atemp');
|
|
||||||
let includePower = additionalDatasets.includes('power');
|
|
||||||
if (chart.data.datasets.length > 0) {
|
|
||||||
chart.data.datasets[1].hidden = !includeSpeed;
|
|
||||||
chart.data.datasets[2].hidden = !includeHeartRate;
|
|
||||||
chart.data.datasets[3].hidden = !includeCadence;
|
|
||||||
chart.data.datasets[4].hidden = !includeTemperature;
|
|
||||||
chart.data.datasets[5].hidden = !includePower;
|
|
||||||
}
|
|
||||||
chart.update();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateOverlay() {
|
|
||||||
if (!canvas) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
overlay.width = canvas.width / window.devicePixelRatio;
|
|
||||||
overlay.height = canvas.height / window.devicePixelRatio;
|
|
||||||
overlay.style.width = `${overlay.width}px`;
|
|
||||||
overlay.style.height = `${overlay.height}px`;
|
|
||||||
|
|
||||||
if ($slicedGPXStatistics) {
|
|
||||||
let startIndex = $slicedGPXStatistics[1];
|
|
||||||
let endIndex = $slicedGPXStatistics[2];
|
|
||||||
|
|
||||||
// Draw selection rectangle
|
|
||||||
let selectionContext = overlay.getContext('2d');
|
|
||||||
if (selectionContext) {
|
|
||||||
selectionContext.fillStyle = mode.current === 'dark' ? 'white' : 'black';
|
|
||||||
selectionContext.globalAlpha = mode.current === 'dark' ? 0.2 : 0.1;
|
|
||||||
selectionContext.clearRect(0, 0, overlay.width, overlay.height);
|
|
||||||
|
|
||||||
let startPixel = chart.scales.x.getPixelForValue(
|
|
||||||
getConvertedDistance($gpxStatistics.local.distance.total[startIndex])
|
|
||||||
);
|
|
||||||
let endPixel = chart.scales.x.getPixelForValue(
|
|
||||||
getConvertedDistance($gpxStatistics.local.distance.total[endIndex])
|
|
||||||
);
|
|
||||||
|
|
||||||
selectionContext.fillRect(
|
|
||||||
startPixel,
|
|
||||||
chart.chartArea.top,
|
|
||||||
endPixel - startPixel,
|
|
||||||
chart.chartArea.height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (overlay) {
|
|
||||||
let selectionContext = overlay.getContext('2d');
|
|
||||||
if (selectionContext) {
|
|
||||||
selectionContext.clearRect(0, 0, overlay.width, overlay.height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if ($slicedGPXStatistics || mode.current) {
|
|
||||||
updateOverlay();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
if (chart) {
|
elevationProfile.destroy();
|
||||||
chart.destroy();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -593,14 +84,14 @@
|
|||||||
<ToggleGroup.Root
|
<ToggleGroup.Root
|
||||||
class="flex flex-col items-start gap-0 p-1 w-full border-none"
|
class="flex flex-col items-start gap-0 p-1 w-full border-none"
|
||||||
type="single"
|
type="single"
|
||||||
bind:value={elevationFill}
|
bind:value={$elevationFill}
|
||||||
>
|
>
|
||||||
<ToggleGroup.Item
|
<ToggleGroup.Item
|
||||||
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
||||||
value="slope"
|
value="slope"
|
||||||
>
|
>
|
||||||
<div class="w-6 flex justify-center items-center">
|
<div class="w-6 flex justify-center items-center">
|
||||||
{#if elevationFill === 'slope'}
|
{#if $elevationFill === 'slope'}
|
||||||
<Circle class="size-1.5 fill-current text-current" />
|
<Circle class="size-1.5 fill-current text-current" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -613,7 +104,7 @@
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
<div class="w-6 flex justify-center items-center">
|
<div class="w-6 flex justify-center items-center">
|
||||||
{#if elevationFill === 'surface'}
|
{#if $elevationFill === 'surface'}
|
||||||
<Circle class="size-1.5 fill-current text-current" />
|
<Circle class="size-1.5 fill-current text-current" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -626,7 +117,7 @@
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
<div class="w-6 flex justify-center items-center">
|
<div class="w-6 flex justify-center items-center">
|
||||||
{#if elevationFill === 'highway'}
|
{#if $elevationFill === 'highway'}
|
||||||
<Circle class="size-1.5 fill-current text-current" />
|
<Circle class="size-1.5 fill-current text-current" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -638,14 +129,14 @@
|
|||||||
<ToggleGroup.Root
|
<ToggleGroup.Root
|
||||||
class="flex flex-col items-start gap-0 p-1"
|
class="flex flex-col items-start gap-0 p-1"
|
||||||
type="multiple"
|
type="multiple"
|
||||||
bind:value={additionalDatasets}
|
bind:value={$additionalDatasets}
|
||||||
>
|
>
|
||||||
<ToggleGroup.Item
|
<ToggleGroup.Item
|
||||||
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
||||||
value="speed"
|
value="speed"
|
||||||
>
|
>
|
||||||
<div class="w-6 flex justify-center items-center">
|
<div class="w-6 flex justify-center items-center">
|
||||||
{#if additionalDatasets.includes('speed')}
|
{#if $additionalDatasets.includes('speed')}
|
||||||
<Check size="14" />
|
<Check size="14" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -659,7 +150,7 @@
|
|||||||
value="hr"
|
value="hr"
|
||||||
>
|
>
|
||||||
<div class="w-6 flex justify-center items-center">
|
<div class="w-6 flex justify-center items-center">
|
||||||
{#if additionalDatasets.includes('hr')}
|
{#if $additionalDatasets.includes('hr')}
|
||||||
<Check size="14" />
|
<Check size="14" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -671,7 +162,7 @@
|
|||||||
value="cad"
|
value="cad"
|
||||||
>
|
>
|
||||||
<div class="w-6 flex justify-center items-center">
|
<div class="w-6 flex justify-center items-center">
|
||||||
{#if additionalDatasets.includes('cad')}
|
{#if $additionalDatasets.includes('cad')}
|
||||||
<Check size="14" />
|
<Check size="14" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -683,7 +174,7 @@
|
|||||||
value="atemp"
|
value="atemp"
|
||||||
>
|
>
|
||||||
<div class="w-6 flex justify-center items-center">
|
<div class="w-6 flex justify-center items-center">
|
||||||
{#if additionalDatasets.includes('atemp')}
|
{#if $additionalDatasets.includes('atemp')}
|
||||||
<Check size="14" />
|
<Check size="14" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -695,7 +186,7 @@
|
|||||||
value="power"
|
value="power"
|
||||||
>
|
>
|
||||||
<div class="w-6 flex justify-center items-center">
|
<div class="w-6 flex justify-center items-center">
|
||||||
{#if additionalDatasets.includes('power')}
|
{#if $additionalDatasets.includes('power')}
|
||||||
<Check size="14" />
|
<Check size="14" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,602 @@
|
|||||||
|
import { i18n } from '$lib/i18n.svelte';
|
||||||
|
import { settings } from '$lib/logic/settings';
|
||||||
|
import {
|
||||||
|
getCadenceWithUnits,
|
||||||
|
getConvertedDistance,
|
||||||
|
getConvertedElevation,
|
||||||
|
getConvertedTemperature,
|
||||||
|
getConvertedVelocity,
|
||||||
|
getDistanceUnits,
|
||||||
|
getDistanceWithUnits,
|
||||||
|
getElevationWithUnits,
|
||||||
|
getHeartRateWithUnits,
|
||||||
|
getPowerWithUnits,
|
||||||
|
getTemperatureWithUnits,
|
||||||
|
getVelocityWithUnits,
|
||||||
|
} from '$lib/units';
|
||||||
|
import Chart from 'chart.js/auto';
|
||||||
|
import mapboxgl from 'mapbox-gl';
|
||||||
|
import { get, type Readable, type Writable } from 'svelte/store';
|
||||||
|
import { map } from '$lib/components/map/map';
|
||||||
|
import type { GPXStatistics } from 'gpx';
|
||||||
|
import { mode } from 'mode-watcher';
|
||||||
|
import { getHighwayColor, getSlopeColor, getSurfaceColor } from '$lib/assets/colors';
|
||||||
|
|
||||||
|
const { distanceUnits, velocityUnits, temperatureUnits } = settings;
|
||||||
|
|
||||||
|
Chart.defaults.font.family =
|
||||||
|
'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'; // Tailwind CSS font
|
||||||
|
|
||||||
|
export class ElevationProfile {
|
||||||
|
private _chart: Chart | null = null;
|
||||||
|
private _canvas: HTMLCanvasElement;
|
||||||
|
private _overlay: HTMLCanvasElement;
|
||||||
|
private _marker: mapboxgl.Marker | null = null;
|
||||||
|
private _dragging = false;
|
||||||
|
private _panning = false;
|
||||||
|
|
||||||
|
private _gpxStatistics: Readable<GPXStatistics>;
|
||||||
|
private _slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>;
|
||||||
|
private _additionalDatasets: Readable<string[]>;
|
||||||
|
private _elevationFill: Readable<'slope' | 'surface' | 'highway' | undefined>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
gpxStatistics: Readable<GPXStatistics>,
|
||||||
|
slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>,
|
||||||
|
additionalDatasets: Readable<string[]>,
|
||||||
|
elevationFill: Readable<'slope' | 'surface' | 'highway' | undefined>,
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
overlay: HTMLCanvasElement
|
||||||
|
) {
|
||||||
|
this._gpxStatistics = gpxStatistics;
|
||||||
|
this._slicedGPXStatistics = slicedGPXStatistics;
|
||||||
|
this._additionalDatasets = additionalDatasets;
|
||||||
|
this._elevationFill = elevationFill;
|
||||||
|
this._canvas = canvas;
|
||||||
|
this._overlay = overlay;
|
||||||
|
|
||||||
|
let element = document.createElement('div');
|
||||||
|
element.className = 'h-4 w-4 rounded-full bg-cyan-500 border-2 border-white';
|
||||||
|
this._marker = new mapboxgl.Marker({
|
||||||
|
element,
|
||||||
|
});
|
||||||
|
|
||||||
|
import('chartjs-plugin-zoom').then((module) => {
|
||||||
|
Chart.register(module.default);
|
||||||
|
this.initialize();
|
||||||
|
|
||||||
|
this._gpxStatistics.subscribe(() => {
|
||||||
|
this.updateData();
|
||||||
|
});
|
||||||
|
this._slicedGPXStatistics.subscribe(() => {
|
||||||
|
this.updateOverlay();
|
||||||
|
});
|
||||||
|
distanceUnits.subscribe(() => {
|
||||||
|
this.updateData();
|
||||||
|
});
|
||||||
|
velocityUnits.subscribe(() => {
|
||||||
|
this.updateData();
|
||||||
|
});
|
||||||
|
temperatureUnits.subscribe(() => {
|
||||||
|
this.updateData();
|
||||||
|
});
|
||||||
|
this._additionalDatasets.subscribe(() => {
|
||||||
|
this.updateDataVisibility();
|
||||||
|
});
|
||||||
|
this._elevationFill.subscribe(() => {
|
||||||
|
this.updateFill();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
let options = {
|
||||||
|
animation: false,
|
||||||
|
parsing: false,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
type: 'linear',
|
||||||
|
ticks: {
|
||||||
|
callback: function (value: number) {
|
||||||
|
return `${value.toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`;
|
||||||
|
},
|
||||||
|
align: 'inner',
|
||||||
|
maxRotation: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: 'linear',
|
||||||
|
ticks: {
|
||||||
|
callback: function (value: number) {
|
||||||
|
return getElevationWithUnits(value, false);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
datasets: {
|
||||||
|
line: {
|
||||||
|
pointRadius: 0,
|
||||||
|
tension: 0.4,
|
||||||
|
borderWidth: 2,
|
||||||
|
cubicInterpolationMode: 'monotone',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
mode: 'nearest',
|
||||||
|
axis: 'x',
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
decimation: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: () => !this._dragging && !this._panning,
|
||||||
|
callbacks: {
|
||||||
|
title: () => {
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
label: (context: Chart.TooltipContext) => {
|
||||||
|
let point = context.raw;
|
||||||
|
if (context.datasetIndex === 0) {
|
||||||
|
const map_ = get(map);
|
||||||
|
if (map_ && this._marker) {
|
||||||
|
if (this._dragging) {
|
||||||
|
this._marker.remove();
|
||||||
|
} else {
|
||||||
|
this._marker.setLngLat(point.coordinates);
|
||||||
|
this._marker.addTo(map_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `${i18n._('quantities.elevation')}: ${getElevationWithUnits(point.y, false)}`;
|
||||||
|
} else if (context.datasetIndex === 1) {
|
||||||
|
return `${get(velocityUnits) === 'speed' ? i18n._('quantities.speed') : i18n._('quantities.pace')}: ${getVelocityWithUnits(point.y, false)}`;
|
||||||
|
} else if (context.datasetIndex === 2) {
|
||||||
|
return `${i18n._('quantities.heartrate')}: ${getHeartRateWithUnits(point.y)}`;
|
||||||
|
} else if (context.datasetIndex === 3) {
|
||||||
|
return `${i18n._('quantities.cadence')}: ${getCadenceWithUnits(point.y)}`;
|
||||||
|
} else if (context.datasetIndex === 4) {
|
||||||
|
return `${i18n._('quantities.temperature')}: ${getTemperatureWithUnits(point.y, false)}`;
|
||||||
|
} else if (context.datasetIndex === 5) {
|
||||||
|
return `${i18n._('quantities.power')}: ${getPowerWithUnits(point.y)}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
afterBody: (contexts: Chart.TooltipContext[]) => {
|
||||||
|
let context = contexts.filter((context) => context.datasetIndex === 0);
|
||||||
|
if (context.length === 0) return;
|
||||||
|
let point = context[0].raw;
|
||||||
|
let slope = {
|
||||||
|
at: point.slope.at.toFixed(1),
|
||||||
|
segment: point.slope.segment.toFixed(1),
|
||||||
|
length: getDistanceWithUnits(point.slope.length),
|
||||||
|
};
|
||||||
|
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 = [
|
||||||
|
` ${i18n._('quantities.distance')}: ${getDistanceWithUnits(point.x, false)}`,
|
||||||
|
` ${i18n._('quantities.slope')}: ${slope.at} %${get(this._elevationFill) === 'slope' ? ` (${slope.length} @${slope.segment} %)` : ''}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (get(this._elevationFill) === 'surface') {
|
||||||
|
labels.push(
|
||||||
|
` ${i18n._('quantities.surface')}: ${i18n._(`toolbar.routing.surface.${surface}`)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get(this._elevationFill) === 'highway') {
|
||||||
|
labels.push(
|
||||||
|
` ${i18n._('quantities.highway')}: ${i18n._(`toolbar.routing.highway.${highway}`)}${
|
||||||
|
sacScale
|
||||||
|
? ` (${i18n._(`toolbar.routing.sac_scale.${sacScale}`)})`
|
||||||
|
: ''
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
if (mtbScale) {
|
||||||
|
labels.push(
|
||||||
|
` ${i18n._('toolbar.routing.mtb_scale')}: ${mtbScale}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point.time) {
|
||||||
|
labels.push(
|
||||||
|
` ${i18n._('quantities.time')}: ${i18n.df.format(point.time)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
pan: {
|
||||||
|
enabled: true,
|
||||||
|
mode: 'x',
|
||||||
|
modifierKey: 'shift',
|
||||||
|
onPanStart: () => {
|
||||||
|
this._panning = true;
|
||||||
|
this._slicedGPXStatistics.set(undefined);
|
||||||
|
},
|
||||||
|
onPanComplete: () => {
|
||||||
|
this._panning = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
wheel: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
mode: 'x',
|
||||||
|
onZoomStart: ({ chart, event }: { chart: Chart; event: any }) => {
|
||||||
|
if (
|
||||||
|
event.deltaY < 0 &&
|
||||||
|
Math.abs(
|
||||||
|
this._chart.getInitialScaleBounds().x.max /
|
||||||
|
this._chart.options.plugins.zoom.limits.x.minRange -
|
||||||
|
this._chart.getZoomLevel()
|
||||||
|
) < 0.01
|
||||||
|
) {
|
||||||
|
// Disable wheel pan if zoomed in to the max, and zooming in
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._slicedGPXStatistics.set(undefined);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
limits: {
|
||||||
|
x: {
|
||||||
|
min: 'original',
|
||||||
|
max: 'original',
|
||||||
|
minRange: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stacked: false,
|
||||||
|
onResize: () => {
|
||||||
|
this.updateOverlay();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let datasets: string[] = ['speed', 'hr', 'cad', 'atemp', 'power'];
|
||||||
|
datasets.forEach((id) => {
|
||||||
|
options.scales[`y${id}`] = {
|
||||||
|
type: 'linear',
|
||||||
|
position: 'right',
|
||||||
|
grid: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
reverse: () => id === 'speed' && get(velocityUnits) === 'pace',
|
||||||
|
display: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
this._chart = new Chart(this._canvas, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [],
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
id: 'toggleMarker',
|
||||||
|
events: ['mouseout'],
|
||||||
|
afterEvent: (chart: Chart, args: { event: Chart.ChartEvent }) => {
|
||||||
|
if (args.event.type === 'mouseout') {
|
||||||
|
const map_ = get(map);
|
||||||
|
if (map_ && this._marker) {
|
||||||
|
this._marker.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let startIndex = 0;
|
||||||
|
let endIndex = 0;
|
||||||
|
const getIndex = (evt) => {
|
||||||
|
if (!this._chart) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const points = this._chart.getElementsAtEventForMode(
|
||||||
|
evt,
|
||||||
|
'x',
|
||||||
|
{
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
if (points.length === 0) {
|
||||||
|
const rect = this._canvas.getBoundingClientRect();
|
||||||
|
if (evt.x - rect.left <= this._chart.chartArea.left) {
|
||||||
|
return 0;
|
||||||
|
} else if (evt.x - rect.left >= this._chart.chartArea.right) {
|
||||||
|
return get(this._gpxStatistics).local.points.length - 1;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let point = points.find((point) => point.element.raw);
|
||||||
|
if (point) {
|
||||||
|
return point.element.raw.index;
|
||||||
|
} else {
|
||||||
|
return points[0].index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let dragStarted = false;
|
||||||
|
const onMouseDown = (evt) => {
|
||||||
|
if (evt.shiftKey) {
|
||||||
|
// Panning interaction
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dragStarted = true;
|
||||||
|
this._canvas.style.cursor = 'col-resize';
|
||||||
|
startIndex = getIndex(evt);
|
||||||
|
};
|
||||||
|
const onMouseMove = (evt) => {
|
||||||
|
if (dragStarted) {
|
||||||
|
this._dragging = true;
|
||||||
|
endIndex = getIndex(evt);
|
||||||
|
if (endIndex !== undefined) {
|
||||||
|
if (startIndex === undefined) {
|
||||||
|
startIndex = endIndex;
|
||||||
|
} else if (startIndex !== endIndex) {
|
||||||
|
this._slicedGPXStatistics.set([
|
||||||
|
get(this._gpxStatistics).slice(
|
||||||
|
Math.min(startIndex, endIndex),
|
||||||
|
Math.max(startIndex, endIndex)
|
||||||
|
),
|
||||||
|
Math.min(startIndex, endIndex),
|
||||||
|
Math.max(startIndex, endIndex),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onMouseUp = (evt) => {
|
||||||
|
dragStarted = false;
|
||||||
|
this._dragging = false;
|
||||||
|
this._canvas.style.cursor = '';
|
||||||
|
endIndex = getIndex(evt);
|
||||||
|
if (startIndex === endIndex) {
|
||||||
|
this._slicedGPXStatistics.set(undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this._canvas.addEventListener('pointerdown', onMouseDown);
|
||||||
|
this._canvas.addEventListener('pointermove', onMouseMove);
|
||||||
|
this._canvas.addEventListener('pointerup', onMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData() {
|
||||||
|
if (!this._chart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = get(this._gpxStatistics);
|
||||||
|
this._chart.data.datasets[0] = {
|
||||||
|
label: i18n._('quantities.elevation'),
|
||||||
|
data: data.local.points.map((point, index) => {
|
||||||
|
return {
|
||||||
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
|
y: point.ele ? getConvertedElevation(point.ele) : 0,
|
||||||
|
time: point.time,
|
||||||
|
slope: {
|
||||||
|
at: data.local.slope.at[index],
|
||||||
|
segment: data.local.slope.segment[index],
|
||||||
|
length: data.local.slope.length[index],
|
||||||
|
},
|
||||||
|
extensions: point.getExtensions(),
|
||||||
|
coordinates: point.getCoordinates(),
|
||||||
|
index: index,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
normalized: true,
|
||||||
|
fill: 'start',
|
||||||
|
order: 1,
|
||||||
|
segment: {},
|
||||||
|
};
|
||||||
|
this._chart.data.datasets[1] = {
|
||||||
|
data: data.local.points.map((point, index) => {
|
||||||
|
return {
|
||||||
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
|
y: getConvertedVelocity(data.local.speed[index]),
|
||||||
|
index: index,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
normalized: true,
|
||||||
|
yAxisID: 'yspeed',
|
||||||
|
};
|
||||||
|
this._chart.data.datasets[2] = {
|
||||||
|
data: data.local.points.map((point, index) => {
|
||||||
|
return {
|
||||||
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
|
y: point.getHeartRate(),
|
||||||
|
index: index,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
normalized: true,
|
||||||
|
yAxisID: 'yhr',
|
||||||
|
};
|
||||||
|
this._chart.data.datasets[3] = {
|
||||||
|
data: data.local.points.map((point, index) => {
|
||||||
|
return {
|
||||||
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
|
y: point.getCadence(),
|
||||||
|
index: index,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
normalized: true,
|
||||||
|
yAxisID: 'ycad',
|
||||||
|
};
|
||||||
|
this._chart.data.datasets[4] = {
|
||||||
|
data: data.local.points.map((point, index) => {
|
||||||
|
return {
|
||||||
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
|
y: getConvertedTemperature(point.getTemperature()),
|
||||||
|
index: index,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
normalized: true,
|
||||||
|
yAxisID: 'yatemp',
|
||||||
|
};
|
||||||
|
this._chart.data.datasets[5] = {
|
||||||
|
data: data.local.points.map((point, index) => {
|
||||||
|
return {
|
||||||
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
|
y: point.getPower(),
|
||||||
|
index: index,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
normalized: true,
|
||||||
|
yAxisID: 'ypower',
|
||||||
|
};
|
||||||
|
this._chart.options.scales.x['min'] = 0;
|
||||||
|
this._chart.options.scales.x['max'] = getConvertedDistance(data.global.distance.total);
|
||||||
|
|
||||||
|
this.setVisibility();
|
||||||
|
this.setFill();
|
||||||
|
|
||||||
|
this._chart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDataVisibility() {
|
||||||
|
if (!this._chart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setVisibility();
|
||||||
|
this._chart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
setVisibility() {
|
||||||
|
if (!this._chart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const additionalDatasets = get(this._additionalDatasets);
|
||||||
|
let includeSpeed = additionalDatasets.includes('speed');
|
||||||
|
let includeHeartRate = additionalDatasets.includes('hr');
|
||||||
|
let includeCadence = additionalDatasets.includes('cad');
|
||||||
|
let includeTemperature = additionalDatasets.includes('atemp');
|
||||||
|
let includePower = additionalDatasets.includes('power');
|
||||||
|
if (this._chart.data.datasets.length == 6) {
|
||||||
|
this._chart.data.datasets[1].hidden = !includeSpeed;
|
||||||
|
this._chart.data.datasets[2].hidden = !includeHeartRate;
|
||||||
|
this._chart.data.datasets[3].hidden = !includeCadence;
|
||||||
|
this._chart.data.datasets[4].hidden = !includeTemperature;
|
||||||
|
this._chart.data.datasets[5].hidden = !includePower;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFill() {
|
||||||
|
if (!this._chart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setFill();
|
||||||
|
this._chart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
setFill() {
|
||||||
|
if (!this._chart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const elevationFill = get(this._elevationFill);
|
||||||
|
if (elevationFill === 'slope') {
|
||||||
|
this._chart.data.datasets[0]['segment'] = {
|
||||||
|
backgroundColor: this.slopeFillCallback,
|
||||||
|
};
|
||||||
|
} else if (elevationFill === 'surface') {
|
||||||
|
this._chart.data.datasets[0]['segment'] = {
|
||||||
|
backgroundColor: this.surfaceFillCallback,
|
||||||
|
};
|
||||||
|
} else if (elevationFill === 'highway') {
|
||||||
|
this._chart.data.datasets[0]['segment'] = {
|
||||||
|
backgroundColor: this.highwayFillCallback,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this._chart.data.datasets[0]['segment'] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOverlay() {
|
||||||
|
if (!this._chart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._overlay.width = this._canvas.width / window.devicePixelRatio;
|
||||||
|
this._overlay.height = this._canvas.height / window.devicePixelRatio;
|
||||||
|
this._overlay.style.width = `${this._overlay.width}px`;
|
||||||
|
this._overlay.style.height = `${this._overlay.height}px`;
|
||||||
|
|
||||||
|
const slicedGPXStatistics = get(this._slicedGPXStatistics);
|
||||||
|
if (slicedGPXStatistics) {
|
||||||
|
let startIndex = slicedGPXStatistics[1];
|
||||||
|
let endIndex = slicedGPXStatistics[2];
|
||||||
|
|
||||||
|
// Draw selection rectangle
|
||||||
|
let selectionContext = this._overlay.getContext('2d');
|
||||||
|
if (selectionContext) {
|
||||||
|
selectionContext.fillStyle = mode.current === 'dark' ? 'white' : 'black';
|
||||||
|
selectionContext.globalAlpha = mode.current === 'dark' ? 0.2 : 0.1;
|
||||||
|
selectionContext.clearRect(0, 0, this._overlay.width, this._overlay.height);
|
||||||
|
|
||||||
|
const gpxStatistics = get(this._gpxStatistics);
|
||||||
|
let startPixel = this._chart.scales.x.getPixelForValue(
|
||||||
|
getConvertedDistance(gpxStatistics.local.distance.total[startIndex])
|
||||||
|
);
|
||||||
|
let endPixel = this._chart.scales.x.getPixelForValue(
|
||||||
|
getConvertedDistance(gpxStatistics.local.distance.total[endIndex])
|
||||||
|
);
|
||||||
|
|
||||||
|
selectionContext.fillRect(
|
||||||
|
startPixel,
|
||||||
|
this._chart.chartArea.top,
|
||||||
|
endPixel - startPixel,
|
||||||
|
this._chart.chartArea.height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (this._overlay) {
|
||||||
|
let selectionContext = this._overlay.getContext('2d');
|
||||||
|
if (selectionContext) {
|
||||||
|
selectionContext.clearRect(0, 0, this._overlay.width, this._overlay.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slopeFillCallback(context) {
|
||||||
|
return getSlopeColor(context.p0.raw.slope.segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
surfaceFillCallback(context) {
|
||||||
|
return getSurfaceColor(context.p0.raw.extensions.surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
highwayFillCallback(context) {
|
||||||
|
return getHighwayColor(
|
||||||
|
context.p0.raw.extensions.highway,
|
||||||
|
context.p0.raw.extensions.sac_scale,
|
||||||
|
context.p0.raw.extensions.mtb_scale
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this._chart) {
|
||||||
|
this._chart.destroy();
|
||||||
|
}
|
||||||
|
if (this._marker) {
|
||||||
|
this._marker.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Fitxers i estadístiques
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ També pots utilitzar la rodeta del ratolí per apropar o allunyar el perfil d'e
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Soubory a statistiky
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Pomocí kolečka myši můžete také výškový profil přiblížit a oddálit
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Dateien und Statistiken
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Sie können auch das Mausrad verwenden, um auf dem Höhenprofil heranzuzoomen un
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Archivos y estadísticas
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Puede usar el ratón para acercar o alejar el perfil de elevación y moverse hac
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Fitxategiak eta estatistikak
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -83,8 +83,8 @@ Saguaren gurpila ere erabil dezakezu altueren profila handitzeko eta mugitzeko e
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Fichiers et statistiques
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Vous pouvez également utiliser la molette de la souris pour zoomer ou dézoomer
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Fájlok és statisztikák
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Az egérgörgővel is méretezheti a magassági profilt. Balra és jobbra mozogh
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: File e statistiche
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Fare clic sul profilo per resettare la selezione.
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Puslapis nerastas
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Bestanden en statistieken
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Je kunt het muiswiel ook gebruiken om in en uit te zoomen op het hoogte profiel,
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Arquivos e estatísticas
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Você também pode usar a roda do mouse para aumentar e diminuir o zoom no perfi
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Файлы и статистика
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ In addition, the file tree view enables you to inspect the [tracks, segments, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Datoteke i statistike
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Takođe možete da koristite točkić miša da zumirate i umanjite profil nadmo
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ Du kan också använda mushjulet för att zooma in och ut på höjdprofilen och
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Dosyalar ve istatistikler
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -83,8 +83,8 @@ Ayrıca, fare tekerleği ile yükselti profiline yakınlaşıp uzaklaşabilir ve
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: Files and statistics
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ title: 文件和统计
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
# { title }
|
# { title }
|
||||||
@@ -84,8 +84,8 @@ title: 文件和统计
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center -mt-6">
|
<div class="flex flex-col items-center -mt-6">
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
let gpxStatistics = writable(exampleGPXFile.getStatistics());
|
||||||
let slicedGPXStatistics = writable(undefined);
|
let slicedGPXStatistics = writable(undefined);
|
||||||
let additionalDatasets = writable(['speed', 'atemp']);
|
let additionalDatasets = writable(['speed', 'atemp']);
|
||||||
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
|
let elevationFill = writable(undefined);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
$currentTool = Tool.SCISSORS;
|
$currentTool = Tool.SCISSORS;
|
||||||
@@ -193,8 +193,8 @@
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
|
|||||||
@@ -132,8 +132,8 @@
|
|||||||
<ElevationProfile
|
<ElevationProfile
|
||||||
{gpxStatistics}
|
{gpxStatistics}
|
||||||
{slicedGPXStatistics}
|
{slicedGPXStatistics}
|
||||||
bind:additionalDatasets={$additionalDatasets}
|
{additionalDatasets}
|
||||||
bind:elevationFill={$elevationFill}
|
{elevationFill}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user