mirror of
https://github.com/protomaps/PMTiles.git
synced 2026-02-04 10:51:07 +00:00
js library has better organization
This commit is contained in:
98
js/adapters.ts
Normal file
98
js/adapters.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
declare const L: any;
|
||||||
|
import { PMTiles, Source } from "./index";
|
||||||
|
|
||||||
|
export const leafletRasterLayer = (source: PMTiles, options: any) => {
|
||||||
|
const cls = L.GridLayer.extend({
|
||||||
|
createTile: function (coord: any, done: any) {
|
||||||
|
const tile: any = document.createElement("img");
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
tile.cancel = () => {
|
||||||
|
controller.abort();
|
||||||
|
};
|
||||||
|
source.getZxy(coord.z, coord.x, coord.y).then((arr) => {
|
||||||
|
if (arr) {
|
||||||
|
const blob = new Blob([arr.data], { type: "image/png" });
|
||||||
|
const imageUrl = window.URL.createObjectURL(blob);
|
||||||
|
tile.src = imageUrl;
|
||||||
|
tile.cancel = null;
|
||||||
|
done(null, tile);
|
||||||
|
} else {
|
||||||
|
// return an empty image
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return tile;
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeTile: function (key: string) {
|
||||||
|
const tile = this._tiles[key];
|
||||||
|
if (!tile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tile.el.cancel) tile.el.cancel();
|
||||||
|
|
||||||
|
tile.el.width = 0;
|
||||||
|
tile.el.height = 0;
|
||||||
|
tile.el.deleted = true;
|
||||||
|
L.DomUtil.remove(tile.el);
|
||||||
|
delete this._tiles[key];
|
||||||
|
this.fire("tileunload", {
|
||||||
|
tile: tile.el,
|
||||||
|
coords: this._keyToTileCoords(key),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return new cls(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Protocol {
|
||||||
|
tiles: Map<string, PMTiles>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.tiles = new Map<string, PMTiles>();
|
||||||
|
}
|
||||||
|
|
||||||
|
add(p: PMTiles) {
|
||||||
|
this.tiles.set(p.source.getKey(), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(url: string) {
|
||||||
|
return this.tiles.get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
tileFunc = (params: any, callback: any) => {
|
||||||
|
const re = new RegExp(/pmtiles:\/\/(.+)\/(\d+)\/(\d+)\/(\d+)/);
|
||||||
|
const result = params.url.match(re);
|
||||||
|
const pmtiles_url = result[1];
|
||||||
|
|
||||||
|
let instance = this.tiles.get(pmtiles_url);
|
||||||
|
if (!instance) {
|
||||||
|
instance = new PMTiles(pmtiles_url);
|
||||||
|
this.tiles.set(pmtiles_url, instance);
|
||||||
|
}
|
||||||
|
const z = result[2];
|
||||||
|
const x = result[3];
|
||||||
|
const y = result[4];
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const signal = controller.signal;
|
||||||
|
let cancel = () => {
|
||||||
|
controller.abort();
|
||||||
|
};
|
||||||
|
|
||||||
|
instance.getZxy(+z, +x, +y, signal).then((arr) => {
|
||||||
|
if (arr) {
|
||||||
|
let data = new Uint8Array(arr.data);
|
||||||
|
callback(null, data, null, null);
|
||||||
|
} else {
|
||||||
|
callback(null, new Uint8Array(), null, null);
|
||||||
|
}
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cancel: cancel
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import './v2.test';
|
|
||||||
import './v3.test';
|
|
||||||
|
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ export interface Entry {
|
|||||||
const ENTRY_SIZE_BYTES = 32;
|
const ENTRY_SIZE_BYTES = 32;
|
||||||
|
|
||||||
export enum Compression {
|
export enum Compression {
|
||||||
None = 0,
|
Unknown = 0,
|
||||||
Gzip = 1,
|
Gzip = 1,
|
||||||
Brotli = 2,
|
Brotli = 2,
|
||||||
Zstd = 3,
|
Zstd = 3,
|
||||||
@@ -149,7 +149,7 @@ function tryDecompress(buf: ArrayBuffer, compression: Compression) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TileType {
|
export enum TileType {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
Mvt = 1,
|
Mvt = 1,
|
||||||
Png = 2,
|
Png = 2,
|
||||||
2
js/test/index.test.ts
Normal file
2
js/test/index.test.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import './v2.test';
|
||||||
|
import './v3.test';
|
||||||
@@ -4,16 +4,16 @@ import {
|
|||||||
unshift,
|
unshift,
|
||||||
getUint24,
|
getUint24,
|
||||||
getUint48,
|
getUint48,
|
||||||
|
deriveLeaf,
|
||||||
queryLeafdir,
|
queryLeafdir,
|
||||||
queryLeafLevel,
|
|
||||||
queryTile,
|
queryTile,
|
||||||
parseEntry,
|
parseEntry,
|
||||||
Entry,
|
EntryV2,
|
||||||
createDirectory,
|
createDirectory,
|
||||||
} from "./index";
|
} from "../v2";
|
||||||
|
|
||||||
test("stub data", (assertion) => {
|
test("stub data", (assertion) => {
|
||||||
let dataview = createDirectory([
|
let 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, is_dir: false },
|
||||||
{
|
{
|
||||||
z: 14,
|
z: 14,
|
||||||
@@ -23,8 +23,7 @@ test("stub data", (assertion) => {
|
|||||||
length: 999,
|
length: 999,
|
||||||
is_dir: false,
|
is_dir: false,
|
||||||
},
|
},
|
||||||
]);
|
]));
|
||||||
|
|
||||||
var z_raw = dataview.getUint8(17 + 0);
|
var z_raw = dataview.getUint8(17 + 0);
|
||||||
var x = getUint24(dataview, 17 + 1);
|
var x = getUint24(dataview, 17 + 1);
|
||||||
var y = getUint24(dataview, 17 + 4);
|
var y = getUint24(dataview, 17 + 4);
|
||||||
@@ -36,7 +35,7 @@ test("stub data", (assertion) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("get entry", (assertion) => {
|
test("get entry", (assertion) => {
|
||||||
let view = createDirectory([
|
let 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, is_dir: false },
|
||||||
{
|
{
|
||||||
z: 14,
|
z: 14,
|
||||||
@@ -46,7 +45,7 @@ test("get entry", (assertion) => {
|
|||||||
length: 999,
|
length: 999,
|
||||||
is_dir: false,
|
is_dir: false,
|
||||||
},
|
},
|
||||||
]);
|
]));
|
||||||
let entry = queryTile(view, 14, 16383, 16383);
|
let entry = queryTile(view, 14, 16383, 16383);
|
||||||
assertion.ok(entry!.z === 14);
|
assertion.ok(entry!.z === 14);
|
||||||
assertion.ok(entry!.x === 16383);
|
assertion.ok(entry!.x === 16383);
|
||||||
@@ -58,7 +57,7 @@ test("get entry", (assertion) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("get leafdir", (assertion) => {
|
test("get leafdir", (assertion) => {
|
||||||
let view = createDirectory([
|
let view = new DataView(createDirectory([
|
||||||
{
|
{
|
||||||
z: 14,
|
z: 14,
|
||||||
x: 16383,
|
x: 16383,
|
||||||
@@ -67,7 +66,7 @@ test("get leafdir", (assertion) => {
|
|||||||
length: 999,
|
length: 999,
|
||||||
is_dir: true,
|
is_dir: true,
|
||||||
},
|
},
|
||||||
]);
|
]));
|
||||||
let entry = queryLeafdir(view, 14, 16383, 16383);
|
let entry = queryLeafdir(view, 14, 16383, 16383);
|
||||||
assertion.ok(entry!.z === 14);
|
assertion.ok(entry!.z === 14);
|
||||||
assertion.ok(entry!.x === 16383);
|
assertion.ok(entry!.x === 16383);
|
||||||
@@ -79,7 +78,7 @@ test("get leafdir", (assertion) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("derive the leaf level", (assertion) => {
|
test("derive the leaf level", (assertion) => {
|
||||||
let view = createDirectory([
|
let view = new DataView(createDirectory([
|
||||||
{
|
{
|
||||||
z: 6,
|
z: 6,
|
||||||
x: 3,
|
x: 3,
|
||||||
@@ -88,10 +87,12 @@ test("derive the leaf level", (assertion) => {
|
|||||||
length: 0,
|
length: 0,
|
||||||
is_dir: true,
|
is_dir: true,
|
||||||
},
|
},
|
||||||
]);
|
]));
|
||||||
let level = queryLeafLevel(view);
|
let leaf = deriveLeaf(view,{z:7,x:6,y:6});
|
||||||
assertion.ok(level === 6);
|
assertion.ok(leaf!.z === 6);
|
||||||
view = createDirectory([
|
assertion.ok(leaf!.x === 3);
|
||||||
|
assertion.ok(leaf!.y === 3);
|
||||||
|
view = new DataView(createDirectory([
|
||||||
{
|
{
|
||||||
z: 6,
|
z: 6,
|
||||||
x: 3,
|
x: 3,
|
||||||
@@ -100,13 +101,13 @@ test("derive the leaf level", (assertion) => {
|
|||||||
length: 0,
|
length: 0,
|
||||||
is_dir: false,
|
is_dir: false,
|
||||||
},
|
},
|
||||||
]);
|
]));
|
||||||
level = queryLeafLevel(view);
|
leaf = deriveLeaf(view,{z:7,x:6,y:6});
|
||||||
assertion.ok(level === null);
|
assertion.ok(leaf === null);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("convert spec v1 directory to spec v2 directory", (assertion) => {
|
test("convert spec v1 directory to spec v2 directory", (assertion) => {
|
||||||
let view = createDirectory([
|
let view = new DataView(createDirectory([
|
||||||
{
|
{
|
||||||
z: 7,
|
z: 7,
|
||||||
x: 3,
|
x: 3,
|
||||||
@@ -131,7 +132,7 @@ test("convert spec v1 directory to spec v2 directory", (assertion) => {
|
|||||||
length: 1,
|
length: 1,
|
||||||
is_dir: false,
|
is_dir: false,
|
||||||
},
|
},
|
||||||
]);
|
]));
|
||||||
let entry = queryLeafdir(view, 7, 3, 3);
|
let entry = queryLeafdir(view, 7, 3, 3);
|
||||||
assertion.ok(entry!.offset === 3);
|
assertion.ok(entry!.offset === 3);
|
||||||
entry = queryTile(view, 6, 2, 2);
|
entry = queryTile(view, 6, 2, 2);
|
||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
RangeResponse,
|
RangeResponse,
|
||||||
VersionMismatch,
|
VersionMismatch,
|
||||||
PMTiles,
|
PMTiles,
|
||||||
} from "./v3";
|
} from "../index";
|
||||||
|
|
||||||
test("varint", (assertion) => {
|
test("varint", (assertion) => {
|
||||||
let b: BufferPosition = {
|
let b: BufferPosition = {
|
||||||
@@ -144,7 +144,7 @@ class TestNodeFileSource implements Source {
|
|||||||
|
|
||||||
// echo '{"type":"Polygon","coordinates":[[[0,0],[0,1],[1,1],[1,0],[0,0]]]}' | ./tippecanoe -zg -o test_fixture_1.pmtiles
|
// echo '{"type":"Polygon","coordinates":[[[0,0],[0,1],[1,1],[1,0],[0,0]]]}' | ./tippecanoe -zg -o test_fixture_1.pmtiles
|
||||||
test("cache getHeader", async (assertion) => {
|
test("cache getHeader", async (assertion) => {
|
||||||
const source = new TestNodeFileSource("test_fixture_1.pmtiles", "1");
|
const source = new TestNodeFileSource("test/data/test_fixture_1.pmtiles", "1");
|
||||||
const cache = new SharedPromiseCache();
|
const cache = new SharedPromiseCache();
|
||||||
const header = await cache.getHeader(source);
|
const header = await cache.getHeader(source);
|
||||||
assertion.eq(header.rootDirectoryOffset, 122);
|
assertion.eq(header.rootDirectoryOffset, 122);
|
||||||
@@ -171,7 +171,7 @@ test("cache getHeader", async (assertion) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("cache check against empty", async (assertion) => {
|
test("cache check against empty", async (assertion) => {
|
||||||
const source = new TestNodeFileSource("empty.pmtiles", "1");
|
const source = new TestNodeFileSource("test/data/empty.pmtiles", "1");
|
||||||
const cache = new SharedPromiseCache();
|
const cache = new SharedPromiseCache();
|
||||||
try {
|
try {
|
||||||
await cache.getHeader(source);
|
await cache.getHeader(source);
|
||||||
@@ -182,7 +182,7 @@ test("cache check against empty", async (assertion) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("cache check magic number", async (assertion) => {
|
test("cache check magic number", async (assertion) => {
|
||||||
const source = new TestNodeFileSource("invalid.pmtiles", "1");
|
const source = new TestNodeFileSource("test/data/invalid.pmtiles", "1");
|
||||||
const cache = new SharedPromiseCache();
|
const cache = new SharedPromiseCache();
|
||||||
try {
|
try {
|
||||||
await cache.getHeader(source);
|
await cache.getHeader(source);
|
||||||
@@ -194,7 +194,7 @@ test("cache check magic number", async (assertion) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("cache getDirectory", async (assertion) => {
|
test("cache getDirectory", async (assertion) => {
|
||||||
const source = new TestNodeFileSource("test_fixture_1.pmtiles", "1");
|
const source = new TestNodeFileSource("test/data/test_fixture_1.pmtiles", "1");
|
||||||
|
|
||||||
let cache = new SharedPromiseCache(6400, false);
|
let cache = new SharedPromiseCache(6400, false);
|
||||||
let header = await cache.getHeader(source);
|
let header = await cache.getHeader(source);
|
||||||
@@ -226,8 +226,8 @@ test("cache getDirectory", async (assertion) => {
|
|||||||
|
|
||||||
test("multiple sources in a single cache", async (assertion) => {
|
test("multiple sources in a single cache", async (assertion) => {
|
||||||
const cache = new SharedPromiseCache();
|
const cache = new SharedPromiseCache();
|
||||||
const source1 = new TestNodeFileSource("test_fixture_1.pmtiles", "1");
|
const source1 = new TestNodeFileSource("test/data/test_fixture_1.pmtiles", "1");
|
||||||
const source2 = new TestNodeFileSource("test_fixture_1.pmtiles", "2");
|
const source2 = new TestNodeFileSource("test/data/test_fixture_1.pmtiles", "2");
|
||||||
await cache.getHeader(source1);
|
await cache.getHeader(source1);
|
||||||
assertion.eq(cache.cache.size, 2);
|
assertion.eq(cache.cache.size, 2);
|
||||||
await cache.getHeader(source2);
|
await cache.getHeader(source2);
|
||||||
@@ -236,7 +236,7 @@ test("multiple sources in a single cache", async (assertion) => {
|
|||||||
|
|
||||||
test("etags are part of key", async (assertion) => {
|
test("etags are part of key", async (assertion) => {
|
||||||
const cache = new SharedPromiseCache(6400, false);
|
const cache = new SharedPromiseCache(6400, false);
|
||||||
const source = new TestNodeFileSource("test_fixture_1.pmtiles", "1");
|
const source = new TestNodeFileSource("test/data/test_fixture_1.pmtiles", "1");
|
||||||
source.etag = "etag_1";
|
source.etag = "etag_1";
|
||||||
let header = await cache.getHeader(source);
|
let header = await cache.getHeader(source);
|
||||||
assertion.eq(header.etag, "etag_1");
|
assertion.eq(header.etag, "etag_1");
|
||||||
@@ -278,7 +278,7 @@ test("cache pruning by byte size", async (assertion) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("pmtiles get metadata", async (assertion) => {
|
test("pmtiles get metadata", async (assertion) => {
|
||||||
const source = new TestNodeFileSource("test_fixture_1.pmtiles", "1");
|
const source = new TestNodeFileSource("test/data/test_fixture_1.pmtiles", "1");
|
||||||
const p = new PMTiles(source);
|
const p = new PMTiles(source);
|
||||||
const metadata = await p.getMetadata();
|
const metadata = await p.getMetadata();
|
||||||
assertion.ok(metadata.name);
|
assertion.ok(metadata.name);
|
||||||
@@ -286,12 +286,12 @@ test("pmtiles get metadata", async (assertion) => {
|
|||||||
|
|
||||||
// echo '{"type":"Polygon","coordinates":[[[0,0],[0,1],[1,0],[0,0]]]}' | ./tippecanoe -zg -o test_fixture_2.pmtiles
|
// echo '{"type":"Polygon","coordinates":[[[0,0],[0,1],[1,0],[0,0]]]}' | ./tippecanoe -zg -o test_fixture_2.pmtiles
|
||||||
test("pmtiles handle retries", async (assertion) => {
|
test("pmtiles handle retries", async (assertion) => {
|
||||||
const source = new TestNodeFileSource("test_fixture_1.pmtiles", "1");
|
const source = new TestNodeFileSource("test/data/test_fixture_1.pmtiles", "1");
|
||||||
source.etag = "1";
|
source.etag = "1";
|
||||||
const p = new PMTiles(source);
|
const p = new PMTiles(source);
|
||||||
const metadata = await p.getMetadata();
|
const metadata = await p.getMetadata();
|
||||||
assertion.ok(metadata.name);
|
assertion.ok(metadata.name);
|
||||||
source.etag = "2";
|
source.etag = "2";
|
||||||
source.replaceData("test_fixture_2.pmtiles");
|
source.replaceData("test/data/test_fixture_2.pmtiles");
|
||||||
assertion.ok(await p.getZxy(0, 0, 0));
|
assertion.ok(await p.getZxy(0, 0, 0));
|
||||||
});
|
});
|
||||||
@@ -8,5 +8,5 @@
|
|||||||
},
|
},
|
||||||
"types": []
|
"types": []
|
||||||
},
|
},
|
||||||
"include": ["*.ts"]
|
"include": ["*.ts","test/*.ts"]
|
||||||
}
|
}
|
||||||
2
js/v2.ts
2
js/v2.ts
@@ -1,4 +1,4 @@
|
|||||||
import { Source, Header, Cache, RangeResponse, Compression } from "./v3";
|
import { Source, Header, Cache, RangeResponse, Compression } from "./index";
|
||||||
import { decompressSync } from "fflate";
|
import { decompressSync } from "fflate";
|
||||||
|
|
||||||
export const shift = (n: number, shift: number) => {
|
export const shift = (n: number, shift: number) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user