@swr-data-lab/components 2.2.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
- <script lang="ts">"use strict";
1
+ <script lang="ts">import { dev } from '$app/environment';
2
+ import { asset } from '$app/paths';
2
3
  let { project, charts, baseUrl } = $props();
3
4
  </script>
4
5
 
@@ -10,22 +11,20 @@ let { project, charts, baseUrl } = $props();
10
11
  <thead>
11
12
  <tr>
12
13
  <th>Title</th>
13
- {#if baseUrl}
14
- <th>Embed URL</th>
15
- {/if}
14
+ <th>Embed URL</th>
16
15
  </tr>
17
16
  </thead>
18
17
  <tbody>
19
18
  {#each charts as chart}
19
+ {@const url = baseUrl ? `${baseUrl}/${chart.slug}` : asset(chart.slug)}
20
+ {@const urlPostfixed = `${url}${dev ? '' : '.html'}`}
20
21
  <tr>
21
22
  <td>
22
- <a href="./{chart.slug}">{chart.title}</a>
23
+ <a href={urlPostfixed}>{chart.title}</a>
24
+ </td>
25
+ <td>
26
+ <input type="text" value={urlPostfixed} />
23
27
  </td>
24
- {#if baseUrl}
25
- <td>
26
- <input type="text" value={`${baseUrl}/${chart.slug}.html`} />
27
- </td>
28
- {/if}
29
28
  </tr>
30
29
  {/each}
31
30
  </tbody>
@@ -3,6 +3,7 @@ import { onMount, onDestroy, getContext, hasContext } from 'svelte';
3
3
  import { createMapContext, MapContext } from '../context.svelte.js';
4
4
  import {} from '../types';
5
5
  import FallbackStyle from './FallbackStyle';
6
+ import { derived } from 'svelte/store';
6
7
  let { children, options, style = FallbackStyle, minZoom = 0, maxZoom = 14.99, zoom = $bindable(), center = $bindable(), pitch = $bindable(0), bearing = $bindable(0), loading = $bindable(true), projection = { type: 'mercator' }, allowRotation = false, allowZoom = true, showDebug = false, initialLocation: receivedInitialLocation,
7
8
  // Future: This should become bindable.readonly when that becomes
8
9
  // available, see: https://github.com/sveltejs/svelte/issues/7712
@@ -74,6 +75,20 @@ $effect(() => {
74
75
  mapContext.map?.scrollZoom.enable();
75
76
  }
76
77
  });
78
+ const debugValues = $derived(Object.entries({ ...center, zoom, pitch, allowZoom, allowRotation }));
79
+ const handleDebugValueClick = (e) => {
80
+ if (e.target) {
81
+ const t = e.target;
82
+ const s = t.innerText;
83
+ navigator.clipboard.writeText(s);
84
+ }
85
+ };
86
+ const handleDebugCopyLocationClick = (e) => {
87
+ if (e.target) {
88
+ const s = JSON.stringify({ lng: center?.lng, lat: center?.lat, zoom, pitch });
89
+ navigator.clipboard.writeText(s);
90
+ }
91
+ };
77
92
  </script>
78
93
 
79
94
  <div bind:this={container} class="container" data-testid="map-container">
@@ -83,10 +98,17 @@ $effect(() => {
83
98
  {/if}
84
99
  {/if}
85
100
  {#if showDebug}
86
- <pre class="debug">
87
- {Object.entries({ ...center, zoom, pitch, allowZoom, allowRotation })
88
- .map(([key, val]) => `${key}: ${val}`)
89
- .join('\n')}</pre>
101
+ <ul class="debug">
102
+ {#each debugValues as [key, value]}
103
+ <li>
104
+ {key}:
105
+ <button onclick={handleDebugValueClick}
106
+ >{value?.toLocaleString('en', { maximumSignificantDigits: 6 })}</button
107
+ >
108
+ </li>
109
+ {/each}
110
+ <li><button onclick={handleDebugCopyLocationClick}>[Copy Location]</button></li>
111
+ </ul>
90
112
  {/if}
91
113
  </div>
92
114
 
@@ -105,6 +127,17 @@ $effect(() => {
105
127
  padding: 2px;
106
128
  font-family: monospace;
107
129
  }
130
+ .debug button {
131
+ appearance: none;
132
+ background: transparent;
133
+ font-family: inherit;
134
+ color: inherit;
135
+ border: 0;
136
+ cursor: pointer;
137
+ }
138
+ .debug button:hover, .debug button:focus-visible {
139
+ text-decoration: underline;
140
+ }
108
141
 
109
142
  :global(.maplibregl-map) {
110
143
  overflow: hidden;
@@ -7,7 +7,7 @@ import makeWalking from './components/Walking';
7
7
  import makeRoads from './components/Roads';
8
8
  const { buildingFootprints, buildingExtrusions, structureExtrusions } = makeBuildings();
9
9
  const { landuse } = makeLanduse();
10
- const { placeLabels } = makePlaceLabels();
10
+ const { placeLabels, boundaryLabels } = makePlaceLabels();
11
11
  const { admin } = makeAdmin();
12
12
  const { airports, transitBridges, transitSurface, transitTunnels } = makeTransit();
13
13
  const { walkingLabels, walkingTunnels, walkingSurface, walkingBridges } = makeWalking();
@@ -77,7 +77,8 @@ const style = (opts) => {
77
77
  // 8. Building extrusions
78
78
  ...(options.enableBuildingExtrusions ? [buildingExtrusions] : []),
79
79
  // 8. Point labels
80
- ...placeLabels
80
+ ...placeLabels,
81
+ ...boundaryLabels
81
82
  ]
82
83
  };
83
84
  };
@@ -3,7 +3,7 @@ import {} from '../../types';
3
3
  export default function makeAdmin() {
4
4
  const admin = [
5
5
  {
6
- id: 'boundary-country:outline',
6
+ id: 'boundary-country:case',
7
7
  filter: [
8
8
  'all',
9
9
  ['==', 'admin_level', 2],
@@ -22,14 +22,10 @@ export default function makeAdmin() {
22
22
  ]
23
23
  },
24
24
  'line-opacity': 0.75
25
- },
26
- layout: {
27
- 'line-cap': 'round',
28
- 'line-join': 'round'
29
25
  }
30
26
  },
31
27
  {
32
- id: 'boundary-country-disputed:outline',
28
+ id: 'boundary-country-disputed:case',
33
29
  filter: [
34
30
  'all',
35
31
  ['==', 'admin_level', 2],
@@ -50,7 +46,7 @@ export default function makeAdmin() {
50
46
  }
51
47
  },
52
48
  {
53
- id: 'boundary-state:outline',
49
+ id: 'boundary-state:case',
54
50
  filter: [
55
51
  'all',
56
52
  ['==', 'admin_level', 4],
@@ -69,10 +65,6 @@ export default function makeAdmin() {
69
65
  ]
70
66
  },
71
67
  'line-opacity': 0.75
72
- },
73
- layout: {
74
- 'line-cap': 'round',
75
- 'line-join': 'round'
76
68
  }
77
69
  },
78
70
  {
@@ -88,7 +80,7 @@ export default function makeAdmin() {
88
80
  'line-color': {
89
81
  stops: [
90
82
  [7, '#cecdcd'],
91
- [10, 'black']
83
+ [10, '#161616']
92
84
  ]
93
85
  },
94
86
  'line-width': {
@@ -99,10 +91,6 @@ export default function makeAdmin() {
99
91
  [12, 3]
100
92
  ]
101
93
  }
102
- },
103
- layout: {
104
- 'line-cap': 'round',
105
- 'line-join': 'round'
106
94
  }
107
95
  },
108
96
  {
@@ -124,9 +112,6 @@ export default function makeAdmin() {
124
112
  },
125
113
  'line-color': 'hsl(246,0%,77%)',
126
114
  'line-dasharray': [2, 1]
127
- },
128
- layout: {
129
- 'line-cap': 'square'
130
115
  }
131
116
  },
132
117
  {
@@ -152,10 +137,6 @@ export default function makeAdmin() {
152
137
  [8, 1]
153
138
  ]
154
139
  }
155
- },
156
- layout: {
157
- 'line-cap': 'round',
158
- 'line-join': 'round'
159
140
  }
160
141
  }
161
142
  ].map((el) => {
@@ -163,7 +144,11 @@ export default function makeAdmin() {
163
144
  source: 'versatiles-osm',
164
145
  'source-layer': 'boundaries',
165
146
  type: 'line',
166
- ...el
147
+ ...el,
148
+ layout: {
149
+ 'line-cap': 'round',
150
+ 'line-join': 'round'
151
+ }
167
152
  };
168
153
  });
169
154
  return { admin };
@@ -14,7 +14,7 @@ export default function makeLanduse() {
14
14
  type: 'fill',
15
15
  'source-layer': 'ocean',
16
16
  paint: {
17
- 'fill-color': 'hsl(220, 30%, 96%)'
17
+ 'fill-color': tokens.water_ocean
18
18
  }
19
19
  },
20
20
  {
@@ -300,11 +300,11 @@ export default function makeLanduse() {
300
300
  'source-layer': 'water_polygons',
301
301
  filter: ['==', 'kind', 'water'],
302
302
  paint: {
303
- 'fill-color': tokens.water_light,
303
+ 'fill-color': tokens.water_ocean,
304
304
  'fill-opacity': {
305
305
  stops: [
306
- [4, 0],
307
- [6, 1]
306
+ [3, 0],
307
+ [4, 1]
308
308
  ]
309
309
  }
310
310
  }
@@ -1,4 +1,5 @@
1
1
  import type { SymbolLayerSpecification } from 'maplibre-gl';
2
2
  export default function makePlaceLabels(): {
3
3
  placeLabels: SymbolLayerSpecification[];
4
+ boundaryLabels: SymbolLayerSpecification[];
4
5
  };
@@ -1,194 +1,239 @@
1
1
  import tokens from '../tokens';
2
+ // Hand-authored list of place labes we want to show at low zoom levels
3
+ // Ideally majorCities would include Frankfurt and Leipzig, but they're not
4
+ // state capitals so they're not available in the versatiles data until z6
5
+ const majorCities = [
6
+ 'Stuttgart',
7
+ 'München',
8
+ 'Mainz',
9
+ 'Bremen',
10
+ 'Düsseldorf',
11
+ 'Hamburg',
12
+ 'Bremen',
13
+ 'Dresden',
14
+ 'Erfurt'
15
+ ];
16
+ const majorCountries = [
17
+ 'Deutschland',
18
+ 'Dänemark',
19
+ 'Frankreich',
20
+ 'Niederlande',
21
+ 'Belgien',
22
+ 'Schweiz',
23
+ 'Polen',
24
+ 'Österreich',
25
+ 'Tschechien',
26
+ 'Slowakei',
27
+ 'Italien',
28
+ 'Ungarn'
29
+ ];
30
+ // For smaller cities we use the population field to derive our hierarchy,
31
+ // though that's limited by the fact that versatiles hard-codes population
32
+ // values for "city" and anything below.
33
+ // See: https://github.com/versatiles-org/shortbread-tilemaker/blob/69e5d4c586a1d2726b746a24829bfb05d4dbeb91/process.lua#L198-L242
2
34
  export default function makePlaceLabels() {
3
35
  const placeLabels = [
4
36
  {
5
- id: 'label-place-neighbourhood',
6
- filter: ['==', 'kind', 'neighbourhood'],
7
- minzoom: 14,
37
+ id: 'label-place-quarter',
38
+ filter: ['all', ['in', 'kind', 'neighbourhood']],
39
+ minzoom: 12,
8
40
  layout: {
9
- 'text-field': '{name_de}',
10
- 'text-font': tokens.sans_regular,
11
41
  'text-size': {
12
- stops: [[14, 14]]
42
+ stops: [
43
+ [10, 13],
44
+ [15, 16]
45
+ ]
13
46
  }
14
47
  },
15
48
  paint: {
16
- 'text-color': tokens.label_secondary,
17
- 'text-halo-color': tokens.background,
18
- 'text-halo-width': 2
49
+ 'text-color': tokens.label_tertiary
19
50
  }
20
51
  },
21
52
  {
22
- id: 'label-place-quarter',
23
- filter: ['==', 'kind', 'quarter'],
24
- minzoom: 14,
53
+ id: 'label-place-suburb',
54
+ filter: [
55
+ 'all',
56
+ ['in', 'kind', 'suburb', 'village', 'hamlet', 'town'],
57
+ ['>', 'population', 1000],
58
+ ['<', 'population', 15_000]
59
+ ],
60
+ minzoom: 11.5,
25
61
  layout: {
26
- 'text-field': '{name_de}',
27
- 'text-font': tokens.sans_regular,
28
62
  'text-size': {
29
- stops: [[10, 14]]
63
+ stops: [
64
+ [11, 13],
65
+ [15, 15]
66
+ ]
30
67
  }
31
68
  },
32
69
  paint: {
33
- 'text-color': tokens.label_secondary,
34
- 'text-halo-color': tokens.background,
35
- 'text-halo-width': 2
70
+ 'text-color': tokens.label_tertiary
36
71
  }
37
72
  },
38
73
  {
39
- id: 'label-place-suburb',
40
- filter: ['==', 'kind', 'suburb'],
41
- minzoom: 11,
74
+ id: 'label-place-town',
75
+ filter: [
76
+ 'all',
77
+ ['in', 'kind', 'village', 'hamlet', 'town'],
78
+ ['<', 'population', 50_000],
79
+ ['>', 'population', 15_000]
80
+ ],
81
+ minzoom: 10,
42
82
  layout: {
43
- 'text-field': '{name_de}',
44
- 'text-font': tokens.sans_regular,
45
83
  'text-size': {
46
84
  stops: [
47
- [11, 13],
48
- [13, 15]
85
+ [10, 14],
86
+ [12, 15]
49
87
  ]
50
88
  }
51
- },
52
- paint: {
53
- 'text-color': tokens.label_secondary,
54
- 'text-halo-color': tokens.background,
55
- 'text-halo-width': 1.5
56
89
  }
57
90
  },
58
91
  {
59
- id: 'label-place-hamlet',
60
- filter: ['==', 'kind', 'hamlet'],
61
- minzoom: 13,
92
+ id: 'label-small-city',
93
+ filter: [
94
+ 'all',
95
+ ['in', 'kind', 'city', 'town'],
96
+ ['>', 'population', 50_000],
97
+ ['<', 'population', 100_000],
98
+ ['!in', 'name_de', ...majorCities]
99
+ ],
100
+ minzoom: 8.5,
62
101
  layout: {
63
- 'text-field': '{name_de}',
64
- 'text-font': tokens.sans_regular,
65
102
  'text-size': {
66
103
  stops: [
67
- [10, 11],
68
- [12, 14]
104
+ [8, 14],
105
+ [12, 16]
69
106
  ]
70
107
  }
71
- },
72
- paint: {
73
- 'text-color': tokens.label_secondary,
74
- 'text-halo-color': tokens.background,
75
- 'text-halo-width': 2
76
108
  }
77
109
  },
78
110
  {
79
- id: 'label-place-village',
80
- filter: ['==', 'kind', 'village'],
81
- minzoom: 11,
111
+ id: 'label-place-medium-city',
112
+ filter: [
113
+ 'all',
114
+ ['in', 'kind', 'city', 'town'],
115
+ ['>', 'population', 100_000],
116
+ ['<', 'population', 400_000],
117
+ ['!in', 'name_de', ...majorCities]
118
+ ],
119
+ minzoom: 7,
120
+ maxzoom: 13,
82
121
  layout: {
83
- 'text-field': '{name_de}',
84
- 'text-font': tokens.sans_regular,
85
122
  'text-size': {
86
123
  stops: [
87
- [9, 11],
88
- [12, 14]
124
+ [7, 12],
125
+ [13, 17]
89
126
  ]
90
127
  }
91
- },
92
- paint: {
93
- 'text-color': tokens.label_secondary,
94
- 'text-halo-color': tokens.background,
95
- 'text-halo-width': 2
96
128
  }
97
129
  },
98
130
  {
99
- id: 'label-place-town',
100
- filter: ['==', 'kind', 'town'],
101
- minzoom: 9,
131
+ id: 'label-place-big-city',
132
+ filter: [
133
+ 'all',
134
+ ['in', 'kind', 'city', 'town', 'state_capital'],
135
+ ['>', 'population', 400_000],
136
+ ['!in', 'name_de', ...majorCities]
137
+ ],
138
+ minzoom: 7,
139
+ maxzoom: 12,
102
140
  layout: {
103
- 'text-field': '{name_de}',
104
- 'text-font': tokens.sans_regular,
105
- 'text-letter-spacing': 0.015,
106
141
  'text-size': {
107
142
  stops: [
108
- [8, 13],
109
- [12, 16]
143
+ [7, 14],
144
+ [15, 20]
110
145
  ]
111
146
  }
112
147
  },
113
148
  paint: {
114
- 'text-color': tokens.label_primary,
115
- 'text-halo-color': tokens.background,
116
- 'text-halo-width': 2
149
+ 'text-color': tokens.label_primary
117
150
  }
118
151
  },
119
152
  {
120
- id: 'label-place-city',
121
- filter: ['==', 'kind', 'city'],
122
- minzoom: 6,
123
- maxzoom: 11,
153
+ id: 'label-place-major-city',
154
+ filter: ['all', ['in', 'name_de', ...majorCities]],
155
+ minzoom: 5,
156
+ maxzoom: 12,
124
157
  layout: {
125
- 'text-field': '{name_de}',
126
- 'text-font': tokens.sans_regular,
127
- 'text-letter-spacing': 0.015,
128
158
  'text-size': {
129
159
  stops: [
130
- [8, 14],
131
- [10, 14]
160
+ [7, 14],
161
+ [15, 20]
132
162
  ]
133
163
  }
134
164
  },
135
165
  paint: {
136
- 'text-color': tokens.label_secondary,
137
- 'text-halo-color': tokens.background,
138
- 'text-halo-width': 2
166
+ 'text-color': tokens.label_primary
139
167
  }
140
168
  },
141
169
  {
142
- id: 'label-place-statecapital',
143
- filter: ['==', 'kind', 'state_capital'],
170
+ id: 'label-place-capital',
171
+ filter: ['all', ['==', 'kind', 'capital'], ['>', 'population', 1000000]],
144
172
  minzoom: 5,
145
173
  maxzoom: 12,
146
174
  layout: {
147
- 'text-field': '{name_de}',
148
- 'text-font': tokens.sans_regular,
149
- 'text-letter-spacing': 0.025,
150
175
  'text-size': {
151
176
  stops: [
152
- [5, 13],
153
- [14, 20]
177
+ [7, 15],
178
+ [14, 23]
154
179
  ]
155
180
  }
156
181
  },
182
+ paint: {
183
+ 'text-color': tokens.label_primary
184
+ }
185
+ }
186
+ ].map((el) => {
187
+ return {
188
+ ...el,
189
+ type: 'symbol',
190
+ source: 'versatiles-osm',
191
+ 'source-layer': 'place_labels',
192
+ layout: {
193
+ 'text-font': tokens.sans_regular,
194
+ 'text-letter-spacing': 0.025,
195
+ 'text-field': '{name_de}',
196
+ ...el.layout
197
+ },
157
198
  paint: {
158
199
  'text-color': tokens.label_secondary,
159
200
  'text-halo-color': tokens.background,
160
- 'text-halo-width': 1
201
+ 'text-halo-width': 1,
202
+ ...el.paint
161
203
  }
162
- },
204
+ };
205
+ });
206
+ const boundaryLabels = [
163
207
  {
164
- id: 'label-place-capital',
165
- filter: ['all', ['==', 'kind', 'capital'], ['==', 'name_de', 'Berlin']],
166
- minzoom: 5,
167
- maxzoom: 12,
208
+ id: 'label-boundary-country',
209
+ filter: ['all', ['==', 'admin_level', 2], ['in', 'name_de', ...majorCountries]],
210
+ minzoom: 4,
211
+ maxzoom: 8,
168
212
  layout: {
169
213
  'text-field': '{name_de}',
170
- 'text-letter-spacing': 0.015,
171
- 'text-font': tokens.sans_medium,
214
+ 'text-letter-spacing': 0.0825,
215
+ 'text-font': tokens.sans_regular,
216
+ 'text-transform': 'uppercase',
172
217
  'text-size': {
173
218
  stops: [
174
- [5, 15],
175
- [14, 20]
219
+ [4, 10],
220
+ [7, 17]
176
221
  ]
177
222
  }
178
223
  },
179
224
  paint: {
180
- 'text-color': tokens.label_primary,
225
+ 'text-color': tokens.label_tertiary,
181
226
  'text-halo-color': tokens.background,
182
- 'text-halo-width': 2
227
+ 'text-halo-width': 1
183
228
  }
184
229
  }
185
230
  ].map((el) => {
186
231
  return {
187
232
  type: 'symbol',
188
233
  source: 'versatiles-osm',
189
- 'source-layer': 'place_labels',
234
+ 'source-layer': 'boundary_labels',
190
235
  ...el
191
236
  };
192
237
  });
193
- return { placeLabels };
238
+ return { placeLabels, boundaryLabels };
194
239
  }