Skip to content

Commit 30a880f

Browse files
authored
feat(VectorTileSource): Add tiles prop, pass url to url parameter (#336)
BREAKING CHANGE: Passes the `url` prop to the `url` parameter instead of `tiles` * Adds a tiles prop to retain support for bare tile URLs * Adds support for TileJSON with relative tile URLs * Refactors <WithLinkLocation> to support svelte's compilerOptions.experimental.async = true * Extends docs
1 parent 93a6cb9 commit 30a880f

13 files changed

Lines changed: 202 additions & 55 deletions

components/src/Switcher/Switcher.stories.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
asChild
6363
play={async ({ canvasElement, step }) => {
6464
const canvas = within(canvasElement);
65-
console.log(canvas);
6665
await step('Options are displayed in a row', async () => {
6766
const list = canvas.getByRole('list');
6867
expect(list.classList).toContain('layout-row');
@@ -84,7 +83,6 @@
8483
asChild
8584
play={async ({ canvasElement, step }) => {
8685
const canvas = within(canvasElement);
87-
console.log(canvas);
8886
await step('Options are displayed in a row', async () => {
8987
const list = canvas.getByRole('list');
9088
expect(list.classList).toContain('layout-column');

components/src/maplibre/Map/Map.svelte

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
type ProjectionSpecification,
66
type StyleSpecification
77
} from 'maplibre-gl';
8+
9+
import { type Location } from '../types';
10+
811
import { onMount, onDestroy, type Snippet, getContext, hasContext } from 'svelte';
12+
913
import { createMapContext, MapContext } from '../context.svelte.js';
10-
import { type Location } from '../types';
1114
import FallbackStyle from './FallbackStyle';
1215
import { de } from './locale';
1316
@@ -33,7 +36,7 @@
3336
showDebug?: boolean;
3437
options?: any;
3538
/**
36-
* Set the mouse cursor. `""` (empty string) restores Maplibre's default behaviour. See VectorLayer/Default for a common usage example
39+
* Set the mouse cursor. `""` (empty string) restores Maplibre's default behaviour. See VectorLayer/Default for a usage example
3740
*/
3841
cursor?: string;
3942
mapContext?: MapContext;
@@ -75,21 +78,23 @@
7578
}: MapProps = $props();
7679
7780
let container: HTMLElement;
81+
mapContext = createMapContext();
82+
83+
// Initial location is determined by (in order of precedence) :
84+
// 1. An arbitrary default location
85+
// 2. initialLocation prop
86+
// 3. initialLocation context (notably set by <WithLinkLocation/>)
7887
79-
// Merge initial location with default object so individual
80-
// properties (like pitch) can be omitted by the caller
81-
let initialLocation = {
88+
let contextLocation = getContext('initialLocation');
89+
90+
let initialLocation = $derived({
8291
lat: 51.3,
8392
lng: 10.2,
8493
zoom: 5,
8594
pitch: 0,
86-
...receivedInitialLocation
87-
};
88-
89-
mapContext = createMapContext();
90-
if (getContext('initialLocation') !== undefined && getContext('initialLocation') !== false) {
91-
initialLocation = getContext('initialLocation');
92-
}
95+
...receivedInitialLocation,
96+
...contextLocation
97+
});
9398
9499
onMount(() => {
95100
mapContext.map = new maplibre.Map({
@@ -151,6 +156,12 @@
151156
}
152157
});
153158
159+
$effect(() => {
160+
mapContext.map?.jumpTo({
161+
center: [initialLocation.lng, initialLocation.lat],
162+
zoom: initialLocation.zoom
163+
});
164+
});
154165
$effect(() => {
155166
if (allowZoom === false) {
156167
mapContext.map?.scrollZoom.disable();

components/src/maplibre/MapStyle/SWRDataLabLight.stories.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
>
9898
<VectorTileSource
9999
id="demo-source"
100-
url={`https://static.datenhub.net/data/p118_correctiv_waermewende/heating_merged_2.versatiles?{z}/{x}/{y}`}
100+
tiles={["https://static.datenhub.net/data/p118_correctiv_waermewende/heating_merged_2.versatiles?{z}/{x}/{y}"]}
101101
attribution="Demo attribution"
102102
/>
103103

@@ -150,7 +150,7 @@
150150
<GeocoderControl languages="de" service="maptiler" key="V32kPHZjMa0Mkn6YvSzA" />
151151
<VectorTileSource
152152
id="ev-infra-source"
153-
url={`https://static.datenhub.net/data/p109_besser_wohnen/rent_merged_4.versatiles?{z}/{x}/{y}`}
153+
tiles={["https://static.datenhub.net/data/p109_besser_wohnen/rent_merged_4.versatiles?{z}/{x}/{y}"]}
154154
attribution="Demo attribution"
155155
/>
156156

components/src/maplibre/Tooltip/Tooltip.stories.svelte

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
>
3434
<VectorTileSource
3535
id="ev-infra-source"
36-
url={`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`}
36+
tiles={[
37+
`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`
38+
]}
3739
/>
3840
<VectorLayer
3941
sourceId="ev-infra-source"
@@ -103,7 +105,9 @@
103105
>
104106
<VectorTileSource
105107
id="ev-infra-source"
106-
url={`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`}
108+
tiles={[
109+
`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`
110+
]}
107111
/>
108112
<VectorLayer
109113
sourceId="ev-infra-source"
@@ -187,7 +191,9 @@
187191
>
188192
<VectorTileSource
189193
id="ev-infra-source"
190-
url={`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`}
194+
tiles={[
195+
`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`
196+
]}
191197
/>
192198
<VectorLayer
193199
sourceId="ev-infra-source"

components/src/maplibre/VectorLayer/VectorLayer.stories.svelte

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@
4040
<Map showDebug={true} style={SWRDataLabLight()} cursor={hovered ? 'pointer' : ''}>
4141
<VectorTileSource
4242
id="ev-infra-source"
43-
url={`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`}
43+
tiles={[
44+
`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`
45+
]}
4446
/>
4547
<VectorLayer
4648
onmousemove={handleMouseMove}
@@ -125,7 +127,9 @@
125127
<Map showDebug={true} style={SWRDataLabLight()}>
126128
<VectorTileSource
127129
id="ev-infra-source"
128-
url={`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`}
130+
tiles={[
131+
`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`
132+
]}
129133
/>
130134
<VectorLayer
131135
sourceId="ev-infra-source"

components/src/maplibre/VectorTileSource/VectorTileSource.mdx

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,43 @@ import * as VectorTileSourceStories from './VectorTileSource.stories.svelte';
66

77
# VectorTileSource
88

9-
Loads tiled vector data from a tileserver. Any mablibre-supported tileserver will work, but we'll typically use tiles served by `versatiles-rs` or `versatiles-node-static-proxy`.
9+
Loads tiled vector data from a tileserver.
10+
11+
Any mablibre-supported tileserver will work, but we typically use tiles served by `versatiles-rs` or `versatiles-node-static-proxy`.
12+
13+
## Using a TileJSON
14+
15+
You should generally load tile data by pointing to a [TileJSON](https://github.com/mapbox/tilejson-spec/blob/master/3.0.0/README.md) file. These are generated by most tile servers and contain useful metadata like `attribution`, `bounds`, `minzoom` and `maxzoom` in addition to the tile data itself.
16+
17+
```jsx
18+
<Map style={SWRDataLabLight()}>
19+
<VectorTileSource id="test-source" url="https://tiles.datenhub.net/tiles/hillshade/tiles.json" />
20+
<VectorLayer
21+
sourceId="test-source"
22+
sourceLayer="boundaries"
23+
id="test-layer"
24+
type="line"
25+
paint={{ 'line-color': shades.red.base, 'line-width': 2 }}
26+
/>
27+
<AttributionControl position="bottom-left" />
28+
</Map>
29+
```
30+
31+
- This component includes a workaround to support for [TileJSON data with relative URLs](https://docs.versatiles.org/compendium/specification_extended_tilejson.html#relaxed-rule-for-tiles) as produced by versatiles-rs.
32+
- TileJSON data can by overridden by passing the relevant props to the component directly.
33+
34+
## Using tile URLs directly
35+
36+
Alternatively, you can point to a tile URL directly using the `tiles` prop:
1037

1138
```jsx
12-
<Map>
39+
<Map style={SWRDataLabLight()}>
1340
<VectorTileSource
14-
url="https://static.datenhub.net/rent_merged.versatiles?{z}/{x}/{y}"
15-
id="chargers"
41+
tiles={['https://static.datenhub.net/rent_merged.versatiles?{z}/{x}/{y}']}
42+
attribution="Demo attribution"
43+
id="test-source"
1644
/>
17-
<VectorLayer sourceId="chargers" />
45+
<VectorLayer sourceId="test-source" />
1846
</Map>
1947
```
2048

components/src/maplibre/VectorTileSource/VectorTileSource.stories.svelte

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<script context="module" lang="ts">
22
import { defineMeta } from '@storybook/addon-svelte-csf';
33
import DesignTokens from '../../DesignTokens/DesignTokens.svelte';
4+
import { shades } from '../../DesignTokens/Tokens.ts';
5+
46
import VectorTileSource from './VectorTileSource.svelte';
57
import VectorLayer from '../VectorLayer/VectorLayer.svelte';
68
import Map from '../Map/Map.svelte';
@@ -14,12 +16,56 @@
1416
</script>
1517

1618
<Story asChild name="Default">
19+
<DesignTokens theme="light">
20+
<div class="container">
21+
<Map showDebug={true} style={SWRDataLabLight()}>
22+
<VectorTileSource id="test-source" url="https://tiles.datenhub.net/tiles/osm/tiles.json" />
23+
<VectorLayer
24+
sourceId="test-source"
25+
sourceLayer="boundaries"
26+
id="test-layer"
27+
type="line"
28+
paint={{
29+
'line-color': shades.red.base,
30+
'line-width': 2
31+
}}
32+
/>
33+
<AttributionControl position="bottom-left" />
34+
</Map>
35+
</div>
36+
</DesignTokens>
37+
</Story>
38+
39+
<Story asChild name="TileJSON with absolute URLs">
40+
<DesignTokens theme="light">
41+
<div class="container">
42+
<Map showDebug={true} style={SWRDataLabLight()}>
43+
<VectorTileSource id="test-source" url="https://demotiles.maplibre.org/tiles/tiles.json" />
44+
<VectorLayer
45+
sourceId="test-source"
46+
sourceLayer="countries"
47+
id="test-layer"
48+
type="line"
49+
paint={{
50+
'line-color': shades.red.base,
51+
'line-width': 2
52+
}}
53+
/>
54+
<AttributionControl position="bottom-left" />
55+
</Map>
56+
</div>
57+
</DesignTokens>
58+
</Story>
59+
60+
<Story asChild name="Usign bare tile URL">
1761
<DesignTokens theme="light">
1862
<div class="container">
1963
<Map showDebug={true} style={SWRDataLabLight()}>
2064
<VectorTileSource
2165
id="ev-infra-source"
22-
url={`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`}
66+
tiles={[
67+
`https://static.datenhub.net/data/p108_e_auto_check/ev_infra_merged.versatiles?{z}/{x}/{y}`
68+
]}
2369
attribution="Demo attribution"
2470
/>
2571
<VectorLayer
@@ -28,9 +74,8 @@
2874
id="ev-infra-outline"
2975
type="line"
3076
paint={{
31-
'line-width': 0.5,
32-
'line-color': 'purple',
33-
'line-opacity': 1
77+
'line-width': 1,
78+
'line-color': shades.red.base
3479
}}
3580
/>
3681
<AttributionControl position="bottom-left" />
Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,43 @@
11
<script lang="ts">
2-
import { type SourceSpecification } from 'maplibre-gl';
2+
import { type PromoteIdSpecification, type VectorSourceSpecification } from 'maplibre-gl';
3+
import type { TileJsonData } from './types';
4+
35
import MapSource from '../Source/MapSource.svelte';
6+
import fetchTileJSON from './fetchTileJson';
47
58
interface VectorTileSourceProps {
69
id: string;
7-
url: string;
10+
url?: string;
11+
tiles?: string[];
812
minZoom?: number;
913
maxZoom?: number;
1014
/**
11-
* Attribution string for your data, usually rendered using an `<AttributionControl>`
15+
* Attribution string for your data, usually rendered using an `<AttributionControl/>`
1216
*/
1317
attribution?: string;
18+
promoteId?: PromoteIdSpecification;
1419
}
1520
16-
const { minZoom = 0, maxZoom = 24, id, url, attribution = '' }: VectorTileSourceProps = $props();
21+
const {
22+
minZoom,
23+
maxZoom,
24+
id,
25+
url,
26+
tiles,
27+
attribution,
28+
promoteId
29+
}: VectorTileSourceProps = $props();
30+
31+
let tileJsonData = $derived(url ? await fetchTileJSON(url) : {});
1732
18-
const sourceSpec: SourceSpecification = {
33+
const sourceSpec: VectorSourceSpecification = $derived({
1934
type: 'vector',
20-
tiles: [url],
21-
maxzoom: maxZoom,
22-
minzoom: minZoom,
23-
attribution
24-
};
35+
tiles: tiles || tileJsonData.tiles || [],
36+
maxzoom: maxZoom || tileJsonData.maxZoom || 24,
37+
minzoom: minZoom || tileJsonData.minZoom || 0,
38+
attribution: attribution || tileJsonData.attribution || '',
39+
promoteId
40+
});
2541
</script>
2642

2743
<MapSource {id} {sourceSpec} />
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { type TileJsonData } from './types';
2+
3+
// Workaround for https://github.com/versatiles-org/versatiles-rs/issues/184
4+
// Drop when/if this lands: https://github.com/maplibre/maplibre-gl-js/issues/182
5+
6+
export default async function fetchTileJSON(url: string): Promise<TileJsonData> {
7+
const u = new URL(url);
8+
const res = await fetch(u);
9+
const data = await res.json();
10+
11+
// Simple heuristic for absolute URLs
12+
const re = /(((http)s?)):\/\/.*/gi;
13+
14+
return {
15+
tiles: data?.tiles.map((path: string) => (re.test(path) ? path : `${u.origin}${path}`)),
16+
attribution: data?.attribution || data?.author,
17+
minZoom: data?.minzoom,
18+
maxZoom: data?.maxzoom
19+
};
20+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
interface TileJsonData {
2+
tiles?: string[];
3+
attribution?: string;
4+
minZoom?: number;
5+
maxZoom?: number;
6+
}
7+
8+
export { type TileJsonData };

0 commit comments

Comments
 (0)