@fusion.dev/helix-map 0.110.3 → 0.110.5
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.
package/dist/snapshot/camera.js
CHANGED
|
@@ -29,7 +29,8 @@ export const cameraOf = (view) => {
|
|
|
29
29
|
},
|
|
30
30
|
toWorld: ([sx, sy]) => {
|
|
31
31
|
const x = sx - view.width / 2;
|
|
32
|
-
const
|
|
32
|
+
const horizon = tilt > 0 ? -distance / Math.tan(tilt) : -Infinity;
|
|
33
|
+
const y = Math.max(sy - view.height / 2, horizon * 0.95);
|
|
33
34
|
const toward = (y * distance) / (distance * Math.cos(tilt) + y * Math.sin(tilt));
|
|
34
35
|
const across = (x * (distance - toward * Math.sin(tilt))) / distance;
|
|
35
36
|
return [
|
|
@@ -47,6 +47,8 @@ export declare const widthAt: ([[z0, w0], [z1, w1]]: readonly [readonly [number,
|
|
|
47
47
|
export interface SnapshotOptions {
|
|
48
48
|
/** The fetch the tiles are asked for with, where a server has its own, such as one that caches. */
|
|
49
49
|
fetch?: typeof fetch | undefined;
|
|
50
|
+
/** How long each request may take, in milliseconds, before the snapshot is given up. 3000 by default. */
|
|
51
|
+
timeout?: number | undefined;
|
|
50
52
|
/** A TileJSON URL for vector tiles in the OpenMapTiles schema: the one given to `mapLibreStyle`. OpenFreeMap's by default. */
|
|
51
53
|
tiles?: string | undefined;
|
|
52
54
|
}
|
|
@@ -68,7 +70,11 @@ export interface SnapshotOptions {
|
|
|
68
70
|
* toward a token in OKLab, so it is drawn in whichever theme the page is in,
|
|
69
71
|
* light or dark, and changes with it.
|
|
70
72
|
*
|
|
71
|
-
* Resolves to an empty string where
|
|
72
|
-
*
|
|
73
|
+
* **Whole or nothing.** Resolves to an empty string where any tile the view
|
|
74
|
+
* needs cannot be read in time, or the view is not numbers, which a page
|
|
75
|
+
* renders as nothing and the renderer then draws. A snapshot with a tile
|
|
76
|
+
* missing looks finished and is not, and one that waited on a slow server
|
|
77
|
+
* held the page: each request is given `timeout` milliseconds. A tile the
|
|
78
|
+
* server says it does not have, a 404, is drawn empty, as the sea is.
|
|
73
79
|
*/
|
|
74
|
-
export declare const mapSnapshot: (
|
|
80
|
+
export declare const mapSnapshot: (asked: MapView, options?: SnapshotOptions) => Promise<string>;
|
|
@@ -152,8 +152,33 @@ const simplified = (points) => {
|
|
|
152
152
|
const OPENFREEMAP = 'https://tiles.openfreemap.org/planet';
|
|
153
153
|
const MOST_TILES = 40;
|
|
154
154
|
const MARGIN = 16;
|
|
155
|
-
export const mapSnapshot = async (
|
|
156
|
-
const
|
|
155
|
+
export const mapSnapshot = async (asked, options = {}) => {
|
|
156
|
+
const view = {
|
|
157
|
+
bearing: Number(asked.bearing ?? 0),
|
|
158
|
+
center: { lat: Number(asked.center?.lat), lng: Number(asked.center?.lng) },
|
|
159
|
+
height: Math.round(Number(asked.height)),
|
|
160
|
+
pitch: Number(asked.pitch ?? 0),
|
|
161
|
+
width: Math.round(Number(asked.width)),
|
|
162
|
+
zoom: Number(asked.zoom),
|
|
163
|
+
};
|
|
164
|
+
if (![view.bearing, view.center.lat, view.center.lng, view.pitch, view.zoom].every(Number.isFinite) ||
|
|
165
|
+
!(view.width > 0 && view.height > 0)) {
|
|
166
|
+
return '';
|
|
167
|
+
}
|
|
168
|
+
const wait = options.timeout ?? 3000;
|
|
169
|
+
const get = (url) => {
|
|
170
|
+
const control = new AbortController();
|
|
171
|
+
let timer;
|
|
172
|
+
return Promise.race([
|
|
173
|
+
(options.fetch ?? fetch)(url, { signal: control.signal }),
|
|
174
|
+
new Promise((_, refuse) => {
|
|
175
|
+
timer = setTimeout(() => {
|
|
176
|
+
control.abort();
|
|
177
|
+
refuse(new Error(`${url} took longer than ${wait} ms`));
|
|
178
|
+
}, wait);
|
|
179
|
+
}),
|
|
180
|
+
]).finally(() => clearTimeout(timer));
|
|
181
|
+
};
|
|
157
182
|
const tiles = await get(options.tiles ?? OPENFREEMAP)
|
|
158
183
|
.then((response) => (response.ok ? response.json() : undefined))
|
|
159
184
|
.catch(() => undefined);
|
|
@@ -179,7 +204,7 @@ export const mapSnapshot = async (view, options = {}) => {
|
|
|
179
204
|
const wanted = [];
|
|
180
205
|
for (let x = Math.min(...xs); x <= Math.max(...xs); x++) {
|
|
181
206
|
for (let y = Math.max(0, Math.min(...ys)); y <= Math.min(count - 1, Math.max(...ys)); y++) {
|
|
182
|
-
wanted.push([
|
|
207
|
+
wanted.push([x, y]);
|
|
183
208
|
}
|
|
184
209
|
}
|
|
185
210
|
return wanted;
|
|
@@ -235,18 +260,32 @@ export const mapSnapshot = async (view, options = {}) => {
|
|
|
235
260
|
}
|
|
236
261
|
return path;
|
|
237
262
|
};
|
|
263
|
+
let whole = true;
|
|
264
|
+
const count = 2 ** depth;
|
|
238
265
|
await Promise.all(wanted.map(async (at) => {
|
|
239
266
|
const url = template
|
|
240
267
|
.replace('{z}', String(depth))
|
|
241
|
-
.replace('{x}', String(at[0]))
|
|
268
|
+
.replace('{x}', String(((at[0] % count) + count) % count))
|
|
242
269
|
.replace('{y}', String(at[1]));
|
|
243
270
|
const bytes = await get(url)
|
|
244
|
-
.then((response) =>
|
|
271
|
+
.then((response) => response.ok
|
|
272
|
+
? response.arrayBuffer()
|
|
273
|
+
: response.status === 404
|
|
274
|
+
? new ArrayBuffer(0)
|
|
275
|
+
: undefined)
|
|
245
276
|
.catch(() => undefined);
|
|
246
277
|
if (!bytes) {
|
|
278
|
+
whole = false;
|
|
247
279
|
return;
|
|
248
280
|
}
|
|
249
|
-
|
|
281
|
+
try {
|
|
282
|
+
drawTile(new VectorTile(new PbfReader(new Uint8Array(bytes))), at);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
whole = false;
|
|
286
|
+
}
|
|
287
|
+
}));
|
|
288
|
+
function drawTile(tile, at) {
|
|
250
289
|
for (const layer of shown) {
|
|
251
290
|
const source = tile.layers[layer.source];
|
|
252
291
|
if (!source) {
|
|
@@ -267,7 +306,10 @@ export const mapSnapshot = async (view, options = {}) => {
|
|
|
267
306
|
}
|
|
268
307
|
}
|
|
269
308
|
}
|
|
270
|
-
}
|
|
309
|
+
}
|
|
310
|
+
if (!whole) {
|
|
311
|
+
return '';
|
|
312
|
+
}
|
|
271
313
|
const body = shown
|
|
272
314
|
.map((layer) => {
|
|
273
315
|
const paths = drawn.get(layer.id) ?? [];
|
|
@@ -276,7 +318,8 @@ export const mapSnapshot = async (view, options = {}) => {
|
|
|
276
318
|
}
|
|
277
319
|
const color = colorOf(layer);
|
|
278
320
|
return layer.kind === 'fill'
|
|
279
|
-
?
|
|
321
|
+
?
|
|
322
|
+
`<path style="fill:${color}" d="${paths.join('')}"/>`
|
|
280
323
|
: `<path style="fill:none;stroke:${color};stroke-width:${+widthAt(layer.width ?? [
|
|
281
324
|
[0, 1],
|
|
282
325
|
[1, 1],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusion.dev/helix-map",
|
|
3
3
|
"description": "Connects the Helix Map frame to a map renderer: MapLibre, Leaflet, or a driver of your own.",
|
|
4
|
-
"version": "0.110.
|
|
4
|
+
"version": "0.110.5",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ross Sheppard <ross.sheppard@fusion.dev>",
|
|
7
7
|
"repository": {
|
|
@@ -43,14 +43,14 @@
|
|
|
43
43
|
},
|
|
44
44
|
"bugs": "https://github.com/project-fusion/fusion/issues",
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@fusion.dev/helix-core": "0.110.
|
|
47
|
-
"@fusion.dev/helix-icons": "0.110.
|
|
46
|
+
"@fusion.dev/helix-core": "0.110.5",
|
|
47
|
+
"@fusion.dev/helix-icons": "0.110.5",
|
|
48
48
|
"@mapbox/vector-tile": "^3.0.0",
|
|
49
49
|
"pbf": "^5.1.2"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@fusion.dev/helix-headless": "0.110.
|
|
53
|
-
"@fusion.dev/helix-testing": "0.110.
|
|
52
|
+
"@fusion.dev/helix-headless": "0.110.5",
|
|
53
|
+
"@fusion.dev/helix-testing": "0.110.5",
|
|
54
54
|
"@maplibre/maplibre-gl-style-spec": "^26.4.4",
|
|
55
55
|
"@types/bun": "^1.3.0",
|
|
56
56
|
"@types/leaflet": "^1.9.20",
|