@math.gl/geospatial 5.0.0-alpha.11 → 5.0.0-alpha.13

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.
Files changed (59) hide show
  1. package/LICENSE +7 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +4 -2
  4. package/dist/constants.js.map +1 -1
  5. package/dist/ellipsoid-helpers/ellipsoid-transform.d.ts.map +1 -1
  6. package/dist/ellipsoid-helpers/ellipsoid-transform.js +46 -15
  7. package/dist/ellipsoid-helpers/ellipsoid-transform.js.map +1 -1
  8. package/dist/ellipsoid-helpers/scale-to-geodetic-surface.d.ts +2 -0
  9. package/dist/ellipsoid-helpers/scale-to-geodetic-surface.d.ts.map +1 -1
  10. package/dist/ellipsoid-helpers/scale-to-geodetic-surface.js +46 -13
  11. package/dist/ellipsoid-helpers/scale-to-geodetic-surface.js.map +1 -1
  12. package/dist/ellipsoid-occluder.d.ts +28 -0
  13. package/dist/ellipsoid-occluder.d.ts.map +1 -0
  14. package/dist/ellipsoid-occluder.js +112 -0
  15. package/dist/ellipsoid-occluder.js.map +1 -0
  16. package/dist/ellipsoid-tangent-plane.d.ts.map +1 -1
  17. package/dist/ellipsoid-tangent-plane.js +4 -2
  18. package/dist/ellipsoid-tangent-plane.js.map +1 -1
  19. package/dist/ellipsoid.d.ts +11 -0
  20. package/dist/ellipsoid.d.ts.map +1 -1
  21. package/dist/ellipsoid.js +77 -7
  22. package/dist/ellipsoid.js.map +1 -1
  23. package/dist/geographic-tiles.d.ts +24 -0
  24. package/dist/geographic-tiles.d.ts.map +1 -0
  25. package/dist/geographic-tiles.js +72 -0
  26. package/dist/geographic-tiles.js.map +1 -0
  27. package/dist/globe-horizon-bounds.d.ts +13 -0
  28. package/dist/globe-horizon-bounds.d.ts.map +1 -0
  29. package/dist/globe-horizon-bounds.js +59 -0
  30. package/dist/globe-horizon-bounds.js.map +1 -0
  31. package/dist/index.cjs +557 -40
  32. package/dist/index.cjs.map +3 -3
  33. package/dist/index.d.ts +7 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +4 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/lng-lat-rectangle.d.ts.map +1 -1
  38. package/dist/lng-lat-rectangle.js +4 -2
  39. package/dist/lng-lat-rectangle.js.map +1 -1
  40. package/dist/make-obb-from-region.d.ts.map +1 -1
  41. package/dist/make-obb-from-region.js +4 -2
  42. package/dist/make-obb-from-region.js.map +1 -1
  43. package/dist/type-utils.d.ts +2 -0
  44. package/dist/type-utils.d.ts.map +1 -1
  45. package/dist/type-utils.js +25 -2
  46. package/dist/type-utils.js.map +1 -1
  47. package/package.json +5 -5
  48. package/src/constants.ts +4 -2
  49. package/src/ellipsoid-helpers/ellipsoid-transform.ts +62 -15
  50. package/src/ellipsoid-helpers/scale-to-geodetic-surface.ts +48 -14
  51. package/src/ellipsoid-occluder.ts +135 -0
  52. package/src/ellipsoid-tangent-plane.ts +4 -2
  53. package/src/ellipsoid.ts +91 -7
  54. package/src/geographic-tiles.ts +87 -0
  55. package/src/globe-horizon-bounds.ts +68 -0
  56. package/src/index.ts +15 -1
  57. package/src/lng-lat-rectangle.ts +4 -2
  58. package/src/make-obb-from-region.ts +4 -2
  59. package/src/type-utils.ts +30 -2
package/dist/index.cjs CHANGED
@@ -20,16 +20,161 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var index_exports = {};
21
21
  __export(index_exports, {
22
22
  Ellipsoid: () => Ellipsoid,
23
+ EllipsoidOccluder: () => EllipsoidOccluder,
23
24
  EllipsoidTangentPlane: () => EllipsoidTangentPlane,
24
25
  LngLatRectangle: () => LngLatRectangle,
26
+ getGeographicTile: () => getGeographicTile,
27
+ getGeographicTileBounds: () => getGeographicTileBounds,
28
+ getGeographicTileRanges: () => getGeographicTileRanges,
29
+ getGlobeHorizonBounds: () => getGlobeHorizonBounds,
25
30
  isWGS84: () => isWGS84,
26
- makeOBBFromRegion: () => makeOBBFromRegion
31
+ makeOBBFromRegion: () => makeOBBFromRegion,
32
+ splitGlobeBounds: () => splitGlobeBounds
27
33
  });
28
34
  module.exports = __toCommonJS(index_exports);
29
35
 
30
36
  // dist/ellipsoid.js
31
37
  var import_core4 = require("@math.gl/core");
32
38
 
