Skip to content

Commit 045c884

Browse files
authored
feat: web-map-links for TileJSON (#311)
Cool! I really need a visual loading indicator, but that can be tricky with these layers which might fail? cc @hrodmn https://github.com/user-attachments/assets/9abb7d34-69e1-4035-b97d-1a1a430a69a2
1 parent a8a27f5 commit 045c884

File tree

5 files changed

+75
-3
lines changed

5 files changed

+75
-3
lines changed

src/components/map.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ import { toProj4 } from "geotiff-geokeys-to-proj4";
1717
import "maplibre-gl/dist/maplibre-gl.css";
1818
import { type RefObject, useEffect, useMemo, useRef, useState } from "react";
1919
import {
20+
Layer as MaplibreLayer,
2021
Map as MaplibreMap,
2122
type MapRef,
23+
Source,
2224
useControl,
2325
} from "react-map-gl/maplibre";
2426
import { useColorModeValue } from "../components/ui/color-mode";
@@ -64,6 +66,7 @@ export default function Map() {
6466
const lineWidth = useStore((store) => store.lineWidth);
6567
const collectionBounds = useCollectionBounds();
6668
const items = useItems();
69+
const webMapLink = useStore((store) => store.webMapLink);
6770
const [hoveredStacGeoparquetItemId, setHoveredStacGeoparquetItemId] =
6871
useState<string | null>(null);
6972

@@ -262,6 +265,16 @@ export default function Map() {
262265
if (sanitizedBbox) setBbox(sanitizedBbox);
263266
}}
264267
>
268+
{webMapLink && webMapLink.rel === "tilejson" && (
269+
<Source
270+
key={webMapLink.href}
271+
id="web-map-link"
272+
type="raster"
273+
url={webMapLink.href}
274+
>
275+
<MaplibreLayer id="web-map-link-layer" type="raster" />
276+
</Source>
277+
)}
265278
<DeckGLOverlay
266279
layers={layers}
267280
getCursor={(props) => getCursor(mapRef, props)}

src/components/value/buttons.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useStore } from "@/store";
12
import type { StacValue } from "@/types/stac";
23
import { fitBounds } from "@/utils/map";
34
import { getSelfHref } from "@/utils/stac";
@@ -11,9 +12,17 @@ import {
1112
Portal,
1213
createShikiAdapter,
1314
} from "@chakra-ui/react";
14-
import { LuExternalLink, LuFileJson, LuFocus } from "react-icons/lu";
15+
import { useMemo } from "react";
16+
import {
17+
LuExternalLink,
18+
LuEye,
19+
LuEyeClosed,
20+
LuFileJson,
21+
LuFocus,
22+
} from "react-icons/lu";
1523
import { useMap } from "react-map-gl/maplibre";
1624
import type { HighlighterGeneric } from "shiki";
25+
import type { StacLink } from "stac-ts";
1726

1827
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1928
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
@@ -33,9 +42,19 @@ const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
3342
export default function Buttons({ value }: { value: StacValue }) {
3443
const selfHref = getSelfHref(value);
3544
const { map } = useMap();
45+
const webMapLink = useStore((store) => store.webMapLink);
46+
const setWebMapLink = useStore((store) => store.setWebMapLink);
47+
48+
const tileJsonLinks = useMemo(
49+
() =>
50+
(value.links as StacLink[] | undefined)?.filter(
51+
(link) => link.rel === "tilejson"
52+
) ?? [],
53+
[value]
54+
);
3655

3756
return (
38-
<ButtonGroup variant={"surface"} size="xs">
57+
<ButtonGroup variant={"surface"} size="xs" flexWrap="wrap">
3958
<Button onClick={() => map && fitBounds(map, value, null)}>
4059
<LuFocus />
4160
Zoom to extents
@@ -55,6 +74,20 @@ export default function Buttons({ value }: { value: StacValue }) {
5574
</a>
5675
</Button>
5776
)}
77+
{tileJsonLinks.map((link) => {
78+
const active = webMapLink?.href === link.href;
79+
return (
80+
<Button
81+
key={link.href}
82+
onClick={() =>
83+
setWebMapLink(active ? null : { href: link.href, rel: link.rel })
84+
}
85+
>
86+
{active ? <LuEye /> : <LuEyeClosed />}
87+
{link.title || "Tiles"}
88+
</Button>
89+
);
90+
})}
5891
<JsonButton value={value} />
5992
</ButtonGroup>
6093
);

src/store/href.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const createHrefSlice: StateCreator<State, [], [], HrefState> = (
3636
cogHref: null,
3737
cogSources: null,
3838
pagedCogSources: null,
39+
webMapLink: null,
3940
});
4041
},
4142
});

src/store/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
type UploadedFileState,
2222
} from "./uploaded-file";
2323
import { createValueSlice, type ValueState } from "./value";
24+
import { createWebMapLinksSlice, type WebMapLinksState } from "./web-map-links";
2425

2526
export interface State
2627
extends
@@ -38,7 +39,8 @@ export interface State
3839
DatetimeState,
3940
MapState,
4041
SettingsState,
41-
StacGeoparquetState {
42+
StacGeoparquetState,
43+
WebMapLinksState {
4244
fillColor: [number, number, number, number];
4345
lineColor: [number, number, number, number];
4446
lineWidth: number;
@@ -62,6 +64,7 @@ export const useStore = create<State>()(
6264
...createDatetimeSlice(...a),
6365
...createMapSlice(...a),
6466
...createSettingsSlice(...a),
67+
...createWebMapLinksSlice(...a),
6568
fillColor: [207, 63, 2, 50] as [number, number, number, number],
6669
lineColor: [207, 63, 2, 100] as [number, number, number, number],
6770
lineWidth: 2,

src/store/web-map-links.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { StateCreator } from "zustand";
2+
import type { State } from ".";
3+
4+
export interface WebMapLink {
5+
href: string;
6+
rel: string;
7+
}
8+
9+
export interface WebMapLinksState {
10+
webMapLink: WebMapLink | null;
11+
setWebMapLink: (link: WebMapLink | null) => void;
12+
}
13+
14+
export const createWebMapLinksSlice: StateCreator<
15+
State,
16+
[],
17+
[],
18+
WebMapLinksState
19+
> = (set) => ({
20+
webMapLink: null,
21+
setWebMapLink: (link) => set({ webMapLink: link }),
22+
});

0 commit comments

Comments
 (0)