mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-01 08:12:32 +00:00
strava heatmap layers (nakarte proxy)
This commit is contained in:
@@ -386,6 +386,62 @@ export const overlays: { [key: string]: AnySourceData; } = {
|
||||
maxzoom: 18,
|
||||
attribution: '© <a href="https://www.waymarkedtrails.org" target="_blank">Waymarked Trails</a>'
|
||||
},
|
||||
stravaHeatmapRun: {
|
||||
type: 'raster',
|
||||
tiles: [],
|
||||
tileSize: 1024,
|
||||
maxzoom: 15,
|
||||
attribution: '© <a href="https://www.strava.com" target="_blank">Strava</a>'
|
||||
},
|
||||
stravaHeatmapTrailRun: {
|
||||
type: 'raster',
|
||||
tiles: [],
|
||||
tileSize: 1024,
|
||||
maxzoom: 15,
|
||||
attribution: '© <a href="https://www.strava.com" target="_blank">Strava</a>'
|
||||
},
|
||||
stravaHeatmapHike: {
|
||||
type: 'raster',
|
||||
tiles: [],
|
||||
tileSize: 1024,
|
||||
maxzoom: 15,
|
||||
attribution: '© <a href="https://www.strava.com" target="_blank">Strava</a>'
|
||||
},
|
||||
stravaHeatmapRide: {
|
||||
type: 'raster',
|
||||
tiles: [],
|
||||
tileSize: 1024,
|
||||
maxzoom: 15,
|
||||
attribution: '© <a href="https://www.strava.com" target="_blank">Strava</a>'
|
||||
},
|
||||
stravaHeatmapGravel: {
|
||||
type: 'raster',
|
||||
tiles: [],
|
||||
tileSize: 1024,
|
||||
maxzoom: 15,
|
||||
attribution: '© <a href="https://www.strava.com" target="_blank">Strava</a>'
|
||||
},
|
||||
stravaHeatmapMTB: {
|
||||
type: 'raster',
|
||||
tiles: [],
|
||||
tileSize: 1024,
|
||||
maxzoom: 15,
|
||||
attribution: '© <a href="https://www.strava.com" target="_blank">Strava</a>'
|
||||
},
|
||||
stravaHeatmapWater: {
|
||||
type: 'raster',
|
||||
tiles: [],
|
||||
tileSize: 1024,
|
||||
maxzoom: 15,
|
||||
attribution: '© <a href="https://www.strava.com" target="_blank">Strava</a>'
|
||||
},
|
||||
stravaHeatmapWinter: {
|
||||
type: 'raster',
|
||||
tiles: [],
|
||||
tileSize: 1024,
|
||||
maxzoom: 15,
|
||||
attribution: '© <a href="https://www.strava.com" target="_blank">Strava</a>'
|
||||
},
|
||||
};
|
||||
|
||||
export const opacities: { [key: string]: number; } = {
|
||||
@@ -452,6 +508,16 @@ export const overlayTree: LayerTreeType = {
|
||||
cyclOSM: {
|
||||
cyclOSMlite: true,
|
||||
},
|
||||
strava: {
|
||||
stravaHeatmapRun: true,
|
||||
stravaHeatmapTrailRun: true,
|
||||
stravaHeatmapHike: true,
|
||||
stravaHeatmapRide: true,
|
||||
stravaHeatmapGravel: true,
|
||||
stravaHeatmapMTB: true,
|
||||
stravaHeatmapWater: true,
|
||||
stravaHeatmapWinter: true,
|
||||
},
|
||||
waymarked_trails: {
|
||||
waymarkedTrailsHiking: true,
|
||||
waymarkedTrailsCycling: true,
|
||||
@@ -487,6 +553,16 @@ export const defaultOverlays = {
|
||||
cyclOSM: {
|
||||
cyclOSMlite: false,
|
||||
},
|
||||
strava: {
|
||||
stravaHeatmapRun: false,
|
||||
stravaHeatmapTrailRun: false,
|
||||
stravaHeatmapHike: false,
|
||||
stravaHeatmapRide: false,
|
||||
stravaHeatmapGravel: false,
|
||||
stravaHeatmapMTB: false,
|
||||
stravaHeatmapWater: false,
|
||||
stravaHeatmapWinter: false,
|
||||
},
|
||||
waymarked_trails: {
|
||||
waymarkedTrailsHiking: false,
|
||||
waymarkedTrailsCycling: false,
|
||||
@@ -568,6 +644,16 @@ export const defaultOverlayTree: LayerTreeType = {
|
||||
cyclOSM: {
|
||||
cyclOSMlite: true,
|
||||
},
|
||||
strava: {
|
||||
stravaHeatmapRun: true,
|
||||
stravaHeatmapTrailRun: true,
|
||||
stravaHeatmapHike: true,
|
||||
stravaHeatmapRide: true,
|
||||
stravaHeatmapGravel: true,
|
||||
stravaHeatmapMTB: true,
|
||||
stravaHeatmapWater: true,
|
||||
stravaHeatmapWinter: true,
|
||||
},
|
||||
waymarked_trails: {
|
||||
waymarkedTrailsHiking: true,
|
||||
waymarkedTrailsCycling: true,
|
||||
@@ -591,4 +677,16 @@ export const defaultOverlayTree: LayerTreeType = {
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const stravaHeatmapServers = ['https://proxy.nakarte.me/https/heatmap-external-a.strava.com/tiles-auth']; //['https://heatmap-external-a.strava.com/tiles-auth', 'https://heatmap-external-b.strava.com/tiles-auth', 'https://heatmap-external-c.strava.com/tiles-auth'];
|
||||
export const stravaHeatmapActivityIds: { [key: string]: string } = {
|
||||
stravaHeatmapRun: 'sport_Run',
|
||||
stravaHeatmapTrailRun: 'sport_TrailRun',
|
||||
stravaHeatmapHike: 'sport_Hike',
|
||||
stravaHeatmapRide: 'sport_Ride',
|
||||
stravaHeatmapGravel: 'sport_GravelRide',
|
||||
stravaHeatmapMTB: 'sport_MountainBikeRide',
|
||||
stravaHeatmapWater: 'water',
|
||||
stravaHeatmapWinter: 'winter',
|
||||
}
|
@@ -5,15 +5,75 @@
|
||||
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
|
||||
import * as Sheet from '$lib/components/ui/sheet';
|
||||
import * as Accordion from '$lib/components/ui/accordion';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
|
||||
import { basemapTree, overlayTree } from '$lib/assets/layers';
|
||||
import { settings } from '$lib/db';
|
||||
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { writable, get } from 'svelte/store';
|
||||
import { map, setStravaHeatmapURLs } from '$lib/stores';
|
||||
|
||||
const { selectedBasemapTree, selectedOverlayTree } = settings;
|
||||
const { selectedBasemapTree, selectedOverlayTree, stravaHeatmapColor, currentOverlays } =
|
||||
settings;
|
||||
|
||||
export let open: boolean;
|
||||
|
||||
const heatmapColors = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'blue', label: $_('layers.color.blue') },
|
||||
{ value: 'bluered', label: $_('layers.color.bluered') },
|
||||
{ value: 'gray', label: $_('layers.color.gray') },
|
||||
{ value: 'hot', label: $_('layers.color.hot') },
|
||||
{ value: 'orange', label: $_('layers.color.orange') },
|
||||
{ value: 'purple', label: $_('layers.color.purple') }
|
||||
];
|
||||
|
||||
let selectedHeatmapColor = writable(heatmapColors[0]);
|
||||
|
||||
$: if ($selectedHeatmapColor !== heatmapColors[0]) {
|
||||
stravaHeatmapColor.set($selectedHeatmapColor.value);
|
||||
|
||||
// remove and add the heatmap layers
|
||||
let m = get(map);
|
||||
if (m) {
|
||||
let currentStravaLayers = [];
|
||||
for (let layer of Object.keys(overlayTree.overlays.world.strava)) {
|
||||
if (m.getLayer(layer)) {
|
||||
m.removeLayer(layer);
|
||||
currentStravaLayers.push(layer);
|
||||
}
|
||||
if (m.getSource(layer)) {
|
||||
m.removeSource(layer);
|
||||
}
|
||||
}
|
||||
if (currentStravaLayers.length > 0) {
|
||||
currentOverlays.update(($currentOverlays) => {
|
||||
for (let layer of currentStravaLayers) {
|
||||
$currentOverlays.overlays.world.strava[layer] = false;
|
||||
}
|
||||
return $currentOverlays;
|
||||
});
|
||||
currentOverlays.update(($currentOverlays) => {
|
||||
for (let layer of currentStravaLayers) {
|
||||
$currentOverlays.overlays.world.strava[layer] = true;
|
||||
}
|
||||
return $currentOverlays;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: if ($stravaHeatmapColor) {
|
||||
setStravaHeatmapURLs();
|
||||
if ($stravaHeatmapColor !== get(selectedHeatmapColor).value) {
|
||||
let toSelect = heatmapColors.find(({ value }) => value === $stravaHeatmapColor);
|
||||
if (toSelect) {
|
||||
selectedHeatmapColor.set(toSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Sheet.Root bind:open>
|
||||
@@ -55,7 +115,21 @@
|
||||
</Accordion.Item>
|
||||
<Accordion.Item value="item-3">
|
||||
<Accordion.Trigger>{$_('layers.heatmap')}</Accordion.Trigger>
|
||||
<Accordion.Content></Accordion.Content>
|
||||
<Accordion.Content>
|
||||
<div class="flex flex-row items-center justify-between gap-4">
|
||||
<Label>{$_('menu.color')}</Label>
|
||||
<Select.Root bind:selected={$selectedHeatmapColor} class="grow">
|
||||
<Select.Trigger class="w-full">
|
||||
<Select.Value placeholder="Theme" />
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each heatmapColors as { value, label }}
|
||||
<Select.Item {value}>{label}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
<Accordion.Item value="item-4">
|
||||
<Accordion.Trigger>{$_('layers.pois')}</Accordion.Trigger>
|
||||
|
@@ -16,7 +16,7 @@ export function anySelectedLayer(node: LayerTreeType) {
|
||||
}
|
||||
|
||||
export function getLayers(node: LayerTreeType, layers: { [key: string]: boolean } = {}): { [key: string]: boolean } {
|
||||
Object.keys(node).find((id) => {
|
||||
Object.keys(node).forEach((id) => {
|
||||
if (typeof node[id] == "boolean") {
|
||||
layers[id] = node[id];
|
||||
} else {
|
||||
|
@@ -97,6 +97,7 @@ export const settings = {
|
||||
selectedOverlayTree: dexieSettingStore('selectedOverlayTree', defaultOverlayTree),
|
||||
directionMarkers: dexieSettingStore('directionMarkers', false),
|
||||
distanceMarkers: dexieSettingStore('distanceMarkers', false),
|
||||
stravaHeatmapColor: dexieSettingStore('stravaHeatmapColor', 'bluered'),
|
||||
fileOrder: dexieSettingStore<string[]>('fileOrder', []),
|
||||
};
|
||||
|
||||
|
@@ -5,10 +5,11 @@ import { GPXFile, buildGPX, parseGPX, GPXStatistics, type Coordinates } from 'gp
|
||||
import { tick } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import type { GPXLayer } from '$lib/components/gpx-layer/GPXLayer';
|
||||
import { dbUtils, fileObservers } from './db';
|
||||
import { dbUtils, fileObservers, settings } from './db';
|
||||
import { applyToOrderedSelectedItemsFromFile, selectFile, selection } from '$lib/components/file-list/Selection';
|
||||
import { ListFileItem, ListWaypointItem, ListWaypointsItem } from '$lib/components/file-list/FileList';
|
||||
import type { RoutingControls } from '$lib/components/toolbar/tools/routing/RoutingControls';
|
||||
import { overlayTree, overlays, stravaHeatmapActivityIds, stravaHeatmapServers } from '$lib/assets/layers';
|
||||
|
||||
export const map = writable<mapboxgl.Map | null>(null);
|
||||
export const selectFiles = writable<{ [key: string]: (fileId?: string) => void }>({});
|
||||
@@ -237,4 +238,44 @@ export function exportFile(file: GPXFile) {
|
||||
a.download = file.metadata.name + '.gpx';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
let stravaCookies: any = null;
|
||||
function refreshStravaCookies() {
|
||||
if (stravaCookies === null) {
|
||||
return fetch('https://s.gpx.studio')
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw new Error('Failed to fetch Strava cookies');
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
stravaCookies = data;
|
||||
console.log('Strava cookies:', stravaCookies);
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export function setStravaHeatmapURLs() {
|
||||
refreshStravaCookies().then(() => {
|
||||
overlays.stravaHeatmapRun.tiles = [];
|
||||
overlays.stravaHeatmapTrailRun.tiles = [];
|
||||
overlays.stravaHeatmapHike.tiles = [];
|
||||
overlays.stravaHeatmapRide.tiles = [];
|
||||
overlays.stravaHeatmapGravel.tiles = [];
|
||||
overlays.stravaHeatmapMTB.tiles = [];
|
||||
overlays.stravaHeatmapWater.tiles = [];
|
||||
overlays.stravaHeatmapWinter.tiles = [];
|
||||
|
||||
for (let activity of Object.keys(overlayTree.overlays.world.strava)) {
|
||||
overlays[activity].tiles = [];
|
||||
for (let server of stravaHeatmapServers) {
|
||||
overlays[activity].tiles.push(`${server}/${stravaHeatmapActivityIds[activity]}/${get(settings.stravaHeatmapColor)}/{z}/{x}/{y}@2x.png?Signature=${stravaCookies['CloudFront-Signature']}&Key-Pair-Id=${stravaCookies['CloudFront-Key-Pair-Id']}&Policy=${stravaCookies['CloudFront-Policy']}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@@ -38,7 +38,8 @@
|
||||
"donate": "Donate",
|
||||
"ctrl": "Ctrl",
|
||||
"click": "Click",
|
||||
"drag": "Drag"
|
||||
"drag": "Drag",
|
||||
"color": "Color"
|
||||
},
|
||||
"toolbar": {
|
||||
"routing": {
|
||||
@@ -116,7 +117,7 @@
|
||||
"settings_help": "Select the map layers you want to show in the interface, add custom ones, and adjust their settings.",
|
||||
"selection": "Layer selection",
|
||||
"custom_layers": "Custom layers",
|
||||
"heatmap": "Heatmap",
|
||||
"heatmap": "Strava Heatmap",
|
||||
"pois": "Points of interest",
|
||||
"label": {
|
||||
"basemaps": "Basemaps",
|
||||
@@ -160,6 +161,15 @@
|
||||
"ignFrCadastre": "IGN Cadastre",
|
||||
"ignSlope": "IGN Slope",
|
||||
"ignSkiTouring": "IGN Ski Touring",
|
||||
"strava": "Strava",
|
||||
"stravaHeatmapRun": "Running",
|
||||
"stravaHeatmapTrailRun": "Trail Running",
|
||||
"stravaHeatmapHike": "Hiking",
|
||||
"stravaHeatmapRide": "Cycling",
|
||||
"stravaHeatmapGravel": "Gravel Cycling",
|
||||
"stravaHeatmapMTB": "MTB",
|
||||
"stravaHeatmapWater": "Water",
|
||||
"stravaHeatmapWinter": "Winter",
|
||||
"waymarked_trails": "Waymarked Trails",
|
||||
"waymarkedTrailsHiking": "Hiking",
|
||||
"waymarkedTrailsCycling": "Cycling",
|
||||
@@ -167,6 +177,14 @@
|
||||
"waymarkedTrailsSkating": "Skating",
|
||||
"waymarkedTrailsHorseRiding": "Horse Riding",
|
||||
"waymarkedTrailsWinter": "Winter"
|
||||
},
|
||||
"color": {
|
||||
"blue": "Blue",
|
||||
"bluered": "Blue Red",
|
||||
"gray": "Gray",
|
||||
"hot": "Hot",
|
||||
"purple": "Purple",
|
||||
"orange": "Orange"
|
||||
}
|
||||
},
|
||||
"chart": {
|
||||
|
Reference in New Issue
Block a user