diff --git a/src/index.ts b/src/index.ts index 688da9f..bc12a69 100644 --- a/src/index.ts +++ b/src/index.ts @@ -177,34 +177,34 @@ type TileID = { * ``` */ export class PmTilesSource extends VectorTileSourceImpl { + [x: string]: any; static SOURCE_TYPE = SOURCE_TYPE id: string; - scheme: string; + scheme: string='zxy'; minzoom!: number; maxzoom!: number; tileSize: number; attribution: string | undefined; tiles: string[]; - map!: MapboxMap; + roundZoom: boolean = true; tileBounds: TileBounds | undefined; minTileCacheSize: number | undefined; maxTileCacheSize: number | undefined; - promoteId: string | undefined; + promoteId: string | undefined=undefined; type: string = 'vector'; - fire!: Function; + scope: string | undefined; - dispatcher; - reparseOverscaled: boolean; - setEventedParent: any; - + dispatcher=undefined; + reparseOverscaled: boolean=true; + map!: MapboxMap; - _loaded; - _tileWorkers!: { [string: string]: any; }; - _dataType; - _implementation; + _loaded= false; + + _dataType= 'vector'; + _implementation:PmTilesOptions |undefined ; _protocol: Protocol; _instance: PMTiles; _tileJSONRequest: Promise | undefined; @@ -251,7 +251,7 @@ export class PmTilesSource extends VectorTileSourceImpl { this.dispatcher = _dispatcher; this._implementation = options; if (!this._implementation) { - this.fire(new ErrorEvent(new Error(`Missing options for ${this.id} custom source`))); + this.fire(new ErrorEvent(new Error(`Missing options for ${this.id} ${SOURCE_TYPE} source`))); } const { url } = options; @@ -267,11 +267,11 @@ export class PmTilesSource extends VectorTileSourceImpl { this.tiles = [`pmtiles://${url}/{z}/{x}/{y}`] - const p = new PMTiles(url); + const pmtilesInstance = new PMTiles(url); // this is so we share one instance across the JS code and the map renderer - this._protocol.add(p); - this._instance = p; + this._protocol.add(pmtilesInstance); + this._instance = pmtilesInstance; } @@ -280,6 +280,8 @@ export class PmTilesSource extends VectorTileSourceImpl { * @returns {mapboxgl.LngLatBoundsLike} */ getExtent(): mapboxgl.LngLatBoundsLike { + if(!this.header) return [[-180, -90], [180, 90] ] + const { minZoom, maxZoom, minLon, minLat, maxLon, maxLat, centerZoom, centerLon, centerLat } = this.header return [minLon, minLat, maxLon, maxLat] @@ -288,11 +290,11 @@ export class PmTilesSource extends VectorTileSourceImpl { hasTile(tileID: TileID) { return !this.tileBounds || this.tileBounds.contains(tileID.canonical); } - load(callback?: Callback) { + async load(callback?: Callback) { this._loaded = false; this.fire(new Event("dataloading", { dataType: "source" })); // We need to get both header and metadata - this._tileJSONRequest = Promise.all([this._instance.getHeader(), this._instance.getMetadata()]).then(([header, tileJSON]: any) => { + this._tileJSONRequest = Promise.all([this._instance.getHeader(), this._instance.getMetadata()]).then(([header, tileJSON]: any) => { //first we set some of the header properties to the source using tileJSON extend(this, tileJSON); // fix for the corrupted tilejson @@ -320,9 +322,7 @@ export class PmTilesSource extends VectorTileSourceImpl { } this._tileJSONRequest = undefined; - this._loaded = true; - - + this._loaded = true; // we set this.type after extend to avoid overwriting this.tileType = tileType @@ -340,6 +340,9 @@ export class PmTilesSource extends VectorTileSourceImpl { case TileType.Avif: this.contentType = 'image/avif'; break; + case TileType.Mvt: + this.contentType = 'application/vnd.mapbox-vector-tile'; + break; } if ([TileType.Jpeg, TileType.Png].includes(this.tileType)) { @@ -365,6 +368,8 @@ export class PmTilesSource extends VectorTileSourceImpl { this.fire(new ErrorEvent(err)); if (callback) callback(err); }); + + return this._tileJSONRequest; } loaded(): boolean { diff --git a/test/mapbox-pmtiles.test.ts b/test/mapbox-pmtiles.test.ts index 6051e68..2096976 100644 --- a/test/mapbox-pmtiles.test.ts +++ b/test/mapbox-pmtiles.test.ts @@ -1,10 +1,69 @@ // sum.test.js -import { expect, test } from 'vitest' +import { expect, test, vi } from 'vitest' import PmTilesSource from '../src/index' const OSM_PMTILE="https://r2-public.protomaps.com/protomaps-sample-datasets/protomaps-basemap-opensource-20230408.pmtiles" +const OSM_RASTER_PMTILE="https://pmtiles.io/stamen_toner(raster)CC-BY+ODbL_z3.pmtiles" test('Get pmtiles metadata', async () => { const metadata = await PmTilesSource.getMetadata(OSM_PMTILE) expect(metadata.name).toBe("Basemap") -}) \ No newline at end of file +}) + +test('Get pmtiles header', async () => { + const header = await PmTilesSource.getHeader(OSM_PMTILE) + expect(header.tileType).toBe(1) +}) + +test('Load A tileset', async () => { + const source = new PmTilesSource("testPmtiles", {url: OSM_PMTILE}, {}, {}) + const mockFire=vi.fn() + source.fire=mockFire + await source.load() + + expect(mockFire.mock.calls.length).toBe(3) + expect(source.type).toBe("vector") + expect(source.loaded()).toBe(true) +}) + + +test('Load A raster tileset', async () => { + const source = new PmTilesSource("testPmtiles", {url: OSM_RASTER_PMTILE}, {}, {}) + const mockFire=vi.fn() + source.fire=mockFire + await source.load() + + expect(source.type).toBe("raster") +}) + +test('Detect the correct contentType from tileset', async () => { + const source = new PmTilesSource("testPmtiles", {url: OSM_PMTILE}, {}, {}) + const mockFire=vi.fn() + source.fire=mockFire + await source.load() + + expect(mockFire.mock.calls.length).toBe(3) + expect(source.contentType).toBe("application/vnd.mapbox-vector-tile") +}) + + +test('Detect the correct contentType from raster tileset', async () => { + const source = new PmTilesSource("testPmtiles", {url: OSM_RASTER_PMTILE}, {}, {}) + const mockFire=vi.fn() + source.fire=mockFire + await source.load() + + expect(mockFire.mock.calls.length).toBe(3) + expect(source.contentType).toBe("image/png") +}) + +test('Handle non-integer zoom levels', async () => { + const source = new PmTilesSource("testPmtiles", {url: "https://r2-public.protomaps.com/protomaps-sample-datasets/cb_2018_us_zcta510_500k.pmtiles"}, {}, {}) + const mockFire=vi.fn() + source.fire=mockFire + await source.load() + + expect(source.maxzoom).toBe(7) + expect(source.minzoom).toBe(0) +}) +