@osmix/core 0.0.1 → 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.
- package/CHANGELOG.md +52 -0
- package/README.md +118 -107
- package/dist/core/src/entities.d.ts +114 -0
- package/dist/core/src/entities.d.ts.map +1 -0
- package/dist/core/src/entities.js +152 -0
- package/dist/core/src/entities.js.map +1 -0
- package/dist/core/src/ids.d.ts +113 -0
- package/dist/core/src/ids.d.ts.map +1 -0
- package/dist/core/src/ids.js +236 -0
- package/dist/core/src/ids.js.map +1 -0
- package/dist/core/src/index.d.ts +32 -0
- package/dist/core/src/index.d.ts.map +1 -0
- package/dist/core/src/index.js +35 -0
- package/dist/core/src/index.js.map +1 -0
- package/dist/core/src/nodes.d.ts +101 -0
- package/dist/core/src/nodes.d.ts.map +1 -0
- package/dist/core/src/nodes.js +309 -0
- package/dist/core/src/nodes.js.map +1 -0
- package/dist/core/src/osm.d.ts +74 -0
- package/dist/core/src/osm.d.ts.map +1 -0
- package/dist/core/src/osm.js +120 -0
- package/dist/core/src/osm.js.map +1 -0
- package/dist/core/src/relations.d.ts +118 -0
- package/dist/core/src/relations.d.ts.map +1 -0
- package/dist/core/src/relations.js +412 -0
- package/dist/core/src/relations.js.map +1 -0
- package/dist/core/src/stringtable.d.ts +95 -0
- package/dist/core/src/stringtable.d.ts.map +1 -0
- package/dist/core/src/stringtable.js +170 -0
- package/dist/core/src/stringtable.js.map +1 -0
- package/dist/core/src/tags.d.ts +126 -0
- package/dist/core/src/tags.d.ts.map +1 -0
- package/dist/core/src/tags.js +228 -0
- package/dist/core/src/tags.js.map +1 -0
- package/dist/core/src/typed-arrays.d.ts +124 -0
- package/dist/core/src/typed-arrays.d.ts.map +1 -0
- package/dist/core/src/typed-arrays.js +198 -0
- package/dist/core/src/typed-arrays.js.map +1 -0
- package/dist/core/src/utils.d.ts +11 -0
- package/dist/core/src/utils.d.ts.map +1 -0
- package/dist/core/src/utils.js +26 -0
- package/dist/core/src/utils.js.map +1 -0
- package/dist/core/src/ways.d.ts +101 -0
- package/dist/core/src/ways.d.ts.map +1 -0
- package/dist/core/src/ways.js +336 -0
- package/dist/core/src/ways.js.map +1 -0
- package/dist/core/test/memory.test.d.ts +7 -0
- package/dist/core/test/memory.test.d.ts.map +1 -0
- package/dist/core/test/memory.test.js +29 -0
- package/dist/core/test/memory.test.js.map +1 -0
- package/dist/core/test/mock-osm.d.ts +13 -0
- package/dist/core/test/mock-osm.d.ts.map +1 -0
- package/dist/core/test/mock-osm.js +122 -0
- package/dist/core/test/mock-osm.js.map +1 -0
- package/dist/core/test/tags-search.test.d.ts +2 -0
- package/dist/core/test/tags-search.test.d.ts.map +1 -0
- package/dist/core/test/tags-search.test.js +56 -0
- package/dist/core/test/tags-search.test.js.map +1 -0
- package/dist/core/test/typed-arrays.test.d.ts +2 -0
- package/dist/core/test/typed-arrays.test.d.ts.map +1 -0
- package/dist/core/test/typed-arrays.test.js +102 -0
- package/dist/core/test/typed-arrays.test.js.map +1 -0
- package/dist/osmix/src/extract.d.ts +43 -0
- package/dist/osmix/src/extract.d.ts.map +1 -0
- package/dist/osmix/src/extract.js +156 -0
- package/dist/osmix/src/extract.js.map +1 -0
- package/dist/osmix/src/pbf.d.ts +55 -0
- package/dist/osmix/src/pbf.d.ts.map +1 -0
- package/dist/osmix/src/pbf.js +187 -0
- package/dist/osmix/src/pbf.js.map +1 -0
- package/dist/osmix/src/utils.d.ts +36 -0
- package/dist/osmix/src/utils.d.ts.map +1 -0
- package/dist/osmix/src/utils.js +108 -0
- package/dist/osmix/src/utils.js.map +1 -0
- package/dist/osmix/test/extract.bench.d.ts +2 -0
- package/dist/osmix/test/extract.bench.d.ts.map +1 -0
- package/dist/osmix/test/extract.bench.js +26 -0
- package/dist/osmix/test/extract.bench.js.map +1 -0
- package/dist/osmix/test/extract.test.d.ts +2 -0
- package/dist/osmix/test/extract.test.d.ts.map +1 -0
- package/dist/osmix/test/extract.test.js +392 -0
- package/dist/osmix/test/extract.test.js.map +1 -0
- package/package.json +11 -11
- package/src/entities.ts +138 -22
- package/src/ids.ts +117 -45
- package/src/index.ts +34 -5
- package/src/nodes.ts +237 -58
- package/src/osm.ts +174 -0
- package/src/relations.ts +383 -54
- package/src/stringtable.ts +111 -36
- package/src/tags.ts +207 -46
- package/src/typed-arrays.ts +97 -10
- package/src/types/geoflatbush.d.ts +37 -0
- package/src/types/geokdbush.d.ts +37 -0
- package/src/types/kdbush.d.ts +24 -6
- package/src/utils.ts +9 -14
- package/src/ways.ts +248 -84
- package/test/memory.test.ts +38 -0
- package/test/mock-osm.ts +8 -8
- package/test/tags-search.test.ts +60 -0
- package/test/typed-arrays.test.ts +5 -6
- package/tsconfig.json +7 -1
- package/src/osmix.ts +0 -556
- package/test/extract.bench.ts +0 -29
- package/test/extract.test.ts +0 -156
- package/test/read.test.ts +0 -22
- package/test/write.test.ts +0 -66
package/src/stringtable.ts
CHANGED
|
@@ -1,44 +1,76 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Deduplicated UTF-8 string storage.
|
|
3
|
+
*
|
|
4
|
+
* Strings are stored in a flat buffer and referenced by index.
|
|
5
|
+
* Supports fast deduplication (string→index) and lazy reconstruction (index→string).
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
3
9
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
10
|
+
import type { OsmPbfStringTable } from "@osmix/pbf"
|
|
11
|
+
import { type BufferType, ResizeableTypedArray as RTA } from "./typed-arrays"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Serializable state for worker transfer.
|
|
15
|
+
*/
|
|
16
|
+
export interface StringTableTransferables<T extends BufferType = BufferType> {
|
|
17
|
+
/** Concatenated UTF-8 bytes. */
|
|
18
|
+
bytes: T
|
|
19
|
+
/** Maps string index → byte offset. */
|
|
20
|
+
start: T
|
|
21
|
+
/** Maps string index → byte length. */
|
|
22
|
+
count: T
|
|
8
23
|
}
|
|
9
24
|
|
|
10
25
|
/**
|
|
11
|
-
*
|
|
26
|
+
* Append-only deduplicated string table.
|
|
27
|
+
*
|
|
28
|
+
* Limits: Max string length 65,535 bytes.
|
|
29
|
+
* Rebuilds reverse index lazily after transfer.
|
|
12
30
|
*/
|
|
13
31
|
export default class StringTable {
|
|
32
|
+
/** UTF-8 encoder for string→bytes conversion */
|
|
14
33
|
private enc = new TextEncoder()
|
|
34
|
+
/** UTF-8 decoder for bytes→string conversion */
|
|
15
35
|
private dec = new TextDecoder() // UTF-8
|
|
16
36
|
|
|
17
|
-
// Serializable
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
37
|
+
// ─── Serializable State ────────────────────────────────────────────────────
|
|
38
|
+
/** Concatenated UTF-8 bytes of all strings */
|
|
39
|
+
bytes: RTA<Uint8Array>
|
|
40
|
+
/** Maps string index → byte offset in bytes array */
|
|
41
|
+
start: RTA<Uint32Array>
|
|
42
|
+
/** Maps string index → byte length */
|
|
43
|
+
count: RTA<Uint16Array>
|
|
21
44
|
|
|
22
|
-
// Builder
|
|
45
|
+
// ─── Builder State (string → index) ────────────────────────────────────────
|
|
46
|
+
/** Forward lookup: string → index (populated during add) */
|
|
23
47
|
private stringToIndex = new Map<string, number>()
|
|
24
48
|
|
|
25
|
-
|
|
26
|
-
private
|
|
49
|
+
/** Whether the reverse index has been built (lazy after transfer) */
|
|
50
|
+
private reverseIndexBuilt = false
|
|
27
51
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
builder.start = ResizeableTypedArray.from(Uint32Array, start)
|
|
32
|
-
builder.count = ResizeableTypedArray.from(Uint16Array, count)
|
|
33
|
-
return builder
|
|
34
|
-
}
|
|
52
|
+
// ─── Retrieval Cache (index → string) ──────────────────────────────────────
|
|
53
|
+
/** Cache of decoded strings to avoid repeated UTF-8 decoding */
|
|
54
|
+
private indexToString = new Map<number, string>()
|
|
35
55
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Create a new StringTable.
|
|
58
|
+
*/
|
|
59
|
+
constructor(opts?: StringTableTransferables) {
|
|
60
|
+
this.bytes = opts?.bytes
|
|
61
|
+
? RTA.from(Uint8Array, opts.bytes)
|
|
62
|
+
: new RTA(Uint8Array)
|
|
63
|
+
this.start = opts?.start
|
|
64
|
+
? RTA.from(Uint32Array, opts.start)
|
|
65
|
+
: new RTA(Uint32Array)
|
|
66
|
+
this.count = opts?.count
|
|
67
|
+
? RTA.from(Uint16Array, opts.count)
|
|
68
|
+
: new RTA(Uint16Array)
|
|
40
69
|
}
|
|
41
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Get transferable objects for passing to another thread.
|
|
73
|
+
*/
|
|
42
74
|
transferables(): StringTableTransferables {
|
|
43
75
|
return {
|
|
44
76
|
bytes: this.bytes.array.buffer,
|
|
@@ -47,6 +79,9 @@ export default class StringTable {
|
|
|
47
79
|
}
|
|
48
80
|
}
|
|
49
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Add a string to the table and return its index.
|
|
84
|
+
*/
|
|
50
85
|
add(str: string): number {
|
|
51
86
|
const existingIndex = this.stringToIndex.get(str)
|
|
52
87
|
if (existingIndex !== undefined) return existingIndex
|
|
@@ -63,19 +98,24 @@ export default class StringTable {
|
|
|
63
98
|
* Decode all the strings in a primitive block and add them to the string table.
|
|
64
99
|
* Return a mapping of block index -> string table index
|
|
65
100
|
*/
|
|
66
|
-
createBlockIndexMap(
|
|
67
|
-
const
|
|
68
|
-
for (let i = 0; i <
|
|
69
|
-
const bytesString =
|
|
101
|
+
createBlockIndexMap(blockStringtable: OsmPbfStringTable) {
|
|
102
|
+
const index = new Uint32Array(blockStringtable.length)
|
|
103
|
+
for (let i = 0; i < blockStringtable.length; i++) {
|
|
104
|
+
const bytesString = blockStringtable[i]
|
|
70
105
|
const str = this.dec.decode(bytesString)
|
|
71
|
-
const
|
|
72
|
-
|
|
106
|
+
const existingIndex = this.stringToIndex.get(str)
|
|
107
|
+
if (existingIndex !== undefined) {
|
|
108
|
+
index[i] = existingIndex
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
index[i] = this.add(str)
|
|
73
112
|
}
|
|
74
|
-
return
|
|
113
|
+
return index
|
|
75
114
|
}
|
|
76
115
|
|
|
77
116
|
/**
|
|
78
|
-
*
|
|
117
|
+
* Get a string by its index.
|
|
118
|
+
* Caches results to avoid repeated UTF-8 decoding.
|
|
79
119
|
*/
|
|
80
120
|
get(index: number): string {
|
|
81
121
|
const string = this.indexToString.get(index)
|
|
@@ -89,27 +129,39 @@ export default class StringTable {
|
|
|
89
129
|
return decoded
|
|
90
130
|
}
|
|
91
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Get the raw UTF-8 bytes of a string.
|
|
134
|
+
* Returns a subarray view (not a copy).
|
|
135
|
+
*/
|
|
92
136
|
getBytes(index: number): Uint8Array {
|
|
93
137
|
if (index < 0 || index >= this.length)
|
|
94
138
|
throw Error(`String index out of range: ${index}`)
|
|
95
139
|
const start = this.start.at(index)
|
|
96
140
|
const count = this.count.at(index)
|
|
97
|
-
// Important: subarray creates a *view*, not a copy; TextDecoder cannot read from it directly.
|
|
98
141
|
return this.bytes.array.subarray(start, start + count)
|
|
99
142
|
}
|
|
100
143
|
|
|
144
|
+
/** Number of strings in the table. */
|
|
101
145
|
get length() {
|
|
102
146
|
return this.start.length
|
|
103
147
|
}
|
|
104
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Finalize the string table by compacting internal arrays.
|
|
151
|
+
*
|
|
152
|
+
* This releases unused buffer capacity and marks the reverse index as built
|
|
153
|
+
* (since all strings were added before this call).
|
|
154
|
+
*/
|
|
105
155
|
buildIndex() {
|
|
106
156
|
this.bytes.compact()
|
|
107
157
|
this.start.compact()
|
|
108
158
|
this.count.compact()
|
|
109
|
-
|
|
110
|
-
this.stringToIndex.clear()
|
|
159
|
+
this.reverseIndexBuilt = true
|
|
111
160
|
}
|
|
112
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Convert the string table to an OSM PBF string table.
|
|
164
|
+
*/
|
|
113
165
|
toOsmPbfStringTable(): OsmPbfStringTable {
|
|
114
166
|
const stringTable: OsmPbfStringTable = []
|
|
115
167
|
for (let i = 0; i < this.length; i++) {
|
|
@@ -117,4 +169,27 @@ export default class StringTable {
|
|
|
117
169
|
}
|
|
118
170
|
return stringTable
|
|
119
171
|
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Lazily build the reverse index (string → index).
|
|
175
|
+
* Decodes all strings once to populate the lookup map.
|
|
176
|
+
*/
|
|
177
|
+
private ensureReverseIndex() {
|
|
178
|
+
if (this.reverseIndexBuilt) return
|
|
179
|
+
// Decode all strings and populate the forward lookup map
|
|
180
|
+
for (let i = 0; i < this.length; i++) {
|
|
181
|
+
this.stringToIndex.set(this.get(i), i)
|
|
182
|
+
}
|
|
183
|
+
this.reverseIndexBuilt = true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Find the index of a string.
|
|
188
|
+
*/
|
|
189
|
+
find(str: string): number {
|
|
190
|
+
const existing = this.stringToIndex.get(str)
|
|
191
|
+
if (existing !== undefined) return existing
|
|
192
|
+
this.ensureReverseIndex()
|
|
193
|
+
return this.stringToIndex.get(str) ?? -1
|
|
194
|
+
}
|
|
120
195
|
}
|
package/src/tags.ts
CHANGED
|
@@ -1,54 +1,108 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Tag storage and lookup.
|
|
3
|
+
*
|
|
4
|
+
* Stores key=value pairs using string table indices. Supports:
|
|
5
|
+
* 1. **Entity → Tags**: Retrieve tags for a given entity.
|
|
6
|
+
* 2. **Key → Entities**: Find entities with a specific tag key.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { OsmTags } from "@osmix/shared/types"
|
|
12
|
+
import StringTable from "./stringtable"
|
|
13
|
+
import { type BufferType, ResizeableTypedArray as RTA } from "./typed-arrays"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Serializable state for worker transfer.
|
|
17
|
+
*/
|
|
18
|
+
export interface TagsTransferables<T extends BufferType = BufferType> {
|
|
19
|
+
/** Maps entity index → start position in tagKeys/tagVals. */
|
|
20
|
+
tagStart: T
|
|
21
|
+
/** Maps entity index → number of tags. */
|
|
22
|
+
tagCount: T
|
|
23
|
+
/** Flattened tag key indices. */
|
|
24
|
+
tagKeys: T
|
|
25
|
+
/** Flattened tag value indices. */
|
|
26
|
+
tagVals: T
|
|
27
|
+
|
|
28
|
+
/** Flattened entity indices for reverse key lookup. */
|
|
29
|
+
keyEntities: T
|
|
30
|
+
/** Maps key index → start position in keyEntities. */
|
|
31
|
+
keyIndexStart: T
|
|
32
|
+
/** Maps key index → count of entities with that key. */
|
|
33
|
+
keyIndexCount: T
|
|
10
34
|
}
|
|
11
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Bidirectional tag storage.
|
|
38
|
+
*
|
|
39
|
+
* Limits: Max 255 tags per entity.
|
|
40
|
+
* Note: String indices reference a shared `StringTable`.
|
|
41
|
+
*/
|
|
12
42
|
export class Tags {
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
private tagCount: ResizeableTypedArray<Uint8Array>
|
|
16
|
-
private tagKeys: ResizeableTypedArray<Uint32Array>
|
|
17
|
-
private tagVals: ResizeableTypedArray<Uint32Array>
|
|
43
|
+
/** Reference to the shared string table for key/value storage */
|
|
44
|
+
private stringTable: StringTable = new StringTable()
|
|
18
45
|
|
|
19
|
-
|
|
46
|
+
// ─── Entity → Tag Lookup ───────────────────────────────────────────────────
|
|
47
|
+
/** Maps entity index → start position in tagKeys/tagVals */
|
|
48
|
+
private tagStart: RTA<Uint32Array>
|
|
49
|
+
/** Maps entity index → number of tags (max 255) */
|
|
50
|
+
private tagCount: RTA<Uint8Array>
|
|
51
|
+
/** All tag key string indices, concatenated */
|
|
52
|
+
private tagKeys: RTA<Uint32Array>
|
|
53
|
+
/** All tag value string indices, concatenated (parallel to tagKeys) */
|
|
54
|
+
private tagVals: RTA<Uint32Array>
|
|
20
55
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
tagIndex.tagCount = ResizeableTypedArray.from(Uint8Array, tagCount)
|
|
28
|
-
tagIndex.tagKeys = ResizeableTypedArray.from(Uint32Array, tagKeys)
|
|
29
|
-
tagIndex.tagVals = ResizeableTypedArray.from(Uint32Array, tagVals)
|
|
30
|
-
tagIndex.indexBuilt = true
|
|
31
|
-
return tagIndex
|
|
32
|
-
}
|
|
56
|
+
// ─── Key → Entity Reverse Lookup ───────────────────────────────────────────
|
|
57
|
+
/**
|
|
58
|
+
* Flattened array of entity indices that have each tag key.
|
|
59
|
+
* Indexed via keyIndexStart and keyIndexCount.
|
|
60
|
+
*/
|
|
61
|
+
private keyEntities: RTA<Uint32Array>
|
|
33
62
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
this.tagKeys = new ResizeableTypedArray(Uint32Array)
|
|
39
|
-
this.tagVals = new ResizeableTypedArray(Uint32Array)
|
|
40
|
-
}
|
|
63
|
+
/** Maps key string index → start position in keyEntities */
|
|
64
|
+
private keyIndexStart: RTA<Uint32Array>
|
|
65
|
+
/** Maps key string index → count of entities with that key */
|
|
66
|
+
private keyIndexCount: RTA<Uint32Array>
|
|
41
67
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Temporary map used during ingestion to collect entity indices per key.
|
|
70
|
+
* Converted to flat arrays during buildIndex() and then cleared.
|
|
71
|
+
*/
|
|
72
|
+
private keyEntityIndexBuilder = new Map<number, number[]>()
|
|
73
|
+
|
|
74
|
+
/** Whether buildIndex() has been called */
|
|
75
|
+
private indexBuilt = false
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Create a new Tags index.
|
|
79
|
+
*/
|
|
80
|
+
constructor(stringTable: StringTable, transferables?: TagsTransferables) {
|
|
81
|
+
this.stringTable = stringTable
|
|
82
|
+
if (transferables) {
|
|
83
|
+
this.tagStart = RTA.from(Uint32Array, transferables.tagStart)
|
|
84
|
+
this.tagCount = RTA.from(Uint8Array, transferables.tagCount)
|
|
85
|
+
this.tagKeys = RTA.from(Uint32Array, transferables.tagKeys)
|
|
86
|
+
this.tagVals = RTA.from(Uint32Array, transferables.tagVals)
|
|
87
|
+
this.keyEntities = RTA.from(Uint32Array, transferables.keyEntities)
|
|
88
|
+
this.keyIndexStart = RTA.from(Uint32Array, transferables.keyIndexStart)
|
|
89
|
+
this.keyIndexCount = RTA.from(Uint32Array, transferables.keyIndexCount)
|
|
90
|
+
this.indexBuilt = true
|
|
91
|
+
} else {
|
|
92
|
+
this.tagStart = new RTA(Uint32Array)
|
|
93
|
+
this.tagCount = new RTA(Uint8Array)
|
|
94
|
+
this.tagKeys = new RTA(Uint32Array)
|
|
95
|
+
this.tagVals = new RTA(Uint32Array)
|
|
96
|
+
this.keyEntities = new RTA(Uint32Array)
|
|
97
|
+
this.keyIndexStart = new RTA(Uint32Array)
|
|
98
|
+
this.keyIndexCount = new RTA(Uint32Array)
|
|
48
99
|
}
|
|
49
100
|
}
|
|
50
101
|
|
|
51
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Add tags to an entity.
|
|
104
|
+
*/
|
|
105
|
+
addTags(index: number, tags?: OsmTags): [number[], number[]] {
|
|
52
106
|
const tagKeys: number[] = []
|
|
53
107
|
const tagValues: number[] = []
|
|
54
108
|
|
|
@@ -59,32 +113,73 @@ export class Tags {
|
|
|
59
113
|
}
|
|
60
114
|
}
|
|
61
115
|
|
|
62
|
-
this.addTagKeysAndValues(tagKeys, tagValues)
|
|
116
|
+
this.addTagKeysAndValues(index, tagKeys, tagValues)
|
|
117
|
+
|
|
118
|
+
return [tagKeys, tagValues]
|
|
63
119
|
}
|
|
64
120
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Add tags to an entity using key and value indexes.
|
|
123
|
+
*/
|
|
124
|
+
addTagKeysAndValues(index: number, keys: number[], values: number[]) {
|
|
125
|
+
this.tagStart.set(index, this.tagKeys.length)
|
|
126
|
+
this.tagCount.set(index, keys.length)
|
|
68
127
|
this.tagKeys.pushMany(keys)
|
|
69
128
|
this.tagVals.pushMany(values)
|
|
129
|
+
|
|
130
|
+
keys.forEach((key) => {
|
|
131
|
+
const keyEntities = this.keyEntityIndexBuilder.get(key)
|
|
132
|
+
if (keyEntities) {
|
|
133
|
+
keyEntities.push(index)
|
|
134
|
+
} else {
|
|
135
|
+
this.keyEntityIndexBuilder.set(key, [index])
|
|
136
|
+
}
|
|
137
|
+
})
|
|
70
138
|
}
|
|
71
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Finalize the tag index.
|
|
142
|
+
*
|
|
143
|
+
* Compacts arrays and builds the reverse key→entity index.
|
|
144
|
+
* Must be called before `hasKey()`.
|
|
145
|
+
*/
|
|
72
146
|
buildIndex() {
|
|
73
147
|
this.tagStart.compact()
|
|
74
148
|
this.tagCount.compact()
|
|
75
149
|
this.tagKeys.compact()
|
|
76
150
|
this.tagVals.compact()
|
|
151
|
+
|
|
152
|
+
// Convert the builder map to flat arrays for the reverse index
|
|
153
|
+
for (const [keyIndex, entityIndexes] of this.keyEntityIndexBuilder) {
|
|
154
|
+
this.keyIndexStart.set(keyIndex, this.keyEntities.length)
|
|
155
|
+
this.keyIndexCount.set(keyIndex, entityIndexes.length)
|
|
156
|
+
this.keyEntities.pushMany(entityIndexes)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.keyIndexStart.compact()
|
|
160
|
+
this.keyIndexCount.compact()
|
|
161
|
+
this.keyEntityIndexBuilder.clear()
|
|
162
|
+
|
|
77
163
|
this.indexBuilt = true
|
|
78
164
|
}
|
|
79
165
|
|
|
80
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Check if the index is built and ready for use.
|
|
168
|
+
*/
|
|
169
|
+
isReady() {
|
|
81
170
|
return this.indexBuilt
|
|
82
171
|
}
|
|
83
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Get the number of tags for an entity.
|
|
175
|
+
*/
|
|
84
176
|
cardinality(index: number): number {
|
|
85
177
|
return this.tagCount.at(index) ?? 0
|
|
86
178
|
}
|
|
87
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Get the tags for an entity.
|
|
182
|
+
*/
|
|
88
183
|
getTags(index: number): OsmTags | undefined {
|
|
89
184
|
const tagCount = this.tagCount.at(index) ?? 0
|
|
90
185
|
if (tagCount === 0) return
|
|
@@ -108,6 +203,9 @@ export class Tags {
|
|
|
108
203
|
return tags
|
|
109
204
|
}
|
|
110
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Get tags from key and value indexes.
|
|
208
|
+
*/
|
|
111
209
|
getTagsFromIndices(keys: number[], values: number[]): OsmTags {
|
|
112
210
|
const tags: OsmTags = {}
|
|
113
211
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -119,4 +217,67 @@ export class Tags {
|
|
|
119
217
|
}
|
|
120
218
|
return tags
|
|
121
219
|
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Find the index of a tag key.
|
|
223
|
+
*/
|
|
224
|
+
find(key: string): number {
|
|
225
|
+
return this.stringTable.find(key)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get all entity indexes that have a specific tag key.
|
|
230
|
+
*/
|
|
231
|
+
hasKey(keyIndex: number): number[] {
|
|
232
|
+
if (keyIndex < 0) return []
|
|
233
|
+
const start = this.keyIndexStart.at(keyIndex) ?? 0
|
|
234
|
+
const count = this.keyIndexCount.at(keyIndex) ?? 0
|
|
235
|
+
return Array.from(this.keyEntities.array.subarray(start, start + count))
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Create a unique composite index for a key=value pair.
|
|
240
|
+
* Uses row-major indexing: `key * width + val`.
|
|
241
|
+
*/
|
|
242
|
+
kvToIndex(key: number, val: number) {
|
|
243
|
+
const width = this.stringTable.length
|
|
244
|
+
return key * width + val
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get transferable objects for passing to another thread.
|
|
249
|
+
*/
|
|
250
|
+
transferables(): TagsTransferables {
|
|
251
|
+
return {
|
|
252
|
+
tagStart: this.tagStart.array.buffer,
|
|
253
|
+
tagCount: this.tagCount.array.buffer,
|
|
254
|
+
tagKeys: this.tagKeys.array.buffer,
|
|
255
|
+
tagVals: this.tagVals.array.buffer,
|
|
256
|
+
keyEntities: this.keyEntities.array.buffer,
|
|
257
|
+
keyIndexStart: this.keyIndexStart.array.buffer,
|
|
258
|
+
keyIndexCount: this.keyIndexCount.array.buffer,
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Get the approximate memory requirements for a given number of tags in bytes.
|
|
264
|
+
*/
|
|
265
|
+
static getBytesRequired(count: number) {
|
|
266
|
+
return (
|
|
267
|
+
count * Uint32Array.BYTES_PER_ELEMENT + // tagStart
|
|
268
|
+
count * Uint8Array.BYTES_PER_ELEMENT // tagCount
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Reconstruct a Tags index from transferable objects.
|
|
274
|
+
*/
|
|
275
|
+
static fromTransferables(
|
|
276
|
+
stringTable: StringTable,
|
|
277
|
+
transferables: TagsTransferables,
|
|
278
|
+
) {
|
|
279
|
+
const tagIndex = new Tags(stringTable, transferables)
|
|
280
|
+
tagIndex.indexBuilt = true
|
|
281
|
+
return tagIndex
|
|
282
|
+
}
|
|
122
283
|
}
|