@osmix/core 0.0.2 → 0.1.1

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 (104) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +118 -107
  3. package/dist/src/entities.d.ts +103 -9
  4. package/dist/src/entities.d.ts.map +1 -1
  5. package/dist/src/entities.js +101 -11
  6. package/dist/src/entities.js.map +1 -1
  7. package/dist/src/ids.d.ts +82 -13
  8. package/dist/src/ids.d.ts.map +1 -1
  9. package/dist/src/ids.js +103 -31
  10. package/dist/src/ids.js.map +1 -1
  11. package/dist/src/index.d.ts +31 -5
  12. package/dist/src/index.d.ts.map +1 -1
  13. package/dist/src/index.js +33 -5
  14. package/dist/src/index.js.map +1 -1
  15. package/dist/src/nodes.d.ts +91 -21
  16. package/dist/src/nodes.d.ts.map +1 -1
  17. package/dist/src/nodes.js +191 -44
  18. package/dist/src/nodes.js.map +1 -1
  19. package/dist/src/osm.d.ts +99 -0
  20. package/dist/src/osm.d.ts.map +1 -0
  21. package/dist/src/osm.js +165 -0
  22. package/dist/src/osm.js.map +1 -0
  23. package/dist/src/relations.d.ts +112 -20
  24. package/dist/src/relations.d.ts.map +1 -1
  25. package/dist/src/relations.js +298 -35
  26. package/dist/src/relations.js.map +1 -1
  27. package/dist/src/stringtable.d.ts +78 -14
  28. package/dist/src/stringtable.d.ts.map +1 -1
  29. package/dist/src/stringtable.js +105 -26
  30. package/dist/src/stringtable.js.map +1 -1
  31. package/dist/src/tags.d.ts +117 -18
  32. package/dist/src/tags.d.ts.map +1 -1
  33. package/dist/src/tags.js +177 -30
  34. package/dist/src/tags.js.map +1 -1
  35. package/dist/src/typed-arrays.d.ts +85 -11
  36. package/dist/src/typed-arrays.d.ts.map +1 -1
  37. package/dist/src/typed-arrays.js +88 -9
  38. package/dist/src/typed-arrays.js.map +1 -1
  39. package/dist/src/utils.d.ts +35 -1
  40. package/dist/src/utils.d.ts.map +1 -1
  41. package/dist/src/utils.js +69 -10
  42. package/dist/src/utils.js.map +1 -1
  43. package/dist/src/ways.d.ts +91 -27
  44. package/dist/src/ways.d.ts.map +1 -1
  45. package/dist/src/ways.js +229 -69
  46. package/dist/src/ways.js.map +1 -1
  47. package/dist/test/memory.test.d.ts +7 -0
  48. package/dist/test/memory.test.d.ts.map +1 -0
  49. package/dist/test/memory.test.js +29 -0
  50. package/dist/test/memory.test.js.map +1 -0
  51. package/dist/test/mock-osm.d.ts +3 -3
  52. package/dist/test/mock-osm.d.ts.map +1 -1
  53. package/dist/test/mock-osm.js +3 -3
  54. package/dist/test/mock-osm.js.map +1 -1
  55. package/dist/test/tags-search.test.d.ts +2 -0
  56. package/dist/test/tags-search.test.d.ts.map +1 -0
  57. package/dist/test/tags-search.test.js +56 -0
  58. package/dist/test/tags-search.test.js.map +1 -0
  59. package/dist/test/typed-arrays.test.js +19 -17
  60. package/dist/test/typed-arrays.test.js.map +1 -1
  61. package/package.json +11 -13
  62. package/src/entities.ts +147 -22
  63. package/src/ids.ts +126 -45
  64. package/src/index.ts +34 -5
  65. package/src/nodes.ts +248 -58
  66. package/src/osm.ts +222 -0
  67. package/src/relations.ts +396 -54
  68. package/src/stringtable.ts +123 -36
  69. package/src/tags.ts +220 -46
  70. package/src/typed-arrays.ts +97 -10
  71. package/src/types/geoflatbush.d.ts +37 -0
  72. package/src/types/geokdbush.d.ts +37 -0
  73. package/src/types/kdbush.d.ts +24 -6
  74. package/src/utils.ts +80 -14
  75. package/src/ways.ts +260 -84
  76. package/test/memory.test.ts +38 -0
  77. package/test/mock-osm.ts +8 -8
  78. package/test/tags-search.test.ts +60 -0
  79. package/test/typed-arrays.test.ts +5 -6
  80. package/dist/src/osmix.d.ts +0 -88
  81. package/dist/src/osmix.d.ts.map +0 -1
  82. package/dist/src/osmix.js +0 -438
  83. package/dist/src/osmix.js.map +0 -1
  84. package/dist/test/extract.bench.d.ts +0 -2
  85. package/dist/test/extract.bench.d.ts.map +0 -1
  86. package/dist/test/extract.bench.js +0 -23
  87. package/dist/test/extract.bench.js.map +0 -1
  88. package/dist/test/extract.test.d.ts +0 -2
  89. package/dist/test/extract.test.d.ts.map +0 -1
  90. package/dist/test/extract.test.js +0 -127
  91. package/dist/test/extract.test.js.map +0 -1
  92. package/dist/test/read.test.d.ts +0 -2
  93. package/dist/test/read.test.d.ts.map +0 -1
  94. package/dist/test/read.test.js +0 -17
  95. package/dist/test/read.test.js.map +0 -1
  96. package/dist/test/write.test.d.ts +0 -2
  97. package/dist/test/write.test.d.ts.map +0 -1
  98. package/dist/test/write.test.js +0 -55
  99. package/dist/test/write.test.js.map +0 -1
  100. package/src/osmix.ts +0 -556
  101. package/test/extract.bench.ts +0 -29
  102. package/test/extract.test.ts +0 -161
  103. package/test/read.test.ts +0 -22
  104. package/test/write.test.ts +0 -69
