mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-02 08:42:31 +00:00
strava heatmap layers (nakarte proxy)
This commit is contained in:
@@ -386,6 +386,62 @@ export const overlays: { [key: string]: AnySourceData; } = {
|
|||||||
maxzoom: 18,
|
maxzoom: 18,
|
||||||
attribution: '© <a href="https://www.waymarkedtrails.org" target="_blank">Waymarked Trails</a>'
|
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; } = {
|
export const opacities: { [key: string]: number; } = {
|
||||||
@@ -452,6 +508,16 @@ export const overlayTree: LayerTreeType = {
|
|||||||
cyclOSM: {
|
cyclOSM: {
|
||||||
cyclOSMlite: true,
|
cyclOSMlite: true,
|
||||||
},
|
},
|
||||||
|
strava: {
|
||||||
|
stravaHeatmapRun: true,
|
||||||
|
stravaHeatmapTrailRun: true,
|
||||||
|
stravaHeatmapHike: true,
|
||||||
|
stravaHeatmapRide: true,
|
||||||
|
stravaHeatmapGravel: true,
|
||||||
|
stravaHeatmapMTB: true,
|
||||||
|
stravaHeatmapWater: true,
|
||||||
|
stravaHeatmapWinter: true,
|
||||||
|
},
|
||||||
waymarked_trails: {
|
waymarked_trails: {
|
||||||
waymarkedTrailsHiking: true,
|
waymarkedTrailsHiking: true,
|
||||||
waymarkedTrailsCycling: true,
|
waymarkedTrailsCycling: true,
|
||||||
@@ -487,6 +553,16 @@ export const defaultOverlays = {
|
|||||||
cyclOSM: {
|
cyclOSM: {
|
||||||
cyclOSMlite: false,
|
cyclOSMlite: false,
|
||||||
},
|
},
|
||||||
|
strava: {
|
||||||
|
stravaHeatmapRun: false,
|
||||||
|
stravaHeatmapTrailRun: false,
|
||||||
|
stravaHeatmapHike: false,
|
||||||
|
stravaHeatmapRide: false,
|
||||||
|
stravaHeatmapGravel: false,
|
||||||
|
stravaHeatmapMTB: false,
|
||||||
|
stravaHeatmapWater: false,
|
||||||
|
stravaHeatmapWinter: false,
|
||||||
|
},
|
||||||
waymarked_trails: {
|
waymarked_trails: {
|
||||||
waymarkedTrailsHiking: false,
|
waymarkedTrailsHiking: false,
|
||||||
waymarkedTrailsCycling: false,
|
waymarkedTrailsCycling: false,
|
||||||
@@ -568,6 +644,16 @@ export const defaultOverlayTree: LayerTreeType = {
|
|||||||
cyclOSM: {
|
cyclOSM: {
|
||||||
cyclOSMlite: true,
|
cyclOSMlite: true,
|
||||||
},
|
},
|
||||||
|
strava: {
|
||||||
|
stravaHeatmapRun: true,
|
||||||
|
stravaHeatmapTrailRun: true,
|
||||||
|
stravaHeatmapHike: true,
|
||||||
|
stravaHeatmapRide: true,
|
||||||
|
stravaHeatmapGravel: true,
|
||||||
|
stravaHeatmapMTB: true,
|
||||||
|
stravaHeatmapWater: true,
|
||||||
|
stravaHeatmapWinter: true,
|
||||||
|
},
|
||||||
waymarked_trails: {
|
waymarked_trails: {
|
||||||
waymarkedTrailsHiking: true,
|
waymarkedTrailsHiking: true,
|
||||||
waymarkedTrailsCycling: 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 { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
|
||||||
import * as Sheet from '$lib/components/ui/sheet';
|
import * as Sheet from '$lib/components/ui/sheet';
|
||||||
import * as Accordion from '$lib/components/ui/accordion';
|
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 { basemapTree, overlayTree } from '$lib/assets/layers';
|
||||||
import { settings } from '$lib/db';
|
import { settings } from '$lib/db';
|
||||||
|
|
||||||
import { _ } from 'svelte-i18n';
|
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;
|
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>
|
</script>
|
||||||
|
|
||||||
<Sheet.Root bind:open>
|
<Sheet.Root bind:open>
|
||||||
@@ -55,7 +115,21 @@
|
|||||||
</Accordion.Item>
|
</Accordion.Item>
|
||||||
<Accordion.Item value="item-3">
|
<Accordion.Item value="item-3">
|
||||||
<Accordion.Trigger>{$_('layers.heatmap')}</Accordion.Trigger>
|
<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>
|
||||||
<Accordion.Item value="item-4">
|
<Accordion.Item value="item-4">
|
||||||
<Accordion.Trigger>{$_('layers.pois')}</Accordion.Trigger>
|
<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 } {
|
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") {
|
if (typeof node[id] == "boolean") {
|
||||||
layers[id] = node[id];
|
layers[id] = node[id];
|
||||||
} else {
|
} else {
|
||||||
|
@@ -97,6 +97,7 @@ export const settings = {
|
|||||||
selectedOverlayTree: dexieSettingStore('selectedOverlayTree', defaultOverlayTree),
|
selectedOverlayTree: dexieSettingStore('selectedOverlayTree', defaultOverlayTree),
|
||||||
directionMarkers: dexieSettingStore('directionMarkers', false),
|
directionMarkers: dexieSettingStore('directionMarkers', false),
|
||||||
distanceMarkers: dexieSettingStore('distanceMarkers', false),
|
distanceMarkers: dexieSettingStore('distanceMarkers', false),
|
||||||
|
stravaHeatmapColor: dexieSettingStore('stravaHeatmapColor', 'bluered'),
|
||||||
fileOrder: dexieSettingStore<string[]>('fileOrder', []),
|
fileOrder: dexieSettingStore<string[]>('fileOrder', []),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -5,10 +5,11 @@ import { GPXFile, buildGPX, parseGPX, GPXStatistics, type Coordinates } from 'gp
|
|||||||
import { tick } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import type { GPXLayer } from '$lib/components/gpx-layer/GPXLayer';
|
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 { applyToOrderedSelectedItemsFromFile, selectFile, selection } from '$lib/components/file-list/Selection';
|
||||||
import { ListFileItem, ListWaypointItem, ListWaypointsItem } from '$lib/components/file-list/FileList';
|
import { ListFileItem, ListWaypointItem, ListWaypointsItem } from '$lib/components/file-list/FileList';
|
||||||
import type { RoutingControls } from '$lib/components/toolbar/tools/routing/RoutingControls';
|
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 map = writable<mapboxgl.Map | null>(null);
|
||||||
export const selectFiles = writable<{ [key: string]: (fileId?: string) => void }>({});
|
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.download = file.metadata.name + '.gpx';
|
||||||
a.click();
|
a.click();
|
||||||
URL.revokeObjectURL(url);
|
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",
|
"donate": "Donate",
|
||||||
"ctrl": "Ctrl",
|
"ctrl": "Ctrl",
|
||||||
"click": "Click",
|
"click": "Click",
|
||||||
"drag": "Drag"
|
"drag": "Drag",
|
||||||
|
"color": "Color"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"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.",
|
"settings_help": "Select the map layers you want to show in the interface, add custom ones, and adjust their settings.",
|
||||||
"selection": "Layer selection",
|
"selection": "Layer selection",
|
||||||
"custom_layers": "Custom layers",
|
"custom_layers": "Custom layers",
|
||||||
"heatmap": "Heatmap",
|
"heatmap": "Strava Heatmap",
|
||||||
"pois": "Points of interest",
|
"pois": "Points of interest",
|
||||||
"label": {
|
"label": {
|
||||||
"basemaps": "Basemaps",
|
"basemaps": "Basemaps",
|
||||||
@@ -160,6 +161,15 @@
|
|||||||
"ignFrCadastre": "IGN Cadastre",
|
"ignFrCadastre": "IGN Cadastre",
|
||||||
"ignSlope": "IGN Slope",
|
"ignSlope": "IGN Slope",
|
||||||
"ignSkiTouring": "IGN Ski Touring",
|
"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",
|
"waymarked_trails": "Waymarked Trails",
|
||||||
"waymarkedTrailsHiking": "Hiking",
|
"waymarkedTrailsHiking": "Hiking",
|
||||||
"waymarkedTrailsCycling": "Cycling",
|
"waymarkedTrailsCycling": "Cycling",
|
||||||
@@ -167,6 +177,14 @@
|
|||||||
"waymarkedTrailsSkating": "Skating",
|
"waymarkedTrailsSkating": "Skating",
|
||||||
"waymarkedTrailsHorseRiding": "Horse Riding",
|
"waymarkedTrailsHorseRiding": "Horse Riding",
|
||||||
"waymarkedTrailsWinter": "Winter"
|
"waymarkedTrailsWinter": "Winter"
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"blue": "Blue",
|
||||||
|
"bluered": "Blue Red",
|
||||||
|
"gray": "Gray",
|
||||||
|
"hot": "Hot",
|
||||||
|
"purple": "Purple",
|
||||||
|
"orange": "Orange"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chart": {
|
"chart": {
|
||||||
|
Reference in New Issue
Block a user