@math.gl/geoarrow 5.0.0-alpha.2 → 5.0.0-alpha.3

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 (54) hide show
  1. package/README.md +45 -15
  2. package/dist/builder-entry.cjs +561 -0
  3. package/dist/builder-entry.cjs.map +6 -0
  4. package/dist/builder-entry.d.ts +4 -0
  5. package/dist/builder-entry.d.ts.map +1 -0
  6. package/dist/builder-entry.js +5 -0
  7. package/dist/builder-entry.js.map +1 -0
  8. package/dist/builder.d.ts +12 -1
  9. package/dist/builder.d.ts.map +1 -1
  10. package/dist/builder.js +112 -10
  11. package/dist/builder.js.map +1 -1
  12. package/dist/codecs.d.ts.map +1 -1
  13. package/dist/codecs.js +205 -7
  14. package/dist/codecs.js.map +1 -1
  15. package/dist/index.cjs +904 -120
  16. package/dist/index.cjs.map +2 -2
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/kernels.d.ts +8 -3
  22. package/dist/kernels.d.ts.map +1 -1
  23. package/dist/kernels.js +610 -107
  24. package/dist/kernels.js.map +1 -1
  25. package/dist/layout.d.ts.map +1 -1
  26. package/dist/layout.js +28 -5
  27. package/dist/layout.js.map +1 -1
  28. package/dist/tessellation-entry.cjs +427 -0
  29. package/dist/tessellation-entry.cjs.map +6 -0
  30. package/dist/tessellation-entry.d.ts +4 -0
  31. package/dist/tessellation-entry.d.ts.map +1 -0
  32. package/dist/tessellation-entry.js +5 -0
  33. package/dist/tessellation-entry.js.map +1 -0
  34. package/dist/types.d.ts +37 -3
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/types.js.map +1 -1
  37. package/dist/wkb-entry.cjs +1005 -0
  38. package/dist/wkb-entry.cjs.map +6 -0
  39. package/dist/wkb-entry.d.ts +3 -0
  40. package/dist/wkb-entry.d.ts.map +1 -0
  41. package/dist/wkb-entry.js +6 -0
  42. package/dist/wkb-entry.js.map +1 -0
  43. package/dist/worker.cjs +4 -0
  44. package/dist/worker.cjs.map +1 -1
  45. package/package.json +21 -6
  46. package/src/builder-entry.ts +18 -0
  47. package/src/builder.ts +133 -19
  48. package/src/codecs.ts +251 -9
  49. package/src/index.ts +1 -0
  50. package/src/kernels.ts +811 -133
  51. package/src/layout.ts +38 -13
  52. package/src/tessellation-entry.ts +7 -0
  53. package/src/types.ts +23 -4
  54. package/src/wkb-entry.ts +11 -0
package/README.md CHANGED
@@ -1,16 +1,19 @@
1
1
  # @math.gl/geoarrow
2
2
 
3
- Columnar geometry descriptors and synchronous CPU kernels for GeoArrow-compatible memory layouts.
3
+ Arrow-independent columnar geometry descriptors and synchronous buffer algorithms for
4
+ GeoArrow-compatible memory layouts.
4
5
 
5
6
  `@math.gl/geoarrow` works directly over borrowed typed arrays. It deliberately does not require an
6
7
  Arrow runtime: a loader, dataframe, database client, or application can adapt its buffers to the
7
- small descriptor ABI and use the same validation, conversion, column-codec, and tessellation
8
- kernels.
8
+ small descriptor ABI and use the same validation, conversion, and coordinate algorithms. Optional
9
+ format and mesh capabilities are isolated behind subpaths so consumers only bundle what they use.
9
10
 
10
11
  ## Install
11
12
 
12
13
  ```bash
13
14
  npm install @math.gl/geoarrow
15
+ # Only needed when using the /wkb subpath directly.
16
+ npm install @math.gl/wkb
14
17
  ```
15
18
 
16
19
  ## Why descriptors?
@@ -34,11 +37,11 @@ identity as a reliable zero-copy signal.
34
37
 
