@math.gl/geospatial 5.0.0-beta.1 → 5.0.0-beta.2

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 (90) hide show
  1. package/dist/crs/crs-identifier.d.ts +23 -0
  2. package/dist/crs/crs-identifier.d.ts.map +1 -0
  3. package/dist/crs/crs-identifier.js +74 -0
  4. package/dist/crs/crs-identifier.js.map +1 -0
  5. package/dist/crs/index.cjs +48 -0
  6. package/dist/crs/index.cjs.map +2 -2
  7. package/dist/crs/index.d.ts +1 -0
  8. package/dist/crs/index.d.ts.map +1 -1
  9. package/dist/crs/index.js +1 -0
  10. package/dist/crs/index.js.map +1 -1
  11. package/dist/discontinuous/cap-map.d.ts.map +1 -1
  12. package/dist/discontinuous/cap-map.js +197 -13
  13. package/dist/discontinuous/cap-map.js.map +1 -1
  14. package/dist/discontinuous/elastic/geometry.d.ts +1 -1
  15. package/dist/discontinuous/elastic/geometry.d.ts.map +1 -1
  16. package/dist/discontinuous/elastic/geometry.js +7 -7
  17. package/dist/discontinuous/elastic/geometry.js.map +1 -1
  18. package/dist/discontinuous/elastic/map.d.ts.map +1 -1
  19. package/dist/discontinuous/elastic/map.js +263 -23
  20. package/dist/discontinuous/elastic/map.js.map +1 -1
  21. package/dist/discontinuous/elastic/source-arc.d.ts +10 -0
  22. package/dist/discontinuous/elastic/source-arc.d.ts.map +1 -0
  23. package/dist/discontinuous/elastic/source-arc.js +111 -0
  24. package/dist/discontinuous/elastic/source-arc.js.map +1 -0
  25. package/dist/discontinuous/global-polygons.d.ts +1 -1
  26. package/dist/discontinuous/global-polygons.d.ts.map +1 -1
  27. package/dist/discontinuous/global-polygons.js +49 -1
  28. package/dist/discontinuous/global-polygons.js.map +1 -1
  29. package/dist/discontinuous/map-engine.d.ts +9 -0
  30. package/dist/discontinuous/map-engine.d.ts.map +1 -1
  31. package/dist/discontinuous/map-layout.d.ts +21 -0
  32. package/dist/discontinuous/map-layout.d.ts.map +1 -1
  33. package/dist/discontinuous/map-layout.js +116 -1
  34. package/dist/discontinuous/map-layout.js.map +1 -1
  35. package/dist/discontinuous/map-transition.d.ts.map +1 -1
  36. package/dist/discontinuous/map-transition.js +66 -3
  37. package/dist/discontinuous/map-transition.js.map +1 -1
  38. package/dist/discontinuous/mercator-map.d.ts +2 -2
  39. package/dist/discontinuous/mercator-map.d.ts.map +1 -1
  40. package/dist/discontinuous/mercator-map.js +123 -11
  41. package/dist/discontinuous/mercator-map.js.map +1 -1
  42. package/dist/discontinuous/polygon-constraints.d.ts +16 -0
  43. package/dist/discontinuous/polygon-constraints.d.ts.map +1 -0
  44. package/dist/discontinuous/polygon-constraints.js +157 -0
  45. package/dist/discontinuous/polygon-constraints.js.map +1 -0
  46. package/dist/discontinuous/prepare-features.d.ts +27 -0
  47. package/dist/discontinuous/prepare-features.d.ts.map +1 -1
  48. package/dist/discontinuous/prepare-features.js +106 -5
  49. package/dist/discontinuous/prepare-features.js.map +1 -1
  50. package/dist/discontinuous/rotated-map.d.ts +9 -0
  51. package/dist/discontinuous/rotated-map.d.ts.map +1 -1
  52. package/dist/discontinuous/source-arc.d.ts +4 -0
  53. package/dist/discontinuous/source-arc.d.ts.map +1 -0
  54. package/dist/discontinuous/source-arc.js +23 -0
  55. package/dist/discontinuous/source-arc.js.map +1 -0
  56. package/dist/discontinuous/world-kernels.d.ts +1 -1
  57. package/dist/discontinuous.cjs +1047 -75
  58. package/dist/discontinuous.cjs.map +3 -3
  59. package/dist/index.cjs +48 -0
  60. package/dist/index.cjs.map +2 -2
  61. package/dist/index.d.ts +1 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/turf.cjs +69 -5
  66. package/dist/turf.cjs.map +2 -2
  67. package/dist/turf.d.ts +9 -0
  68. package/dist/turf.d.ts.map +1 -1
  69. package/dist/turf.js +67 -0
  70. package/dist/turf.js.map +1 -1
  71. package/dist/workers/index.cjs +1075 -79
  72. package/dist/workers/index.cjs.map +3 -3
  73. package/dist/workers/operations.cjs +1049 -77
  74. package/dist/workers/operations.cjs.map +3 -3
  75. package/package.json +5 -5
  76. package/src/crs/crs-identifier.ts +86 -0
  77. package/src/crs/index.ts +1 -0
  78. package/src/discontinuous/cap-map.ts +248 -19
  79. package/src/discontinuous/elastic/geometry.ts +18 -7
  80. package/src/discontinuous/elastic/map.ts +334 -30
  81. package/src/discontinuous/elastic/source-arc.ts +129 -0
  82. package/src/discontinuous/global-polygons.ts +56 -2
  83. package/src/discontinuous/map-layout.ts +165 -2
  84. package/src/discontinuous/map-transition.ts +67 -3
  85. package/src/discontinuous/mercator-map.ts +151 -12
  86. package/src/discontinuous/polygon-constraints.ts +184 -0
  87. package/src/discontinuous/prepare-features.ts +154 -6
  88. package/src/discontinuous/source-arc.ts +31 -0
  89. package/src/index.ts +1 -0
  90. package/src/turf.ts +86 -0
