diff --git a/website/src/lib/components/elevation-profile/ElevationProfile.svelte b/website/src/lib/components/elevation-profile/ElevationProfile.svelte index e2e63e87..33501df5 100644 --- a/website/src/lib/components/elevation-profile/ElevationProfile.svelte +++ b/website/src/lib/components/elevation-profile/ElevationProfile.svelte @@ -3,8 +3,6 @@ import * as Popover from '$lib/components/ui/popover/index.js'; import * as ToggleGroup from '$lib/components/ui/toggle-group/index.js'; 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 { BrickWall, @@ -19,552 +17,45 @@ ChartNoAxesColumn, Construction, } 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 { GPXStatistics } from 'gpx'; - import { mode } from 'mode-watcher'; import { settings } from '$lib/logic/settings'; - import { map } from '$lib/components/map/map'; import { i18n } from '$lib/i18n.svelte'; + import { ElevationProfile } from '$lib/components/elevation-profile/elevation-profile'; - const { distanceUnits, velocityUnits, temperatureUnits } = settings; + const { velocityUnits } = settings; let { gpxStatistics, slicedGPXStatistics, - additionalDatasets = $bindable(), - elevationFill = $bindable(), + additionalDatasets, + elevationFill, showControls = true, }: { gpxStatistics: Readable; slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>; - additionalDatasets: string[]; - elevationFill: 'slope' | 'surface' | 'highway' | undefined; + additionalDatasets: Writable; + elevationFill: Writable<'slope' | 'surface' | 'highway' | undefined>; showControls?: boolean; } = $props(); let canvas: HTMLCanvasElement; let overlay: HTMLCanvasElement; - let chart: Chart; + let elevationProfile: ElevationProfile; - 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 - - let marker: mapboxgl.Marker | null = null; - let dragging = false; - let panning = false; - - 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: () => !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 + onMount(() => { + elevationProfile = new ElevationProfile( + gpxStatistics, + slicedGPXStatistics, + additionalDatasets, + elevationFill, + canvas, + overlay ); - } - - $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(() => { - if (chart) { - chart.destroy(); - } + elevationProfile.destroy(); }); @@ -593,14 +84,14 @@
- {#if elevationFill === 'slope'} + {#if $elevationFill === 'slope'} {/if}
@@ -613,7 +104,7 @@ variant="outline" >
- {#if elevationFill === 'surface'} + {#if $elevationFill === 'surface'} {/if}
@@ -626,7 +117,7 @@ variant="outline" >
- {#if elevationFill === 'highway'} + {#if $elevationFill === 'highway'} {/if}
@@ -638,14 +129,14 @@
- {#if additionalDatasets.includes('speed')} + {#if $additionalDatasets.includes('speed')} {/if}
@@ -659,7 +150,7 @@ value="hr" >
- {#if additionalDatasets.includes('hr')} + {#if $additionalDatasets.includes('hr')} {/if}
@@ -671,7 +162,7 @@ value="cad" >
- {#if additionalDatasets.includes('cad')} + {#if $additionalDatasets.includes('cad')} {/if}
@@ -683,7 +174,7 @@ value="atemp" >
- {#if additionalDatasets.includes('atemp')} + {#if $additionalDatasets.includes('atemp')} {/if}
@@ -695,7 +186,7 @@ value="power" >
- {#if additionalDatasets.includes('power')} + {#if $additionalDatasets.includes('power')} {/if}
diff --git a/website/src/lib/components/elevation-profile/elevation-profile.ts b/website/src/lib/components/elevation-profile/elevation-profile.ts new file mode 100644 index 00000000..070992fc --- /dev/null +++ b/website/src/lib/components/elevation-profile/elevation-profile.ts @@ -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; + private _slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>; + private _additionalDatasets: Readable; + private _elevationFill: Readable<'slope' | 'surface' | 'highway' | undefined>; + + constructor( + gpxStatistics: Readable, + slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>, + additionalDatasets: Readable, + 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(); + } + } +} diff --git a/website/src/lib/docs/be/files-and-stats.mdx b/website/src/lib/docs/be/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/be/files-and-stats.mdx +++ b/website/src/lib/docs/be/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/ca/files-and-stats.mdx b/website/src/lib/docs/ca/files-and-stats.mdx index 9da662a6..1efe9ccd 100644 --- a/website/src/lib/docs/ca/files-and-stats.mdx +++ b/website/src/lib/docs/ca/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Fitxers i estadístiques let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ També pots utilitzar la rodeta del ratolí per apropar o allunyar el perfil d'e
diff --git a/website/src/lib/docs/cs/files-and-stats.mdx b/website/src/lib/docs/cs/files-and-stats.mdx index d4878b9d..89ac2d72 100644 --- a/website/src/lib/docs/cs/files-and-stats.mdx +++ b/website/src/lib/docs/cs/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Soubory a statistiky let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Pomocí kolečka myši můžete také výškový profil přiblížit a oddálit
diff --git a/website/src/lib/docs/da/files-and-stats.mdx b/website/src/lib/docs/da/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/da/files-and-stats.mdx +++ b/website/src/lib/docs/da/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/de/files-and-stats.mdx b/website/src/lib/docs/de/files-and-stats.mdx index b6c5634e..7a35d45b 100644 --- a/website/src/lib/docs/de/files-and-stats.mdx +++ b/website/src/lib/docs/de/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Dateien und Statistiken let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Sie können auch das Mausrad verwenden, um auf dem Höhenprofil heranzuzoomen un
diff --git a/website/src/lib/docs/el/files-and-stats.mdx b/website/src/lib/docs/el/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/el/files-and-stats.mdx +++ b/website/src/lib/docs/el/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/en/files-and-stats.mdx b/website/src/lib/docs/en/files-and-stats.mdx index a1d5031a..634f19f9 100644 --- a/website/src/lib/docs/en/files-and-stats.mdx +++ b/website/src/lib/docs/en/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/es/files-and-stats.mdx b/website/src/lib/docs/es/files-and-stats.mdx index e46a45f8..5349ea5e 100644 --- a/website/src/lib/docs/es/files-and-stats.mdx +++ b/website/src/lib/docs/es/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Archivos y estadísticas let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Puede usar el ratón para acercar o alejar el perfil de elevación y moverse hac
diff --git a/website/src/lib/docs/eu/files-and-stats.mdx b/website/src/lib/docs/eu/files-and-stats.mdx index 8f06bfce..cf176e2a 100644 --- a/website/src/lib/docs/eu/files-and-stats.mdx +++ b/website/src/lib/docs/eu/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Fitxategiak eta estatistikak let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -83,8 +83,8 @@ Saguaren gurpila ere erabil dezakezu altueren profila handitzeko eta mugitzeko e
diff --git a/website/src/lib/docs/fi/files-and-stats.mdx b/website/src/lib/docs/fi/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/fi/files-and-stats.mdx +++ b/website/src/lib/docs/fi/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/fr/files-and-stats.mdx b/website/src/lib/docs/fr/files-and-stats.mdx index 674e20f3..61450176 100644 --- a/website/src/lib/docs/fr/files-and-stats.mdx +++ b/website/src/lib/docs/fr/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Fichiers et statistiques let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Vous pouvez également utiliser la molette de la souris pour zoomer ou dézoomer
diff --git a/website/src/lib/docs/he/files-and-stats.mdx b/website/src/lib/docs/he/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/he/files-and-stats.mdx +++ b/website/src/lib/docs/he/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/hu/files-and-stats.mdx b/website/src/lib/docs/hu/files-and-stats.mdx index fc4d49ce..538088a5 100644 --- a/website/src/lib/docs/hu/files-and-stats.mdx +++ b/website/src/lib/docs/hu/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Fájlok és statisztikák let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Az egérgörgővel is méretezheti a magassági profilt. Balra és jobbra mozogh
diff --git a/website/src/lib/docs/it/files-and-stats.mdx b/website/src/lib/docs/it/files-and-stats.mdx index 91ae4c1b..a72f05f9 100644 --- a/website/src/lib/docs/it/files-and-stats.mdx +++ b/website/src/lib/docs/it/files-and-stats.mdx @@ -13,7 +13,7 @@ title: File e statistiche let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Fare clic sul profilo per resettare la selezione.
diff --git a/website/src/lib/docs/ko/files-and-stats.mdx b/website/src/lib/docs/ko/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/ko/files-and-stats.mdx +++ b/website/src/lib/docs/ko/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/lt/files-and-stats.mdx b/website/src/lib/docs/lt/files-and-stats.mdx index ce61f57d..0711ee7f 100644 --- a/website/src/lib/docs/lt/files-and-stats.mdx +++ b/website/src/lib/docs/lt/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Puslapis nerastas let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/lv/files-and-stats.mdx b/website/src/lib/docs/lv/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/lv/files-and-stats.mdx +++ b/website/src/lib/docs/lv/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/nl/files-and-stats.mdx b/website/src/lib/docs/nl/files-and-stats.mdx index 7cbd1e5b..ff49af3b 100644 --- a/website/src/lib/docs/nl/files-and-stats.mdx +++ b/website/src/lib/docs/nl/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Bestanden en statistieken let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Je kunt het muiswiel ook gebruiken om in en uit te zoomen op het hoogte profiel,
diff --git a/website/src/lib/docs/no/files-and-stats.mdx b/website/src/lib/docs/no/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/no/files-and-stats.mdx +++ b/website/src/lib/docs/no/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/pl/files-and-stats.mdx b/website/src/lib/docs/pl/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/pl/files-and-stats.mdx +++ b/website/src/lib/docs/pl/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/pt-BR/files-and-stats.mdx b/website/src/lib/docs/pt-BR/files-and-stats.mdx index c97defe0..d83e9e0f 100644 --- a/website/src/lib/docs/pt-BR/files-and-stats.mdx +++ b/website/src/lib/docs/pt-BR/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Arquivos e estatísticas let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Você também pode usar a roda do mouse para aumentar e diminuir o zoom no perfi
diff --git a/website/src/lib/docs/pt/files-and-stats.mdx b/website/src/lib/docs/pt/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/pt/files-and-stats.mdx +++ b/website/src/lib/docs/pt/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/ro/files-and-stats.mdx b/website/src/lib/docs/ro/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/ro/files-and-stats.mdx +++ b/website/src/lib/docs/ro/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/ru/files-and-stats.mdx b/website/src/lib/docs/ru/files-and-stats.mdx index 9cdacd81..340be748 100644 --- a/website/src/lib/docs/ru/files-and-stats.mdx +++ b/website/src/lib/docs/ru/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Файлы и статистика let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ In addition, the file tree view enables you to inspect the [tracks, segments, an
diff --git a/website/src/lib/docs/sr/files-and-stats.mdx b/website/src/lib/docs/sr/files-and-stats.mdx index 7d9a02ac..f91eb222 100644 --- a/website/src/lib/docs/sr/files-and-stats.mdx +++ b/website/src/lib/docs/sr/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Datoteke i statistike let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Takođe možete da koristite točkić miša da zumirate i umanjite profil nadmo
diff --git a/website/src/lib/docs/sv/files-and-stats.mdx b/website/src/lib/docs/sv/files-and-stats.mdx index f9bd0a95..a800a6ce 100644 --- a/website/src/lib/docs/sv/files-and-stats.mdx +++ b/website/src/lib/docs/sv/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ Du kan också använda mushjulet för att zooma in och ut på höjdprofilen och
diff --git a/website/src/lib/docs/tr/files-and-stats.mdx b/website/src/lib/docs/tr/files-and-stats.mdx index 85999c56..80c50291 100644 --- a/website/src/lib/docs/tr/files-and-stats.mdx +++ b/website/src/lib/docs/tr/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Dosyalar ve istatistikler let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -83,8 +83,8 @@ Ayrıca, fare tekerleği ile yükselti profiline yakınlaşıp uzaklaşabilir ve
diff --git a/website/src/lib/docs/uk/files-and-stats.mdx b/website/src/lib/docs/uk/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/uk/files-and-stats.mdx +++ b/website/src/lib/docs/uk/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/vi/files-and-stats.mdx b/website/src/lib/docs/vi/files-and-stats.mdx index 1a15d542..c682ff7f 100644 --- a/website/src/lib/docs/vi/files-and-stats.mdx +++ b/website/src/lib/docs/vi/files-and-stats.mdx @@ -13,7 +13,7 @@ title: Files and statistics let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
diff --git a/website/src/lib/docs/zh/files-and-stats.mdx b/website/src/lib/docs/zh/files-and-stats.mdx index 07debe51..e7e6a3fc 100644 --- a/website/src/lib/docs/zh/files-and-stats.mdx +++ b/website/src/lib/docs/zh/files-and-stats.mdx @@ -13,7 +13,7 @@ title: 文件和统计 let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); # { title } @@ -84,8 +84,8 @@ title: 文件和统计
diff --git a/website/src/routes/[[language]]/+page.svelte b/website/src/routes/[[language]]/+page.svelte index 56d8b364..0753289d 100644 --- a/website/src/routes/[[language]]/+page.svelte +++ b/website/src/routes/[[language]]/+page.svelte @@ -36,7 +36,7 @@ let gpxStatistics = writable(exampleGPXFile.getStatistics()); let slicedGPXStatistics = writable(undefined); let additionalDatasets = writable(['speed', 'atemp']); - let elevationFill = writable<'slope' | 'surface' | undefined>(undefined); + let elevationFill = writable(undefined); onMount(() => { $currentTool = Tool.SCISSORS; @@ -193,8 +193,8 @@
diff --git a/website/src/routes/[[language]]/app/+page.svelte b/website/src/routes/[[language]]/app/+page.svelte index e4e0119c..5859e47f 100644 --- a/website/src/routes/[[language]]/app/+page.svelte +++ b/website/src/routes/[[language]]/app/+page.svelte @@ -132,8 +132,8 @@ {/if}