@osmix/geoparquet 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +47 -0
  3. package/dist/src/from-geoparquet.d.ts +68 -0
  4. package/dist/src/from-geoparquet.d.ts.map +1 -0
  5. package/dist/src/from-geoparquet.js +455 -0
  6. package/dist/src/from-geoparquet.js.map +1 -0
  7. package/dist/src/index.d.ts +27 -0
  8. package/dist/src/index.d.ts.map +1 -0
  9. package/dist/src/index.js +27 -0
  10. package/dist/src/index.js.map +1 -0
  11. package/dist/src/types.d.ts +47 -0
  12. package/dist/src/types.d.ts.map +1 -0
  13. package/dist/src/types.js +6 -0
  14. package/dist/src/types.js.map +1 -0
  15. package/dist/src/wkb.d.ts +22 -0
  16. package/dist/src/wkb.d.ts.map +1 -0
  17. package/dist/src/wkb.js +181 -0
  18. package/dist/src/wkb.js.map +1 -0
  19. package/dist/test/from-geoparquet.test.d.ts +2 -0
  20. package/dist/test/from-geoparquet.test.d.ts.map +1 -0
  21. package/dist/test/from-geoparquet.test.js +445 -0
  22. package/dist/test/from-geoparquet.test.js.map +1 -0
  23. package/dist/test/monaco-parquet.test.d.ts +2 -0
  24. package/dist/test/monaco-parquet.test.d.ts.map +1 -0
  25. package/dist/test/monaco-parquet.test.js +200 -0
  26. package/dist/test/monaco-parquet.test.js.map +1 -0
  27. package/dist/test/wkb.test.d.ts +2 -0
  28. package/dist/test/wkb.test.d.ts.map +1 -0
  29. package/dist/test/wkb.test.js +234 -0
  30. package/dist/test/wkb.test.js.map +1 -0
  31. package/package.json +53 -0
  32. package/src/from-geoparquet.ts +565 -0
  33. package/src/index.ts +27 -0
  34. package/src/types.ts +51 -0
  35. package/src/wkb.ts +218 -0
  36. package/test/download-monaco-highways.sh +40 -0
  37. package/test/from-geoparquet.test.ts +520 -0
  38. package/test/monaco-parquet.test.ts +249 -0
  39. package/test/wkb.test.ts +296 -0
  40. package/tsconfig.json +9 -0
