@math.gl/core 5.0.0-alpha.19 → 5.0.0-alpha.20
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/APACHE-2.0-LICENSE.txt +55 -0
- package/ARROW-NOTICE.txt +18 -0
- package/LICENSE +146 -44
- package/README.md +5 -0
- package/THIRD-PARTY-NOTICES.md +16 -0
- package/dist/classes/euler.d.ts.map +1 -1
- package/dist/classes/euler.js +2 -0
- package/dist/classes/euler.js.map +1 -1
- package/dist/expressions/function-registry.d.ts.map +1 -1
- package/dist/expressions/function-registry.js +9 -1
- package/dist/expressions/function-registry.js.map +1 -1
- package/dist/expressions/get.js +1 -1
- package/dist/expressions/get.js.map +1 -1
- package/dist/expressions/index.cjs +12 -4
- package/dist/expressions/index.cjs.map +1 -1
- package/dist/expressions/parse-expression-string.js +3 -3
- package/dist/expressions/parse-expression-string.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/spheroid.cjs +34 -57
- package/dist/spheroid.cjs.map +1 -1
- package/dist/spheroid.d.ts +1 -1
- package/dist/spheroid.d.ts.map +1 -1
- package/dist/spheroid.js +53 -64
- package/dist/spheroid.js.map +1 -1
- package/dist/types/bigint.d.ts +1 -1
- package/dist/types/bigint.d.ts.map +1 -1
- package/dist/types/bigint.js +21 -4
- package/dist/types/bigint.js.map +1 -1
- package/dist/types/bounds-types.js +1 -1
- package/dist/types/bounds-types.js.map +1 -1
- package/package.json +7 -3
- package/src/classes/euler.ts +2 -0
- package/src/expressions/function-registry.ts +9 -1
- package/src/expressions/get.ts +1 -1
- package/src/expressions/parse-expression-string.ts +3 -3
- package/src/spheroid.ts +62 -85
- package/src/types/bigint.ts +22 -4
- package/src/types/bounds-types.ts +1 -1
|
@@ -14,9 +14,9 @@ import {get} from './get';
|
|
|
14
14
|
*/
|
|
15
15
|
export type AccessorFunction = (row: Record<string, unknown>) => unknown;
|
|
16
16
|
|
|
17
|
-
const cachedExpressionMap
|
|
18
|
-
'-': object => object
|
|
19
|
-
};
|
|
17
|
+
const cachedExpressionMap = Object.assign(Object.create(null) as Record<string, AccessorFunction>, {
|
|
18
|
+
'-': (object: Record<string, unknown>) => object
|
|
19
|
+
});
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Compiles a JSON-style expression string into an accessor function.
|
package/src/spheroid.ts
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// SPDX-FileCopyrightText: 2014 Mike Adair, Richard Greenwood, Didier Richard, Stephen Irons, Olivier Terral and Calvin Metcalf (proj4js)
|
|
4
4
|
// SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
|
|
5
|
-
// SPDX-FileComment: Forward geocentric equations and Hannover inverse moved/adapted from math.gl projection's proj4js 2.22.0 datumUtils.js port. See ../PROJ4-LICENSE.md. Safeguarded nearest-footpoint inverse and numeric output commit are original math.gl code.
|
|
5
|
+
// SPDX-FileComment: Forward geocentric equations and Hannover inverse moved/adapted from math.gl projection's proj4js 2.22.0 datumUtils.js port. See ../PROJ4-LICENSE.md. Safeguarded compensated nearest-footpoint inverse, thin-exterior/pole fixes and numeric output commit are original math.gl code, qualified with independent mathematics and offline GeographicLib 2.7 (Charles Karney, MIT).
|
|
6
6
|
|
|
7
7
|
/** Numeric leaf: angles in radians, distances in axis units; no CRS, global config or scratch. */
|
|
8
8
|
import type {SpheroidParameters} from './types';
|
|
9
9
|
export type SpheroidGeometry = SpheroidParameters & {readonly eccentricitySquared: number};
|
|
10
10
|
export type SpheroidPoint = {x: number; y: number; z: number};
|
|
11
11
|
|
|
12
|
+
const EPSILON = Number.EPSILON;
|
|
13
|
+
|
|
12
14
|
/** Caller supplies validated positive finite axes and eccentricity. No latitude wrapping. */
|
|
13
15
|
export function spheroidToCartesian(
|
|
14
16
|
point: SpheroidPoint,
|
|
@@ -20,7 +22,7 @@ export function spheroidToCartesian(
|
|
|
20
22
|
height = point.z;
|
|
21
23
|
const {semiMajorAxis: a, semiMinorAxis: b, eccentricitySquared: es} = geometry;
|
|
22
24
|
const sin = Math.sin(latitude),
|
|
23
|
-
cos = Math.cos(latitude);
|
|
25
|
+
cos = Math.abs(latitude) === Math.PI / 2 ? 0 : Math.cos(latitude);
|
|
24
26
|
// Use the axis ratio for very flat shapes: 1 - es*sin² loses polar precision.
|
|
25
27
|
const ratio = b / a;
|
|
26
28
|
const radius = es < 0.9 ? a / Math.sqrt(1 - es * sin * sin) : a / Math.hypot(cos, ratio * sin);
|
|
@@ -32,7 +34,7 @@ export function spheroidToCartesian(
|
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
36
|
* Bounded inverse. Sphere is analytic; ordinary spheroids use the retained Hannover
|
|
35
|
-
* iteration on exterior points. Interiors
|
|
37
|
+
* iteration on exterior points. Interiors and thin-shape exteriors share a safeguarded nearest-footpoint solve.
|
|
36
38
|
* Equatorial interiors below the evolute cusp have two nearest normals: reject.
|
|
37
39
|
* false leaves output untouched, including the center and unresolved binary64 cases.
|
|
38
40
|
* Only exact poles canonicalize longitude; adapters may retain other conventions.
|
|
@@ -46,16 +48,17 @@ export function cartesianToSpheroid(
|
|
|
46
48
|
y = point.y,
|
|
47
49
|
z = point.z;
|
|
48
50
|
const {semiMajorAxis: a, semiMinorAxis: b, eccentricitySquared: es} = geometry;
|
|
51
|
+
const longitude = Math.atan2(y, x);
|
|
49
52
|
const p = Math.hypot(x, y),
|
|
50
53
|
rr = Math.hypot(x, y, z);
|
|
51
54
|
if (!(rr > 0) || !Number.isFinite(rr)) return false;
|
|
52
|
-
if (es === 0) return commit(result, p === 0 ? 0 :
|
|
55
|
+
if (es === 0) return commit(result, p === 0 ? 0 : longitude, Math.atan2(z, p), rr - a);
|
|
53
56
|
if (p === 0) return commit(result, 0, (Math.sign(z) * Math.PI) / 2, Math.abs(z) - b);
|
|
54
57
|
const horizontal = p / a,
|
|
55
58
|
vertical = z / b;
|
|
56
|
-
if (horizontal * horizontal + vertical * vertical < 1 - 16 *
|
|
59
|
+
if (horizontal * horizontal + vertical * vertical < 1 - 16 * EPSILON)
|
|
57
60
|
return interiorInverse(x, y, z, p, a, b, result);
|
|
58
|
-
if (es >= 0.9) return
|
|
61
|
+
if (es >= 0.9) return interiorInverse(x, y, z, p, a, b, result);
|
|
59
62
|
const ct = z / rr,
|
|
60
63
|
st = p / rr;
|
|
61
64
|
let rx = 1 / Math.sqrt(1 - es * (2 - es) * st * st);
|
|
@@ -69,16 +72,16 @@ export function cartesianToSpheroid(
|
|
|
69
72
|
const nextCos = st * (1 - rk) * rx,
|
|
70
73
|
nextSin = ct * rx;
|
|
71
74
|
if (Math.abs(nextSin * cos - nextCos * sin) <= 1e-12) {
|
|
72
|
-
if (!Number.isFinite(height)
|
|
73
|
-
return commit(result,
|
|
75
|
+
if (!Number.isFinite(height)) break;
|
|
76
|
+
return commit(result, longitude, Math.atan2(nextSin, Math.abs(nextCos)), height);
|
|
74
77
|
}
|
|
75
78
|
cos = nextCos;
|
|
76
79
|
sin = nextSin;
|
|
77
80
|
}
|
|
78
|
-
return
|
|
81
|
+
return interiorInverse(x, y, z, p, a, b, result);
|
|
79
82
|
}
|
|
80
83
|
|
|
81
|
-
/** Original nearest-normal solve in t = (lambda + b²)/a², avoiding interior cancellation.
|
|
84
|
+
/** Original nearest-normal solve in t = (lambda + b²)/a², avoiding interior cancellation and thin-exterior residual loss.
|
|
82
85
|
* The positive interval selects the globally nearest branch, rather than a stationary normal.
|
|
83
86
|
*/
|
|
84
87
|
function interiorInverse(
|
|
@@ -90,16 +93,13 @@ function interiorInverse(
|
|
|
90
93
|
b: number,
|
|
91
94
|
result: SpheroidPoint
|
|
92
95
|
): boolean {
|
|
96
|
+
const longitude = Math.atan2(y, x);
|
|
93
97
|
const ratio = b / a,
|
|
94
98
|
ratio2 = ratio * ratio;
|
|
95
99
|
const horizontal = p / a,
|
|
96
100
|
vertical = z / a,
|
|
97
101
|
delta = 1 - ratio2;
|
|
98
102
|
if (!(ratio2 > 0) || ratio > 1) return false;
|
|
99
|
-
if (z === 0) {
|
|
100
|
-
if (horizontal < delta) return false;
|
|
101
|
-
return commit(result, Math.atan2(y, x), 0, p - a);
|
|
102
|
-
}
|
|
103
103
|
const aa = a * a,
|
|
104
104
|
bb = b * b;
|
|
105
105
|
const ratioError =
|
|
@@ -126,21 +126,44 @@ function interiorInverse(
|
|
|
126
126
|
(2 * p)
|
|
127
127
|
: 0;
|
|
128
128
|
const shift = a * ratio2,
|
|
129
|
-
|
|
129
|
+
distance = p - a,
|
|
130
|
+
base = distance + shift;
|
|
130
131
|
const correction =
|
|
131
|
-
radiusError +
|
|
132
|
+
radiusError +
|
|
133
|
+
sumError(p, -a, distance) +
|
|
134
|
+
sumError(distance, shift, base) +
|
|
135
|
+
productError(a, ratio2, shift) +
|
|
136
|
+
a * ratioError;
|
|
137
|
+
if (z === 0) {
|
|
138
|
+
// A rounded hypot can equal the cusp for points just inside it. Include
|
|
139
|
+
// the radius/axis corrections before selecting a unique equatorial normal.
|
|
140
|
+
if (base + correction < 0) return false;
|
|
141
|
+
return commit(result, longitude, 0, distance);
|
|
142
|
+
}
|
|
132
143
|
const weighted = Math.abs(vertical) * ratio;
|
|
133
|
-
if (!(weighted > 0)) return false;
|
|
134
144
|
let lower = 0,
|
|
135
|
-
upper = ratio2;
|
|
145
|
+
upper = ratio2 + Math.hypot(horizontal, vertical * ratio);
|
|
146
|
+
if (!Number.isFinite(upper)) return false;
|
|
136
147
|
// A lower bound near the short-axis singularity prevents thousands of bisections
|
|
137
148
|
// for deep, nearly equatorial interiors. No subtraction of lambda and -b².
|
|
138
149
|
let t = Math.max(weighted, horizontal - delta);
|
|
139
150
|
if (!(t > lower && t < upper)) t = upper / 2;
|
|
140
|
-
|
|
151
|
+
// Below the cusp, a vanishing polar input can make t subnormal. The
|
|
152
|
+
// limiting vertical footpoint remains well conditioned. Use it only when
|
|
153
|
+
// its bound t <= abs(z)*ratio/(a*v) is negligible relative to the cusp gap.
|
|
154
|
+
const gap = -(base + correction) / a;
|
|
155
|
+
const limiting = Math.sqrt(gap * (horizontal + delta)) / delta;
|
|
156
|
+
let latitude: number | undefined =
|
|
157
|
+
gap > 0 && (Math.abs(z) / limiting / a) * ratio <= EPSILON * gap
|
|
158
|
+
? Math.atan2((Math.sign(z) * limiting) / ratio, horizontal / delta)
|
|
159
|
+
: undefined;
|
|
160
|
+
for (let i = 0; i < 1024 && latitude === undefined; i++) {
|
|
141
161
|
const equatorial = delta + t;
|
|
142
162
|
const u = horizontal / equatorial,
|
|
143
|
-
|
|
163
|
+
// Divide by t first: both z/a and a*t can lose precision in the
|
|
164
|
+
// subnormal range although the near-cusp latitude is representable.
|
|
165
|
+
verticalAt = z / t / a,
|
|
166
|
+
v = verticalAt * ratio;
|
|
144
167
|
const at = a * t,
|
|
145
168
|
difference = base - at;
|
|
146
169
|
const offset =
|
|
@@ -148,40 +171,22 @@ function interiorInverse(
|
|
|
148
171
|
(a * equatorial);
|
|
149
172
|
const term = offset * (u + 1);
|
|
150
173
|
const residual = term + v * v;
|
|
151
|
-
if (
|
|
152
|
-
Math.abs(residual) <= 4 * Number.EPSILON * (Math.abs(term) + v * v) ||
|
|
153
|
-
upper - lower <= 4 * Number.EPSILON * t
|
|
154
|
-
) {
|
|
155
|
-
const latitude = Math.atan2(vertical / t, horizontal / equatorial);
|
|
156
|
-
const sin = Math.sin(latitude),
|
|
157
|
-
cos = Math.cos(latitude);
|
|
158
|
-
return commit(
|
|
159
|
-
result,
|
|
160
|
-
Math.atan2(y, x),
|
|
161
|
-
latitude,
|
|
162
|
-
p * cos + z * sin - a * Math.hypot(cos, ratio * sin)
|
|
163
|
-
);
|
|
164
|
-
}
|
|
174
|
+
if (!Number.isFinite(residual)) return false;
|
|
165
175
|
if (residual > 0) lower = t;
|
|
166
176
|
else upper = t;
|
|
167
177
|
const derivative = -2 * ((u * u) / equatorial + (v * v) / t);
|
|
168
178
|
const next = t - residual / derivative;
|
|
169
|
-
const candidate =
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
latitude,
|
|
177
|
-
p * Math.cos(latitude) +
|
|
178
|
-
z * Math.sin(latitude) -
|
|
179
|
-
a * Math.hypot(Math.cos(latitude), ratio * Math.sin(latitude))
|
|
180
|
-
);
|
|
179
|
+
const candidate = next > lower && next < upper ? next : lower + (upper - lower) / 2;
|
|
180
|
+
if (
|
|
181
|
+
Math.abs(residual) <= 4 * EPSILON * (Math.abs(term) + v * v) ||
|
|
182
|
+
upper - lower <= 4 * EPSILON * t
|
|
183
|
+
) {
|
|
184
|
+
latitude = Math.atan2(verticalAt, u);
|
|
185
|
+
break;
|
|
181
186
|
}
|
|
182
187
|
t = candidate;
|
|
183
188
|
}
|
|
184
|
-
return
|
|
189
|
+
return latitude !== undefined && commitNormal(result, longitude, latitude, p, z, a, ratio);
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
/** Error-free transforms on the finite squared-axis domain used by geometry adapters. */
|
|
@@ -200,47 +205,19 @@ function productError(x: number, y: number, product: number): number {
|
|
|
200
205
|
return hx * hy - product + hx * ly + lx * hy + lx * ly;
|
|
201
206
|
}
|
|
202
207
|
|
|
203
|
-
/**
|
|
204
|
-
function
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
+
/** Shared signed distance along the solved surface normal. */
|
|
209
|
+
function commitNormal(
|
|
210
|
+
result: SpheroidPoint,
|
|
211
|
+
longitude: number,
|
|
212
|
+
latitude: number,
|
|
208
213
|
p: number,
|
|
214
|
+
z: number,
|
|
209
215
|
a: number,
|
|
210
|
-
|
|
211
|
-
result: SpheroidPoint
|
|
216
|
+
ratio: number
|
|
212
217
|
): boolean {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
vertical = z / a;
|
|
217
|
-
const norm = Math.hypot(horizontal, z / b);
|
|
218
|
-
if (!Number.isFinite(norm) || norm < 1 - 8 * Number.EPSILON || !(ratio2 > 0)) return false;
|
|
219
|
-
let lower = 0,
|
|
220
|
-
upper = Math.hypot(horizontal, vertical * ratio);
|
|
221
|
-
if (!Number.isFinite(upper)) return false;
|
|
222
|
-
let multiplier = norm <= 1 + 8 * Number.EPSILON ? 0 : upper / 2;
|
|
223
|
-
for (let i = 0; i < 96; i++) {
|
|
224
|
-
const equatorial = 1 + multiplier,
|
|
225
|
-
polar = ratio2 + multiplier;
|
|
226
|
-
const u = horizontal / equatorial,
|
|
227
|
-
v = (vertical * ratio) / polar;
|
|
228
|
-
const length = Math.hypot(u, v);
|
|
229
|
-
const residual = length - 1;
|
|
230
|
-
if (Math.abs(residual) <= 4 * Number.EPSILON) {
|
|
231
|
-
const latitude = Math.atan2(vertical / polar, horizontal / equatorial);
|
|
232
|
-
const sin = Math.sin(latitude),
|
|
233
|
-
cos = Math.cos(latitude);
|
|
234
|
-
const height = p * cos + z * sin - a * Math.hypot(cos, ratio * sin);
|
|
235
|
-
return commit(result, Math.atan2(y, x), latitude, height);
|
|
236
|
-
}
|
|
237
|
-
if (residual > 0) lower = multiplier;
|
|
238
|
-
else upper = multiplier;
|
|
239
|
-
const derivative = -((u * u) / equatorial + (v * v) / polar) / length;
|
|
240
|
-
const next = multiplier - residual / derivative;
|
|
241
|
-
multiplier = Number.isFinite(next) && next > lower && next < upper ? next : (lower + upper) / 2;
|
|
242
|
-
}
|
|
243
|
-
return false;
|
|
218
|
+
const sin = Math.sin(latitude),
|
|
219
|
+
cos = Math.cos(latitude);
|
|
220
|
+
return commit(result, longitude, latitude, p * cos + z * sin - a * Math.hypot(cos, ratio * sin));
|
|
244
221
|
}
|
|
245
222
|
|
|
246
223
|
/** Values are captured before invoking application setters; recursive writes are safe. */
|
package/src/types/bigint.ts
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
// math.gl
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-License-Identifier: MIT AND Apache-2.0
|
|
3
3
|
// SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
|
|
4
|
+
// SPDX-FileCopyrightText: Copyright 2016-2019 The Apache Software Foundation
|
|
5
|
+
// SPDX-FileComment: BigInt availability/fallback structure adapted from Apache Arrow JS util/compat.ts; compared with apache-arrow-4.0.0. See ../../ARROW-NOTICE.txt and ../../APACHE-2.0-LICENSE.txt. Modified to separate availability flags, share errors and narrow exports.
|
|
6
|
+
|
|
7
|
+
// Licensed to the Apache Software Foundation (ASF) under one
|
|
8
|
+
// or more contributor license agreements. See the NOTICE file
|
|
9
|
+
// distributed with this work for additional information
|
|
10
|
+
// regarding copyright ownership. The ASF licenses this file
|
|
11
|
+
// to you under the Apache License, Version 2.0 (the
|
|
12
|
+
// "License"); you may not use this file except in compliance
|
|
13
|
+
// with the License. You may obtain a copy of the License at
|
|
14
|
+
//
|
|
15
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
+
//
|
|
17
|
+
// Unless required by applicable law or agreed to in writing,
|
|
18
|
+
// software distributed under the License is distributed on an
|
|
19
|
+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
20
|
+
// KIND, either express or implied. See the License for the
|
|
21
|
+
// specific language governing permissions and limitations
|
|
22
|
+
// under the License.
|
|
4
23
|
|
|
5
24
|
// BigInt compatibility layer
|
|
6
|
-
//
|
|
7
|
-
// https://github.com/apache/arrow/blob/master/js/src/util/compat.ts
|
|
25
|
+
// Source comparison: https://github.com/apache/arrow/blob/apache-arrow-4.0.0/js/src/util/compat.ts
|
|
8
26
|
// Requires tsconfig.json: target=esnext or (lib: esnext.bigint)
|
|
9
27
|
// Requires eslint: env: {es2020: true}
|
|
10
28
|
|
|
@@ -48,7 +66,7 @@ const BigInt64ArrayCtor = (() => {
|
|
|
48
66
|
})();
|
|
49
67
|
|
|
50
68
|
const BigUint64ArrayCtor = (() => {
|
|
51
|
-
return BigUint64ArrayAvailable ?
|
|
69
|
+
return BigUint64ArrayAvailable ? BigUint64Array : BigInt64ArrayUnavailable;
|
|
52
70
|
})();
|
|
53
71
|
|
|
54
72
|
export {BigIntCtor as BigInt};
|
|
@@ -15,7 +15,7 @@ export type Bounds =
|
|
|
15
15
|
|
|
16
16
|
/** Checks if a `Bounds` is a `Bounds2D` */
|
|
17
17
|
export function isBounds2D(bounds: Bounds): bounds is Bounds2D {
|
|
18
|
-
return bounds.length === 2;
|
|
18
|
+
return bounds.length === 2 && bounds[0].length === 2 && bounds[1].length === 2;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/** Accepts 2D and 3D bounds and returns a 2D bound, truncating 3D dimension if necessary */
|