@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # @osmix/core
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d4f4b1f: Add content hashing for quick `isEqual`s checks of two similar `Osm` indexes. Typically used for checking post-merge.
8
+
9
+ ## 0.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 29ed376: Routing: make graph transferable
14
+
15
+ ### Patch Changes
16
+
17
+ - 803c05c: Merge app style cleanup
18
+ - cbe4273: Osm index storage and reload
19
+ - ff40416: Refactor main `osmix` lib external APIs
20
+
21
+ ## 0.0.6
22
+
23
+ ### Patch Changes
24
+
25
+ - 3846a0c: Integrate geographic spatial indexing libraries
26
+ - cdab4db: Add router
27
+ - Updated dependencies [0cd8a2e]
28
+ - @osmix/shared@0.0.6
29
+
30
+ ## 0.0.5
31
+
32
+ ### Patch Changes
33
+
34
+ - a33a280: Create an easy way to estimate bytes required for a given OSM size
35
+ - edbb26b: Handle more Relation types
36
+ - 69a36bd: Switch Nodes coordinate storage to Int32Array
37
+ - Updated dependencies [bb629cf]
38
+ - Updated dependencies [edbb26b]
39
+ - Updated dependencies [69a36bd]
40
+ - @osmix/shared@0.0.5
41
+
42
+ ## 0.0.4
43
+
44
+ ### Patch Changes
45
+
46
+ - d001d9a: Refactor to align around new main external API
47
+ - 4303c40: Refactor core Osmix index to prepare for future work
48
+ - Updated dependencies [572cbd8]
49
+ - Updated dependencies [d001d9a]
50
+ - @osmix/shared@0.0.4
51
+
52
+ ## 0.0.3
53
+
54
+ ### Patch Changes
55
+
56
+ - b4a3ff2: Improve Relation handling and display
57
+ - c0193dd: Import GeoJSON into Osmix
58
+ - Updated dependencies [b4a3ff2]
59
+ - @osmix/shared@0.0.3
60
+ - @osmix/json@0.0.3
61
+ - @osmix/pbf@0.0.2
62
+
3
63
  ## 0.0.2
4
64
 
5
65
  ### Patch Changes
package/README.md CHANGED
@@ -1,160 +1,171 @@
1
1
  # @osmix/core
2
2
 
3
- @osmix/core wraps the `Osmix` index: a typed-array OpenStreetMap engine that reads `.osm.pbf` streams, builds spatial indexes, and emits JSON, PBF, or raster tiles without leaving modern JavaScript runtimes.
3
+ In-memory OSM entity storage with spatial indexing. Provides core data structures for efficiently storing and querying OpenStreetMap nodes, ways, and relations.
4
4
 
5
5
  ## Highlights
6
6
 
7
- - Ingest `.osm.pbf` sources into node, way, and relation stores backed by transferable typed arrays using readers from [`@osmix/pbf`](../pbf/README.md).
8
- - Run fast bounding-box searches with KDBush/Flatbush and convert matches straight to GeoJSON.
9
- - Trim extracts, write new PBF buffers, or stream entities to downstream tooling with [`@osmix/json`](../json/README.md) or [`@osmix/pbf`](../pbf/README.md).
10
- - Pair with [`@osmix/change`](../change/README.md) when you need deduplication, intersection, or merge pipelines.
11
- - Ship fully indexed datasets across workers via `transferables()` + `Osmix.from`.
7
+ - **Memory-efficient**: Typed arrays and microdegree coordinates (`Int32Array`) minimize memory while maintaining precision.
8
+ - **Spatial indexing**: KDBush (points) and Flatbush (bboxes) for fast geographic queries. Supports bbox and radius searches via geokdbush/geoflatbush.
9
+ - **Worker-ready**: Serialize to `ArrayBuffer`/`SharedArrayBuffer` for zero-copy transfer via `transferables()`.
10
+ - **Tag search**: Reverse-index lookup for fast entity searches by tag key.
11
+ - **Streaming**: Iterate entities or sorted-by-ID without materializing full arrays.
12
12
 