@@ -1,12 +1,28 @@
1
+ /**
2
+ * Resizable typed array utilities.
3
+ *
4
+ * Wraps typed arrays with automatic buffer expansion (ArrayList semantics).
5
+ * Uses SharedArrayBuffer (if available) for zero-copy worker transfer.
6
+ *
7
+ * @module
8
+ */
9
+
1
10
  /**
2
11
  * Use SharedArrayBuffer if the runtime supports it, otherwise fall back to ArrayBuffer.
12
+ * SharedArrayBuffer enables zero-copy transfer between workers.
3
13
  */
4
14
  export const BufferConstructor =
5
15
  typeof SharedArrayBuffer !== "undefined"
6
16
  ? (SharedArrayBuffer as SharedArrayBufferConstructor)
7
17
  : (ArrayBuffer as ArrayBufferConstructor)
18
+
19
+ /** The buffer type used by this runtime (SharedArrayBuffer or ArrayBuffer). */
8
20
  export type BufferType = InstanceType<typeof BufferConstructor>
9
21
 
22
+ /**
23
+ * Union of all standard typed array types.
24
+ * Generic over buffer type to preserve SharedArrayBuffer compatibility.
25
+ */
10
26
  export type TypedArray<B extends BufferType = BufferType> =
11
27
  | Int8Array<B>
12
28
  | Uint8Array<B>
@@ -18,6 +34,9 @@ export type TypedArray<B extends BufferType = BufferType> =
18
34
  | Float32Array<B>
19
35
  | Float64Array<B>
20
36
 
37
+ /**
38
+ * Constructor interface for typed arrays.
39
+ */
21
40
  export interface TypedArrayConstructor<
22
41
  T extends TypedArray<BufferType> = TypedArray<BufferType>,
23
42
  > {
@@ -26,37 +45,63 @@ export interface TypedArrayConstructor<
26
45
  }
27
46
 
28
47
  /**
29
- * OSM IDs can be stored as 64-bit floating point numbers.
48
+ * Float64Array for storing OSM IDs.
49
+ *
50
+ * OSM IDs are 64-bit integers. JavaScript's number type (IEEE 754 double)
51
+ * can exactly represent integers up to 2^53, which covers all current OSM IDs.
52
+ * Float64Array allows typed array operations while maintaining precision.
30
53
  */
