@xyo-network/quadkey 2.38.8 → 2.39.0-rc.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.
- package/dist/cjs/Quadkey.d.ts +57 -33
- package/dist/cjs/Quadkey.d.ts.map +1 -1
- package/dist/cjs/Quadkey.js +190 -149
- package/dist/cjs/Quadkey.js.map +1 -1
- package/dist/docs.json +949 -514
- package/dist/esm/Quadkey.d.ts +57 -33
- package/dist/esm/Quadkey.d.ts.map +1 -1
- package/dist/esm/Quadkey.js +191 -150
- package/dist/esm/Quadkey.js.map +1 -1
- package/package.json +2 -1
- package/src/Quadkey.spec.ts +1 -1
- package/src/Quadkey.ts +217 -169
package/src/Quadkey.ts
CHANGED
|
@@ -30,8 +30,13 @@ const RelativeDirectionConstantLookup: Record<string, number> = {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export class Quadkey {
|
|
33
|
+
static Zero = Quadkey.from(0, Buffer.alloc(31, 0))
|
|
34
|
+
static root = new Quadkey()
|
|
35
|
+
static type = 'Quadkey'
|
|
36
|
+
|
|
33
37
|
public type = Quadkey.type
|
|
34
38
|
|
|
39
|
+
private _geoJson?: GeoJson
|
|
35
40
|
private key = Buffer.alloc(32)
|
|
36
41
|
|
|
37
42
|
constructor(key = Buffer.alloc(32)) {
|
|
@@ -39,24 +44,114 @@ export class Quadkey {
|
|
|
39
44
|
this.guessZoom()
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
return
|
|
47
|
+
get base10String() {
|
|
48
|
+
return this.bigNumber.toString(10)
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
get base4Hash() {
|
|
52
|
+
const bn = new BigNumber(this.id.toString('hex'), 16)
|
|
53
|
+
const zoom = this.zoom
|
|
54
|
+
if (zoom === 0) {
|
|
55
|
+
return ''
|
|
56
|
+
}
|
|
57
|
+
let quadkeySimple = bn.toString(4)
|
|
58
|
+
while (quadkeySimple.length < zoom) {
|
|
59
|
+
quadkeySimple = `0${quadkeySimple}`
|
|
60
|
+
}
|
|
61
|
+
return quadkeySimple
|
|
62
|
+
}
|
|
47
63
|
|
|
48
|
-
|
|
64
|
+
get base4HashLabel() {
|
|
65
|
+
const hash = this.base4Hash
|
|
66
|
+
return hash.length === 0 ? 'fhr' : hash
|
|
67
|
+
}
|
|
49
68
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
get bigNumber() {
|
|
70
|
+
return new BigNumber(`${this.key.toString('hex')}`, 'hex')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get boundingBox(): MercatorBoundingBox {
|
|
74
|
+
return tileToBoundingBox(this.tile)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get buffer() {
|
|
78
|
+
return this.key
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get center() {
|
|
82
|
+
const result = boundingBoxToCenter(this.boundingBox)
|
|
83
|
+
return new LngLat(result[0], result[1])
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get children() {
|
|
87
|
+
assertEx(this.zoom < MAX_ZOOM - 1, 'Can not get children of bottom tiles')
|
|
88
|
+
const result: Quadkey[] = []
|
|
89
|
+
const shiftedId = bitShiftLeft(bitShiftLeft(this.id))
|
|
90
|
+
for (let i = 0; i < 4; i++) {
|
|
91
|
+
const currentLastByte = shiftedId.readUInt8(shiftedId.length - 1)
|
|
92
|
+
shiftedId.writeUInt8((currentLastByte & 0xfc) | i, shiftedId.length - 1)
|
|
93
|
+
result.push(new Quadkey().setId(shiftedId).setZoom(this.zoom + 1))
|
|
53
94
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
95
|
+
return result
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get gridLocation() {
|
|
99
|
+
const tileData = tileFromQuadkey(this.base4Hash)
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
col: 2 ** tileData[2] - tileData[1] - 1,
|
|
103
|
+
row: tileData[0],
|
|
104
|
+
zoom: tileData[2],
|
|
57
105
|
}
|
|
58
106
|
}
|
|
59
107
|
|
|
108
|
+
get hex() {
|
|
109
|
+
return `0x${this.key.toString('hex')}`
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get id() {
|
|
113
|
+
return this.buffer.slice(1)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get parent(): Quadkey | undefined {
|
|
117
|
+
if (this.zoom > 0) {
|
|
118
|
+
return new Quadkey().setId(bitShiftRight(bitShiftRight(this.id))).setZoom(this.zoom - 1)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get siblings() {
|
|
123
|
+
const siblings = assertEx(this.parent?.children, `siblings: parentChildren ${this.base4Hash}`)
|
|
124
|
+
const filteredSiblings = siblings.filter((quadkey) => this.compareTo(quadkey) !== 0)
|
|
125
|
+
assertEx(filteredSiblings.length === 3, `siblings: expected 3 [${filteredSiblings.length}]`)
|
|
126
|
+
return filteredSiblings
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get tile(): MercatorTile {
|
|
130
|
+
return tileFromQuadkey(this.base4Hash)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get valid() {
|
|
134
|
+
const zoom = this.zoom
|
|
135
|
+
const shift = (MAX_ZOOM - zoom) * 2
|
|
136
|
+
const id = this.id
|
|
137
|
+
let testId = id
|
|
138
|
+
for (let i = 0; i < shift; i++) {
|
|
139
|
+
testId = bitShiftLeft(testId)
|
|
140
|
+
}
|
|
141
|
+
for (let i = 0; i < shift; i++) {
|
|
142
|
+
testId = bitShiftRight(testId)
|
|
143
|
+
}
|
|
144
|
+
return testId.compare(id) === 0
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
get zoom() {
|
|
148
|
+
return this.buffer.readUInt8(0)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static from(zoom: number, id: Buffer) {
|
|
152
|
+
return new Quadkey().setId(id).setZoom(zoom)
|
|
153
|
+
}
|
|
154
|
+
|
|
60
155
|
static fromBase10String(value: string) {
|
|
61
156
|
return new Quadkey(Buffer.from(padHex(new BigNumber(value, 10).toString(16)), 'hex'))
|
|
62
157
|
}
|
|
@@ -66,10 +161,36 @@ export class Quadkey {
|
|
|
66
161
|
return new Quadkey(Buffer.from(padHex(valueToUse), 'hex'))
|
|
67
162
|
}
|
|
68
163
|
|
|
164
|
+
static fromBase4String(value?: string) {
|
|
165
|
+
if (value === 'fhr' || value === '') {
|
|
166
|
+
return Quadkey.root
|
|
167
|
+
}
|
|
168
|
+
if (value && value.length && value.length > 0) {
|
|
169
|
+
const quadkey = new Quadkey(Buffer.from(padHex(new BigNumber(value, 4).toString(16)), 'hex')).setZoom(value.length)
|
|
170
|
+
return quadkey.valid ? quadkey : undefined
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
static fromBoundingBox(boundingBox: MercatorBoundingBox, zoom: number) {
|
|
175
|
+
const tiles = tilesFromBoundingBox(boundingBox, Math.floor(zoom))
|
|
176
|
+
const result: Quadkey[] = []
|
|
177
|
+
for (const tile of tiles) {
|
|
178
|
+
result.push(assertEx(Quadkey.fromTile(tile), 'Bad Quadkey'))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return result
|
|
182
|
+
}
|
|
183
|
+
|
|
69
184
|
static fromBuffer(value: Buffer) {
|
|
70
185
|
return Quadkey.fromBase16String(value.toString('hex'))
|
|
71
186
|
}
|
|
72
187
|
|
|
188
|
+
static fromLngLat(point: LngLatLike, zoom: number) {
|
|
189
|
+
const tile = tileFromPoint(LngLat.convert(point), zoom)
|
|
190
|
+
const quadkeyString = tileToQuadkey(tile)
|
|
191
|
+
return Quadkey.fromBase4String(quadkeyString)
|
|
192
|
+
}
|
|
193
|
+
|
|
73
194
|
static fromString(zoom: number, id: string, base = 10) {
|
|
74
195
|
switch (base) {
|
|
75
196
|
case 10:
|
|
@@ -81,42 +202,43 @@ export class Quadkey {
|
|
|
81
202
|
}
|
|
82
203
|
}
|
|
83
204
|
|
|
84
|
-
|
|
85
|
-
return
|
|
205
|
+
static fromTile(tile: MercatorTile) {
|
|
206
|
+
return Quadkey.fromBase4String(tileToQuadkey(tile))
|
|
86
207
|
}
|
|
87
208
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
209
|
+
childrenByZoom(zoom: number) {
|
|
210
|
+
// if we are limiting by zoom, and we are already at that limit, just return this quadkey
|
|
211
|
+
if (zoom && zoom === this.zoom) {
|
|
212
|
+
return [this]
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// recursively get children
|
|
216
|
+
let deepResult: Quadkey[] = []
|
|
217
|
+
for (const quadkey of this.children) {
|
|
218
|
+
deepResult = deepResult.concat(quadkey.childrenByZoom(zoom))
|
|
219
|
+
}
|
|
220
|
+
return deepResult
|
|
92
221
|
}
|
|
93
222
|
|
|
94
|
-
|
|
95
|
-
return Quadkey.
|
|
223
|
+
clone() {
|
|
224
|
+
return Quadkey.fromBase10String(this.base10String)
|
|
96
225
|
}
|
|
97
226
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
boundingBox.contains(tileBoundingBox.getNorthEast()) ||
|
|
102
|
-
boundingBox.contains(tileBoundingBox.getNorthWest()) ||
|
|
103
|
-
boundingBox.contains(tileBoundingBox.getSouthEast()) ||
|
|
104
|
-
boundingBox.contains(tileBoundingBox.getSouthWest())
|
|
105
|
-
)
|
|
227
|
+
compareTo(quadkey: Quadkey) {
|
|
228
|
+
return this.bigNumber.cmp(quadkey.bigNumber)
|
|
106
229
|
}
|
|
107
230
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
for (const tile of tiles) {
|
|
112
|
-
result.push(assertEx(Quadkey.fromTile(tile), 'Bad Quadkey'))
|
|
113
|
-
}
|
|
231
|
+
equals(obj: Quadkey): boolean {
|
|
232
|
+
return obj.base4HashLabel == this.base4HashLabel
|
|
233
|
+
}
|
|
114
234
|
|
|
115
|
-
|
|
235
|
+
geoJson() {
|
|
236
|
+
this._geoJson = this._geoJson ?? new GeoJson(this.base4Hash)
|
|
237
|
+
return this._geoJson
|
|
116
238
|
}
|
|
117
239
|
|
|
118
|
-
|
|
119
|
-
const hash = this.
|
|
240
|
+
getGridBoundingBox(size: number) {
|
|
241
|
+
const hash = this.base4Hash
|
|
120
242
|
let index = 0
|
|
121
243
|
let left = 0
|
|
122
244
|
let top = 0
|
|
@@ -148,27 +270,24 @@ export class Quadkey {
|
|
|
148
270
|
}
|
|
149
271
|
}
|
|
150
272
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
this.
|
|
154
|
-
return this
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
public setZoom(zoom: number) {
|
|
158
|
-
assertEx(zoom < MAX_ZOOM, `Invalid zoom [${zoom}] max=${MAX_ZOOM}`)
|
|
159
|
-
this.setKey(zoom, this.id)
|
|
160
|
-
return this
|
|
273
|
+
/** @deprecated use .gridLocation instead */
|
|
274
|
+
getGridLocation() {
|
|
275
|
+
return this.gridLocation
|
|
161
276
|
}
|
|
162
277
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
278
|
+
isInBoundingBox(boundingBox: MercatorBoundingBox) {
|
|
279
|
+
const tileBoundingBox = tileToBoundingBox(this.tile)
|
|
280
|
+
return (
|
|
281
|
+
boundingBox.contains(tileBoundingBox.getNorthEast()) ||
|
|
282
|
+
boundingBox.contains(tileBoundingBox.getNorthWest()) ||
|
|
283
|
+
boundingBox.contains(tileBoundingBox.getSouthEast()) ||
|
|
284
|
+
boundingBox.contains(tileBoundingBox.getSouthWest())
|
|
285
|
+
)
|
|
167
286
|
}
|
|
168
287
|
|
|
169
|
-
|
|
288
|
+
relative(direction: string) {
|
|
170
289
|
const directionConstant = assertEx(RelativeDirectionConstantLookup[direction], 'Invalid direction')
|
|
171
|
-
let quadkey = this.
|
|
290
|
+
let quadkey = this.base4Hash
|
|
172
291
|
if (quadkey.length === 0) {
|
|
173
292
|
return this
|
|
174
293
|
}
|
|
@@ -191,123 +310,82 @@ export class Quadkey {
|
|
|
191
310
|
return Quadkey.fromBase4String(quadkey)
|
|
192
311
|
}
|
|
193
312
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return [this]
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// recursively get children
|
|
201
|
-
let deepResult: Quadkey[] = []
|
|
202
|
-
for (const quadkey of this.children()) {
|
|
203
|
-
deepResult = deepResult.concat(quadkey.childrenByZoom(zoom))
|
|
204
|
-
}
|
|
205
|
-
return deepResult
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
public children() {
|
|
209
|
-
assertEx(this.zoom < MAX_ZOOM - 1, 'Can not get children of bottom tiles')
|
|
210
|
-
const result: Quadkey[] = []
|
|
211
|
-
const shiftedId = bitShiftLeft(bitShiftLeft(this.id))
|
|
212
|
-
for (let i = 0; i < 4; i++) {
|
|
213
|
-
const currentLastByte = shiftedId.readUInt8(shiftedId.length - 1)
|
|
214
|
-
shiftedId.writeUInt8((currentLastByte & 0xfc) | i, shiftedId.length - 1)
|
|
215
|
-
result.push(new Quadkey().setId(shiftedId).setZoom(this.zoom + 1))
|
|
216
|
-
}
|
|
217
|
-
return result
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
public siblings() {
|
|
221
|
-
const siblings = assertEx(this.parent()?.children(), `siblings: parentChildren ${this.toBase4Hash()}`)
|
|
222
|
-
const filteredSiblings = siblings.filter((quadkey) => this.compareTo(quadkey) !== 0)
|
|
223
|
-
assertEx(filteredSiblings.length === 3, `siblings: expected 3 [${filteredSiblings.length}]`)
|
|
224
|
-
return filteredSiblings
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
public clone() {
|
|
228
|
-
return Quadkey.fromBase10String(this.toBase10String())
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
public get zoom() {
|
|
232
|
-
return this.toBuffer().readUInt8(0)
|
|
313
|
+
setId(id: Buffer) {
|
|
314
|
+
this.setKey(this.zoom, id)
|
|
315
|
+
return this
|
|
233
316
|
}
|
|
234
317
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
return this._geoJson
|
|
318
|
+
setKey(zoom: number, id: Buffer) {
|
|
319
|
+
id.copy(this.key, this.key.length - id.length)
|
|
320
|
+
this.key.writeUInt8(zoom, 0)
|
|
321
|
+
return this
|
|
240
322
|
}
|
|
241
323
|
|
|
242
|
-
|
|
243
|
-
|
|
324
|
+
setZoom(zoom: number) {
|
|
325
|
+
assertEx(zoom < MAX_ZOOM, `Invalid zoom [${zoom}] max=${MAX_ZOOM}`)
|
|
326
|
+
this.setKey(zoom, this.id)
|
|
244
327
|
return this
|
|
245
328
|
}
|
|
246
329
|
|
|
247
|
-
|
|
248
|
-
|
|
330
|
+
/** @deprecated use .base10String*/
|
|
331
|
+
toBase10String() {
|
|
332
|
+
return this.base10String
|
|
249
333
|
}
|
|
250
334
|
|
|
251
|
-
|
|
252
|
-
|
|
335
|
+
/** @deprecated use .base4Hash */
|
|
336
|
+
toBase4Hash() {
|
|
337
|
+
return this.base4Hash
|
|
253
338
|
}
|
|
254
339
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
340
|
+
/** @deprecated use .base4HashLabel */
|
|
341
|
+
toBase4HashLabel() {
|
|
342
|
+
const hash = this.base4HashLabel
|
|
343
|
+
return hash.length === 0 ? 'fhr' : hash
|
|
258
344
|
}
|
|
259
345
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
col: 2 ** tileData[2] - tileData[1] - 1,
|
|
265
|
-
row: tileData[0],
|
|
266
|
-
zoom: tileData[2],
|
|
267
|
-
}
|
|
346
|
+
/** @deprecated use .bigNumber */
|
|
347
|
+
toBigNumber() {
|
|
348
|
+
return this.bigNumber
|
|
268
349
|
}
|
|
269
350
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const id = this.id
|
|
274
|
-
let testId = id
|
|
275
|
-
for (let i = 0; i < shift; i++) {
|
|
276
|
-
testId = bitShiftLeft(testId)
|
|
277
|
-
}
|
|
278
|
-
for (let i = 0; i < shift; i++) {
|
|
279
|
-
testId = bitShiftRight(testId)
|
|
280
|
-
}
|
|
281
|
-
return testId.compare(id) === 0
|
|
351
|
+
/** @deprecated use .boundingBox */
|
|
352
|
+
toBoundingBox(): MercatorBoundingBox {
|
|
353
|
+
return this.boundingBox
|
|
282
354
|
}
|
|
283
355
|
|
|
284
|
-
|
|
285
|
-
|
|
356
|
+
/** @deprecated use .buffer */
|
|
357
|
+
toBuffer() {
|
|
358
|
+
return this.buffer
|
|
286
359
|
}
|
|
287
360
|
|
|
288
|
-
|
|
289
|
-
|
|
361
|
+
/** @deprecated use .center */
|
|
362
|
+
toCenter() {
|
|
363
|
+
return this.center
|
|
290
364
|
}
|
|
291
365
|
|
|
292
|
-
|
|
293
|
-
|
|
366
|
+
/** @deprecated use .hex instead */
|
|
367
|
+
toHex() {
|
|
368
|
+
return this.hex
|
|
294
369
|
}
|
|
295
370
|
|
|
296
|
-
|
|
297
|
-
return
|
|
371
|
+
toJSON(): string {
|
|
372
|
+
return this.base4HashLabel
|
|
298
373
|
}
|
|
299
374
|
|
|
300
|
-
|
|
301
|
-
|
|
375
|
+
toShortString() {
|
|
376
|
+
const buffer = this.buffer
|
|
377
|
+
const part1 = buffer.slice(0, 2)
|
|
378
|
+
const part2 = buffer.slice(buffer.length - 2, buffer.length)
|
|
379
|
+
return `${part1.toString('hex')}...${part2.toString('hex')}`
|
|
302
380
|
}
|
|
303
381
|
|
|
304
|
-
|
|
305
|
-
return this.
|
|
382
|
+
toString() {
|
|
383
|
+
return `0x${padHex(this.bigNumber.toString(16))}`
|
|
306
384
|
}
|
|
307
385
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return
|
|
386
|
+
/** @deprecated use .tile instead */
|
|
387
|
+
public toTile(): MercatorTile {
|
|
388
|
+
return this.tile
|
|
311
389
|
}
|
|
312
390
|
|
|
313
391
|
protected guessZoom() {
|
|
@@ -315,34 +393,4 @@ export class Quadkey {
|
|
|
315
393
|
const quadkeySimple = bn.toString(4)
|
|
316
394
|
this.setZoom(quadkeySimple.length)
|
|
317
395
|
}
|
|
318
|
-
|
|
319
|
-
public toBase4Hash() {
|
|
320
|
-
const bn = new BigNumber(this.id.toString('hex'), 16)
|
|
321
|
-
const zoom = this.zoom
|
|
322
|
-
if (zoom === 0) {
|
|
323
|
-
return ''
|
|
324
|
-
}
|
|
325
|
-
let quadkeySimple = bn.toString(4)
|
|
326
|
-
while (quadkeySimple.length < zoom) {
|
|
327
|
-
quadkeySimple = `0${quadkeySimple}`
|
|
328
|
-
}
|
|
329
|
-
return quadkeySimple
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
public toHex() {
|
|
333
|
-
return `0x${this.key.toString('hex')}`
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
public toShortString() {
|
|
337
|
-
const buffer = this.toBuffer()
|
|
338
|
-
const part1 = buffer.slice(0, 2)
|
|
339
|
-
const part2 = buffer.slice(buffer.length - 2, buffer.length)
|
|
340
|
-
return `${part1.toString('hex')}...${part2.toString('hex')}`
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
public compareTo(quadkey: Quadkey) {
|
|
344
|
-
return this.toBigNumber().cmp(quadkey.toBigNumber())
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
public static Zero = Quadkey.from(0, Buffer.alloc(31, 0))
|
|
348
396
|
}
|