39
+ // ../core/src/spheroid.ts
40
+ function spheroidToCartesian(point, geometry, result = point) {
41
+ const longitude = point.x, latitude = point.y, height = point.z;
42
+ const { semiMajorAxis: a, semiMinorAxis: b, eccentricitySquared: es } = geometry;
43
+ const sin = Math.sin(latitude), cos = Math.cos(latitude);
44
+ const ratio = b / a;
45
+ const radius = es < 0.9 ? a / Math.sqrt(1 - es * sin * sin) : a / Math.hypot(cos, ratio * sin);
46
+ const x = (radius + height) * cos * Math.cos(longitude);
47
+ const y = (radius + height) * cos * Math.sin(longitude);
48
+ const z = (radius * (es < 0.9 ? 1 - es : ratio * ratio) + height) * sin;
49
+ return commit(result, x, y, z);
50
+ }
51
+ function cartesianToSpheroid(point, geometry, result = point) {
52
+ const x = point.x, y = point.y, z = point.z;
53
+ const { semiMajorAxis: a, semiMinorAxis: b, eccentricitySquared: es } = geometry;
54
+ const p = Math.hypot(x, y), rr = Math.hypot(x, y, z);
55
+ if (!(rr > 0) || !Number.isFinite(rr)) return false;
56
+ if (es === 0) return commit(result, p === 0 ? 0 : Math.atan2(y, x), Math.atan2(z, p), rr - a);
57
+ if (p === 0) return commit(result, 0, Math.sign(z) * Math.PI / 2, Math.abs(z) - b);
58
+ const horizontal = p / a, vertical = z / b;
59
+ if (horizontal * horizontal + vertical * vertical < 1 - 16 * Number.EPSILON)
60
+ return interiorInverse(x, y, z, p, a, b, result);
61
+ if (es >= 0.9) return exteriorInverse(x, y, z, p, a, b, result);
62
+ const ct = z / rr, st = p / rr;
63
+ let rx = 1 / Math.sqrt(1 - es * (2 - es) * st * st);
64
+ let cos = st * (1 - es) * rx, sin = ct * rx;
65
+ for (let i = 0; i < 30; i++) {
66
+ const rn = a / Math.sqrt(1 - es * sin * sin);
67
+ const height = p * cos + z * sin - rn * (1 - es * sin * sin);
68
+ const rk = es * rn / (rn + height);
69
+ rx = 1 / Math.sqrt(1 - rk * (2 - rk) * st * st);
70
+ const nextCos = st * (1 - rk) * rx, nextSin = ct * rx;
71
+ if (Math.abs(nextSin * cos - nextCos * sin) <= 1e-12) {
72
+ if (!Number.isFinite(height) || !Number.isFinite(nextSin) || !Number.isFinite(nextCos)) break;
73
+ return commit(result, Math.atan2(y, x), Math.atan2(nextSin, Math.abs(nextCos)), height);
74
+ }
75
+ cos = nextCos;
76
+ sin = nextSin;
77
+ }
78
+ return exteriorInverse(x, y, z, p, a, b, result);
79
+ }
80
+ function interiorInverse(x, y, z, p, a, b, result) {
81
+ const ratio = b / a, ratio2 = ratio * ratio;
82
+ const horizontal = p / a, vertical = z / a, delta = 1 - ratio2;
83
+ if (!(ratio2 > 0) || ratio > 1) return false;
84
+ if (z === 0) {
85
+ if (horizontal < delta) return false;
86
+ return commit(result, Math.atan2(y, x), 0, p - a);
87
+ }
88
+ const aa = a * a, bb = b * b;
89
+ const ratioError = Number.isFinite(aa) && aa > 0 ? (bb - ratio2 * aa + productError(b, b, bb) - ratio2 * productError(a, a, aa) - productError(ratio2, aa, ratio2 * aa)) / aa : 0;
90
+ const pp = p * p, xx = x * x, yy = y * y, sum = xx + yy;
91
+ const radiusError = Number.isFinite(pp) && pp > 0 ? (sum - pp + sumError(xx, yy, sum) + productError(x, x, xx) + productError(y, y, yy) - productError(p, p, pp)) / (2 * p) : 0;
92
+ const shift = a * ratio2, base = p - a + shift;
93
+ const correction = radiusError + sumError(p - a, shift, base) + productError(a, ratio2, shift) + a * ratioError;
94
+ const weighted = Math.abs(vertical) * ratio;
95
+ if (!(weighted > 0)) return false;
96
+ let lower = 0, upper = ratio2;
97
+ let t = Math.max(weighted, horizontal - delta);
98
+ if (!(t > lower && t < upper)) t = upper / 2;
99
+ for (let i = 0; i < 128; i++) {
100
+ const equatorial = delta + t;
101
+ const u = horizontal / equatorial, v = weighted / t;
102
+ const at = a * t, difference = base - at;
103
+ const offset = (difference + (sumError(base, -at, difference) + correction - productError(a, t, at))) / (a * equatorial);
104
+ const term = offset * (u + 1);
105
+ const residual = term + v * v;
106
+ if (Math.abs(residual) <= 4 * Number.EPSILON * (Math.abs(term) + v * v) || upper - lower <= 4 * Number.EPSILON * t) {
107
+ const latitude = Math.atan2(vertical / t, horizontal / equatorial);
108
+ const sin = Math.sin(latitude), cos = Math.cos(latitude);
109
+ return commit(
110
+ result,
111
+ Math.atan2(y, x),
112
+ latitude,
113
+ p * cos + z * sin - a * Math.hypot(cos, ratio * sin)
114
+ );
115
+ }
116
+ if (residual > 0) lower = t;
117
+ else upper = t;
118
+ const derivative = -2 * (u * u / equatorial + v * v / t);
119
+ const next = t - residual / derivative;
120
+ const candidate = Number.isFinite(next) && next > lower && next < upper ? next : lower + (upper - lower) / 2;
121
+ if (candidate === t) {
122
+ const latitude = Math.atan2(vertical / t, horizontal / equatorial);
123
+ return commit(
124
+ result,
125
+ Math.atan2(y, x),
126
+ latitude,
127
+ p * Math.cos(latitude) + z * Math.sin(latitude) - a * Math.hypot(Math.cos(latitude), ratio * Math.sin(latitude))
128
+ );
129
+ }
130
+ t = candidate;
131
+ }
132
+ return false;
133
+ }
134
+ function sumError(x, y, sum) {
135
+ const part = sum - x;
136
+ return x - (sum - part) + (y - part);
137
+ }
138
+ function productError(x, y, product) {
139
+ const sx = 134217729 * x, sy = 134217729 * y;
140
+ if (!Number.isFinite(sx) || !Number.isFinite(sy)) return 0;
141
+ const hx = sx - (sx - x), hy = sy - (sy - y), lx = x - hx, ly = y - hy;
142
+ return hx * hy - product + hx * ly + lx * hy + lx * ly;
143
+ }
144
+ function exteriorInverse(x, y, z, p, a, b, result) {
145
+ const ratio = b / a, ratio2 = ratio * ratio, horizontal = p / a, vertical = z / a;
146
+ const norm = Math.hypot(horizontal, z / b);
147
+ if (!Number.isFinite(norm) || norm < 1 - 8 * Number.EPSILON || !(ratio2 > 0)) return false;
148
+ let lower = 0, upper = Math.hypot(horizontal, vertical * ratio);
149
+ if (!Number.isFinite(upper)) return false;
150
+ let multiplier = norm <= 1 + 8 * Number.EPSILON ? 0 : upper / 2;
151
+ for (let i = 0; i < 96; i++) {
152
+ const equatorial = 1 + multiplier, polar = ratio2 + multiplier;
153
+ const u = horizontal / equatorial, v = vertical * ratio / polar;
154
+ const length2 = Math.hypot(u, v);
155
+ const residual = length2 - 1;
156
+ if (Math.abs(residual) <= 4 * Number.EPSILON) {
157
+ const latitude = Math.atan2(vertical / polar, horizontal / equatorial);
158
+ const sin = Math.sin(latitude), cos = Math.cos(latitude);
159
+ const height = p * cos + z * sin - a * Math.hypot(cos, ratio * sin);
160
+ return commit(result, Math.atan2(y, x), latitude, height);
161
+ }
162
+ if (residual > 0) lower = multiplier;
163
+ else upper = multiplier;
164
+ const derivative = -(u * u / equatorial + v * v / polar) / length2;
165
+ const next = multiplier - residual / derivative;
166
+ multiplier = Number.isFinite(next) && next > lower && next < upper ? next : (lower + upper) / 2;
167
+ }
168
+ return false;
169
+ }
170
+ function commit(result, x, y, z) {
171
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) return false;
172
+ result.x = x;
173
+ result.y = y;
174
+ result.z = z;
175
+ return true;
176
+ }
177
+
33
178
  // ../core/src/gl-matrix/vec3.ts
