@osmix/core 0.0.2 → 0.0.6

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 (165) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +118 -107
  3. package/dist/core/src/entities.d.ts +114 -0
  4. package/dist/core/src/entities.d.ts.map +1 -0
  5. package/dist/core/src/entities.js +152 -0
  6. package/dist/core/src/entities.js.map +1 -0
  7. package/dist/core/src/ids.d.ts +113 -0
  8. package/dist/core/src/ids.d.ts.map +1 -0
  9. package/dist/{src → core/src}/ids.js +96 -31
  10. package/dist/core/src/ids.js.map +1 -0
  11. package/dist/core/src/index.d.ts +32 -0
  12. package/dist/core/src/index.d.ts.map +1 -0
  13. package/dist/core/src/index.js +35 -0
  14. package/dist/core/src/index.js.map +1 -0
  15. package/dist/core/src/nodes.d.ts +101 -0
  16. package/dist/core/src/nodes.d.ts.map +1 -0
  17. package/dist/core/src/nodes.js +309 -0
  18. package/dist/core/src/nodes.js.map +1 -0
  19. package/dist/core/src/osm.d.ts +74 -0
  20. package/dist/core/src/osm.d.ts.map +1 -0
  21. package/dist/core/src/osm.js +120 -0
  22. package/dist/core/src/osm.js.map +1 -0
  23. package/dist/core/src/relations.d.ts +118 -0
  24. package/dist/core/src/relations.d.ts.map +1 -0
  25. package/dist/core/src/relations.js +412 -0
  26. package/dist/core/src/relations.js.map +1 -0
  27. package/dist/core/src/stringtable.d.ts +95 -0
  28. package/dist/core/src/stringtable.d.ts.map +1 -0
  29. package/dist/core/src/stringtable.js +170 -0
  30. package/dist/core/src/stringtable.js.map +1 -0
  31. package/dist/core/src/tags.d.ts +126 -0
  32. package/dist/core/src/tags.d.ts.map +1 -0
  33. package/dist/core/src/tags.js +228 -0
  34. package/dist/core/src/tags.js.map +1 -0
  35. package/dist/core/src/typed-arrays.d.ts +124 -0
  36. package/dist/core/src/typed-arrays.d.ts.map +1 -0
  37. package/dist/{src → core/src}/typed-arrays.js +88 -9
  38. package/dist/core/src/typed-arrays.js.map +1 -0
  39. package/dist/core/src/utils.d.ts +11 -0
  40. package/dist/core/src/utils.d.ts.map +1 -0
  41. package/dist/{src → core/src}/utils.js +8 -10
  42. package/dist/core/src/utils.js.map +1 -0
  43. package/dist/core/src/ways.d.ts +101 -0
  44. package/dist/core/src/ways.d.ts.map +1 -0
  45. package/dist/core/src/ways.js +336 -0
  46. package/dist/core/src/ways.js.map +1 -0
  47. package/dist/core/test/memory.test.d.ts +7 -0
  48. package/dist/core/test/memory.test.d.ts.map +1 -0
  49. package/dist/core/test/memory.test.js +29 -0
  50. package/dist/core/test/memory.test.js.map +1 -0
  51. package/dist/{test → core/test}/mock-osm.d.ts +3 -3
  52. package/dist/core/test/mock-osm.d.ts.map +1 -0
  53. package/dist/{test → core/test}/mock-osm.js +3 -3
  54. package/dist/core/test/mock-osm.js.map +1 -0
  55. package/dist/core/test/tags-search.test.d.ts +2 -0
  56. package/dist/core/test/tags-search.test.d.ts.map +1 -0
  57. package/dist/core/test/tags-search.test.js +56 -0
  58. package/dist/core/test/tags-search.test.js.map +1 -0
  59. package/dist/core/test/typed-arrays.test.d.ts.map +1 -0
  60. package/dist/{test → core/test}/typed-arrays.test.js +19 -17
  61. package/dist/core/test/typed-arrays.test.js.map +1 -0
  62. package/dist/osmix/src/extract.d.ts +43 -0
  63. package/dist/osmix/src/extract.d.ts.map +1 -0
  64. package/dist/osmix/src/extract.js +156 -0
  65. package/dist/osmix/src/extract.js.map +1 -0
  66. package/dist/osmix/src/pbf.d.ts +55 -0
  67. package/dist/osmix/src/pbf.d.ts.map +1 -0
  68. package/dist/osmix/src/pbf.js +187 -0
  69. package/dist/osmix/src/pbf.js.map +1 -0
  70. package/dist/osmix/src/utils.d.ts +36 -0
  71. package/dist/osmix/src/utils.d.ts.map +1 -0
  72. package/dist/osmix/src/utils.js +108 -0
  73. package/dist/osmix/src/utils.js.map +1 -0
  74. package/dist/osmix/test/extract.bench.d.ts.map +1 -0
  75. package/dist/{test → osmix/test}/extract.bench.js +8 -5
  76. package/dist/osmix/test/extract.bench.js.map +1 -0
  77. package/dist/osmix/test/extract.test.d.ts.map +1 -0
  78. package/dist/osmix/test/extract.test.js +392 -0
  79. package/dist/osmix/test/extract.test.js.map +1 -0
  80. package/package.json +10 -12
  81. package/src/entities.ts +138 -22
  82. package/src/ids.ts +117 -45
  83. package/src/index.ts +34 -5
  84. package/src/nodes.ts +237 -58
  85. package/src/osm.ts +174 -0
  86. package/src/relations.ts +383 -54
  87. package/src/stringtable.ts +111 -36
  88. package/src/tags.ts +207 -46
  89. package/src/typed-arrays.ts +97 -10
  90. package/src/types/geoflatbush.d.ts +37 -0
  91. package/src/types/geokdbush.d.ts +37 -0
  92. package/src/types/kdbush.d.ts +24 -6
  93. package/src/utils.ts +9 -14
  94. package/src/ways.ts +248 -84
  95. package/test/memory.test.ts +38 -0
  96. package/test/mock-osm.ts +8 -8
  97. package/test/tags-search.test.ts +60 -0
  98. package/test/typed-arrays.test.ts +5 -6
  99. package/tsconfig.json +7 -1
  100. package/dist/src/entities.d.ts +0 -26
  101. package/dist/src/entities.d.ts.map +0 -1
  102. package/dist/src/entities.js +0 -69
  103. package/dist/src/entities.js.map +0 -1
  104. package/dist/src/ids.d.ts +0 -50
  105. package/dist/src/ids.d.ts.map +0 -1
  106. package/dist/src/ids.js.map +0 -1
  107. package/dist/src/index.d.ts +0 -6
  108. package/dist/src/index.d.ts.map +0 -1
  109. package/dist/src/index.js +0 -7
  110. package/dist/src/index.js.map +0 -1
  111. package/dist/src/nodes.d.ts +0 -36
  112. package/dist/src/nodes.d.ts.map +0 -1
  113. package/dist/src/nodes.js +0 -171
  114. package/dist/src/nodes.js.map +0 -1
  115. package/dist/src/osmix.d.ts +0 -88
  116. package/dist/src/osmix.d.ts.map +0 -1
  117. package/dist/src/osmix.js +0 -438
  118. package/dist/src/osmix.js.map +0 -1
  119. package/dist/src/relations.d.ts +0 -31
  120. package/dist/src/relations.d.ts.map +0 -1
  121. package/dist/src/relations.js +0 -161
  122. package/dist/src/relations.js.map +0 -1
  123. package/dist/src/stringtable.d.ts +0 -37
  124. package/dist/src/stringtable.d.ts.map +0 -1
  125. package/dist/src/stringtable.js +0 -101
  126. package/dist/src/stringtable.js.map +0 -1
  127. package/dist/src/tags.d.ts +0 -33
  128. package/dist/src/tags.d.ts.map +0 -1
  129. package/dist/src/tags.js +0 -92
  130. package/dist/src/tags.js.map +0 -1
  131. package/dist/src/typed-arrays.d.ts +0 -50
  132. package/dist/src/typed-arrays.d.ts.map +0 -1
  133. package/dist/src/typed-arrays.js.map +0 -1
  134. package/dist/src/utils.d.ts +0 -4
  135. package/dist/src/utils.d.ts.map +0 -1
  136. package/dist/src/utils.js.map +0 -1
  137. package/dist/src/ways.d.ts +0 -42
  138. package/dist/src/ways.d.ts.map +0 -1
  139. package/dist/src/ways.js +0 -186
  140. package/dist/src/ways.js.map +0 -1
  141. package/dist/test/extract.bench.d.ts.map +0 -1
  142. package/dist/test/extract.bench.js.map +0 -1
  143. package/dist/test/extract.test.d.ts.map +0 -1
  144. package/dist/test/extract.test.js +0 -127
  145. package/dist/test/extract.test.js.map +0 -1
  146. package/dist/test/mock-osm.d.ts.map +0 -1
  147. package/dist/test/mock-osm.js.map +0 -1
  148. package/dist/test/read.test.d.ts +0 -2
  149. package/dist/test/read.test.d.ts.map +0 -1
  150. package/dist/test/read.test.js +0 -17
  151. package/dist/test/read.test.js.map +0 -1
  152. package/dist/test/typed-arrays.test.d.ts.map +0 -1
  153. package/dist/test/typed-arrays.test.js.map +0 -1
  154. package/dist/test/write.test.d.ts +0 -2
  155. package/dist/test/write.test.d.ts.map +0 -1
  156. package/dist/test/write.test.js +0 -55
  157. package/dist/test/write.test.js.map +0 -1
  158. package/src/osmix.ts +0 -556
  159. package/test/extract.bench.ts +0 -29
  160. package/test/extract.test.ts +0 -161
  161. package/test/read.test.ts +0 -22
  162. package/test/write.test.ts +0 -69
  163. /package/dist/{test → core/test}/typed-arrays.test.d.ts +0 -0
  164. /package/dist/{test → osmix/test}/extract.bench.d.ts +0 -0
  165. /package/dist/{test → osmix/test}/extract.test.d.ts +0 -0