13
13
  ## Installation
14
14
 
15
15
  ```sh
16
- npm install @osmix/core
16
+ bun add @osmix/core
17
17
  ```
18
18
 
19
19
  ## Usage
20
20
 
21
- ### Load a PBF in one call
21
+ ### Loading PBF files
22
22
 
23
- ```ts
24
- import { Osmix } from "@osmix/core"
25
- import { readFile } from "node:fs/promises"
23
+ `@osmix/core` provides the in-memory data structures. To parse `.osm.pbf` files, use the high-level `osmix` package:
26
24
 
27
- const osm = await Osmix.fromPbf(readFile("example.osm.pbf"))
25
+ ```ts
26
+ import { fromPbf } from "osmix"
28
27
 
28
+ const osm = await fromPbf(Bun.file("./monaco.pbf").stream())
29
29
  console.log(osm.nodes.size, osm.ways.size, osm.relations.size)
30
30
  ```
31
31
 
32
- `fromPbf` accepts `ArrayBufferLike`, async (iterable) chunks, or Web `ReadableStream` inputs.
32
+ ### Building data directly
33
33
 
34
- ### Stream with progress and extraction
35
-
36
- Pass a logger or set one later to receive throttled progress messages. Provide `extractBbox` to keep only features intersecting the desired bounds while parsing.
34
+ For synthetic data or tests, instantiate `Osm` directly and call `buildIndexes()` / `buildSpatialIndexes()` before querying:
37
35
 
38
36
  ```ts
39
- import { Osmix } from "@osmix/core"
40
- import { createReadStream } from "node:fs"
37
+ import { Osm } from "@osmix/core"
41
38
 
42
- const osm = new Osmix({ id: "planet-seattle" })
43
- osm.setLogger((message) => console.log(message))
39
+ const osm = new Osm({ id: "fixture" })
44
40
 