34
179
  function length(a) {
35
180
  const x = a[0];
@@ -88,25 +233,24 @@ function fromCartographic(cartographic, result = [], map = identity) {
88
233
  function fromCartographicToRadians(cartographic, vector = []) {
89
234
  return fromCartographic(cartographic, vector, import_core.config._cartographicRadians ? identity : import_core.toRadians);
90
235
  }
91
- function toCartographic(vector, cartographic, map = identity) {
236
+ function toCartographicFromRadiansComponents(longitude, latitude, height, cartographic) {
237
+ const map = import_core.config._cartographicRadians ? identity : import_core.toDegrees;
238
+ const mappedLongitude = map(longitude), mappedLatitude = map(latitude);
92
239
  if ("longitude" in cartographic) {
93
- cartographic.longitude = map(vector[0]);
94
- cartographic.latitude = map(vector[1]);
95
- cartographic.height = vector[2];
240
+ cartographic.longitude = mappedLongitude;
241
+ cartographic.latitude = mappedLatitude;
242
+ cartographic.height = height;
96
243
  } else if ("x" in cartographic) {
97
- cartographic.x = map(vector[0]);
98
- cartographic.y = map(vector[1]);
99
- cartographic.z = vector[2];
244
+ cartographic.x = mappedLongitude;
245
+ cartographic.y = mappedLatitude;
246
+ cartographic.z = height;
100
247
  } else {
101
- cartographic[0] = map(vector[0]);
102
- cartographic[1] = map(vector[1]);
103
- cartographic[2] = vector[2];
248
+ cartographic[0] = mappedLongitude;
249
+ cartographic[1] = mappedLatitude;
250
+ cartographic[2] = height;
104
251
  }
105
252
  return cartographic;
106
253
  }
107
- function toCartographicFromRadians(vector, cartographic) {
108
- return toCartographic(vector, cartographic, import_core.config._cartographicRadians ? identity : import_core.toDegrees);
109
- }
110
254
  function isWGS84(vector) {
111
255
  if (!vector) {
112
256
  return false;
@@ -121,8 +265,93 @@ function isWGS84(vector) {
121
265
 
122
266
  // dist/ellipsoid-helpers/ellipsoid-transform.js
123
267
  var import_core2 = require("@math.gl/core");
268
+
269
+ // ../core/src/local-frame.ts
270
+ function createLocalFrameBasis() {
271
+ return { eastX: 0, eastY: 1, northZ: 1, upX: 1, upY: 0, upZ: 0 };
272
+ }
273
+ function eastNorthUpBasisFromDirections(east, up, result) {
274
+ const ex = east.x, ey = east.y, ez = east.z;
275
+ const ux = up.x, uy = up.y, uz = up.z;
276
+ if (ez !== 0) return false;
277
+ return commitBasis(result, ex, ey, ux * ey - uy * ex, ux, uy, uz);
278
+ }
279
+ function localFrameToMatrix(basis, origin, firstAxis, secondAxis, thirdAxis, result) {
280
+ const first = axisCode(firstAxis), second = axisCode(secondAxis), third = axisCode(thirdAxis);
281
+ const a = Math.abs(first), b = Math.abs(second), c = Math.abs(third);
282
+ if (!a || !b || !c || a === b || a === c || b === c) return false;
283
+ const parity = a % 3 + 1 === b ? 1 : -1;
284
+ if (parity * Math.sign(first * second * third) !== 1 || !Array.isArray(result) && result.length < 16)
285
+ return false;
286
+ const ex = basis.eastX, ey = basis.eastY, nz = basis.northZ;
287
+ const ux = basis.upX, uy = basis.upY, uz = basis.upZ;
288
+ const nx = -uz * ey, ny = uz * ex;
289
+ const x = origin.x, y = origin.y, z = origin.z;
290
+ const ax = (a === 1 ? ex : a === 2 ? nx : ux) * Math.sign(first);
291
+ const ay = (a === 1 ? ey : a === 2 ? ny : uy) * Math.sign(first);
292
+ const az = (a === 1 ? 0 : a === 2 ? nz : uz) * Math.sign(first);
293
+ const bx = (b === 1 ? ex : b === 2 ? nx : ux) * Math.sign(second);
294
+ const by = (b === 1 ? ey : b === 2 ? ny : uy) * Math.sign(second);
295
+ const bz = (b === 1 ? 0 : b === 2 ? nz : uz) * Math.sign(second);
296
+ const cx = (c === 1 ? ex : c === 2 ? nx : ux) * Math.sign(third);
297
+ const cy = (c === 1 ? ey : c === 2 ? ny : uy) * Math.sign(third);
298
+ const cz = (c === 1 ? 0 : c === 2 ? nz : uz) * Math.sign(third);
299
+ if (!Number.isFinite(ax) || !Number.isFinite(ay) || !Number.isFinite(az) || !Number.isFinite(bx) || !Number.isFinite(by) || !Number.isFinite(bz) || !Number.isFinite(cx) || !Number.isFinite(cy) || !Number.isFinite(cz) || !Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z))
300
+ return false;
301
+ if (result instanceof Float32Array && (!Number.isFinite(Math.fround(ax)) || !Number.isFinite(Math.fround(ay)) || !Number.isFinite(Math.fround(az)) || !Number.isFinite(Math.fround(bx)) || !Number.isFinite(Math.fround(by)) || !Number.isFinite(Math.fround(bz)) || !Number.isFinite(Math.fround(cx)) || !Number.isFinite(Math.fround(cy)) || !Number.isFinite(Math.fround(cz)) || !Number.isFinite(Math.fround(x)) || !Number.isFinite(Math.fround(y)) || !Number.isFinite(Math.fround(z))))
302
+ return false;
303
+ result[0] = ax;
304
+ result[1] = ay;
305
+ result[2] = az;
306
+ result[3] = 0;
307
+ result[4] = bx;
308
+ result[5] = by;
309
+ result[6] = bz;
310
+ result[7] = 0;
311
+ result[8] = cx;
312
+ result[9] = cy;
313
+ result[10] = cz;
314
+ result[11] = 0;
315
+ result[12] = x;
316
+ result[13] = y;
317
+ result[14] = z;
318
+ result[15] = 1;
319
+ return true;
320
+ }
321
+ function axisCode(axis) {
322
+ switch (axis) {
323
+ case "east":
324
+ return 1;
325
+ case "west":
326
+ return -1;
327
+ case "north":
328
+ return 2;
329
+ case "south":
330
+ return -2;
331
+ case "up":
332
+ return 3;
333
+ case "down":
334
+ return -3;
335
+ default:
336
+ return 0;
337
+ }
338
+ }
339
+ function commitBasis(result, ex, ey, nz, ux, uy, uz) {
340
+ if (!Number.isFinite(ex) || !Number.isFinite(ey) || !Number.isFinite(nz) || !Number.isFinite(ux) || !Number.isFinite(uy) || !Number.isFinite(uz))
341
+ return false;
342
+ result.eastX = ex;
343
+ result.eastY = ey;
344
+ result.northZ = nz;
345
+ result.upX = ux;
346
+ result.upY = uy;
347
+ result.upZ = uz;
348
+ return true;
349
+ }
350
+
351
+ // dist/ellipsoid-helpers/ellipsoid-transform.js
124
352
  var EPSILON14 = 1e-14;
125
353
  var scratchOrigin = new import_core2.Vector3();
354
+ var scratchBasis = createLocalFrameBasis();
126
355
  var VECTOR_PRODUCT_LOCAL_FRAME = {
127
356
  up: {
128
357
  south: "east",
@@ -188,6 +417,20 @@ function localFrameToFixedFrame(ellipsoid, firstAxis, secondAxis, thirdAxis, car
188
417
  let thirdAxisVector;
189
418
  const origin = scratchOrigin.copy(cartesianOrigin);
190
419
  const atPole = (0, import_core2.equals)(origin.x, 0, EPSILON14) && (0, import_core2.equals)(origin.y, 0, EPSILON14);
420
+ const radii = ellipsoid.radii;
421
+ if (radii.x === radii.y && radii.x > 0 && radii.z > 0 && radii.z <= radii.x && (!atPole || origin.z !== 0)) {
422
+ const { east, up } = scratchAxisVectors;
423
+ if (atPole) {
424
+ east.set(0, 1, 0);
425
+ up.set(0, 0, Math.sign(origin.z));
426
+ } else {
427
+ east.set(-origin.y, origin.x, 0).normalize();
428
+ ellipsoid.geodeticSurfaceNormal(origin, up);
429
+ }
430
+ if (!eastNorthUpBasisFromDirections(east, up, scratchBasis) || !localFrameToMatrix(scratchBasis, origin, firstAxis, secondAxis, thirdAxis, result))
431
+ throw new Error("Unsupported local frame");
432
+ return result;
433
+ }
191
434
  if (atPole) {
192
435
  const sign = Math.sign(origin.z);
193
436
  firstAxisVector = scratchVector1.fromArray(degeneratePositionLocalFrame[firstAxis]);
@@ -215,21 +458,25 @@ function localFrameToFixedFrame(ellipsoid, firstAxis, secondAxis, thirdAxis, car
215
458
  secondAxisVector = scratchAxisVectors[secondAxis];
216
459
  thirdAxisVector = scratchAxisVectors[thirdAxis];
217
460
  }
218
- result[0] = firstAxisVector.x;
219
- result[1] = firstAxisVector.y;
220
- result[2] = firstAxisVector.z;
461
+ const ax = firstAxisVector.x, ay = firstAxisVector.y, az = firstAxisVector.z;
462
+ const bx = secondAxisVector.x, by = secondAxisVector.y, bz = secondAxisVector.z;
463
+ const cx = thirdAxisVector.x, cy = thirdAxisVector.y, cz = thirdAxisVector.z;
464
+ const x = origin.x, y = origin.y, z = origin.z;
465
+ result[0] = ax;
466
+ result[1] = ay;
467
+ result[2] = az;
221
468
  result[3] = 0;
222
- result[4] = secondAxisVector.x;
223
- result[5] = secondAxisVector.y;
224
- result[6] = secondAxisVector.z;
469
+ result[4] = bx;
470
+ result[5] = by;
471
+ result[6] = bz;
225
472
  result[7] = 0;
226
- result[8] = thirdAxisVector.x;
227
- result[9] = thirdAxisVector.y;
228
- result[10] = thirdAxisVector.z;
473
+ result[8] = cx;
474
+ result[9] = cy;
475
+ result[10] = cz;
229
476
  result[11] = 0;
230
- result[12] = origin.x;
231
- result[13] = origin.y;
232
- result[14] = origin.z;
477
+ result[12] = x;
478
+ result[13] = y;
479
+ result[14] = z;
233
480
  result[15] = 1;
234
481
  return result;
235
482
  }
@@ -241,10 +488,14 @@ var scaleToGeodeticSurfaceIntersection = new import_core3.Vector3();
241
488
  var scaleToGeodeticSurfaceGradient = new import_core3.Vector3();
242
489
  function scaleToGeodeticSurface(cartesian, ellipsoid, result = []) {
243
490
  const { oneOverRadii, oneOverRadiiSquared, centerToleranceSquared } = ellipsoid;
244
- scratchVector4.from(cartesian);
245
- const positionX = scratchVector4.x;
246
- const positionY = scratchVector4.y;
247
- const positionZ = scratchVector4.z;
491
+ const object = cartesian;
492
+ const positionX = "x" in cartesian ? object.x : cartesian[0];
493
+ const positionY = "x" in cartesian ? object.y : cartesian[1];
494
+ const positionZ = "x" in cartesian ? object.z : cartesian[2];
495
+ if (!Number.isFinite(positionX) || !Number.isFinite(positionY) || !Number.isFinite(positionZ)) {
496
+ return void 0;
497
+ }
498
+ scratchVector4.set(positionX, positionY, positionZ);
248
499
  const oneOverRadiiX = oneOverRadii.x;
249
500
  const oneOverRadiiY = oneOverRadii.y;
250
501
  const oneOverRadiiZ = oneOverRadii.z;
@@ -253,13 +504,13 @@ function scaleToGeodeticSurface(cartesian, ellipsoid, result = []) {
253
504
  const z2 = positionZ * positionZ * oneOverRadiiZ * oneOverRadiiZ;
254
505
  const squaredNorm = x2 + y2 + z2;
255
506
  const ratio = Math.sqrt(1 / squaredNorm);
256
- if (!Number.isFinite(ratio)) {
507
+ if (!Number.isFinite(ratio) || !Number.isFinite(squaredNorm)) {
257
508
  return void 0;
258
509
  }
259
510
  const intersection = scaleToGeodeticSurfaceIntersection;
260
- intersection.copy(cartesian).scale(ratio);
511
+ intersection.set(positionX, positionY, positionZ).scale(ratio);
261
512
  if (squaredNorm < centerToleranceSquared) {
262
- return intersection.to(result);
513
+ return writeResult(result, intersection.x, intersection.y, intersection.z);
263
514
  }
264
515
  const oneOverRadiiSquaredX = oneOverRadiiSquared.x;
265
516
  const oneOverRadiiSquaredY = oneOverRadiiSquared.y;
@@ -272,7 +523,7 @@ function scaleToGeodeticSurface(cartesian, ellipsoid, result = []) {
272
523
  let yMultiplier;
273
524
  let zMultiplier;
274
525
  let func;
275
- do {
526
+ for (let iteration = 0; iteration < 64; iteration++) {
276
527
  lambda -= correction;
277
528
  xMultiplier = 1 / (1 + lambda * oneOverRadiiSquaredX);
278
529
  yMultiplier = 1 / (1 + lambda * oneOverRadiiSquaredY);
@@ -284,21 +535,52 @@ function scaleToGeodeticSurface(cartesian, ellipsoid, result = []) {
284
535
  const yMultiplier3 = yMultiplier2 * yMultiplier;
285
536
  const zMultiplier3 = zMultiplier2 * zMultiplier;
286
537
  func = x2 * xMultiplier2 + y2 * yMultiplier2 + z2 * zMultiplier2 - 1;
538
+ if (!Number.isFinite(func))
539
+ return void 0;
540
+ if (Math.abs(func) <= import_core3._MathUtils.EPSILON12) {
541
+ return writeResult(result, positionX * xMultiplier, positionY * yMultiplier, positionZ * zMultiplier);
542
+ }
287
543
  const denominator = x2 * xMultiplier3 * oneOverRadiiSquaredX + y2 * yMultiplier3 * oneOverRadiiSquaredY + z2 * zMultiplier3 * oneOverRadiiSquaredZ;
288
544
  const derivative = -2 * denominator;
545
+ if (!Number.isFinite(derivative) || derivative === 0)
546
+ return void 0;
289
547
  correction = func / derivative;
290
- } while (Math.abs(func) > import_core3._MathUtils.EPSILON12);
291
- return scratchVector4.scale([xMultiplier, yMultiplier, zMultiplier]).to(result);
548
+ if (!Number.isFinite(correction))
549
+ return void 0;
550
+ }
551
+ return void 0;
552
+ }
553
+ function writeResult(result, x, y, z) {
554
+ if ((0, import_core3.isArray)(result)) {
555
+ result[0] = x;
556
+ result[1] = y;
557
+ result[2] = z;
558
+ } else {
559
+ const point = result;
560
+ point.x = x;
561
+ point.y = y;
562
+ point.z = z;
563
+ }
564
+ return result;
292
565
  }
293
566
 
294
567
  // dist/ellipsoid.js
568
+ var spheroidScratch = { x: 0, y: 0, z: 0 };
295
569
  var scratchVector5 = new import_core4.Vector3();
296
570
  var scratchNormal = new import_core4.Vector3();
297
571
  var scratchK = new import_core4.Vector3();
298
572
  var scratchPosition = new import_core4.Vector3();
299
573
  var scratchHeight = new import_core4.Vector3();
300
574
  var scratchCartesian = new import_core4.Vector3();
301
- var Ellipsoid = class {
575
+ var Ellipsoid = class _Ellipsoid {
576
+ /** Construct from two-axis geometry, including normalized projection ellipsoids.
577
+ * The equatorial X/Y radii are equal; no CRS or datum information is inferred.
578
+ */
579
+ static fromSpheroid(parameters) {
580
+ const { semiMajorAxis, semiMinorAxis } = parameters;
581
+ validateSpheroid(semiMajorAxis, semiMinorAxis);
582
+ return new _Ellipsoid(semiMajorAxis, semiMajorAxis, semiMinorAxis);
583
+ }
302
584
  constructor(x = 0, y = 0, z = 0) {
303
585
  this.centerToleranceSquared = import_core4._MathUtils.EPSILON1;
304
586
  (0, import_core4.assert)(x >= 0);
@@ -314,8 +596,25 @@ var Ellipsoid = class {
314
596
  if (this.radiiSquared.z !== 0) {
315
597
  this.squaredXOverSquaredZ = this.radiiSquared.x / this.radiiSquared.z;
316
598
  }
599
+ if (x === y && z > 0 && z <= x && Number.isFinite(this.radiiSquared.x) && this.radiiSquared.z > 0)
600
+ this.spheroidGeometry = Object.freeze({
601
+ semiMajorAxis: x,
602
+ semiMinorAxis: z,
603
+ eccentricitySquared: 1 - this.radiiSquared.z / this.radiiSquared.x
604
+ });
317
605
  Object.freeze(this);
318
606
  }
607
+ /** Return an owned geometry snapshot for a sphere or oblate spheroid.
608
+ * Triaxial, prolate and degenerate ellipsoids cannot be represented by this contract.
609
+ * Call during setup, outside coordinate loops.
610
+ */
611
+ toSpheroid() {
612
+ const { x, y, z } = this.radii;
613
+ if (x !== y)
614
+ throw new Error("Spheroid requires equal X/Y radii");
615
+ validateSpheroid(x, z);
616
+ return Object.freeze({ semiMajorAxis: x, semiMinorAxis: z });
617
+ }
319
618
  /** Compares this Ellipsoid against the provided Ellipsoid componentwise */
320
619
  equals(right) {
321
620
  return this === right || Boolean(right && this.radii.equals(right.radii));
@@ -325,6 +624,15 @@ var Ellipsoid = class {
325
624
  return this.radii.toString();
326
625
  }
327
626
  cartographicToCartesian(cartographic, result = [0, 0, 0]) {
627
+ if (this.spheroidGeometry) {
628
+ const llh = fromCartographicToRadians(cartographic, scratchCartesian);
629
+ spheroidScratch.x = llh[0];
630
+ spheroidScratch.y = llh[1];
631
+ spheroidScratch.z = llh[2];
632
+ if (!spheroidToCartesian(spheroidScratch, this.spheroidGeometry))
633
+ return void 0;
634
+ return writeResult(result, spheroidScratch.x, spheroidScratch.y, spheroidScratch.z);
635
+ }
328
636
  const normal = scratchNormal;
329
637
  const k = scratchK;
330
638
  const [, , height] = cartographic;
@@ -337,7 +645,22 @@ var Ellipsoid = class {
337
645
  return k.to(result);
338
646
  }
339
647
  cartesianToCartographic(cartesian, result = [0, 0, 0]) {
340
- scratchCartesian.from(cartesian);
648
+ const object = cartesian;
649
+ const x = "x" in cartesian ? object.x : cartesian[0];
650
+ const y = "x" in cartesian ? object.y : cartesian[1];
651
+ const z = "x" in cartesian ? object.z : cartesian[2];
652
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z))
653
+ return void 0;
654
+ if (this.spheroidGeometry && Number.isFinite(x * x + y * y + z * z) && Number.isFinite(this.oneOverRadiiSquared.x) && Number.isFinite(this.oneOverRadiiSquared.z)) {
655
+ spheroidScratch.x = x;
656
+ spheroidScratch.y = y;
657
+ spheroidScratch.z = z;
658
+ if (!cartesianToSpheroid(spheroidScratch, this.spheroidGeometry))
659
+ return void 0;
660
+ const longitude2 = x === 0 && y === 0 ? Math.atan2(y, x) : spheroidScratch.x;
661
+ return toCartographicFromRadiansComponents(longitude2, spheroidScratch.y, spheroidScratch.z, result);
662
+ }
663
+ scratchCartesian.set(x, y, z);
341
664
  const point = this.scaleToGeodeticSurface(scratchCartesian, scratchPosition);
342
665
  if (!point) {
343
666
  return void 0;
@@ -346,9 +669,9 @@ var Ellipsoid = class {
346
669
  const h = scratchHeight;
347
670
  h.copy(scratchCartesian).subtract(point);
348
671
  const longitude = Math.atan2(normal.y, normal.x);
349
- const latitude = Math.asin(normal.z);
672
+ const latitude = Math.atan2(normal.z, Math.hypot(normal.x, normal.y));
350
673
  const height = Math.sign(dot(h, scratchCartesian)) * length(h);
351
- return toCartographicFromRadians([longitude, latitude, height], result);
674
+ return toCartographicFromRadiansComponents(longitude, latitude, height, result);
352
675
  }
353
676
  eastNorthUpToFixedFrame(origin, result = new import_core4.Matrix4()) {
354
677
  return localFrameToFixedFrame(this, "east", "north", "up", origin, result);
@@ -412,6 +735,10 @@ var Ellipsoid = class {
412
735
  }
413
736
  };
414
737
  Ellipsoid.WGS84 = new Ellipsoid(WGS84_RADIUS_X, WGS84_RADIUS_Y, WGS84_RADIUS_Z);
738
+ function validateSpheroid(a, b) {
739
+ if (!Number.isFinite(a) || !Number.isFinite(b) || a <= 0 || b <= 0 || b > a)
740
+ throw new Error("Spheroid requires finite positive axes with semiMinorAxis <= semiMajorAxis");
741
+ }
415
742
 
416
743
  // dist/ellipsoid-tangent-plane.js
417
744
  var import_core5 = require("@math.gl/core");
@@ -730,4 +1057,194 @@ function fromPlaneExtents(planeOrigin, planeXAxis, planeYAxis, planeZAxis, minim
730
1057
  center.copy(planeOrigin).add(centerOffset);
731
1058
  return result;
732
1059
  }
1060
+
1061
+ // dist/ellipsoid-occluder.js
1062
+ var EllipsoidOccluder = class {
1063
+ constructor(radii, center = [0, 0, 0]) {
1064
+ validateVector(radii);
1065
+ validateVector(center);
1066
+ if (Array.from(radii).some((value) => value <= 0))
1067
+ throw new RangeError("Radii must be positive");
1068
+ this.radii = Object.freeze(Array.from(radii));
1069
+ this.center = Object.freeze(Array.from(center));
1070
+ }
1071
+ /** Nonnegative ray parameter interval, including tangency and inside-origin rays.
1072
+ * Direction need not be normalized. Undefined is a true miss; never clamps a miss
1073
+ * to the limb. Result is untouched on miss. Distances equal parameters only for
1074
+ * unit directions. Zero direction is a miss.
1075
+ */
1076
+ intersectRay(origin, direction, result = [0, 0]) {
1077
+ validateVector(origin);
1078
+ validateVector(direction);
1079
+ const [x, y, z] = this.scalePoint(origin);
1080
+ const dx = direction[0] / this.radii[0];
1081
+ const dy = direction[1] / this.radii[1];
1082
+ const dz = direction[2] / this.radii[2];
1083
+ const length2 = Math.hypot(dx, dy, dz);
1084
+ if (length2 === 0 || !Number.isFinite(length2))
1085
+ return void 0;
1086
+ const ux = dx / length2, uy = dy / length2, uz = dz / length2;
1087
+ const along = -(x * ux + y * uy + z * uz);
1088
+ const perpendicular = Math.hypot(y * uz - z * uy, z * ux - x * uz, x * uy - y * ux);
1089
+ if (perpendicular > 1)
1090
+ return void 0;
1091
+ const half = Math.sqrt((1 - perpendicular) * (1 + perpendicular));
1092
+ let near = along - half;
1093
+ let far = along + half;
1094
+ const radius = Math.hypot(x, y, z);
1095
+ if (!Number.isFinite(radius))
1096
+ throw new RangeError("Scaled origin magnitude must be finite");
1097
+ if (half > 0 && along > 0 && far !== 0)
1098
+ near = (radius - 1) / far * (radius + 1);
1099
+ if (half > 0 && along < 0 && near !== 0)
1100
+ far = (radius - 1) / near * (radius + 1);
1101
+ if (far < 0)
1102
+ return void 0;
1103
+ result[0] = Math.max(0, Math.min(near, far)) / length2;
1104
+ result[1] = Math.max(0, far) / length2;
1105
+ return result;
1106
+ }
1107
+ /** True if the finite camera-to-point segment enters the solid interior.
1108
+ * Surface contact/tangency alone is not occlusion. Works for elevated points;
1109
+ * unlike a facing-normal test it does not discard visible points beyond the
1110
+ * surface horizon. tolerance is a nonnegative scaled-space squared-radius margin.
1111
+ */
1112
+ isPointOccluded(camera, point, tolerance = 1e-12) {
1113
+ validateVector(camera);
1114
+ validateVector(point);
1115
+ if (!Number.isFinite(tolerance) || tolerance < 0 || tolerance >= 1)
1116
+ throw new RangeError("Tolerance must be in [0, 1)");
1117
+ const a = this.scalePoint(camera);
1118
+ const b = this.scalePoint(point);
1119
+ const dx = b[0] - a[0], dy = b[1] - a[1], dz = b[2] - a[2];
1120
+ const length2 = Math.hypot(dx, dy, dz);
1121
+ if (!Number.isFinite(length2))
1122
+ throw new RangeError("Segment magnitude must be finite");
1123
+ if (length2 === 0)
1124
+ return Math.hypot(...a) ** 2 < 1 - tolerance;
1125
+ const ux = dx / length2, uy = dy / length2, uz = dz / length2;
1126
+ const distance = Math.max(0, Math.min(length2, -(a[0] * ux + a[1] * uy + a[2] * uz)));
1127
+ return Math.hypot(a[0] + distance * ux, a[1] + distance * uy, a[2] + distance * uz) ** 2 < 1 - tolerance;
1128
+ }
1129
+ /** Exact apparent limb ellipse for an exterior camera; undefined on/inside surface. */
1130
+ getHorizon(camera) {
1131
+ validateVector(camera);
1132
+ const scaled = this.scalePoint(camera);
1133
+ const distance = Math.hypot(...scaled);
1134
+ if (!Number.isFinite(distance))
1135
+ throw new RangeError("Camera magnitude must be finite");
1136
+ if (distance <= 1)
1137
+ return void 0;
1138
+ const normal = scaled.map((value) => value / distance);
1139
+ const reference = Math.abs(normal[2]) < 0.9 ? [0, 0, 1] : [0, 1, 0];
1140
+ const u = cross(normal, reference);
1141
+ const uLength = Math.hypot(...u);
1142
+ const v = cross(normal, u.map((value) => value / uLength));
1143
+ const radius = Math.sqrt((1 - 1 / distance) * (1 + 1 / distance));
1144
+ return {
1145
+ center: normal.map((value, i) => this.center[i] + this.radii[i] * value / distance),
1146
+ axis1: u.map((value, i) => this.radii[i] * radius * value / uLength),
1147
+ axis2: v.map((value, i) => this.radii[i] * radius * value)
1148
+ };
1149
+ }
1150
+ scalePoint(point) {
1151
+ const result = this.radii.map((radius, i) => (point[i] - this.center[i]) / radius);
1152
+ if (!result.every(Number.isFinite))
1153
+ throw new RangeError("Scaled coordinates must be finite");
1154
+ return result;
1155
+ }
1156
+ };
1157
+ function validateVector(vector) {
1158
+ if (vector.length !== 3 || !Array.from(vector).every(Number.isFinite))
1159
+ throw new RangeError("Expected three finite Cartesian components");
1160
+ }
1161
+ function cross(a, b) {
1162
+ return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
1163
+ }
1164
+
1165
+ // dist/globe-horizon-bounds.js
1166
+ function getGlobeHorizonBounds(camera, radius) {
1167
+ if (camera.length !== 3 || !Array.from(camera).every(Number.isFinite) || !Number.isFinite(radius) || radius <= 0)
1168
+ throw new RangeError("Expected a finite camera and positive sphere radius");
1169
+ const distance = Math.hypot(camera[0], camera[1], camera[2]);
1170
+ if (!Number.isFinite(distance))
1171
+ throw new RangeError("Camera magnitude must be finite");
1172
+ if (distance <= radius)
1173
+ return void 0;
1174
+ const latitude = Math.atan2(camera[2], Math.hypot(camera[0], camera[1]));
1175
+ const longitude = Math.atan2(camera[1], camera[0]);
1176
+ const angle = Math.acos(radius / distance);
1177
+ const degrees2 = 180 / Math.PI;
1178
+ const padding = 1e-10;
1179
+ const south = Math.max(-90, (latitude - angle) * degrees2 - padding);
1180
+ const north = Math.min(90, (latitude + angle) * degrees2 + padding);
1181
+ if (Math.abs(latitude) + angle >= Math.PI / 2 - 1e-12)
1182
+ return [-180, south, 180, north];
1183
+ const extent = Math.asin(Math.min(1, Math.sin(angle) / Math.cos(latitude))) * degrees2 + padding;
1184
+ return [wrap(longitude * degrees2 - extent), south, wrap(longitude * degrees2 + extent), north];
1185
+ }
1186
+ function splitGlobeBounds(bounds) {
1187
+ const [west, south, east, north] = bounds;
1188
+ if (bounds.length !== 4 || !bounds.every(Number.isFinite) || west < -180 || west > 180 || east < -180 || east > 180 || south < -90 || north > 90 || south > north)
1189
+ throw new RangeError("Invalid degree bounds");
1190
+ return west <= east ? [[west, south, east, north]] : [
1191
+ [west, south, 180, north],
1192
+ [-180, south, east, north]
1193
+ ];
1194
+ }
1195
+ function wrap(longitude) {
1196
+ return ((longitude + 180) % 360 + 360) % 360 - 180;
1197
+ }
1198
+
1199
+ // dist/geographic-tiles.js
1200
+ function getGeographicTileBounds(tile) {
1201
+ const size = gridSize(tile.level);
1202
+ if (![tile.x, tile.y].every((v) => Number.isInteger(v) && v >= 0 && v < size))
1203
+ throw new RangeError("Tile coordinates must be integers inside the level grid");
1204
+ return [
1205
+ -180 + 360 * tile.x / size,
1206
+ 90 - 180 * (tile.y + 1) / size,
1207
+ -180 + 360 * (tile.x + 1) / size,
1208
+ 90 - 180 * tile.y / size
1209
+ ];
1210
+ }
1211
+ function getGeographicTile(longitude, latitude, level) {
1212
+ const size = gridSize(level);
1213
+ if (!Number.isFinite(longitude) || longitude < -180 || longitude > 180 || !Number.isFinite(latitude) || latitude < -90 || latitude > 90)
1214
+ throw new RangeError("Expected longitude in [-180, 180] and latitude in [-90, 90]");
1215
+ return {
1216
+ x: Math.min(size - 1, Math.floor((longitude + 180) / 360 * size)),
1217
+ y: Math.min(size - 1, Math.floor((90 - latitude) / 180 * size)),
1218
+ level
1219
+ };
1220
+ }
1221
+ function getGeographicTileRanges(bounds, level) {
1222
+ const size = gridSize(level);
1223
+ const split = splitGlobeBounds(bounds);
1224
+ const positiveWidth = split.filter(([west, , east]) => east > west);
1225
+ const rectangles = positiveWidth.length ? positiveWidth : split;
1226
+ const intervals = rectangles.map(([west, south, east, north]) => {
1227
+ const start = getGeographicTile(west, north, level);
1228
+ return {
1229
+ minX: start.x,
1230
+ maxX: west === east ? start.x : Math.max(start.x, Math.min(size - 1, Math.ceil((east + 180) / 360 * size) - 1)),
1231
+ minY: start.y,
1232
+ maxY: south === north ? start.y : Math.max(start.y, Math.min(size - 1, Math.ceil((90 - south) / 180 * size) - 1))
1233
+ };
1234
+ }).sort((a, b) => a.minX - b.minX);
1235
+ const result = [];
1236
+ for (const range of intervals) {
1237
+ const previous = result[result.length - 1];
1238
+ if (previous && range.minX <= previous.maxX + 1)
1239
+ previous.maxX = Math.max(previous.maxX, range.maxX);
1240
+ else
1241
+ result.push(range);
1242
+ }
1243
+ return result;
1244
+ }
1245
+ function gridSize(level) {
1246
+ if (!Number.isInteger(level) || level < 0 || level > 30)
1247
+ throw new RangeError("Level must be an integer from 0 through 30");
1248
+ return 2 ** level;
1249
+ }
733
1250
  //# sourceMappingURL=index.cjs.map