@@ -48,18 +48,45 @@ export type PreparedMapGeometry = {
48
48
  regionIds: string[];
49
49
  /** One vertex per part. Point ownership follows MapEngine.projectPoint(). */
50
50
  points: PreparedMapVertices & {parts: PreparedMapPart[]};
51
- paths: PreparedMapVertices & {parts: PreparedMapPathPart[]; pathStarts: Uint32Array};
51
+ paths: PreparedMapVertices & {
52
+ parts: PreparedMapPathPart[];
53
+ pathStarts: Uint32Array;
54
+ provenance?: {
55
+ sourceSegmentIndices?: Uint32Array;
56
+ sourceSegmentParameters?: Float64Array;
57
+ sourceVertexIndices?: Uint32Array;
58
+ sourceDistances?: Float64Array;
59
+ distanceRadius?: number;
60
+ /** Per part: source endpoint, coverage boundary, cut, or continuous join. */
61
+ startReasons?: string[];
62
+ endReasons?: string[];
63
+ };
64
+ };
52
65
  polygons: PreparedMapVertices & {
53
66
  parts: PreparedMapPart[];
54
67
  /** Vertex offsets for all rings, with a terminal sentinel. Rings repeat their first vertex. */
55
68
  ringStarts: Uint32Array;
56
69
  /** Ring offsets for polygon parts, with a terminal sentinel. Outer ring comes first. */
57
70
  polygonStarts: Uint32Array;
71
+ /** One edge from each vertex to the next; repeated closing vertices have kind 0. */
72
+ edgeProvenance?: {
73
+ kinds: Uint8Array;
74
+ sourceRingIndices: Uint32Array;
75
+ sourceSegmentIndices: Uint32Array;
76
+ sourceParameterRanges: Float64Array;
77
+ };
58
78
  };
59
79
  };
60
80
  /** Static preparation controls. This does not triangulate, interpolate attributes or pose meshes. */
