diff --git a/apps/planner/app/components/PlannerMap.tsx b/apps/planner/app/components/PlannerMap.tsx
index 26d38e4..5632fa6 100644
--- a/apps/planner/app/components/PlannerMap.tsx
+++ b/apps/planner/app/components/PlannerMap.tsx
@@ -5,7 +5,7 @@ import * as Y from "yjs";
import { useTranslation } from "react-i18next";
import type { DayStage } from "@trails-cool/gpx";
import type { YjsState } from "~/lib/use-yjs";
-import { baseLayers } from "@trails-cool/map";
+import { baseLayers, overlayLayers } from "@trails-cool/map";
import { parseGpxAsync, extractWaypoints } from "@trails-cool/gpx";
import { isOvernight } from "~/lib/overnight";
import { setOvernight } from "~/lib/overnight";
@@ -455,6 +455,11 @@ export function PlannerMap({ yjs, onRouteRequest, highlightPosition, highlighted
))}
+ {overlayLayers.map((layer) => (
+
+
+
+ ))}
diff --git a/openspec/changes/osm-overlays/tasks.md b/openspec/changes/osm-overlays/tasks.md
index 28a5d99..16bcb6e 100644
--- a/openspec/changes/osm-overlays/tasks.md
+++ b/openspec/changes/osm-overlays/tasks.md
@@ -1,8 +1,8 @@
## 1. Tile Overlay Definitions
-- [ ] 1.1 Add `overlayLayers` export to `packages/map/src/layers.ts` with hillshading, Waymarked Cycling, Waymarked Hiking, Waymarked MTB tile configs
-- [ ] 1.2 Add `LayersControl.Overlay` entries in `MapView.tsx` and `PlannerMap.tsx` for each overlay
-- [ ] 1.3 Verify overlay attribution updates correctly when toggling overlays on/off
+- [x] 1.1 Add `overlayLayers` export to `packages/map/src/layers.ts` with hillshading, Waymarked Cycling, Waymarked Hiking, Waymarked MTB tile configs
+- [x] 1.2 Add `LayersControl.Overlay` entries in `MapView.tsx` and `PlannerMap.tsx` for each overlay
+- [x] 1.3 Verify overlay attribution updates correctly when toggling overlays on/off
## 2. Overlay State Sync
diff --git a/packages/map/src/index.ts b/packages/map/src/index.ts
index 4e91e78..8c3d126 100644
--- a/packages/map/src/index.ts
+++ b/packages/map/src/index.ts
@@ -2,5 +2,5 @@ export { MapView } from "./MapView.tsx";
export type { MapViewProps } from "./MapView.tsx";
export { RouteLayer } from "./RouteLayer.tsx";
export type { RouteLayerProps } from "./RouteLayer.tsx";
-export { baseLayers } from "./layers.ts";
-export type { TileLayerConfig } from "./layers.ts";
+export { baseLayers, overlayLayers } from "./layers.ts";
+export type { TileLayerConfig, OverlayLayerConfig } from "./layers.ts";
diff --git a/packages/map/src/layers.ts b/packages/map/src/layers.ts
index afa6e36..dfa04c3 100644
--- a/packages/map/src/layers.ts
+++ b/packages/map/src/layers.ts
@@ -5,6 +5,43 @@ export interface TileLayerConfig {
maxZoom?: number;
}
+export interface OverlayLayerConfig extends TileLayerConfig {
+ id: string;
+ opacity?: number;
+}
+
+export const overlayLayers: OverlayLayerConfig[] = [
+ {
+ id: "hillshading",
+ name: "Hillshading",
+ url: "https://tiles.wmflabs.org/hillshading/{z}/{x}/{y}.png",
+ attribution: "Hillshading: SRTM/Mapzen",
+ maxZoom: 17,
+ opacity: 0.5,
+ },
+ {
+ id: "waymarked-cycling",
+ name: "Cycling Routes",
+ url: "https://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png",
+ attribution: '© Waymarked Trails (CC-BY-SA)',
+ maxZoom: 18,
+ },
+ {
+ id: "waymarked-hiking",
+ name: "Hiking Routes",
+ url: "https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png",
+ attribution: '© Waymarked Trails (CC-BY-SA)',
+ maxZoom: 18,
+ },
+ {
+ id: "waymarked-mtb",
+ name: "MTB Routes",
+ url: "https://tile.waymarkedtrails.org/mtb/{z}/{x}/{y}.png",
+ attribution: '© Waymarked Trails (CC-BY-SA)',
+ maxZoom: 18,
+ },
+];
+
export const baseLayers: TileLayerConfig[] = [
{
name: "OpenStreetMap",