35
38
  ```typescript
36
39
  import {
37
- GeoArrowBuilder,
38
40
  getGeoArrowBounds,
39
41
  getGeoArrowVertexCount,
40
42
  mapGeoArrowCoordinates
41
43
  } from '@math.gl/geoarrow';
44
+ import {GeoArrowBuilder} from '@math.gl/geoarrow/builder';
42
45
 
43
46
  const column = GeoArrowBuilder.build(
44
47
  [
@@ -173,11 +176,13 @@ The following operations traverse descriptors directly and do not create per-row
173
176
  - `validateGeoArrowColumn`
174
177
  - `getGeoArrowVertexCount`
175
178
  - `getGeoArrowBounds`
179
+ - `getGeoArrowRowBounds`
176
180
  - `visitGeoArrowCoordinates`
177
181
  - `getGeoArrowTransferList`
178
182
 
179
- Allocating transforms such as coordinate mapping, physical conversion, winding normalization, and
180
- codec decoding return new descriptors. Their inputs remain unchanged.
183
+ Allocating transforms such as coordinate mapping, physical conversion, and winding normalization
184
+ return new descriptors. Their inputs remain unchanged. Serialized codecs are isolated in the
185
+ `/wkb` subpath.
181
186
 
182
187
  ## Conversion and winding
183
188
 
@@ -207,7 +212,7 @@ import {
207
212
  encodeGeoArrowWKB,
208
213
  decodeGeoArrowWKT,
209
214
  encodeGeoArrowWKT
210
- } from '@math.gl/geoarrow';
215
+ } from '@math.gl/geoarrow/wkb';
211
216
 
212
217
  const wkb = encodeGeoArrowWKB(polygons);
213
218
  const decoded = decodeGeoArrowWKB(wkb);
@@ -221,19 +226,20 @@ families, dimension tokens, both MultiPoint spellings, and empties. Decoding nor
221
226
  serialized coordinates to the column's declared semantic dimension.
222
227
 
223
228
  Parsing or formatting one geometry is intentionally provided by the dependency-free
224
- `@math.gl/wkb` package:
229
+ `@math.gl/wkb` package. The GeoArrow `/wkb` bridge consumes its visitors and two-pass builder:
225
230
 
226
231
  ```typescript
227
232
  import {parseWKB, writeWKB, parseWKT, formatWKT} from '@math.gl/wkb';
228
233
  ```
229
234
 
230
- `@math.gl/geoarrow` depends on `@math.gl/wkb`; the format package never depends on GeoArrow or
231
- Apache Arrow.
235
+ The descriptor and buffer-algorithm implementation does not require Apache Arrow. The `/wkb`
236
+ subpath (and the legacy root codec re-exports) uses `@math.gl/wkb`; install it when using those
237
+ functions.
232
238
 
233
239
  ## Polygon tessellation
234
240
 
235
241
  ```typescript
236
- import {tessellateGeoArrowPolygons} from '@math.gl/geoarrow';
242
+ import {tessellateGeoArrowPolygons} from '@math.gl/geoarrow/tessellation';
237
243
 
