diff --git a/db/migrations/1733093484_updated_settings.go b/db/migrations/1733093484_updated_settings.go
new file mode 100644
index 00000000..1b7ebdc1
--- /dev/null
+++ b/db/migrations/1733093484_updated_settings.go
@@ -0,0 +1,54 @@
+package migrations
+
+import (
+ "encoding/json"
+
+ "github.com/pocketbase/dbx"
+ "github.com/pocketbase/pocketbase/daos"
+ m "github.com/pocketbase/pocketbase/migrations"
+ "github.com/pocketbase/pocketbase/models/schema"
+)
+
+func init() {
+ m.Register(func(db dbx.Builder) error {
+ dao := daos.New(db);
+
+ collection, err := dao.FindCollectionByNameOrId("uavt73rsqcn1n13")
+ if err != nil {
+ return err
+ }
+
+ // add
+ new_terrain := &schema.SchemaField{}
+ if err := json.Unmarshal([]byte(`{
+ "system": false,
+ "id": "mvxf9llv",
+ "name": "terrain",
+ "type": "url",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "exceptDomains": [],
+ "onlyDomains": []
+ }
+ }`), new_terrain); err != nil {
+ return err
+ }
+ collection.Schema.AddField(new_terrain)
+
+ return dao.SaveCollection(collection)
+ }, func(db dbx.Builder) error {
+ dao := daos.New(db);
+
+ collection, err := dao.FindCollectionByNameOrId("uavt73rsqcn1n13")
+ if err != nil {
+ return err
+ }
+
+ // remove
+ collection.Schema.RemoveField("mvxf9llv")
+
+ return dao.SaveCollection(collection)
+ })
+}
diff --git a/web/src/lib/components/trail/map_with_elevation_maplibre.svelte b/web/src/lib/components/trail/map_with_elevation_maplibre.svelte
index 3fdbf5e5..8db08aaf 100644
--- a/web/src/lib/components/trail/map_with_elevation_maplibre.svelte
+++ b/web/src/lib/components/trail/map_with_elevation_maplibre.svelte
@@ -17,6 +17,7 @@
import { createEventDispatcher, onDestroy, onMount } from "svelte";
import MaplibreGraticule from "$lib/vendor/maplibre-graticule/maplibre-graticule";
import { FullscreenControl } from "$lib/vendor/maplibre-fullscreen/fullscreen-control";
+ import type { Settings } from "$lib/models/settings";
export let trails: Trail[] = [];
export let markers: M.Marker[] = [];
@@ -27,6 +28,7 @@
export let showGrid: boolean = false;
export let showStyleSwitcher: boolean = true;
export let showFullscreen: boolean = false;
+ export let showTerrain: boolean = false;
export let fitBounds: "animate" | "instant" | "off" = "instant";
export let elevationProfileContainer: string | HTMLDivElement | undefined =
@@ -194,6 +196,11 @@
const bounds = data[activeTrail]
? (data[activeTrail].bbox as M.LngLatBoundsLike)
: getBounds();
+
+ if (!bounds) {
+ return;
+ }
+
map!.fitBounds(bounds, {
animate: animate,
padding: {
@@ -336,8 +343,6 @@
dispatch("select", trail);
const index = trails.findIndex((t) => t.id == trail.id);
if (index == -1) {
- console.log("here");
-
return;
}
activeTrail = index;
@@ -425,30 +430,37 @@
)
).ElevationProfileControl;
- const mapStyles = [
- {
- text: "Open Street Maps",
- value: "/styles/osm.json",
- thumbnail: "https://tile.openstreetmap.org/1/0/0.png",
- },
- {
- text: "Open Topo Maps",
- value: "/styles/otm.json",
- thumbnail: "https://tile.opentopomap.org/1/0/0.png",
- },
- {
- text: "Carto Light",
- value: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
- thumbnail:
- "https://basemaps.cartocdn.com/light_all/1/0/0@2x.png",
- },
- {
- text: "Carto Dark",
- value: "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
- thumbnail:
- "https://basemaps.cartocdn.com/dark_all/1/0/0@2x.png",
- },
- ];
+ const mapStyles: { text: string; value: string; thumbnail?: string }[] =
+ [
+ ...(($page.data.settings as Settings).tilesets ?? []).map(
+ (t) => ({
+ text: t.name,
+ value: t.url,
+ }),
+ ),
+ {
+ text: "Open Street Maps",
+ value: "/styles/osm.json",
+ thumbnail: "https://tile.openstreetmap.org/1/0/0.png",
+ },
+ {
+ text: "Open Topo Maps",
+ value: "/styles/otm.json",
+ thumbnail: "https://tile.opentopomap.org/1/0/0.png",
+ },
+ {
+ text: "Carto Light",
+ value: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
+ thumbnail:
+ "https://basemaps.cartocdn.com/light_all/1/0/0@2x.png",
+ },
+ {
+ text: "Carto Dark",
+ value: "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
+ thumbnail:
+ "https://basemaps.cartocdn.com/dark_all/1/0/0@2x.png",
+ },
+ ];
const preferredMapStyleIndex = mapStyles.findIndex(
(s) => s.text === localStorage.getItem("layer"),
);
@@ -490,7 +502,9 @@
selectedIndex:
preferredMapStyleIndex !== -1 ? preferredMapStyleIndex : 0,
});
- map.addControl(new M.NavigationControl());
+ map.addControl(
+ new M.NavigationControl({ visualizePitch: showTerrain }),
+ );
map.addControl(
new M.ScaleControl({
maxWidth: 120,
@@ -536,10 +550,25 @@
);
}
+ if (showTerrain) {
+ map!.addControl(
+ new M.TerrainControl({
+ source: "terrain",
+ }),
+ );
+ }
+
map.on("styledata", () => {
trails.forEach((t, i) => {
addTrailLayer(t, t.id ?? i.toString(), data?.at(i));
});
+
+ if (showTerrain && $page.data.settings?.terrain) {
+ map!.addSource("terrain", {
+ type: "raster-dem",
+ url: $page.data.settings.terrain,
+ });
+ }
});
map.on("moveend", (e) => {
diff --git a/web/src/lib/models/settings.ts b/web/src/lib/models/settings.ts
index 6131a92e..95ff3236 100644
--- a/web/src/lib/models/settings.ts
+++ b/web/src/lib/models/settings.ts
@@ -7,6 +7,7 @@ class Settings {
location?: { name: string, lat: number, lon: number };
category?: string;
tilesets?: {name: string, url: string}[]
+ terrain?: string;
user?: string;
constructor(
@@ -18,6 +19,7 @@ class Settings {
location?: { name: string, lat: number, lon: number }
category?: string
tilesets?: {name: string, url: string}[]
+ terrain?: string;
}
) {
this.unit = unit;
@@ -27,6 +29,7 @@ class Settings {
this.location = params?.location;
this.category = params?.category;
this.tilesets = params?.tilesets ?? [];
+ this.terrain = params?.terrain;
}
}
diff --git a/web/src/lib/util/maplibre_util.ts b/web/src/lib/util/maplibre_util.ts
index ae0f3503..01fa5c60 100644
--- a/web/src/lib/util/maplibre_util.ts
+++ b/web/src/lib/util/maplibre_util.ts
@@ -85,7 +85,7 @@ export function createPopupFromTrail(trail: Trail) {
: "/imgs/default_thumbnail.webp";
const popup = new M.Popup({maxWidth: "320px"});
popup.setHTML(
- `
+ `
diff --git a/web/src/lib/vendor/maplibre-elevation-profile/elevationprofile.ts b/web/src/lib/vendor/maplibre-elevation-profile/elevationprofile.ts
index 5d3ac4ab..b8df1f6f 100644
--- a/web/src/lib/vendor/maplibre-elevation-profile/elevationprofile.ts
+++ b/web/src/lib/vendor/maplibre-elevation-profile/elevationprofile.ts
@@ -415,7 +415,6 @@ export class ElevationProfile {
Chart.register(...registerables);
Chart.register(zoomPlugin);
- Chart.register(CrosshairPlugin);
this.settings = {
...elevationProfileDefaultOptions,
@@ -676,6 +675,7 @@ export class ElevationProfile {
},
plugins: [
+ CrosshairPlugin,
{
id: "waypointPlugin",
afterDraw: (chart, args, options) => {
diff --git a/web/src/lib/vendor/maplibre-style-switcher/style-switcher-control.ts b/web/src/lib/vendor/maplibre-style-switcher/style-switcher-control.ts
index 24779657..75206818 100644
--- a/web/src/lib/vendor/maplibre-style-switcher/style-switcher-control.ts
+++ b/web/src/lib/vendor/maplibre-style-switcher/style-switcher-control.ts
@@ -7,7 +7,7 @@ import { type ControlPosition, type IControl } from "maplibre-gl";
type MapStyle = {
text: string;
value: string;
- thumbnail: string;
+ thumbnail?: string;
}
export type StyleSwitcherControlOptions = {
@@ -73,20 +73,30 @@ export class StyleSwitcherControl implements IControl {
this.switcherContainer.appendChild(headingDiv)
const buttonDiv = document.createElement("ul")
- buttonDiv.classList.add("mt-2");
+ buttonDiv.classList.add("mt-2", "max-h-64", "overflow-y-scroll");
this.settings.styles.forEach((style, i) => {
const styleLi = document.createElement("li");
styleLi.classList.add("flex", "items-center", "gap-x-4", "px-3", "py-2", "cursor-pointer", "hover:bg-menu-item-background-hover")
- const styleImg = document.createElement("img");
- styleImg.classList.add("w-12", "h-12", "rounded-md")
- styleImg.src = style.thumbnail;
+ if (style.thumbnail) {
+ const styleImg = document.createElement("img");
+ styleImg.classList.add("w-12", "h-12", "rounded-md")
+ styleImg.src = style.thumbnail;
+ styleLi.appendChild(styleImg)
+ } else {
+ const styleIconContainer = document.createElement("i");
+ styleIconContainer.classList.add("w-12", "h-12", "rounded-md", "bg-blue-200", "flex", "items-center", "justify-center")
+ const styleIcon = document.createElement("i");
+ styleIcon.classList.add("fa", "fa-map-location-dot", "text-xl")
+ styleIconContainer.appendChild(styleIcon)
+ styleLi.appendChild(styleIconContainer)
+ }
+
const styleName = document.createElement("span");
styleName.classList.add("!text-sm")
if (i == this.settings.selectedIndex) {
styleName.classList.add("font-semibold")
}
styleName.textContent = style.text;
- styleLi.appendChild(styleImg)
styleLi.appendChild(styleName)
styleLi.addEventListener("click", () => {
diff --git a/web/src/routes/+layout.server.ts b/web/src/routes/+layout.server.ts
index e9923a94..406ecb10 100644
--- a/web/src/routes/+layout.server.ts
+++ b/web/src/routes/+layout.server.ts
@@ -2,6 +2,7 @@
import '$lib/i18n';
import type { LayoutServerLoad } from './$types';
import { env } from "$env/dynamic/private";
+import type { Settings } from '$lib/models/settings';
export const load: LayoutServerLoad = async ({ locals, url }) => {
return { settings: locals.settings, origin: env.ORIGIN }
diff --git a/web/src/routes/lists/+page.svelte b/web/src/routes/lists/+page.svelte
index 662a6afc..74c53d88 100644
--- a/web/src/routes/lists/+page.svelte
+++ b/web/src/routes/lists/+page.svelte
@@ -27,7 +27,7 @@
let map: M.Map;
let mapWithElevation: MapWithElevationMaplibre;
- let markers: any[];
+ let markers: M.Marker[];
let showMap: boolean = true;
let selectedList: List | null = null;
diff --git a/web/src/routes/map/trail/[id]/+page.svelte b/web/src/routes/map/trail/[id]/+page.svelte
index 34ccab11..e21ec5c4 100644
--- a/web/src/routes/map/trail/[id]/+page.svelte
+++ b/web/src/routes/map/trail/[id]/+page.svelte
@@ -15,7 +15,7 @@
-
+
diff --git a/web/src/routes/settings/display/+page.svelte b/web/src/routes/settings/display/+page.svelte
index 2735ed27..e2abcf17 100644
--- a/web/src/routes/settings/display/+page.svelte
+++ b/web/src/routes/settings/display/+page.svelte
@@ -49,11 +49,14 @@
let customTilesetName: string = "";
let customTilesetURL: string = "";
+ let terrainURL: string = "";
onMount(() => {
citySearchQuery = settings?.location?.name ?? "";
selectedLanguage = settings?.language || "en";
selectedMapFocus = settings?.mapFocus ?? "trails";
+
+ terrainURL = settings?.terrain ?? "";
});
async function searchCities(q: string) {
@@ -127,6 +130,13 @@
tilesets: settings.tilesets,
});
}
+
+ async function handleTerrainAdd() {
+ await settings_update({
+ id: settings!.id,
+ terrain: terrainURL,
+ });
+ }
@@ -189,14 +199,14 @@
+ {$_("Terrain")}
+
{/if}