61
81
  export type MapPrepareFeaturesOptions = {
62
82
  sampling?: MapSamplingOptions;
83
+ provenance?: {
84
+ sourceSegments?: boolean;
85
+ boundaries?: boolean;
86
+ polygonEdges?: boolean;
87
+ /** Source minor-great-circle distance in meters on the declared sphere. */
88
+ distances?: {metric: 'spherical'; radius: number};
89
+ };
63
90
  /** Project original polygon boundaries separately; default true. */
64
91
  polygonOutlines?: boolean;
65
92
  /** Directed global spherical polygons; original outlines never include atlas connectors. */
@@ -108,6 +135,45 @@ export function prepareMapFeatures(
108
135
  if (!Array.isArray(features)) throw new Error('Expected a feature array');
109
136
  if (options.polygonOutlines !== undefined && typeof options.polygonOutlines !== 'boolean')
110
137
  throw new Error('polygonOutlines must be boolean');
138
+ const provenance = options.provenance;
139
+ if (
140
+ provenance !== undefined &&
141
+ (!provenance || typeof provenance !== 'object' || Array.isArray(provenance))
142
+ )
143
+ throw new Error('Unsupported provenance policy');
144
+ if (
145
+ provenance?.distances !== undefined &&
146
+ (!provenance.distances || typeof provenance.distances !== 'object')
147
+ )
148
+ throw new Error('Unsupported provenance distance policy');
149
+ if (
150
+ provenance &&
151
+ (typeof provenance !== 'object' ||
152
+ Object.keys(provenance).some(
153
+ k => !['sourceSegments', 'boundaries', 'distances', 'polygonEdges'].includes(k)
154
+ ) ||
155
+ [provenance.sourceSegments, provenance.boundaries, provenance.polygonEdges].some(
156
+ v => v !== undefined && typeof v !== 'boolean'
157
+ ) ||
158
+ (provenance.distances &&
159
+ (provenance.distances.metric !== 'spherical' ||
160
+ !Number.isFinite(provenance.distances.radius) ||
161
+ provenance.distances.radius <= 0)))
162
+ )
163
+ throw new Error('Unsupported provenance policy');
164
+ const edgeKinds: number[] = [],
165
+ edgeRings: number[] = [],
166
+ edgeSegments: number[] = [],
167
+ edgeParameters: number[] = [];
168
+ const lineage = Boolean(
169
+ provenance?.sourceSegments || provenance?.boundaries || provenance?.distances
170
+ );
171
+ const segments: number[] = [],
172
+ parameters: number[] = [],
173
+ originalVertices: number[] = [],
174
+ distances: number[] = [],
175
+ startReasons: string[] = [],
176
+ endReasons: string[] = [];
111
177
  const regionIds = map.regions.map(r => r.id),
112
178
  regionIndices = new Map(regionIds.map((id, i) => [id, i]));
113
179
  const points = vertices(),
@@ -140,7 +206,28 @@ export function prepareMapFeatures(
140
206
  if (line.length < 2) throw new Error('A line needs at least two positions');
141
207
  const isRing = ringIndex !== undefined;
142
208
  if (isRing && !closed(line)) line.push([...line[0]]);
143
- for (const p of map.projectLine(line, options.sampling)) {
209
+ for (const p of map.projectLine(line, {
210
+ ...options.sampling,
211
+ pathProvenance: lineage,
212
+ pathDistances: Boolean(provenance?.distances),
213
+ pathBoundaries: Boolean(provenance?.boundaries)
214
+ })) {
215
+ if (lineage) {
216
+ if (!p.provenance) throw new Error('Map backend does not support path provenance');
217
+ if (provenance?.sourceSegments) {
218
+ segments.push(...p.provenance.segments);
219
+ parameters.push(...p.provenance.parameters);
220
+ originalVertices.push(...p.provenance.vertices);
221
+ }
222
+ if (provenance?.distances)
223
+ distances.push(
224
+ ...p.provenance.angularDistances.map(d => d * provenance.distances!.radius)
225
+ );
226
+ if (provenance?.boundaries) {
227
+ startReasons.push(p.provenance.startReason!);
228
+ endReasons.push(p.provenance.endReason!);
229
+ }
230
+ }
144
231
  append(paths, p.coordinates, p.sourceCoordinates);
145
232
  pathParts.push({
146
233
  ...part(featureIndex, geometryPath, p.regionId),
@@ -159,10 +246,37 @@ export function prepareMapFeatures(
159
246
  if (!input.length) return;
160
247
  const rings = input.map(r => r.map(position));
161
248
  const parts = options.sphericalCuts
162
- ? map.projectGlobalPolygon(rings, {...options.sampling, ...options.sphericalCuts})
163
- : map.projectPolygon(rings, options.sampling);
249
+ ? map.projectGlobalPolygon(rings, {
250
+ ...options.sampling,
251
+ ...options.sphericalCuts,
252
+ polygonEdgeProvenance: provenance?.polygonEdges
253
+ })
254
+ : map.projectPolygon(rings, {
255
+ ...options.sampling,
256
+ polygonEdgeProvenance: provenance?.polygonEdges
257
+ });
164
258
  for (const p of parts) {
165
259
  for (let i = 0; i < p.coordinates.length; i++) {
260
+ if (provenance?.polygonEdges) {
261
+ const edges = p.edgeProvenance?.[i];
262
+ if (!edges) throw new Error('Map backend does not support polygon edge provenance');
263
+ const kinds = [
264
+ 'source-boundary',
265
+ 'coverage-boundary',
266
+ 'chart-cut',
267
+ 'continuous-chart-join'
268
+ ];
269
+ for (const edge of edges) {
270
+ edgeKinds.push(kinds.indexOf(edge.kind) + 1);
271
+ edgeRings.push(edge.ring);
272
+ edgeSegments.push(edge.segment);
273
+ edgeParameters.push(edge.t0, edge.t1);
274
+ }
275
+ edgeKinds.push(0);
276
+ edgeRings.push(0xffffffff);
277
+ edgeSegments.push(0xffffffff);
278
+ edgeParameters.push(0, 0);
279
+ }
166
280
  append(polygons, p.coordinates[i], p.sourceCoordinates[i]);
167
281
  ringStarts.push(polygons.positions.length / 2);
168
282
  }
@@ -234,12 +348,46 @@ export function prepareMapFeatures(
234
348
  featureIds,
235
349
  regionIds,
236
350
  points: {...pack(points), parts: pointParts},
237
- paths: {...pack(paths), parts: pathParts, pathStarts: new Uint32Array(pathStarts)},
351
+ paths: {
352
+ ...pack(paths),
353
+ parts: pathParts,
354
+ pathStarts: new Uint32Array(pathStarts),
355
+ ...(lineage
356
+ ? {
357
+ provenance: {
358
+ ...(provenance?.sourceSegments
359
+ ? {
360
+ sourceSegmentIndices: new Uint32Array(segments),
361
+ sourceSegmentParameters: new Float64Array(parameters),
362
+ sourceVertexIndices: new Uint32Array(originalVertices)
363
+ }
364
+ : {}),
365
+ ...(provenance?.distances
366
+ ? {
367
+ sourceDistances: new Float64Array(distances),
368
+ distanceRadius: provenance.distances.radius
369
+ }
370
+ : {}),
371
+ ...(provenance?.boundaries ? {startReasons, endReasons} : {})
372
+ }
373
+ }
374
+ : {})
375
+ },
238
376
  polygons: {
239
377
  ...pack(polygons),
240
378
  parts: polygonParts,
241
379
  ringStarts: new Uint32Array(ringStarts),
242
- polygonStarts: new Uint32Array(polygonStarts)
380
+ polygonStarts: new Uint32Array(polygonStarts),
381
+ ...(provenance?.polygonEdges
382
+ ? {
383
+ edgeProvenance: {
384
+ kinds: new Uint8Array(edgeKinds),
385
+ sourceRingIndices: new Uint32Array(edgeRings),
386
+ sourceSegmentIndices: new Uint32Array(edgeSegments),
387
+ sourceParameterRanges: new Float64Array(edgeParameters)
388
+ }
389
+ }
390
+ : {})
243
391
  }
244
392
  };
245
393
  }
@@ -0,0 +1,31 @@
1
+ // math.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
4
+ import {angle, coordinates, geographic, unit, type Position, type Vector} from './math';
5
+
6
+ /** Preserve the source minor arc, including each owning chart's longitude side. */
7
+ export function sourceArc(a: Position, b: Position) {
8
+ const va = geographic(a),
9
+ vb = geographic(b),
10
+ length = angle(va, vb);
11
+ const poleA = Math.abs(a[1]) === 90,
12
+ poleB = Math.abs(b[1]) === 90;
13
+ const start: Position = poleA && !poleB ? [b[0], a[1]] : [...a],
14
+ end: Position = poleB && !poleA ? [a[0], b[1]] : [...b];
15
+ return (t: number): Position => {
16
+ if (t === 0) return [...start];
17
+ if (t === 1) return [...end];
18
+ if (length < 1e-14) return [a[0] + t * (b[0] - a[0]), a[1] + t * (b[1] - a[1])];
19
+ const p = coordinates(
20
+ unit(
21
+ va.map(
22
+ (v, k) =>
23
+ (Math.sin((1 - t) * length) * v + Math.sin(t * length) * vb[k]) / Math.sin(length)
24
+ ) as Vector
25
+ )
26
+ );
27
+ const reference = start[0] + t * (end[0] - start[0]);
28
+ p[0] += 360 * Math.round((reference - p[0]) / 360);
29
+ return p;
30
+ };
31
+ }
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ export {
16
16
  createCRSExecutionIdentity,
17
17
  getCRSExecutionKey,
18
18
  inferCRSRepresentation,
19
+ normalizeCRSIdentifier,
19
20
  encodeWKTCRS,
20
21
  parseWKTCRS,
21
22
  validateWKTCRS,
package/src/turf.ts CHANGED
@@ -1198,3 +1198,89 @@ export function kinks(column: GeoArrowColumn): GeoArrowColumn {
1198
1198
  return true;
1199
1199
  });
1200
1200
  }
1201
+ /** Arithmetic vertex mean per geometry, including polygon closing vertices (Turf centerMean semantics). */
1202
+ export function centerMean(column: GeoArrowColumn): GeoArrowColumn {
1203
+ const input = rows(column, native);
1204
+ return pointOutput(column, input.length, i => {
1205
+ const row = input[i];
1206
+ if (!row.valid) return null;
1207
+ let x = 0,
1208
+ y = 0,
1209
+ count = 0;
1210
+ walk(row.array, row.index, depths[column.encoding], p => {
1211
+ x += p[0];
1212
+ y += p[1];
1213
+ count++;
1214
+ });
1215
+ return count ? [x / count, y / count] : null;
1216
+ });
1217
+ }
1218
+
1219
+ /** Clockwise angle at the middle point, with single-row broadcasting on all three inputs. */
1220
+ export function angle(
1221
+ start: GeoArrowColumn,
1222
+ middle: GeoArrowColumn,
1223
+ end: GeoArrowColumn,
1224
+ options: {mercator?: boolean; explementary?: boolean} = {}
1225
+ ): TurfBatchResult<Float64Array> {
1226
+ const inputs = [start, middle, end].map(c => rows(c, points));
1227
+ const sizes = inputs.map(r => r.length),
1228
+ count = sizes.find(n => n !== 1) ?? 1;
1229
+ if (sizes.some(n => n !== 1 && n !== count))
1230
+ throw new RangeError('Angle batch lengths must match or contain one row');
1231
+ const out = result(count) as TurfBatchResult<Float64Array>;
1232
+ for (let i = 0; i < count; i++) {
1233
+ const p = inputs.map(r => {
1234
+ const row = r[r.length === 1 ? 0 : i];
1235
+ return row.valid ? point(row.array, row.index) : null;
1236
+ });
1237
+ if (p.some(v => !v)) continue;
1238
+ const heading = (a: Point, b: Point) =>
1239
+ options.mercator
1240
+ ? rhumbAngle(a, b) / RAD
1241
+ : Math.atan2(
1242
+ Math.sin((b[0] - a[0]) * RAD) * Math.cos(b[1] * RAD),
1243
+ Math.cos(a[1] * RAD) * Math.sin(b[1] * RAD) -
1244
+ Math.sin(a[1] * RAD) * Math.cos(b[1] * RAD) * Math.cos((b[0] - a[0]) * RAD)
1245
+ ) / RAD;
1246
+ const value = (heading(p[1], p[2]) - heading(p[1], p[0]) + 360) % 360;
1247
+ out.values[i] = options.explementary ? 360 - value : value;
1248
+ valid(out.validity, i);
1249
+ }
1250
+ return out;
1251
+ }
1252
+
1253
+ /** Tests corresponding LineString segments for parallel rhumb headings. */
1254
+ export function booleanParallel(a: GeoArrowColumn, b: GeoArrowColumn): TurfBatchResult<Uint8Array> {
1255
+ const left = rows(a, ['geoarrow.linestring']),
1256
+ right = rows(b, ['geoarrow.linestring']),
1257
+ count = paired(left, right),
1258
+ out = result(count, true) as TurfBatchResult<Uint8Array>;
1259
+ for (let i = 0; i < count; i++) {
1260
+ const x = left[left.length === 1 ? 0 : i],
1261
+ y = right[right.length === 1 ? 0 : i];
1262
+ if (!x.valid || !y.valid) continue;
1263
+ const p = segments(x, a.encoding),
1264
+ q = segments(y, b.encoding);
1265
+ let parallel = p.length > 0 && p.length === q.length;
1266
+ for (let j = 0; parallel && j < p.length; j++) {
1267
+ const u = point(p[j].array, p[j].index),
1268
+ v = point(p[j].array, p[j].index + 1),
1269
+ w = point(q[j].array, q[j].index),
1270
+ z = point(q[j].array, q[j].index + 1);
1271
+ if (!u || !v || !w || !z) {
1272
+ parallel = false;
1273
+ break;
1274
+ }
1275
+ const difference = (rhumbAngle(u, v) - rhumbAngle(w, z)) / RAD;
1276
+ const deviation = Math.abs(difference - Math.round(difference / 180) * 180);
1277
+ if (deviation > 1e-10) {
1278
+ parallel = false;
1279
+ break;
1280
+ }
1281
+ }
1282
+ out.values[i] = Number(parallel);
1283
+ valid(out.validity, i);
1284
+ }
1285
+ return out;
1286
+ }