31
54
  export const IdArrayType = Float64Array
32
55
 
33
56
  /**
34
- * When we are storing coordinates, we need to be able to store 64-bit floating point numbers.
35
- * However, for benchmarking it is handy to test 32-bit floating point numbers.
36
- */
37
- export const CoordinateArrayType = Float64Array
38
-
39
- /**
40
- * When we are storing indexes into other arrays, we never need an index to exceed 2^32.
57
+ * Uint32Array for storing array indices.
58
+ *
59
+ * Internal indices into typed arrays never exceed 2^32 elements,
60
+ * so Uint32Array provides the best balance of range and memory efficiency.
41
61
  */
42
62
  export const IndexArrayType = Uint32Array
43
63
 
44
64
  /**
45
- * Default buffer size.
65
+ * Initial buffer size for ResizeableTypedArray.
66
+ * 1 MiB provides reasonable initial capacity while avoiding excessive memory allocation.
46
67
  */
47
68
  export const DEFAULT_BUFFER_SIZE = 2 ** 20 // 1 MiB
48
69
 
70
+ /**
71
+ * Auto-expanding typed array wrapper.
72
+ *
73
+ * - `push()` appends elements, doubling buffer size as needed.
74
+ * - `compact()` shrinks buffer to fit data.
75
+ * - Supports growable SharedArrayBuffer and resizable ArrayBuffer.
76
+ */
49
77
  export class ResizeableTypedArray<TA extends TypedArray> {
78
+ /** The typed array constructor for this instance */
50
79
  ArrayType: TypedArrayConstructor<TA>
80
+ /** The current typed array view into the buffer */
51
81
  array: TA
82
+ /** Number of items actually stored (may be less than array.length) */
52
83
  items = 0
53
84
 
85
+ /** The underlying ArrayBuffer or SharedArrayBuffer */
54
86
  buffer: BufferType
87
+ /** Current buffer size in bytes */
55
88
  bufferSize: number
89
+ /** Maximum byte length for growable buffers */
56
90
  maxByteLength: number
57
91
 
92
+ /** Buffer constructor (SharedArrayBuffer or ArrayBuffer) */
58
93
  BC: SharedArrayBufferConstructor | ArrayBufferConstructor
59
94
 
95
+ /**
96
+ * Reconstruct a ResizeableTypedArray from an existing buffer.
97
+ *
98
+ * Used after transferring buffers between workers. The resulting array
99
+ * is considered "compacted" with items = array.length.
100
+ *
101
+ * @param ArrayType - The typed array constructor.
102
+ * @param buffer - The existing buffer to wrap.
103
+ * @returns A new ResizeableTypedArray wrapping the buffer.
104
+ */
60
105
  static from<TA extends TypedArray>(
61
106
  ArrayType: TypedArrayConstructor<TA>,
62
107
  buffer: BufferType,
@@ -71,6 +116,12 @@ export class ResizeableTypedArray<TA extends TypedArray> {
71
116
  return rta
72
117
  }
73
118
 
119
+ /**
120
+ * Create a new ResizeableTypedArray with an empty buffer.
121
+ *
122
+ * @param ArrayType - The typed array constructor (e.g., Float64Array).
123
+ * @param BC - Buffer constructor to use (defaults to SharedArrayBuffer if available).
124
+ */
74
125
  constructor(
75
126
  ArrayType: TypedArrayConstructor<TA>,
76
127
  BC:
@@ -87,13 +138,21 @@ export class ResizeableTypedArray<TA extends TypedArray> {
87
138
  this.array = new this.ArrayType(this.buffer)
88
139
  }
89
140
 
141
+ /**
142
+ * Iterate over the array values.
143
+ */
90
144
  [Symbol.iterator](): ArrayIterator<number> {
91
145
  return this.array[Symbol.iterator]()
92
146
  }
93
147
 
148
+ /**
149
+ * Double the buffer capacity.
150
+ * Uses in-place grow/resize if supported, otherwise allocates new buffer and copies.
151
+ */
94
152
  expandArray() {
95
153
  this.bufferSize *= 2
96
154
  if (this.bufferSize > this.buffer.maxByteLength) {
155
+ // Need a completely new buffer with larger maxByteLength
97
156
  this.maxByteLength *= 2
98
157
  const newBuffer = new this.BC(this.bufferSize, {
99
158
  maxByteLength: this.maxByteLength,
@@ -103,6 +162,7 @@ export class ResizeableTypedArray<TA extends TypedArray> {
103
162
  this.buffer = newBuffer
104
163
  this.array = newArray
105
164
  } else {
165
+ // Can grow/resize the existing buffer in place
106
166
  if (this.buffer instanceof SharedArrayBuffer && this.buffer.growable) {
107
167
  this.buffer.grow(this.bufferSize)
108
168
  } else if (this.buffer instanceof ArrayBuffer && this.buffer.resizable) {
@@ -114,7 +174,7 @@ export class ResizeableTypedArray<TA extends TypedArray> {
114
174
  }
115
175
 
116
176
  /**
117
- * Handles negative numbers
177
+ * Get the value at an index. Handles negative indices.
118
178
  */
119
179
  at(index: number): number {
120
180
  if (index < -this.length || index >= this.length)
@@ -125,6 +185,9 @@ export class ResizeableTypedArray<TA extends TypedArray> {
125
185
  return result
126
186
  }
127
187
 
188
+ /**
189
+ * Get a slice of the array.
190
+ */
128
191
  slice(start: number, end: number) {
129
192
  return this.array.slice(start, end)
130
193
  }
@@ -133,6 +196,9 @@ export class ResizeableTypedArray<TA extends TypedArray> {
133
196
  return this.items
134
197
  }
135
198
 
199
+ /**
200
+ * Push a value to the end of the array.
201
+ */
136
202
  push(value: number): number {
137
203
  if (this.length >= this.array.length) {
138
204
  this.expandArray()
@@ -141,19 +207,40 @@ export class ResizeableTypedArray<TA extends TypedArray> {
141
207
  return this.length - 1
142
208
  }
143
209
 
210
+ /**
211
+ * Set a value at a specific index. Expands array if needed.
212
+ */
213
+ set(index: number, value: number) {
214
+ if (index < 0) throw Error("Index out of bounds")
215
+ if (index >= this.length) {
216
+ while (index >= this.array.length) this.expandArray()
217
+ this.items = index + 1
218
+ }
219
+ this.array[index] = value
220
+ }
221
+
222
+ /**
223
+ * Push multiple values to the end of the array.
224
+ */
144
225
  pushMany(values: number[] | TypedArray) {
145
226
  while (this.length + values.length > this.array.length) this.expandArray()
146
227
  this.array.set(values, this.length)
147
228
  this.items += values.length
148
229
  }
149
230
 
231
+ /**
232
+ * Shrink the buffer to exactly fit stored items.
233
+ * Buffer becomes fixed-length after compacting.
234
+ */
150
235
  compact() {
151
236
  if (this.buffer instanceof SharedArrayBuffer) {
237
+ // SharedArrayBuffer uses slice() to create a new fixed-size buffer
152
238
  this.buffer = this.buffer.slice(
153
239
  0,
154
240
  this.length * this.ArrayType.BYTES_PER_ELEMENT,
155
241
  )
156
242
  } else {
243
+ // ArrayBuffer uses transferToFixedLength() to detach and resize
157
244
  this.buffer = this.buffer.transferToFixedLength(
158
245
  this.length * this.ArrayType.BYTES_PER_ELEMENT,
159
246
  )
@@ -0,0 +1,37 @@
1
+ declare module "geoflatbush" {
2
+ import type Flatbush from "flatbush"
3
+
4
+ /**
5
+ * Returns an array of the closest items from a given location in order of increasing distance.
6
+ * @param index - A Flatbush index with geographic bounding boxes in degrees (minLng, minLat, maxLng, maxLat).
7
+ * @param lng - Query point longitude in degrees.
8
+ * @param lat - Query point latitude in degrees.
9
+ * @param maxResults - Maximum number of items to return (default: Infinity).
10
+ * @param maxDistance - Maximum distance in kilometers (default: Infinity).
11
+ * @param filterFn - Optional filter function called with index of each item.
12
+ * @returns Array of item indices from the index, sorted by distance.
13
+ */
14
+ export function around(
15
+ index: Flatbush,
16
+ lng: number,
17
+ lat: number,
18
+ maxResults?: number,
19
+ maxDistance?: number,
20
+ filterFn?: (index: number) => boolean,
21
+ ): number[]
22
+
23
+ /**
24
+ * Returns the great circle distance between two locations in kilometers.
25
+ * @param lng1 - First point longitude in degrees.
26
+ * @param lat1 - First point latitude in degrees.
27
+ * @param lng2 - Second point longitude in degrees.
28
+ * @param lat2 - Second point latitude in degrees.
29
+ * @returns Distance in kilometers.
30
+ */
31
+ export function distance(
32
+ lng: number,
33
+ lat: number,
34
+ lng2: number,
35
+ lat2: number,
36
+ ): number
37
+ }
@@ -0,0 +1,37 @@
1
+ declare module "geokdbush" {
2
+ import type KDBush from "kdbush"
3
+
4
+ /**
5
+ * Returns an array of the closest points from a given location in order of increasing distance.
6
+ * @param index - A KDBush index with geographic coordinates (lng, lat) in degrees.
7
+ * @param lng - Query point longitude in degrees.
8
+ * @param lat - Query point latitude in degrees.
9
+ * @param maxResults - Maximum number of points to return (default: Infinity).
10
+ * @param maxDistance - Maximum distance in kilometers (default: Infinity).
11
+ * @param predicate - Optional filter function called with index of each point.
12
+ * @returns Array of point indices from the index, sorted by distance.
13
+ */
14
+ export function around(
15
+ index: KDBush,
16
+ lng: number,
17
+ lat: number,
18
+ maxResults?: number,
19
+ maxDistance?: number,
20
+ predicate?: (index: number) => boolean,
21
+ ): number[]
22
+
23
+ /**
24
+ * Returns the great circle distance between two locations in kilometers.
25
+ * @param lng1 - First point longitude in degrees.
26
+ * @param lat1 - First point latitude in degrees.
27
+ * @param lng2 - Second point longitude in degrees.
28
+ * @param lat2 - Second point latitude in degrees.
29
+ * @returns Distance in kilometers.
30
+ */
31
+ export function distance(
32
+ lng1: number,
33
+ lat1: number,
34
+ lng2: number,
35
+ lat2: number,
36
+ ): number
37
+ }
@@ -1,21 +1,39 @@
1
1
  declare module "kdbush" {
2
- import type { BufferConstructor, BufferType } from "../typed-arrays"
2
+ import type {
3
+ BufferConstructor,
4
+ BufferType,
5
+ TypedArray,
6
+ TypedArrayConstructor,
7
+ } from "../typed-arrays"
8
+
3
9
  export default class KDBush {
4
10
  constructor(
5
11
  length: number,
6
- nodeSize: number,
7
- ArrayType: Float64ArrayConstructor,
8
- BufferConstructor: BufferConstructor,
12
+ nodeSize?: number,
13
+ ArrayType?: TypedArrayConstructor,
14
+ BufferConstructor?: BufferConstructor,
9
15
  )
10
16
 
17
+ /** Number of items in the index */
18
+ numItems: number
19
+
20
+ /** Size of the KD-tree node */
21
+ nodeSize: number
22
+
11
23
  /** Serialized index data buffer */
12
24
  data: BufferType
13
25
 
26
+ /** Internal array of indices (used by geokdbush) */
27
+ ids: Uint16Array | Uint32Array
28
+
29
+ /** Internal array of coordinates [x0, y0, x1, y1, ...] (used by geokdbush) */
30
+ coords: TypedArray
31
+
14
32
  /** Add a point before calling finish */
15
- add(x: number, y: number): void
33
+ add(x: number, y: number): number
16
34
 
17
35
  /** Finalize the index after adding all points */
18
- finish(): void
36
+ finish(): this
19
37
 
20
38
  /** Query points within a bounding box; returns internal point indexes */
21
39
  range(minX: number, minY: number, maxX: number, maxY: number): number[]
package/src/utils.ts CHANGED
@@ -1,19 +1,14 @@
1
- import type { GeoBbox2D, LonLat } from "@osmix/shared/types"
1
+ /**
2
+ * Utility functions for @osmix/core.
3
+ * @module
4
+ */
2
5
 
3
- export function throttle<T extends unknown[]>(
4
- func: (...args: T) => void,
5
- timeFrame: number,
6
- ) {
7
- let lastTime = 0
8
- return (...args: T) => {
9
- const now = Date.now()
10
- if (now - lastTime >= timeFrame) {
11
- func(...args)
12
- lastTime = now
13
- }
14
- }
15
- }
6
+ import type { GeoBbox2D, LonLat } from "@osmix/shared/types"
16
7
 
8
+ /**
9
+ * Compute the bounding box of a set of coordinates.
10
+ * Returns `[minLon, minLat, maxLon, maxLat]`.
11
+ */
17
12
  export function bboxFromLonLats(lonLats: LonLat[]): GeoBbox2D {
18
13
  let minLon = Number.POSITIVE_INFINITY
19
14
  let minLat = Number.POSITIVE_INFINITY
@@ -27,3 +22,74 @@ export function bboxFromLonLats(lonLats: LonLat[]): GeoBbox2D {
27
22
  }
28
23
  return [minLon, minLat, maxLon, maxLat]
29
24
  }
25
+
26
+ // FNV-1a hash constants (32-bit)
27
+ const FNV_OFFSET_BASIS = 2166136261
28
+ const FNV_PRIME = 16777619
29
+
30
+ /**
31
+ * FNV-1a hash implementation for Uint8Array.
32
+ * Fast, non-cryptographic hash suitable for content comparison.
33
+ */
34
+ function fnv1aHash(data: Uint8Array, initialHash = FNV_OFFSET_BASIS): number {
35
+ let hash = initialHash
36
+ for (let i = 0; i < data.length; i++) {
37
+ hash ^= data[i] as number
38
+ hash = Math.imul(hash, FNV_PRIME) >>> 0
39
+ }
40
+ return hash >>> 0
41
+ }
42
+
43
+ /**
44
+ * Hash state that can be incrementally updated with multiple buffers.
45
+ */
46
+ export class ContentHasher {
47
+ private hash = FNV_OFFSET_BASIS
48
+
49
+ /**
50
+ * Update the hash with a typed array's underlying bytes.
51
+ */
52
+ update(data: ArrayBufferView | ArrayBuffer): this {
53
+ const buffer = data instanceof ArrayBuffer ? data : data.buffer
54
+ const bytes = new Uint8Array(buffer)
55
+ this.hash = fnv1aHash(bytes, this.hash)
56
+ return this
57
+ }
58
+
59
+ /**
60
+ * Update the hash with a number (as 8 bytes).
61
+ */
62
+ updateNumber(n: number): this {
63
+ const buffer = new ArrayBuffer(8)
64
+ new Float64Array(buffer)[0] = n
65
+ return this.update(buffer)
66
+ }
67
+
68
+ /**
69
+ * Get the final hash as a hex string.
70
+ */
71
+ digest(): string {
72
+ return this.hash.toString(16).padStart(8, "0")
73
+ }
74
+
75
+ /**
76
+ * Get the raw hash value.
77
+ */
78
+ digestNumber(): number {
79
+ return this.hash
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create a content hash from multiple typed arrays.
85
+ * Returns a hex string suitable for use as a content identifier.
86
+ */
87
+ export function hashBuffers(
88
+ ...buffers: (ArrayBufferView | ArrayBuffer)[]
89
+ ): string {
90
+ const hasher = new ContentHasher()
91
+ for (const buffer of buffers) {
92
+ hasher.update(buffer)
93
+ }
94
+ return hasher.digest()
95
+ }