@@ -1,34 +1,70 @@
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
+ */
1
9
  /**
2
10
  * Use SharedArrayBuffer if the runtime supports it, otherwise fall back to ArrayBuffer.
11
+ * SharedArrayBuffer enables zero-copy transfer between workers.
3
12
  */
4
13
  export const BufferConstructor = typeof SharedArrayBuffer !== "undefined"
5
14
  ? SharedArrayBuffer
6
15
  : ArrayBuffer;
7
16
  /**
8
- * OSM IDs can be stored as 64-bit floating point numbers.
17
+ * Float64Array for storing OSM IDs.
18
+ *
19
+ * OSM IDs are 64-bit integers. JavaScript's number type (IEEE 754 double)
20
+ * can exactly represent integers up to 2^53, which covers all current OSM IDs.
21
+ * Float64Array allows typed array operations while maintaining precision.
9
22
  */
10
23
  export const IdArrayType = Float64Array;
11
24
  /**
12
- * When we are storing coordinates, we need to be able to store 64-bit floating point numbers.
13
- * However, for benchmarking it is handy to test 32-bit floating point numbers.
14
- */
15
- export const CoordinateArrayType = Float64Array;
16
- /**
17
- * When we are storing indexes into other arrays, we never need an index to exceed 2^32.
25
+ * Uint32Array for storing array indices.
26
+ *
27
+ * Internal indices into typed arrays never exceed 2^32 elements,
28
+ * so Uint32Array provides the best balance of range and memory efficiency.
18
29
  */
