@navaramap/three 0.0.8 → 0.1.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.
- package/LICENSE +1 -1
- package/README.md +20 -8
- package/dist/assets/fontWorker-C5cHhkWQ.js +2 -0
- package/dist/assets/fontWorker-C5cHhkWQ.js.map +1 -0
- package/dist/assets/{index-DaejBdUz.js → index-VfUncKuS.js} +2 -2
- package/dist/assets/index-VfUncKuS.js.map +1 -0
- package/dist/assets/{navara_wasm_font_worker_bg-CSmoDs_Q.wasm → navara_wasm_font_worker_bg-CKqHBoCR.wasm} +0 -0
- package/dist/assets/{navara_wasm_worker_bg-C1dUd_Nk.wasm → navara_wasm_worker_bg-CT26EH41.wasm} +0 -0
- package/dist/index.d.ts +298 -54
- package/dist/index.js +1569 -1396
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +90 -69
- package/dist/index.umd.cjs.map +1 -1
- package/package.json +12 -12
- package/src/core/BaseDesc.ts +25 -1
- package/src/core/MeshDesc.test.ts +6 -8
- package/src/core/MeshDesc.ts +4 -6
- package/src/evaluations/FeatureEvaluator.ts +17 -10
- package/src/event/feature.ts +15 -2
- package/src/event/features/billboard.test.ts +62 -0
- package/src/event/features/billboard.ts +4 -2
- package/src/index.ts +266 -98
- package/src/material/enhancer/model/modelBaseEnhancer/shader.ts +2 -2
- package/src/material/enhancer/polygon/polygonBaseEnhancer/shader.ts +2 -2
- package/src/material/macro/tileShader.ts +12 -2
- package/src/mesh/polyline.ts +32 -4
- package/src/mesh/sdfText/batchedSdfText.test.ts +65 -0
- package/src/mesh/sdfText/batchedSdfText.ts +121 -43
- package/src/mesh/sprite/instancedSprite.ts +173 -65
- package/src/pick/pickHelper.test.ts +389 -4
- package/src/pick/pickHelper.ts +212 -37
- package/src/plugins/AttributionPlugin/index.ts +1 -1
- package/src/renderOrder.ts +10 -0
- package/src/terrain/sampleTerrainMostDetailed.test.ts +6 -6
- package/src/terrain/sampleTerrainMostDetailed.ts +7 -5
- package/src/type/index.ts +22 -1
- package/dist/assets/fontWorker-CkLGtrXf.js +0 -2
- package/dist/assets/fontWorker-CkLGtrXf.js.map +0 -1
- package/dist/assets/index-DaejBdUz.js.map +0 -1
package/src/pick/pickHelper.ts
CHANGED
|
@@ -19,22 +19,40 @@ export type PickHelperOptions = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Travel tolerance in CSS pixels between
|
|
22
|
+
* Travel tolerance in CSS pixels between pointerdown and pointerup to count as a click.
|
|
23
23
|
* Follows Cesium's `_clickPixelTolerance` (MapLibre uses 3px).
|
|
24
24
|
* ref: https://github.com/CesiumGS/cesium/blob/9fda7ab97a762e40c74b1d0e1814c98a2de43337/packages/engine/Source/Core/ScreenSpaceEventHandler.js#L1012
|
|
25
25
|
* ref: https://github.com/maplibre/maplibre-gl-js/blob/3a0a4f795fef5b2a29034d71833475589c344eaf/src/ui/map.ts#L542
|
|
26
26
|
*/
|
|
27
|
-
const CLICK_PIXEL_TOLERANCE = 5;
|
|
27
|
+
export const CLICK_PIXEL_TOLERANCE = 5;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Travel tolerance for touch taps. Finger contact jitters far more than a
|
|
31
|
+
* mouse, so follows MapLibre's tap `MAX_DIST` instead.
|
|
32
|
+
* ref: https://github.com/maplibre/maplibre-gl-js/blob/3a0a4f795fef5b2a29034d71833475589c344eaf/src/ui/handler/tap_recognizer.ts
|
|
33
|
+
*/
|
|
34
|
+
export const TAP_PIXEL_TOLERANCE = 30;
|
|
28
35
|
|
|
29
36
|
export function isClickGesture(
|
|
30
37
|
downPosition: Vector2,
|
|
31
38
|
upPosition: Vector2,
|
|
39
|
+
tolerance: number = CLICK_PIXEL_TOLERANCE,
|
|
32
40
|
): boolean {
|
|
33
|
-
return downPosition.distanceTo(upPosition) <
|
|
41
|
+
return downPosition.distanceTo(upPosition) < tolerance;
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
/**
|
|
37
|
-
*
|
|
45
|
+
* Pick search radius in CSS pixels around the pointer. The pick reads a
|
|
46
|
+
* (2r+1)² window and takes the non-zero batch id closest to its center, so
|
|
47
|
+
* small features (points, thin lines) don't demand pixel-perfect aim.
|
|
48
|
+
*/
|
|
49
|
+
export const PICK_RADIUS = 3;
|
|
50
|
+
|
|
51
|
+
/** Pick search radius for touch: fingertips land less precisely than a cursor. */
|
|
52
|
+
export const TOUCH_PICK_RADIUS = 10;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* GPU picking using a dedicated render pass over a small search window.
|
|
38
56
|
*
|
|
39
57
|
* The pick pass does NOT reuse the main render pipeline (globe / MRT /
|
|
40
58
|
* draped / copy passes). Instead, every pickable raw — regardless of
|
|
@@ -47,6 +65,15 @@ export function isClickGesture(
|
|
|
47
65
|
* Re-parenting is safe because pass scenes all have identity world
|
|
48
66
|
* transforms, so world matrices of the re-parented objects are
|
|
49
67
|
* unaffected.
|
|
68
|
+
*
|
|
69
|
+
* Two gestures trigger a pick: a click or touch tap (pointerdown/pointerup
|
|
70
|
+
* within {@link CLICK_PIXEL_TOLERANCE} / {@link TAP_PIXEL_TOLERANCE}) and
|
|
71
|
+
* hovering, throttled to at most one pick per animation frame. Both are
|
|
72
|
+
* lazy: a pick runs only while the matching `shouldClickPick` /
|
|
73
|
+
* `shouldHoverPick` gate returns true (i.e. someone is listening for the
|
|
74
|
+
* result). The gestures are detected from pointer events, which fire for
|
|
75
|
+
* touch as well. The camera-input handlers call `preventDefault()` on
|
|
76
|
+
* touch events, so the compatibility mouse events never do.
|
|
50
77
|
*/
|
|
51
78
|
export class PickHelper {
|
|
52
79
|
private element: HTMLElement;
|
|
@@ -64,9 +91,21 @@ export class PickHelper {
|
|
|
64
91
|
/** Full-size render target used for click picking with scissor restriction. */
|
|
65
92
|
private pickRenderTarget: WebGLRenderTarget;
|
|
66
93
|
|
|
67
|
-
private
|
|
68
|
-
|
|
69
|
-
private
|
|
94
|
+
private onHoverCallback: (pickArr: number[]) => void;
|
|
95
|
+
/** Click picks run only while this returns true (e.g. featureClick listeners exist). */
|
|
96
|
+
private shouldClickPick: () => boolean;
|
|
97
|
+
/** Hover picks run only while this returns true (e.g. hover listeners exist). */
|
|
98
|
+
private shouldHoverPick: () => boolean;
|
|
99
|
+
|
|
100
|
+
private pointerDownPosition?: Vector2;
|
|
101
|
+
private pointerDownHandler: (event: PointerEvent) => void;
|
|
102
|
+
private pointerUpHandler: (event: PointerEvent) => void;
|
|
103
|
+
private pointerCancelHandler: () => void;
|
|
104
|
+
private hoverMoveHandler: (event: PointerEvent) => void;
|
|
105
|
+
private hoverLeaveHandler: () => void;
|
|
106
|
+
/** Latest pointermove position awaiting a hover pick, in client coords. */
|
|
107
|
+
private hoverPosition?: Vector2;
|
|
108
|
+
private hoverRafId?: number;
|
|
70
109
|
|
|
71
110
|
constructor(
|
|
72
111
|
element: HTMLElement,
|
|
@@ -74,6 +113,9 @@ export class PickHelper {
|
|
|
74
113
|
camera: PerspectiveCamera,
|
|
75
114
|
meshes: MeshCache,
|
|
76
115
|
onPickCallback: (pickArr: number[]) => void,
|
|
116
|
+
onHoverCallback: (pickArr: number[]) => void,
|
|
117
|
+
shouldClickPick: () => boolean,
|
|
118
|
+
shouldHoverPick: () => boolean,
|
|
77
119
|
options?: PickHelperOptions,
|
|
78
120
|
) {
|
|
79
121
|
this.element = element;
|
|
@@ -82,9 +124,17 @@ export class PickHelper {
|
|
|
82
124
|
this._camera = camera;
|
|
83
125
|
this._meshes = meshes;
|
|
84
126
|
this.onPickCallback = onPickCallback;
|
|
85
|
-
|
|
86
|
-
this.
|
|
87
|
-
this.
|
|
127
|
+
this.onHoverCallback = onHoverCallback;
|
|
128
|
+
this.shouldClickPick = shouldClickPick;
|
|
129
|
+
this.shouldHoverPick = shouldHoverPick;
|
|
130
|
+
|
|
131
|
+
this.pointerDownHandler = (event: PointerEvent) =>
|
|
132
|
+
this.onPointerDown(event);
|
|
133
|
+
this.pointerUpHandler = (event: PointerEvent) => this.onPointerUp(event);
|
|
134
|
+
this.pointerCancelHandler = () => this.onPointerCancel();
|
|
135
|
+
this.hoverMoveHandler = (event: PointerEvent) =>
|
|
136
|
+
this.onHoverPointerMove(event);
|
|
137
|
+
this.hoverLeaveHandler = () => this.onHoverPointerLeave();
|
|
88
138
|
|
|
89
139
|
const width = this._renderer.getContext().drawingBufferWidth;
|
|
90
140
|
const height = this._renderer.getContext().drawingBufferHeight;
|
|
@@ -105,32 +155,113 @@ export class PickHelper {
|
|
|
105
155
|
}
|
|
106
156
|
}
|
|
107
157
|
|
|
108
|
-
private
|
|
109
|
-
|
|
158
|
+
private onPointerDown(event: PointerEvent) {
|
|
159
|
+
// A second concurrent pointer (e.g. the pinch-zoom finger) turns the
|
|
160
|
+
// gesture into something other than a tap; abandon the pending click.
|
|
161
|
+
if (!event.isPrimary) {
|
|
162
|
+
this.pointerDownPosition = undefined;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.pointerDownPosition = new Vector2(event.clientX, event.clientY);
|
|
110
166
|
}
|
|
111
167
|
|
|
112
|
-
private
|
|
113
|
-
|
|
114
|
-
|
|
168
|
+
private onPointerUp(event: PointerEvent) {
|
|
169
|
+
if (!event.isPrimary) return;
|
|
170
|
+
|
|
171
|
+
const downPosition = this.pointerDownPosition;
|
|
172
|
+
this.pointerDownPosition = undefined;
|
|
115
173
|
if (!downPosition) return;
|
|
116
174
|
|
|
175
|
+
// Skip the GPU pick entirely when nobody is listening for the result.
|
|
176
|
+
if (!this.shouldClickPick()) return;
|
|
177
|
+
|
|
178
|
+
const tolerance =
|
|
179
|
+
event.pointerType === "touch"
|
|
180
|
+
? TAP_PIXEL_TOLERANCE
|
|
181
|
+
: CLICK_PIXEL_TOLERANCE;
|
|
117
182
|
if (
|
|
118
|
-
isClickGesture(
|
|
183
|
+
isClickGesture(
|
|
184
|
+
downPosition,
|
|
185
|
+
new Vector2(event.clientX, event.clientY),
|
|
186
|
+
tolerance,
|
|
187
|
+
)
|
|
119
188
|
) {
|
|
120
|
-
this.
|
|
189
|
+
this.onClickPick(event);
|
|
121
190
|
}
|
|
122
191
|
}
|
|
123
192
|
|
|
193
|
+
/** The browser took over the gesture (scroll, app switch): not a click. */
|
|
194
|
+
private onPointerCancel() {
|
|
195
|
+
this.pointerDownPosition = undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
124
198
|
enablePick(bPick: boolean) {
|
|
125
199
|
if (bPick) {
|
|
126
|
-
this.element.addEventListener("
|
|
127
|
-
this.element.addEventListener("
|
|
200
|
+
this.element.addEventListener("pointerdown", this.pointerDownHandler);
|
|
201
|
+
this.element.addEventListener("pointerup", this.pointerUpHandler);
|
|
202
|
+
this.element.addEventListener("pointercancel", this.pointerCancelHandler);
|
|
203
|
+
this.element.addEventListener("pointermove", this.hoverMoveHandler);
|
|
204
|
+
this.element.addEventListener("pointerleave", this.hoverLeaveHandler);
|
|
128
205
|
} else {
|
|
129
|
-
this.element.removeEventListener("
|
|
130
|
-
this.element.removeEventListener("
|
|
206
|
+
this.element.removeEventListener("pointerdown", this.pointerDownHandler);
|
|
207
|
+
this.element.removeEventListener("pointerup", this.pointerUpHandler);
|
|
208
|
+
this.element.removeEventListener(
|
|
209
|
+
"pointercancel",
|
|
210
|
+
this.pointerCancelHandler,
|
|
211
|
+
);
|
|
212
|
+
this.element.removeEventListener("pointermove", this.hoverMoveHandler);
|
|
213
|
+
this.element.removeEventListener("pointerleave", this.hoverLeaveHandler);
|
|
214
|
+
this.cancelPendingHoverPick();
|
|
131
215
|
}
|
|
132
216
|
}
|
|
133
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Hover picking: schedules at most one GPU pick per animation frame from
|
|
220
|
+
* the latest pointermove position. Suppressed while any button is pressed
|
|
221
|
+
* (Camera drags would otherwise trigger a pick per frame. For touch,
|
|
222
|
+
* contact itself sets `buttons`, so touch never hover-picks) and while
|
|
223
|
+
* `shouldHoverPick` returns false, so pages without hover listeners pay
|
|
224
|
+
* nothing.
|
|
225
|
+
*/
|
|
226
|
+
private onHoverPointerMove(event: PointerEvent) {
|
|
227
|
+
if (event.buttons !== 0 || !this.shouldHoverPick()) return;
|
|
228
|
+
|
|
229
|
+
if (!this.hoverPosition) this.hoverPosition = new Vector2();
|
|
230
|
+
this.hoverPosition.set(event.clientX, event.clientY);
|
|
231
|
+
|
|
232
|
+
if (this.hoverRafId !== undefined) return;
|
|
233
|
+
this.hoverRafId = requestAnimationFrame(() => {
|
|
234
|
+
this.hoverRafId = undefined;
|
|
235
|
+
const position = this.hoverPosition;
|
|
236
|
+
if (!position) return;
|
|
237
|
+
this.hoverPosition = undefined;
|
|
238
|
+
|
|
239
|
+
// Listeners may have been removed between the pointermove that
|
|
240
|
+
// scheduled this frame and now. Re-check so no orphan pick runs.
|
|
241
|
+
if (!this.shouldHoverPick()) return;
|
|
242
|
+
|
|
243
|
+
const batchId = this.pickBatchIdAt(position.x, position.y);
|
|
244
|
+
this.onHoverCallback(batchId > 0 ? [batchId] : []);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private onHoverPointerLeave() {
|
|
249
|
+
this.cancelPendingHoverPick();
|
|
250
|
+
// Report "nothing hovered" so diff-based enter/leave synthesis can
|
|
251
|
+
// close out the current hover when the cursor exits the canvas.
|
|
252
|
+
if (this.shouldHoverPick()) {
|
|
253
|
+
this.onHoverCallback([]);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private cancelPendingHoverPick() {
|
|
258
|
+
if (this.hoverRafId !== undefined) {
|
|
259
|
+
cancelAnimationFrame(this.hoverRafId);
|
|
260
|
+
this.hoverRafId = undefined;
|
|
261
|
+
}
|
|
262
|
+
this.hoverPosition = undefined;
|
|
263
|
+
}
|
|
264
|
+
|
|
134
265
|
/**
|
|
135
266
|
* Moves every pickable raw (visible, currently parented) into the
|
|
136
267
|
* dedicated `pickScene` and activates the picking uniforms on its
|
|
@@ -224,10 +355,27 @@ export class PickHelper {
|
|
|
224
355
|
this.debugBufferView.render(this._renderer, this.debugRenderTarget);
|
|
225
356
|
}
|
|
226
357
|
|
|
227
|
-
private
|
|
358
|
+
private onClickPick(event: PointerEvent) {
|
|
359
|
+
const radius =
|
|
360
|
+
event.pointerType === "touch" ? TOUCH_PICK_RADIUS : PICK_RADIUS;
|
|
361
|
+
const batchId = this.pickBatchIdAt(event.clientX, event.clientY, radius);
|
|
362
|
+
const pickArr = batchId > 0 ? [batchId] : [];
|
|
363
|
+
this.onPickCallback(pickArr);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Runs the pick render in a search window around the given client
|
|
368
|
+
* coordinates and returns the non-zero batch ID closest to its center
|
|
369
|
+
* (0 when nothing pickable is inside the window).
|
|
370
|
+
*/
|
|
371
|
+
private pickBatchIdAt(
|
|
372
|
+
clientX: number,
|
|
373
|
+
clientY: number,
|
|
374
|
+
radiusCss: number = PICK_RADIUS,
|
|
375
|
+
): number {
|
|
228
376
|
const rect = this.element.getBoundingClientRect();
|
|
229
|
-
const x =
|
|
230
|
-
const y =
|
|
377
|
+
const x = clientX - rect.left;
|
|
378
|
+
const y = clientY - rect.top;
|
|
231
379
|
|
|
232
380
|
const pixelRatio = this._renderer.getPixelRatio();
|
|
233
381
|
const fullWidth = this._renderer.getContext().drawingBufferWidth;
|
|
@@ -249,31 +397,58 @@ export class PickHelper {
|
|
|
249
397
|
);
|
|
250
398
|
const readY = fullHeight - 1 - pixelY;
|
|
251
399
|
|
|
400
|
+
// The search window in device pixels, clamped to the viewport.
|
|
401
|
+
const radius = Math.max(0, Math.round(radiusCss * pixelRatio));
|
|
402
|
+
const minX = Math.max(0, pixelX - radius);
|
|
403
|
+
const maxX = Math.min(fullWidth - 1, pixelX + radius);
|
|
404
|
+
const minY = Math.max(0, readY - radius);
|
|
405
|
+
const maxY = Math.min(fullHeight - 1, readY + radius);
|
|
406
|
+
const width = maxX - minX + 1;
|
|
407
|
+
const height = maxY - minY + 1;
|
|
408
|
+
|
|
252
409
|
// Keep the camera projection unchanged for wide lines / screen-space
|
|
253
|
-
// expanded shaders, and limit fragment work to
|
|
254
|
-
this._renderer.setScissor(
|
|
410
|
+
// expanded shaders, and limit fragment work to the window via scissor.
|
|
411
|
+
this._renderer.setScissor(minX, minY, width, height);
|
|
255
412
|
this._renderer.setScissorTest(true);
|
|
256
413
|
|
|
257
414
|
this.processRender(this.pickRenderTarget, pickingCoord);
|
|
258
415
|
|
|
416
|
+
if (this.pixelBuffer.length < width * height * 4) {
|
|
417
|
+
this.pixelBuffer = new Uint8Array(width * height * 4);
|
|
418
|
+
}
|
|
259
419
|
this._renderer.readRenderTargetPixels(
|
|
260
420
|
this.pickRenderTarget,
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
421
|
+
minX,
|
|
422
|
+
minY,
|
|
423
|
+
width,
|
|
424
|
+
height,
|
|
265
425
|
this.pixelBuffer,
|
|
266
426
|
);
|
|
267
427
|
|
|
268
428
|
this._renderer.setScissorTest(false);
|
|
269
429
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
430
|
+
// The non-zero batch id nearest to the pointer wins, so a small feature
|
|
431
|
+
// beside a large one is still pickable by aiming at it.
|
|
432
|
+
let bestId = 0;
|
|
433
|
+
let bestDistSq = Infinity;
|
|
434
|
+
for (let row = 0; row < height; row++) {
|
|
435
|
+
for (let col = 0; col < width; col++) {
|
|
436
|
+
const i = (row * width + col) * 4;
|
|
437
|
+
const id =
|
|
438
|
+
(this.pixelBuffer[i] << 16) +
|
|
439
|
+
(this.pixelBuffer[i + 1] << 8) +
|
|
440
|
+
this.pixelBuffer[i + 2];
|
|
441
|
+
if (id === 0) continue;
|
|
442
|
+
const dx = minX + col - pixelX;
|
|
443
|
+
const dy = minY + row - readY;
|
|
444
|
+
const distSq = dx * dx + dy * dy;
|
|
445
|
+
if (distSq < bestDistSq) {
|
|
446
|
+
bestDistSq = distSq;
|
|
447
|
+
bestId = id;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return bestId;
|
|
277
452
|
}
|
|
278
453
|
|
|
279
454
|
public dispose() {
|
package/src/renderOrder.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
export const FEATURE_RENDER_ORDER = 1;
|
|
2
|
+
/**
|
|
3
|
+
* Polylines (strokes) must draw after polygon fills. In the per-tile bake
|
|
4
|
+
* scene, sibling fill/stroke meshes are created asynchronously and land in
|
|
5
|
+
* the scene in arbitrary order, so a fill baked later paints over the stroke.
|
|
6
|
+
* In the MRT scene, polyline materials skip the depth test but still write
|
|
7
|
+
* depth, so a coplanar fill drawn after a stroke z-fights against the
|
|
8
|
+
* stroke's depth per fragment (mottled boundaries). Encoding the stacking in
|
|
9
|
+
* renderOrder makes both cases deterministic.
|
|
10
|
+
*/
|
|
11
|
+
export const STROKE_RENDER_ORDER = FEATURE_RENDER_ORDER + 1;
|
|
2
12
|
export const SKY_RENDER_ORDER = 100;
|
|
3
13
|
export const STARS_RENDER_ORDER = SKY_RENDER_ORDER + 1;
|
|
@@ -37,6 +37,7 @@ vi.mock("@navaramap/engine-api", () => {
|
|
|
37
37
|
};
|
|
38
38
|
return {
|
|
39
39
|
default: vi.fn().mockResolvedValue(undefined),
|
|
40
|
+
angleToRadian: (deg: number) => (deg * Math.PI) / 180,
|
|
40
41
|
terrainPositionsToTiles: vi.fn(
|
|
41
42
|
(geographic: boolean, level: number, positions: Float64Array) => {
|
|
42
43
|
const result = new Uint32Array(positions.length);
|
|
@@ -87,12 +88,11 @@ vi.mock("@navaramap/engine-api", () => {
|
|
|
87
88
|
};
|
|
88
89
|
});
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
};
|
|
95
|
-
const TOKYO = { lat: (35.68 * Math.PI) / 180, lng: (139.76 * Math.PI) / 180 };
|
|
91
|
+
// Public inputs are degrees; the mocked engine functions still receive the
|
|
92
|
+
// radians the implementation converts to.
|
|
93
|
+
const KARIYA = { lat: 35.0, lng: 137.0 };
|
|
94
|
+
const NEARBY = { lat: 35.001, lng: 137.001 };
|
|
95
|
+
const TOKYO = { lat: 35.68, lng: 139.76 };
|
|
96
96
|
|
|
97
97
|
const SOURCE: QuantizedMeshSource = {
|
|
98
98
|
type: "quantized-mesh",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LatLng } from "@navaramap/core";
|
|
2
2
|
import {
|
|
3
|
+
angleToRadian,
|
|
3
4
|
sampleQuantizedMeshHeights,
|
|
4
5
|
sampleRasterDemHeights,
|
|
5
6
|
terrainPositionsToTiles,
|
|
@@ -29,9 +30,9 @@ export type SampleTerrainOptions = {
|
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
export type SampledTerrainPosition = {
|
|
32
|
-
/** Latitude in
|
|
33
|
+
/** Latitude in degrees, echoed from the input. */
|
|
33
34
|
lat: number;
|
|
34
|
-
/** Longitude in
|
|
35
|
+
/** Longitude in degrees, echoed from the input. */
|
|
35
36
|
lng: number;
|
|
36
37
|
/**
|
|
37
38
|
* Terrain height in meters, or `undefined` when no tile covering the
|
|
@@ -104,7 +105,7 @@ type SourceConfig = {
|
|
|
104
105
|
*
|
|
105
106
|
* @param source - A `quantized-mesh` or `raster-dem` source description (the
|
|
106
107
|
* same shape `addSource` accepts)
|
|
107
|
-
* @param positions - Geodetic positions (lat/lng in
|
|
108
|
+
* @param positions - Geodetic positions (lat/lng in degrees)
|
|
108
109
|
* @param options - Optional abort signal and fixed-level sampling
|
|
109
110
|
* @returns One result per input position, in the same order (the input
|
|
110
111
|
* objects are not mutated)
|
|
@@ -123,10 +124,11 @@ export async function sampleTerrainMostDetailed(
|
|
|
123
124
|
// Fixed-level sampling never falls back to parents.
|
|
124
125
|
const floorLevel = options?.level != null ? startLevel : config.minZoom;
|
|
125
126
|
|
|
127
|
+
// The engine samplers work in radians; inputs and echoed outputs are degrees.
|
|
126
128
|
const flat = new Float64Array(positions.length * 2);
|
|
127
129
|
positions.forEach((p, i) => {
|
|
128
|
-
flat[i * 2] = p.lng;
|
|
129
|
-
flat[i * 2 + 1] = p.lat;
|
|
130
|
+
flat[i * 2] = angleToRadian(p.lng);
|
|
131
|
+
flat[i * 2 + 1] = angleToRadian(p.lat);
|
|
130
132
|
});
|
|
131
133
|
|
|
132
134
|
// Group positions by their containing tile at the starting level; each
|
package/src/type/index.ts
CHANGED
|
@@ -75,6 +75,27 @@ type ConvertColorFields<T> = {
|
|
|
75
75
|
*/
|
|
76
76
|
type WithColorSupport<T> = ConvertColorFields<T>;
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Source geometry categories a vector appearance can consume via its
|
|
80
|
+
* `geometryTypes` option. Each appearance defaults to its native geometry only
|
|
81
|
+
* (`"point"` for point/billboard/text, `"line"` for polyline); adding entries
|
|
82
|
+
* derives extra representations, e.g. `polyline: { geometryTypes: ["line",
|
|
83
|
+
* "polygon"] }` also renders polygon boundary rings as polylines.
|
|
84
|
+
*/
|
|
85
|
+
export type SourceGeometryType = "point" | "line" | "polygon";
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Helper type to narrow the generated `geometryTypes?: string[]` material
|
|
89
|
+
* fields to the {@link SourceGeometryType} literal union.
|
|
90
|
+
*/
|
|
91
|
+
type ConvertGeometryTypeFields<T> = {
|
|
92
|
+
[K in keyof T]: K extends "geometryTypes"
|
|
93
|
+
? SourceGeometryType[] | Extract<T[K], undefined>
|
|
94
|
+
: T[K] extends object | undefined
|
|
95
|
+
? ConvertGeometryTypeFields<T[K]> | Extract<T[K], undefined>
|
|
96
|
+
: T[K];
|
|
97
|
+
};
|
|
98
|
+
|
|
78
99
|
/**
|
|
79
100
|
* A reference to a {@link Source}: either the source handle returned by
|
|
80
101
|
* `addSource`, or its `id` string.
|
|
@@ -87,7 +108,7 @@ export type SourceLayerBase<Layer extends { source?: string | undefined }> =
|
|
|
87
108
|
};
|
|
88
109
|
|
|
89
110
|
type VectorLayerBase = WithColorSupport<
|
|
90
|
-
Layer<VectorLayerDescription & { type: "vector" }
|
|
111
|
+
ConvertGeometryTypeFields<Layer<VectorLayerDescription & { type: "vector" }>>
|
|
91
112
|
>;
|
|
92
113
|
|
|
93
114
|
/**
|