diff --git a/packages/lambda-tiler/src/__tests__/wmts.capability.test.ts b/packages/lambda-tiler/src/__tests__/wmts.capability.test.ts index e597a2073..872f14d7f 100644 --- a/packages/lambda-tiler/src/__tests__/wmts.capability.test.ts +++ b/packages/lambda-tiler/src/__tests__/wmts.capability.test.ts @@ -229,6 +229,86 @@ describe('WmtsCapabilities', () => { ); }); + it('should limit a bounding box to the tileMatrix extent WebMercatorQuad', () => { + const wmts = new WmtsCapabilities({ httpBase: 'https://basemaps.test', apiKey }); + + const bigImagery = new Map(); + bigImagery.set(Imagery3857.id, { + ...Imagery3857, + bounds: { + // These bounds are slightly outside the extent bounds of 3857 (approx 0.3m offset) + // expected bounds: {x: -20037508.34 y:-20037508.34, width: 40075016.6855784 , height: 40075016.6855784 } + x: -20037508.6276, + y: -20037508.6276, + width: 40075016.9626, + height: 40075014.197799996, + }, + }); + + wmts.fromParams({ + provider: Provider, + tileMatrix: [GoogleTms], + tileSet: TileSetAerial, + imagery: bigImagery, + formats: ['png'], + }); + const raw = wmts.toVNode(); + + const wgs84 = raw.find('Contents', 'Layer', 'ows:WGS84BoundingBox'); + const epsg3857 = raw.find('Contents', 'Layer', 'ows:BoundingBox'); + + assert.equal( + epsg3857?.find('ows:LowerCorner')?.toString(), + `-20037508.3428 -20037508.3428`, + ); + assert.equal( + epsg3857?.find('ows:UpperCorner')?.toString(), + `20037508.3428 20037508.3428`, + ); + + assert.equal(wgs84?.find('ows:LowerCorner')?.toString(), '-180 -85.051129'); + assert.equal(wgs84?.find('ows:UpperCorner')?.toString(), '180 85.051129'); + }); + + it('should limit a bounding box to the tileMatrix extent NZTM2000Quad', () => { + const wmts = new WmtsCapabilities({ httpBase: 'https://basemaps.test', apiKey }); + const bigImagery = new Map(); + + bigImagery.set(Imagery2193.id, { + ...Imagery2193, + bounds: { + // These bounds are slightly outside the extent bounds of NZTM2000Quad (approx 0.3m offset) + x: -3260586.9214, + y: 419435.7552, + width: 10018754.086099999, + height: 10018754.086099999, + }, + }); + + wmts.fromParams({ + provider: Provider, + tileMatrix: [Nztm2000QuadTms], + tileSet: TileSetAerial, + imagery: bigImagery, + formats: ['png'], + }); + const raw = wmts.toVNode(); + + const wgs84 = raw.find('Contents', 'Layer', 'ows:WGS84BoundingBox'); + const crsBounds = raw.find('Contents', 'Layer', 'ows:BoundingBox'); + assert.equal( + crsBounds?.find('ows:LowerCorner')?.toString(), + `419435.9938 -3260586.7284`, + ); + assert.equal( + crsBounds?.find('ows:UpperCorner')?.toString(), + `10438190.1652 6758167.443`, + ); + + assert.equal(wgs84?.find('ows:LowerCorner')?.toString(), '-180 -49.929855'); + assert.equal(wgs84?.find('ows:UpperCorner')?.toString(), '180 2.938603'); + }); + it('should include output the correct TileMatrix', () => { const wmts = new WmtsCapabilities({ httpBase: 'https://basemaps.test', @@ -512,11 +592,12 @@ describe('WmtsCapabilities', () => { .split('\n') .map((c) => c.trim()), ); + assert.deepEqual(boundingBox[0][1], '-180 -85.0511'); - assert.equal(boundingBox[0][2], '180 0'); + assert.equal(boundingBox[0][2], '180 85.0511'); assert.deepEqual(boundingBox[1][1], '-180 -85.0511'); - assert.equal(boundingBox[1][2], '180 0'); + assert.equal(boundingBox[1][2], '180 85.0511'); }); it('should work with NZTM2000Quad', () => { diff --git a/packages/lambda-tiler/src/wmts.capability.ts b/packages/lambda-tiler/src/wmts.capability.ts index 3e782c99b..7b69da04e 100644 --- a/packages/lambda-tiler/src/wmts.capability.ts +++ b/packages/lambda-tiler/src/wmts.capability.ts @@ -94,18 +94,25 @@ export class WmtsBuilder { } buildWgs84BoundingBox(tms: TileMatrixSet, layers: Bounds[]): VNodeElement { - let bbox: BBox; + let bbox: BBox | null = null; + if (layers.length > 0) { let bounds = layers[0]; for (let i = 1; i < layers.length; i++) { bounds = bounds.union(layers[i]); } - bbox = wgs84Extent(tms, bounds.toJson()); - } else { - // No layers provided assume extent is the size of the tile matrix set :shrug: ? - bbox = wgs84Extent(tms, tms.extent); + + // If imagery is outside of the bounds of the tileMatrix, fall back to the tileMatrix extent for the bounds + if (!tms.extent.containsBounds(bounds)) { + bbox = wgs84Extent(tms, tms.extent); + } else { + bbox = wgs84Extent(tms, bounds); + } } + // No layers provided assume extent is the size of the tile matrix set :shrug: ? + if (bbox == null) bbox = wgs84Extent(tms, tms.extent); + // If east is less than west, then this has crossed the anti meridian, so cover the entire globe if (bbox[2] < bbox[0]) { bbox[0] = -180; @@ -120,7 +127,7 @@ export class WmtsBuilder { /** Combine all the bounds of the imagery inside the layers into a extent for the imagery set */ buildBoundingBoxFromImagery(tms: TileMatrixSet, layers: ConfigLayer[]): VNodeElement | null { - let bounds; + let bounds: Bounds | null = null; for (const layer of layers) { const imgId = layer[tms.projection.code]; if (imgId == null) continue; @@ -131,7 +138,11 @@ export class WmtsBuilder { } if (bounds == null) return null; + // If imagery is outside of the bounds of the tileMatrix, fall back to the tileMatrix extent for the bounds + if (!tms.extent.containsBounds(bounds)) bounds = tms.extent; + const bbox = bounds.toBbox(); + return V('ows:BoundingBox', { crs: tms.projection.toUrn() }, [ V('ows:LowerCorner', formatBbox(bbox[tms.indexX], bbox[tms.indexY], MeterPrecision)), V('ows:UpperCorner', formatBbox(bbox[tms.indexX + 2], bbox[tms.indexY + 2], MeterPrecision)), diff --git a/packages/smoke/src/base.ts b/packages/smoke/src/base.ts index 84bc6903b..c5d3d466b 100644 --- a/packages/smoke/src/base.ts +++ b/packages/smoke/src/base.ts @@ -30,7 +30,19 @@ async function req(path: string, opts?: RequestInit): Promise { const res = await fetch(target, opts); // eslint-disable-next-line no-console - console.log({ url: target.href, status: res.status, ...opts, duration: performance.now() - startTime }, 'Fetch:Done'); + console.log( + // Create a fake log line approximating the pino log format + JSON.stringify({ + pid: 0, + time: new Date().toISOString(), + level: 30, + msg: 'Fetch:Done', + url: target.href, + status: res.status, + ...opts, + duration: performance.now() - startTime, + }), + ); return res; } diff --git a/packages/smoke/src/wmts.test.ts b/packages/smoke/src/wmts.test.ts index 99ba56c43..6eae1e994 100644 --- a/packages/smoke/src/wmts.test.ts +++ b/packages/smoke/src/wmts.test.ts @@ -28,13 +28,23 @@ test(`GET /v1/tiles/aerial/WebMercatorQuad/WMTSCapabilities.xml`, async () => { assert.ok( xml.includes( - `-20037508.3428 -20037508.342820037508.3428 20037508.3428`, + [ + ``, + `-20037508.3428 -20037508.3428`, + `20037508.3428 20037508.3428`, + ``, + ].join(''), ), ); assert.ok( xml.includes( - `-180 -85.051129180 85.051129`, + [ + ``, + `-180 -85.051129`, + `180 85.051129`, + ``, + ].join(''), ), );