mirror of
https://github.com/protomaps/PMTiles.git
synced 2026-02-04 10:51:07 +00:00
* make use of ===, if branches, let/const, string templates, var names consistent style.
This commit is contained in:
118
js/adapters.ts
118
js/adapters.ts
@@ -17,17 +17,17 @@ export const leafletRasterLayer = (source: PMTiles, options: any) => {
|
||||
};
|
||||
if (!loaded) {
|
||||
source.getHeader().then((header) => {
|
||||
if (header.tileType == TileType.Mvt) {
|
||||
if (header.tileType === TileType.Mvt) {
|
||||
console.error(
|
||||
"Error: archive contains MVT vector tiles, but leafletRasterLayer is for displaying raster tiles. See https://github.com/protomaps/PMTiles/tree/main/js for details."
|
||||
);
|
||||
} else if (header.tileType == 2) {
|
||||
} else if (header.tileType === 2) {
|
||||
mimeType = "image/png";
|
||||
} else if (header.tileType == 3) {
|
||||
} else if (header.tileType === 3) {
|
||||
mimeType = "image/jpeg";
|
||||
} else if (header.tileType == 4) {
|
||||
} else if (header.tileType === 4) {
|
||||
mimeType = "image/webp";
|
||||
} else if (header.tileType == 5) {
|
||||
} else if (header.tileType === 5) {
|
||||
mimeType = "image/avif";
|
||||
}
|
||||
});
|
||||
@@ -115,7 +115,7 @@ export class Protocol {
|
||||
params: RequestParameters,
|
||||
callback: ResponseCallback
|
||||
): Cancelable => {
|
||||
if (params.type == "json") {
|
||||
if (params.type === "json") {
|
||||
const pmtilesUrl = params.url.substr(10);
|
||||
let instance = this.tiles.get(pmtilesUrl);
|
||||
if (!instance) {
|
||||
@@ -127,7 +127,7 @@ export class Protocol {
|
||||
.getHeader()
|
||||
.then((h) => {
|
||||
const tilejson = {
|
||||
tiles: [params.url + "/{z}/{x}/{y}"],
|
||||
tiles: [`${params.url}/{z}/{x}/{y}`],
|
||||
minzoom: h.minZoom,
|
||||
maxzoom: h.maxZoom,
|
||||
bounds: [h.minLon, h.minLat, h.maxLon, h.maxLat],
|
||||
@@ -141,60 +141,56 @@ export class Protocol {
|
||||
return {
|
||||
cancel: () => {},
|
||||
};
|
||||
} else {
|
||||
const re = new RegExp(/pmtiles:\/\/(.+)\/(\d+)\/(\d+)\/(\d+)/);
|
||||
const result = params.url.match(re);
|
||||
if (!result) {
|
||||
throw new Error("Invalid PMTiles protocol URL");
|
||||
return {
|
||||
cancel: () => {},
|
||||
};
|
||||
}
|
||||
const pmtilesUrl = result[1];
|
||||
|
||||
let instance = this.tiles.get(pmtilesUrl);
|
||||
if (!instance) {
|
||||
instance = new PMTiles(pmtilesUrl);
|
||||
this.tiles.set(pmtilesUrl, instance);
|
||||
}
|
||||
const z = result[2];
|
||||
const x = result[3];
|
||||
const y = result[4];
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const cancel = () => {
|
||||
controller.abort();
|
||||
};
|
||||
|
||||
instance.getHeader().then((header) => {
|
||||
instance!
|
||||
.getZxy(+z, +x, +y, signal)
|
||||
.then((resp) => {
|
||||
if (resp) {
|
||||
callback(
|
||||
null,
|
||||
new Uint8Array(resp.data),
|
||||
resp.cacheControl,
|
||||
resp.expires
|
||||
);
|
||||
} else {
|
||||
if (header.tileType == TileType.Mvt) {
|
||||
callback(null, new Uint8Array(), null, null);
|
||||
} else {
|
||||
callback(null, null, null, null);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if ((e as Error).name !== "AbortError") {
|
||||
callback(e, null, null, null);
|
||||
}
|
||||
});
|
||||
});
|
||||
return {
|
||||
cancel: cancel,
|
||||
};
|
||||
}
|
||||
const re = new RegExp(/pmtiles:\/\/(.+)\/(\d+)\/(\d+)\/(\d+)/);
|
||||
const result = params.url.match(re);
|
||||
if (!result) {
|
||||
throw new Error("Invalid PMTiles protocol URL");
|
||||
}
|
||||
const pmtilesUrl = result[1];
|
||||
|
||||
let instance = this.tiles.get(pmtilesUrl);
|
||||
if (!instance) {
|
||||
instance = new PMTiles(pmtilesUrl);
|
||||
this.tiles.set(pmtilesUrl, instance);
|
||||
}
|
||||
const z = result[2];
|
||||
const x = result[3];
|
||||
const y = result[4];
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const cancel = () => {
|
||||
controller.abort();
|
||||
};
|
||||
|
||||
instance.getHeader().then((header) => {
|
||||
instance
|
||||
?.getZxy(+z, +x, +y, signal)
|
||||
.then((resp) => {
|
||||
if (resp) {
|
||||
callback(
|
||||
null,
|
||||
new Uint8Array(resp.data),
|
||||
resp.cacheControl,
|
||||
resp.expires
|
||||
);
|
||||
} else {
|
||||
if (header.tileType === TileType.Mvt) {
|
||||
callback(null, new Uint8Array(), null, null);
|
||||
} else {
|
||||
callback(null, null, null, null);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if ((e as Error).name !== "AbortError") {
|
||||
callback(e, null, null, null);
|
||||
}
|
||||
});
|
||||
});
|
||||
return {
|
||||
cancel: cancel,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
117
js/index.ts
117
js/index.ts
@@ -13,9 +13,8 @@ function toNum(low: number, high: number): number {
|
||||
|
||||
function readVarintRemainder(l: number, p: BufferPosition): number {
|
||||
const buf = p.buf;
|
||||
let h, b;
|
||||
b = buf[p.pos++];
|
||||
h = (b & 0x70) >> 4;
|
||||
let b = buf[p.pos++];
|
||||
let h = (b & 0x70) >> 4;
|
||||
if (b < 0x80) return toNum(l, h);
|
||||
b = buf[p.pos++];
|
||||
h |= (b & 0x7f) << 3;
|
||||
@@ -37,10 +36,8 @@ function readVarintRemainder(l: number, p: BufferPosition): number {
|
||||
|
||||
export function readVarint(p: BufferPosition): number {
|
||||
const buf = p.buf;
|
||||
let val, b;
|
||||
|
||||
b = buf[p.pos++];
|
||||
val = b & 0x7f;
|
||||
let b = buf[p.pos++];
|
||||
let val = b & 0x7f;
|
||||
if (b < 0x80) return val;
|
||||
b = buf[p.pos++];
|
||||
val |= (b & 0x7f) << 7;
|
||||
@@ -58,8 +55,8 @@ export function readVarint(p: BufferPosition): number {
|
||||
}
|
||||
|
||||
function rotate(n: number, xy: number[], rx: number, ry: number): void {
|
||||
if (ry == 0) {
|
||||
if (rx == 1) {
|
||||
if (ry === 0) {
|
||||
if (rx === 1) {
|
||||
xy[0] = n - 1 - xy[0];
|
||||
xy[1] = n - 1 - xy[1];
|
||||
}
|
||||
@@ -70,7 +67,7 @@ function rotate(n: number, xy: number[], rx: number, ry: number): void {
|
||||
}
|
||||
|
||||
function idOnLevel(z: number, pos: number): [number, number, number] {
|
||||
const n = Math.pow(2, z);
|
||||
const n = 2 ** z;
|
||||
let rx = pos;
|
||||
let ry = pos;
|
||||
let t = pos;
|
||||
@@ -99,12 +96,12 @@ export function zxyToTileId(z: number, x: number, y: number): number {
|
||||
if (z > 26) {
|
||||
throw Error("Tile zoom level exceeds max safe number limit (26)");
|
||||
}
|
||||
if (x > Math.pow(2, z) - 1 || y > Math.pow(2, z) - 1) {
|
||||
if (x > 2 ** z - 1 || y > 2 ** z - 1) {
|
||||
throw Error("tile x/y outside zoom level bounds");
|
||||
}
|
||||
|
||||
const acc = tzValues[z];
|
||||
const n = Math.pow(2, z);
|
||||
const n = 2 ** z;
|
||||
let rx = 0;
|
||||
let ry = 0;
|
||||
let d = 0;
|
||||
@@ -161,19 +158,18 @@ async function defaultDecompress(
|
||||
): Promise<ArrayBuffer> {
|
||||
if (compression === Compression.None || compression === Compression.Unknown) {
|
||||
return buf;
|
||||
} else if (compression === Compression.Gzip) {
|
||||
if (typeof (globalThis as any).DecompressionStream == "undefined") {
|
||||
return decompressSync(new Uint8Array(buf));
|
||||
} else {
|
||||
const stream = new Response(buf).body!;
|
||||
const result: ReadableStream<Uint8Array> = stream.pipeThrough(
|
||||
new (globalThis as any).DecompressionStream("gzip")
|
||||
);
|
||||
return new Response(result).arrayBuffer();
|
||||
}
|
||||
} else {
|
||||
throw Error("Compression method not supported");
|
||||
}
|
||||
if (compression === Compression.Gzip) {
|
||||
if (typeof (globalThis as any).DecompressionStream === "undefined") {
|
||||
return decompressSync(new Uint8Array(buf));
|
||||
}
|
||||
const stream = new Response(buf).body!;
|
||||
const result: ReadableStream<Uint8Array> = stream.pipeThrough(
|
||||
new (globalThis as any).DecompressionStream("gzip")
|
||||
);
|
||||
return new Response(result).arrayBuffer();
|
||||
}
|
||||
throw Error("Compression method not supported");
|
||||
}
|
||||
|
||||
export enum TileType {
|
||||
@@ -310,10 +306,7 @@ export class FetchSource implements Source {
|
||||
}
|
||||
|
||||
const requestHeaders = new Headers(this.customHeaders);
|
||||
requestHeaders.set(
|
||||
"Range",
|
||||
"bytes=" + offset + "-" + (offset + length - 1)
|
||||
);
|
||||
requestHeaders.set("Range", `bytes=${offset}-${offset + length - 1}`);
|
||||
|
||||
let resp = await fetch(this.url, {
|
||||
signal: signal,
|
||||
@@ -333,12 +326,12 @@ export class FetchSource implements Source {
|
||||
const actualLength = +contentRange.substr(8);
|
||||
resp = await fetch(this.url, {
|
||||
signal: signal,
|
||||
headers: { Range: "bytes=0-" + (actualLength - 1) },
|
||||
headers: { range: `bytes=0-${actualLength - 1}` },
|
||||
});
|
||||
}
|
||||
|
||||
if (resp.status >= 300) {
|
||||
throw Error("Bad response code: " + resp.status);
|
||||
throw Error(`Bad response code: ${resp.status}`);
|
||||
}
|
||||
|
||||
const contentLength = resp.headers.get("Content-Length");
|
||||
@@ -365,7 +358,7 @@ export class FetchSource implements Source {
|
||||
export function getUint64(v: DataView, offset: number): number {
|
||||
const wh = v.getUint32(offset + 4, true);
|
||||
const wl = v.getUint32(offset + 0, true);
|
||||
return wh * Math.pow(2, 32) + wl;
|
||||
return wh * 2 ** 32 + wl;
|
||||
}
|
||||
|
||||
export function bytesToHeader(bytes: ArrayBuffer, etag?: string): Header {
|
||||
@@ -447,7 +440,8 @@ function detectVersion(a: ArrayBuffer): number {
|
||||
"PMTiles spec version 2 has been deprecated; please see github.com/protomaps/PMTiles for tools to upgrade"
|
||||
);
|
||||
return 2;
|
||||
} else if (v.getUint16(2, true) === 1) {
|
||||
}
|
||||
if (v.getUint16(2, true) === 1) {
|
||||
console.warn(
|
||||
"PMTiles spec version 1 has been deprecated; please see github.com/protomaps/PMTiles for tools to upgrade"
|
||||
);
|
||||
@@ -496,10 +490,9 @@ async function getHeaderAndRoot(
|
||||
const headerData = resp.data.slice(0, HEADER_SIZE_BYTES);
|
||||
|
||||
let respEtag = resp.etag;
|
||||
if (currentEtag && resp.etag != currentEtag) {
|
||||
if (currentEtag && resp.etag !== currentEtag) {
|
||||
console.warn(
|
||||
"ETag conflict detected; your HTTP server might not support content-based ETag headers. ETags disabled for " +
|
||||
source.getKey()
|
||||
`ETag conflict detected; your HTTP server might not support content-based ETag headers. ETags disabled for ${source.getKey()}`
|
||||
);
|
||||
respEtag = undefined;
|
||||
}
|
||||
@@ -513,14 +506,9 @@ async function getHeaderAndRoot(
|
||||
header.rootDirectoryOffset,
|
||||
header.rootDirectoryOffset + header.rootDirectoryLength
|
||||
);
|
||||
const dirKey =
|
||||
source.getKey() +
|
||||
"|" +
|
||||
(header.etag || "") +
|
||||
"|" +
|
||||
header.rootDirectoryOffset +
|
||||
"|" +
|
||||
header.rootDirectoryLength;
|
||||
const dirKey = `${source.getKey()}|${header.etag || ""}|${
|
||||
header.rootDirectoryOffset
|
||||
}|${header.rootDirectoryLength}`;
|
||||
|
||||
const rootDir = deserializeIndex(
|
||||
await decompress(rootDirData, header.internalCompression)
|
||||
@@ -581,7 +569,7 @@ export class ResolvedValueCache {
|
||||
const cacheKey = source.getKey();
|
||||
if (this.cache.has(cacheKey)) {
|
||||
this.cache.get(cacheKey)!.lastUsed = this.counter++;
|
||||
const data = this.cache.get(cacheKey)!.data;
|
||||
const data = this.cache.get(cacheKey)?.data;
|
||||
return data as Header;
|
||||
}
|
||||
|
||||
@@ -612,11 +600,12 @@ export class ResolvedValueCache {
|
||||
length: number,
|
||||
header: Header
|
||||
): Promise<Entry[]> {
|
||||
const cacheKey =
|
||||
source.getKey() + "|" + (header.etag || "") + "|" + offset + "|" + length;
|
||||
const cacheKey = `${source.getKey()}|${
|
||||
header.etag || ""
|
||||
}|${offset}|${length}`;
|
||||
if (this.cache.has(cacheKey)) {
|
||||
this.cache.get(cacheKey)!.lastUsed = this.counter++;
|
||||
const data = this.cache.get(cacheKey)!.data;
|
||||
const data = this.cache.get(cacheKey)?.data;
|
||||
return data as Entry[];
|
||||
}
|
||||
|
||||
@@ -642,11 +631,12 @@ export class ResolvedValueCache {
|
||||
length: number,
|
||||
header: Header
|
||||
): Promise<ArrayBuffer> {
|
||||
const cacheKey =
|
||||
source.getKey() + "|" + (header.etag || "") + "|" + offset + "|" + length;
|
||||
const cacheKey = `${source.getKey()}|${
|
||||
header.etag || ""
|
||||
}|${offset}|${length}`;
|
||||
if (this.cache.has(cacheKey)) {
|
||||
this.cache.get(cacheKey)!.lastUsed = this.counter++;
|
||||
const data = await this.cache.get(cacheKey)!.data;
|
||||
const data = await this.cache.get(cacheKey)?.data;
|
||||
return data as ArrayBuffer;
|
||||
}
|
||||
|
||||
@@ -716,7 +706,7 @@ export class SharedPromiseCache {
|
||||
const cacheKey = source.getKey();
|
||||
if (this.cache.has(cacheKey)) {
|
||||
this.cache.get(cacheKey)!.lastUsed = this.counter++;
|
||||
const data = await this.cache.get(cacheKey)!.data;
|
||||
const data = await this.cache.get(cacheKey)?.data;
|
||||
return data as Header;
|
||||
}
|
||||
|
||||
@@ -746,11 +736,12 @@ export class SharedPromiseCache {
|
||||
length: number,
|
||||
header: Header
|
||||
): Promise<Entry[]> {
|
||||
const cacheKey =
|
||||
source.getKey() + "|" + (header.etag || "") + "|" + offset + "|" + length;
|
||||
const cacheKey = `${source.getKey()}|${
|
||||
header.etag || ""
|
||||
}|${offset}|${length}`;
|
||||
if (this.cache.has(cacheKey)) {
|
||||
this.cache.get(cacheKey)!.lastUsed = this.counter++;
|
||||
const data = await this.cache.get(cacheKey)!.data;
|
||||
const data = await this.cache.get(cacheKey)?.data;
|
||||
return data as Entry[];
|
||||
}
|
||||
|
||||
@@ -775,11 +766,12 @@ export class SharedPromiseCache {
|
||||
length: number,
|
||||
header: Header
|
||||
): Promise<ArrayBuffer> {
|
||||
const cacheKey =
|
||||
source.getKey() + "|" + (header.etag || "") + "|" + offset + "|" + length;
|
||||
const cacheKey = `${source.getKey()}|${
|
||||
header.etag || ""
|
||||
}|${offset}|${length}`;
|
||||
if (this.cache.has(cacheKey)) {
|
||||
this.cache.get(cacheKey)!.lastUsed = this.counter++;
|
||||
const data = await this.cache.get(cacheKey)!.data;
|
||||
const data = await this.cache.get(cacheKey)?.data;
|
||||
return data as ArrayBuffer;
|
||||
}
|
||||
|
||||
@@ -899,10 +891,9 @@ export class PMTiles {
|
||||
cacheControl: resp.cacheControl,
|
||||
expires: resp.expires,
|
||||
};
|
||||
} else {
|
||||
dO = header.leafDirectoryOffset + entry.offset;
|
||||
dL = entry.length;
|
||||
}
|
||||
dO = header.leafDirectoryOffset + entry.offset;
|
||||
dL = entry.length;
|
||||
} else {
|
||||
// TODO: We should in fact return a valid RangeResponse
|
||||
// with empty data, but filled in cache control / expires headers
|
||||
@@ -924,9 +915,8 @@ export class PMTiles {
|
||||
if (e instanceof EtagMismatch) {
|
||||
this.cache.invalidate(this.source, e.message);
|
||||
return await this.getZxyAttempt(z, x, y, signal);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,9 +945,8 @@ export class PMTiles {
|
||||
if (e instanceof EtagMismatch) {
|
||||
this.cache.invalidate(this.source, e.message);
|
||||
return await this.getMetadataAttempt();
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"test": "tsx test/index.test.ts",
|
||||
"tsc": "tsc --noEmit --watch",
|
||||
"prettier": "prettier --write *.ts test/*.ts",
|
||||
"prettier-check": "prettier --check *.ts test/*.ts"
|
||||
"prettier-check": "prettier --check *.ts test/*.ts",
|
||||
"biome": "biome check adapters.ts index.ts v2.ts test"
|
||||
},
|
||||
"homepage": "https://github.com/protomaps/pmtiles",
|
||||
"author": "Brandon Liu",
|
||||
|
||||
@@ -14,22 +14,22 @@ import {
|
||||
test("stub data", () => {
|
||||
const dataview = new DataView(
|
||||
createDirectory([
|
||||
{ z: 5, x: 1000, y: 2000, offset: 1000, length: 2000, is_dir: false },
|
||||
{ z: 5, x: 1000, y: 2000, offset: 1000, length: 2000, isDir: false },
|
||||
{
|
||||
z: 14,
|
||||
x: 16383,
|
||||
y: 16383,
|
||||
offset: 999999,
|
||||
length: 999,
|
||||
is_dir: false,
|
||||
isDir: false,
|
||||
},
|
||||
])
|
||||
);
|
||||
var zRaw = dataview.getUint8(17 + 0);
|
||||
var x = getUint24(dataview, 17 + 1);
|
||||
var y = getUint24(dataview, 17 + 4);
|
||||
var offset = getUint48(dataview, 17 + 7);
|
||||
var length = dataview.getUint32(17 + 13, true);
|
||||
const zRaw = dataview.getUint8(17 + 0);
|
||||
const x = getUint24(dataview, 17 + 1);
|
||||
const y = getUint24(dataview, 17 + 4);
|
||||
const offset = getUint48(dataview, 17 + 7);
|
||||
const length = dataview.getUint32(17 + 13, true);
|
||||
assert.strictEqual(zRaw, 14);
|
||||
assert.strictEqual(x, 16383);
|
||||
assert.strictEqual(y, 16383);
|
||||
@@ -38,24 +38,24 @@ test("stub data", () => {
|
||||
test("get entry", () => {
|
||||
const view = new DataView(
|
||||
createDirectory([
|
||||
{ z: 5, x: 1000, y: 2000, offset: 1000, length: 2000, is_dir: false },
|
||||
{ z: 5, x: 1000, y: 2000, offset: 1000, length: 2000, isDir: false },
|
||||
{
|
||||
z: 14,
|
||||
x: 16383,
|
||||
y: 16383,
|
||||
offset: 999999,
|
||||
length: 999,
|
||||
is_dir: false,
|
||||
isDir: false,
|
||||
},
|
||||
])
|
||||
);
|
||||
const entry = queryTile(view, 14, 16383, 16383);
|
||||
assert.strictEqual(entry!.z, 14);
|
||||
assert.strictEqual(entry!.x, 16383);
|
||||
assert.strictEqual(entry!.y, 16383);
|
||||
assert.strictEqual(entry!.offset, 999999);
|
||||
assert.strictEqual(entry!.length, 999);
|
||||
assert.strictEqual(entry!.is_dir, false);
|
||||
assert.strictEqual(entry?.z, 14);
|
||||
assert.strictEqual(entry?.x, 16383);
|
||||
assert.strictEqual(entry?.y, 16383);
|
||||
assert.strictEqual(entry?.offset, 999999);
|
||||
assert.strictEqual(entry?.length, 999);
|
||||
assert.strictEqual(entry?.isDir, false);
|
||||
assert.strictEqual(queryLeafdir(view, 14, 16383, 16383), null);
|
||||
});
|
||||
|
||||
@@ -68,17 +68,17 @@ test("get leafdir", () => {
|
||||
y: 16383,
|
||||
offset: 999999,
|
||||
length: 999,
|
||||
is_dir: true,
|
||||
isDir: true,
|
||||
},
|
||||
])
|
||||
);
|
||||
const entry = queryLeafdir(view, 14, 16383, 16383);
|
||||
assert.strictEqual(entry!.z, 14);
|
||||
assert.strictEqual(entry!.x, 16383);
|
||||
assert.strictEqual(entry!.y, 16383);
|
||||
assert.strictEqual(entry!.offset, 999999);
|
||||
assert.strictEqual(entry!.length, 999);
|
||||
assert.strictEqual(entry!.is_dir, true);
|
||||
assert.strictEqual(entry?.z, 14);
|
||||
assert.strictEqual(entry?.x, 16383);
|
||||
assert.strictEqual(entry?.y, 16383);
|
||||
assert.strictEqual(entry?.offset, 999999);
|
||||
assert.strictEqual(entry?.length, 999);
|
||||
assert.strictEqual(entry?.isDir, true);
|
||||
assert.strictEqual(queryTile(view, 14, 16383, 16383), null);
|
||||
});
|
||||
|
||||
@@ -91,14 +91,14 @@ test("derive the leaf level", () => {
|
||||
y: 3,
|
||||
offset: 0,
|
||||
length: 0,
|
||||
is_dir: true,
|
||||
isDir: true,
|
||||
},
|
||||
])
|
||||
);
|
||||
let leaf = deriveLeaf(view, { z: 7, x: 6, y: 6 });
|
||||
assert.strictEqual(leaf!.z, 6);
|
||||
assert.strictEqual(leaf!.x, 3);
|
||||
assert.strictEqual(leaf!.y, 3);
|
||||
assert.strictEqual(leaf?.z, 6);
|
||||
assert.strictEqual(leaf?.x, 3);
|
||||
assert.strictEqual(leaf?.y, 3);
|
||||
view = new DataView(
|
||||
createDirectory([
|
||||
{
|
||||
@@ -107,7 +107,7 @@ test("derive the leaf level", () => {
|
||||
y: 3,
|
||||
offset: 0,
|
||||
length: 0,
|
||||
is_dir: false,
|
||||
isDir: false,
|
||||
},
|
||||
])
|
||||
);
|
||||
@@ -124,7 +124,7 @@ test("convert spec v1 directory to spec v2 directory", () => {
|
||||
y: 3,
|
||||
offset: 3,
|
||||
length: 3,
|
||||
is_dir: true,
|
||||
isDir: true,
|
||||
},
|
||||
{
|
||||
z: 6,
|
||||
@@ -132,7 +132,7 @@ test("convert spec v1 directory to spec v2 directory", () => {
|
||||
y: 2,
|
||||
offset: 2,
|
||||
length: 2,
|
||||
is_dir: false,
|
||||
isDir: false,
|
||||
},
|
||||
{
|
||||
z: 6,
|
||||
@@ -140,21 +140,21 @@ test("convert spec v1 directory to spec v2 directory", () => {
|
||||
y: 1,
|
||||
offset: 1,
|
||||
length: 1,
|
||||
is_dir: false,
|
||||
isDir: false,
|
||||
},
|
||||
])
|
||||
);
|
||||
let entry = queryLeafdir(view, 7, 3, 3);
|
||||
assert.strictEqual(entry!.offset, 3);
|
||||
assert.strictEqual(entry?.offset, 3);
|
||||
entry = queryTile(view, 6, 2, 2);
|
||||
assert.strictEqual(entry!.offset, 2);
|
||||
assert.strictEqual(entry?.offset, 2);
|
||||
entry = queryTile(view, 6, 2, 1);
|
||||
assert.strictEqual(entry!.offset, 1);
|
||||
assert.strictEqual(entry?.offset, 1);
|
||||
|
||||
entry = parseEntry(view, 0);
|
||||
assert.strictEqual(entry!.offset, 1);
|
||||
assert.strictEqual(entry?.offset, 1);
|
||||
entry = parseEntry(view, 1);
|
||||
assert.strictEqual(entry!.offset, 2);
|
||||
assert.strictEqual(entry?.offset, 2);
|
||||
entry = parseEntry(view, 2);
|
||||
assert.strictEqual(entry!.offset, 3);
|
||||
assert.strictEqual(entry?.offset, 3);
|
||||
});
|
||||
|
||||
@@ -64,8 +64,8 @@ test("a lot of tiles", () => {
|
||||
});
|
||||
|
||||
test("tile extremes", () => {
|
||||
for (var z = 0; z < 27; z++) {
|
||||
const dim = Math.pow(2, z) - 1;
|
||||
for (let z = 0; z < 27; z++) {
|
||||
const dim = 2 ** z - 1;
|
||||
const tl = tileIdToZxy(zxyToTileId(z, 0, 0));
|
||||
assert.deepEqual([z, 0, 0], tl);
|
||||
const tr = tileIdToZxy(zxyToTileId(z, dim, 0));
|
||||
@@ -100,9 +100,9 @@ test("tile search for first entry == id", () => {
|
||||
const entries: Entry[] = [
|
||||
{ tileId: 100, offset: 1, length: 1, runLength: 1 },
|
||||
];
|
||||
const entry = findTile(entries, 100)!;
|
||||
assert.strictEqual(entry.offset, 1);
|
||||
assert.strictEqual(entry.length, 1);
|
||||
const entry = findTile(entries, 100);
|
||||
assert.strictEqual(entry?.offset, 1);
|
||||
assert.strictEqual(entry?.length, 1);
|
||||
assert.strictEqual(findTile(entries, 101), null);
|
||||
});
|
||||
|
||||
@@ -111,32 +111,32 @@ test("tile search with runlength", () => {
|
||||
{ tileId: 3, offset: 3, length: 1, runLength: 2 },
|
||||
{ tileId: 5, offset: 5, length: 1, runLength: 2 },
|
||||
];
|
||||
const entry = findTile(entries, 4)!;
|
||||
assert.strictEqual(entry.offset, 3);
|
||||
const entry = findTile(entries, 4);
|
||||
assert.strictEqual(entry?.offset, 3);
|
||||
});
|
||||
|
||||
test("tile search with multiple tile entries", () => {
|
||||
let entries: Entry[] = [{ tileId: 100, offset: 1, length: 1, runLength: 2 }];
|
||||
let entry = findTile(entries, 101)!;
|
||||
assert.strictEqual(entry.offset, 1);
|
||||
assert.strictEqual(entry.length, 1);
|
||||
let entry = findTile(entries, 101);
|
||||
assert.strictEqual(entry?.offset, 1);
|
||||
assert.strictEqual(entry?.length, 1);
|
||||
|
||||
entries = [
|
||||
{ tileId: 100, offset: 1, length: 1, runLength: 1 },
|
||||
{ tileId: 150, offset: 2, length: 2, runLength: 2 },
|
||||
];
|
||||
entry = findTile(entries, 151)!;
|
||||
assert.strictEqual(entry.offset, 2);
|
||||
assert.strictEqual(entry.length, 2);
|
||||
entry = findTile(entries, 151);
|
||||
assert.strictEqual(entry?.offset, 2);
|
||||
assert.strictEqual(entry?.length, 2);
|
||||
|
||||
entries = [
|
||||
{ tileId: 50, offset: 1, length: 1, runLength: 2 },
|
||||
{ tileId: 100, offset: 2, length: 2, runLength: 1 },
|
||||
{ tileId: 150, offset: 3, length: 3, runLength: 1 },
|
||||
];
|
||||
entry = findTile(entries, 51)!;
|
||||
assert.strictEqual(entry.offset, 1);
|
||||
assert.strictEqual(entry.length, 1);
|
||||
entry = findTile(entries, 51);
|
||||
assert.strictEqual(entry?.offset, 1);
|
||||
assert.strictEqual(entry?.length, 1);
|
||||
});
|
||||
|
||||
test("leaf search", () => {
|
||||
@@ -144,8 +144,8 @@ test("leaf search", () => {
|
||||
{ tileId: 100, offset: 1, length: 1, runLength: 0 },
|
||||
];
|
||||
const entry = findTile(entries, 150);
|
||||
assert.strictEqual(entry!.offset, 1);
|
||||
assert.strictEqual(entry!.length, 1);
|
||||
assert.strictEqual(entry?.offset, 1);
|
||||
assert.strictEqual(entry?.length, 1);
|
||||
});
|
||||
|
||||
// inefficient method only for testing
|
||||
|
||||
30
js/v2.ts
30
js/v2.ts
@@ -9,11 +9,11 @@ import {
|
||||
} from "./index";
|
||||
|
||||
export const shift = (n: number, shift: number) => {
|
||||
return n * Math.pow(2, shift);
|
||||
return n * 2 ** shift;
|
||||
};
|
||||
|
||||
export const unshift = (n: number, shift: number) => {
|
||||
return Math.floor(n / Math.pow(2, shift));
|
||||
return Math.floor(n / 2 ** shift);
|
||||
};
|
||||
|
||||
export const getUint24 = (view: DataView, pos: number) => {
|
||||
@@ -36,7 +36,7 @@ export interface EntryV2 {
|
||||
y: number;
|
||||
offset: number;
|
||||
length: number;
|
||||
is_dir: boolean;
|
||||
isDir: boolean;
|
||||
}
|
||||
|
||||
const compare = (
|
||||
@@ -46,11 +46,11 @@ const compare = (
|
||||
view: DataView,
|
||||
i: number
|
||||
) => {
|
||||
if (tz != view.getUint8(i)) return tz - view.getUint8(i);
|
||||
if (tz !== view.getUint8(i)) return tz - view.getUint8(i);
|
||||
const x = getUint24(view, i + 1);
|
||||
if (tx != x) return tx - x;
|
||||
if (tx !== x) return tx - x;
|
||||
const y = getUint24(view, i + 4);
|
||||
if (ty != y) return ty - y;
|
||||
if (ty !== y) return ty - y;
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -68,7 +68,7 @@ export const queryLeafdir = (
|
||||
y: y,
|
||||
offset: offsetLen[0],
|
||||
length: offsetLen[1],
|
||||
is_dir: true,
|
||||
isDir: true,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
@@ -83,7 +83,7 @@ export const queryTile = (view: DataView, z: number, x: number, y: number) => {
|
||||
y: y,
|
||||
offset: offsetLen[0],
|
||||
length: offsetLen[1],
|
||||
is_dir: false,
|
||||
isDir: false,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
@@ -112,10 +112,10 @@ const queryView = (
|
||||
};
|
||||
|
||||
const entrySort = (a: EntryV2, b: EntryV2): number => {
|
||||
if (a.is_dir && !b.is_dir) {
|
||||
if (a.isDir && !b.isDir) {
|
||||
return 1;
|
||||
}
|
||||
if (!a.is_dir && b.is_dir) {
|
||||
if (!a.isDir && b.isDir) {
|
||||
return -1;
|
||||
}
|
||||
if (a.z !== b.z) {
|
||||
@@ -136,7 +136,7 @@ export const parseEntry = (dataview: DataView, i: number): EntryV2 => {
|
||||
y: getUint24(dataview, i * 17 + 4),
|
||||
offset: getUint48(dataview, i * 17 + 7),
|
||||
length: dataview.getUint32(i * 17 + 13, true),
|
||||
is_dir: zRaw >> 7 === 1,
|
||||
isDir: zRaw >> 7 === 1,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -157,7 +157,7 @@ export const createDirectory = (entries: EntryV2[]): ArrayBuffer => {
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i];
|
||||
let z = entry.z;
|
||||
if (entry.is_dir) z = z | 0x80;
|
||||
if (entry.isDir) z = z | 0x80;
|
||||
arr[i * 17] = z;
|
||||
|
||||
arr[i * 17 + 1] = entry.x & 0xff;
|
||||
@@ -187,7 +187,7 @@ export const deriveLeaf = (view: DataView, tile: Zxy): Zxy | null => {
|
||||
if (view.byteLength < 17) return null;
|
||||
const numEntries = view.byteLength / 17;
|
||||
const entry = parseEntry(view, numEntries - 1);
|
||||
if (entry.is_dir) {
|
||||
if (entry.isDir) {
|
||||
const leafLevel = entry.z;
|
||||
const levelDiff = tile.z - leafLevel;
|
||||
const leafX = Math.trunc(tile.x / (1 << levelDiff));
|
||||
@@ -303,7 +303,7 @@ async function getZxy(
|
||||
let tileData = resp.data;
|
||||
|
||||
const view = new DataView(tileData);
|
||||
if (view.getUint8(0) == 0x1f && view.getUint8(1) == 0x8b) {
|
||||
if (view.getUint8(0) === 0x1f && view.getUint8(1) === 0x8b) {
|
||||
tileData = decompressSync(new Uint8Array(tileData));
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ async function getZxy(
|
||||
let tileData = resp.data;
|
||||
|
||||
const view = new DataView(tileData);
|
||||
if (view.getUint8(0) == 0x1f && view.getUint8(1) == 0x8b) {
|
||||
if (view.getUint8(0) === 0x1f && view.getUint8(1) === 0x8b) {
|
||||
tileData = decompressSync(new Uint8Array(tileData));
|
||||
}
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user