Map: sidebar pills for 4 tile layers (CyclOSM/OSM/Topo/Sat); fix elevation ascend property
This commit is contained in:
+36
-28
@@ -30,22 +30,37 @@
|
|||||||
'https://c.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png',
|
'https://c.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png',
|
||||||
],
|
],
|
||||||
attribution: '© <a href="https://www.cyclosm.org">CyclOSM</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
attribution: '© <a href="https://www.cyclosm.org">CyclOSM</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||||
label: 'CyclOSM',
|
label: 'Cycle',
|
||||||
},
|
},
|
||||||
osm: {
|
osm: {
|
||||||
tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
|
tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||||
label: 'OSM',
|
label: 'OSM',
|
||||||
},
|
},
|
||||||
|
topo: {
|
||||||
|
tiles: [
|
||||||
|
'https://a.tile.opentopomap.org/{z}/{x}/{y}.png',
|
||||||
|
'https://b.tile.opentopomap.org/{z}/{x}/{y}.png',
|
||||||
|
'https://c.tile.opentopomap.org/{z}/{x}/{y}.png',
|
||||||
|
],
|
||||||
|
attribution: '© <a href="https://opentopomap.org">OpenTopoMap</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||||
|
label: 'Topo',
|
||||||
|
},
|
||||||
|
satellite: {
|
||||||
|
tiles: ['https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'],
|
||||||
|
attribution: '© <a href="https://www.esri.com">Esri</a> World Imagery',
|
||||||
|
label: 'Sat',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
const TILE_ORDER = ['cyclosm', 'osm', 'topo', 'satellite'];
|
||||||
|
|
||||||
let tileLayer = $state('cyclosm');
|
let tileLayer = $state('cyclosm');
|
||||||
|
|
||||||
function toggleTiles() {
|
function setTileLayer(key) {
|
||||||
tileLayer = tileLayer === 'cyclosm' ? 'osm' : 'cyclosm';
|
tileLayer = key;
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
const src = map.getSource('base');
|
const src = map.getSource('base');
|
||||||
if (src) src.setTiles(TILES[tileLayer].tiles);
|
if (src) src.setTiles(TILES[key].tiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Map init ───────────────────────────────────────────────────────────────
|
// ── Map init ───────────────────────────────────────────────────────────────
|
||||||
@@ -199,7 +214,7 @@ ${trkpts}
|
|||||||
if (!route) return null;
|
if (!route) return null;
|
||||||
const p = route.properties ?? {};
|
const p = route.properties ?? {};
|
||||||
const dist = parseFloat(p['track-length'] ?? 0) / 1000;
|
const dist = parseFloat(p['track-length'] ?? 0) / 1000;
|
||||||
const up = parseInt(p['filtered ascent'] ?? p.ascent ?? 0);
|
const up = parseInt(p['filtered ascend'] ?? p.ascend ?? 0);
|
||||||
return { dist: dist.toFixed(1), up };
|
return { dist: dist.toFixed(1), up };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +229,20 @@ ${trkpts}
|
|||||||
<a href={activityUrl} class="back-link">← Activity</a>
|
<a href={activityUrl} class="back-link">← Activity</a>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<!-- Map layer selector -->
|
||||||
|
<section class="section">
|
||||||
|
<p class="label">Map</p>
|
||||||
|
<div class="pills">
|
||||||
|
{#each TILE_ORDER as key}
|
||||||
|
<button
|
||||||
|
class="pill"
|
||||||
|
class:active={tileLayer === key}
|
||||||
|
onclick={() => setTileLayer(key)}
|
||||||
|
>{TILES[key].label}</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Profile selector -->
|
<!-- Profile selector -->
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<p class="label">Profile</p>
|
<p class="label">Profile</p>
|
||||||
@@ -269,11 +298,7 @@ ${trkpts}
|
|||||||
|
|
||||||
<!-- Map + elevation -->
|
<!-- Map + elevation -->
|
||||||
<main class="map-area">
|
<main class="map-area">
|
||||||
<div class="map-wrap" bind:this={mapEl}>
|
<div class="map-wrap" bind:this={mapEl}></div>
|
||||||
<button class="tile-toggle" onclick={toggleTiles} title="Switch map layer">
|
|
||||||
{TILES[tileLayer].label}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{#if route}
|
{#if route}
|
||||||
<div class="elevation-wrap">
|
<div class="elevation-wrap">
|
||||||
<ElevationChart {route} />
|
<ElevationChart {route} />
|
||||||
@@ -391,24 +416,7 @@ ${trkpts}
|
|||||||
.small { font-size: 0.75rem; margin: 0; }
|
.small { font-size: 0.75rem; margin: 0; }
|
||||||
|
|
||||||
.map-area { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
|
.map-area { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
|
||||||
.map-wrap { flex: 1; position: relative; }
|
.map-wrap { flex: 1; }
|
||||||
|
|
||||||
.tile-toggle {
|
|
||||||
position: absolute;
|
|
||||||
top: 0.5rem;
|
|
||||||
left: 0.5rem;
|
|
||||||
z-index: 10;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
background: var(--bg-card);
|
|
||||||
color: var(--text-4);
|
|
||||||
font-size: 0.7rem;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
.tile-toggle:hover { border-color: var(--accent); color: var(--accent); }
|
|
||||||
.elevation-wrap { height: 140px; flex-shrink: 0; background: var(--bg-card); border-top: 1px solid var(--border); }
|
.elevation-wrap { height: 140px; flex-shrink: 0; background: var(--bg-card); border-top: 1px solid var(--border); }
|
||||||
|
|
||||||
:global(.wp-marker) {
|
:global(.wp-marker) {
|
||||||
|
|||||||
Reference in New Issue
Block a user