238
244
  const mesh = tessellateGeoArrowPolygons(polygons, {
239
245
  positionSize: 3,
@@ -280,6 +286,29 @@ worker.postMessage(payload.column, {transfer: payload.transferList});
280
286
  explicit `postMessage` call transfers ownership. Shared buffers are omitted because they are not
281
287
  transferable.
282
288
 
289
+ ## Loaders.gl integration
290
+
291
+ The intended loaders.gl architecture keeps Apache Arrow at the boundary:
292
+
293
+ ```text
294
+ Arrow Data / Vector / Table
295
+ │ loaders.gl adapter
296
+
297
+ GeoArrowColumn descriptors ──► @math.gl/geoarrow buffer algorithms
298
+
299
+ └── loaders.gl Arrow field + extension metadata
300
+ ```
301
+
302
+ The adapter should borrow Arrow buffers and preserve chunking, offsets, validity, dense-union
303
+ dispatch, dimensions, layouts, CRS, and extension metadata. It should not call Arrow scalar
304
+ accessors in conversion paths. Keep GeoJSON conversion, adaptive target policy, Arrow schema
305
+ construction, and row-shaped compatibility values in loaders.gl.
306
+
307
+ Use the optional `/wkb` bridge only for serialized columns and `/builder` when emitting native
308
+ descriptors from a feature stream. For event-built MultiPolygons, call `beginPolygon()` before each
309
+ part; omitting it retains the compatibility behavior of treating all rings as one polygon. This
310
+ keeps the common Arrow-to-native path independent of text and binary codec code.
311
+
283
312
  ## Adapting another columnar runtime
284
313
 
285
314
  Keep runtime-specific objects at the boundary. Read their physical buffers and construct a
@@ -299,11 +328,12 @@ the producer runtime.
299
328
 
300
329
  - Descriptors: `GeoArrowColumn`, `GeoArrowArray`, all physical array descriptor types
301
330
  - Layout: `inspectGeoArrowColumn`, `validateGeoArrowColumn`, slicing and traversal
302
- - Kernels: count, bounds, map, interleave, convert, rewind, union normalization, resource limits
303
- - Construction: `GeoArrowBuilder`, `makeGeoArrowColumnFromGeometryRows`
304
- - Column codecs: WKB/WKT descriptor encode and decode (individual parsing/formatting is tested in
331
+ - Buffer algorithms: count, bounds, row bounds, map, interleave, convert, rewind, union
332
+ normalization, resource limits
333
+ - Construction: `@math.gl/geoarrow/builder`
334
+ - Column codecs: `@math.gl/geoarrow/wkb` (individual parsing/formatting is provided by
305
335
  `@math.gl/wkb`)
306
- - Meshes: `tessellateGeoArrowPolygons`
336
+ - Meshes: `@math.gl/geoarrow/tessellation`
307
337
  - Transfer: `getGeoArrowTransferList`, plus `@math.gl/geoarrow/worker`
308
338
 
309
339
  See the math.gl documentation for the full [physical-layout guide](../../docs/modules/geoarrow/physical-layouts.md),
@@ -0,0 +1,561 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // dist/builder-entry.js
20
+ var builder_entry_exports = {};
21
+ __export(builder_entry_exports, {
22
+ GeoArrowBuilder: () => GeoArrowBuilder,
23
+ allocateGeoArrowBuilderTarget: () => allocateGeoArrowBuilderTarget,
24
+ makeGeoArrowColumnFromGeometryRows: () => makeGeoArrowColumnFromGeometryRows
25
+ });
26
+ module.exports = __toCommonJS(builder_entry_exports);
27
+
28
+ // dist/types.js
29
+ function getGeoArrowDimensionSize(dimension) {
30
+ switch (dimension) {
31
+ case "xy":
32
+ return 2;
33
+ case "xyz":
34
+ case "xym":
35
+ return 3;
36
+ case "xyzm":
37
+ return 4;
38
+ }
39
+ }
40
+ function getGeoArrowGeometryType(encoding) {
41
+ switch (encoding) {
42
+ case "geoarrow.point":
43
+ return "Point";
44
+ case "geoarrow.linestring":
45
+ return "LineString";
46
+ case "geoarrow.polygon":
47
+ return "Polygon";
48
+ case "geoarrow.multipoint":
49
+ return "MultiPoint";
50
+ case "geoarrow.multilinestring":
51
+ return "MultiLineString";
52
+ case "geoarrow.multipolygon":
53
+ return "MultiPolygon";
54
+ case "geoarrow.geometrycollection":
55
+ return "GeometryCollection";
56
+ default:
57
+ return null;
58
+ }
59
+ }
60
+ function getGeoArrowEncodingForGeometry(type) {
61
+ return `geoarrow.${type.toLowerCase()}`;
62
+ }
63
+
64
+ // dist/builder.js
65
+ var GeoArrowBuilder = class _GeoArrowBuilder {
66
+ constructor(options) {
67
+ this.length = 0;
68
+ this.nullCount = 0;
69
+ this.coordinateCount = 0;
70
+ this.partCount = 0;
71
+ this.ringCount = 0;
72
+ this.eventStack = [];
73
+ this.mode = options.mode;
74
+ this.encoding = options.encoding;
75
+ this.dimension = options.dimension || "xy";
76
+ this.coordinateLayout = options.coordinateLayout || "interleaved";
77
+ this.offsetType = options.offsetType || "int32";
78
+ this.coordinateType = options.coordinateType || "float64";
79
+ this.target = options.mode === "write" ? options.target : void 0;
80
+ if (this.target)
81
+ this.initializeTargetOffsets(this.target);
82
+ }
83
+ /** Appends one geometry row or null. */
84
+ append(geometry) {
85
+ var _a, _b, _c, _d, _e, _f;
86
+ const rowIndex = this.length++;
87
+ if (!geometry) {
88
+ this.nullCount++;
89
+ this.appendNull(rowIndex);
90
+ return this;
91
+ }
92
+ const expectedType = getGeoArrowGeometryType(this.encoding);
93
+ if (geometry.type !== expectedType) {
94
+ throw new Error(`GeoArrowBuilder for ${this.encoding} cannot append ${geometry.type}`);
95
+ }
96
+ if (geometry.type === "GeometryCollection") {
97
+ throw new Error("GeoArrowBuilder only accepts concrete geometry families");
98
+ }
99
+ if (this.target)
100
+ setValidityBit(this.target.validity, rowIndex);
101
+ const depth = getBuilderDepth(this.encoding);
102
+ const coordinates = geometry.coordinates;
103
+ if (depth === 0) {
104
+ this.writeCoordinateValues(coordinates);
105
+ } else if (depth === 1) {
106
+ this.writeCoordinateList(coordinates);
107
+ this.writeOffset((_a = this.target) == null ? void 0 : _a.geometryOffsets, rowIndex + 1, this.coordinateCount);
108
+ } else if (depth === 2) {
109
+ for (const part of coordinates) {
110
+ this.writeCoordinateList(part);
111
+ this.partCount++;
112
+ this.writeOffset((_b = this.target) == null ? void 0 : _b.partOffsets, this.partCount, this.coordinateCount);
113
+ }
114
+ this.writeOffset((_c = this.target) == null ? void 0 : _c.geometryOffsets, rowIndex + 1, this.partCount);
115
+ } else {
116
+ for (const polygon of coordinates) {
117
+ for (const ring of polygon) {
118
+ this.writeCoordinateList(ring);
119
+ this.ringCount++;
120
+ this.writeOffset((_d = this.target) == null ? void 0 : _d.ringOffsets, this.ringCount, this.coordinateCount);
121
+ }
122
+ this.partCount++;
123
+ this.writeOffset((_e = this.target) == null ? void 0 : _e.partOffsets, this.partCount, this.ringCount);
124
+ }
125
+ this.writeOffset((_f = this.target) == null ? void 0 : _f.geometryOffsets, rowIndex + 1, this.partCount);
126
+ }
127
+ return this;
128
+ }
129
+ /** Returns current exact allocation counts. */
130
+ getMeasurement() {
131
+ const depth = getBuilderDepth(this.encoding);
132
+ return {
133
+ length: this.length,
134
+ nullCount: this.nullCount,
135
+ coordinateCount: this.coordinateCount,
136
+ geometryOffsetCount: depth >= 1 ? this.length + 1 : 0,
137
+ partOffsetCount: depth >= 2 ? this.partCount + 1 : 0,
138
+ ringOffsetCount: depth >= 3 ? this.ringCount + 1 : 0
139
+ };
140
+ }
141
+ /** Allocates a write target from the current measure pass. */
142
+ allocateTarget() {
143
+ if (this.mode !== "measure")
144
+ throw new Error("Only a measure builder can allocate a target");
145
+ return allocateGeoArrowBuilderTarget(this.getMeasurement(), {
146
+ encoding: this.encoding,
147
+ dimension: this.dimension,
148
+ coordinateLayout: this.coordinateLayout,
149
+ offsetType: this.offsetType,
150
+ coordinateType: this.coordinateType
151
+ });
152
+ }
153
+ /** Finishes a write pass and returns a one-chunk borrowed column. */
154
+ finish() {
155
+ if (!this.target)
156
+ throw new Error("A measure builder has no finished column");
157
+ const measurement = this.getMeasurement();
158
+ assertTargetCapacity(this.target, measurement, getGeoArrowDimensionSize(this.dimension));
159
+ const coordinates = makeCoordinateArray(this.target.coordinates, measurement.coordinateCount, this.dimension, this.coordinateLayout);
160
+ const depth = getBuilderDepth(this.encoding);
161
+ let chunk = coordinates;
162
+ if (depth >= 3) {
163
+ chunk = {
164
+ kind: "list",
165
+ length: this.ringCount,
166
+ offsets: this.target.ringOffsets,
167
+ child: chunk
168
+ };
169
+ }
170
+ if (depth >= 2) {
171
+ chunk = {
172
+ kind: "list",
173
+ length: this.partCount,
174
+ offsets: this.target.partOffsets,
175
+ child: chunk
176
+ };
177
+ }
178
+ if (depth >= 1) {
179
+ chunk = {
180
+ kind: "list",
181
+ length: this.length,
182
+ offsets: this.target.geometryOffsets,
183
+ child: chunk,
184
+ validity: { values: this.target.validity }
185
+ };
186
+ } else {
187
+ chunk = { ...chunk, validity: { values: this.target.validity } };
188
+ }
189
+ return {
190
+ encoding: this.encoding,
191
+ dimension: this.dimension,
192
+ coordinateLayout: this.coordinateLayout,
193
+ chunks: [chunk]
194
+ };
195
+ }
196
+ /** Builds a homogeneous column using an internal measure/write pair. */
197
+ static build(rows, options) {
198
+ const measure = new _GeoArrowBuilder({ ...options, mode: "measure" });
199
+ for (const row of rows)
200
+ measure.append(row);
201
+ const write = new _GeoArrowBuilder({
202
+ ...options,
203
+ mode: "write",
204
+ target: measure.allocateTarget()
205
+ });
206
+ for (const row of rows)
207
+ write.append(row);
208
+ return write.finish();
209
+ }
210
+ appendNull(rowIndex) {
211
+ var _a;
212
+ const depth = getBuilderDepth(this.encoding);
213
+ if (depth === 0) {
214
+ this.writeCoordinateValues(new Array(getGeoArrowDimensionSize(this.dimension)).fill(0));
215
+ } else {
216
+ this.writeOffset((_a = this.target) == null ? void 0 : _a.geometryOffsets, rowIndex + 1, depth === 1 ? this.coordinateCount : this.partCount);
217
+ }
218
+ }
219
+ writeCoordinateList(coordinates) {
220
+ for (const coordinate of coordinates)
221
+ this.writeCoordinateValues(coordinate);
222
+ }
223
+ writeCoordinateValues(coordinate) {
224
+ const size = getGeoArrowDimensionSize(this.dimension);
225
+ if (coordinate.length !== size) {
226
+ throw new Error(`Expected ${size} coordinate values for ${this.dimension}`);
227
+ }
228
+ if (this.target)
229
+ writeCoordinate(this.target.coordinates, this.coordinateCount, coordinate, this.dimension);
230
+ this.coordinateCount++;
231
+ }
232
+ /** Begins an event-driven geometry. Events are accepted by both measure and write builders. */
233
+ beginGeometry(type, dimension = this.dimension, _count) {
234
+ this.eventStack.push({ type, dimension, rings: [], polygons: [], children: [] });
235
+ return this;
236
+ }
237
+ /** Begins a polygon part inside an event-built MultiPolygon. */
238
+ beginPolygon() {
239
+ const state = this.eventStack[this.eventStack.length - 1];
240
+ if (!state || state.type !== "MultiPolygon") {
241
+ throw new Error("beginPolygon must follow beginGeometry('MultiPolygon')");
242
+ }
243
+ state.currentPolygon = [];
244
+ state.polygons.push(state.currentPolygon);
245
+ state.currentRing = void 0;
246
+ return this;
247
+ }
248
+ /** Begins a ring/part in the current event geometry. */
249
+ beginRing(_count) {
250
+ const state = this.eventStack[this.eventStack.length - 1];
251
+ if (!state)
252
+ throw new Error("beginRing must follow beginGeometry");
253
+ state.currentRing = [];
254
+ if (state.type === "MultiPolygon") {
255
+ if (!state.currentPolygon)
256
+ this.beginPolygon();
257
+ state.currentPolygon.push(state.currentRing);
258
+ } else {
259
+ state.rings.push(state.currentRing);
260
+ }
261
+ return this;
262
+ }
263
+ /** Writes one coordinate into the current event ring or point. */
264
+ writeCoordinate(x, y, z, m) {
265
+ const state = this.eventStack[this.eventStack.length - 1];
266
+ if (!state)
267
+ throw new Error("writeCoordinate must follow beginGeometry");
268
+ const coordinate = Array.isArray(x) ? [...x] : [x, y, ...z === void 0 ? [] : [z], ...m === void 0 ? [] : [m]];
269
+ const size = getGeoArrowDimensionSize(state.dimension);
270
+ if (coordinate.length !== size)
271
+ throw new Error(`Expected ${size} coordinate values for ${state.dimension}`);
272
+ if (state.type === "Point") {
273
+ state.rings = [[coordinate]];
274
+ return this;
275
+ }
276
+ if (!state.currentRing)
277
+ this.beginRing();
278
+ state.currentRing.push(coordinate);
279
+ return this;
280
+ }
281
+ /** Ends the current event geometry and appends it to the builder/parent. */
282
+ endGeometry() {
283
+ var _a;
284
+ const state = this.eventStack.pop();
285
+ if (!state)
286
+ throw new Error("endGeometry without beginGeometry");
287
+ const coordinates = state.rings;
288
+ let geometry;
289
+ switch (state.type) {
290
+ case "Point":
291
+ geometry = { type: "Point", coordinates: ((_a = coordinates[0]) == null ? void 0 : _a[0]) || [] };
292
+ break;
293
+ case "LineString":
294
+ geometry = { type: "LineString", coordinates: coordinates[0] || [] };
295
+ break;
296
+ case "MultiPoint":
297
+ geometry = { type: "MultiPoint", coordinates: coordinates.flat() };
298
+ break;
299
+ case "Polygon":
300
+ geometry = { type: "Polygon", coordinates };
301
+ break;
302
+ case "MultiLineString":
303
+ geometry = { type: "MultiLineString", coordinates };
304
+ break;
305
+ case "MultiPolygon":
306
+ geometry = {
307
+ type: "MultiPolygon",
308
+ coordinates: state.polygons.length ? state.polygons : [coordinates]
309
+ };
310
+ break;
311
+ case "GeometryCollection":
312
+ geometry = { type: "GeometryCollection", geometries: state.children };
313
+ break;
314
+ }
315
+ const parent = this.eventStack[this.eventStack.length - 1];
316
+ if ((parent == null ? void 0 : parent.type) === "GeometryCollection")
317
+ parent.children.push(geometry);
318
+ else
319
+ this.append(geometry);
320
+ return this;
321
+ }
322
+ writeOffset(target, index, value) {
323
+ if (!target)
324
+ return;
325
+ if (target instanceof BigInt64Array)
326
+ target[index] = BigInt(value);
327
+ else
328
+ target[index] = value;
329
+ }
330
+ initializeTargetOffsets(target) {
331
+ this.writeOffset(target.geometryOffsets, 0, 0);
332
+ this.writeOffset(target.partOffsets, 0, 0);
333
+ this.writeOffset(target.ringOffsets, 0, 0);
334
+ }
335
+ };
336
+ function allocateGeoArrowBuilderTarget(measurement, options) {
337
+ const dimension = options.dimension || "xy";
338
+ const size = getGeoArrowDimensionSize(dimension);
339
+ const FloatArray = options.coordinateType === "float32" ? Float32Array : Float64Array;
340
+ const coordinateLayout = options.coordinateLayout || "interleaved";
341
+ const coordinates = coordinateLayout === "interleaved" ? new FloatArray(measurement.coordinateCount * size) : makeSeparatedCoordinateTarget(FloatArray, measurement.coordinateCount, dimension);
342
+ const OffsetArray = options.offsetType === "int64" ? BigInt64Array : Int32Array;
343
+ return {
344
+ validity: new Uint8Array(Math.ceil(measurement.length / 8)),
345
+ coordinates,
346
+ geometryOffsets: measurement.geometryOffsetCount ? new OffsetArray(measurement.geometryOffsetCount) : void 0,
347
+ partOffsets: measurement.partOffsetCount ? new OffsetArray(measurement.partOffsetCount) : void 0,
348
+ ringOffsets: measurement.ringOffsetCount ? new OffsetArray(measurement.ringOffsetCount) : void 0
349
+ };
350
+ }
351
+ function makeGeoArrowColumnFromGeometryRows(rows, options = {}) {
352
+ const geometryTypes = [...new Set(rows.filter(Boolean).map((row) => row.type))];
353
+ const dimension = options.dimension || inferRowsDimension(rows);
354
+ if (options.encoding === "geoarrow.geometry") {
355
+ return {
356
+ encoding: "geoarrow.geometry",
357
+ dimension,
358
+ coordinateLayout: options.coordinateLayout || "interleaved",
359
+ chunks: [makeDenseUnionArray(rows, options, dimension)]
360
+ };
361
+ }
362
+ if (geometryTypes.length === 0) {
363
+ return GeoArrowBuilder.build(rows, { ...options, dimension, encoding: "geoarrow.point" });
364
+ }
365
+ if (geometryTypes.length === 1 && geometryTypes[0] !== "GeometryCollection") {
366
+ return GeoArrowBuilder.build(rows, {
367
+ ...options,
368
+ dimension,
369
+ encoding: getGeoArrowEncodingForGeometry(geometryTypes[0])
370
+ });
371
+ }
372
+ if (geometryTypes.length === 1 && geometryTypes[0] === "GeometryCollection") {
373
+ return {
374
+ encoding: "geoarrow.geometrycollection",
375
+ dimension,
376
+ coordinateLayout: options.coordinateLayout || "interleaved",
377
+ chunks: [makeGeometryCollectionArray(rows, options, dimension)]
378
+ };
379
+ }
380
+ return {
381
+ encoding: "geoarrow.geometry",
382
+ dimension,
383
+ coordinateLayout: options.coordinateLayout || "interleaved",
384
+ chunks: [makeDenseUnionArray(rows, options, dimension)]
385
+ };
386
+ }
387
+ function makeDenseUnionArray(rows, options, dimension) {
388
+ const nonNullTypes = [...new Set(rows.filter(Boolean).map((row) => row.type))];
389
+ const fallbackType = nonNullTypes[0] || "Point";
390
+ const childRows = /* @__PURE__ */ new Map();
391
+ for (const type of nonNullTypes.length ? nonNullTypes : [fallbackType])
392
+ childRows.set(type, []);
393
+ const typeIds = new Int8Array(rows.length);
394
+ const valueOffsets = new Int32Array(rows.length);
395
+ const validity = new Uint8Array(Math.ceil(rows.length / 8));
396
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
397
+ const geometry = rows[rowIndex];
398
+ const type = (geometry == null ? void 0 : geometry.type) || fallbackType;
399
+ const values = childRows.get(type) || [];
400
+ typeIds[rowIndex] = getGeometryTypeId(type, dimension);
401
+ valueOffsets[rowIndex] = values.length;
402
+ if (geometry) {
403
+ values.push(geometry);
404
+ childRows.set(type, values);
405
+ setValidityBit(validity, rowIndex);
406
+ }
407
+ }
408
+ const children = [...childRows.entries()].sort(([left], [right]) => getGeometryTypeId(left, dimension) - getGeometryTypeId(right, dimension)).map(([type, values]) => {
409
+ const data = type === "GeometryCollection" ? makeGeometryCollectionArray(values, options, dimension) : GeoArrowBuilder.build(values, {
410
+ ...options,
411
+ dimension,
412
+ encoding: getGeoArrowEncodingForGeometry(type)
413
+ }).chunks[0];
414
+ return {
415
+ name: type,
416
+ typeId: getGeometryTypeId(type, dimension),
417
+ encoding: getGeoArrowEncodingForGeometry(type),
418
+ dimension,
419
+ coordinateLayout: options.coordinateLayout || "interleaved",
420
+ data
421
+ };
422
+ });
423
+ return {
424
+ kind: "dense-union",
425
+ length: rows.length,
426
+ typeIds,
427
+ valueOffsets,
428
+ children,
429
+ validity: { values: validity }
430
+ };
431
+ }
432
+ function makeGeometryCollectionArray(rows, options, dimension) {
433
+ const offsets = new Int32Array(rows.length + 1);
434
+ const validity = new Uint8Array(Math.ceil(rows.length / 8));
435
+ const flattened = [];
436
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
437
+ const row = rows[rowIndex];
438
+ if ((row == null ? void 0 : row.type) === "GeometryCollection") {
439
+ flattened.push(...row.geometries);
440
+ setValidityBit(validity, rowIndex);
441
+ }
442
+ offsets[rowIndex + 1] = flattened.length;
443
+ }
444
+ return {
445
+ kind: "list",
446
+ length: rows.length,
447
+ offsets,
448
+ child: makeDenseUnionArray(flattened, options, dimension),
449
+ validity: { values: validity }
450
+ };
451
+ }
452
+ function makeCoordinateArray(coordinates, coordinateCount, dimension, layout) {
453
+ const size = getGeoArrowDimensionSize(dimension);
454
+ if (layout === "interleaved") {
455
+ const values = coordinates;
456
+ return {
457
+ kind: "fixed-size-list",
458
+ length: coordinateCount,
459
+ size,
460
+ child: { kind: "primitive", length: coordinateCount * size, values }
461
+ };
462
+ }
463
+ const separated = coordinates;
464
+ const children = {
465
+ x: { kind: "primitive", length: coordinateCount, values: separated.x },
466
+ y: { kind: "primitive", length: coordinateCount, values: separated.y }
467
+ };
468
+ if (separated.z) {
469
+ children["z"] = { kind: "primitive", length: coordinateCount, values: separated.z };
470
+ }
471
+ if (separated.m) {
472
+ children["m"] = { kind: "primitive", length: coordinateCount, values: separated.m };
473
+ }
474
+ return { kind: "struct", length: coordinateCount, children };
475
+ }
476
+ function makeSeparatedCoordinateTarget(FloatArray, count, dimension) {
477
+ return {
478
+ x: new FloatArray(count),
479
+ y: new FloatArray(count),
480
+ z: dimension === "xyz" || dimension === "xyzm" ? new FloatArray(count) : void 0,
481
+ m: dimension === "xym" || dimension === "xyzm" ? new FloatArray(count) : void 0
482
+ };
483
+ }
484
+ function writeCoordinate(target, coordinateIndex, coordinate, dimension) {
485
+ if (target instanceof Float32Array || target instanceof Float64Array) {
486
+ target.set(coordinate, coordinateIndex * coordinate.length);
487
+ return;
488
+ }
489
+ target.x[coordinateIndex] = coordinate[0];
490
+ target.y[coordinateIndex] = coordinate[1];
491
+ if (dimension === "xyz")
492
+ target.z[coordinateIndex] = coordinate[2];
493
+ else if (dimension === "xym")
494
+ target.m[coordinateIndex] = coordinate[2];
495
+ else if (dimension === "xyzm") {
496
+ target.z[coordinateIndex] = coordinate[2];
497
+ target.m[coordinateIndex] = coordinate[3];
498
+ }
499
+ }
500
+ function setValidityBit(validity, index) {
501
+ validity[index >> 3] |= 1 << (index & 7);
502
+ }
503
+ function getBuilderDepth(encoding) {
504
+ switch (encoding) {
505
+ case "geoarrow.point":
506
+ return 0;
507
+ case "geoarrow.linestring":
508
+ case "geoarrow.multipoint":
509
+ return 1;
510
+ case "geoarrow.polygon":
511
+ case "geoarrow.multilinestring":
512
+ return 2;
513
+ case "geoarrow.multipolygon":
514
+ return 3;
515
+ }
516
+ }
517
+ function getGeometryTypeId(type, dimension = "xy") {
518
+ const family = [
519
+ "Point",
520
+ "LineString",
521
+ "Polygon",
522
+ "MultiPoint",
523
+ "MultiLineString",
524
+ "MultiPolygon",
525
+ "GeometryCollection"
526
+ ].indexOf(type);
527
+ const dimensionIndex = ["xy", "xyz", "xym", "xyzm"].indexOf(dimension);
528
+ return family < 0 || dimensionIndex < 0 ? 1 : family * 4 + dimensionIndex + 1;
529
+ }
530
+ function inferRowsDimension(rows) {
531
+ let size = 2;
532
+ const visit = (value) => {
533
+ if (!Array.isArray(value) || value.length === 0)
534
+ return;
535
+ if (typeof value[0] === "number")
536
+ size = Math.max(size, value.length);
537
+ else
538
+ for (const child of value)
539
+ visit(child);
540
+ };
541
+ const visitGeometry = (geometry) => {
542
+ if (geometry.type === "GeometryCollection") {
543
+ for (const child of geometry.geometries)
544
+ visitGeometry(child);
545
+ } else {
546
+ visit(geometry.coordinates);
547
+ }
548
+ };
549
+ for (const row of rows) {
550
+ if (row)
551
+ visitGeometry(row);
552
+ }
553
+ return size >= 4 ? "xyzm" : size === 3 ? "xyz" : "xy";
554
+ }
555
+ function assertTargetCapacity(target, measurement, size) {
556
+ const coordinateLength = target.coordinates instanceof Float32Array || target.coordinates instanceof Float64Array ? target.coordinates.length / size : target.coordinates.x.length;
557
+ if (coordinateLength < measurement.coordinateCount || target.validity.length * 8 < measurement.length) {
558
+ throw new Error("GeoArrowBuilder target is smaller than its measured output");
559
+ }
560
+ }
561
+ //# sourceMappingURL=builder-entry.cjs.map