@math.gl/geoarrow 5.0.0-alpha.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.
- package/LICENSE +140 -0
- package/README.md +311 -0
- package/dist/builder.d.ts +90 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +428 -0
- package/dist/builder.js.map +1 -0
- package/dist/codecs.d.ts +10 -0
- package/dist/codecs.d.ts.map +1 -0
- package/dist/codecs.js +122 -0
- package/dist/codecs.js.map +1 -0
- package/dist/index.cjs +1651 -0
- package/dist/index.cjs.map +6 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/kernels.d.ts +51 -0
- package/dist/kernels.d.ts.map +1 -0
- package/dist/kernels.js +359 -0
- package/dist/kernels.js.map +1 -0
- package/dist/layout.d.ts +52 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +669 -0
- package/dist/layout.js.map +1 -0
- package/dist/tessellate.d.ts +31 -0
- package/dist/tessellate.d.ts.map +1 -0
- package/dist/tessellate.js +102 -0
- package/dist/tessellate.js.map +1 -0
- package/dist/types.d.ts +113 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +41 -0
- package/dist/types.js.map +1 -0
- package/dist/worker.cjs +73 -0
- package/dist/worker.cjs.map +6 -0
- package/dist/worker.d.ts +11 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +10 -0
- package/dist/worker.js.map +1 -0
- package/package.json +48 -0
- package/src/builder.ts +569 -0
- package/src/codecs.ts +155 -0
- package/src/index.ts +93 -0
- package/src/kernels.ts +463 -0
- package/src/layout.ts +833 -0
- package/src/tessellate.ts +133 -0
- package/src/types.ts +200 -0
- package/src/worker.ts +20 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1651 @@
|
|
|
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/index.js
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
GeoArrowBuilder: () => GeoArrowBuilder,
|
|
23
|
+
allocateGeoArrowBuilderTarget: () => allocateGeoArrowBuilderTarget,
|
|
24
|
+
assertGeoArrowResourceLimits: () => assertGeoArrowResourceLimits,
|
|
25
|
+
convertGeoArrowColumn: () => convertGeoArrowColumn,
|
|
26
|
+
decodeGeoArrowWKB: () => decodeGeoArrowWKB,
|
|
27
|
+
decodeGeoArrowWKT: () => decodeGeoArrowWKT,
|
|
28
|
+
encodeGeoArrowWKB: () => encodeGeoArrowWKB,
|
|
29
|
+
encodeGeoArrowWKT: () => encodeGeoArrowWKT,
|
|
30
|
+
getGeoArrowBounds: () => getGeoArrowBounds,
|
|
31
|
+
getGeoArrowDimensionSize: () => getGeoArrowDimensionSize,
|
|
32
|
+
getGeoArrowEncodingForGeometry: () => getGeoArrowEncodingForGeometry,
|
|
33
|
+
getGeoArrowGeometryType: () => getGeoArrowGeometryType,
|
|
34
|
+
getGeoArrowRowCount: () => getGeoArrowRowCount,
|
|
35
|
+
getGeoArrowTransferList: () => getGeoArrowTransferList,
|
|
36
|
+
getGeoArrowVertexCount: () => getGeoArrowVertexCount,
|
|
37
|
+
inspectGeoArrowColumn: () => inspectGeoArrowColumn,
|
|
38
|
+
interleaveGeoArrowCoordinates: () => interleaveGeoArrowCoordinates,
|
|
39
|
+
isGeoArrowValueValid: () => isGeoArrowValueValid,
|
|
40
|
+
makeGeoArrowColumnFromGeometryRows: () => makeGeoArrowColumnFromGeometryRows,
|
|
41
|
+
mapGeoArrowCoordinates: () => mapGeoArrowCoordinates,
|
|
42
|
+
mapGeoArrowCoordinatesInto: () => mapGeoArrowCoordinatesInto,
|
|
43
|
+
normalizeGeoArrowUnion: () => normalizeGeoArrowUnion,
|
|
44
|
+
rewindGeoArrow: () => rewindGeoArrow,
|
|
45
|
+
sliceGeoArrowArray: () => sliceGeoArrowArray,
|
|
46
|
+
sliceGeoArrowColumn: () => sliceGeoArrowColumn,
|
|
47
|
+
tessellateGeoArrowPolygons: () => tessellateGeoArrowPolygons,
|
|
48
|
+
validateGeoArrowColumn: () => validateGeoArrowColumn,
|
|
49
|
+
visitGeoArrowCoordinates: () => visitGeoArrowCoordinates
|
|
50
|
+
});
|
|
51
|
+
module.exports = __toCommonJS(index_exports);
|
|
52
|
+
|
|
53
|
+
// dist/types.js
|
|
54
|
+
function getGeoArrowDimensionSize(dimension) {
|
|
55
|
+
switch (dimension) {
|
|
56
|
+
case "xy":
|
|
57
|
+
return 2;
|
|
58
|
+
case "xyz":
|
|
59
|
+
case "xym":
|
|
60
|
+
return 3;
|
|
61
|
+
case "xyzm":
|
|
62
|
+
return 4;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function getGeoArrowGeometryType(encoding) {
|
|
66
|
+
switch (encoding) {
|
|
67
|
+
case "geoarrow.point":
|
|
68
|
+
return "Point";
|
|
69
|
+
case "geoarrow.linestring":
|
|
70
|
+
return "LineString";
|
|
71
|
+
case "geoarrow.polygon":
|
|
72
|
+
return "Polygon";
|
|
73
|
+
case "geoarrow.multipoint":
|
|
74
|
+
return "MultiPoint";
|
|
75
|
+
case "geoarrow.multilinestring":
|
|
76
|
+
return "MultiLineString";
|
|
77
|
+
case "geoarrow.multipolygon":
|
|
78
|
+
return "MultiPolygon";
|
|
79
|
+
case "geoarrow.geometrycollection":
|
|
80
|
+
return "GeometryCollection";
|
|
81
|
+
default:
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function getGeoArrowEncodingForGeometry(type) {
|
|
86
|
+
return `geoarrow.${type.toLowerCase()}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// dist/layout.js
|
|
90
|
+
function isGeoArrowValueValid(validity, index) {
|
|
91
|
+
if (!validity)
|
|
92
|
+
return true;
|
|
93
|
+
const bitIndex = (validity.bitOffset || 0) + index;
|
|
94
|
+
return Boolean(validity.values[bitIndex >> 3] & 1 << (bitIndex & 7));
|
|
95
|
+
}
|
|
96
|
+
function getGeoArrowRowCount(column) {
|
|
97
|
+
return column.chunks.reduce((count, chunk) => count + chunk.length, 0);
|
|
98
|
+
}
|
|
99
|
+
function visitGeoArrowCoordinates(column, visitor) {
|
|
100
|
+
let rowOffset = 0;
|
|
101
|
+
for (const chunk of column.chunks) {
|
|
102
|
+
visitChunkCoordinates(chunk, column.encoding, rowOffset, visitor);
|
|
103
|
+
rowOffset += chunk.length;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function sliceGeoArrowColumn(column, begin = 0, end = getGeoArrowRowCount(column)) {
|
|
107
|
+
const rowCount = getGeoArrowRowCount(column);
|
|
108
|
+
const first = normalizeSliceIndex(begin, rowCount);
|
|
109
|
+
const last = Math.max(first, normalizeSliceIndex(end, rowCount));
|
|
110
|
+
if (first === 0 && last === rowCount)
|
|
111
|
+
return column;
|
|
112
|
+
const chunks = [];
|
|
113
|
+
let chunkStart = 0;
|
|
114
|
+
for (const chunk of column.chunks) {
|
|
115
|
+
const chunkEnd = chunkStart + chunk.length;
|
|
116
|
+
const localFirst = Math.max(0, first - chunkStart);
|
|
117
|
+
const localLast = Math.min(chunk.length, last - chunkStart);
|
|
118
|
+
if (localLast > localFirst) {
|
|
119
|
+
chunks.push(sliceGeoArrowArray(chunk, localFirst, localLast));
|
|
120
|
+
}
|
|
121
|
+
chunkStart = chunkEnd;
|
|
122
|
+
if (chunkStart >= last)
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
return { ...column, chunks };
|
|
126
|
+
}
|
|
127
|
+
function sliceGeoArrowArray(array, begin, end) {
|
|
128
|
+
if (begin === 0 && end === array.length)
|
|
129
|
+
return array;
|
|
130
|
+
const length = end - begin;
|
|
131
|
+
const validity = array.validity ? { ...array.validity, bitOffset: (array.validity.bitOffset || 0) + begin } : void 0;
|
|
132
|
+
switch (array.kind) {
|
|
133
|
+
case "primitive":
|
|
134
|
+
return {
|
|
135
|
+
...array,
|
|
136
|
+
length,
|
|
137
|
+
offset: (array.offset || 0) + begin * (array.stride || 1),
|
|
138
|
+
validity
|
|
139
|
+
};
|
|
140
|
+
case "fixed-size-list":
|
|
141
|
+
return { ...array, length, offset: (array.offset || 0) + begin, validity };
|
|
142
|
+
case "list":
|
|
143
|
+
case "serialized":
|
|
144
|
+
return { ...array, length, offset: (array.offset || 0) + begin, validity };
|
|
145
|
+
case "struct":
|
|
146
|
+
return { ...array, length, offset: (array.offset || 0) + begin, validity };
|
|
147
|
+
case "dense-union":
|
|
148
|
+
return { ...array, length, offset: (array.offset || 0) + begin, validity };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function validateGeoArrowColumn(column) {
|
|
152
|
+
const issues = [];
|
|
153
|
+
for (let chunkIndex = 0; chunkIndex < column.chunks.length; chunkIndex++) {
|
|
154
|
+
validateArray(column.chunks[chunkIndex], `chunks[${chunkIndex}]`, issues);
|
|
155
|
+
}
|
|
156
|
+
validateColumnLayout(column, issues);
|
|
157
|
+
return { valid: issues.length === 0, issues };
|
|
158
|
+
}
|
|
159
|
+
function inspectGeoArrowColumn(column) {
|
|
160
|
+
const validation = validateGeoArrowColumn(column);
|
|
161
|
+
const storageKinds = [];
|
|
162
|
+
let nullCount = 0;
|
|
163
|
+
for (const chunk of column.chunks) {
|
|
164
|
+
collectStorageKinds(chunk, storageKinds);
|
|
165
|
+
for (let rowIndex = 0; rowIndex < chunk.length; rowIndex++) {
|
|
166
|
+
if (!isGeoArrowValueValid(chunk.validity, rowIndex))
|
|
167
|
+
nullCount++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
encoding: column.encoding,
|
|
172
|
+
rowCount: getGeoArrowRowCount(column),
|
|
173
|
+
chunkCount: column.chunks.length,
|
|
174
|
+
nullCount,
|
|
175
|
+
coordinateCount: getGeoArrowVertexCount(column),
|
|
176
|
+
storageKinds,
|
|
177
|
+
valid: validation.valid,
|
|
178
|
+
issues: validation.issues
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function getGeoArrowVertexCount(column) {
|
|
182
|
+
if (column.encoding === "geoarrow.wkb" || column.encoding === "geoarrow.wkt" || column.encoding === "geoarrow.box") {
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
let count = 0;
|
|
186
|
+
visitGeoArrowCoordinates(column, (coordinate) => {
|
|
187
|
+
count++;
|
|
188
|
+
return coordinate;
|
|
189
|
+
});
|
|
190
|
+
return count;
|
|
191
|
+
}
|
|
192
|
+
function getGeoArrowTransferList(column) {
|
|
193
|
+
const buffers = /* @__PURE__ */ new Set();
|
|
194
|
+
for (const chunk of column.chunks)
|
|
195
|
+
collectArrayBuffers(chunk, buffers);
|
|
196
|
+
return [...buffers];
|
|
197
|
+
}
|
|
198
|
+
function materializeGeoArrowRows(column) {
|
|
199
|
+
if (column.encoding === "geoarrow.wkb" || column.encoding === "geoarrow.wkt") {
|
|
200
|
+
throw new Error("Serialized GeoArrow columns must be decoded before materialization");
|
|
201
|
+
}
|
|
202
|
+
const rows = [];
|
|
203
|
+
for (const chunk of column.chunks) {
|
|
204
|
+
for (let rowIndex = 0; rowIndex < chunk.length; rowIndex++) {
|
|
205
|
+
rows.push(materializeGeometryRow(chunk, rowIndex, column.encoding));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return rows;
|
|
209
|
+
}
|
|
210
|
+
function materializeGeometryRow(array, rowIndex, encoding) {
|
|
211
|
+
if (!isGeoArrowValueValid(array.validity, rowIndex))
|
|
212
|
+
return null;
|
|
213
|
+
if (encoding === "geoarrow.geometry") {
|
|
214
|
+
if (array.kind !== "dense-union")
|
|
215
|
+
return null;
|
|
216
|
+
return materializeUnionRow(array, rowIndex);
|
|
217
|
+
}
|
|
218
|
+
if (encoding === "geoarrow.geometrycollection") {
|
|
219
|
+
if (array.kind !== "list")
|
|
220
|
+
return null;
|
|
221
|
+
const [first, last] = getListRange(array, rowIndex);
|
|
222
|
+
const geometries = [];
|
|
223
|
+
for (let index = first; index < last; index++) {
|
|
224
|
+
if (array.child.kind !== "dense-union")
|
|
225
|
+
continue;
|
|
226
|
+
const geometry = materializeUnionRow(array.child, index);
|
|
227
|
+
if (geometry)
|
|
228
|
+
geometries.push(geometry);
|
|
229
|
+
}
|
|
230
|
+
return { type: "GeometryCollection", geometries };
|
|
231
|
+
}
|
|
232
|
+
const geometryType = getGeoArrowGeometryType(encoding);
|
|
233
|
+
if (!geometryType || geometryType === "GeometryCollection")
|
|
234
|
+
return null;
|
|
235
|
+
const depth = getEncodingDepth(encoding);
|
|
236
|
+
const coordinates = readNestedCoordinates(array, rowIndex, depth);
|
|
237
|
+
if (!coordinates)
|
|
238
|
+
return null;
|
|
239
|
+
return { type: geometryType, coordinates };
|
|
240
|
+
}
|
|
241
|
+
function getGeoArrowOffset(offsets, index) {
|
|
242
|
+
const value = offsets[index];
|
|
243
|
+
const numericValue = typeof value === "bigint" ? Number(value) : value;
|
|
244
|
+
if (!Number.isSafeInteger(numericValue)) {
|
|
245
|
+
throw new Error(`GeoArrow offset ${String(value)} exceeds JavaScript's safe integer range`);
|
|
246
|
+
}
|
|
247
|
+
return numericValue;
|
|
248
|
+
}
|
|
249
|
+
function getListRange(list, rowIndex) {
|
|
250
|
+
const offsetIndex = (list.offset || 0) + rowIndex;
|
|
251
|
+
const baseValue = list.offsetBase ?? 0;
|
|
252
|
+
const base = typeof baseValue === "bigint" ? Number(baseValue) : baseValue;
|
|
253
|
+
return [
|
|
254
|
+
getGeoArrowOffset(list.offsets, offsetIndex) - base,
|
|
255
|
+
getGeoArrowOffset(list.offsets, offsetIndex + 1) - base
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
function visitChunkCoordinates(array, encoding, rowOffset, visitor) {
|
|
259
|
+
for (let rowIndex = 0; rowIndex < array.length; rowIndex++) {
|
|
260
|
+
if (!isGeoArrowValueValid(array.validity, rowIndex))
|
|
261
|
+
continue;
|
|
262
|
+
if (encoding === "geoarrow.geometry" && array.kind === "dense-union") {
|
|
263
|
+
visitUnionRowCoordinates(array, rowIndex, rowOffset + rowIndex, visitor);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (encoding === "geoarrow.geometrycollection" && array.kind === "list") {
|
|
267
|
+
const [first, last] = getListRange(array, rowIndex);
|
|
268
|
+
if (array.child.kind === "dense-union") {
|
|
269
|
+
for (let index = first; index < last; index++) {
|
|
270
|
+
visitUnionRowCoordinates(array.child, index, rowOffset + rowIndex, visitor);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
const depth = getEncodingDepth(encoding);
|
|
276
|
+
visitNestedCoordinates(array, rowIndex, depth, rowOffset + rowIndex, visitor);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function visitUnionRowCoordinates(union, rowIndex, sourceRowIndex, visitor) {
|
|
280
|
+
if (!isGeoArrowValueValid(union.validity, rowIndex))
|
|
281
|
+
return;
|
|
282
|
+
const physicalIndex = (union.offset || 0) + rowIndex;
|
|
283
|
+
const typeId = union.typeIds[physicalIndex];
|
|
284
|
+
const valueOffset = union.valueOffsets[physicalIndex];
|
|
285
|
+
const child = union.children.find((candidate) => candidate.typeId === typeId);
|
|
286
|
+
if (!child)
|
|
287
|
+
return;
|
|
288
|
+
const encoding = getEncodingFromChildName(child.name);
|
|
289
|
+
if (encoding === "geoarrow.geometrycollection" && child.data.kind === "list") {
|
|
290
|
+
const [first, last] = getListRange(child.data, valueOffset);
|
|
291
|
+
if (child.data.child.kind === "dense-union") {
|
|
292
|
+
for (let index = first; index < last; index++) {
|
|
293
|
+
visitUnionRowCoordinates(child.data.child, index, sourceRowIndex, visitor);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
visitNestedCoordinates(child.data, valueOffset, getEncodingDepth(encoding), sourceRowIndex, visitor);
|
|
299
|
+
}
|
|
300
|
+
function visitNestedCoordinates(array, index, depth, rowIndex, visitor) {
|
|
301
|
+
if (depth === 0) {
|
|
302
|
+
const coordinate = readCoordinate(array, index);
|
|
303
|
+
if (coordinate)
|
|
304
|
+
visitor(coordinate, rowIndex);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (array.kind !== "list" || !isGeoArrowValueValid(array.validity, index))
|
|
308
|
+
return;
|
|
309
|
+
const [first, last] = getListRange(array, index);
|
|
310
|
+
for (let childIndex = first; childIndex < last; childIndex++) {
|
|
311
|
+
visitNestedCoordinates(array.child, childIndex, depth - 1, rowIndex, visitor);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function readNestedCoordinates(array, index, depth) {
|
|
315
|
+
if (depth === 0)
|
|
316
|
+
return readCoordinate(array, index);
|
|
317
|
+
if (array.kind !== "list" || !isGeoArrowValueValid(array.validity, index))
|
|
318
|
+
return null;
|
|
319
|
+
const [first, last] = getListRange(array, index);
|
|
320
|
+
const values = [];
|
|
321
|
+
for (let childIndex = first; childIndex < last; childIndex++) {
|
|
322
|
+
const value = readNestedCoordinates(array.child, childIndex, depth - 1);
|
|
323
|
+
if (value !== null)
|
|
324
|
+
values.push(value);
|
|
325
|
+
}
|
|
326
|
+
return values;
|
|
327
|
+
}
|
|
328
|
+
function readCoordinate(array, index) {
|
|
329
|
+
if (!isGeoArrowValueValid(array.validity, index))
|
|
330
|
+
return null;
|
|
331
|
+
if (array.kind === "fixed-size-list") {
|
|
332
|
+
const listIndex = (array.offset || 0) + index;
|
|
333
|
+
const coordinate = [];
|
|
334
|
+
for (let component = 0; component < array.size; component++) {
|
|
335
|
+
coordinate.push(readPrimitive(array.child, listIndex * array.size + component));
|
|
336
|
+
}
|
|
337
|
+
return coordinate;
|
|
338
|
+
}
|
|
339
|
+
if (array.kind === "struct") {
|
|
340
|
+
const structIndex = (array.offset || 0) + index;
|
|
341
|
+
const coordinate = [];
|
|
342
|
+
for (const name of ["x", "y", "z", "m"]) {
|
|
343
|
+
const child = array.children[name];
|
|
344
|
+
if (child)
|
|
345
|
+
coordinate.push(readPrimitive(child, structIndex));
|
|
346
|
+
}
|
|
347
|
+
return coordinate.length >= 2 ? coordinate : null;
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
function readPrimitive(array, index) {
|
|
352
|
+
if (array.kind !== "primitive")
|
|
353
|
+
throw new Error("GeoArrow coordinate child must be primitive");
|
|
354
|
+
const valueIndex = (array.offset || 0) + index * (array.stride || 1);
|
|
355
|
+
return Number(array.values[valueIndex]);
|
|
356
|
+
}
|
|
357
|
+
function materializeUnionRow(union, rowIndex) {
|
|
358
|
+
if (!isGeoArrowValueValid(union.validity, rowIndex))
|
|
359
|
+
return null;
|
|
360
|
+
const physicalIndex = (union.offset || 0) + rowIndex;
|
|
361
|
+
const typeId = union.typeIds[physicalIndex];
|
|
362
|
+
const valueOffset = union.valueOffsets[physicalIndex];
|
|
363
|
+
const child = union.children.find((candidate) => candidate.typeId === typeId);
|
|
364
|
+
if (!child || valueOffset < 0 || valueOffset >= child.data.length)
|
|
365
|
+
return null;
|
|
366
|
+
return materializeGeometryRow(child.data, valueOffset, getEncodingFromChildName(child.name));
|
|
367
|
+
}
|
|
368
|
+
function getEncodingFromChildName(name) {
|
|
369
|
+
const normalized = name.replace(/[^a-z]/gi, "").toLowerCase();
|
|
370
|
+
switch (normalized) {
|
|
371
|
+
case "point":
|
|
372
|
+
return "geoarrow.point";
|
|
373
|
+
case "linestring":
|
|
374
|
+
return "geoarrow.linestring";
|
|
375
|
+
case "polygon":
|
|
376
|
+
return "geoarrow.polygon";
|
|
377
|
+
case "multipoint":
|
|
378
|
+
return "geoarrow.multipoint";
|
|
379
|
+
case "multilinestring":
|
|
380
|
+
return "geoarrow.multilinestring";
|
|
381
|
+
case "multipolygon":
|
|
382
|
+
return "geoarrow.multipolygon";
|
|
383
|
+
case "geometrycollection":
|
|
384
|
+
return "geoarrow.geometrycollection";
|
|
385
|
+
default:
|
|
386
|
+
throw new Error(`Unknown GeoArrow dense-union child ${name}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function getEncodingDepth(encoding) {
|
|
390
|
+
switch (encoding) {
|
|
391
|
+
case "geoarrow.point":
|
|
392
|
+
return 0;
|
|
393
|
+
case "geoarrow.linestring":
|
|
394
|
+
case "geoarrow.multipoint":
|
|
395
|
+
return 1;
|
|
396
|
+
case "geoarrow.polygon":
|
|
397
|
+
case "geoarrow.multilinestring":
|
|
398
|
+
return 2;
|
|
399
|
+
case "geoarrow.multipolygon":
|
|
400
|
+
return 3;
|
|
401
|
+
default:
|
|
402
|
+
return 0;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function validateArray(array, path, issues) {
|
|
406
|
+
if (!Number.isSafeInteger(array.length) || array.length < 0) {
|
|
407
|
+
issues.push({
|
|
408
|
+
code: "invalid-length",
|
|
409
|
+
path,
|
|
410
|
+
message: "Length must be a non-negative safe integer."
|
|
411
|
+
});
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
validateValidity(array.validity, array.length, path, issues);
|
|
415
|
+
switch (array.kind) {
|
|
416
|
+
case "primitive": {
|
|
417
|
+
const stride = array.stride || 1;
|
|
418
|
+
const offset = array.offset || 0;
|
|
419
|
+
if (!Number.isSafeInteger(stride) || stride < 1) {
|
|
420
|
+
issues.push({ code: "invalid-stride", path, message: "Primitive stride must be positive." });
|
|
421
|
+
}
|
|
422
|
+
const requiredLength = array.length === 0 ? offset : offset + (array.length - 1) * stride + 1;
|
|
423
|
+
if (offset < 0 || requiredLength > array.values.length) {
|
|
424
|
+
issues.push({
|
|
425
|
+
code: "invalid-length",
|
|
426
|
+
path,
|
|
427
|
+
message: "Primitive values do not cover the logical range."
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
case "fixed-size-list": {
|
|
433
|
+
const offset = array.offset ?? 0;
|
|
434
|
+
const validOffset = Number.isSafeInteger(offset) && offset >= 0;
|
|
435
|
+
if (!validOffset) {
|
|
436
|
+
issues.push({
|
|
437
|
+
code: "invalid-offset",
|
|
438
|
+
path,
|
|
439
|
+
message: "Fixed-size list offset must be a non-negative safe integer."
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
if (!Number.isSafeInteger(array.size) || array.size < 1) {
|
|
443
|
+
issues.push({
|
|
444
|
+
code: "invalid-length",
|
|
445
|
+
path,
|
|
446
|
+
message: "Fixed-size list size must be positive."
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
const childEnd = (offset + array.length) * array.size;
|
|
450
|
+
if (validOffset && childEnd > array.child.length) {
|
|
451
|
+
issues.push({ code: "invalid-child", path, message: "Fixed-size list child is too short." });
|
|
452
|
+
}
|
|
453
|
+
validateArray(array.child, `${path}.child`, issues);
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
case "list":
|
|
457
|
+
validateList(array, path, issues);
|
|
458
|
+
validateArray(array.child, `${path}.child`, issues);
|
|
459
|
+
break;
|
|
460
|
+
case "struct":
|
|
461
|
+
if (Object.keys(array.children).length === 0) {
|
|
462
|
+
issues.push({ code: "invalid-child", path, message: "Struct must contain children." });
|
|
463
|
+
}
|
|
464
|
+
for (const [name, child] of Object.entries(array.children)) {
|
|
465
|
+
if ((array.offset || 0) + array.length > child.length) {
|
|
466
|
+
issues.push({
|
|
467
|
+
code: "invalid-child",
|
|
468
|
+
path: `${path}.${name}`,
|
|
469
|
+
message: "Struct child is too short."
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
validateArray(child, `${path}.${name}`, issues);
|
|
473
|
+
}
|
|
474
|
+
break;
|
|
475
|
+
case "dense-union": {
|
|
476
|
+
const end = (array.offset || 0) + array.length;
|
|
477
|
+
if (end > array.typeIds.length || end > array.valueOffsets.length) {
|
|
478
|
+
issues.push({
|
|
479
|
+
code: "invalid-union",
|
|
480
|
+
path,
|
|
481
|
+
message: "Dense-union dispatch buffers are too short."
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
const childIds = /* @__PURE__ */ new Set();
|
|
485
|
+
for (const child of array.children) {
|
|
486
|
+
if (childIds.has(child.typeId)) {
|
|
487
|
+
issues.push({
|
|
488
|
+
code: "invalid-union",
|
|
489
|
+
path,
|
|
490
|
+
message: `Duplicate dense-union type ID ${child.typeId}.`
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
childIds.add(child.typeId);
|
|
494
|
+
validateArray(child.data, `${path}.${child.name}`, issues);
|
|
495
|
+
}
|
|
496
|
+
for (let index = array.offset || 0; index < end; index++) {
|
|
497
|
+
if (!isGeoArrowValueValid(array.validity, index - (array.offset || 0)))
|
|
498
|
+
continue;
|
|
499
|
+
const child = array.children.find((candidate) => candidate.typeId === array.typeIds[index]);
|
|
500
|
+
const valueOffset = array.valueOffsets[index];
|
|
501
|
+
if (!child || valueOffset < 0 || valueOffset >= child.data.length) {
|
|
502
|
+
issues.push({
|
|
503
|
+
code: "invalid-union",
|
|
504
|
+
path: `${path}[${index}]`,
|
|
505
|
+
message: "Dense-union row references an invalid child value."
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
case "serialized": {
|
|
512
|
+
validateOffsets(array.offsets, array.offset || 0, array.length, array.values.length, array.offsetBase, path, issues);
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function validateList(list, path, issues) {
|
|
518
|
+
validateOffsets(list.offsets, list.offset || 0, list.length, list.child.length, list.offsetBase, path, issues);
|
|
519
|
+
}
|
|
520
|
+
function validateOffsets(offsets, offset, length, childLength, offsetBase, path, issues) {
|
|
521
|
+
if (offset < 0 || offset + length >= offsets.length) {
|
|
522
|
+
issues.push({ code: "invalid-offset", path, message: "Offset buffer is too short." });
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const base = typeof offsetBase === "bigint" ? Number(offsetBase) : offsetBase || 0;
|
|
526
|
+
let previous;
|
|
527
|
+
try {
|
|
528
|
+
previous = getGeoArrowOffset(offsets, offset) - base;
|
|
529
|
+
} catch (error) {
|
|
530
|
+
issues.push({ code: "unsafe-offset", path, message: error.message });
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
for (let index = 1; index <= length; index++) {
|
|
534
|
+
let current;
|
|
535
|
+
try {
|
|
536
|
+
current = getGeoArrowOffset(offsets, offset + index) - base;
|
|
537
|
+
} catch (error) {
|
|
538
|
+
issues.push({ code: "unsafe-offset", path, message: error.message });
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
if (current < previous || current < 0 || current > childLength) {
|
|
542
|
+
issues.push({
|
|
543
|
+
code: "invalid-offset",
|
|
544
|
+
path,
|
|
545
|
+
message: "Offsets must be monotonic and within child storage."
|
|
546
|
+
});
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
previous = current;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function validateValidity(validity, length, path, issues) {
|
|
553
|
+
if (!validity)
|
|
554
|
+
return;
|
|
555
|
+
const bitOffset = validity.bitOffset || 0;
|
|
556
|
+
if (bitOffset < 0 || bitOffset + length > validity.values.length * 8) {
|
|
557
|
+
issues.push({ code: "invalid-validity", path, message: "Validity bitmap is too short." });
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function validateColumnLayout(column, issues) {
|
|
561
|
+
var _a;
|
|
562
|
+
if (column.encoding === "geoarrow.wkb" || column.encoding === "geoarrow.wkt") {
|
|
563
|
+
for (const [index, chunk] of column.chunks.entries()) {
|
|
564
|
+
const expectedEncoding = column.encoding === "geoarrow.wkb" ? "binary" : "utf8";
|
|
565
|
+
if (chunk.kind !== "serialized" || chunk.encoding !== expectedEncoding) {
|
|
566
|
+
issues.push({
|
|
567
|
+
code: "invalid-layout",
|
|
568
|
+
path: `chunks[${index}]`,
|
|
569
|
+
message: `Serialized encoding requires ${expectedEncoding} storage.`
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (column.encoding === "geoarrow.box") {
|
|
576
|
+
for (const [index, chunk] of column.chunks.entries()) {
|
|
577
|
+
if (chunk.kind !== "struct") {
|
|
578
|
+
addInvalidLayout(`chunks[${index}]`, "Box encoding requires struct storage.", issues);
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
const requiredNames = column.dimension === "xy" ? ["xmin", "ymin", "xmax", "ymax"] : column.dimension === "xyz" ? ["xmin", "ymin", "zmin", "xmax", "ymax", "zmax"] : column.dimension === "xym" ? ["xmin", "ymin", "mmin", "xmax", "ymax", "mmax"] : ["xmin", "ymin", "zmin", "mmin", "xmax", "ymax", "zmax", "mmax"];
|
|
582
|
+
for (const name of requiredNames) {
|
|
583
|
+
if (((_a = chunk.children[name]) == null ? void 0 : _a.kind) !== "primitive") {
|
|
584
|
+
addInvalidLayout(`chunks[${index}].${name}`, `Box storage requires primitive ${name}.`, issues);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (!column.coordinateLayout) {
|
|
591
|
+
addInvalidLayout("coordinateLayout", "Native geometry requires a coordinate layout.", issues);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
for (const [index, chunk] of column.chunks.entries()) {
|
|
595
|
+
validateGeometryLayout(chunk, column.encoding, column, `chunks[${index}]`, issues);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
function validateGeometryLayout(array, encoding, column, path, issues) {
|
|
599
|
+
if (encoding === "geoarrow.geometry") {
|
|
600
|
+
if (array.kind !== "dense-union") {
|
|
601
|
+
addInvalidLayout(path, "Mixed geometry encoding requires dense-union storage.", issues);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
validateUnionLayouts(array, column, path, issues);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (encoding === "geoarrow.geometrycollection") {
|
|
608
|
+
if (array.kind !== "list" || array.child.kind !== "dense-union") {
|
|
609
|
+
addInvalidLayout(path, "GeometryCollection requires list-of-dense-union storage.", issues);
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
validateUnionLayouts(array.child, column, `${path}.child`, issues);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
let coordinateArray = array;
|
|
616
|
+
const depth = getEncodingDepth(encoding);
|
|
617
|
+
for (let level = 0; level < depth; level++) {
|
|
618
|
+
if (coordinateArray.kind !== "list") {
|
|
619
|
+
addInvalidLayout(path, `${encoding} requires ${depth} variable-list levels.`, issues);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
coordinateArray = coordinateArray.child;
|
|
623
|
+
}
|
|
624
|
+
validateCoordinateLayout(coordinateArray, column, path, issues);
|
|
625
|
+
}
|
|
626
|
+
function validateUnionLayouts(union, column, path, issues) {
|
|
627
|
+
for (const child of union.children) {
|
|
628
|
+
let encoding;
|
|
629
|
+
try {
|
|
630
|
+
encoding = getEncodingFromChildName(child.name);
|
|
631
|
+
} catch (error) {
|
|
632
|
+
addInvalidLayout(`${path}.${child.name}`, error.message, issues);
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
validateGeometryLayout(child.data, encoding, column, `${path}.${child.name}`, issues);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function validateCoordinateLayout(array, column, path, issues) {
|
|
639
|
+
var _a;
|
|
640
|
+
const dimensionSize = getGeoArrowDimensionSize(column.dimension);
|
|
641
|
+
if (column.coordinateLayout === "interleaved") {
|
|
642
|
+
if (array.kind !== "fixed-size-list" || array.size !== dimensionSize || array.child.kind !== "primitive") {
|
|
643
|
+
addInvalidLayout(path, `Interleaved ${column.dimension} coordinates require fixed-size-list<${dimensionSize}> primitive storage.`, issues);
|
|
644
|
+
}
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
if (array.kind !== "struct") {
|
|
648
|
+
addInvalidLayout(path, `Separated ${column.dimension} coordinates require struct storage.`, issues);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const requiredNames = column.dimension === "xy" ? ["x", "y"] : column.dimension === "xyz" ? ["x", "y", "z"] : column.dimension === "xym" ? ["x", "y", "m"] : ["x", "y", "z", "m"];
|
|
652
|
+
for (const name of requiredNames) {
|
|
653
|
+
if (((_a = array.children[name]) == null ? void 0 : _a.kind) !== "primitive") {
|
|
654
|
+
addInvalidLayout(`${path}.${name}`, `Separated coordinates require primitive ${name}.`, issues);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
function addInvalidLayout(path, message, issues) {
|
|
659
|
+
issues.push({ code: "invalid-layout", path, message });
|
|
660
|
+
}
|
|
661
|
+
function collectStorageKinds(array, kinds) {
|
|
662
|
+
kinds.push(array.kind);
|
|
663
|
+
switch (array.kind) {
|
|
664
|
+
case "fixed-size-list":
|
|
665
|
+
case "list":
|
|
666
|
+
collectStorageKinds(array.child, kinds);
|
|
667
|
+
break;
|
|
668
|
+
case "struct":
|
|
669
|
+
for (const child of Object.values(array.children))
|
|
670
|
+
collectStorageKinds(child, kinds);
|
|
671
|
+
break;
|
|
672
|
+
case "dense-union":
|
|
673
|
+
for (const child of array.children)
|
|
674
|
+
collectStorageKinds(child.data, kinds);
|
|
675
|
+
break;
|
|
676
|
+
default:
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function collectArrayBuffers(array, buffers) {
|
|
681
|
+
if (array.validity)
|
|
682
|
+
addBuffer(array.validity.values.buffer, buffers);
|
|
683
|
+
switch (array.kind) {
|
|
684
|
+
case "primitive":
|
|
685
|
+
addBuffer(array.values.buffer, buffers);
|
|
686
|
+
break;
|
|
687
|
+
case "fixed-size-list":
|
|
688
|
+
collectArrayBuffers(array.child, buffers);
|
|
689
|
+
break;
|
|
690
|
+
case "list":
|
|
691
|
+
addBuffer(array.offsets.buffer, buffers);
|
|
692
|
+
collectArrayBuffers(array.child, buffers);
|
|
693
|
+
break;
|
|
694
|
+
case "struct":
|
|
695
|
+
for (const child of Object.values(array.children))
|
|
696
|
+
collectArrayBuffers(child, buffers);
|
|
697
|
+
break;
|
|
698
|
+
case "dense-union":
|
|
699
|
+
addBuffer(array.typeIds.buffer, buffers);
|
|
700
|
+
addBuffer(array.valueOffsets.buffer, buffers);
|
|
701
|
+
for (const child of array.children)
|
|
702
|
+
collectArrayBuffers(child.data, buffers);
|
|
703
|
+
break;
|
|
704
|
+
case "serialized":
|
|
705
|
+
addBuffer(array.offsets.buffer, buffers);
|
|
706
|
+
addBuffer(array.values.buffer, buffers);
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
function addBuffer(buffer, buffers) {
|
|
711
|
+
if (buffer instanceof ArrayBuffer)
|
|
712
|
+
buffers.add(buffer);
|
|
713
|
+
}
|
|
714
|
+
function normalizeSliceIndex(index, length) {
|
|
715
|
+
const normalized = index < 0 ? Math.max(0, length + index) : Math.min(length, index);
|
|
716
|
+
return Math.max(0, Math.trunc(normalized));
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// dist/builder.js
|
|
720
|
+
var GeoArrowBuilder = class _GeoArrowBuilder {
|
|
721
|
+
constructor(options) {
|
|
722
|
+
this.length = 0;
|
|
723
|
+
this.nullCount = 0;
|
|
724
|
+
this.coordinateCount = 0;
|
|
725
|
+
this.partCount = 0;
|
|
726
|
+
this.ringCount = 0;
|
|
727
|
+
this.mode = options.mode;
|
|
728
|
+
this.encoding = options.encoding;
|
|
729
|
+
this.dimension = options.dimension || "xy";
|
|
730
|
+
this.coordinateLayout = options.coordinateLayout || "interleaved";
|
|
731
|
+
this.offsetType = options.offsetType || "int32";
|
|
732
|
+
this.coordinateType = options.coordinateType || "float64";
|
|
733
|
+
this.target = options.mode === "write" ? options.target : void 0;
|
|
734
|
+
if (this.target)
|
|
735
|
+
this.initializeTargetOffsets(this.target);
|
|
736
|
+
}
|
|
737
|
+
/** Appends one geometry row or null. */
|
|
738
|
+
append(geometry) {
|
|
739
|
+
var _a, _b, _c, _d, _e, _f;
|
|
740
|
+
const rowIndex = this.length++;
|
|
741
|
+
if (!geometry) {
|
|
742
|
+
this.nullCount++;
|
|
743
|
+
this.appendNull(rowIndex);
|
|
744
|
+
return this;
|
|
745
|
+
}
|
|
746
|
+
const expectedType = getGeoArrowGeometryType(this.encoding);
|
|
747
|
+
if (geometry.type !== expectedType) {
|
|
748
|
+
throw new Error(`GeoArrowBuilder for ${this.encoding} cannot append ${geometry.type}`);
|
|
749
|
+
}
|
|
750
|
+
if (geometry.type === "GeometryCollection") {
|
|
751
|
+
throw new Error("GeoArrowBuilder only accepts concrete geometry families");
|
|
752
|
+
}
|
|
753
|
+
if (this.target)
|
|
754
|
+
setValidityBit(this.target.validity, rowIndex);
|
|
755
|
+
const depth = getBuilderDepth(this.encoding);
|
|
756
|
+
const coordinates = geometry.coordinates;
|
|
757
|
+
if (depth === 0) {
|
|
758
|
+
this.writeCoordinate(coordinates);
|
|
759
|
+
} else if (depth === 1) {
|
|
760
|
+
this.writeCoordinateList(coordinates);
|
|
761
|
+
this.writeOffset((_a = this.target) == null ? void 0 : _a.geometryOffsets, rowIndex + 1, this.coordinateCount);
|
|
762
|
+
} else if (depth === 2) {
|
|
763
|
+
for (const part of coordinates) {
|
|
764
|
+
this.writeCoordinateList(part);
|
|
765
|
+
this.partCount++;
|
|
766
|
+
this.writeOffset((_b = this.target) == null ? void 0 : _b.partOffsets, this.partCount, this.coordinateCount);
|
|
767
|
+
}
|
|
768
|
+
this.writeOffset((_c = this.target) == null ? void 0 : _c.geometryOffsets, rowIndex + 1, this.partCount);
|
|
769
|
+
} else {
|
|
770
|
+
for (const polygon of coordinates) {
|
|
771
|
+
for (const ring of polygon) {
|
|
772
|
+
this.writeCoordinateList(ring);
|
|
773
|
+
this.ringCount++;
|
|
774
|
+
this.writeOffset((_d = this.target) == null ? void 0 : _d.ringOffsets, this.ringCount, this.coordinateCount);
|
|
775
|
+
}
|
|
776
|
+
this.partCount++;
|
|
777
|
+
this.writeOffset((_e = this.target) == null ? void 0 : _e.partOffsets, this.partCount, this.ringCount);
|
|
778
|
+
}
|
|
779
|
+
this.writeOffset((_f = this.target) == null ? void 0 : _f.geometryOffsets, rowIndex + 1, this.partCount);
|
|
780
|
+
}
|
|
781
|
+
return this;
|
|
782
|
+
}
|
|
783
|
+
/** Returns current exact allocation counts. */
|
|
784
|
+
getMeasurement() {
|
|
785
|
+
const depth = getBuilderDepth(this.encoding);
|
|
786
|
+
return {
|
|
787
|
+
length: this.length,
|
|
788
|
+
nullCount: this.nullCount,
|
|
789
|
+
coordinateCount: this.coordinateCount,
|
|
790
|
+
geometryOffsetCount: depth >= 1 ? this.length + 1 : 0,
|
|
791
|
+
partOffsetCount: depth >= 2 ? this.partCount + 1 : 0,
|
|
792
|
+
ringOffsetCount: depth >= 3 ? this.ringCount + 1 : 0
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
/** Allocates a write target from the current measure pass. */
|
|
796
|
+
allocateTarget() {
|
|
797
|
+
if (this.mode !== "measure")
|
|
798
|
+
throw new Error("Only a measure builder can allocate a target");
|
|
799
|
+
return allocateGeoArrowBuilderTarget(this.getMeasurement(), {
|
|
800
|
+
encoding: this.encoding,
|
|
801
|
+
dimension: this.dimension,
|
|
802
|
+
coordinateLayout: this.coordinateLayout,
|
|
803
|
+
offsetType: this.offsetType,
|
|
804
|
+
coordinateType: this.coordinateType
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
/** Finishes a write pass and returns a one-chunk borrowed column. */
|
|
808
|
+
finish() {
|
|
809
|
+
if (!this.target)
|
|
810
|
+
throw new Error("A measure builder has no finished column");
|
|
811
|
+
const measurement = this.getMeasurement();
|
|
812
|
+
assertTargetCapacity(this.target, measurement, getGeoArrowDimensionSize(this.dimension));
|
|
813
|
+
const coordinates = makeCoordinateArray(this.target.coordinates, measurement.coordinateCount, this.dimension, this.coordinateLayout);
|
|
814
|
+
const depth = getBuilderDepth(this.encoding);
|
|
815
|
+
let chunk = coordinates;
|
|
816
|
+
if (depth >= 3) {
|
|
817
|
+
chunk = {
|
|
818
|
+
kind: "list",
|
|
819
|
+
length: this.ringCount,
|
|
820
|
+
offsets: this.target.ringOffsets,
|
|
821
|
+
child: chunk
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
if (depth >= 2) {
|
|
825
|
+
chunk = {
|
|
826
|
+
kind: "list",
|
|
827
|
+
length: this.partCount,
|
|
828
|
+
offsets: this.target.partOffsets,
|
|
829
|
+
child: chunk
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
if (depth >= 1) {
|
|
833
|
+
chunk = {
|
|
834
|
+
kind: "list",
|
|
835
|
+
length: this.length,
|
|
836
|
+
offsets: this.target.geometryOffsets,
|
|
837
|
+
child: chunk,
|
|
838
|
+
validity: { values: this.target.validity }
|
|
839
|
+
};
|
|
840
|
+
} else {
|
|
841
|
+
chunk = { ...chunk, validity: { values: this.target.validity } };
|
|
842
|
+
}
|
|
843
|
+
return {
|
|
844
|
+
encoding: this.encoding,
|
|
845
|
+
dimension: this.dimension,
|
|
846
|
+
coordinateLayout: this.coordinateLayout,
|
|
847
|
+
chunks: [chunk]
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
/** Builds a homogeneous column using an internal measure/write pair. */
|
|
851
|
+
static build(rows, options) {
|
|
852
|
+
const measure = new _GeoArrowBuilder({ ...options, mode: "measure" });
|
|
853
|
+
for (const row of rows)
|
|
854
|
+
measure.append(row);
|
|
855
|
+
const write = new _GeoArrowBuilder({
|
|
856
|
+
...options,
|
|
857
|
+
mode: "write",
|
|
858
|
+
target: measure.allocateTarget()
|
|
859
|
+
});
|
|
860
|
+
for (const row of rows)
|
|
861
|
+
write.append(row);
|
|
862
|
+
return write.finish();
|
|
863
|
+
}
|
|
864
|
+
appendNull(rowIndex) {
|
|
865
|
+
var _a;
|
|
866
|
+
const depth = getBuilderDepth(this.encoding);
|
|
867
|
+
if (depth === 0) {
|
|
868
|
+
this.writeCoordinate(new Array(getGeoArrowDimensionSize(this.dimension)).fill(0));
|
|
869
|
+
} else {
|
|
870
|
+
this.writeOffset((_a = this.target) == null ? void 0 : _a.geometryOffsets, rowIndex + 1, depth === 1 ? this.coordinateCount : this.partCount);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
writeCoordinateList(coordinates) {
|
|
874
|
+
for (const coordinate of coordinates)
|
|
875
|
+
this.writeCoordinate(coordinate);
|
|
876
|
+
}
|
|
877
|
+
writeCoordinate(coordinate) {
|
|
878
|
+
const size = getGeoArrowDimensionSize(this.dimension);
|
|
879
|
+
if (coordinate.length !== size) {
|
|
880
|
+
throw new Error(`Expected ${size} coordinate values for ${this.dimension}`);
|
|
881
|
+
}
|
|
882
|
+
if (this.target)
|
|
883
|
+
writeCoordinate(this.target.coordinates, this.coordinateCount, coordinate, this.dimension);
|
|
884
|
+
this.coordinateCount++;
|
|
885
|
+
}
|
|
886
|
+
writeOffset(target, index, value) {
|
|
887
|
+
if (!target)
|
|
888
|
+
return;
|
|
889
|
+
if (target instanceof BigInt64Array)
|
|
890
|
+
target[index] = BigInt(value);
|
|
891
|
+
else
|
|
892
|
+
target[index] = value;
|
|
893
|
+
}
|
|
894
|
+
initializeTargetOffsets(target) {
|
|
895
|
+
this.writeOffset(target.geometryOffsets, 0, 0);
|
|
896
|
+
this.writeOffset(target.partOffsets, 0, 0);
|
|
897
|
+
this.writeOffset(target.ringOffsets, 0, 0);
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
function allocateGeoArrowBuilderTarget(measurement, options) {
|
|
901
|
+
const dimension = options.dimension || "xy";
|
|
902
|
+
const size = getGeoArrowDimensionSize(dimension);
|
|
903
|
+
const FloatArray = options.coordinateType === "float32" ? Float32Array : Float64Array;
|
|
904
|
+
const coordinateLayout = options.coordinateLayout || "interleaved";
|
|
905
|
+
const coordinates = coordinateLayout === "interleaved" ? new FloatArray(measurement.coordinateCount * size) : makeSeparatedCoordinateTarget(FloatArray, measurement.coordinateCount, dimension);
|
|
906
|
+
const OffsetArray = options.offsetType === "int64" ? BigInt64Array : Int32Array;
|
|
907
|
+
return {
|
|
908
|
+
validity: new Uint8Array(Math.ceil(measurement.length / 8)),
|
|
909
|
+
coordinates,
|
|
910
|
+
geometryOffsets: measurement.geometryOffsetCount ? new OffsetArray(measurement.geometryOffsetCount) : void 0,
|
|
911
|
+
partOffsets: measurement.partOffsetCount ? new OffsetArray(measurement.partOffsetCount) : void 0,
|
|
912
|
+
ringOffsets: measurement.ringOffsetCount ? new OffsetArray(measurement.ringOffsetCount) : void 0
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
function makeGeoArrowColumnFromGeometryRows(rows, options = {}) {
|
|
916
|
+
const geometryTypes = [...new Set(rows.filter(Boolean).map((row) => row.type))];
|
|
917
|
+
const dimension = options.dimension || inferRowsDimension(rows);
|
|
918
|
+
if (options.encoding === "geoarrow.geometry") {
|
|
919
|
+
return {
|
|
920
|
+
encoding: "geoarrow.geometry",
|
|
921
|
+
dimension,
|
|
922
|
+
coordinateLayout: options.coordinateLayout || "interleaved",
|
|
923
|
+
chunks: [makeDenseUnionArray(rows, options, dimension)]
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
if (geometryTypes.length === 0) {
|
|
927
|
+
return GeoArrowBuilder.build(rows, { ...options, dimension, encoding: "geoarrow.point" });
|
|
928
|
+
}
|
|
929
|
+
if (geometryTypes.length === 1 && geometryTypes[0] !== "GeometryCollection") {
|
|
930
|
+
return GeoArrowBuilder.build(rows, {
|
|
931
|
+
...options,
|
|
932
|
+
dimension,
|
|
933
|
+
encoding: getGeoArrowEncodingForGeometry(geometryTypes[0])
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
if (geometryTypes.length === 1 && geometryTypes[0] === "GeometryCollection") {
|
|
937
|
+
return {
|
|
938
|
+
encoding: "geoarrow.geometrycollection",
|
|
939
|
+
dimension,
|
|
940
|
+
coordinateLayout: options.coordinateLayout || "interleaved",
|
|
941
|
+
chunks: [makeGeometryCollectionArray(rows, options, dimension)]
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
return {
|
|
945
|
+
encoding: "geoarrow.geometry",
|
|
946
|
+
dimension,
|
|
947
|
+
coordinateLayout: options.coordinateLayout || "interleaved",
|
|
948
|
+
chunks: [makeDenseUnionArray(rows, options, dimension)]
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
function makeDenseUnionArray(rows, options, dimension) {
|
|
952
|
+
const nonNullTypes = [...new Set(rows.filter(Boolean).map((row) => row.type))];
|
|
953
|
+
const fallbackType = nonNullTypes[0] || "Point";
|
|
954
|
+
const childRows = /* @__PURE__ */ new Map();
|
|
955
|
+
for (const type of nonNullTypes.length ? nonNullTypes : [fallbackType])
|
|
956
|
+
childRows.set(type, []);
|
|
957
|
+
const typeIds = new Int8Array(rows.length);
|
|
958
|
+
const valueOffsets = new Int32Array(rows.length);
|
|
959
|
+
const validity = new Uint8Array(Math.ceil(rows.length / 8));
|
|
960
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
961
|
+
const geometry = rows[rowIndex];
|
|
962
|
+
const type = (geometry == null ? void 0 : geometry.type) || fallbackType;
|
|
963
|
+
const values = childRows.get(type) || [];
|
|
964
|
+
typeIds[rowIndex] = getGeometryTypeId(type);
|
|
965
|
+
valueOffsets[rowIndex] = values.length;
|
|
966
|
+
if (geometry) {
|
|
967
|
+
values.push(geometry);
|
|
968
|
+
childRows.set(type, values);
|
|
969
|
+
setValidityBit(validity, rowIndex);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
const children = [...childRows.entries()].sort(([left], [right]) => getGeometryTypeId(left) - getGeometryTypeId(right)).map(([type, values]) => {
|
|
973
|
+
const data = type === "GeometryCollection" ? makeGeometryCollectionArray(values, options, dimension) : GeoArrowBuilder.build(values, {
|
|
974
|
+
...options,
|
|
975
|
+
dimension,
|
|
976
|
+
encoding: getGeoArrowEncodingForGeometry(type)
|
|
977
|
+
}).chunks[0];
|
|
978
|
+
return { name: type, typeId: getGeometryTypeId(type), data };
|
|
979
|
+
});
|
|
980
|
+
return {
|
|
981
|
+
kind: "dense-union",
|
|
982
|
+
length: rows.length,
|
|
983
|
+
typeIds,
|
|
984
|
+
valueOffsets,
|
|
985
|
+
children,
|
|
986
|
+
validity: { values: validity }
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
function makeGeometryCollectionArray(rows, options, dimension) {
|
|
990
|
+
const offsets = new Int32Array(rows.length + 1);
|
|
991
|
+
const validity = new Uint8Array(Math.ceil(rows.length / 8));
|
|
992
|
+
const flattened = [];
|
|
993
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
994
|
+
const row = rows[rowIndex];
|
|
995
|
+
if ((row == null ? void 0 : row.type) === "GeometryCollection") {
|
|
996
|
+
flattened.push(...row.geometries);
|
|
997
|
+
setValidityBit(validity, rowIndex);
|
|
998
|
+
}
|
|
999
|
+
offsets[rowIndex + 1] = flattened.length;
|
|
1000
|
+
}
|
|
1001
|
+
return {
|
|
1002
|
+
kind: "list",
|
|
1003
|
+
length: rows.length,
|
|
1004
|
+
offsets,
|
|
1005
|
+
child: makeDenseUnionArray(flattened, options, dimension),
|
|
1006
|
+
validity: { values: validity }
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
function makeCoordinateArray(coordinates, coordinateCount, dimension, layout) {
|
|
1010
|
+
const size = getGeoArrowDimensionSize(dimension);
|
|
1011
|
+
if (layout === "interleaved") {
|
|
1012
|
+
const values = coordinates;
|
|
1013
|
+
return {
|
|
1014
|
+
kind: "fixed-size-list",
|
|
1015
|
+
length: coordinateCount,
|
|
1016
|
+
size,
|
|
1017
|
+
child: { kind: "primitive", length: coordinateCount * size, values }
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
const separated = coordinates;
|
|
1021
|
+
const children = {
|
|
1022
|
+
x: { kind: "primitive", length: coordinateCount, values: separated.x },
|
|
1023
|
+
y: { kind: "primitive", length: coordinateCount, values: separated.y }
|
|
1024
|
+
};
|
|
1025
|
+
if (separated.z) {
|
|
1026
|
+
children["z"] = { kind: "primitive", length: coordinateCount, values: separated.z };
|
|
1027
|
+
}
|
|
1028
|
+
if (separated.m) {
|
|
1029
|
+
children["m"] = { kind: "primitive", length: coordinateCount, values: separated.m };
|
|
1030
|
+
}
|
|
1031
|
+
return { kind: "struct", length: coordinateCount, children };
|
|
1032
|
+
}
|
|
1033
|
+
function makeSeparatedCoordinateTarget(FloatArray, count, dimension) {
|
|
1034
|
+
return {
|
|
1035
|
+
x: new FloatArray(count),
|
|
1036
|
+
y: new FloatArray(count),
|
|
1037
|
+
z: dimension === "xyz" || dimension === "xyzm" ? new FloatArray(count) : void 0,
|
|
1038
|
+
m: dimension === "xym" || dimension === "xyzm" ? new FloatArray(count) : void 0
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
function writeCoordinate(target, coordinateIndex, coordinate, dimension) {
|
|
1042
|
+
if (target instanceof Float32Array || target instanceof Float64Array) {
|
|
1043
|
+
target.set(coordinate, coordinateIndex * coordinate.length);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
target.x[coordinateIndex] = coordinate[0];
|
|
1047
|
+
target.y[coordinateIndex] = coordinate[1];
|
|
1048
|
+
if (dimension === "xyz")
|
|
1049
|
+
target.z[coordinateIndex] = coordinate[2];
|
|
1050
|
+
else if (dimension === "xym")
|
|
1051
|
+
target.m[coordinateIndex] = coordinate[2];
|
|
1052
|
+
else if (dimension === "xyzm") {
|
|
1053
|
+
target.z[coordinateIndex] = coordinate[2];
|
|
1054
|
+
target.m[coordinateIndex] = coordinate[3];
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
function setValidityBit(validity, index) {
|
|
1058
|
+
validity[index >> 3] |= 1 << (index & 7);
|
|
1059
|
+
}
|
|
1060
|
+
function getBuilderDepth(encoding) {
|
|
1061
|
+
switch (encoding) {
|
|
1062
|
+
case "geoarrow.point":
|
|
1063
|
+
return 0;
|
|
1064
|
+
case "geoarrow.linestring":
|
|
1065
|
+
case "geoarrow.multipoint":
|
|
1066
|
+
return 1;
|
|
1067
|
+
case "geoarrow.polygon":
|
|
1068
|
+
case "geoarrow.multilinestring":
|
|
1069
|
+
return 2;
|
|
1070
|
+
case "geoarrow.multipolygon":
|
|
1071
|
+
return 3;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
function getGeometryTypeId(type) {
|
|
1075
|
+
return [
|
|
1076
|
+
"Point",
|
|
1077
|
+
"LineString",
|
|
1078
|
+
"Polygon",
|
|
1079
|
+
"MultiPoint",
|
|
1080
|
+
"MultiLineString",
|
|
1081
|
+
"MultiPolygon",
|
|
1082
|
+
"GeometryCollection"
|
|
1083
|
+
].indexOf(type) + 1;
|
|
1084
|
+
}
|
|
1085
|
+
function inferRowsDimension(rows) {
|
|
1086
|
+
let size = 2;
|
|
1087
|
+
const visit = (value) => {
|
|
1088
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
1089
|
+
return;
|
|
1090
|
+
if (typeof value[0] === "number")
|
|
1091
|
+
size = Math.max(size, value.length);
|
|
1092
|
+
else
|
|
1093
|
+
for (const child of value)
|
|
1094
|
+
visit(child);
|
|
1095
|
+
};
|
|
1096
|
+
const visitGeometry = (geometry) => {
|
|
1097
|
+
if (geometry.type === "GeometryCollection") {
|
|
1098
|
+
for (const child of geometry.geometries)
|
|
1099
|
+
visitGeometry(child);
|
|
1100
|
+
} else {
|
|
1101
|
+
visit(geometry.coordinates);
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
for (const row of rows) {
|
|
1105
|
+
if (row)
|
|
1106
|
+
visitGeometry(row);
|
|
1107
|
+
}
|
|
1108
|
+
return size >= 4 ? "xyzm" : size === 3 ? "xyz" : "xy";
|
|
1109
|
+
}
|
|
1110
|
+
function assertTargetCapacity(target, measurement, size) {
|
|
1111
|
+
const coordinateLength = target.coordinates instanceof Float32Array || target.coordinates instanceof Float64Array ? target.coordinates.length / size : target.coordinates.x.length;
|
|
1112
|
+
if (coordinateLength < measurement.coordinateCount || target.validity.length * 8 < measurement.length) {
|
|
1113
|
+
throw new Error("GeoArrowBuilder target is smaller than its measured output");
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// dist/kernels.js
|
|
1118
|
+
function getGeoArrowBounds(column) {
|
|
1119
|
+
if (column.encoding === "geoarrow.box")
|
|
1120
|
+
return getGeoArrowBoxBounds(column);
|
|
1121
|
+
let minimumX = Number.POSITIVE_INFINITY;
|
|
1122
|
+
let minimumY = Number.POSITIVE_INFINITY;
|
|
1123
|
+
let maximumX = Number.NEGATIVE_INFINITY;
|
|
1124
|
+
let maximumY = Number.NEGATIVE_INFINITY;
|
|
1125
|
+
visitGeoArrowCoordinates(column, (coordinate) => {
|
|
1126
|
+
const x = coordinate[0];
|
|
1127
|
+
const y = coordinate[1];
|
|
1128
|
+
if (Number.isFinite(x) && Number.isFinite(y)) {
|
|
1129
|
+
minimumX = Math.min(minimumX, x);
|
|
1130
|
+
minimumY = Math.min(minimumY, y);
|
|
1131
|
+
maximumX = Math.max(maximumX, x);
|
|
1132
|
+
maximumY = Math.max(maximumY, y);
|
|
1133
|
+
}
|
|
1134
|
+
return coordinate;
|
|
1135
|
+
});
|
|
1136
|
+
return Number.isFinite(minimumX) ? [minimumX, minimumY, maximumX, maximumY] : null;
|
|
1137
|
+
}
|
|
1138
|
+
function getGeoArrowBoxBounds(column) {
|
|
1139
|
+
let minimumX = Number.POSITIVE_INFINITY;
|
|
1140
|
+
let minimumY = Number.POSITIVE_INFINITY;
|
|
1141
|
+
let maximumX = Number.NEGATIVE_INFINITY;
|
|
1142
|
+
let maximumY = Number.NEGATIVE_INFINITY;
|
|
1143
|
+
for (const chunk of column.chunks) {
|
|
1144
|
+
if (chunk.kind !== "struct")
|
|
1145
|
+
continue;
|
|
1146
|
+
const xmin = chunk.children["xmin"];
|
|
1147
|
+
const ymin = chunk.children["ymin"];
|
|
1148
|
+
const xmax = chunk.children["xmax"];
|
|
1149
|
+
const ymax = chunk.children["ymax"];
|
|
1150
|
+
if (!xmin || !ymin || !xmax || !ymax)
|
|
1151
|
+
continue;
|
|
1152
|
+
for (let rowIndex = 0; rowIndex < chunk.length; rowIndex++) {
|
|
1153
|
+
if (!isGeoArrowValueValid(chunk.validity, rowIndex))
|
|
1154
|
+
continue;
|
|
1155
|
+
const structIndex = (chunk.offset || 0) + rowIndex;
|
|
1156
|
+
const bounds = [
|
|
1157
|
+
readPrimitiveNumber(xmin, structIndex),
|
|
1158
|
+
readPrimitiveNumber(ymin, structIndex),
|
|
1159
|
+
readPrimitiveNumber(xmax, structIndex),
|
|
1160
|
+
readPrimitiveNumber(ymax, structIndex)
|
|
1161
|
+
];
|
|
1162
|
+
if (bounds.every(Number.isFinite)) {
|
|
1163
|
+
minimumX = Math.min(minimumX, bounds[0]);
|
|
1164
|
+
minimumY = Math.min(minimumY, bounds[1]);
|
|
1165
|
+
maximumX = Math.max(maximumX, bounds[2]);
|
|
1166
|
+
maximumY = Math.max(maximumY, bounds[3]);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return Number.isFinite(minimumX) ? [minimumX, minimumY, maximumX, maximumY] : null;
|
|
1171
|
+
}
|
|
1172
|
+
function readPrimitiveNumber(array, index) {
|
|
1173
|
+
if (array.kind !== "primitive" || !isGeoArrowValueValid(array.validity, index))
|
|
1174
|
+
return Number.NaN;
|
|
1175
|
+
const valueIndex = (array.offset || 0) + index * (array.stride || 1);
|
|
1176
|
+
return Number(array.values[valueIndex]);
|
|
1177
|
+
}
|
|
1178
|
+
function mapGeoArrowCoordinates(column, mapper, options = {}) {
|
|
1179
|
+
assertGeoArrowResourceLimits(column, options.limits);
|
|
1180
|
+
const rows = materializeGeoArrowRows(column);
|
|
1181
|
+
let rowIndex = 0;
|
|
1182
|
+
const mappedRows = rows.map((row) => {
|
|
1183
|
+
const mapped = row ? mapGeometryCoordinates(row, (coordinate) => mapper(coordinate, rowIndex)) : null;
|
|
1184
|
+
rowIndex++;
|
|
1185
|
+
return mapped;
|
|
1186
|
+
});
|
|
1187
|
+
const result = makeGeoArrowColumnFromGeometryRows(mappedRows, {
|
|
1188
|
+
dimension: options.dimension || column.dimension,
|
|
1189
|
+
coordinateLayout: options.coordinateLayout || column.coordinateLayout || "interleaved"
|
|
1190
|
+
});
|
|
1191
|
+
return copyColumnMetadata(column, result);
|
|
1192
|
+
}
|
|
1193
|
+
function mapGeoArrowCoordinatesInto(target, source, mapper, options = {}) {
|
|
1194
|
+
const mapped = mapGeoArrowCoordinates(source, mapper, {
|
|
1195
|
+
...options,
|
|
1196
|
+
coordinateLayout: target.coordinateLayout || void 0
|
|
1197
|
+
});
|
|
1198
|
+
const sourceLeaves = collectCoordinateLeaves(mapped);
|
|
1199
|
+
const targetLeaves = collectCoordinateLeaves(target);
|
|
1200
|
+
if (sourceLeaves.length !== targetLeaves.length) {
|
|
1201
|
+
throw new Error("GeoArrow destination has different coordinate topology");
|
|
1202
|
+
}
|
|
1203
|
+
for (let index = 0; index < sourceLeaves.length; index++) {
|
|
1204
|
+
const sourceLeaf = sourceLeaves[index];
|
|
1205
|
+
const targetLeaf = targetLeaves[index];
|
|
1206
|
+
if (sourceLeaf.length !== targetLeaf.length) {
|
|
1207
|
+
throw new Error("GeoArrow destination coordinate buffer has a different length");
|
|
1208
|
+
}
|
|
1209
|
+
targetLeaf.set(sourceLeaf);
|
|
1210
|
+
}
|
|
1211
|
+
return target;
|
|
1212
|
+
}
|
|
1213
|
+
function interleaveGeoArrowCoordinates(column) {
|
|
1214
|
+
if (column.coordinateLayout === "interleaved")
|
|
1215
|
+
return column;
|
|
1216
|
+
if (!column.coordinateLayout)
|
|
1217
|
+
return column;
|
|
1218
|
+
return mapGeoArrowCoordinates(column, (coordinate) => coordinate, {
|
|
1219
|
+
coordinateLayout: "interleaved"
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
function normalizeGeoArrowUnion(column) {
|
|
1223
|
+
if (column.encoding !== "geoarrow.geometry")
|
|
1224
|
+
return column;
|
|
1225
|
+
let changed = false;
|
|
1226
|
+
const chunks = column.chunks.map((chunk) => {
|
|
1227
|
+
if (chunk.kind !== "dense-union")
|
|
1228
|
+
return chunk;
|
|
1229
|
+
const children = [...chunk.children].sort((left, right) => left.typeId - right.typeId);
|
|
1230
|
+
changed || (changed = children.some((child, index) => child !== chunk.children[index]));
|
|
1231
|
+
return children.every((child, index) => child === chunk.children[index]) ? chunk : { ...chunk, children };
|
|
1232
|
+
});
|
|
1233
|
+
return changed ? { ...column, chunks } : column;
|
|
1234
|
+
}
|
|
1235
|
+
function convertGeoArrowColumn(column, options = {}) {
|
|
1236
|
+
const encoding = options.encoding || column.encoding;
|
|
1237
|
+
const dimension = options.dimension || column.dimension;
|
|
1238
|
+
const coordinateLayout = options.coordinateLayout || column.coordinateLayout;
|
|
1239
|
+
if (encoding === column.encoding && dimension === column.dimension && coordinateLayout === column.coordinateLayout) {
|
|
1240
|
+
return column;
|
|
1241
|
+
}
|
|
1242
|
+
if (encoding === "geoarrow.wkb" || encoding === "geoarrow.wkt") {
|
|
1243
|
+
throw new Error("Use encodeGeoArrowWKB or encodeGeoArrowWKT for serialized output");
|
|
1244
|
+
}
|
|
1245
|
+
assertGeoArrowResourceLimits(column, options.limits);
|
|
1246
|
+
const rows = materializeGeoArrowRows(column).map((row) => row ? convertGeometryFamily(row, encoding, column.dimension, dimension) : null);
|
|
1247
|
+
const result = makeGeoArrowColumnFromGeometryRows(rows, {
|
|
1248
|
+
encoding: encoding === "geoarrow.geometry" ? encoding : void 0,
|
|
1249
|
+
dimension,
|
|
1250
|
+
coordinateLayout: coordinateLayout || "interleaved"
|
|
1251
|
+
});
|
|
1252
|
+
if (result.encoding !== encoding && encoding !== "geoarrow.geometry") {
|
|
1253
|
+
throw new Error(`Rows cannot be represented as ${encoding}`);
|
|
1254
|
+
}
|
|
1255
|
+
return copyColumnMetadata(column, result);
|
|
1256
|
+
}
|
|
1257
|
+
function rewindGeoArrow(column, options = {}) {
|
|
1258
|
+
assertGeoArrowResourceLimits(column, options.limits);
|
|
1259
|
+
const outerClockwise = (options.outer || "counter-clockwise") === "clockwise";
|
|
1260
|
+
let changed = false;
|
|
1261
|
+
const rows = materializeGeoArrowRows(column).map((row) => {
|
|
1262
|
+
if (!row)
|
|
1263
|
+
return null;
|
|
1264
|
+
const rewound = rewindGeometry(row, outerClockwise);
|
|
1265
|
+
changed || (changed = rewound !== row);
|
|
1266
|
+
return rewound;
|
|
1267
|
+
});
|
|
1268
|
+
if (!changed)
|
|
1269
|
+
return column;
|
|
1270
|
+
const result = makeGeoArrowColumnFromGeometryRows(rows, {
|
|
1271
|
+
dimension: column.dimension,
|
|
1272
|
+
coordinateLayout: column.coordinateLayout || "interleaved"
|
|
1273
|
+
});
|
|
1274
|
+
return copyColumnMetadata(column, result);
|
|
1275
|
+
}
|
|
1276
|
+
function assertGeoArrowResourceLimits(column, options = {}) {
|
|
1277
|
+
const rows = getGeoArrowRowCount(column);
|
|
1278
|
+
if (rows > (options.maximumRows ?? Number.POSITIVE_INFINITY)) {
|
|
1279
|
+
throw new Error(`GeoArrow row count ${rows} exceeds maximumRows`);
|
|
1280
|
+
}
|
|
1281
|
+
if (column.chunks.length > (options.maximumChunks ?? Number.POSITIVE_INFINITY)) {
|
|
1282
|
+
throw new Error(`GeoArrow chunk count ${column.chunks.length} exceeds maximumChunks`);
|
|
1283
|
+
}
|
|
1284
|
+
const coordinates = getGeoArrowVertexCount(column);
|
|
1285
|
+
if (coordinates > (options.maximumCoordinates ?? Number.POSITIVE_INFINITY)) {
|
|
1286
|
+
throw new Error(`GeoArrow coordinate count ${coordinates} exceeds maximumCoordinates`);
|
|
1287
|
+
}
|
|
1288
|
+
if (options.maximumNestingDepth !== void 0) {
|
|
1289
|
+
const depth = Math.max(0, ...column.chunks.map(getArrayDepth));
|
|
1290
|
+
if (depth > options.maximumNestingDepth) {
|
|
1291
|
+
throw new Error(`GeoArrow nesting depth ${depth} exceeds maximumNestingDepth`);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
if (options.maximumOutputBytes !== void 0) {
|
|
1295
|
+
const estimatedBytes = coordinates * getGeoArrowDimensionSize(column.dimension) * 8;
|
|
1296
|
+
if (estimatedBytes > options.maximumOutputBytes) {
|
|
1297
|
+
throw new Error(`GeoArrow estimated output ${estimatedBytes} exceeds maximumOutputBytes`);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
function mapGeometryCoordinates(geometry, mapper) {
|
|
1302
|
+
if (geometry.type === "GeometryCollection") {
|
|
1303
|
+
return {
|
|
1304
|
+
...geometry,
|
|
1305
|
+
geometries: geometry.geometries.map((child) => mapGeometryCoordinates(child, mapper))
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
return {
|
|
1309
|
+
...geometry,
|
|
1310
|
+
coordinates: mapCoordinateNesting(geometry.coordinates, mapper)
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
function mapCoordinateNesting(value, mapper) {
|
|
1314
|
+
if (value.length === 0)
|
|
1315
|
+
return [];
|
|
1316
|
+
if (typeof value[0] === "number")
|
|
1317
|
+
return [...mapper(value)];
|
|
1318
|
+
return value.map((child) => mapCoordinateNesting(child, mapper));
|
|
1319
|
+
}
|
|
1320
|
+
function rewindGeometry(geometry, outerClockwise) {
|
|
1321
|
+
if (geometry.type === "GeometryCollection") {
|
|
1322
|
+
const geometries = geometry.geometries.map((child) => rewindGeometry(child, outerClockwise));
|
|
1323
|
+
return geometries.some((child, index) => child !== geometry.geometries[index]) ? { ...geometry, geometries } : geometry;
|
|
1324
|
+
}
|
|
1325
|
+
if (geometry.type === "Polygon") {
|
|
1326
|
+
const coordinates = rewindPolygon(geometry.coordinates, outerClockwise);
|
|
1327
|
+
return coordinates === geometry.coordinates ? geometry : { ...geometry, coordinates };
|
|
1328
|
+
}
|
|
1329
|
+
if (geometry.type === "MultiPolygon") {
|
|
1330
|
+
const coordinates = geometry.coordinates.map((polygon) => rewindPolygon(polygon, outerClockwise));
|
|
1331
|
+
return coordinates.every((polygon, index) => polygon === geometry.coordinates[index]) ? geometry : { ...geometry, coordinates };
|
|
1332
|
+
}
|
|
1333
|
+
return geometry;
|
|
1334
|
+
}
|
|
1335
|
+
function rewindPolygon(rings, outerClockwise) {
|
|
1336
|
+
let changed = false;
|
|
1337
|
+
const result = rings.map((ring, ringIndex) => {
|
|
1338
|
+
const shouldBeClockwise = ringIndex === 0 ? outerClockwise : !outerClockwise;
|
|
1339
|
+
const isClockwise = getRingSignedArea(ring) < 0;
|
|
1340
|
+
if (isClockwise === shouldBeClockwise)
|
|
1341
|
+
return ring;
|
|
1342
|
+
changed = true;
|
|
1343
|
+
return [...ring].reverse();
|
|
1344
|
+
});
|
|
1345
|
+
return changed ? result : rings;
|
|
1346
|
+
}
|
|
1347
|
+
function getRingSignedArea(ring) {
|
|
1348
|
+
let area = 0;
|
|
1349
|
+
for (let index = 0; index < ring.length; index++) {
|
|
1350
|
+
const current = ring[index];
|
|
1351
|
+
const next = ring[(index + 1) % ring.length];
|
|
1352
|
+
area += current[0] * next[1] - next[0] * current[1];
|
|
1353
|
+
}
|
|
1354
|
+
return area / 2;
|
|
1355
|
+
}
|
|
1356
|
+
function convertGeometryFamily(geometry, encoding, sourceDimension, targetDimension) {
|
|
1357
|
+
if (geometry.type === "GeometryCollection") {
|
|
1358
|
+
const geometries = geometry.geometries.map((child) => convertGeometryFamily(child, "geoarrow.geometry", sourceDimension, targetDimension));
|
|
1359
|
+
if (encoding === "geoarrow.geometry" || encoding === "geoarrow.geometrycollection") {
|
|
1360
|
+
return { ...geometry, geometries };
|
|
1361
|
+
}
|
|
1362
|
+
throw new Error(`${geometry.type} cannot be represented as ${encoding}`);
|
|
1363
|
+
}
|
|
1364
|
+
const coordinates = mapCoordinateNesting(geometry.coordinates, (coordinate) => resizeCoordinate(coordinate, sourceDimension, targetDimension));
|
|
1365
|
+
if (encoding === "geoarrow.geometry") {
|
|
1366
|
+
return { ...geometry, coordinates };
|
|
1367
|
+
}
|
|
1368
|
+
const target = encoding.replace("geoarrow.", "");
|
|
1369
|
+
if (geometry.type.toLowerCase() !== target) {
|
|
1370
|
+
throw new Error(`${geometry.type} cannot be represented as ${encoding}`);
|
|
1371
|
+
}
|
|
1372
|
+
return { ...geometry, coordinates };
|
|
1373
|
+
}
|
|
1374
|
+
function resizeCoordinate(coordinate, sourceDimension, targetDimension) {
|
|
1375
|
+
const sourceNames = getDimensionNames(sourceDimension);
|
|
1376
|
+
const targetNames = getDimensionNames(targetDimension);
|
|
1377
|
+
return targetNames.map((name) => {
|
|
1378
|
+
const sourceIndex = sourceNames.indexOf(name);
|
|
1379
|
+
return sourceIndex >= 0 ? coordinate[sourceIndex] ?? 0 : 0;
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
function getDimensionNames(dimension) {
|
|
1383
|
+
switch (dimension) {
|
|
1384
|
+
case "xy":
|
|
1385
|
+
return ["x", "y"];
|
|
1386
|
+
case "xyz":
|
|
1387
|
+
return ["x", "y", "z"];
|
|
1388
|
+
case "xym":
|
|
1389
|
+
return ["x", "y", "m"];
|
|
1390
|
+
case "xyzm":
|
|
1391
|
+
return ["x", "y", "z", "m"];
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
function copyColumnMetadata(source, target) {
|
|
1395
|
+
return {
|
|
1396
|
+
...target,
|
|
1397
|
+
spatialReference: source.spatialReference,
|
|
1398
|
+
edges: source.edges,
|
|
1399
|
+
metadata: source.metadata
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
function collectCoordinateLeaves(column) {
|
|
1403
|
+
const leaves = [];
|
|
1404
|
+
for (const chunk of column.chunks)
|
|
1405
|
+
collectArrayCoordinateLeaves(chunk, leaves);
|
|
1406
|
+
return leaves;
|
|
1407
|
+
}
|
|
1408
|
+
function collectArrayCoordinateLeaves(array, leaves) {
|
|
1409
|
+
switch (array.kind) {
|
|
1410
|
+
case "primitive":
|
|
1411
|
+
if (array.values instanceof Float32Array || array.values instanceof Float64Array) {
|
|
1412
|
+
leaves.push(array.values);
|
|
1413
|
+
}
|
|
1414
|
+
break;
|
|
1415
|
+
case "fixed-size-list":
|
|
1416
|
+
case "list":
|
|
1417
|
+
collectArrayCoordinateLeaves(array.child, leaves);
|
|
1418
|
+
break;
|
|
1419
|
+
case "struct":
|
|
1420
|
+
for (const name of ["x", "y", "z", "m"]) {
|
|
1421
|
+
const child = array.children[name];
|
|
1422
|
+
if (child)
|
|
1423
|
+
collectArrayCoordinateLeaves(child, leaves);
|
|
1424
|
+
}
|
|
1425
|
+
break;
|
|
1426
|
+
case "dense-union":
|
|
1427
|
+
for (const child of array.children)
|
|
1428
|
+
collectArrayCoordinateLeaves(child.data, leaves);
|
|
1429
|
+
break;
|
|
1430
|
+
default:
|
|
1431
|
+
break;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
function getArrayDepth(array) {
|
|
1435
|
+
switch (array.kind) {
|
|
1436
|
+
case "list":
|
|
1437
|
+
case "fixed-size-list":
|
|
1438
|
+
return 1 + getArrayDepth(array.child);
|
|
1439
|
+
case "struct":
|
|
1440
|
+
return 1 + Math.max(0, ...Object.values(array.children).map(getArrayDepth));
|
|
1441
|
+
case "dense-union":
|
|
1442
|
+
return 1 + Math.max(0, ...array.children.map((child) => getArrayDepth(child.data)));
|
|
1443
|
+
default:
|
|
1444
|
+
return 1;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// dist/codecs.js
|
|
1449
|
+
var import_wkb = require("@math.gl/wkb");
|
|
1450
|
+
var textEncoder = new TextEncoder();
|
|
1451
|
+
var textDecoder = new TextDecoder();
|
|
1452
|
+
function decodeGeoArrowWKB(column) {
|
|
1453
|
+
if (column.encoding !== "geoarrow.wkb")
|
|
1454
|
+
throw new Error("Expected a geoarrow.wkb column");
|
|
1455
|
+
const rows = normalizeRowsToDimension(decodeSerializedRows(column, (bytes) => (0, import_wkb.parseWKB)(bytes).geometry), column.dimension);
|
|
1456
|
+
return copyMetadata(column, makeGeoArrowColumnFromGeometryRows(rows, { dimension: column.dimension }));
|
|
1457
|
+
}
|
|
1458
|
+
function encodeGeoArrowWKB(column) {
|
|
1459
|
+
if (column.encoding === "geoarrow.wkb")
|
|
1460
|
+
return column;
|
|
1461
|
+
const rows = materializeGeoArrowRows(column);
|
|
1462
|
+
const values = rows.map((row) => row ? (0, import_wkb.writeWKB)(row, column.dimension) : null);
|
|
1463
|
+
return copyMetadata(column, makeSerializedColumn(values, "geoarrow.wkb", column.dimension));
|
|
1464
|
+
}
|
|
1465
|
+
function decodeGeoArrowWKT(column) {
|
|
1466
|
+
if (column.encoding !== "geoarrow.wkt")
|
|
1467
|
+
throw new Error("Expected a geoarrow.wkt column");
|
|
1468
|
+
const rows = normalizeRowsToDimension(decodeSerializedRows(column, (bytes) => (0, import_wkb.parseWKT)(textDecoder.decode(bytes))), column.dimension);
|
|
1469
|
+
return copyMetadata(column, makeGeoArrowColumnFromGeometryRows(rows, { dimension: column.dimension }));
|
|
1470
|
+
}
|
|
1471
|
+
function encodeGeoArrowWKT(column) {
|
|
1472
|
+
if (column.encoding === "geoarrow.wkt")
|
|
1473
|
+
return column;
|
|
1474
|
+
const rows = materializeGeoArrowRows(column);
|
|
1475
|
+
const values = rows.map((row) => row ? textEncoder.encode((0, import_wkb.formatWKT)(row, column.dimension)) : null);
|
|
1476
|
+
return copyMetadata(column, makeSerializedColumn(values, "geoarrow.wkt", column.dimension));
|
|
1477
|
+
}
|
|
1478
|
+
function normalizeRowsToDimension(rows, dimension) {
|
|
1479
|
+
const size = getGeoArrowDimensionSize(dimension);
|
|
1480
|
+
const normalizeGeometry = (geometry) => {
|
|
1481
|
+
if (geometry.type === "GeometryCollection") {
|
|
1482
|
+
return { ...geometry, geometries: geometry.geometries.map(normalizeGeometry) };
|
|
1483
|
+
}
|
|
1484
|
+
return {
|
|
1485
|
+
...geometry,
|
|
1486
|
+
coordinates: normalizeCoordinateNesting(geometry.coordinates, size)
|
|
1487
|
+
};
|
|
1488
|
+
};
|
|
1489
|
+
return rows.map((row) => row ? normalizeGeometry(row) : null);
|
|
1490
|
+
}
|
|
1491
|
+
function normalizeCoordinateNesting(value, size) {
|
|
1492
|
+
if (value.length === 0)
|
|
1493
|
+
return [];
|
|
1494
|
+
if (typeof value[0] === "number") {
|
|
1495
|
+
const coordinate = value.slice(0, size);
|
|
1496
|
+
while (coordinate.length < size)
|
|
1497
|
+
coordinate.push(0);
|
|
1498
|
+
return coordinate;
|
|
1499
|
+
}
|
|
1500
|
+
return value.map((child) => normalizeCoordinateNesting(child, size));
|
|
1501
|
+
}
|
|
1502
|
+
function decodeSerializedRows(column, decoder) {
|
|
1503
|
+
const rows = [];
|
|
1504
|
+
for (const chunk of column.chunks) {
|
|
1505
|
+
if (chunk.kind !== "serialized")
|
|
1506
|
+
throw new Error("Serialized column contains native storage");
|
|
1507
|
+
for (let rowIndex = 0; rowIndex < chunk.length; rowIndex++) {
|
|
1508
|
+
if (!isGeoArrowValueValid(chunk.validity, rowIndex)) {
|
|
1509
|
+
rows.push(null);
|
|
1510
|
+
continue;
|
|
1511
|
+
}
|
|
1512
|
+
rows.push(decoder(getSerializedBytes(chunk, rowIndex)));
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
return rows;
|
|
1516
|
+
}
|
|
1517
|
+
function getSerializedBytes(array, rowIndex) {
|
|
1518
|
+
const offsetIndex = (array.offset || 0) + rowIndex;
|
|
1519
|
+
const baseValue = array.offsetBase ?? 0;
|
|
1520
|
+
const base = typeof baseValue === "bigint" ? Number(baseValue) : baseValue;
|
|
1521
|
+
const first = getGeoArrowOffset(array.offsets, offsetIndex) - base;
|
|
1522
|
+
const last = getGeoArrowOffset(array.offsets, offsetIndex + 1) - base;
|
|
1523
|
+
return array.values.subarray(first, last);
|
|
1524
|
+
}
|
|
1525
|
+
function makeSerializedColumn(rows, encoding, dimension) {
|
|
1526
|
+
let byteLength = 0;
|
|
1527
|
+
for (const row of rows)
|
|
1528
|
+
byteLength += (row == null ? void 0 : row.byteLength) || 0;
|
|
1529
|
+
const values = new Uint8Array(byteLength);
|
|
1530
|
+
const offsets = new Int32Array(rows.length + 1);
|
|
1531
|
+
const validity = new Uint8Array(Math.ceil(rows.length / 8));
|
|
1532
|
+
let byteOffset = 0;
|
|
1533
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
1534
|
+
const row = rows[rowIndex];
|
|
1535
|
+
if (row) {
|
|
1536
|
+
values.set(row, byteOffset);
|
|
1537
|
+
byteOffset += row.byteLength;
|
|
1538
|
+
validity[rowIndex >> 3] |= 1 << (rowIndex & 7);
|
|
1539
|
+
}
|
|
1540
|
+
offsets[rowIndex + 1] = byteOffset;
|
|
1541
|
+
}
|
|
1542
|
+
const chunk = {
|
|
1543
|
+
kind: "serialized",
|
|
1544
|
+
encoding: encoding === "geoarrow.wkb" ? "binary" : "utf8",
|
|
1545
|
+
length: rows.length,
|
|
1546
|
+
offsets,
|
|
1547
|
+
values,
|
|
1548
|
+
validity: { values: validity }
|
|
1549
|
+
};
|
|
1550
|
+
return { encoding, dimension, coordinateLayout: null, chunks: [chunk] };
|
|
1551
|
+
}
|
|
1552
|
+
function copyMetadata(source, target) {
|
|
1553
|
+
return {
|
|
1554
|
+
...target,
|
|
1555
|
+
spatialReference: source.spatialReference,
|
|
1556
|
+
edges: source.edges,
|
|
1557
|
+
metadata: source.metadata
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
// dist/tessellate.js
|
|
1562
|
+
var import_polygon = require("@math.gl/polygon");
|
|
1563
|
+
function tessellateGeoArrowPolygons(column, options = {}) {
|
|
1564
|
+
assertGeoArrowResourceLimits(column, options.limits);
|
|
1565
|
+
const rows = materializeGeoArrowRows(column);
|
|
1566
|
+
const sourceDimension = getGeoArrowDimensionSize(column.dimension);
|
|
1567
|
+
const positionSize = options.positionSize || sourceDimension;
|
|
1568
|
+
const sourceRowOffset = options.sourceRowOffset || 0;
|
|
1569
|
+
const positions = [];
|
|
1570
|
+
const sourceRowIndices = [];
|
|
1571
|
+
const indices = [];
|
|
1572
|
+
let polygonCount = 0;
|
|
1573
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
1574
|
+
const row = rows[rowIndex];
|
|
1575
|
+
if (row) {
|
|
1576
|
+
visitPolygons(row, (polygon) => {
|
|
1577
|
+
const vertexOffset = positions.length / positionSize;
|
|
1578
|
+
const flatCoordinates = [];
|
|
1579
|
+
const holeIndices = [];
|
|
1580
|
+
let localVertexCount = 0;
|
|
1581
|
+
for (let ringIndex = 0; ringIndex < polygon.length; ringIndex++) {
|
|
1582
|
+
const ring = removeClosingCoordinate(polygon[ringIndex]);
|
|
1583
|
+
if (ring.length < 3)
|
|
1584
|
+
continue;
|
|
1585
|
+
if (localVertexCount > 0)
|
|
1586
|
+
holeIndices.push(localVertexCount);
|
|
1587
|
+
for (const coordinate of ring) {
|
|
1588
|
+
for (let component = 0; component < sourceDimension; component++) {
|
|
1589
|
+
flatCoordinates.push(coordinate[component] ?? 0);
|
|
1590
|
+
}
|
|
1591
|
+
for (let component = 0; component < positionSize; component++) {
|
|
1592
|
+
positions.push(coordinate[component] ?? 0);
|
|
1593
|
+
}
|
|
1594
|
+
sourceRowIndices.push(sourceRowOffset + rowIndex);
|
|
1595
|
+
localVertexCount++;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
if (localVertexCount >= 3) {
|
|
1599
|
+
const localIndices = (0, import_polygon.earcut)(flatCoordinates, holeIndices, sourceDimension);
|
|
1600
|
+
for (const index of localIndices)
|
|
1601
|
+
indices.push(vertexOffset + index);
|
|
1602
|
+
polygonCount++;
|
|
1603
|
+
}
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
const vertexCount = positions.length / positionSize;
|
|
1608
|
+
const IndexArray = vertexCount <= 65535 ? Uint16Array : Uint32Array;
|
|
1609
|
+
return {
|
|
1610
|
+
positions: Float32Array.from(positions),
|
|
1611
|
+
sourceRowIndices: Uint32Array.from(sourceRowIndices),
|
|
1612
|
+
indices: IndexArray.from(indices),
|
|
1613
|
+
sourceDimension,
|
|
1614
|
+
positionSize,
|
|
1615
|
+
rowCount: rows.length,
|
|
1616
|
+
polygonCount,
|
|
1617
|
+
vertexCount,
|
|
1618
|
+
triangleCount: indices.length / 3
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
function visitPolygons(geometry, visitor) {
|
|
1622
|
+
switch (geometry.type) {
|
|
1623
|
+
case "Polygon":
|
|
1624
|
+
visitor(geometry.coordinates);
|
|
1625
|
+
break;
|
|
1626
|
+
case "MultiPolygon":
|
|
1627
|
+
for (const polygon of geometry.coordinates)
|
|
1628
|
+
visitor(polygon);
|
|
1629
|
+
break;
|
|
1630
|
+
case "GeometryCollection":
|
|
1631
|
+
for (const child of geometry.geometries)
|
|
1632
|
+
visitPolygons(child, visitor);
|
|
1633
|
+
break;
|
|
1634
|
+
default:
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
function removeClosingCoordinate(ring) {
|
|
1639
|
+
if (ring.length < 2)
|
|
1640
|
+
return ring;
|
|
1641
|
+
const first = ring[0];
|
|
1642
|
+
const last = ring[ring.length - 1];
|
|
1643
|
+
if (first.length !== last.length)
|
|
1644
|
+
return ring;
|
|
1645
|
+
for (let index = 0; index < first.length; index++) {
|
|
1646
|
+
if (first[index] !== last[index])
|
|
1647
|
+
return ring;
|
|
1648
|
+
}
|
|
1649
|
+
return ring.slice(0, -1);
|
|
1650
|
+
}
|
|
1651
|
+
//# sourceMappingURL=index.cjs.map
|