package/src/wkb.ts ADDED
@@ -0,0 +1,218 @@
1
+ /**
2
+ * WKB (Well-Known Binary) geometry parsing utilities.
3
+ *
4
+ * Browser-compatible WKB parser using DataView instead of Node.js Buffer.
5
+ * Supports standard WKB and EWKB (with SRID) formats.
6
+ *
7
+ * @module
8
+ */
9
+
10
+ import type {
11
+ Geometry,
12
+ GeometryCollection,
13
+ LineString,
14
+ MultiLineString,
15
+ MultiPoint,
16
+ MultiPolygon,
17
+ Point,
18
+ Polygon,
19
+ Position,
20
+ } from "geojson"
21
+
22
+ /** WKB geometry type codes */
23
+ const WKB_POINT = 1
24
+ const WKB_LINESTRING = 2
25
+ const WKB_POLYGON = 3
26
+ const WKB_MULTIPOINT = 4
27
+ const WKB_MULTILINESTRING = 5
28
+ const WKB_MULTIPOLYGON = 6
29
+ const WKB_GEOMETRYCOLLECTION = 7
30
+
31
+ /** EWKB flags */
32
+ const EWKB_SRID_FLAG = 0x20000000
33
+ const EWKB_Z_FLAG = 0x80000000
34
+ const EWKB_M_FLAG = 0x40000000
35
+
36
+ /**
37
+ * Binary reader using DataView for browser compatibility.
38
+ */
39
+ class WkbReader {
40
+ private view: DataView
41
+ private offset = 0
42
+ private littleEndian = true
43
+
44
+ constructor(data: Uint8Array) {
45
+ // Create DataView from the Uint8Array's underlying buffer with correct offset
46
+ this.view = new DataView(data.buffer, data.byteOffset, data.byteLength)
47
+ }
48
+
49
+ readByte(): number {
50
+ const value = this.view.getUint8(this.offset)
51
+ this.offset += 1
52
+ return value
53
+ }
54
+
55
+ readUint32(): number {
56
+ const value = this.view.getUint32(this.offset, this.littleEndian)
57
+ this.offset += 4
58
+ return value
59
+ }
60
+
61
+ readDouble(): number {
62
+ const value = this.view.getFloat64(this.offset, this.littleEndian)
63
+ this.offset += 8
64
+ return value
65
+ }
66
+
67
+ setLittleEndian(littleEndian: boolean): void {
68
+ this.littleEndian = littleEndian
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Parse a WKB geometry into a GeoJSON Geometry object.
74
+ *
75
+ * Browser-compatible implementation using DataView.
76
+ * Supports Point, LineString, Polygon, MultiPoint, MultiLineString,
77
+ * MultiPolygon, and GeometryCollection. Also handles EWKB with SRID.
78
+ *
79
+ * @param wkb - WKB-encoded geometry as Uint8Array
80
+ * @returns Parsed GeoJSON Geometry
81
+ * @throws Error if geometry type is unsupported
82
+ */
83
+ export function parseWkb(wkb: Uint8Array): Geometry {
84
+ const reader = new WkbReader(wkb)
85
+ return parseGeometry(reader)
86
+ }
87
+
88
+ /**
89
+ * Parse a geometry from the reader at current position.
90
+ */
91
+ function parseGeometry(reader: WkbReader): Geometry {
92
+ // Read byte order
93
+ const byteOrder = reader.readByte()
94
+ reader.setLittleEndian(byteOrder === 1)
95
+
96
+ // Read geometry type (may include EWKB flags)
97
+ let geometryType = reader.readUint32()
98
+
99
+ // Handle EWKB SRID flag
100
+ if (geometryType & EWKB_SRID_FLAG) {
101
+ // Skip SRID (4 bytes)
102
+ reader.readUint32()
103
+ geometryType &= ~EWKB_SRID_FLAG
104
+ }
105
+
106
+ // Check for Z/M flags and mask them out
107
+ const hasZ = (geometryType & EWKB_Z_FLAG) !== 0
108
+ const hasM = (geometryType & EWKB_M_FLAG) !== 0
109
+ geometryType &= 0x0000ffff // Keep only the base type
110
+
111
+ // Determine coordinate dimensions
112
+ const dimensions = 2 + (hasZ ? 1 : 0) + (hasM ? 1 : 0)
113
+
114
+ switch (geometryType) {
115
+ case WKB_POINT:
116
+ return parsePoint(reader, dimensions)
117
+ case WKB_LINESTRING:
118
+ return parseLineString(reader, dimensions)
119
+ case WKB_POLYGON:
120
+ return parsePolygon(reader, dimensions)
121
+ case WKB_MULTIPOINT:
122
+ return parseMultiPoint(reader)
123
+ case WKB_MULTILINESTRING:
124
+ return parseMultiLineString(reader)
125
+ case WKB_MULTIPOLYGON:
126
+ return parseMultiPolygon(reader)
127
+ case WKB_GEOMETRYCOLLECTION:
128
+ return parseGeometryCollection(reader)
129
+ default:
130
+ throw new Error(`Unsupported WKB geometry type: ${geometryType}`)
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Read a coordinate (lon, lat, and optionally z/m).
136
+ * Only returns [lon, lat] for GeoJSON compatibility.
137
+ */
138
+ function readCoordinate(reader: WkbReader, dimensions: number): Position {
139
+ const x = reader.readDouble()
140
+ const y = reader.readDouble()
141
+
142
+ // Read and discard extra dimensions (Z, M)
143
+ for (let i = 2; i < dimensions; i++) {
144
+ reader.readDouble()
145
+ }
146
+
147
+ return [x, y]
148
+ }
149
+
150
+ /**
151
+ * Read an array of coordinates.
152
+ */
153
+ function readCoordinates(reader: WkbReader, dimensions: number): Position[] {
154
+ const count = reader.readUint32()
155
+ const coords: Position[] = []
156
+ for (let i = 0; i < count; i++) {
157
+ coords.push(readCoordinate(reader, dimensions))
158
+ }
159
+ return coords
160
+ }
161
+
162
+ function parsePoint(reader: WkbReader, dimensions: number): Point {
163
+ const coordinates = readCoordinate(reader, dimensions)
164
+ return { type: "Point", coordinates }
165
+ }
166
+
167
+ function parseLineString(reader: WkbReader, dimensions: number): LineString {
168
+ const coordinates = readCoordinates(reader, dimensions)
169
+ return { type: "LineString", coordinates }
170
+ }
171
+
172
+ function parsePolygon(reader: WkbReader, dimensions: number): Polygon {
173
+ const numRings = reader.readUint32()
174
+ const coordinates: Position[][] = []
175
+ for (let i = 0; i < numRings; i++) {
176
+ coordinates.push(readCoordinates(reader, dimensions))
177
+ }
178
+ return { type: "Polygon", coordinates }
179
+ }
180
+
181
+ function parseMultiPoint(reader: WkbReader): MultiPoint {
182
+ const numPoints = reader.readUint32()
183
+ const coordinates: Position[] = []
184
+ for (let i = 0; i < numPoints; i++) {
185
+ const point = parseGeometry(reader) as Point
186
+ coordinates.push(point.coordinates)
187
+ }
188
+ return { type: "MultiPoint", coordinates }
189
+ }
190
+
191
+ function parseMultiLineString(reader: WkbReader): MultiLineString {
192
+ const numLineStrings = reader.readUint32()
193
+ const coordinates: Position[][] = []
194
+ for (let i = 0; i < numLineStrings; i++) {
195
+ const lineString = parseGeometry(reader) as LineString
196
+ coordinates.push(lineString.coordinates)
197
+ }
198
+ return { type: "MultiLineString", coordinates }
199
+ }
200
+
201
+ function parseMultiPolygon(reader: WkbReader): MultiPolygon {
202
+ const numPolygons = reader.readUint32()
203
+ const coordinates: Position[][][] = []
204
+ for (let i = 0; i < numPolygons; i++) {
205
+ const polygon = parseGeometry(reader) as Polygon
206
+ coordinates.push(polygon.coordinates)
207
+ }
208
+ return { type: "MultiPolygon", coordinates }
209
+ }
210
+
211
+ function parseGeometryCollection(reader: WkbReader): GeometryCollection {
212
+ const numGeometries = reader.readUint32()
213
+ const geometries: Geometry[] = []
214
+ for (let i = 0; i < numGeometries; i++) {
215
+ geometries.push(parseGeometry(reader))
216
+ }
217
+ return { type: "GeometryCollection", geometries }
218
+ }
@@ -0,0 +1,40 @@
1
+ #!/bin/bash
2
+
3
+ # Download Monaco highway data from LayerCake (OSM US) using DuckDB
4
+ # Monaco bounding box coordinates from MapTiler:
5
+ # - xmin: 7.409205
6
+ # - xmax: 7.448637
7
+ # - ymin: 43.72335
8
+ # - ymax: 43.75169
9
+
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ OUTPUT_DIR="$SCRIPT_DIR/../../../fixtures"
12
+ OUTPUT_FILE="$OUTPUT_DIR/monaco-highways.parquet"
13
+
14
+ echo "Downloading Monaco highway data from LayerCake..."
15
+ echo "Output: $OUTPUT_FILE"
16
+
17
+ duckdb <<EOF
18
+ INSTALL spatial;
19
+ LOAD spatial;
20
+
21
+ COPY (
22
+ FROM 'https://data.openstreetmap.us/layercake/highways.parquet'
23
+ SELECT
24
+ id,
25
+ tags,
26
+ geometry
27
+ WHERE
28
+ bbox.xmin >= 7.409205 AND bbox.xmax <= 7.448637 AND
29
+ bbox.ymin >= 43.72335 AND bbox.ymax <= 43.75169
30
+ ) TO '$OUTPUT_FILE' WITH (FORMAT PARQUET);
31
+ EOF
32
+
33
+ if [ -f "$OUTPUT_FILE" ]; then
34
+ echo "Successfully downloaded Monaco highways to $OUTPUT_FILE"
35
+ echo "File size: $(ls -lh "$OUTPUT_FILE" | awk '{print $5}')"
36
+ else
37
+ echo "Error: Failed to download file"
38
+ exit 1
39
+ fi
40
+