19
30
  export const IndexArrayType = Uint32Array;
20
31
  /**
21
- * Default buffer size.
32
+ * Initial buffer size for ResizeableTypedArray.
33
+ * 1 MiB provides reasonable initial capacity while avoiding excessive memory allocation.
22
34
  */
23
35
  export const DEFAULT_BUFFER_SIZE = 2 ** 20; // 1 MiB
36
+ /**
37
+ * Auto-expanding typed array wrapper.
38
+ *
39
+ * - `push()` appends elements, doubling buffer size as needed.
40
+ * - `compact()` shrinks buffer to fit data.
41
+ * - Supports growable SharedArrayBuffer and resizable ArrayBuffer.
42
+ */
24
43
  export class ResizeableTypedArray {
44
+ /** The typed array constructor for this instance */
25
45
  ArrayType;
46
+ /** The current typed array view into the buffer */
26
47
  array;
48
+ /** Number of items actually stored (may be less than array.length) */
27
49
  items = 0;
50
+ /** The underlying ArrayBuffer or SharedArrayBuffer */
28
51
  buffer;
52
+ /** Current buffer size in bytes */
29
53
  bufferSize;
54
+ /** Maximum byte length for growable buffers */
30
55
  maxByteLength;
56
+ /** Buffer constructor (SharedArrayBuffer or ArrayBuffer) */
31
57
  BC;
58
+ /**
59
+ * Reconstruct a ResizeableTypedArray from an existing buffer.
60
+ *
61
+ * Used after transferring buffers between workers. The resulting array
62
+ * is considered "compacted" with items = array.length.
63
+ *
64
+ * @param ArrayType - The typed array constructor.
65
+ * @param buffer - The existing buffer to wrap.
66
+ * @returns A new ResizeableTypedArray wrapping the buffer.
67
+ */
32
68
  static from(ArrayType, buffer) {
33
69
  const rta = new ResizeableTypedArray(ArrayType, buffer instanceof SharedArrayBuffer ? SharedArrayBuffer : ArrayBuffer);
34
70
  rta.buffer = buffer;
@@ -36,6 +72,12 @@ export class ResizeableTypedArray {
36
72
  rta.items = rta.array.length;
37
73
  return rta;
38
74
  }
75
+ /**
76
+ * Create a new ResizeableTypedArray with an empty buffer.
77
+ *
78
+ * @param ArrayType - The typed array constructor (e.g., Float64Array).
79
+ * @param BC - Buffer constructor to use (defaults to SharedArrayBuffer if available).
80
+ */
39
81
  constructor(ArrayType, BC = BufferConstructor) {
40
82
  this.ArrayType = ArrayType;
41
83
  this.bufferSize = DEFAULT_BUFFER_SIZE;
@@ -46,12 +88,20 @@ export class ResizeableTypedArray {
46
88
  });
47
89
  this.array = new this.ArrayType(this.buffer);
48
90
  }
91
+ /**
92
+ * Iterate over the array values.
93
+ */
49
94
  [Symbol.iterator]() {
50
95
  return this.array[Symbol.iterator]();
51
96
  }
97
+ /**
98
+ * Double the buffer capacity.
99
+ * Uses in-place grow/resize if supported, otherwise allocates new buffer and copies.
100
+ */
52
101
  expandArray() {
53
102
  this.bufferSize *= 2;
54
103
  if (this.bufferSize > this.buffer.maxByteLength) {
104
+ // Need a completely new buffer with larger maxByteLength
55
105
  this.maxByteLength *= 2;
56
106
  const newBuffer = new this.BC(this.bufferSize, {
57
107
  maxByteLength: this.maxByteLength,
@@ -62,6 +112,7 @@ export class ResizeableTypedArray {
62
112
  this.array = newArray;
63
113
  }
64
114
  else {
115
+ // Can grow/resize the existing buffer in place
65
116
  if (this.buffer instanceof SharedArrayBuffer && this.buffer.growable) {
66
117
  this.buffer.grow(this.bufferSize);
67
118
  }
@@ -74,7 +125,7 @@ export class ResizeableTypedArray {
74
125
  }
75
126
  }
76
127
  /**
77
- * Handles negative numbers
128
+ * Get the value at an index. Handles negative indices.
78
129
  */
79
130
  at(index) {
80
131
  if (index < -this.length || index >= this.length)
@@ -86,12 +137,18 @@ export class ResizeableTypedArray {
86
137
  throw Error(`No value at index: ${index}`);
87
138
  return result;
88
139
  }
140
+ /**
141
+ * Get a slice of the array.
142
+ */
89
143
  slice(start, end) {
90
144
  return this.array.slice(start, end);
91
145
  }
92
146
  get length() {
93
147
  return this.items;
94
148
  }
149
+ /**
150
+ * Push a value to the end of the array.
151
+ */
95
152
  push(value) {
96
153
  if (this.length >= this.array.length) {
97
154
  this.expandArray();
@@ -99,17 +156,39 @@ export class ResizeableTypedArray {
99
156
  this.array[this.items++] = value;
100
157
  return this.length - 1;
101
158
  }
159
+ /**
160
+ * Set a value at a specific index. Expands array if needed.
161
+ */
162
+ set(index, value) {
163
+ if (index < 0)
164
+ throw Error("Index out of bounds");
165
+ if (index >= this.length) {
166
+ while (index >= this.array.length)
167
+ this.expandArray();
168
+ this.items = index + 1;
169
+ }
170
+ this.array[index] = value;
171
+ }
172
+ /**
173
+ * Push multiple values to the end of the array.
174
+ */
102
175
  pushMany(values) {
103
176
  while (this.length + values.length > this.array.length)
104
177
  this.expandArray();
105
178
  this.array.set(values, this.length);
106
179
  this.items += values.length;
107
180
  }
181
+ /**
182
+ * Shrink the buffer to exactly fit stored items.
183
+ * Buffer becomes fixed-length after compacting.
184
+ */
108
185
  compact() {
109
186
  if (this.buffer instanceof SharedArrayBuffer) {
187
+ // SharedArrayBuffer uses slice() to create a new fixed-size buffer
110
188
  this.buffer = this.buffer.slice(0, this.length * this.ArrayType.BYTES_PER_ELEMENT);
111
189
  }
112
190
  else {
191
+ // ArrayBuffer uses transferToFixedLength() to detach and resize
113
192
  this.buffer = this.buffer.transferToFixedLength(this.length * this.ArrayType.BYTES_PER_ELEMENT);
114
193
  }
115
194
  this.array = new this.ArrayType(this.buffer);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed-arrays.js","sourceRoot":"","sources":["../../../src/typed-arrays.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Utility functions for @osmix/core.
3
+ * @module
4
+ */
5
+ import type { GeoBbox2D, LonLat } from "@osmix/shared/types";
6
+ /**
7
+ * Compute the bounding box of a set of coordinates.
8
+ * Returns `[minLon, minLat, maxLon, maxLat]`.
9
+ */
10
+ export declare function bboxFromLonLats(lonLats: LonLat[]): GeoBbox2D;
11
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAE5D;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAY5D"}
@@ -1,13 +1,11 @@
1
- export function throttle(func, timeFrame) {
2
- let lastTime = 0;
3
- return (...args) => {
4
- const now = Date.now();
5
- if (now - lastTime >= timeFrame) {
6
- func(...args);
7
- lastTime = now;
8
- }
9
- };
10
- }
1
+ /**
2
+ * Utility functions for @osmix/core.
3
+ * @module
4
+ */
5
+ /**
6
+ * Compute the bounding box of a set of coordinates.
7
+ * Returns `[minLon, minLat, maxLon, maxLat]`.
8
+ */
11
9
  export function bboxFromLonLats(lonLats) {
12
10
  let minLon = Number.POSITIVE_INFINITY;
13
11
  let minLat = Number.POSITIVE_INFINITY;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAiB;IAChD,IAAI,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAA;IACrC,IAAI,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAA;IACrC,IAAI,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAA;IACrC,IAAI,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAA;IACrC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,OAAO,EAAE,CAAC;QAClC,IAAI,GAAG,GAAG,MAAM;YAAE,MAAM,GAAG,GAAG,CAAA;QAC9B,IAAI,GAAG,GAAG,MAAM;YAAE,MAAM,GAAG,GAAG,CAAA;QAC9B,IAAI,GAAG,GAAG,MAAM;YAAE,MAAM,GAAG,GAAG,CAAA;QAC9B,IAAI,GAAG,GAAG,MAAM;YAAE,MAAM,GAAG,GAAG,CAAA;IAC/B,CAAC;IACD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AACxC,CAAC"}
@@ -0,0 +1,101 @@
1
+ import type { OsmPbfWay } from "@osmix/pbf";
2
+ import type { GeoBbox2D, LonLat, OsmTags, OsmWay } from "@osmix/shared/types";
3
+ import Flatbush from "flatbush";
4
+ import { Entities, type EntitiesTransferables } from "./entities";
5
+ import { type IdOrIndex } from "./ids";
6
+ import type { Nodes } from "./nodes";
7
+ import type StringTable from "./stringtable";
8
+ import { type BufferType } from "./typed-arrays";
9
+ export interface WaysTransferables<T extends BufferType = BufferType> extends EntitiesTransferables<T> {
10
+ refStart: T;
11
+ refCount: T;
12
+ refs: T;
13
+ bbox: T;
14
+ /** Optional - can be rebuilt via buildSpatialIndex() */
15
+ spatialIndex?: T;
16
+ }
17
+ export declare class Ways extends Entities<OsmWay> {
18
+ private spatialIndex;
19
+ private spatialIndexBuilt;
20
+ private refStart;
21
+ private refCount;
22
+ private refs;
23
+ private bbox;
24
+ private nodes;
25
+ /**
26
+ * Create a new Ways index.
27
+ */
28
+ constructor(stringTable: StringTable, nodes: Nodes, transferables?: WaysTransferables);
29
+ /**
30
+ * Add a single way to the index.
31
+ */
32
+ addWay(way: OsmWay): number;
33
+ /**
34
+ * Bulk add ways directly from a PBF PrimitiveBlock.
35
+ */
36
+ addWays(ways: OsmPbfWay[], blockStringIndexMap: Uint32Array, filter?: (way: OsmWay) => OsmWay | null): number;
37
+ /**
38
+ * Compact the internal arrays to free up memory.
39
+ */
40
+ buildEntityIndex(): void;
41
+ /**
42
+ * Build the spatial index for ways.
43
+ * If bbox data already exists (e.g., loaded from storage), reuses it.
44
+ */
45
+ buildSpatialIndex(): Flatbush;
46
+ /**
47
+ * Check if the spatial index has been built.
48
+ */
49
+ hasSpatialIndex(): boolean;
50
+ /**
51
+ * Get the full way entity.
52
+ */
53
+ getFullEntity(index: number, id: number, tags?: OsmTags): OsmWay;
54
+ /**
55
+ * Get the node IDs referenced by a way.
56
+ */
57
+ getRefIds(index: number): number[];
58
+ /**
59
+ * Get the bounding box of a way.
60
+ */
61
+ getEntityBbox(idOrIndex: IdOrIndex): GeoBbox2D;
62
+ /**
63
+ * Get the coordinates of a way as a flat array.
64
+ */
65
+ getLine(index: number): Float64Array<ArrayBuffer>;
66
+ /**
67
+ * Get the coordinates of a way as an array of [lon, lat] pairs.
68
+ */
69
+ getCoordinates(index: number): LonLat[];
70
+ /**
71
+ * Find way indexes that intersect a bounding box.
72
+ */
73
+ intersects(bbox: GeoBbox2D, filterFn?: (index: number) => boolean): number[];
74
+ /**
75
+ * Find way indexes near a point using great-circle distance.
76
+ * @param lon - Longitude in degrees.
77
+ * @param lat - Latitude in degrees.
78
+ * @param maxResults - Maximum number of results to return.
79
+ * @param maxDistanceKm - Maximum distance in kilometers.
80
+ * @returns Array of way indexes sorted by distance.
81
+ */
82
+ neighbors(lon: number, lat: number, maxResults?: number, maxDistanceKm?: number): number[];
83
+ /**
84
+ * Get ways within a bounding box.
85
+ */
86
+ withinBbox(bbox: GeoBbox2D, include?: (index: number) => boolean): {
87
+ ids: Float64Array;
88
+ positions: Float64Array;
89
+ startIndices: Uint32Array;
90
+ };
91
+ /**
92
+ * Get transferable objects for passing to another thread.
93
+ * Only includes spatialIndex if it has been built.
94
+ */
95
+ transferables(): WaysTransferables;
96
+ /**
97
+ * Get the approximate memory requirements for a given number of ways in bytes.
98
+ */
99
+ static getBytesRequired(count: number): number;
100
+ }
101
+ //# sourceMappingURL=ways.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ways.d.ts","sourceRoot":"","sources":["../../../src/ways.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,336 @@
1
+ import Flatbush from "flatbush";
2
+ import { around as geoAround } from "geoflatbush";
3
+ import { Entities } from "./entities";
4
+ import { Ids } from "./ids";
5
+ import { Tags } from "./tags";
6
+ import { BufferConstructor, IdArrayType, ResizeableTypedArray as RTA, } from "./typed-arrays";
7
+ export class Ways extends Entities {
8
+ spatialIndex = new Flatbush(1);
9
+ // Track if spatial index was properly built (vs default empty)
10
+ spatialIndexBuilt = false;
11
+ refStart;
12
+ refCount; // Maximum 2,000 nodes per way
13
+ // Node IDs
14
+ refs;
15
+ // Bounding box of the way in geographic coordinates
16
+ bbox;
17
+ // Node reference index
18
+ nodes;
19
+ /**
20
+ * Create a new Ways index.
21
+ */
22
+ constructor(stringTable, nodes, transferables) {
23
+ if (transferables) {
24
+ super("way", new Ids(transferables), new Tags(stringTable, transferables));
25
+ this.refStart = RTA.from(Uint32Array, transferables.refStart);
26
+ this.refCount = RTA.from(Uint16Array, transferables.refCount);
27
+ this.refs = RTA.from(IdArrayType, transferables.refs);
28
+ this.bbox = RTA.from(Float64Array, transferables.bbox);
29
+ // Only load spatial index if provided (not stored in IndexedDB)
30
+ if (transferables.spatialIndex?.byteLength) {
31
+ this.spatialIndex = Flatbush.from(transferables.spatialIndex);
32
+ this.spatialIndexBuilt = true;
33
+ }
34
+ this.indexBuilt = true;
35
+ }
36
+ else {
37
+ super("way", new Ids(), new Tags(stringTable));
38
+ this.refStart = new RTA(Uint32Array);
39
+ this.refCount = new RTA(Uint16Array);
40
+ this.refs = new RTA(IdArrayType);
41
+ this.bbox = new RTA(Float64Array);
42
+ }
43
+ this.nodes = nodes;
44
+ }
45
+ /**
46
+ * Add a single way to the index.
47
+ */
48
+ addWay(way) {
49
+ const wayIndex = this.addEntity(way.id, way.tags ?? {});
50
+ this.refStart.push(this.refs.length);
51
+ this.refCount.push(way.refs.length);
52
+ for (const ref of way.refs)
53
+ this.refs.push(ref);
54
+ return wayIndex;
55
+ }
56
+ /**
57
+ * Bulk add ways directly from a PBF PrimitiveBlock.
58
+ */
59
+ addWays(ways, blockStringIndexMap, filter) {
60
+ let added = 0;
61
+ for (const way of ways) {
62
+ let prevRefId = 0;
63
+ const refs = way.refs.map((refId) => {
64
+ prevRefId += refId;
65
+ return prevRefId;
66
+ });
67
+ const filteredWay = filter
68
+ ? filter({
69
+ id: way.id,
70
+ refs,
71
+ tags: this.tags.getTagsFromIndices(way.keys, way.vals),
72
+ })
73
+ : null;
74
+ if (filter && filteredWay === null)
75
+ continue;
76
+ const tagKeys = way.keys.map((key) => {
77
+ const index = blockStringIndexMap[key];
78
+ if (index === undefined)
79
+ throw Error("Tag key not found");
80
+ return index;
81
+ });
82
+ const tagValues = way.vals.map((val) => {
83
+ const index = blockStringIndexMap[val];
84
+ if (index === undefined)
85
+ throw Error("Tag value not found");
86
+ return index;
87
+ });
88
+ this.addEntity(way.id, tagKeys, tagValues);
89
+ this.refStart.push(this.refs.length);
90
+ this.refCount.push(filteredWay?.refs.length ?? refs.length);
91
+ this.refs.pushMany(filteredWay?.refs ?? refs);
92
+ added++;
93
+ }
94
+ return added;
95
+ }
96
+ /**
97
+ * Compact the internal arrays to free up memory.
98
+ */
99
+ buildEntityIndex() {
100
+ this.refStart.compact();
101
+ this.refCount.compact();
102
+ this.refs.compact();
103
+ }
104
+ /**
105
+ * Build the spatial index for ways.
106
+ * If bbox data already exists (e.g., loaded from storage), reuses it.
107
+ */
108
+ buildSpatialIndex() {
109
+ if (!this.nodes.isReady())
110
+ throw Error("Node index is not ready.");
111
+ if (this.size === 0)
112
+ return this.spatialIndex;
113
+ console.time("WayIndex.buildSpatialIndex");
114
+ this.spatialIndex = new Flatbush(this.size, 128, Float64Array, BufferConstructor);
115
+ // If bbox already has data (loaded from storage), use it directly
116
+ const hasBboxData = this.bbox.length >= this.size * 4;
117
+ for (let i = 0; i < this.size; i++) {
118
+ let minX;
119
+ let minY;
120
+ let maxX;
121
+ let maxY;
122
+ if (hasBboxData) {
123
+ // Use stored bbox values
124
+ minX = this.bbox.at(i * 4);
125
+ minY = this.bbox.at(i * 4 + 1);
126
+ maxX = this.bbox.at(i * 4 + 2);
127
+ maxY = this.bbox.at(i * 4 + 3);
128
+ }
129
+ else {
130
+ // Calculate bbox from node coordinates
131
+ minX = Number.POSITIVE_INFINITY;
132
+ minY = Number.POSITIVE_INFINITY;
133
+ maxX = Number.NEGATIVE_INFINITY;
134
+ maxY = Number.NEGATIVE_INFINITY;
135
+ const start = this.refStart.at(i);
136
+ const count = this.refCount.at(i);
137
+ for (let j = start; j < start + count; j++) {
138
+ const refId = this.refs.at(j);
139
+ const [lon, lat] = this.nodes.getNodeLonLat({ id: refId });
140
+ if (lon < minX)
141
+ minX = lon;
142
+ if (lon > maxX)
143
+ maxX = lon;
144
+ if (lat < minY)
145
+ minY = lat;
146
+ if (lat > maxY)
147
+ maxY = lat;
148
+ }
149
+ this.bbox.push(minX);
150
+ this.bbox.push(minY);
151
+ this.bbox.push(maxX);
152
+ this.bbox.push(maxY);
153
+ }
154
+ this.spatialIndex.add(minX, minY, maxX, maxY);
155
+ }
156
+ // Compact bbox if we just calculated it
157
+ if (!hasBboxData) {
158
+ this.bbox.compact();
159
+ }
160
+ this.spatialIndex.finish();
161
+ this.spatialIndexBuilt = true;
162
+ console.timeEnd("WayIndex.buildSpatialIndex");
163
+ return this.spatialIndex;
164
+ }
165
+ /**
166
+ * Check if the spatial index has been built.
167
+ */
168
+ hasSpatialIndex() {
169
+ return this.spatialIndexBuilt;
170
+ }
171
+ /**
172
+ * Get the full way entity.
173
+ */
174
+ getFullEntity(index, id, tags) {
175
+ return {
176
+ id,
177
+ refs: [...this.getRefIds(index)],
178
+ tags,
179
+ };
180
+ }
181
+ /**
182
+ * Get the node IDs referenced by a way.
183
+ */
184
+ getRefIds(index) {
185
+ const start = this.refStart.at(index);
186
+ const count = this.refCount.at(index);
187
+ return Array.from(this.refs.slice(start, start + count));
188
+ }
189
+ /**
190
+ * Get the bounding box of a way.
191
+ */
192
+ getEntityBbox(idOrIndex) {
193
+ const index = "index" in idOrIndex ? idOrIndex.index : this.ids.idOrIndex(idOrIndex)[0];
194
+ return [
195
+ this.bbox.at(index * 4),
196
+ this.bbox.at(index * 4 + 1),
197
+ this.bbox.at(index * 4 + 2),
198
+ this.bbox.at(index * 4 + 3),
199
+ ];
200
+ }
201
+ /**
202
+ * Get the coordinates of a way as a flat array.
203
+ */
204
+ getLine(index) {
205
+ const count = this.refCount.at(index);
206
+ const start = this.refStart.at(index);
207
+ const line = new Float64Array(count * 2);
208
+ for (let i = 0; i < count; i++) {
209
+ const ref = this.refs.at(start + i);
210
+ const [lon, lat] = this.nodes.getNodeLonLat({ id: ref });
211
+ line[i * 2] = lon;
212
+ line[i * 2 + 1] = lat;
213
+ }
214
+ return line;
215
+ }
216
+ /**
217
+ * Get the coordinates of a way as an array of [lon, lat] pairs.
218
+ */
219
+ getCoordinates(index) {
220
+ const count = this.refCount.at(index);
221
+ const start = this.refStart.at(index);
222
+ const coords = [];
223
+ for (let refIndex = start; refIndex < start + count; refIndex++) {
224
+ const ref = this.refs.at(refIndex);
225
+ const coord = this.nodes.getNodeLonLat({ id: ref });
226
+ if (coord === undefined ||
227
+ coord[0] === undefined ||
228
+ coord[1] === undefined) {
229
+ console.error("node index has ref", this.nodes.ids.has(ref));
230
+ throw Error(`Invalid coordinate for way id ${this.ids.at(index)}, index ${index}, node ref ${ref}, ref index ${refIndex}`);
231
+ }
232
+ coords.push(coord);
233
+ }
234
+ return coords;
235
+ }
236
+ /**
237
+ * Find way indexes that intersect a bounding box.
238
+ */
239
+ intersects(bbox, filterFn) {
240
+ if (this.size === 0)
241
+ return [];
242
+ return this.spatialIndex.search(bbox[0], bbox[1], bbox[2], bbox[3], filterFn);
243
+ }
244
+ /**
245
+ * Find way indexes near a point using great-circle distance.
246
+ * @param lon - Longitude in degrees.
247
+ * @param lat - Latitude in degrees.
248
+ * @param maxResults - Maximum number of results to return.
249
+ * @param maxDistanceKm - Maximum distance in kilometers.
250
+ * @returns Array of way indexes sorted by distance.
251
+ */
252
+ neighbors(lon, lat, maxResults, maxDistanceKm) {
253
+ if (this.size === 0)
254
+ return [];
255
+ // Use geoflatbush for proper geographic distance calculations
256
+ return geoAround(this.spatialIndex, lon, lat, maxResults, maxDistanceKm);
257
+ }
258
+ /**
259
+ * Get ways within a bounding box.
260
+ */
261
+ withinBbox(bbox, include) {
262
+ console.time("Ways.withinBbox");
263
+ const wayCandidates = this.intersects(bbox, include);
264
+ const ids = new Float64Array(wayCandidates.length);
265
+ const wayPositions = [];
266
+ const wayStartIndices = new Uint32Array(wayCandidates.length + 1);
267
+ wayStartIndices[0] = 0;
268
+ console.time("Ways.withinBbox.loop");
269
+ let size = 0;
270
+ wayCandidates.forEach((wayIndex, i) => {
271
+ ids[i] = this.ids.at(wayIndex);
272
+ const way = this.getLine(wayIndex);
273
+ size += way.length;
274
+ wayPositions.push(way);
275
+ const prevIndex = wayStartIndices[i];
276
+ if (prevIndex === undefined)
277
+ throw Error("Previous index is undefined");
278
+ wayStartIndices[i + 1] = prevIndex + way.length / 2;
279
+ });
280
+ console.timeEnd("Ways.withinBbox.loop");
281
+ const wayPositionsArray = new Float64Array(size);
282
+ let pIndex = 0;
283
+ for (const way of wayPositions) {
284
+ wayPositionsArray.set(way, pIndex);
285
+ pIndex += way.length;
286
+ }
287
+ console.timeEnd("Ways.withinBbox");
288
+ return {
289
+ ids,
290
+ positions: wayPositionsArray,
291
+ startIndices: wayStartIndices,
292
+ };
293
+ }
294
+ /**
295
+ * Get transferable objects for passing to another thread.
296
+ * Only includes spatialIndex if it has been built.
297
+ */
298
+ transferables() {
299
+ const base = {
300
+ ...super.transferables(),
301
+ refStart: this.refStart.array.buffer,
302
+ refCount: this.refCount.array.buffer,
303
+ refs: this.refs.array.buffer,
304
+ bbox: this.bbox.array.buffer,
305
+ };
306
+ // Only include spatial index if it was built
307
+ if (this.spatialIndexBuilt) {
308
+ return { ...base, spatialIndex: this.spatialIndex.data };
309
+ }
310
+ return base;
311
+ }
312
+ /**
313
+ * Get the approximate memory requirements for a given number of ways in bytes.
314
+ */
315
+ static getBytesRequired(count) {
316
+ if (count === 0)
317
+ return 0;
318
+ // Approximate nodes per way
319
+ let numNodes = count;
320
+ let n = count;
321
+ while (n !== 1) {
322
+ n = Math.ceil(n / 128);
323
+ numNodes += n;
324
+ }
325
+ const indexBytes = (numNodes < 16384 ? 2 : 4) * numNodes;
326
+ const boxesBytes = numNodes * 4 * Float64Array.BYTES_PER_ELEMENT;
327
+ const spatialIndexBytes = 8 + indexBytes + boxesBytes;
328
+ return (Ids.getBytesRequired(count) +
329
+ Tags.getBytesRequired(count) +
330
+ count * Uint32Array.BYTES_PER_ELEMENT + // refStart
331
+ count * Uint16Array.BYTES_PER_ELEMENT + // refCount
332
+ count * 4 * Float64Array.BYTES_PER_ELEMENT + // bbox
333
+ spatialIndexBytes);
334
+ }
335
+ }
336
+ //# sourceMappingURL=ways.js.map