@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.
- package/README.md +45 -15
- package/dist/builder-entry.cjs +561 -0
- package/dist/builder-entry.cjs.map +6 -0
- package/dist/builder-entry.d.ts +4 -0
- package/dist/builder-entry.d.ts.map +1 -0
- package/dist/builder-entry.js +5 -0
- package/dist/builder-entry.js.map +1 -0
- package/dist/builder.d.ts +12 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +112 -10
- package/dist/builder.js.map +1 -1
- package/dist/codecs.d.ts.map +1 -1
- package/dist/codecs.js +205 -7
- package/dist/codecs.js.map +1 -1
- package/dist/index.cjs +904 -120
- package/dist/index.cjs.map +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/kernels.d.ts +8 -3
- package/dist/kernels.d.ts.map +1 -1
- package/dist/kernels.js +610 -107
- package/dist/kernels.js.map +1 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +28 -5
- package/dist/layout.js.map +1 -1
- package/dist/tessellation-entry.cjs +427 -0
- package/dist/tessellation-entry.cjs.map +6 -0
- package/dist/tessellation-entry.d.ts +4 -0
- package/dist/tessellation-entry.d.ts.map +1 -0
- package/dist/tessellation-entry.js +5 -0
- package/dist/tessellation-entry.js.map +1 -0
- package/dist/types.d.ts +37 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/wkb-entry.cjs +1005 -0
- package/dist/wkb-entry.cjs.map +6 -0
- package/dist/wkb-entry.d.ts +3 -0
- package/dist/wkb-entry.d.ts.map +1 -0
- package/dist/wkb-entry.js +6 -0
- package/dist/wkb-entry.js.map +1 -0
- package/dist/worker.cjs +4 -0
- package/dist/worker.cjs.map +1 -1
- package/package.json +21 -6
- package/src/builder-entry.ts +18 -0
- package/src/builder.ts +133 -19
- package/src/codecs.ts +251 -9
- package/src/index.ts +1 -0
- package/src/kernels.ts +811 -133
- package/src/layout.ts +38 -13
- package/src/tessellation-entry.ts +7 -0
- package/src/types.ts +23 -4
- package/src/wkb-entry.ts +11 -0
package/src/codecs.ts
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {formatWKT, parseWKB, parseWKT
|
|
5
|
+
import {formatWKT, parseWKB, parseWKT} from '@math.gl/wkb';
|
|
6
|
+
import type {WKBGeometryWriter} from '@math.gl/wkb';
|
|
7
|
+
import {WKBBuilder} from '@math.gl/wkb';
|
|
6
8
|
import type {
|
|
7
9
|
GeoArrowColumn,
|
|
8
10
|
GeoArrowDimension,
|
|
@@ -11,7 +13,7 @@ import type {
|
|
|
11
13
|
} from './types';
|
|
12
14
|
import {getGeoArrowDimensionSize} from './types';
|
|
13
15
|
import {makeGeoArrowColumnFromGeometryRows} from './builder';
|
|
14
|
-
import {getGeoArrowOffset, isGeoArrowValueValid,
|
|
16
|
+
import {getGeoArrowOffset, isGeoArrowValueValid, materializeGeometryRow} from './layout';
|
|
15
17
|
|
|
16
18
|
const textEncoder = new TextEncoder();
|
|
17
19
|
const textDecoder = new TextDecoder();
|
|
@@ -32,9 +34,42 @@ export function decodeGeoArrowWKB(column: GeoArrowColumn): GeoArrowColumn {
|
|
|
32
34
|
/** Encodes a native GeoArrow column as variable-width WKB bytes. */
|
|
33
35
|
export function encodeGeoArrowWKB(column: GeoArrowColumn): GeoArrowColumn {
|
|
34
36
|
if (column.encoding === 'geoarrow.wkb') return column;
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
37
|
+
const writers: Array<WKBGeometryWriter | null> = [];
|
|
38
|
+
const dimensions: GeoArrowDimension[] = [];
|
|
39
|
+
for (const chunk of column.chunks) {
|
|
40
|
+
for (let rowIndex = 0; rowIndex < chunk.length; rowIndex++) {
|
|
41
|
+
if (!isGeoArrowValueValid(chunk.validity, rowIndex)) {
|
|
42
|
+
writers.push(null);
|
|
43
|
+
dimensions.push(column.dimension);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
dimensions.push(
|
|
47
|
+
getPhysicalGeometryDimension(chunk, rowIndex, column.encoding, column.dimension)
|
|
48
|
+
);
|
|
49
|
+
writers.push(builder =>
|
|
50
|
+
writePhysicalGeometry(builder, chunk, rowIndex, column.encoding, column.dimension)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const built = WKBBuilder.buildGeometryArray(writers, {
|
|
55
|
+
dimension: column.dimension,
|
|
56
|
+
dimensionResolver: index => dimensions[index] || column.dimension
|
|
57
|
+
});
|
|
58
|
+
return copyMetadata(column, {
|
|
59
|
+
encoding: 'geoarrow.wkb',
|
|
60
|
+
dimension: column.dimension,
|
|
61
|
+
coordinateLayout: null,
|
|
62
|
+
chunks: [
|
|
63
|
+
{
|
|
64
|
+
kind: 'serialized',
|
|
65
|
+
encoding: 'binary',
|
|
66
|
+
length: writers.length,
|
|
67
|
+
offsets: built.valueOffsets,
|
|
68
|
+
values: built.values,
|
|
69
|
+
...(built.nullBitmap ? {validity: {values: built.nullBitmap}} : {})
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
});
|
|
38
73
|
}
|
|
39
74
|
|
|
40
75
|
/** Decodes a GeoArrow WKT column into native physical geometry buffers. */
|
|
@@ -53,13 +88,206 @@ export function decodeGeoArrowWKT(column: GeoArrowColumn): GeoArrowColumn {
|
|
|
53
88
|
/** Encodes a native GeoArrow column as variable-width UTF-8 WKT. */
|
|
54
89
|
export function encodeGeoArrowWKT(column: GeoArrowColumn): GeoArrowColumn {
|
|
55
90
|
if (column.encoding === 'geoarrow.wkt') return column;
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
91
|
+
const values: Array<Uint8Array | null> = [];
|
|
92
|
+
for (const chunk of column.chunks) {
|
|
93
|
+
for (let rowIndex = 0; rowIndex < chunk.length; rowIndex++) {
|
|
94
|
+
const row = isGeoArrowValueValid(chunk.validity, rowIndex)
|
|
95
|
+
? materializeGeometryRow(chunk, rowIndex, column.encoding)
|
|
96
|
+
: null;
|
|
97
|
+
values.push(row ? textEncoder.encode(formatWKT(row, column.dimension)) : null);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
60
100
|
return copyMetadata(column, makeSerializedColumn(values, 'geoarrow.wkt', column.dimension));
|
|
61
101
|
}
|
|
62
102
|
|
|
103
|
+
function writePhysicalGeometry(
|
|
104
|
+
builder: WKBBuilder,
|
|
105
|
+
array: import('./types').GeoArrowArray,
|
|
106
|
+
rowIndex: number,
|
|
107
|
+
encoding: import('./types').GeoArrowEncoding,
|
|
108
|
+
dimension: GeoArrowDimension
|
|
109
|
+
): void {
|
|
110
|
+
const depth = getEncodingDepth(encoding);
|
|
111
|
+
if (encoding === 'geoarrow.geometry' && array.kind === 'dense-union') {
|
|
112
|
+
const physical = (array.offset || 0) + rowIndex;
|
|
113
|
+
const typeId = array.typeIds[physical];
|
|
114
|
+
const child = array.children.find(candidate => candidate.typeId === typeId);
|
|
115
|
+
if (!child) throw new Error('Dense-union row references an unknown child');
|
|
116
|
+
const childDimension = child.dimension || dimension;
|
|
117
|
+
builder.withDimension(childDimension, () =>
|
|
118
|
+
writePhysicalGeometry(
|
|
119
|
+
builder,
|
|
120
|
+
child.data,
|
|
121
|
+
array.valueOffsets[physical],
|
|
122
|
+
child.encoding || encodingFromName(child.name),
|
|
123
|
+
childDimension
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (encoding === 'geoarrow.geometrycollection' && array.kind === 'list') {
|
|
129
|
+
const [first, last] = getListRange(array, rowIndex);
|
|
130
|
+
builder.beginGeometry('GeometryCollection', last - first);
|
|
131
|
+
if (array.child.kind !== 'dense-union')
|
|
132
|
+
throw new Error('GeometryCollection requires dense union child');
|
|
133
|
+
const union = array.child;
|
|
134
|
+
for (let index = first; index < last; index++) {
|
|
135
|
+
const physical = (union.offset || 0) + index;
|
|
136
|
+
const child = union.children.find(candidate => candidate.typeId === union.typeIds[physical]);
|
|
137
|
+
if (child) {
|
|
138
|
+
const childDimension = child.dimension || dimension;
|
|
139
|
+
builder.withDimension(childDimension, () =>
|
|
140
|
+
writePhysicalGeometry(
|
|
141
|
+
builder,
|
|
142
|
+
child.data,
|
|
143
|
+
union.valueOffsets[physical],
|
|
144
|
+
child.encoding || encodingFromName(child.name),
|
|
145
|
+
childDimension
|
|
146
|
+
)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (array.kind !== 'list' && depth > 0) throw new Error(`Invalid ${encoding} storage`);
|
|
153
|
+
const writeCoordinate = (leaf: import('./types').GeoArrowArray, index: number): void => {
|
|
154
|
+
const coordinate = readPhysicalCoordinate(leaf, index);
|
|
155
|
+
if (!coordinate) throw new Error('Invalid GeoArrow coordinate storage');
|
|
156
|
+
if (dimension === 'xym')
|
|
157
|
+
builder.writeCoordinate(coordinate[0], coordinate[1], undefined, coordinate[2]);
|
|
158
|
+
else if (dimension === 'xyzm')
|
|
159
|
+
builder.writeCoordinate(coordinate[0], coordinate[1], coordinate[2], coordinate[3]);
|
|
160
|
+
else builder.writeCoordinate(coordinate[0], coordinate[1], coordinate[2]);
|
|
161
|
+
};
|
|
162
|
+
const writeSequence = (
|
|
163
|
+
leaf: import('./types').GeoArrowArray,
|
|
164
|
+
first: number,
|
|
165
|
+
last: number
|
|
166
|
+
): void => {
|
|
167
|
+
for (let index = first; index < last; index++) writeCoordinate(leaf, index);
|
|
168
|
+
};
|
|
169
|
+
if (encoding === 'geoarrow.point') {
|
|
170
|
+
builder.beginPoint();
|
|
171
|
+
writeCoordinate(array, rowIndex);
|
|
172
|
+
} else if (encoding === 'geoarrow.linestring' || encoding === 'geoarrow.multipoint') {
|
|
173
|
+
const [first, last] = getListRange(array as import('./types').GeoArrowList, rowIndex);
|
|
174
|
+
if (encoding === 'geoarrow.linestring') {
|
|
175
|
+
builder.beginLineString(last - first);
|
|
176
|
+
writeSequence((array as import('./types').GeoArrowList).child, first, last);
|
|
177
|
+
} else {
|
|
178
|
+
builder.beginMultiPoint(last - first);
|
|
179
|
+
const leaf = (array as import('./types').GeoArrowList).child;
|
|
180
|
+
for (let index = first; index < last; index++) {
|
|
181
|
+
builder.beginPoint();
|
|
182
|
+
writeCoordinate(leaf, index);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} else if (encoding === 'geoarrow.polygon' || encoding === 'geoarrow.multilinestring') {
|
|
186
|
+
const [first, last] = getListRange(array as import('./types').GeoArrowList, rowIndex);
|
|
187
|
+
const parts = (array as import('./types').GeoArrowList).child;
|
|
188
|
+
if (encoding === 'geoarrow.polygon') builder.beginPolygon(last - first);
|
|
189
|
+
else builder.beginMultiLineString(last - first);
|
|
190
|
+
for (let partIndex = first; partIndex < last; partIndex++) {
|
|
191
|
+
const [ringFirst, ringLast] = getListRange(
|
|
192
|
+
parts as import('./types').GeoArrowList,
|
|
193
|
+
partIndex
|
|
194
|
+
);
|
|
195
|
+
const leaf = (parts as import('./types').GeoArrowList).child;
|
|
196
|
+
if (encoding === 'geoarrow.polygon') builder.beginLinearRing(ringLast - ringFirst);
|
|
197
|
+
else builder.beginLineString(ringLast - ringFirst);
|
|
198
|
+
writeSequence(leaf, ringFirst, ringLast);
|
|
199
|
+
}
|
|
200
|
+
} else if (encoding === 'geoarrow.multipolygon') {
|
|
201
|
+
const [first, last] = getListRange(array as import('./types').GeoArrowList, rowIndex);
|
|
202
|
+
const polygons = (array as import('./types').GeoArrowList).child;
|
|
203
|
+
builder.beginMultiPolygon(last - first);
|
|
204
|
+
for (let polygonIndex = first; polygonIndex < last; polygonIndex++) {
|
|
205
|
+
const [partFirst, partLast] = getListRange(
|
|
206
|
+
polygons as import('./types').GeoArrowList,
|
|
207
|
+
polygonIndex
|
|
208
|
+
);
|
|
209
|
+
const parts = (polygons as import('./types').GeoArrowList).child;
|
|
210
|
+
builder.beginPolygon(partLast - partFirst);
|
|
211
|
+
for (let partIndex = partFirst; partIndex < partLast; partIndex++) {
|
|
212
|
+
const [ringFirst, ringLast] = getListRange(
|
|
213
|
+
parts as import('./types').GeoArrowList,
|
|
214
|
+
partIndex
|
|
215
|
+
);
|
|
216
|
+
builder.beginLinearRing(ringLast - ringFirst);
|
|
217
|
+
writeSequence((parts as import('./types').GeoArrowList).child, ringFirst, ringLast);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function getPhysicalGeometryDimension(
|
|
224
|
+
array: import('./types').GeoArrowArray,
|
|
225
|
+
rowIndex: number,
|
|
226
|
+
encoding: import('./types').GeoArrowEncoding,
|
|
227
|
+
dimension: GeoArrowDimension
|
|
228
|
+
): GeoArrowDimension {
|
|
229
|
+
if (encoding !== 'geoarrow.geometry' || array.kind !== 'dense-union') return dimension;
|
|
230
|
+
const physical = (array.offset || 0) + rowIndex;
|
|
231
|
+
const child = array.children.find(candidate => candidate.typeId === array.typeIds[physical]);
|
|
232
|
+
return child?.dimension || dimension;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function readPhysicalCoordinate(
|
|
236
|
+
array: import('./types').GeoArrowArray,
|
|
237
|
+
index: number
|
|
238
|
+
): number[] | null {
|
|
239
|
+
if (array.kind === 'fixed-size-list' && array.child.kind === 'primitive') {
|
|
240
|
+
const logical = (array.offset || 0) + index;
|
|
241
|
+
const values: number[] = [];
|
|
242
|
+
for (let component = 0; component < array.size; component++)
|
|
243
|
+
values.push(
|
|
244
|
+
Number(
|
|
245
|
+
array.child.values[
|
|
246
|
+
(array.child.offset || 0) +
|
|
247
|
+
(logical * array.size + component) * (array.child.stride || 1)
|
|
248
|
+
]
|
|
249
|
+
)
|
|
250
|
+
);
|
|
251
|
+
return values;
|
|
252
|
+
}
|
|
253
|
+
if (array.kind === 'struct') {
|
|
254
|
+
const logical = (array.offset || 0) + index;
|
|
255
|
+
return ['x', 'y', 'z', 'm'].flatMap(name => {
|
|
256
|
+
const child = array.children[name];
|
|
257
|
+
return child?.kind === 'primitive'
|
|
258
|
+
? [Number(child.values[(child.offset || 0) + logical * (child.stride || 1)])]
|
|
259
|
+
: [];
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function getListRange(list: import('./types').GeoArrowList, index: number): [number, number] {
|
|
266
|
+
const offset = (list.offset || 0) + index;
|
|
267
|
+
const base =
|
|
268
|
+
typeof (list.offsetBase ?? 0) === 'bigint'
|
|
269
|
+
? Number(list.offsetBase)
|
|
270
|
+
: Number(list.offsetBase ?? 0);
|
|
271
|
+
return [
|
|
272
|
+
getGeoArrowOffset(list.offsets, offset) - base,
|
|
273
|
+
getGeoArrowOffset(list.offsets, offset + 1) - base
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function getEncodingDepth(encoding: import('./types').GeoArrowEncoding): number {
|
|
278
|
+
return encoding === 'geoarrow.point'
|
|
279
|
+
? 0
|
|
280
|
+
: encoding === 'geoarrow.linestring' || encoding === 'geoarrow.multipoint'
|
|
281
|
+
? 1
|
|
282
|
+
: encoding === 'geoarrow.polygon' || encoding === 'geoarrow.multilinestring'
|
|
283
|
+
? 2
|
|
284
|
+
: 3;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function encodingFromName(name: string): import('./types').GeoArrowEncoding {
|
|
288
|
+
return `geoarrow.${name.toLowerCase()}` as import('./types').GeoArrowEncoding;
|
|
289
|
+
}
|
|
290
|
+
|
|
63
291
|
function normalizeRowsToDimension(
|
|
64
292
|
rows: readonly (GeoArrowGeometryValue | null)[],
|
|
65
293
|
dimension: GeoArrowDimension
|
|
@@ -106,6 +334,20 @@ function decodeSerializedRows(
|
|
|
106
334
|
}
|
|
107
335
|
|
|
108
336
|
function getSerializedBytes(array: GeoArrowSerialized, rowIndex: number): Uint8Array {
|
|
337
|
+
if (array.views) {
|
|
338
|
+
const viewIndex = ((array.offset || 0) + rowIndex) * 4;
|
|
339
|
+
const length = array.views[viewIndex];
|
|
340
|
+
// Arrow BinaryView stores short values inline in the 16-byte view record.
|
|
341
|
+
if (length <= 12) {
|
|
342
|
+
const byteOffset = array.views.byteOffset + viewIndex * 4 + 4;
|
|
343
|
+
return new Uint8Array(array.views.buffer, byteOffset, length);
|
|
344
|
+
}
|
|
345
|
+
const bufferIndex = array.views[viewIndex + 2];
|
|
346
|
+
const byteOffset = array.views[viewIndex + 3];
|
|
347
|
+
const data = array.dataBuffers?.[bufferIndex];
|
|
348
|
+
if (!data) throw new Error(`Serialized view references missing data buffer ${bufferIndex}`);
|
|
349
|
+
return data.subarray(byteOffset, byteOffset + length);
|
|
350
|
+
}
|
|
109
351
|
const offsetIndex = (array.offset || 0) + rowIndex;
|
|
110
352
|
const baseValue = array.offsetBase ?? 0;
|
|
111
353
|
const base = typeof baseValue === 'bigint' ? Number(baseValue) : baseValue;
|