45
- await osm.readPbf(createReadStream("planet.osm.pbf"), {
46
- extractBbox: [-122.5, 47.45, -122.2, 47.75],
41
+ osm.nodes.addNode({
42
+ id: 1,
43
+ lon: -122.4,
44
+ lat: 47.6,
45
+ tags: { amenity: "cafe" },
47
46
  })
48
- ```
49
47
 
50
- ### Build synthetic fixtures
51
-
52
- When generating test data, insert entities directly and call `buildIndexes()` before querying.
48
+ osm.ways.addWay({
49
+ id: 10,
50
+ refs: [1],
51
+ tags: { highway: "service" },
52
+ })
53
53
 
54
- ```ts
55
- const osm = new Osmix({ id: "fixture" })
56
- osm.nodes.addNode({ id: 1, lon: -0.1, lat: 51.5, tags: { amenity: "cafe" } })
57
- osm.ways.addWay({ id: 10, refs: [1], tags: { highway: "service" } })
58
54
  osm.buildIndexes()
55
+ osm.buildSpatialIndexes()
59
56
  ```
60
57
 
61
- ## Query the index
62
-
63
- - `osm.get("node", 123)` or `osm.getById("n123")` fetches specific entities.
64
- - `osm.nodes.sorted()` / `osm.ways.sorted()` iterate ids in ascending order.
65
- - `osm.getEntityGeoJson(entity)` returns a GeoJSON feature with coordinates resolved from the
66
- indexed nodes.
67
-
68
- Spatial lookups emit compact typed arrays that can be transferred across Web Workers and used directly in `Deck.gl`:
58
+ ### Querying entities
69
59
 
70
60
  ```ts
71
- const bbox = [-122.34, 47.57, -122.30, 47.61]
72
- const { ids, positions } = osm.getNodesInBbox(bbox)
73
-
74
- const { ids: wayIds, positions: wayCoords, startIndices } = osm.getWaysInBbox(bbox)
75
- ```
61
+ // Get by ID
62
+ const node = osm.nodes.getById(123)
63
+ const way = osm.ways.get({ id: 456 })
76
64
 
77
- `positions` stores `[lon, lat]` pairs; `startIndices` splits `wayCoords` into per-way sequences.
78
-
79
- ## Extract and export data
65
+ // Iterate all entities
66
+ for (const node of osm.nodes) {
67
+ console.log(node.id, node.lon, node.lat)
68
+ }
80
69
 
81
- ```ts
82
- const downtown = osm.extract([-122.35, 47.60, -122.32, 47.62])
70
+ // Iterate sorted by ID (useful for PBF export)
71
+ for (const way of osm.ways.sorted()) {
72
+ console.log(way.id, way.refs)
73
+ }
83
74
 
84
- const entityStream = downtown.toEntityStream() // header + entities
85
- const pbfStream = downtown.toPbfStream() // Uint8Array chunks
86
- const pbfBuffer = await downtown.toPbfBuffer()
75
+ // Search by tag key
76
+ const highways = osm.ways.search("highway")
77
+ const cafes = osm.nodes.search("amenity", "cafe")
87
78
  ```
88
79
 
89
- `extract` returns a new, fully indexed `Osmix` instance with the header bbox updated.
90
-
91
- ## Changesets and merging
92
-
93
- Change orchestration now lives in the dedicated [`@osmix/change`](../change/README.md) package. Install it alongside
94
- `@osmix/core` when you want to deduplicate entities, generate direct merges, or script intersection creation:
80
+ ### Spatial queries
95
81
 
96
82
  ```ts
97
- import { Osmix } from "@osmix/core"
98
- import { merge, OsmixChangeset } from "@osmix/change"
99
- import { readFile } from "node:fs/promises"
83
+ // Bounding box query
84
+ const bbox: [number, number, number, number] = [-122.34, 47.57, -122.30, 47.61]
85
+ const { ids, positions } = osm.nodes.withinBbox(bbox)
86
+ const { ids: wayIds, positions: wayCoords, startIndices } = osm.ways.withinBbox(bbox)
100
87
 
101
- const base = await Osmix.fromPbf(readFile("base.osm.pbf"))
102
- const patch = await Osmix.fromPbf(readFile("patch.osm.pbf"))
88
+ // Radius query (uses great-circle distance)
89
+ const nearbyIndexes = osm.nodes.findIndexesWithinRadius(-122.4, 47.6, 10) // 10km
103
90
 
104
- const changeset = new OsmixChangeset(base)
105
- changeset.deduplicateWays(base.ways)
91
+ // Get entities from indexes
92
+ const nearbyNodes = nearbyIndexes.map((i) => osm.nodes.getByIndex(i))
106
93
 
107
- const merged = await merge(base, patch, { directMerge: true })
94
+ // Ways/relations use Flatbush for bounding-box intersection
95
+ const intersectingWays = osm.ways.intersects(bbox)
96
+ const nearestWays = osm.ways.neighbors(-122.4, 47.6, 5, 10) // 5 results, 10km max
108
97
  ```
109
98
 
110
- Refer to the [`@osmix/change` README](../change/README.md) for the full API surface.
111
-
112
- ## Transfer between threads
113
-
114
- Typed arrays keep the index transferable.
99
+ ### Transferring between workers
115
100
 
116
101
  ```ts
117
- // main thread
118
- const payload = osm.transferables()
119
- const buffers = []
120
- const collect = (value: unknown) => {
121
- if (value instanceof ArrayBuffer) buffers.push(value)
122
- else if (ArrayBuffer.isView(value)) buffers.push(value.buffer)
123
- else if (Array.isArray(value)) value.forEach(collect)
124
- else if (value && typeof value === "object")
125
- Object.values(value).forEach(collect)
126
- return buffers
127
- }
128
-
129
- worker.postMessage(payload, { transfer: collect(payload) })
102
+ // In the main thread
103
+ const transferables = osm.transferables()
104
+ worker.postMessage(transferables, Object.values(transferables).filter(ArrayBuffer.isView))
130
105
 
131
- // worker
132
- import { Osmix } from "@osmix/core"
133
-
134
- self.addEventListener("message", ({ data }) => {
135
- const osm = Osmix.from(data)
136
- // ready for queries immediately
137
- })
106
+ // In the worker
107
+ const osm = new Osm(transferables)
108
+ // Index is already built, ready for queries
138
109
  ```
139
110
 
140
- ## API overview
141
-
142
- - `Osmix` – ingest PBF sources, build indexes, query entities, extract subsets, and emit JSON/PBF.
143
- - `Nodes` / `Ways` / `Relations` – typed-array backed stores exposed for advanced workflows.
144
- - `OsmixRasterTile` – rasterize indexed data into tile-sized buffers with lon/lat helpers.
145
- - Utilities – `throttle`, plus the shared type definitions used across the package.
146
- - Changesets and merge helpers now live in [`@osmix/change`](../change/README.md).
147
-
148
- ## Environment and limitations
149
-
150
- - Requires runtimes with Web Streams, `TextEncoder`/`TextDecoder`, and zlib-compatible `CompressionStream`/`DecompressionStream` support (Bun, Node 20+, modern browsers).
151
- - `readPbf` expects dense-node blocks; PBFs that omit dense encodings are currently unsupported.
152
- - Filtering during ingest depends on node membership; emit nodes, then ways, then relations when supplying custom entity generators.
111
+ ## API
112
+
113
+ ### Classes
114
+
115
+ | Class | Description |
116
+ |-------|-------------|
117
+ | `Osm` | Main container with nodes, ways, relations, and shared string table |
118
+ | `Nodes` | Node storage with KDBush spatial index |
119
+ | `Ways` | Way storage with Flatbush spatial index |
120
+ | `Relations` | Relation storage with Flatbush spatial index |
121
+ | `StringTable` | Deduplicated UTF-8 string storage for tags |
122
+ | `Tags` | Bidirectional tag storage (entity→tags, key→entities) |
123
+
124
+ ### Osm Methods
125
+
126
+ | Method | Description |
127
+ |--------|-------------|
128
+ | `buildIndexes()` | Finalize ID, tag, and entity indexes after adding data |
129
+ | `buildSpatialIndexes()` | Build spatial indexes for all entity types |
130
+ | `bbox()` | Get bounding box of all entities |
131
+ | `info()` | Get summary info (id, bbox, header, stats) |
132
+ | `transferables()` | Get serializable buffers for worker transfer |
133
+ | `isReady()` | Check if all indexes are built |
134
+
135
+ ### Entity Collection Methods
136
+
137
+ | Method | Description |
138
+ |--------|-------------|
139
+ | `getById(id)` | Get entity by OSM ID |
140
+ | `get({ id } \| { index })` | Get by ID or internal index |
141
+ | `getByIndex(index)` | Get by internal array index |
142
+ | `search(key, value?)` | Find entities with tag key (and optional value) |
143
+ | `withinBbox(bbox)` | Get entities within bounding box |
144
+ | `findIndexesWithinRadius(lon, lat, km)` | Find nearby entities (nodes) |
145
+ | `intersects(bbox)` | Find intersecting entities (ways/relations) |
146
+ | `neighbors(lon, lat, max, maxKm)` | Find nearest entities (ways/relations) |
147
+ | `sorted()` | Iterator over entities sorted by ID |
148
+
149
+ ## Environment and Limitations
150
+
151
+ - Requires Web Streams, `TextEncoder`/`TextDecoder`, `CompressionStream`/`DecompressionStream` (Bun, Node 20+, modern browsers).
152
+ - Uses ES2024 resizable `ArrayBuffer` and growable `SharedArrayBuffer` when available.
153
+ - Coordinates stored as `Int32Array` microdegrees (1e-7 degree precision, ~1cm at equator); converted to degrees at API boundaries.
154
+
155
+ ## Related Packages
156
+
157
+ - [`osmix`](../osmix/README.md) — High-level API for loading PBF files and worker orchestration.
158
+ - [`@osmix/pbf`](../pbf/README.md) — Low-level PBF parsing and serialization.
159
+ - [`@osmix/json`](../json/README.md) — JSON entity conversion.
160
+ - [`@osmix/geojson`](../geojson/README.md) — Convert entities to GeoJSON features.
161
+ - [`@osmix/change`](../change/README.md) — Changeset generation, deduplication, and merge.
153
162
 
154
163
  ## Development
155
164
 
156
- - `bun run test packages/core`
157
- - `bun run lint packages/core`
158
- - `bun run typecheck packages/core`
165
+ ```sh
166
+ bun run test packages/core
167
+ bun run typecheck packages/core
168
+ bun run lint packages/core
169
+ ```
159
170
 
160
- Run `bun run check` at the repo root before publishing to ensure formatting, lint, and type coverage.
171
+ Run `bun run check` at the repo root before publishing.
@@ -1,26 +1,120 @@
1
- import type { OsmEntity, OsmEntityType, OsmTags } from "@osmix/json";
2
- import { type IdOrIndex, Ids, type IdsTransferables } from "./ids";
3
- import type StringTable from "./stringtable";
4
- import { Tags, type TagsTransferables } from "./tags";
5
- export interface EntitiesTransferables extends IdsTransferables, TagsTransferables {
1
+ /**
2
+ * Base class for OSM entity collections.
3
+ *
4
+ * Provides common ID and tag storage, streaming iteration, and sorted access.
5
+ * Subclasses implement entity-specific storage and spatial indexing.
6
+ *
7
+ * @module
8
+ */
9
+ import type { GeoBbox2D, OsmEntity, OsmEntityType, OsmTags } from "@osmix/shared/types";
10
+ import type { IdOrIndex, Ids, IdsTransferables } from "./ids";
11
+ import type { Tags, TagsTransferables } from "./tags";
12
+ import type { BufferType } from "./typed-arrays";
13
+ import type { ContentHasher } from "./utils";
14
+ /**
15
+ * Serializable representation of an Entities collection for worker transfer.
16
+ * Combines ID and tag transferables; subclasses add entity-specific data.
17
+ */
18
+ export interface EntitiesTransferables<T extends BufferType = BufferType> extends IdsTransferables<T>, TagsTransferables<T> {
6
19
  }
20
+ /**
21
+ * Abstract base for typed entity collections.
22
+ *
23
+ * Lifecycle:
24
+ * 1. **Ingest**: `addEntity()` (no lookups).
25
+ * 2. **Finalize**: `buildIndex()`.
26
+ * 3. **Query**: Lookups and iteration enabled.
27
+ */
7
28
  export declare abstract class Entities<T extends OsmEntity> {
29
+ /** The type of entity stored in this collection ("node", "way", or "relation"). */
8
30
  indexType: OsmEntityType;
9
- stringTable: StringTable;
31
+ /** ID storage and lookup */
10
32
  ids: Ids;
33
+ /** Tag storage and search */
11
34
  tags: Tags;
12
- private indexBuilt;
13
- constructor(indexType: OsmEntityType, stringTable: StringTable, ids?: Ids, tags?: Tags);
14
- get isReady(): boolean;
35
+ /** Whether buildIndex() has been called */
36
+ protected indexBuilt: boolean;
37
+ /**
38
+ * Create a new Entities collection.
39
+ * @param indexType - The entity type ("node", "way", or "relation").
40
+ * @param ids - The ID storage instance.
41
+ * @param tags - The tag storage instance.
42
+ */
43
+ constructor(indexType: OsmEntityType, ids: Ids, tags: Tags);
44
+ /**
45
+ * Get transferable objects for passing to another thread.
46
+ */
47
+ transferables(): EntitiesTransferables;
48
+ /**
49
+ * Check if the index is built and ready for use.
50
+ */
51
+ isReady(): boolean;
52
+ /** Number of entities in this collection. */
15
53
  get size(): number;
54
+ /**
55
+ * Compact entity-specific typed arrays.
56
+ * Called by `buildIndex()` after ID and tag indexes are built.
57
+ * @abstract
58
+ */
16
59
  abstract buildEntityIndex(): void;
60
+ /**
61
+ * Finalize indexes.
62
+ * Must be called after adding entities and before querying.
63
+ */
17
64
  buildIndex(): void;
65
+ /**
66
+ * Get the bounding box of an entity.
67
+ * @param idOrIndex - Entity identifier (by ID or internal index).
68
+ * @returns Geographic bounding box [minLon, minLat, maxLon, maxLat].
69
+ * @abstract
70
+ */
71
+ abstract getEntityBbox(idOrIndex: IdOrIndex): GeoBbox2D;
72
+ /**
73
+ * Reconstruct a full entity object from index data.
74
+ * @param index - Internal array index.
75
+ * @param id - OSM entity ID.
76
+ * @param tags - Optional pre-fetched tags.
77
+ * @returns The complete entity object.
78
+ * @abstract
79
+ */
18
80
  abstract getFullEntity(index: number, id: number, tags?: OsmTags): T;
81
+ /**
82
+ * Add an entity to the index. Tags can be provided as an array of keys and values, or as an object.
83
+ */
84
+ addEntity(id: number, tags: number[], values: number[]): number;
85
+ addEntity(id: number, tags: OsmTags): number;
86
+ /**
87
+ * Get an entity by ID or index.
88
+ */
19
89
  get(idOrIndex: IdOrIndex): T | null;
90
+ /**
91
+ * Get an entity by index.
92
+ */
20
93
  getByIndex(index: number): T;
94
+ /**
95
+ * Get multiple entities by their indexes.
96
+ */
21
97
  getEntitiesByIndex(indexes: number[]): T[];
98
+ /**
99
+ * Iterate over all entities in the index.
100
+ */
22
101
  [Symbol.iterator](): Generator<T>;
102
+ /**
103
+ * Get an entity by ID.
104
+ */
23
105
  getById(id: number): T | null;
106
+ /**
107
+ * Iterate over entities sorted by ID.
108
+ */
24
109
  sorted(): Generator<T>;
110
+ /**
111
+ * Search for entities with a specific tag key and optional value.
112
+ */
113
+ search(key: string, val?: string): T[];
114
+ /**
115
+ * Update a ContentHasher with this entity collection's base data (IDs and tags).
116
+ * Subclasses should override to add entity-specific data.
117
+ */
118
+ updateHash(hasher: ContentHasher): ContentHasher;
25
119
  }
26
120
  //# sourceMappingURL=entities.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../../src/entities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACpE,OAAO,EAAE,KAAK,SAAS,EAAE,GAAG,EAAE,KAAK,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAClE,OAAO,KAAK,WAAW,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,IAAI,EAAE,KAAK,iBAAiB,EAAE,MAAM,QAAQ,CAAA;AAErD,MAAM,WAAW,qBAChB,SAAQ,gBAAgB,EACvB,iBAAiB;CAAG;AAEtB,8BAAsB,QAAQ,CAAC,CAAC,SAAS,SAAS;IACjD,SAAS,EAAE,aAAa,CAAA;IACxB,WAAW,EAAE,WAAW,CAAA;IACxB,GAAG,EAAE,GAAG,CAAA;IACR,IAAI,EAAE,IAAI,CAAA;IAEV,OAAO,CAAC,UAAU,CAAQ;gBAGzB,SAAS,EAAE,aAAa,EACxB,WAAW,EAAE,WAAW,EACxB,GAAG,CAAC,EAAE,GAAG,EACT,IAAI,CAAC,EAAE,IAAI;IAQZ,IAAI,OAAO,YAEV;IAED,IAAI,IAAI,WAEP;IAED,QAAQ,CAAC,gBAAgB,IAAI,IAAI;IAEjC,UAAU;IASV,QAAQ,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,CAAC;IAEpE,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,CAAC,GAAG,IAAI;IAMnC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC;IAQ5B,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE;IAUzC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC;IAMlC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAO5B,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC;CAOvB"}
1
+ {"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../../src/entities.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACX,SAAS,EACT,SAAS,EACT,aAAa,EACb,OAAO,EACP,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAC7D,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C;;;GAGG;AACH,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,CACvE,SAAQ,gBAAgB,CAAC,CAAC,CAAC,EAC1B,iBAAiB,CAAC,CAAC,CAAC;CAAG;AAEzB;;;;;;;GAOG;AACH,8BAAsB,QAAQ,CAAC,CAAC,SAAS,SAAS;IACjD,mFAAmF;IACnF,SAAS,EAAE,aAAa,CAAA;IACxB,4BAA4B;IAC5B,GAAG,EAAE,GAAG,CAAA;IACR,6BAA6B;IAC7B,IAAI,EAAE,IAAI,CAAA;IAEV,2CAA2C;IAC3C,SAAS,CAAC,UAAU,UAAQ;IAE5B;;;;;OAKG;gBACS,SAAS,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI;IAM1D;;OAEG;IACH,aAAa,IAAI,qBAAqB;IAOtC;;OAEG;IACH,OAAO;IAIP,6CAA6C;IAC7C,IAAI,IAAI,WAEP;IAED;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,IAAI,IAAI;IAEjC;;;OAGG;IACH,UAAU;IAUV;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS;IAEvD;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,CAAC;IAEpE;;OAEG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;IAC/D,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM;IAa5C;;OAEG;IACH,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,CAAC,GAAG,IAAI;IAMnC;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC;IAQ5B;;OAEG;IACH,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE;IAU1C;;OAEG;IACF,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC;IAMlC;;OAEG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAO7B;;OAEG;IACF,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC;IAQvB;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE;IAStC;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa;CAGhD"}
@@ -1,40 +1,102 @@
1
- import { Ids } from "./ids";
2
- import { Tags } from "./tags";
1
+ /**
2
+ * Base class for OSM entity collections.
3
+ *
4
+ * Provides common ID and tag storage, streaming iteration, and sorted access.
5
+ * Subclasses implement entity-specific storage and spatial indexing.
6
+ *
7
+ * @module
8
+ */
9
+ /**
10
+ * Abstract base for typed entity collections.
11
+ *
12
+ * Lifecycle:
13
+ * 1. **Ingest**: `addEntity()` (no lookups).
14
+ * 2. **Finalize**: `buildIndex()`.
15
+ * 3. **Query**: Lookups and iteration enabled.
16
+ */
3
17
  export class Entities {
18
+ /** The type of entity stored in this collection ("node", "way", or "relation"). */
4
19
  indexType;
5
- stringTable;
20
+ /** ID storage and lookup */
6
21
  ids;
22
+ /** Tag storage and search */
7
23
  tags;
24
+ /** Whether buildIndex() has been called */
8
25
  indexBuilt = false;
9
- constructor(indexType, stringTable, ids, tags) {
10
- this.stringTable = stringTable;
26
+ /**
27
+ * Create a new Entities collection.
28
+ * @param indexType - The entity type ("node", "way", or "relation").
29
+ * @param ids - The ID storage instance.
30
+ * @param tags - The tag storage instance.
31
+ */
32
+ constructor(indexType, ids, tags) {
11
33
  this.indexType = indexType;
12
- this.ids = ids ?? new Ids();
13
- this.tags = tags ?? new Tags(stringTable);
34
+ this.ids = ids;
35
+ this.tags = tags;
14
36
  }
15
- get isReady() {
16
- return this.ids.isReady && this.tags.isReady && this.indexBuilt;
37
+ /**
38
+ * Get transferable objects for passing to another thread.
39
+ */
40
+ transferables() {
41
+ return {
42
+ ...this.ids.transferables(),
43
+ ...this.tags.transferables(),
44
+ };
17
45
  }
46
+ /**
47
+ * Check if the index is built and ready for use.
48
+ */
49
+ isReady() {
50
+ return this.ids.isReady() && this.tags.isReady() && this.indexBuilt;
51
+ }
52
+ /** Number of entities in this collection. */
18
53
  get size() {
19
54
  return this.ids.size;
20
55
  }
56
+ /**
57
+ * Finalize indexes.
58
+ * Must be called after adding entities and before querying.
59
+ */
21
60
  buildIndex() {
22
- console.time(`${this.indexType}Index.finish`);
61
+ if (this.indexBuilt)
62
+ return;
63
+ console.time(`${this.indexType}Index.buildIndex`);
23
64
  this.ids.buildIndex();
24
65
  this.tags.buildIndex();
25
66
  this.buildEntityIndex();
26
67
  this.indexBuilt = true;
27
- console.timeEnd(`${this.indexType}Index.finish`);
68
+ console.timeEnd(`${this.indexType}Index.buildIndex`);
69
+ }
70
+ addEntity(id, tags, values) {
71
+ const entityIndex = this.ids.add(id);
72
+ if (Array.isArray(tags)) {
73
+ if (values === undefined)
74
+ throw Error("Values are required when tags is an array");
75
+ this.tags.addTagKeysAndValues(entityIndex, tags, values);
76
+ }
77
+ else {
78
+ this.tags.addTags(entityIndex, tags);
79
+ }
80
+ return entityIndex;
28
81
  }
82
+ /**
83
+ * Get an entity by ID or index.
84
+ */
29
85
  get(idOrIndex) {
30
86
  const [index, id] = this.ids.idOrIndex(idOrIndex);
31
87
  if (index === -1)
32
88
  return null;
33
89
  return this.getFullEntity(index, id, this.tags.getTags(index));
34
90
  }
91
+ /**
92
+ * Get an entity by index.
93
+ */
35
94
  getByIndex(index) {
36
95
  return this.getFullEntity(index, this.ids.at(index), this.tags.getTags(index));
37
96
  }
97
+ /**
98
+ * Get multiple entities by their indexes.
99
+ */
38
100
  getEntitiesByIndex(indexes) {
39
101
  const entities = [];
40
102
  for (const index of indexes) {
@@ -46,17 +108,26 @@ export class Entities {
46
108
  }
47
109
  return entities;
48
110
  }
111
+ /**
112
+ * Iterate over all entities in the index.
113
+ */
49
114
  *[Symbol.iterator]() {
50
115
  for (let i = 0; i < this.size; i++) {
51
116
  yield this.getFullEntity(i, this.ids.at(i), this.tags.getTags(i));
52
117
  }
53
118
  }
119
+ /**
120
+ * Get an entity by ID.
121
+ */
54
122
  getById(id) {
55
123
  const index = this.ids.getIndexFromId(id);
56
124
  if (index !== -1)
57
125
  return this.getFullEntity(index, id, this.tags.getTags(index));
58
126
  return null;
59
127
  }
128
+ /**
129
+ * Iterate over entities sorted by ID.
130
+ */
60
131
  *sorted() {
61
132
  for (const id of this.ids.sorted) {
62
133
  const index = this.ids.getIndexFromId(id);
@@ -65,5 +136,24 @@ export class Entities {
65
136
  yield this.getFullEntity(index, id, this.tags.getTags(index));
66
137
  }
67
138
  }
139
+ /**
140
+ * Search for entities with a specific tag key and optional value.
141
+ */
142
+ search(key, val) {
143
+ const keyIndex = this.tags.find(key);
144
+ const entities = this.tags
145
+ .hasKey(keyIndex)
146
+ .map((index) => this.getByIndex(index));
147
+ if (val === undefined)
148
+ return entities;
149
+ return entities.filter((entity) => entity.tags?.[key] === val);
150
+ }
151
+ /**
152
+ * Update a ContentHasher with this entity collection's base data (IDs and tags).
153
+ * Subclasses should override to add entity-specific data.
154
+ */
155
+ updateHash(hasher) {
156
+ return this.ids.updateHash(this.tags.updateHash(hasher));
157
+ }
68
158
  }
69
159
  //# sourceMappingURL=entities.js.map