2024-07-11 18:42:49 +02:00
|
|
|
<script lang="ts">
|
|
|
|
import * as Card from '$lib/components/ui/card';
|
|
|
|
import { Label } from '$lib/components/ui/label';
|
|
|
|
import { Input } from '$lib/components/ui/input';
|
|
|
|
import * as Select from '$lib/components/ui/select';
|
|
|
|
import { Checkbox } from '$lib/components/ui/checkbox';
|
|
|
|
import * as RadioGroup from '$lib/components/ui/radio-group';
|
|
|
|
import { Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
|
|
|
|
import { _ } from 'svelte-i18n';
|
2024-07-12 15:00:33 +02:00
|
|
|
import {
|
|
|
|
allowedEmbeddingBasemaps,
|
|
|
|
getCleanedEmbeddingOptions,
|
|
|
|
getDefaultEmbeddingOptions
|
|
|
|
} from './Embedding';
|
|
|
|
import { PUBLIC_MAPBOX_TOKEN } from '$env/static/public';
|
|
|
|
import Embedding from './Embedding.svelte';
|
|
|
|
import { map } from '$lib/stores';
|
|
|
|
import { tick } from 'svelte';
|
|
|
|
import { base } from '$app/paths';
|
2024-07-11 18:42:49 +02:00
|
|
|
|
2024-07-12 15:00:33 +02:00
|
|
|
let options = getDefaultEmbeddingOptions();
|
|
|
|
options.token = 'YOUR_MAPBOX_TOKEN';
|
|
|
|
options.files = [
|
|
|
|
'https://raw.githubusercontent.com/gpxstudio/gpx.studio/main/gpx/test-data/simple.gpx'
|
|
|
|
];
|
2024-07-11 18:42:49 +02:00
|
|
|
|
2024-07-12 15:00:33 +02:00
|
|
|
let files = options.files[0];
|
2024-07-11 18:42:49 +02:00
|
|
|
$: if (files) {
|
2024-07-12 15:00:33 +02:00
|
|
|
let urls = files.split(',');
|
|
|
|
urls = urls.filter((url) => url.length > 0);
|
|
|
|
if (JSON.stringify(urls) !== JSON.stringify(options.files)) {
|
|
|
|
options.files = urls;
|
|
|
|
}
|
2024-07-11 18:42:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
let manualCamera = false;
|
|
|
|
|
|
|
|
let zoom = 0;
|
|
|
|
let lat = 0;
|
|
|
|
let lon = 0;
|
|
|
|
let bearing = 0;
|
|
|
|
let pitch = 0;
|
|
|
|
|
|
|
|
$: hash = manualCamera ? `#${zoom}/${lat}/${lon}/${bearing}/${pitch}` : '';
|
|
|
|
|
2024-07-12 15:00:33 +02:00
|
|
|
$: iframeOptions =
|
|
|
|
options.token.length === 0 || options.token === 'YOUR_MAPBOX_TOKEN'
|
|
|
|
? Object.assign({}, options, { token: PUBLIC_MAPBOX_TOKEN })
|
|
|
|
: options;
|
2024-07-11 18:42:49 +02:00
|
|
|
|
2024-07-12 15:00:33 +02:00
|
|
|
async function resizeMap() {
|
|
|
|
if ($map) {
|
|
|
|
await tick();
|
|
|
|
$map.resize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$: if (options.elevation.height || options.elevation.show) {
|
|
|
|
resizeMap();
|
|
|
|
}
|
|
|
|
</script>
|
2024-07-11 18:42:49 +02:00
|
|
|
|
|
|
|
<Card.Root>
|
|
|
|
<Card.Header>
|
2024-07-12 15:00:33 +02:00
|
|
|
<Card.Title>{$_('embedding.title')}</Card.Title>
|
2024-07-11 18:42:49 +02:00
|
|
|
</Card.Header>
|
|
|
|
<Card.Content>
|
|
|
|
<fieldset class="flex flex-col gap-3">
|
2024-07-12 15:00:33 +02:00
|
|
|
<Label for="token">{$_('embedding.mapbox_token')}</Label>
|
|
|
|
<Input id="token" type="text" class="h-8" bind:value={options.token} />
|
2024-07-11 18:42:49 +02:00
|
|
|
<Label for="file_urls">{$_('embedding.file_urls')}</Label>
|
|
|
|
<Input id="file_urls" type="text" class="h-8" bind:value={files} />
|
|
|
|
<Label for="basemap">{$_('embedding.basemap')}</Label>
|
|
|
|
<Select.Root
|
2024-07-12 15:00:33 +02:00
|
|
|
selected={{ value: options.basemap, label: $_(`layers.label.${options.basemap}`) }}
|
2024-07-11 18:42:49 +02:00
|
|
|
onSelectedChange={(selected) => {
|
|
|
|
if (selected?.value) {
|
|
|
|
options.basemap = selected?.value;
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
2024-07-12 15:00:33 +02:00
|
|
|
<Select.Trigger id="basemap" class="w-full h-8">
|
2024-07-11 18:42:49 +02:00
|
|
|
<Select.Value />
|
|
|
|
</Select.Trigger>
|
|
|
|
<Select.Content class="max-h-60 overflow-y-scroll">
|
2024-07-12 15:00:33 +02:00
|
|
|
{#each allowedEmbeddingBasemaps as basemap}
|
2024-07-11 18:42:49 +02:00
|
|
|
<Select.Item value={basemap}>{$_(`layers.label.${basemap}`)}</Select.Item>
|
|
|
|
{/each}
|
|
|
|
</Select.Content>
|
|
|
|
</Select.Root>
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
|
|
|
<Label for="profile">{$_('menu.elevation_profile')}</Label>
|
|
|
|
<Checkbox id="profile" bind:checked={options.elevation.show} />
|
|
|
|
</div>
|
|
|
|
{#if options.elevation.show}
|
|
|
|
<div class="grid grid-cols-2 gap-x-6 gap-y-3 rounded-md border p-3 mt-1">
|
|
|
|
<Label class="flex flex-row items-center gap-2">
|
|
|
|
{$_('embedding.height')}
|
|
|
|
<Input type="number" bind:value={options.elevation.height} class="h-8 w-20" />
|
|
|
|
</Label>
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
|
|
|
<span class="shrink-0">
|
|
|
|
{$_('embedding.fill_by')}
|
|
|
|
</span>
|
|
|
|
<Select.Root
|
2024-07-12 15:00:33 +02:00
|
|
|
selected={{ value: 'none', label: $_('embedding.none') }}
|
2024-07-11 18:42:49 +02:00
|
|
|
onSelectedChange={(selected) => {
|
2024-07-12 15:00:33 +02:00
|
|
|
let value = selected?.value;
|
|
|
|
if (value === 'none') {
|
|
|
|
options.elevation.fill = undefined;
|
|
|
|
} else if (value === 'slope' || value === 'surface') {
|
|
|
|
options.elevation.fill = value;
|
2024-07-11 18:42:49 +02:00
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Select.Trigger class="grow h-8">
|
|
|
|
<Select.Value />
|
|
|
|
</Select.Trigger>
|
|
|
|
<Select.Content>
|
|
|
|
<Select.Item value="slope">{$_('quantities.slope')}</Select.Item>
|
|
|
|
<Select.Item value="surface">{$_('quantities.surface')}</Select.Item>
|
|
|
|
<Select.Item value="none">{$_('embedding.none')}</Select.Item>
|
|
|
|
</Select.Content>
|
|
|
|
</Select.Root>
|
|
|
|
</div>
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
|
|
|
<Checkbox id="controls" bind:checked={options.elevation.controls} />
|
|
|
|
<Label for="controls">{$_('embedding.show_controls')}</Label>
|
|
|
|
</div>
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
2024-07-12 15:00:33 +02:00
|
|
|
<Checkbox id="show-speed" bind:checked={options.elevation.speed} />
|
2024-07-11 18:42:49 +02:00
|
|
|
<Label for="show-speed" class="flex flex-row items-center gap-1">
|
|
|
|
<Zap size="16" />
|
|
|
|
{$_('chart.show_speed')}
|
|
|
|
</Label>
|
|
|
|
</div>
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
2024-07-12 15:00:33 +02:00
|
|
|
<Checkbox id="show-hr" bind:checked={options.elevation.hr} />
|
2024-07-11 18:42:49 +02:00
|
|
|
<Label for="show-hr" class="flex flex-row items-center gap-1">
|
|
|
|
<HeartPulse size="16" />
|
|
|
|
{$_('chart.show_heartrate')}
|
|
|
|
</Label>
|
|
|
|
</div>
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
2024-07-12 15:00:33 +02:00
|
|
|
<Checkbox id="show-cad" bind:checked={options.elevation.cad} />
|
2024-07-11 18:42:49 +02:00
|
|
|
<Label for="show-cad" class="flex flex-row items-center gap-1">
|
|
|
|
<Orbit size="16" />
|
|
|
|
{$_('chart.show_cadence')}
|
|
|
|
</Label>
|
|
|
|
</div>
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
2024-07-12 15:00:33 +02:00
|
|
|
<Checkbox id="show-temp" bind:checked={options.elevation.temp} />
|
2024-07-11 18:42:49 +02:00
|
|
|
<Label for="show-temp" class="flex flex-row items-center gap-1">
|
|
|
|
<Thermometer size="16" />
|
|
|
|
{$_('chart.show_temperature')}
|
|
|
|
</Label>
|
|
|
|
</div>
|
|
|
|
<div class="flex flex-row items-center gap-2">
|
2024-07-12 15:00:33 +02:00
|
|
|
<Checkbox id="show-power" bind:checked={options.elevation.power} />
|
2024-07-11 18:42:49 +02:00
|
|
|
<Label for="show-power" class="flex flex-row items-center gap-1">
|
|
|
|
<SquareActivity size="16" />
|
|
|
|
{$_('chart.show_power')}
|
|
|
|
</Label>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/if}
|
|
|
|
<div class="flex flex-row flex-wrap justify-between gap-3">
|
|
|
|
<Label class="flex flex-col items-start gap-2">
|
|
|
|
{$_('menu.distance_units')}
|
|
|
|
<RadioGroup.Root bind:value={options.distanceUnits}>
|
|
|
|
<div class="flex items-center space-x-2">
|
|
|
|
<RadioGroup.Item value="metric" id="metric" />
|
|
|
|
<Label for="metric">{$_('menu.metric')}</Label>
|
|
|
|
</div>
|
|
|
|
<div class="flex items-center space-x-2">
|
|
|
|
<RadioGroup.Item value="imperial" id="imperial" />
|
|
|
|
<Label for="imperial">{$_('menu.imperial')}</Label>
|
|
|
|
</div>
|
|
|
|
</RadioGroup.Root>
|
|
|
|
</Label>
|
|
|
|
<Label class="flex flex-col items-start gap-2">
|
|
|
|
{$_('menu.velocity_units')}
|
|
|
|
<RadioGroup.Root bind:value={options.velocityUnits}>
|
|
|
|
<div class="flex items-center space-x-2">
|
|
|
|
<RadioGroup.Item value="speed" id="speed" />
|
|
|
|
<Label for="speed">{$_('quantities.speed')}</Label>
|
|
|
|
</div>
|
|
|
|
<div class="flex items-center space-x-2">
|
|
|
|
<RadioGroup.Item value="pace" id="pace" />
|
|
|
|
<Label for="pace">{$_('quantities.pace')}</Label>
|
|
|
|
</div>
|
|
|
|
</RadioGroup.Root>
|
|
|
|
</Label>
|
|
|
|
<Label class="flex flex-col items-start gap-2">
|
|
|
|
{$_('menu.temperature_units')}
|
|
|
|
<RadioGroup.Root bind:value={options.temperatureUnits}>
|
|
|
|
<div class="flex items-center space-x-2">
|
|
|
|
<RadioGroup.Item value="celsius" id="celsius" />
|
|
|
|
<Label for="celsius">{$_('menu.celsius')}</Label>
|
|
|
|
</div>
|
|
|
|
<div class="flex items-center space-x-2">
|
|
|
|
<RadioGroup.Item value="fahrenheit" id="fahrenheit" />
|
|
|
|
<Label for="fahrenheit">{$_('menu.fahrenheit')}</Label>
|
|
|
|
</div>
|
|
|
|
</RadioGroup.Root>
|
|
|
|
</Label>
|
|
|
|
</div>
|
2024-07-12 15:00:33 +02:00
|
|
|
<Label>
|
|
|
|
{$_('embedding.preview')}
|
|
|
|
</Label>
|
|
|
|
<div class="relative h-[600px]">
|
|
|
|
<Embedding bind:options={iframeOptions} />
|
|
|
|
</div>
|
|
|
|
<Label>
|
|
|
|
{$_('embedding.code')}
|
|
|
|
</Label>
|
|
|
|
<pre class="bg-primary text-primary-foreground p-3 rounded-md whitespace-normal break-all">
|
|
|
|
<code class="language-html">
|
2024-07-12 15:56:35 +02:00
|
|
|
{`<iframe src="https://gpx.studio${base}/embed?options=${encodeURIComponent(JSON.stringify(getCleanedEmbeddingOptions(options)))}${hash}" width="100%" height="600px" frameborder="0" />`}
|
2024-07-12 15:00:33 +02:00
|
|
|
</code>
|
|
|
|
</pre>
|
2024-07-11 18:42:49 +02:00
|
|
|
</fieldset>
|
|
|
|
</Card.Content>
|
|
|
|
</Card.Root>
|