@navaramap/three-plugins 0.0.4 → 0.0.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/index.d.ts +65 -9
- package/dist/index.js +124 -110
- package/dist/index.umd.cjs +1 -1
- package/package.json +5 -5
- package/src/TileJsonPlugin.test.ts +119 -3
- package/src/TileJsonPlugin.ts +145 -32
- package/src/index.ts +4 -0
package/dist/index.d.ts
CHANGED
|
@@ -388,8 +388,28 @@ export declare type TileJson = {
|
|
|
388
388
|
maxzoom?: number;
|
|
389
389
|
/** Tiling scheme. `"tms"` flips the Y axis (raster sources only). @defaultValue "xyz" */
|
|
390
390
|
scheme?: "xyz" | "tms";
|
|
391
|
+
/**
|
|
392
|
+
* Tile size in pixels. Not part of the TileJSON spec, but emitted by
|
|
393
|
+
* MapLibre-oriented tile servers; applied to `raster-dem` sources only.
|
|
394
|
+
* @defaultValue 512
|
|
395
|
+
*/
|
|
396
|
+
tileSize?: number;
|
|
397
|
+
/**
|
|
398
|
+
* Elevation encoding of DEM tiles. Not part of the TileJSON spec, but
|
|
399
|
+
* emitted by MapLibre-oriented tile servers; applied to `raster-dem` sources
|
|
400
|
+
* only.
|
|
401
|
+
* @defaultValue "mapbox"
|
|
402
|
+
*/
|
|
403
|
+
encoding?: TileJsonDemEncoding;
|
|
391
404
|
};
|
|
392
405
|
|
|
406
|
+
/**
|
|
407
|
+
* How a `raster-dem` document's RGB tiles encode elevation, matching MapLibre's
|
|
408
|
+
* `encoding` values. MapLibre's `"custom"` (free-form decode factors) is not
|
|
409
|
+
* supported.
|
|
410
|
+
*/
|
|
411
|
+
export declare type TileJsonDemEncoding = "mapbox" | "terrarium";
|
|
412
|
+
|
|
393
413
|
/** Detail passed to the {@link TileJsonPlugin} `loaded` event. */
|
|
394
414
|
export declare type TileJsonLoadedEvent = {
|
|
395
415
|
/** The Navara source created for the document. */
|
|
@@ -420,9 +440,13 @@ export declare class TileJsonPlugin extends Plugin_2<View_4, ViewContext> {
|
|
|
420
440
|
*
|
|
421
441
|
* `desc.url` is the TileJSON document URL; the plugin fetches it, then creates
|
|
422
442
|
* one source of the requested `desc.type` using the document's first tile
|
|
423
|
-
* endpoint, `minzoom`/`maxzoom`, and `scheme
|
|
424
|
-
*
|
|
425
|
-
*
|
|
443
|
+
* endpoint, `minzoom`/`maxzoom`, and `scheme` — plus, for `raster-dem`, the
|
|
444
|
+
* MapLibre-compatible `tileSize` and `encoding` fields (defaulting to
|
|
445
|
+
* MapLibre's 512 and `"mapbox"`). Each of those fields can also be set on
|
|
446
|
+
* `desc` itself, taking precedence over the document. The document's
|
|
447
|
+
* `attribution` is surfaced through the view's built-in attribution UI
|
|
448
|
+
* (`view.attribution`), and a `loaded` event is emitted carrying the source,
|
|
449
|
+
* document, and attribution.
|
|
426
450
|
*
|
|
427
451
|
* A TileJSON `tiles` array lists mirror endpoints for the same tileset (as in
|
|
428
452
|
* MapLibre, which shards requests across them). Navara sources take a single
|
|
@@ -456,15 +480,36 @@ export declare type TileJsonPluginEventMap = {
|
|
|
456
480
|
loaded: (event: TileJsonLoadedEvent) => void;
|
|
457
481
|
};
|
|
458
482
|
|
|
483
|
+
/** Registers a TileJSON document as a `raster-dem` (elevation) source. */
|
|
484
|
+
export declare type TileJsonRasterDemSourceDescription = TileJsonSourceDescriptionBase & {
|
|
485
|
+
type: "raster-dem";
|
|
486
|
+
/** Overrides the document's `tileSize` when set. @defaultValue 512 */
|
|
487
|
+
tileSize?: number;
|
|
488
|
+
/** Overrides the document's `encoding` when set. @defaultValue "mapbox" */
|
|
489
|
+
encoding?: TileJsonDemEncoding;
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
/** Registers a TileJSON document as a `raster-tile` (imagery) source. */
|
|
493
|
+
export declare type TileJsonRasterTileSourceDescription = TileJsonSourceDescriptionBase & {
|
|
494
|
+
type: "raster-tile";
|
|
495
|
+
};
|
|
496
|
+
|
|
459
497
|
/**
|
|
460
498
|
* Describes a TileJSON tile source to register via {@link TileJsonPlugin.addSource}.
|
|
461
499
|
* Mirrors the shape of {@link ThreeView.addSource}, except `url` is the TileJSON
|
|
462
500
|
* document URL (not a tile template) — the plugin fetches it and derives the tile
|
|
463
|
-
* URL, zoom range, and attribution from the document.
|
|
501
|
+
* URL, zoom range, and attribution from the document. Document-derived fields
|
|
502
|
+
* (`minzoom`, `maxzoom`, `scheme` — plus `tileSize` and `encoding` for
|
|
503
|
+
* `raster-dem`) can also be set here to take precedence over the document.
|
|
464
504
|
*/
|
|
465
|
-
export declare type TileJsonSourceDescription =
|
|
466
|
-
|
|
467
|
-
|
|
505
|
+
export declare type TileJsonSourceDescription = TileJsonRasterTileSourceDescription | TileJsonVectorTileSourceDescription | TileJsonRasterDemSourceDescription;
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Fields shared by every {@link TileJsonSourceDescription} variant. Alongside
|
|
509
|
+
* `url` and `id`, it accepts the same optional fields the TileJSON document
|
|
510
|
+
* carries; a field set here takes precedence over the fetched document's value.
|
|
511
|
+
*/
|
|
512
|
+
declare type TileJsonSourceDescriptionBase = {
|
|
468
513
|
/** URL of the TileJSON 3.0.0 document to fetch and expand. */
|
|
469
514
|
url: string;
|
|
470
515
|
/**
|
|
@@ -473,14 +518,25 @@ export declare type TileJsonSourceDescription = {
|
|
|
473
518
|
* handle. When omitted, the engine generates one.
|
|
474
519
|
*/
|
|
475
520
|
id?: string;
|
|
521
|
+
/** Overrides the document's `minzoom` when set. */
|
|
522
|
+
minzoom?: number;
|
|
523
|
+
/** Overrides the document's `maxzoom` when set. */
|
|
524
|
+
maxzoom?: number;
|
|
525
|
+
/** Overrides the document's `scheme` when set. */
|
|
526
|
+
scheme?: "xyz" | "tms";
|
|
476
527
|
};
|
|
477
528
|
|
|
478
529
|
/**
|
|
479
530
|
* Which Navara source type a TileJSON document is materialized into. Declared by
|
|
480
531
|
* the caller because TileJSON has no field that reliably distinguishes raster
|
|
481
|
-
* imagery from vector tiles.
|
|
532
|
+
* imagery from vector or elevation tiles.
|
|
482
533
|
*/
|
|
483
|
-
export declare type TileJsonSourceType = "raster-tile" | "vector-tile";
|
|
534
|
+
export declare type TileJsonSourceType = "raster-tile" | "vector-tile" | "raster-dem";
|
|
535
|
+
|
|
536
|
+
/** Registers a TileJSON document as a `vector-tile` (MVT) source. */
|
|
537
|
+
export declare type TileJsonVectorTileSourceDescription = TileJsonSourceDescriptionBase & {
|
|
538
|
+
type: "vector-tile";
|
|
539
|
+
};
|
|
484
540
|
|
|
485
541
|
declare type UpdateListener = (state: OverlayState) => void;
|
|
486
542
|
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { EventHandler as e,
|
|
2
|
-
import { Matrix4 as
|
|
1
|
+
import { EventHandler as e, MAPBOX_ELEVATION_DECODER as t, Plugin as n, TERRARIUM_ELEVATION_DECODER as r, convertWorldToScreen as i, degreeToRadian as a, eastNorthUpToFixedFrame as o, geodeticToVector3 as s, radianToDegree as c, vector3ToGeodetic as l } from "@navaramap/three";
|
|
2
|
+
import { Matrix4 as u, Vector3 as d } from "three";
|
|
3
3
|
//#region src/PersonViewPlugin.ts
|
|
4
|
-
var
|
|
4
|
+
var f = {
|
|
5
5
|
x: 0,
|
|
6
6
|
y: 0,
|
|
7
7
|
z: 0
|
|
8
|
-
},
|
|
8
|
+
}, p = {
|
|
9
9
|
forward: ["KeyW"],
|
|
10
10
|
backward: ["KeyS"],
|
|
11
11
|
turnLeft: ["KeyA"],
|
|
@@ -19,7 +19,7 @@ var u = {
|
|
|
19
19
|
dash: ["ShiftLeft", "ShiftRight"],
|
|
20
20
|
orbitCamera: ["AltLeft", "AltRight"],
|
|
21
21
|
toggleView: ["KeyV"]
|
|
22
|
-
},
|
|
22
|
+
}, m = {
|
|
23
23
|
allowCameraControl: !1,
|
|
24
24
|
initialView: "tpv",
|
|
25
25
|
moveSpeed: 50,
|
|
@@ -37,7 +37,7 @@ var u = {
|
|
|
37
37
|
startLng: 139.7671,
|
|
38
38
|
startHeight: 500,
|
|
39
39
|
startHeading: Math.PI * 1.3
|
|
40
|
-
},
|
|
40
|
+
}, h = 3, g = 1e3, _ = class extends n {
|
|
41
41
|
view;
|
|
42
42
|
config;
|
|
43
43
|
character;
|
|
@@ -60,21 +60,21 @@ var u = {
|
|
|
60
60
|
movementSuppressed = !1;
|
|
61
61
|
boundKeyDown;
|
|
62
62
|
boundKeyUp;
|
|
63
|
-
_curPos = new
|
|
64
|
-
_east = new
|
|
65
|
-
_north = new
|
|
66
|
-
_worldForward = new
|
|
67
|
-
_offset = new
|
|
68
|
-
_headingMatrix = new
|
|
69
|
-
_characterFrame = new
|
|
63
|
+
_curPos = new d();
|
|
64
|
+
_east = new d();
|
|
65
|
+
_north = new d();
|
|
66
|
+
_worldForward = new d();
|
|
67
|
+
_offset = new d();
|
|
68
|
+
_headingMatrix = new u();
|
|
69
|
+
_characterFrame = new u();
|
|
70
70
|
constructor(e = {}) {
|
|
71
71
|
super();
|
|
72
72
|
let { character: t, keys: n, ...r } = e;
|
|
73
73
|
this.config = {
|
|
74
|
-
...
|
|
74
|
+
...m,
|
|
75
75
|
...r
|
|
76
76
|
}, this.character = t ? this.resolveCharacter(t) : null, this.keys = {
|
|
77
|
-
...
|
|
77
|
+
...p,
|
|
78
78
|
...n
|
|
79
79
|
}, this.keyToAction = this.buildKeyMap(this.keys), this.modelHeading = this.config.startHeading, this.cameraHeading = this.config.startHeading, this.viewMode = this.config.initialView, this.currentAnimState = this.character ? this.character.animation.idleClip : null, this.boundKeyDown = this.onKeyDown.bind(this), this.boundKeyUp = this.onKeyUp.bind(this);
|
|
80
80
|
}
|
|
@@ -92,18 +92,18 @@ var u = {
|
|
|
92
92
|
start() {
|
|
93
93
|
if (!this.view || this.animId != null) return;
|
|
94
94
|
this.animId = -1;
|
|
95
|
-
let { startLat: e, startLng: t, startHeight: n, startHeading:
|
|
96
|
-
lat:
|
|
97
|
-
lng:
|
|
95
|
+
let { startLat: e, startLng: t, startHeight: n, startHeading: r } = this.config, i = s({
|
|
96
|
+
lat: a(e),
|
|
97
|
+
lng: a(t),
|
|
98
98
|
height: n
|
|
99
99
|
});
|
|
100
|
-
if (this.modelHeading =
|
|
101
|
-
let { animation: e, modelRotationOffset: t, modelScale: n, castShadow:
|
|
100
|
+
if (this.modelHeading = r, this.cameraHeading = r, this.character) {
|
|
101
|
+
let { animation: e, modelRotationOffset: t, modelScale: n, castShadow: a, receiveShadow: o } = this.character;
|
|
102
102
|
this.handle = this.view.addMesh({
|
|
103
103
|
gltfModel: {
|
|
104
104
|
url: this.character.modelUrl,
|
|
105
|
-
castShadow:
|
|
106
|
-
receiveShadow:
|
|
105
|
+
castShadow: a,
|
|
106
|
+
receiveShadow: o,
|
|
107
107
|
animationEnabled: !0,
|
|
108
108
|
animationAutoPlay: !0,
|
|
109
109
|
animationActiveClip: e.idleClip,
|
|
@@ -111,7 +111,7 @@ var u = {
|
|
|
111
111
|
animationLoop: !0,
|
|
112
112
|
animationCrossfadeDuration: e.crossfadeDuration
|
|
113
113
|
},
|
|
114
|
-
matrixWorld: this.composeCharacterFrame(
|
|
114
|
+
matrixWorld: this.composeCharacterFrame(i, r),
|
|
115
115
|
rotation: {
|
|
116
116
|
x: t.x,
|
|
117
117
|
y: t.y,
|
|
@@ -130,20 +130,20 @@ var u = {
|
|
|
130
130
|
}
|
|
131
131
|
teleport(e) {
|
|
132
132
|
if (!this.view) return;
|
|
133
|
-
let { lng: t, lat: n, alt:
|
|
133
|
+
let { lng: t, lat: n, alt: r } = e, i = e.heading == null ? this.cameraHeading : e.heading;
|
|
134
134
|
if (this.handle && this.character) {
|
|
135
|
-
let e =
|
|
136
|
-
lat:
|
|
137
|
-
lng:
|
|
138
|
-
height:
|
|
135
|
+
let e = s({
|
|
136
|
+
lat: a(n),
|
|
137
|
+
lng: a(t),
|
|
138
|
+
height: r
|
|
139
139
|
});
|
|
140
|
-
this.handle.update({ matrixWorld: this.composeCharacterFrame(e,
|
|
140
|
+
this.handle.update({ matrixWorld: this.composeCharacterFrame(e, i) });
|
|
141
141
|
}
|
|
142
|
-
this.modelHeading =
|
|
142
|
+
this.modelHeading = i, this.cameraHeading = i, this.placeChaseCamera(n, t, r, i), this.state = {
|
|
143
143
|
lng: t,
|
|
144
144
|
lat: n,
|
|
145
|
-
alt:
|
|
146
|
-
heading:
|
|
145
|
+
alt: r,
|
|
146
|
+
heading: i,
|
|
147
147
|
speed: 0,
|
|
148
148
|
animationState: this.currentAnimState,
|
|
149
149
|
mode: this.viewMode
|
|
@@ -169,16 +169,16 @@ var u = {
|
|
|
169
169
|
}
|
|
170
170
|
setHeading(e) {
|
|
171
171
|
if (!this.view) return;
|
|
172
|
-
let { lat: t, lng: n, alt:
|
|
172
|
+
let { lat: t, lng: n, alt: r } = this.state;
|
|
173
173
|
if (this.modelHeading = e, this.cameraHeading = e, this.handle && this.character) {
|
|
174
|
-
let
|
|
175
|
-
lat:
|
|
176
|
-
lng:
|
|
177
|
-
height:
|
|
174
|
+
let i = s({
|
|
175
|
+
lat: a(t),
|
|
176
|
+
lng: a(n),
|
|
177
|
+
height: r
|
|
178
178
|
});
|
|
179
|
-
this.handle.update({ matrixWorld: this.composeCharacterFrame(
|
|
179
|
+
this.handle.update({ matrixWorld: this.composeCharacterFrame(i, e) });
|
|
180
180
|
}
|
|
181
|
-
this.isFreeCamera() || this.placeChaseCamera(t, n,
|
|
181
|
+
this.isFreeCamera() || this.placeChaseCamera(t, n, r, e), this.state = {
|
|
182
182
|
...this.state,
|
|
183
183
|
heading: e
|
|
184
184
|
}, this.notify();
|
|
@@ -212,10 +212,10 @@ var u = {
|
|
|
212
212
|
modelUrl: e.modelUrl,
|
|
213
213
|
animation: e.animation,
|
|
214
214
|
modelRotationOffset: e.modelRotationOffset ? {
|
|
215
|
-
...
|
|
215
|
+
...f,
|
|
216
216
|
...e.modelRotationOffset
|
|
217
|
-
} :
|
|
218
|
-
modelScale: e.modelScale ??
|
|
217
|
+
} : f,
|
|
218
|
+
modelScale: e.modelScale ?? h,
|
|
219
219
|
hideModelInFpv: e.hideModelInFpv ?? !0,
|
|
220
220
|
castShadow: e.castShadow ?? !1,
|
|
221
221
|
receiveShadow: e.receiveShadow ?? !1
|
|
@@ -246,7 +246,7 @@ var u = {
|
|
|
246
246
|
if (!this.view) return;
|
|
247
247
|
let { cameraDistance: i, cameraPitch: a, fpvPitch: o, fpvHeightOffset: s, fpvForwardOffset: c } = this.config, l = this.viewMode === "fpv", u = n + s;
|
|
248
248
|
if (l) {
|
|
249
|
-
let n =
|
|
249
|
+
let n = g, i = c + g, a = Math.tan(o) * n, s = this.advanceLatLng(e, t, r, i);
|
|
250
250
|
this._offset.set(-Math.sin(r) * n, -Math.cos(r) * n, a), this.view.cameraFollow(!0, {
|
|
251
251
|
lat: s.lat,
|
|
252
252
|
lng: s.lng,
|
|
@@ -262,20 +262,20 @@ var u = {
|
|
|
262
262
|
}, this._offset);
|
|
263
263
|
}
|
|
264
264
|
composeCharacterFrame(e, t) {
|
|
265
|
-
let n =
|
|
265
|
+
let n = o(e);
|
|
266
266
|
return this._headingMatrix.makeRotationZ(-t), this._characterFrame.multiplyMatrices(n, this._headingMatrix);
|
|
267
267
|
}
|
|
268
|
-
advanceLatLng(e, t, n,
|
|
269
|
-
let
|
|
270
|
-
lat:
|
|
271
|
-
lng:
|
|
268
|
+
advanceLatLng(e, t, n, r) {
|
|
269
|
+
let i = s({
|
|
270
|
+
lat: a(e),
|
|
271
|
+
lng: a(t),
|
|
272
272
|
height: 0
|
|
273
|
-
}), u = i
|
|
274
|
-
this._east.setFromMatrixColumn(u, 0).normalize(), this._north.setFromMatrixColumn(u, 1).normalize(), this._worldForward.copy(this._east).multiplyScalar(Math.sin(n)).addScaledVector(this._north, Math.cos(n)),
|
|
275
|
-
let d =
|
|
273
|
+
}), u = o(i);
|
|
274
|
+
this._east.setFromMatrixColumn(u, 0).normalize(), this._north.setFromMatrixColumn(u, 1).normalize(), this._worldForward.copy(this._east).multiplyScalar(Math.sin(n)).addScaledVector(this._north, Math.cos(n)), i.addScaledVector(this._worldForward, r);
|
|
275
|
+
let d = l(i);
|
|
276
276
|
return {
|
|
277
|
-
lat:
|
|
278
|
-
lng:
|
|
277
|
+
lat: c(d.lat),
|
|
278
|
+
lng: c(d.lng)
|
|
279
279
|
};
|
|
280
280
|
}
|
|
281
281
|
notify() {
|
|
@@ -323,27 +323,27 @@ var u = {
|
|
|
323
323
|
this.animId = requestAnimationFrame(this.tick);
|
|
324
324
|
let t = Math.min((e - this.lastTime) / 1e3, .1);
|
|
325
325
|
this.lastTime = e;
|
|
326
|
-
let n = 0,
|
|
327
|
-
this.heldActions.has("forward") && (
|
|
326
|
+
let n = 0, r = 0, i = 0;
|
|
327
|
+
this.heldActions.has("forward") && (r += 1), this.heldActions.has("backward") && --r, this.heldActions.has("turnLeft") && --n, this.heldActions.has("turnRight") && (n += 1), this.heldActions.has("ascend") && (i += 1), this.heldActions.has("descend") && --i;
|
|
328
328
|
let { moveSpeed: u, rotationSpeed: d, altSpeed: f, minAlt: p, maxAlt: m, cameraLerpSpeed: h } = this.config;
|
|
329
|
-
n !== 0 && (this.modelHeading +=
|
|
329
|
+
n !== 0 && (this.modelHeading += a(d * n));
|
|
330
330
|
let g = this._curPos;
|
|
331
331
|
if (this.handle && this.modelRef) {
|
|
332
332
|
let e = this.modelRef.getWorldPosition();
|
|
333
333
|
if (!e) return;
|
|
334
334
|
g.copy(e);
|
|
335
|
-
} else g.copy(
|
|
336
|
-
lat:
|
|
337
|
-
lng:
|
|
335
|
+
} else g.copy(s({
|
|
336
|
+
lat: a(this.state.lat),
|
|
337
|
+
lng: a(this.state.lng),
|
|
338
338
|
height: this.state.alt
|
|
339
339
|
}));
|
|
340
|
-
let _ =
|
|
340
|
+
let _ = o(g);
|
|
341
341
|
this._east.setFromMatrixColumn(_, 0).normalize(), this._north.setFromMatrixColumn(_, 1).normalize();
|
|
342
|
-
let v =
|
|
343
|
-
this._worldForward.copy(this._east).multiplyScalar(Math.sin(this.modelHeading)).addScaledVector(this._north, Math.cos(this.modelHeading)),
|
|
344
|
-
let y =
|
|
342
|
+
let v = l(g);
|
|
343
|
+
this._worldForward.copy(this._east).multiplyScalar(Math.sin(this.modelHeading)).addScaledVector(this._north, Math.cos(this.modelHeading)), r !== 0 && g.addScaledVector(this._worldForward, u * this.dashMultiplier * t * r);
|
|
344
|
+
let y = l(g), b = v.height + i * f * t, x = Math.max(p, Math.min(m, b)), S = c(y.lat), C = c(y.lng), w = x;
|
|
345
345
|
if (this.handle && this.character) {
|
|
346
|
-
let e =
|
|
346
|
+
let e = s({
|
|
347
347
|
lat: y.lat,
|
|
348
348
|
lng: y.lng,
|
|
349
349
|
height: x
|
|
@@ -364,7 +364,7 @@ var u = {
|
|
|
364
364
|
height: w + this.config.fpvHeightOffset
|
|
365
365
|
});
|
|
366
366
|
else this.placeChaseCamera(S, C, w, this.cameraHeading);
|
|
367
|
-
let E =
|
|
367
|
+
let E = r !== 0 || n !== 0 || i !== 0, D = E && this.dashMultiplier > 1, O = this.currentAnimState;
|
|
368
368
|
if (this.character && this.modelRef) {
|
|
369
369
|
let { animation: e } = this.character, t = e.walkClip ?? e.idleClip, n;
|
|
370
370
|
n = D ? e.dashClip : E ? t : e.idleClip, n !== this.currentAnimState && (this.modelRef.crossFadeAnimation(this.currentAnimState ?? "", n, e.crossfadeDuration), this.currentAnimState = n), O = n;
|
|
@@ -386,10 +386,10 @@ var u = {
|
|
|
386
386
|
};
|
|
387
387
|
//#endregion
|
|
388
388
|
//#region src/OverlayPlugin.ts
|
|
389
|
-
function
|
|
389
|
+
function v(e, t, n) {
|
|
390
390
|
e.style.transform = `translate(${t}px, ${n}px)`, e.style.willChange = "transform";
|
|
391
391
|
}
|
|
392
|
-
var
|
|
392
|
+
var y = 1e5, b = class extends n {
|
|
393
393
|
view;
|
|
394
394
|
maxDistance;
|
|
395
395
|
positions = [];
|
|
@@ -397,7 +397,7 @@ var _ = 1e5, v = class extends t {
|
|
|
397
397
|
listeners = /* @__PURE__ */ new Set();
|
|
398
398
|
boundHandler;
|
|
399
399
|
constructor(e) {
|
|
400
|
-
super(), this.maxDistance = e?.maxDistance ??
|
|
400
|
+
super(), this.maxDistance = e?.maxDistance ?? y, this.boundHandler = this.handlePreRender.bind(this);
|
|
401
401
|
}
|
|
402
402
|
async init(e, t) {
|
|
403
403
|
this.view = e, e.on("preRender", this.boundHandler);
|
|
@@ -411,47 +411,47 @@ var _ = 1e5, v = class extends t {
|
|
|
411
411
|
dispose() {
|
|
412
412
|
this.view && this.view.off("preRender", this.boundHandler), this.listeners.clear(), this.positions = [], this.projected.clear();
|
|
413
413
|
}
|
|
414
|
-
worldToScreen(e, t,
|
|
414
|
+
worldToScreen(e, t, n) {
|
|
415
415
|
if (!this.view) return null;
|
|
416
|
-
let
|
|
417
|
-
lng:
|
|
418
|
-
lat:
|
|
419
|
-
height:
|
|
420
|
-
}),
|
|
416
|
+
let r = s({
|
|
417
|
+
lng: a(e),
|
|
418
|
+
lat: a(t),
|
|
419
|
+
height: n
|
|
420
|
+
}), o = i({
|
|
421
421
|
width: this.view.screenSize.x,
|
|
422
422
|
height: this.view.screenSize.y,
|
|
423
423
|
pixelRatio: this.view.pixelRatio
|
|
424
|
-
}, this.view.camera.raw,
|
|
425
|
-
return
|
|
426
|
-
x:
|
|
427
|
-
y:
|
|
424
|
+
}, this.view.camera.raw, r);
|
|
425
|
+
return o ? {
|
|
426
|
+
x: o.x,
|
|
427
|
+
y: o.y
|
|
428
428
|
} : null;
|
|
429
429
|
}
|
|
430
430
|
handlePreRender() {
|
|
431
431
|
if (!this.view) return;
|
|
432
|
-
let e = this.view.camera.positionGeographic, t =
|
|
433
|
-
lng:
|
|
434
|
-
lat:
|
|
432
|
+
let e = this.view.camera.positionGeographic, t = s({
|
|
433
|
+
lng: a(e.lng),
|
|
434
|
+
lat: a(e.lat),
|
|
435
435
|
height: e.height
|
|
436
436
|
}), n = /* @__PURE__ */ new Map();
|
|
437
437
|
for (let e of this.positions) {
|
|
438
|
-
let
|
|
439
|
-
lng:
|
|
440
|
-
lat:
|
|
438
|
+
let r = s({
|
|
439
|
+
lng: a(e.lng),
|
|
440
|
+
lat: a(e.lat),
|
|
441
441
|
height: e.alt
|
|
442
|
-
}),
|
|
443
|
-
if (
|
|
444
|
-
let
|
|
445
|
-
|
|
446
|
-
...
|
|
447
|
-
distance:
|
|
442
|
+
}), i = t.distanceTo(r);
|
|
443
|
+
if (i > this.maxDistance) continue;
|
|
444
|
+
let o = this.worldToScreen(e.lng, e.lat, e.alt);
|
|
445
|
+
o && n.set(e.id, {
|
|
446
|
+
...o,
|
|
447
|
+
distance: i
|
|
448
448
|
});
|
|
449
449
|
}
|
|
450
450
|
this.projected = n;
|
|
451
|
-
let
|
|
452
|
-
for (let e of this.listeners) e(
|
|
451
|
+
let r = { projected: this.projected };
|
|
452
|
+
for (let e of this.listeners) e(r);
|
|
453
453
|
}
|
|
454
|
-
},
|
|
454
|
+
}, x = "https://api.cesium.com/v1/assets", S = class extends n {
|
|
455
455
|
view;
|
|
456
456
|
config;
|
|
457
457
|
endpoint;
|
|
@@ -460,7 +460,7 @@ var _ = 1e5, v = class extends t {
|
|
|
460
460
|
}
|
|
461
461
|
async init(e, t) {
|
|
462
462
|
this.view = e;
|
|
463
|
-
let { assetId: n, accessToken: r, endpoint: i =
|
|
463
|
+
let { assetId: n, accessToken: r, endpoint: i = x } = this.config, a = `${i}/${encodeURIComponent(String(n))}/endpoint?access_token=${encodeURIComponent(r)}`, o = await fetch(a);
|
|
464
464
|
if (!o.ok) throw Error(`CesiumIonPlugin: failed to fetch asset ${n} endpoint (${o.status} ${o.statusText})`);
|
|
465
465
|
let s = await o.json();
|
|
466
466
|
this.endpoint = {
|
|
@@ -489,12 +489,19 @@ var _ = 1e5, v = class extends t {
|
|
|
489
489
|
});
|
|
490
490
|
return l.once("deleted", () => c.delete()), l;
|
|
491
491
|
}
|
|
492
|
-
},
|
|
493
|
-
function
|
|
494
|
-
if (typeof e.tilejson != "string" || !
|
|
492
|
+
}, C = /^3\.\d+\.\d+$/, w = 512, T = "mapbox";
|
|
493
|
+
function E(e) {
|
|
494
|
+
if (typeof e.tilejson != "string" || !C.test(e.tilejson)) throw Error(`TileJsonPlugin: invalid or missing "tilejson" version field (got ${JSON.stringify(e.tilejson)}).`);
|
|
495
495
|
if (!Array.isArray(e.tiles) || e.tiles.length === 0) throw Error("TileJsonPlugin: \"tiles\" must be a non-empty array of URL templates.");
|
|
496
496
|
}
|
|
497
|
-
|
|
497
|
+
function D(e) {
|
|
498
|
+
switch (e) {
|
|
499
|
+
case "mapbox": return t();
|
|
500
|
+
case "terrarium": return r();
|
|
501
|
+
default: throw Error(`TileJsonPlugin: unsupported raster-dem encoding ${JSON.stringify(e)}. Supported encodings: "mapbox", "terrarium".`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
var O = class extends n {
|
|
498
505
|
view;
|
|
499
506
|
events = new e();
|
|
500
507
|
credits = [];
|
|
@@ -514,27 +521,34 @@ var C = class extends t {
|
|
|
514
521
|
let t = this.view;
|
|
515
522
|
if (!t) throw Error("TileJsonPlugin: addSource() must be called after view.init().");
|
|
516
523
|
let n = await this.fetchTileJson(e.url);
|
|
517
|
-
|
|
524
|
+
E(n);
|
|
518
525
|
let [r, ...i] = n.tiles;
|
|
519
526
|
i.length > 0 && console.warn(`TileJsonPlugin: TileJSON at ${e.url} lists ${n.tiles.length} mirror tile endpoints; Navara sources use a single URL, so only the first is used.`);
|
|
520
|
-
let
|
|
521
|
-
type: "raster-tile",
|
|
527
|
+
let a = e.minzoom ?? n.minzoom, o = e.maxzoom ?? n.maxzoom, s = e.scheme ?? n.scheme, c = e.id, l = {
|
|
522
528
|
url: r,
|
|
523
529
|
...c === void 0 ? {} : { id: c },
|
|
530
|
+
...o === void 0 ? {} : { maxZoom: o }
|
|
531
|
+
}, u = {
|
|
532
|
+
...l,
|
|
524
533
|
...a === void 0 ? {} : { minZoom: a },
|
|
525
|
-
...o === void 0 ? {} : { maxZoom: o },
|
|
526
534
|
...s === "tms" ? { tms: !0 } : {}
|
|
535
|
+
}, d;
|
|
536
|
+
return d = e.type === "raster-tile" ? t.addSource({
|
|
537
|
+
type: "raster-tile",
|
|
538
|
+
...u
|
|
539
|
+
}) : e.type === "raster-dem" ? t.addSource({
|
|
540
|
+
type: "raster-dem",
|
|
541
|
+
...u,
|
|
542
|
+
tileSize: e.tileSize ?? n.tileSize ?? w,
|
|
543
|
+
elevationDecoder: D(e.encoding ?? n.encoding ?? T)
|
|
527
544
|
}) : t.addSource({
|
|
528
545
|
type: "vector-tile",
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
});
|
|
533
|
-
return n.attribution && this.addCredit(n.attribution), this.events.emit("loaded", {
|
|
534
|
-
source: l,
|
|
546
|
+
...l
|
|
547
|
+
}), n.attribution && this.addCredit(n.attribution), this.events.emit("loaded", {
|
|
548
|
+
source: d,
|
|
535
549
|
tilejson: n,
|
|
536
550
|
attribution: n.attribution
|
|
537
|
-
}),
|
|
551
|
+
}), d;
|
|
538
552
|
}
|
|
539
553
|
dispose() {
|
|
540
554
|
let e = this.view?.attribution;
|
|
@@ -550,4 +564,4 @@ var C = class extends t {
|
|
|
550
564
|
}
|
|
551
565
|
};
|
|
552
566
|
//#endregion
|
|
553
|
-
export {
|
|
567
|
+
export { S as CesiumIonPlugin, b as OverlayPlugin, _ as PersonViewPlugin, O as TileJsonPlugin, v as moveOverlayElement };
|
package/dist/index.umd.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("@navaramap/three"),require("three")):typeof define==`function`&&define.amd?define([`exports`,`@navaramap/three`,`three`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.NavaraThreePlugins={},e._navaramap_three,e.three))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var s=o(t),l=0,u=s.length,d;l<u;l++)d=s[l],!c.call(e,d)&&d!==n&&i(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=a(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:r(s(e)),l(t||!e||!e.__esModule?i(n,`default`,{value:e,enumerable:!0}):n,e)))(t,1);var u={x:0,y:0,z:0},d={forward:[`KeyW`],backward:[`KeyS`],turnLeft:[`KeyA`],turnRight:[`KeyD`],ascend:[`ArrowUp`,`Space`],descend:[`ArrowDown`,`ControlLeft`,`ControlRight`],dash:[`ShiftLeft`,`ShiftRight`],orbitCamera:[`AltLeft`,`AltRight`],toggleView:[`KeyV`]},f={allowCameraControl:!1,initialView:`tpv`,moveSpeed:50,rotationSpeed:3,altSpeed:30,minAlt:50,maxAlt:5e3,cameraDistance:50,cameraPitch:0,cameraLerpSpeed:3,fpvForwardOffset:0,fpvHeightOffset:1,fpvPitch:0,startLat:35.6812,startLng:139.7671,startHeight:500,startHeading:Math.PI*1.3},p=3,m=1e3,h=class extends t.Plugin{view;config;character;keys;keyToAction;handle=null;modelRef=null;animId=null;lastTime=0;state;listeners=new Set;heldActions=new Set;dashMultiplier=1;orbitKeyHeld=!1;orbitLatched=!1;currentAnimState;modelHeading;cameraHeading;viewMode;movementSuppressed=!1;boundKeyDown;boundKeyUp;_curPos=new n.Vector3;_east=new n.Vector3;_north=new n.Vector3;_worldForward=new n.Vector3;_offset=new n.Vector3;_headingMatrix=new n.Matrix4;_characterFrame=new n.Matrix4;constructor(e={}){super();let{character:t,keys:n,...r}=e;this.config={...f,...r},this.character=t?this.resolveCharacter(t):null,this.keys={...d,...n},this.keyToAction=this.buildKeyMap(this.keys),this.modelHeading=this.config.startHeading,this.cameraHeading=this.config.startHeading,this.viewMode=this.config.initialView,this.currentAnimState=this.character?this.character.animation.idleClip:null,this.boundKeyDown=this.onKeyDown.bind(this),this.boundKeyUp=this.onKeyUp.bind(this)}async init(e,t){this.view=e,this.state={lng:this.config.startLng,lat:this.config.startLat,alt:this.config.startHeight,heading:this.config.startHeading,speed:0,animationState:this.currentAnimState,mode:this.viewMode}}start(){if(!this.view||this.animId!=null)return;this.animId=-1;let{startLat:e,startLng:n,startHeight:r,startHeading:i}=this.config,a=(0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(e),lng:(0,t.degreeToRadian)(n),height:r});if(this.modelHeading=i,this.cameraHeading=i,this.character){let{animation:e,modelRotationOffset:t,modelScale:n,castShadow:r,receiveShadow:o}=this.character;this.handle=this.view.addMesh({gltfModel:{url:this.character.modelUrl,castShadow:r,receiveShadow:o,animationEnabled:!0,animationAutoPlay:!0,animationActiveClip:e.idleClip,animationSpeed:e.speed,animationLoop:!0,animationCrossfadeDuration:e.crossfadeDuration},matrixWorld:this.composeCharacterFrame(a,i),rotation:{x:t.x,y:t.y,z:t.z},scale:{x:n,y:n,z:n}}),this.modelRef=this.handle.ref,this.modelRef.on(`load`,()=>{this.view&&(this.lastTime=performance.now(),this.animId=requestAnimationFrame(this.tick))})}else this.lastTime=performance.now(),this.animId=requestAnimationFrame(this.tick);this.applyInitialCamera(),this.applyModelVisibility(),document.addEventListener(`keydown`,this.boundKeyDown),document.addEventListener(`keyup`,this.boundKeyUp)}teleport(e){if(!this.view)return;let{lng:n,lat:r,alt:i}=e,a=e.heading==null?this.cameraHeading:e.heading;if(this.handle&&this.character){let e=(0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(r),lng:(0,t.degreeToRadian)(n),height:i});this.handle.update({matrixWorld:this.composeCharacterFrame(e,a)})}this.modelHeading=a,this.cameraHeading=a,this.placeChaseCamera(r,n,i,a),this.state={lng:n,lat:r,alt:i,heading:a,speed:0,animationState:this.currentAnimState,mode:this.viewMode},this.notify()}getState(){return this.state}onStateChange(e){return this.listeners.add(e),()=>this.listeners.delete(e)}setViewMode(e){this.viewMode!==e&&(this.viewMode=e,this.state={...this.state,mode:e},this.applyModelVisibility(),this.placeChaseCamera(this.state.lat,this.state.lng,this.state.alt,this.cameraHeading),this.notify())}toggleViewMode(){this.setViewMode(this.viewMode===`tpv`?`fpv`:`tpv`)}setAllowCameraControl(e){this.config.allowCameraControl=e}setHeading(e){if(!this.view)return;let{lat:n,lng:r,alt:i}=this.state;if(this.modelHeading=e,this.cameraHeading=e,this.handle&&this.character){let a=(0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(n),lng:(0,t.degreeToRadian)(r),height:i});this.handle.update({matrixWorld:this.composeCharacterFrame(a,e)})}this.isFreeCamera()||this.placeChaseCamera(n,r,i,e),this.state={...this.state,heading:e},this.notify()}getHeading(){return this.state.heading}setCameraPitch(e){this.config.cameraPitch=e,this.isFreeCamera()||this.placeChaseCamera(this.state.lat,this.state.lng,this.state.alt,this.cameraHeading)}getCameraPitch(){return this.config.cameraPitch}setFpvPitch(e){this.config.fpvPitch=e,this.isFreeCamera()||this.placeChaseCamera(this.state.lat,this.state.lng,this.state.alt,this.cameraHeading)}getFpvPitch(){return this.config.fpvPitch}setFpvHeightOffset(e){this.config.fpvHeightOffset=e,this.isFreeCamera()||this.placeChaseCamera(this.state.lat,this.state.lng,this.state.alt,this.cameraHeading)}getFpvHeightOffset(){return this.config.fpvHeightOffset}dispose(){this.animId!=null&&cancelAnimationFrame(this.animId),this.animId=null,document.removeEventListener(`keydown`,this.boundKeyDown),document.removeEventListener(`keyup`,this.boundKeyUp),this.handle&&=(this.handle.delete(),null),this.modelRef=null,this.heldActions.clear(),this.listeners.clear(),this.orbitKeyHeld=!1,this.orbitLatched=!1,this.dashMultiplier=1,this.view=void 0}resolveCharacter(e){return{modelUrl:e.modelUrl,animation:e.animation,modelRotationOffset:e.modelRotationOffset?{...u,...e.modelRotationOffset}:u,modelScale:e.modelScale??p,hideModelInFpv:e.hideModelInFpv??!0,castShadow:e.castShadow??!1,receiveShadow:e.receiveShadow??!1}}buildKeyMap(e){let t=new Map,n=(e,n)=>{for(let r of e)t.set(r,n)};return n(e.forward,`forward`),n(e.backward,`backward`),n(e.turnLeft,`turnLeft`),n(e.turnRight,`turnRight`),n(e.ascend,`ascend`),n(e.descend,`descend`),n(e.dash,`dash`),n(e.orbitCamera,`orbitCamera`),n(e.toggleView,`toggleView`),t}isFreeCamera(){return this.config.allowCameraControl||this.orbitKeyHeld||this.orbitLatched}isMovementAction(e){return e===`forward`||e===`backward`||e===`turnLeft`||e===`turnRight`||e===`ascend`||e===`descend`}applyModelVisibility(){if(!this.handle||!this.character)return;let e=this.viewMode===`fpv`&&this.character.hideModelInFpv;this.handle.visible=!e}applyInitialCamera(){let{startLat:e,startLng:t,startHeight:n,startHeading:r}=this.config;this.placeChaseCamera(e,t,n,r)}placeChaseCamera(e,t,n,r){if(!this.view)return;let{cameraDistance:i,cameraPitch:a,fpvPitch:o,fpvHeightOffset:s,fpvForwardOffset:c}=this.config,l=this.viewMode===`fpv`,u=n+s;if(l){let n=m,i=c+m,a=Math.tan(o)*n,s=this.advanceLatLng(e,t,r,i);this._offset.set(-Math.sin(r)*n,-Math.cos(r)*n,a),this.view.cameraFollow(!0,{lat:s.lat,lng:s.lng,height:u-a},this._offset);return}let d=i*Math.cos(a),f=i*Math.sin(a);this._offset.set(-Math.sin(r)*d,-Math.cos(r)*d,f),this.view.cameraFollow(!0,{lat:e,lng:t,height:u},this._offset)}composeCharacterFrame(e,n){let r=(0,t.eastNorthUpToFixedFrame)(e);return this._headingMatrix.makeRotationZ(-n),this._characterFrame.multiplyMatrices(r,this._headingMatrix)}advanceLatLng(e,n,r,i){let a=(0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(e),lng:(0,t.degreeToRadian)(n),height:0}),o=(0,t.eastNorthUpToFixedFrame)(a);this._east.setFromMatrixColumn(o,0).normalize(),this._north.setFromMatrixColumn(o,1).normalize(),this._worldForward.copy(this._east).multiplyScalar(Math.sin(r)).addScaledVector(this._north,Math.cos(r)),a.addScaledVector(this._worldForward,i);let s=(0,t.vector3ToGeodetic)(a);return{lat:(0,t.radianToDegree)(s.lat),lng:(0,t.radianToDegree)(s.lng)}}notify(){for(let e of this.listeners)e(this.state)}onKeyDown(e){let t=e.target;if(t.tagName===`INPUT`||t.tagName===`TEXTAREA`||t.isContentEditable)return;let n=this.keyToAction.get(e.code);if(n!==void 0){if(n===`toggleView`){if(e.repeat)return;this.toggleViewMode();return}if(n===`orbitCamera`){this.orbitKeyHeld=!0,this.orbitLatched=!0;return}if(!this.movementSuppressed){if(n===`dash`){this.dashMultiplier=2.5;return}this.isMovementAction(n)&&(this.orbitLatched=!1),this.heldActions.add(n)}}}onKeyUp(e){let t=this.keyToAction.get(e.code);if(t!==void 0&&t!==`toggleView`){if(t===`orbitCamera`){this.orbitKeyHeld=!1;return}if(t===`dash`){this.dashMultiplier=1;return}this.heldActions.delete(t)}}tick=e=>{if(!this.view)return;this.animId=requestAnimationFrame(this.tick);let n=Math.min((e-this.lastTime)/1e3,.1);this.lastTime=e;let r=0,i=0,a=0;this.heldActions.has(`forward`)&&(i+=1),this.heldActions.has(`backward`)&&--i,this.heldActions.has(`turnLeft`)&&--r,this.heldActions.has(`turnRight`)&&(r+=1),this.heldActions.has(`ascend`)&&(a+=1),this.heldActions.has(`descend`)&&--a;let{moveSpeed:o,rotationSpeed:s,altSpeed:c,minAlt:l,maxAlt:u,cameraLerpSpeed:d}=this.config;r!==0&&(this.modelHeading+=(0,t.degreeToRadian)(s*r));let f=this._curPos;if(this.handle&&this.modelRef){let e=this.modelRef.getWorldPosition();if(!e)return;f.copy(e)}else f.copy((0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(this.state.lat),lng:(0,t.degreeToRadian)(this.state.lng),height:this.state.alt}));let p=(0,t.eastNorthUpToFixedFrame)(f);this._east.setFromMatrixColumn(p,0).normalize(),this._north.setFromMatrixColumn(p,1).normalize();let m=(0,t.vector3ToGeodetic)(f);this._worldForward.copy(this._east).multiplyScalar(Math.sin(this.modelHeading)).addScaledVector(this._north,Math.cos(this.modelHeading)),i!==0&&f.addScaledVector(this._worldForward,o*this.dashMultiplier*n*i);let h=(0,t.vector3ToGeodetic)(f),g=m.height+a*c*n,_=Math.max(l,Math.min(u,g)),v=(0,t.radianToDegree)(h.lat),y=(0,t.radianToDegree)(h.lng),b=_;if(this.handle&&this.character){let e=(0,t.geodeticToVector3)({lat:h.lat,lng:h.lng,height:_});this.handle.update({matrixWorld:this.composeCharacterFrame(e,this.modelHeading)})}let x=this.modelHeading-this.cameraHeading;if(x-=Math.round(x/(Math.PI*2))*(Math.PI*2),this.cameraHeading+=x*Math.min(n*d,1),this.isFreeCamera())if(this.viewMode===`fpv`){let e=this.advanceLatLng(v,y,this.cameraHeading,this.config.fpvForwardOffset);this.view.cameraFreeLook(!0,{lat:e.lat,lng:e.lng,height:b+this.config.fpvHeightOffset})}else this.view.cameraFollow(!0,{lat:v,lng:y,height:b+this.config.fpvHeightOffset});else this.placeChaseCamera(v,y,b,this.cameraHeading);let S=i!==0||r!==0||a!==0,C=S&&this.dashMultiplier>1,w=this.currentAnimState;if(this.character&&this.modelRef){let{animation:e}=this.character,t=e.walkClip??e.idleClip,n;n=C?e.dashClip:S?t:e.idleClip,n!==this.currentAnimState&&(this.modelRef.crossFadeAnimation(this.currentAnimState??``,n,e.crossfadeDuration),this.currentAnimState=n),w=n}let T={lng:y,lat:v,alt:b,heading:this.modelHeading,speed:o*this.dashMultiplier,animationState:w,mode:this.viewMode};this.hasStateChanged(this.state,T)&&(this.state=T,this.notify())};hasStateChanged(e,t){return e.lng!==t.lng||e.lat!==t.lat||e.alt!==t.alt||e.heading!==t.heading||e.speed!==t.speed||e.animationState!==t.animationState||e.mode!==t.mode}};function g(e,t,n){e.style.transform=`translate(${t}px, ${n}px)`,e.style.willChange=`transform`}var _=1e5,v=class extends t.Plugin{view;maxDistance;positions=[];projected=new Map;listeners=new Set;boundHandler;constructor(e){super(),this.maxDistance=e?.maxDistance??_,this.boundHandler=this.handlePreRender.bind(this)}async init(e,t){this.view=e,e.on(`preRender`,this.boundHandler)}setPositions(e){this.positions=e}onUpdate(e){return this.listeners.add(e),()=>this.listeners.delete(e)}dispose(){this.view&&this.view.off(`preRender`,this.boundHandler),this.listeners.clear(),this.positions=[],this.projected.clear()}worldToScreen(e,n,r){if(!this.view)return null;let i=(0,t.geodeticToVector3)({lng:(0,t.degreeToRadian)(e),lat:(0,t.degreeToRadian)(n),height:r}),a=(0,t.convertWorldToScreen)({width:this.view.screenSize.x,height:this.view.screenSize.y,pixelRatio:this.view.pixelRatio},this.view.camera.raw,i);return a?{x:a.x,y:a.y}:null}handlePreRender(){if(!this.view)return;let e=this.view.camera.positionGeographic,n=(0,t.geodeticToVector3)({lng:(0,t.degreeToRadian)(e.lng),lat:(0,t.degreeToRadian)(e.lat),height:e.height}),r=new Map;for(let e of this.positions){let i=(0,t.geodeticToVector3)({lng:(0,t.degreeToRadian)(e.lng),lat:(0,t.degreeToRadian)(e.lat),height:e.alt}),a=n.distanceTo(i);if(a>this.maxDistance)continue;let o=this.worldToScreen(e.lng,e.lat,e.alt);o&&r.set(e.id,{...o,distance:a})}this.projected=r;let i={projected:this.projected};for(let e of this.listeners)e(i)}},y=`https://api.cesium.com/v1/assets`,b=class extends t.Plugin{view;config;endpoint;constructor(e){super(),this.config=e}async init(e,t){this.view=e;let{assetId:n,accessToken:r,endpoint:i=y}=this.config,a=`${i}/${encodeURIComponent(String(n))}/endpoint?access_token=${encodeURIComponent(r)}`,o=await fetch(a);if(!o.ok)throw Error(`CesiumIonPlugin: failed to fetch asset ${n} endpoint (${o.status} ${o.statusText})`);let s=await o.json();this.endpoint={url:s.url,accessToken:s.accessToken}}addTerrain(e={}){if(!this.view||!this.endpoint)throw Error(`CesiumIonPlugin: addTerrain() must be called after view.init().`);let{castShadow:t,receiveShadow:n,show:r,showBoundingBox:i,skirt:a,skirtExaggeration:o,...s}=e,c=this.view.addSource({type:`quantized-mesh`,url:`${this.endpoint.url}{z}/{x}/{y}.terrain`,token:this.endpoint.accessToken,...s}),l=this.view.addLayer({type:`terrain`,source:c,terrain:{castShadow:t,receiveShadow:n,show:r,showBoundingBox:i,skirt:a,skirtExaggeration:o}});return l.once(`deleted`,()=>c.delete()),l}},x=/^3\.\d+\.\d+$/;function S(e){if(typeof e.tilejson!=`string`||!x.test(e.tilejson))throw Error(`TileJsonPlugin: invalid or missing "tilejson" version field (got ${JSON.stringify(e.tilejson)}).`);if(!Array.isArray(e.tiles)||e.tiles.length===0)throw Error(`TileJsonPlugin: "tiles" must be a non-empty array of URL templates.`)}var C=class extends t.Plugin{view;events=new t.EventHandler;credits=[];async init(e,t){this.view=e}on(e,t){this.events.on(e,t)}once(e,t){this.events.once(e,t)}off(e,t){this.events.off(e,t)}async addSource(e){let t=this.view;if(!t)throw Error(`TileJsonPlugin: addSource() must be called after view.init().`);let n=await this.fetchTileJson(e.url);S(n);let[r,...i]=n.tiles;i.length>0&&console.warn(`TileJsonPlugin: TileJSON at ${e.url} lists ${n.tiles.length} mirror tile endpoints; Navara sources use a single URL, so only the first is used.`);let{minzoom:a,maxzoom:o,scheme:s}=n,c=e.id,l=e.type===`raster-tile`?t.addSource({type:`raster-tile`,url:r,...c===void 0?{}:{id:c},...a===void 0?{}:{minZoom:a},...o===void 0?{}:{maxZoom:o},...s===`tms`?{tms:!0}:{}}):t.addSource({type:`vector-tile`,url:r,...c===void 0?{}:{id:c},...o===void 0?{}:{maxZoom:o}});return n.attribution&&this.addCredit(n.attribution),this.events.emit(`loaded`,{source:l,tilejson:n,attribution:n.attribution}),l}dispose(){let e=this.view?.attribution;e&&this.credits.length>0&&e.remove(this.credits.map(e=>({attributionHtml:e}))),this.credits.length=0,this.events.clear(`loaded`),this.view=void 0}async fetchTileJson(e){let t=await fetch(e);if(!t.ok)throw Error(`TileJsonPlugin: failed to fetch TileJSON from ${e} (${t.status} ${t.statusText})`);return await t.json()}addCredit(e){this.credits.includes(e)||(this.credits.push(e),this.view?.attribution?.add([{attributionHtml:e}]))}};e.CesiumIonPlugin=b,e.OverlayPlugin=v,e.PersonViewPlugin=h,e.TileJsonPlugin=C,e.moveOverlayElement=g});
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("@navaramap/three"),require("three")):typeof define==`function`&&define.amd?define([`exports`,`@navaramap/three`,`three`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.NavaraThreePlugins={},e._navaramap_three,e.three))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var s=o(t),l=0,u=s.length,d;l<u;l++)d=s[l],!c.call(e,d)&&d!==n&&i(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=a(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:r(s(e)),l(t||!e||!e.__esModule?i(n,`default`,{value:e,enumerable:!0}):n,e)))(t,1);var u={x:0,y:0,z:0},d={forward:[`KeyW`],backward:[`KeyS`],turnLeft:[`KeyA`],turnRight:[`KeyD`],ascend:[`ArrowUp`,`Space`],descend:[`ArrowDown`,`ControlLeft`,`ControlRight`],dash:[`ShiftLeft`,`ShiftRight`],orbitCamera:[`AltLeft`,`AltRight`],toggleView:[`KeyV`]},f={allowCameraControl:!1,initialView:`tpv`,moveSpeed:50,rotationSpeed:3,altSpeed:30,minAlt:50,maxAlt:5e3,cameraDistance:50,cameraPitch:0,cameraLerpSpeed:3,fpvForwardOffset:0,fpvHeightOffset:1,fpvPitch:0,startLat:35.6812,startLng:139.7671,startHeight:500,startHeading:Math.PI*1.3},p=3,m=1e3,h=class extends t.Plugin{view;config;character;keys;keyToAction;handle=null;modelRef=null;animId=null;lastTime=0;state;listeners=new Set;heldActions=new Set;dashMultiplier=1;orbitKeyHeld=!1;orbitLatched=!1;currentAnimState;modelHeading;cameraHeading;viewMode;movementSuppressed=!1;boundKeyDown;boundKeyUp;_curPos=new n.Vector3;_east=new n.Vector3;_north=new n.Vector3;_worldForward=new n.Vector3;_offset=new n.Vector3;_headingMatrix=new n.Matrix4;_characterFrame=new n.Matrix4;constructor(e={}){super();let{character:t,keys:n,...r}=e;this.config={...f,...r},this.character=t?this.resolveCharacter(t):null,this.keys={...d,...n},this.keyToAction=this.buildKeyMap(this.keys),this.modelHeading=this.config.startHeading,this.cameraHeading=this.config.startHeading,this.viewMode=this.config.initialView,this.currentAnimState=this.character?this.character.animation.idleClip:null,this.boundKeyDown=this.onKeyDown.bind(this),this.boundKeyUp=this.onKeyUp.bind(this)}async init(e,t){this.view=e,this.state={lng:this.config.startLng,lat:this.config.startLat,alt:this.config.startHeight,heading:this.config.startHeading,speed:0,animationState:this.currentAnimState,mode:this.viewMode}}start(){if(!this.view||this.animId!=null)return;this.animId=-1;let{startLat:e,startLng:n,startHeight:r,startHeading:i}=this.config,a=(0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(e),lng:(0,t.degreeToRadian)(n),height:r});if(this.modelHeading=i,this.cameraHeading=i,this.character){let{animation:e,modelRotationOffset:t,modelScale:n,castShadow:r,receiveShadow:o}=this.character;this.handle=this.view.addMesh({gltfModel:{url:this.character.modelUrl,castShadow:r,receiveShadow:o,animationEnabled:!0,animationAutoPlay:!0,animationActiveClip:e.idleClip,animationSpeed:e.speed,animationLoop:!0,animationCrossfadeDuration:e.crossfadeDuration},matrixWorld:this.composeCharacterFrame(a,i),rotation:{x:t.x,y:t.y,z:t.z},scale:{x:n,y:n,z:n}}),this.modelRef=this.handle.ref,this.modelRef.on(`load`,()=>{this.view&&(this.lastTime=performance.now(),this.animId=requestAnimationFrame(this.tick))})}else this.lastTime=performance.now(),this.animId=requestAnimationFrame(this.tick);this.applyInitialCamera(),this.applyModelVisibility(),document.addEventListener(`keydown`,this.boundKeyDown),document.addEventListener(`keyup`,this.boundKeyUp)}teleport(e){if(!this.view)return;let{lng:n,lat:r,alt:i}=e,a=e.heading==null?this.cameraHeading:e.heading;if(this.handle&&this.character){let e=(0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(r),lng:(0,t.degreeToRadian)(n),height:i});this.handle.update({matrixWorld:this.composeCharacterFrame(e,a)})}this.modelHeading=a,this.cameraHeading=a,this.placeChaseCamera(r,n,i,a),this.state={lng:n,lat:r,alt:i,heading:a,speed:0,animationState:this.currentAnimState,mode:this.viewMode},this.notify()}getState(){return this.state}onStateChange(e){return this.listeners.add(e),()=>this.listeners.delete(e)}setViewMode(e){this.viewMode!==e&&(this.viewMode=e,this.state={...this.state,mode:e},this.applyModelVisibility(),this.placeChaseCamera(this.state.lat,this.state.lng,this.state.alt,this.cameraHeading),this.notify())}toggleViewMode(){this.setViewMode(this.viewMode===`tpv`?`fpv`:`tpv`)}setAllowCameraControl(e){this.config.allowCameraControl=e}setHeading(e){if(!this.view)return;let{lat:n,lng:r,alt:i}=this.state;if(this.modelHeading=e,this.cameraHeading=e,this.handle&&this.character){let a=(0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(n),lng:(0,t.degreeToRadian)(r),height:i});this.handle.update({matrixWorld:this.composeCharacterFrame(a,e)})}this.isFreeCamera()||this.placeChaseCamera(n,r,i,e),this.state={...this.state,heading:e},this.notify()}getHeading(){return this.state.heading}setCameraPitch(e){this.config.cameraPitch=e,this.isFreeCamera()||this.placeChaseCamera(this.state.lat,this.state.lng,this.state.alt,this.cameraHeading)}getCameraPitch(){return this.config.cameraPitch}setFpvPitch(e){this.config.fpvPitch=e,this.isFreeCamera()||this.placeChaseCamera(this.state.lat,this.state.lng,this.state.alt,this.cameraHeading)}getFpvPitch(){return this.config.fpvPitch}setFpvHeightOffset(e){this.config.fpvHeightOffset=e,this.isFreeCamera()||this.placeChaseCamera(this.state.lat,this.state.lng,this.state.alt,this.cameraHeading)}getFpvHeightOffset(){return this.config.fpvHeightOffset}dispose(){this.animId!=null&&cancelAnimationFrame(this.animId),this.animId=null,document.removeEventListener(`keydown`,this.boundKeyDown),document.removeEventListener(`keyup`,this.boundKeyUp),this.handle&&=(this.handle.delete(),null),this.modelRef=null,this.heldActions.clear(),this.listeners.clear(),this.orbitKeyHeld=!1,this.orbitLatched=!1,this.dashMultiplier=1,this.view=void 0}resolveCharacter(e){return{modelUrl:e.modelUrl,animation:e.animation,modelRotationOffset:e.modelRotationOffset?{...u,...e.modelRotationOffset}:u,modelScale:e.modelScale??p,hideModelInFpv:e.hideModelInFpv??!0,castShadow:e.castShadow??!1,receiveShadow:e.receiveShadow??!1}}buildKeyMap(e){let t=new Map,n=(e,n)=>{for(let r of e)t.set(r,n)};return n(e.forward,`forward`),n(e.backward,`backward`),n(e.turnLeft,`turnLeft`),n(e.turnRight,`turnRight`),n(e.ascend,`ascend`),n(e.descend,`descend`),n(e.dash,`dash`),n(e.orbitCamera,`orbitCamera`),n(e.toggleView,`toggleView`),t}isFreeCamera(){return this.config.allowCameraControl||this.orbitKeyHeld||this.orbitLatched}isMovementAction(e){return e===`forward`||e===`backward`||e===`turnLeft`||e===`turnRight`||e===`ascend`||e===`descend`}applyModelVisibility(){if(!this.handle||!this.character)return;let e=this.viewMode===`fpv`&&this.character.hideModelInFpv;this.handle.visible=!e}applyInitialCamera(){let{startLat:e,startLng:t,startHeight:n,startHeading:r}=this.config;this.placeChaseCamera(e,t,n,r)}placeChaseCamera(e,t,n,r){if(!this.view)return;let{cameraDistance:i,cameraPitch:a,fpvPitch:o,fpvHeightOffset:s,fpvForwardOffset:c}=this.config,l=this.viewMode===`fpv`,u=n+s;if(l){let n=m,i=c+m,a=Math.tan(o)*n,s=this.advanceLatLng(e,t,r,i);this._offset.set(-Math.sin(r)*n,-Math.cos(r)*n,a),this.view.cameraFollow(!0,{lat:s.lat,lng:s.lng,height:u-a},this._offset);return}let d=i*Math.cos(a),f=i*Math.sin(a);this._offset.set(-Math.sin(r)*d,-Math.cos(r)*d,f),this.view.cameraFollow(!0,{lat:e,lng:t,height:u},this._offset)}composeCharacterFrame(e,n){let r=(0,t.eastNorthUpToFixedFrame)(e);return this._headingMatrix.makeRotationZ(-n),this._characterFrame.multiplyMatrices(r,this._headingMatrix)}advanceLatLng(e,n,r,i){let a=(0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(e),lng:(0,t.degreeToRadian)(n),height:0}),o=(0,t.eastNorthUpToFixedFrame)(a);this._east.setFromMatrixColumn(o,0).normalize(),this._north.setFromMatrixColumn(o,1).normalize(),this._worldForward.copy(this._east).multiplyScalar(Math.sin(r)).addScaledVector(this._north,Math.cos(r)),a.addScaledVector(this._worldForward,i);let s=(0,t.vector3ToGeodetic)(a);return{lat:(0,t.radianToDegree)(s.lat),lng:(0,t.radianToDegree)(s.lng)}}notify(){for(let e of this.listeners)e(this.state)}onKeyDown(e){let t=e.target;if(t.tagName===`INPUT`||t.tagName===`TEXTAREA`||t.isContentEditable)return;let n=this.keyToAction.get(e.code);if(n!==void 0){if(n===`toggleView`){if(e.repeat)return;this.toggleViewMode();return}if(n===`orbitCamera`){this.orbitKeyHeld=!0,this.orbitLatched=!0;return}if(!this.movementSuppressed){if(n===`dash`){this.dashMultiplier=2.5;return}this.isMovementAction(n)&&(this.orbitLatched=!1),this.heldActions.add(n)}}}onKeyUp(e){let t=this.keyToAction.get(e.code);if(t!==void 0&&t!==`toggleView`){if(t===`orbitCamera`){this.orbitKeyHeld=!1;return}if(t===`dash`){this.dashMultiplier=1;return}this.heldActions.delete(t)}}tick=e=>{if(!this.view)return;this.animId=requestAnimationFrame(this.tick);let n=Math.min((e-this.lastTime)/1e3,.1);this.lastTime=e;let r=0,i=0,a=0;this.heldActions.has(`forward`)&&(i+=1),this.heldActions.has(`backward`)&&--i,this.heldActions.has(`turnLeft`)&&--r,this.heldActions.has(`turnRight`)&&(r+=1),this.heldActions.has(`ascend`)&&(a+=1),this.heldActions.has(`descend`)&&--a;let{moveSpeed:o,rotationSpeed:s,altSpeed:c,minAlt:l,maxAlt:u,cameraLerpSpeed:d}=this.config;r!==0&&(this.modelHeading+=(0,t.degreeToRadian)(s*r));let f=this._curPos;if(this.handle&&this.modelRef){let e=this.modelRef.getWorldPosition();if(!e)return;f.copy(e)}else f.copy((0,t.geodeticToVector3)({lat:(0,t.degreeToRadian)(this.state.lat),lng:(0,t.degreeToRadian)(this.state.lng),height:this.state.alt}));let p=(0,t.eastNorthUpToFixedFrame)(f);this._east.setFromMatrixColumn(p,0).normalize(),this._north.setFromMatrixColumn(p,1).normalize();let m=(0,t.vector3ToGeodetic)(f);this._worldForward.copy(this._east).multiplyScalar(Math.sin(this.modelHeading)).addScaledVector(this._north,Math.cos(this.modelHeading)),i!==0&&f.addScaledVector(this._worldForward,o*this.dashMultiplier*n*i);let h=(0,t.vector3ToGeodetic)(f),g=m.height+a*c*n,_=Math.max(l,Math.min(u,g)),v=(0,t.radianToDegree)(h.lat),y=(0,t.radianToDegree)(h.lng),b=_;if(this.handle&&this.character){let e=(0,t.geodeticToVector3)({lat:h.lat,lng:h.lng,height:_});this.handle.update({matrixWorld:this.composeCharacterFrame(e,this.modelHeading)})}let x=this.modelHeading-this.cameraHeading;if(x-=Math.round(x/(Math.PI*2))*(Math.PI*2),this.cameraHeading+=x*Math.min(n*d,1),this.isFreeCamera())if(this.viewMode===`fpv`){let e=this.advanceLatLng(v,y,this.cameraHeading,this.config.fpvForwardOffset);this.view.cameraFreeLook(!0,{lat:e.lat,lng:e.lng,height:b+this.config.fpvHeightOffset})}else this.view.cameraFollow(!0,{lat:v,lng:y,height:b+this.config.fpvHeightOffset});else this.placeChaseCamera(v,y,b,this.cameraHeading);let S=i!==0||r!==0||a!==0,C=S&&this.dashMultiplier>1,w=this.currentAnimState;if(this.character&&this.modelRef){let{animation:e}=this.character,t=e.walkClip??e.idleClip,n;n=C?e.dashClip:S?t:e.idleClip,n!==this.currentAnimState&&(this.modelRef.crossFadeAnimation(this.currentAnimState??``,n,e.crossfadeDuration),this.currentAnimState=n),w=n}let T={lng:y,lat:v,alt:b,heading:this.modelHeading,speed:o*this.dashMultiplier,animationState:w,mode:this.viewMode};this.hasStateChanged(this.state,T)&&(this.state=T,this.notify())};hasStateChanged(e,t){return e.lng!==t.lng||e.lat!==t.lat||e.alt!==t.alt||e.heading!==t.heading||e.speed!==t.speed||e.animationState!==t.animationState||e.mode!==t.mode}};function g(e,t,n){e.style.transform=`translate(${t}px, ${n}px)`,e.style.willChange=`transform`}var _=1e5,v=class extends t.Plugin{view;maxDistance;positions=[];projected=new Map;listeners=new Set;boundHandler;constructor(e){super(),this.maxDistance=e?.maxDistance??_,this.boundHandler=this.handlePreRender.bind(this)}async init(e,t){this.view=e,e.on(`preRender`,this.boundHandler)}setPositions(e){this.positions=e}onUpdate(e){return this.listeners.add(e),()=>this.listeners.delete(e)}dispose(){this.view&&this.view.off(`preRender`,this.boundHandler),this.listeners.clear(),this.positions=[],this.projected.clear()}worldToScreen(e,n,r){if(!this.view)return null;let i=(0,t.geodeticToVector3)({lng:(0,t.degreeToRadian)(e),lat:(0,t.degreeToRadian)(n),height:r}),a=(0,t.convertWorldToScreen)({width:this.view.screenSize.x,height:this.view.screenSize.y,pixelRatio:this.view.pixelRatio},this.view.camera.raw,i);return a?{x:a.x,y:a.y}:null}handlePreRender(){if(!this.view)return;let e=this.view.camera.positionGeographic,n=(0,t.geodeticToVector3)({lng:(0,t.degreeToRadian)(e.lng),lat:(0,t.degreeToRadian)(e.lat),height:e.height}),r=new Map;for(let e of this.positions){let i=(0,t.geodeticToVector3)({lng:(0,t.degreeToRadian)(e.lng),lat:(0,t.degreeToRadian)(e.lat),height:e.alt}),a=n.distanceTo(i);if(a>this.maxDistance)continue;let o=this.worldToScreen(e.lng,e.lat,e.alt);o&&r.set(e.id,{...o,distance:a})}this.projected=r;let i={projected:this.projected};for(let e of this.listeners)e(i)}},y=`https://api.cesium.com/v1/assets`,b=class extends t.Plugin{view;config;endpoint;constructor(e){super(),this.config=e}async init(e,t){this.view=e;let{assetId:n,accessToken:r,endpoint:i=y}=this.config,a=`${i}/${encodeURIComponent(String(n))}/endpoint?access_token=${encodeURIComponent(r)}`,o=await fetch(a);if(!o.ok)throw Error(`CesiumIonPlugin: failed to fetch asset ${n} endpoint (${o.status} ${o.statusText})`);let s=await o.json();this.endpoint={url:s.url,accessToken:s.accessToken}}addTerrain(e={}){if(!this.view||!this.endpoint)throw Error(`CesiumIonPlugin: addTerrain() must be called after view.init().`);let{castShadow:t,receiveShadow:n,show:r,showBoundingBox:i,skirt:a,skirtExaggeration:o,...s}=e,c=this.view.addSource({type:`quantized-mesh`,url:`${this.endpoint.url}{z}/{x}/{y}.terrain`,token:this.endpoint.accessToken,...s}),l=this.view.addLayer({type:`terrain`,source:c,terrain:{castShadow:t,receiveShadow:n,show:r,showBoundingBox:i,skirt:a,skirtExaggeration:o}});return l.once(`deleted`,()=>c.delete()),l}},x=/^3\.\d+\.\d+$/,S=512,C=`mapbox`;function w(e){if(typeof e.tilejson!=`string`||!x.test(e.tilejson))throw Error(`TileJsonPlugin: invalid or missing "tilejson" version field (got ${JSON.stringify(e.tilejson)}).`);if(!Array.isArray(e.tiles)||e.tiles.length===0)throw Error(`TileJsonPlugin: "tiles" must be a non-empty array of URL templates.`)}function T(e){switch(e){case`mapbox`:return(0,t.MAPBOX_ELEVATION_DECODER)();case`terrarium`:return(0,t.TERRARIUM_ELEVATION_DECODER)();default:throw Error(`TileJsonPlugin: unsupported raster-dem encoding ${JSON.stringify(e)}. Supported encodings: "mapbox", "terrarium".`)}}var E=class extends t.Plugin{view;events=new t.EventHandler;credits=[];async init(e,t){this.view=e}on(e,t){this.events.on(e,t)}once(e,t){this.events.once(e,t)}off(e,t){this.events.off(e,t)}async addSource(e){let t=this.view;if(!t)throw Error(`TileJsonPlugin: addSource() must be called after view.init().`);let n=await this.fetchTileJson(e.url);w(n);let[r,...i]=n.tiles;i.length>0&&console.warn(`TileJsonPlugin: TileJSON at ${e.url} lists ${n.tiles.length} mirror tile endpoints; Navara sources use a single URL, so only the first is used.`);let a=e.minzoom??n.minzoom,o=e.maxzoom??n.maxzoom,s=e.scheme??n.scheme,c=e.id,l={url:r,...c===void 0?{}:{id:c},...o===void 0?{}:{maxZoom:o}},u={...l,...a===void 0?{}:{minZoom:a},...s===`tms`?{tms:!0}:{}},d;return d=e.type===`raster-tile`?t.addSource({type:`raster-tile`,...u}):e.type===`raster-dem`?t.addSource({type:`raster-dem`,...u,tileSize:e.tileSize??n.tileSize??S,elevationDecoder:T(e.encoding??n.encoding??C)}):t.addSource({type:`vector-tile`,...l}),n.attribution&&this.addCredit(n.attribution),this.events.emit(`loaded`,{source:d,tilejson:n,attribution:n.attribution}),d}dispose(){let e=this.view?.attribution;e&&this.credits.length>0&&e.remove(this.credits.map(e=>({attributionHtml:e}))),this.credits.length=0,this.events.clear(`loaded`),this.view=void 0}async fetchTileJson(e){let t=await fetch(e);if(!t.ok)throw Error(`TileJsonPlugin: failed to fetch TileJSON from ${e} (${t.status} ${t.statusText})`);return await t.json()}addCredit(e){this.credits.includes(e)||(this.credits.push(e),this.view?.attribution?.add([{attributionHtml:e}]))}};e.CesiumIonPlugin=b,e.OverlayPlugin=v,e.PersonViewPlugin=h,e.TileJsonPlugin=E,e.moveOverlayElement=g});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@navaramap/three-plugins",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"main": "dist/index.umd.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"three": ">=0.183.0",
|
|
23
|
-
"@navaramap/three": "0.0.
|
|
24
|
-
"@navaramap/three-default-plugin": "0.0.
|
|
23
|
+
"@navaramap/three": "0.0.5",
|
|
24
|
+
"@navaramap/three-default-plugin": "0.0.5"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/three": "0.185.1",
|
|
28
28
|
"three": "0.185.1",
|
|
29
|
-
"@navaramap/three
|
|
30
|
-
"@navaramap/three": "0.0.
|
|
29
|
+
"@navaramap/three": "0.0.5",
|
|
30
|
+
"@navaramap/three-default-plugin": "0.0.5"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"type-check": "tsc && tsc -p tsconfig.node.json",
|
|
@@ -3,13 +3,17 @@ import { describe, it, expect, vi, afterEach } from "vitest";
|
|
|
3
3
|
import { TileJsonPlugin, type TileJson } from "./TileJsonPlugin";
|
|
4
4
|
|
|
5
5
|
// Importing the real @navaramap/three touches WASM/os at module load, which fails
|
|
6
|
-
// in the test environment. TileJsonPlugin only needs `Plugin
|
|
7
|
-
// as runtime values (everything else it
|
|
8
|
-
// stub the module. The
|
|
6
|
+
// in the test environment. TileJsonPlugin only needs `Plugin`, `EventHandler`,
|
|
7
|
+
// and the elevation decoder factories as runtime values (everything else it
|
|
8
|
+
// imports from there is type-only), so stub the module. The decoder factories
|
|
9
|
+
// return sentinels the tests can assert on; the EventHandler stub mirrors the
|
|
10
|
+
// real on/once/off/emit/clear.
|
|
9
11
|
/* eslint-disable @typescript-eslint/no-extraneous-class */
|
|
10
12
|
vi.mock("@navaramap/three", () => ({
|
|
11
13
|
default: class ThreeView {},
|
|
12
14
|
Plugin: class Plugin {},
|
|
15
|
+
MAPBOX_ELEVATION_DECODER: () => ({ decoder: "mapbox" }),
|
|
16
|
+
TERRARIUM_ELEVATION_DECODER: () => ({ decoder: "terrarium" }),
|
|
13
17
|
EventHandler: class EventHandler {
|
|
14
18
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
19
|
private listeners = new Map<string, Set<(...a: any[]) => void>>();
|
|
@@ -192,6 +196,118 @@ describe("TileJsonPlugin.addSource", () => {
|
|
|
192
196
|
expect(desc).not.toHaveProperty("tms");
|
|
193
197
|
});
|
|
194
198
|
|
|
199
|
+
it("creates a raster-dem source with MapLibre defaults (tileSize 512, mapbox encoding)", async () => {
|
|
200
|
+
const { view } = makeFakeView();
|
|
201
|
+
const plugin = initPlugin(view);
|
|
202
|
+
stubTileJson({
|
|
203
|
+
tilejson: "3.0.0",
|
|
204
|
+
tiles: ["https://a/{z}/{x}/{y}.png"],
|
|
205
|
+
minzoom: 2,
|
|
206
|
+
maxzoom: 12,
|
|
207
|
+
} satisfies TileJson);
|
|
208
|
+
|
|
209
|
+
await plugin.addSource({ type: "raster-dem", id: "dem", url: DOC_URL });
|
|
210
|
+
|
|
211
|
+
expect(view.addSource).toHaveBeenCalledWith({
|
|
212
|
+
type: "raster-dem",
|
|
213
|
+
url: "https://a/{z}/{x}/{y}.png",
|
|
214
|
+
id: "dem",
|
|
215
|
+
minZoom: 2,
|
|
216
|
+
maxZoom: 12,
|
|
217
|
+
tileSize: 512,
|
|
218
|
+
elevationDecoder: { decoder: "mapbox" },
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("honors the document's MapLibre-compatible tileSize/encoding for raster-dem", async () => {
|
|
223
|
+
const { view } = makeFakeView();
|
|
224
|
+
const plugin = initPlugin(view);
|
|
225
|
+
stubTileJson({
|
|
226
|
+
tilejson: "3.0.0",
|
|
227
|
+
tiles: ["https://a/{z}/{x}/{y}.png"],
|
|
228
|
+
tileSize: 256,
|
|
229
|
+
encoding: "terrarium",
|
|
230
|
+
} satisfies TileJson);
|
|
231
|
+
|
|
232
|
+
await plugin.addSource({ type: "raster-dem", url: DOC_URL });
|
|
233
|
+
|
|
234
|
+
expect(view.addSource).toHaveBeenCalledWith(
|
|
235
|
+
expect.objectContaining({
|
|
236
|
+
tileSize: 256,
|
|
237
|
+
elevationDecoder: { decoder: "terrarium" },
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("rejects an unsupported raster-dem encoding", async () => {
|
|
243
|
+
const { view } = makeFakeView();
|
|
244
|
+
const plugin = initPlugin(view);
|
|
245
|
+
stubTileJson({
|
|
246
|
+
tilejson: "3.0.0",
|
|
247
|
+
tiles: ["https://a/{z}/{x}/{y}.png"],
|
|
248
|
+
encoding: "custom" as never,
|
|
249
|
+
} satisfies TileJson);
|
|
250
|
+
|
|
251
|
+
await expect(
|
|
252
|
+
plugin.addSource({ type: "raster-dem", url: DOC_URL }),
|
|
253
|
+
).rejects.toThrow(/unsupported raster-dem encoding/);
|
|
254
|
+
expect(view.addSource).not.toHaveBeenCalled();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("prefers description-level fields over the fetched document", async () => {
|
|
258
|
+
const { view } = makeFakeView();
|
|
259
|
+
const plugin = initPlugin(view);
|
|
260
|
+
stubTileJson({
|
|
261
|
+
tilejson: "3.0.0",
|
|
262
|
+
tiles: ["https://a/{z}/{x}/{y}.png"],
|
|
263
|
+
minzoom: 0,
|
|
264
|
+
maxzoom: 10,
|
|
265
|
+
scheme: "xyz",
|
|
266
|
+
tileSize: 512,
|
|
267
|
+
encoding: "mapbox",
|
|
268
|
+
} satisfies TileJson);
|
|
269
|
+
|
|
270
|
+
await plugin.addSource({
|
|
271
|
+
type: "raster-dem",
|
|
272
|
+
url: DOC_URL,
|
|
273
|
+
minzoom: 3,
|
|
274
|
+
maxzoom: 15,
|
|
275
|
+
scheme: "tms",
|
|
276
|
+
tileSize: 256,
|
|
277
|
+
encoding: "terrarium",
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
expect(view.addSource).toHaveBeenCalledWith({
|
|
281
|
+
type: "raster-dem",
|
|
282
|
+
url: "https://a/{z}/{x}/{y}.png",
|
|
283
|
+
minZoom: 3,
|
|
284
|
+
maxZoom: 15,
|
|
285
|
+
tms: true,
|
|
286
|
+
tileSize: 256,
|
|
287
|
+
elevationDecoder: { decoder: "terrarium" },
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("applies description-level fields even when the document omits them", async () => {
|
|
292
|
+
const { view } = makeFakeView();
|
|
293
|
+
const plugin = initPlugin(view);
|
|
294
|
+
stubTileJson({
|
|
295
|
+
tilejson: "3.0.0",
|
|
296
|
+
tiles: ["https://a/{z}/{x}/{y}.png"],
|
|
297
|
+
} satisfies TileJson);
|
|
298
|
+
|
|
299
|
+
await plugin.addSource({
|
|
300
|
+
type: "raster-tile",
|
|
301
|
+
url: DOC_URL,
|
|
302
|
+
minzoom: 4,
|
|
303
|
+
maxzoom: 8,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
expect(view.addSource).toHaveBeenCalledWith(
|
|
307
|
+
expect.objectContaining({ minZoom: 4, maxZoom: 8 }),
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
|
|
195
311
|
it("throws when the TileJSON fetch fails", async () => {
|
|
196
312
|
const { view } = makeFakeView();
|
|
197
313
|
const plugin = initPlugin(view);
|
package/src/TileJsonPlugin.ts
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* document and registers it as a single Navara source. {@link TileJsonPlugin.addSource}
|
|
7
7
|
* mirrors {@link ThreeView.addSource}: a discriminated `type` plus an optional
|
|
8
8
|
* `id`, with `url` pointing at the TileJSON document. The plugin derives the tile
|
|
9
|
-
* URL, `minzoom`/`maxzoom`, and `scheme` from the document
|
|
9
|
+
* URL, `minzoom`/`maxzoom`, and `scheme` from the document, plus — for
|
|
10
|
+
* `raster-dem` sources — the MapLibre-compatible `tileSize` and `encoding`
|
|
11
|
+
* fields. Each derived field can also be set on the description itself, which
|
|
12
|
+
* takes precedence over the document.
|
|
10
13
|
*
|
|
11
14
|
* Each document's `attribution` is surfaced automatically through the view's
|
|
12
15
|
* built-in attribution UI (`view.attribution`). Callers who want a custom credit
|
|
@@ -36,6 +39,16 @@
|
|
|
36
39
|
* view.addLayer({ type: "raster", source });
|
|
37
40
|
* // ...or directly by the id passed above.
|
|
38
41
|
* view.addLayer({ type: "raster", source: "basemap" });
|
|
42
|
+
*
|
|
43
|
+
* // Raster-DEM terrain. The MapLibre-compatible `tileSize`/`encoding` fields
|
|
44
|
+
* // are honored from the document (or the description below), defaulting to
|
|
45
|
+
* // MapLibre's 512 / "mapbox".
|
|
46
|
+
* const dem = await tilejson.addSource({
|
|
47
|
+
* type: "raster-dem",
|
|
48
|
+
* url: "https://example.com/terrain.json",
|
|
49
|
+
* encoding: "terrarium", // takes precedence over the document
|
|
50
|
+
* });
|
|
51
|
+
* view.addLayer({ type: "terrain", source: dem, terrain: {} });
|
|
39
52
|
* ```
|
|
40
53
|
*
|
|
41
54
|
* ### Custom attribution UI
|
|
@@ -55,6 +68,8 @@
|
|
|
55
68
|
import ThreeView, {
|
|
56
69
|
Plugin,
|
|
57
70
|
EventHandler,
|
|
71
|
+
MAPBOX_ELEVATION_DECODER,
|
|
72
|
+
TERRARIUM_ELEVATION_DECODER,
|
|
58
73
|
type Source,
|
|
59
74
|
type ViewContext,
|
|
60
75
|
} from "@navaramap/three";
|
|
@@ -65,6 +80,17 @@ type View = ThreeView<DefaultDescriptions>;
|
|
|
65
80
|
/** Matches a TileJSON version such as "3.0.0" (major.minor.patch). */
|
|
66
81
|
const TILEJSON_VERSION = /^3\.\d+\.\d+$/;
|
|
67
82
|
|
|
83
|
+
/** MapLibre's raster-dem defaults, applied when neither the description nor the document sets them. */
|
|
84
|
+
const DEFAULT_DEM_TILE_SIZE = 512;
|
|
85
|
+
const DEFAULT_DEM_ENCODING: TileJsonDemEncoding = "mapbox";
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* How a `raster-dem` document's RGB tiles encode elevation, matching MapLibre's
|
|
89
|
+
* `encoding` values. MapLibre's `"custom"` (free-form decode factors) is not
|
|
90
|
+
* supported.
|
|
91
|
+
*/
|
|
92
|
+
export type TileJsonDemEncoding = "mapbox" | "terrarium";
|
|
93
|
+
|
|
68
94
|
/**
|
|
69
95
|
* The subset of a TileJSON 3.0.0 document this plugin consumes. Other spec
|
|
70
96
|
* fields (`bounds`, `center`, `grids`, ...) are ignored.
|
|
@@ -82,24 +108,34 @@ export type TileJson = {
|
|
|
82
108
|
maxzoom?: number;
|
|
83
109
|
/** Tiling scheme. `"tms"` flips the Y axis (raster sources only). @defaultValue "xyz" */
|
|
84
110
|
scheme?: "xyz" | "tms";
|
|
111
|
+
/**
|
|
112
|
+
* Tile size in pixels. Not part of the TileJSON spec, but emitted by
|
|
113
|
+
* MapLibre-oriented tile servers; applied to `raster-dem` sources only.
|
|
114
|
+
* @defaultValue 512
|
|
115
|
+
*/
|
|
116
|
+
tileSize?: number;
|
|
117
|
+
/**
|
|
118
|
+
* Elevation encoding of DEM tiles. Not part of the TileJSON spec, but
|
|
119
|
+
* emitted by MapLibre-oriented tile servers; applied to `raster-dem` sources
|
|
120
|
+
* only.
|
|
121
|
+
* @defaultValue "mapbox"
|
|
122
|
+
*/
|
|
123
|
+
encoding?: TileJsonDemEncoding;
|
|
85
124
|
};
|
|
86
125
|
|
|
87
126
|
/**
|
|
88
127
|
* Which Navara source type a TileJSON document is materialized into. Declared by
|
|
89
128
|
* the caller because TileJSON has no field that reliably distinguishes raster
|
|
90
|
-
* imagery from vector tiles.
|
|
129
|
+
* imagery from vector or elevation tiles.
|
|
91
130
|
*/
|
|
92
|
-
export type TileJsonSourceType = "raster-tile" | "vector-tile";
|
|
131
|
+
export type TileJsonSourceType = "raster-tile" | "vector-tile" | "raster-dem";
|
|
93
132
|
|
|
94
133
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* URL, zoom range, and attribution from the document.
|
|
134
|
+
* Fields shared by every {@link TileJsonSourceDescription} variant. Alongside
|
|
135
|
+
* `url` and `id`, it accepts the same optional fields the TileJSON document
|
|
136
|
+
* carries; a field set here takes precedence over the fetched document's value.
|
|
99
137
|
*/
|
|
100
|
-
|
|
101
|
-
/** Navara source type to create, as in `addSource`. */
|
|
102
|
-
type: TileJsonSourceType;
|
|
138
|
+
type TileJsonSourceDescriptionBase = {
|
|
103
139
|
/** URL of the TileJSON 3.0.0 document to fetch and expand. */
|
|
104
140
|
url: string;
|
|
105
141
|
/**
|
|
@@ -108,8 +144,49 @@ export type TileJsonSourceDescription = {
|
|
|
108
144
|
* handle. When omitted, the engine generates one.
|
|
109
145
|
*/
|
|
110
146
|
id?: string;
|
|
147
|
+
/** Overrides the document's `minzoom` when set. */
|
|
148
|
+
minzoom?: number;
|
|
149
|
+
/** Overrides the document's `maxzoom` when set. */
|
|
150
|
+
maxzoom?: number;
|
|
151
|
+
/** Overrides the document's `scheme` when set. */
|
|
152
|
+
scheme?: "xyz" | "tms";
|
|
111
153
|
};
|
|
112
154
|
|
|
155
|
+
/** Registers a TileJSON document as a `raster-tile` (imagery) source. */
|
|
156
|
+
export type TileJsonRasterTileSourceDescription =
|
|
157
|
+
TileJsonSourceDescriptionBase & {
|
|
158
|
+
type: "raster-tile";
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/** Registers a TileJSON document as a `vector-tile` (MVT) source. */
|
|
162
|
+
export type TileJsonVectorTileSourceDescription =
|
|
163
|
+
TileJsonSourceDescriptionBase & {
|
|
164
|
+
type: "vector-tile";
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/** Registers a TileJSON document as a `raster-dem` (elevation) source. */
|
|
168
|
+
export type TileJsonRasterDemSourceDescription =
|
|
169
|
+
TileJsonSourceDescriptionBase & {
|
|
170
|
+
type: "raster-dem";
|
|
171
|
+
/** Overrides the document's `tileSize` when set. @defaultValue 512 */
|
|
172
|
+
tileSize?: number;
|
|
173
|
+
/** Overrides the document's `encoding` when set. @defaultValue "mapbox" */
|
|
174
|
+
encoding?: TileJsonDemEncoding;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Describes a TileJSON tile source to register via {@link TileJsonPlugin.addSource}.
|
|
179
|
+
* Mirrors the shape of {@link ThreeView.addSource}, except `url` is the TileJSON
|
|
180
|
+
* document URL (not a tile template) — the plugin fetches it and derives the tile
|
|
181
|
+
* URL, zoom range, and attribution from the document. Document-derived fields
|
|
182
|
+
* (`minzoom`, `maxzoom`, `scheme` — plus `tileSize` and `encoding` for
|
|
183
|
+
* `raster-dem`) can also be set here to take precedence over the document.
|
|
184
|
+
*/
|
|
185
|
+
export type TileJsonSourceDescription =
|
|
186
|
+
| TileJsonRasterTileSourceDescription
|
|
187
|
+
| TileJsonVectorTileSourceDescription
|
|
188
|
+
| TileJsonRasterDemSourceDescription;
|
|
189
|
+
|
|
113
190
|
/** Detail passed to the {@link TileJsonPlugin} `loaded` event. */
|
|
114
191
|
export type TileJsonLoadedEvent = {
|
|
115
192
|
/** The Navara source created for the document. */
|
|
@@ -153,6 +230,26 @@ function validateTileJson(doc: TileJson): void {
|
|
|
153
230
|
}
|
|
154
231
|
}
|
|
155
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Map a MapLibre `encoding` value to the engine's elevation decoder. Throws for
|
|
235
|
+
* unknown encodings (including MapLibre's `"custom"`, whose free-form decode
|
|
236
|
+
* factors have no TileJSON representation) so a raster-dem source is never
|
|
237
|
+
* silently created with a wrong decoder.
|
|
238
|
+
*/
|
|
239
|
+
function resolveElevationDecoder(encoding: TileJsonDemEncoding) {
|
|
240
|
+
switch (encoding) {
|
|
241
|
+
case "mapbox":
|
|
242
|
+
return MAPBOX_ELEVATION_DECODER();
|
|
243
|
+
case "terrarium":
|
|
244
|
+
return TERRARIUM_ELEVATION_DECODER();
|
|
245
|
+
default:
|
|
246
|
+
throw new Error(
|
|
247
|
+
`TileJsonPlugin: unsupported raster-dem encoding ${JSON.stringify(encoding)}. ` +
|
|
248
|
+
'Supported encodings: "mapbox", "terrarium".',
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
156
253
|
export class TileJsonPlugin extends Plugin<View, ViewContext> {
|
|
157
254
|
private view?: View;
|
|
158
255
|
private readonly events = new EventHandler<TileJsonPluginEventMap>();
|
|
@@ -195,9 +292,13 @@ export class TileJsonPlugin extends Plugin<View, ViewContext> {
|
|
|
195
292
|
*
|
|
196
293
|
* `desc.url` is the TileJSON document URL; the plugin fetches it, then creates
|
|
197
294
|
* one source of the requested `desc.type` using the document's first tile
|
|
198
|
-
* endpoint, `minzoom`/`maxzoom`, and `scheme
|
|
199
|
-
*
|
|
200
|
-
*
|
|
295
|
+
* endpoint, `minzoom`/`maxzoom`, and `scheme` — plus, for `raster-dem`, the
|
|
296
|
+
* MapLibre-compatible `tileSize` and `encoding` fields (defaulting to
|
|
297
|
+
* MapLibre's 512 and `"mapbox"`). Each of those fields can also be set on
|
|
298
|
+
* `desc` itself, taking precedence over the document. The document's
|
|
299
|
+
* `attribution` is surfaced through the view's built-in attribution UI
|
|
300
|
+
* (`view.attribution`), and a `loaded` event is emitted carrying the source,
|
|
301
|
+
* document, and attribution.
|
|
201
302
|
*
|
|
202
303
|
* A TileJSON `tiles` array lists mirror endpoints for the same tileset (as in
|
|
203
304
|
* MapLibre, which shards requests across them). Navara sources take a single
|
|
@@ -228,26 +329,38 @@ export class TileJsonPlugin extends Plugin<View, ViewContext> {
|
|
|
228
329
|
);
|
|
229
330
|
}
|
|
230
331
|
|
|
231
|
-
|
|
332
|
+
// Description-level fields take precedence over the fetched document.
|
|
333
|
+
const minzoom = desc.minzoom ?? doc.minzoom;
|
|
334
|
+
const maxzoom = desc.maxzoom ?? doc.maxzoom;
|
|
335
|
+
const scheme = desc.scheme ?? doc.scheme;
|
|
232
336
|
const id = desc.id;
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
337
|
+
const shared = {
|
|
338
|
+
url,
|
|
339
|
+
...(id !== undefined ? { id } : {}),
|
|
340
|
+
...(maxzoom !== undefined ? { maxZoom: maxzoom } : {}),
|
|
341
|
+
};
|
|
342
|
+
const rasterShared = {
|
|
343
|
+
...shared,
|
|
344
|
+
...(minzoom !== undefined ? { minZoom: minzoom } : {}),
|
|
345
|
+
...(scheme === "tms" ? { tms: true } : {}),
|
|
346
|
+
};
|
|
347
|
+
let source: Source;
|
|
348
|
+
if (desc.type === "raster-tile") {
|
|
349
|
+
source = view.addSource({ type: "raster-tile", ...rasterShared });
|
|
350
|
+
} else if (desc.type === "raster-dem") {
|
|
351
|
+
source = view.addSource({
|
|
352
|
+
type: "raster-dem",
|
|
353
|
+
...rasterShared,
|
|
354
|
+
tileSize: desc.tileSize ?? doc.tileSize ?? DEFAULT_DEM_TILE_SIZE,
|
|
355
|
+
elevationDecoder: resolveElevationDecoder(
|
|
356
|
+
desc.encoding ?? doc.encoding ?? DEFAULT_DEM_ENCODING,
|
|
357
|
+
),
|
|
358
|
+
});
|
|
359
|
+
} else {
|
|
360
|
+
// vector-tile sources have no minZoom / tms fields in the engine, so
|
|
361
|
+
// only maxzoom carries over.
|
|
362
|
+
source = view.addSource({ type: "vector-tile", ...shared });
|
|
363
|
+
}
|
|
251
364
|
|
|
252
365
|
if (doc.attribution) {
|
|
253
366
|
this.addCredit(doc.attribution);
|
package/src/index.ts
CHANGED
|
@@ -28,8 +28,12 @@ export {
|
|
|
28
28
|
export {
|
|
29
29
|
TileJsonPlugin,
|
|
30
30
|
type TileJson,
|
|
31
|
+
type TileJsonDemEncoding,
|
|
31
32
|
type TileJsonSourceType,
|
|
32
33
|
type TileJsonSourceDescription,
|
|
34
|
+
type TileJsonRasterTileSourceDescription,
|
|
35
|
+
type TileJsonVectorTileSourceDescription,
|
|
36
|
+
type TileJsonRasterDemSourceDescription,
|
|
33
37
|
type TileJsonLoadedEvent,
|
|
34
38
|
type TileJsonPluginEventMap,
|
|
35
39
|
} from "./TileJsonPlugin";
|