@basemaps/lambda-tiler 7.4.0 → 7.5.0

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/build/__tests__/config.data.d.ts +2 -0
  3. package/build/__tests__/config.data.js +24 -1
  4. package/build/__tests__/config.data.js.map +1 -1
  5. package/build/__tests__/index.test.js +1 -2
  6. package/build/__tests__/index.test.js.map +1 -1
  7. package/build/__tests__/tile.style.json.test.js +2 -2
  8. package/build/__tests__/tile.style.json.test.js.map +1 -1
  9. package/build/__tests__/wmts.capability.test.js +5 -2
  10. package/build/__tests__/wmts.capability.test.js.map +1 -1
  11. package/build/__tests__/xyz.util.js.map +1 -1
  12. package/build/cli/render.preview.js +5 -2
  13. package/build/cli/render.preview.js.map +1 -1
  14. package/build/cli/render.tile.js +1 -1
  15. package/build/cli/render.tile.js.map +1 -1
  16. package/build/index.js +1 -1
  17. package/build/index.js.map +1 -1
  18. package/build/routes/__tests__/attribution.test.js +1 -1
  19. package/build/routes/__tests__/attribution.test.js.map +1 -1
  20. package/build/routes/__tests__/health.test.js +1 -1
  21. package/build/routes/__tests__/health.test.js.map +1 -1
  22. package/build/routes/__tests__/imagery.test.js.map +1 -1
  23. package/build/routes/__tests__/memory.fs.js +1 -1
  24. package/build/routes/__tests__/memory.fs.js.map +1 -1
  25. package/build/routes/__tests__/preview.index.test.js +1 -1
  26. package/build/routes/__tests__/preview.index.test.js.map +1 -1
  27. package/build/routes/__tests__/tile.json.test.js.map +1 -1
  28. package/build/routes/__tests__/tile.style.json.test.js +144 -11
  29. package/build/routes/__tests__/tile.style.json.test.js.map +1 -1
  30. package/build/routes/__tests__/wmts.test.js +18 -2
  31. package/build/routes/__tests__/wmts.test.js.map +1 -1
  32. package/build/routes/config.js +1 -1
  33. package/build/routes/config.js.map +1 -1
  34. package/build/routes/ping.js +2 -2
  35. package/build/routes/ping.js.map +1 -1
  36. package/build/routes/preview.index.js +2 -1
  37. package/build/routes/preview.index.js.map +1 -1
  38. package/build/routes/preview.js +3 -1
  39. package/build/routes/preview.js.map +1 -1
  40. package/build/routes/sprites.js.map +1 -1
  41. package/build/routes/tile.style.json.d.ts +2 -2
  42. package/build/routes/tile.style.json.js +59 -10
  43. package/build/routes/tile.style.json.js.map +1 -1
  44. package/build/routes/tile.wmts.js +1 -0
  45. package/build/routes/tile.wmts.js.map +1 -1
  46. package/build/routes/tile.xyz.raster.js +6 -5
  47. package/build/routes/tile.xyz.raster.js.map +1 -1
  48. package/build/routes/version.js +2 -2
  49. package/build/routes/version.js.map +1 -1
  50. package/build/util/__test__/config.loader.test.js +12 -12
  51. package/build/util/__test__/config.loader.test.js.map +1 -1
  52. package/build/util/config.cache.js.map +1 -1
  53. package/build/util/source.cache.js +5 -4
  54. package/build/util/source.cache.js.map +1 -1
  55. package/build/util/validate.js +1 -1
  56. package/build/util/validate.js.map +1 -1
  57. package/build/wmts.capability.d.ts +8 -4
  58. package/build/wmts.capability.js +18 -8
  59. package/build/wmts.capability.js.map +1 -1
  60. package/package.json +10 -10
  61. package/src/__tests__/config.data.ts +31 -2
  62. package/src/__tests__/index.test.ts +2 -3
  63. package/src/__tests__/tile.style.json.test.ts +2 -5
  64. package/src/__tests__/wmts.capability.test.ts +13 -10
  65. package/src/__tests__/xyz.util.ts +4 -4
  66. package/src/cli/render.preview.ts +5 -2
  67. package/src/cli/render.tile.ts +1 -1
  68. package/src/routes/__tests__/attribution.test.ts +7 -7
  69. package/src/routes/__tests__/health.test.ts +3 -3
  70. package/src/routes/__tests__/imagery.test.ts +1 -1
  71. package/src/routes/__tests__/memory.fs.ts +2 -2
  72. package/src/routes/__tests__/preview.index.test.ts +3 -3
  73. package/src/routes/__tests__/tile.json.test.ts +1 -1
  74. package/src/routes/__tests__/tile.style.json.test.ts +175 -15
  75. package/src/routes/__tests__/wmts.test.ts +23 -3
  76. package/src/routes/config.ts +1 -1
  77. package/src/routes/ping.ts +2 -2
  78. package/src/routes/preview.index.ts +2 -1
  79. package/src/routes/preview.ts +4 -2
  80. package/src/routes/sprites.ts +1 -1
  81. package/src/routes/tile.style.json.ts +72 -9
  82. package/src/routes/tile.wmts.ts +1 -0
  83. package/src/routes/tile.xyz.raster.ts +7 -5
  84. package/src/routes/version.ts +2 -2
  85. package/src/util/__test__/config.loader.test.ts +17 -20
  86. package/src/util/config.cache.ts +2 -1
  87. package/src/util/source.cache.ts +6 -4
  88. package/src/util/validate.ts +1 -1
  89. package/src/wmts.capability.ts +17 -9
  90. package/tsconfig.json +10 -10
  91. package/tsconfig.tsbuildinfo +1 -1
@@ -1,11 +1,12 @@
1
1
  import assert from 'node:assert';
2
2
  import { afterEach, before, beforeEach, describe, it } from 'node:test';
3
3
 
4
- import { ConfigProviderMemory, StyleJson } from '@basemaps/config';
4
+ import { ConfigProviderMemory, SourceRaster, StyleJson } from '@basemaps/config';
5
+ import { Terrain } from '@basemaps/config/src/config/vector.style.js';
5
6
  import { Env } from '@basemaps/shared';
6
7
  import { createSandbox } from 'sinon';
7
8
 
8
- import { FakeData } from '../../__tests__/config.data.js';
9
+ import { FakeData, TileSetAerial, TileSetElevation } from '../../__tests__/config.data.js';
9
10
  import { Api, mockRequest, mockUrlRequest } from '../../__tests__/xyz.util.js';
10
11
  import { handler } from '../../index.js';
11
12
  import { ConfigLoader } from '../../util/config.loader.js';
@@ -48,6 +49,10 @@ describe('/v1/styles', () => {
48
49
  type: 'raster',
49
50
  tiles: [`/raster/{z}/{x}/{y}.webp`], // Shouldn't encode the {}
50
51
  },
52
+ basemaps_terrain: {
53
+ type: 'raster-dem',
54
+ tiles: [`/elevation/{z}/{x}/{y}.png?pipeline=terrain-rgb`],
55
+ },
51
56
  test_vector: {
52
57
  type: 'vector',
53
58
  url: 'vector.url.co.nz',
@@ -127,6 +132,11 @@ describe('/v1/styles', () => {
127
132
  tiles: [`${host}/raster/{z}/{x}/{y}.webp?api=${Api.key}`],
128
133
  };
129
134
 
135
+ fakeStyle.sources['basemaps_terrain'] = {
136
+ type: 'raster-dem',
137
+ tiles: [`${host}/elevation/{z}/{x}/{y}.png?pipeline=terrain-rgb&api=${Api.key}`],
138
+ };
139
+
130
140
  fakeStyle.sprite = `${host}/sprite`;
131
141
  fakeStyle.glyphs = `${host}/glyphs`;
132
142
 
@@ -174,14 +184,16 @@ describe('/v1/styles', () => {
174
184
  const res = await handler.router.handle(request);
175
185
  assert.equal(res.status, 200, res.statusDescription);
176
186
 
177
- const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
187
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString()) as StyleJson;
178
188
 
179
189
  assert.equal(body.version, 8);
180
- assert.deepEqual(body.sources['basemaps-aerial'].type, 'raster');
181
- assert.deepEqual(body.sources['basemaps-aerial'].tiles, [
190
+
191
+ const aerialSource = body.sources['basemaps-aerial'] as SourceRaster;
192
+ assert.deepEqual(aerialSource.type, 'raster');
193
+ assert.deepEqual(aerialSource.tiles, [
182
194
  `https://tiles.test/v1/tiles/aerial/WebMercatorQuad/{z}/{x}/{y}.webp?api=${Api.key}`,
183
195
  ]);
184
- assert.deepEqual(body.sources['basemaps-aerial'].tileSize, 256);
196
+ assert.deepEqual(aerialSource.tileSize, 256);
185
197
  assert.deepEqual(body.layers, [{ id: 'basemaps-aerial', type: 'raster', source: 'basemaps-aerial' }]);
186
198
  });
187
199
 
@@ -192,14 +204,16 @@ describe('/v1/styles', () => {
192
204
  const res = await handler.router.handle(request);
193
205
  assert.equal(res.status, 200, res.statusDescription);
194
206
 
195
- const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
207
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString()) as StyleJson;
196
208
 
197
209
  assert.equal(body.version, 8);
198
- assert.deepEqual(body.sources['basemaps-aerial'].type, 'raster');
199
- assert.deepEqual(body.sources['basemaps-aerial'].tiles, [
210
+ const aerialSource = body.sources['basemaps-aerial'] as unknown as SourceRaster;
211
+
212
+ assert.deepEqual(aerialSource.type, 'raster');
213
+ assert.deepEqual(aerialSource.tiles, [
200
214
  `https://tiles.test/v1/tiles/aerial/NZTM2000Quad/{z}/{x}/{y}.jpeg?api=${Api.key}`,
201
215
  ]);
202
- assert.deepEqual(body.sources['basemaps-aerial'].tileSize, 256);
216
+ assert.deepEqual(aerialSource.tileSize, 256);
203
217
  assert.deepEqual(body.layers, [{ id: 'basemaps-aerial', type: 'raster', source: 'basemaps-aerial' }]);
204
218
  });
205
219
 
@@ -210,14 +224,160 @@ describe('/v1/styles', () => {
210
224
  const res = await handler.router.handle(request);
211
225
  assert.equal(res.status, 200, res.statusDescription);
212
226
 
213
- const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
227
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString()) as StyleJson;
228
+ body.sources['basemaps-aerial'];
229
+ body.sources['basemaps-aerial'];
230
+ body.sources['basemaps-aerial'];
231
+ const aerialSource = body.sources['basemaps-aerial'] as unknown as SourceRaster;
214
232
 
215
- assert.equal(body.version, 8);
216
- assert.deepEqual(body.sources['basemaps-aerial'].type, 'raster');
217
- assert.deepEqual(body.sources['basemaps-aerial'].tiles, [
233
+ assert.deepEqual(aerialSource.type, 'raster');
234
+ assert.deepEqual(aerialSource.tiles, [
218
235
  `https://tiles.test/v1/tiles/aerial/WebMercatorQuad/{z}/{x}/{y}.webp?api=${Api.key}&config=${configId}`,
219
236
  ]);
220
- assert.deepEqual(body.sources['basemaps-aerial'].tileSize, 256);
237
+ assert.deepEqual(aerialSource.tileSize, 256);
221
238
  assert.deepEqual(body.layers, [{ id: 'basemaps-aerial', type: 'raster', source: 'basemaps-aerial' }]);
222
239
  });
240
+
241
+ it('should create individual raster styles with terrain', async () => {
242
+ const configId = FakeData.bundle([FakeData.tileSetRaster('christchurch-urban-2020-2021-0.075m'), TileSetElevation]);
243
+ const request = mockUrlRequest(
244
+ '/v1/styles/christchurch-urban-2020-2021-0.075m.json',
245
+ `?config=${configId}`,
246
+ Api.header,
247
+ );
248
+
249
+ const res = await handler.router.handle(request);
250
+ assert.equal(res.status, 200, res.statusDescription);
251
+
252
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString()) as StyleJson;
253
+
254
+ assert.equal(body.version, 8);
255
+ const rasterSource = body.sources['basemaps-christchurch-urban-2020-2021-0.075m'] as unknown as SourceRaster;
256
+
257
+ assert.deepEqual(rasterSource.type, 'raster');
258
+ assert.deepEqual(rasterSource.tiles, [
259
+ `https://tiles.test/v1/tiles/christchurch-urban-2020-2021-0.075m/WebMercatorQuad/{z}/{x}/{y}.webp?api=${Api.key}&config=${configId}`,
260
+ ]);
261
+ assert.deepEqual(rasterSource.tileSize, 256);
262
+ assert.deepEqual(body.layers, [
263
+ {
264
+ id: 'basemaps-christchurch-urban-2020-2021-0.075m',
265
+ type: 'raster',
266
+ source: 'basemaps-christchurch-urban-2020-2021-0.075m',
267
+ },
268
+ ]);
269
+
270
+ const rasterDemSource = body.sources['LINZ-Terrain'] as unknown as SourceRaster;
271
+
272
+ assert.deepEqual(rasterDemSource.type, 'raster-dem');
273
+ assert.deepEqual(rasterDemSource.tiles, [
274
+ `https://tiles.test/v1/tiles/elevation/WebMercatorQuad/{z}/{x}/{y}.png?api=${Api.key}&config=${configId}&pipeline=terrain-rgb`,
275
+ ]);
276
+ });
277
+
278
+ const fakeVectorStyleConfig = {
279
+ id: 'test',
280
+ name: 'test',
281
+ sources: {
282
+ basemaps_vector: {
283
+ type: 'vector',
284
+ url: `/vector`,
285
+ },
286
+ },
287
+ layers: [
288
+ {
289
+ layout: {
290
+ visibility: 'visible',
291
+ },
292
+ paint: {
293
+ 'background-color': 'rgba(206, 229, 242, 1)',
294
+ },
295
+ id: 'Background1',
296
+ type: 'background',
297
+ minzoom: 0,
298
+ },
299
+ ],
300
+ };
301
+
302
+ const fakeVectorRecord = {
303
+ id: 'st_topolite',
304
+ name: 'topolite',
305
+ style: fakeVectorStyleConfig,
306
+ };
307
+
308
+ it('should ensure terrain for all style config', async () => {
309
+ const request = mockUrlRequest('/v1/styles/topolite.json', '?terrain=LINZ-Terrain', Api.header);
310
+ config.put(fakeVectorRecord);
311
+ config.put(TileSetElevation);
312
+
313
+ const res = await handler.router.handle(request);
314
+ assert.equal(res.status, 200, res.statusDescription);
315
+
316
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString()) as StyleJson;
317
+ const rasterDemSource = body.sources['LINZ-Terrain'] as unknown as SourceRaster;
318
+
319
+ assert.deepEqual(rasterDemSource.type, 'raster-dem');
320
+ assert.deepEqual(rasterDemSource.tiles, [
321
+ `https://tiles.test/v1/tiles/elevation/WebMercatorQuad/{z}/{x}/{y}.png?api=${Api.key}&pipeline=terrain-rgb`,
322
+ ]);
323
+ });
324
+
325
+ const fakeAerialStyleConfig = {
326
+ id: 'test',
327
+ name: 'test',
328
+ sources: {
329
+ basemaps_raster: {
330
+ type: 'raster',
331
+ tiles: [`/raster/{z}/{x}/{y}.webp`],
332
+ },
333
+ basemaps_terrain: {
334
+ type: 'raster-dem',
335
+ tiles: [`/elevation/{z}/{x}/{y}.png?pipeline=terrain-rgb`],
336
+ },
337
+ },
338
+ layers: [
339
+ {
340
+ layout: {
341
+ visibility: 'visible',
342
+ },
343
+ paint: {
344
+ 'background-color': 'rgba(206, 229, 242, 1)',
345
+ },
346
+ id: 'Background1',
347
+ type: 'background',
348
+ minzoom: 0,
349
+ },
350
+ ],
351
+ };
352
+
353
+ const fakeAerialRecord = {
354
+ id: 'st_aerial',
355
+ name: 'aerial',
356
+ style: fakeAerialStyleConfig,
357
+ };
358
+
359
+ it('should set terrain via parameter for style config', async () => {
360
+ const request = mockUrlRequest('/v1/styles/aerial.json', '?terrain=basemaps_terrain', Api.header);
361
+ config.put(fakeAerialRecord);
362
+ const res = await handler.router.handle(request);
363
+ assert.equal(res.status, 200, res.statusDescription);
364
+
365
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString()) as StyleJson;
366
+ const terrain = body.terrain as unknown as Terrain;
367
+ assert.deepEqual(terrain.source, 'basemaps_terrain');
368
+ assert.deepEqual(terrain.exaggeration, 1.2);
369
+ });
370
+
371
+ it('should set terrain via parameter for tileSet config', async () => {
372
+ config.put(TileSetAerial);
373
+ config.put(TileSetElevation);
374
+ const request = mockUrlRequest('/v1/styles/aerial.json', `?terrain=LINZ-Terrain`, Api.header);
375
+ const res = await handler.router.handle(request);
376
+ assert.equal(res.status, 200, res.statusDescription);
377
+
378
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString()) as StyleJson;
379
+ const terrain = body.terrain as unknown as Terrain;
380
+ assert.deepEqual(terrain.source, 'LINZ-Terrain');
381
+ assert.deepEqual(terrain.exaggeration, 1.2);
382
+ });
223
383
  });
@@ -4,7 +4,7 @@ import { afterEach, beforeEach, describe, it } from 'node:test';
4
4
  import { ConfigProviderMemory, ConfigTileSetRaster } from '@basemaps/config';
5
5
  import { Env } from '@basemaps/shared';
6
6
 
7
- import { Imagery2193, Imagery3857, Provider, TileSetAerial } from '../../__tests__/config.data.js';
7
+ import { Imagery2193, Imagery3857, Provider, TileSetAerial, TileSetElevation } from '../../__tests__/config.data.js';
8
8
  import { Api, mockUrlRequest } from '../../__tests__/xyz.util.js';
9
9
  import { handler } from '../../index.js';
10
10
  import { ConfigLoader } from '../../util/config.loader.js';
@@ -27,6 +27,28 @@ describe('WMTSRouting', () => {
27
27
  config.objects.clear();
28
28
  });
29
29
 
30
+ it('should support pipeline', async (t) => {
31
+ t.mock.method(Env, 'get', (arg: string) => {
32
+ if (arg === Env.PublicUrlBase) return 'https://tiles.test';
33
+ return process.env[arg];
34
+ });
35
+ config.put(TileSetElevation);
36
+ t.mock.method(ConfigLoader, 'load', () => Promise.resolve(config));
37
+ const req = mockUrlRequest(
38
+ '/v1/tiles/elevation/WebMercatorQuad/WMTSCapabilities.xml',
39
+ `tileFormat=png&api=${Api.key}&config=s3://linz-basemaps/config.json&pipeline=terrain-rgb`,
40
+ );
41
+ const res = await handler.router.handle(req);
42
+
43
+ assert.equal(res.status, 200);
44
+ const lines = Buffer.from(res.body, 'base64').toString().split('\n');
45
+ const resourceUrl = lines.find((f) => f.includes('ResourceURL'))?.trim();
46
+
47
+ assert.ok(resourceUrl);
48
+ assert.ok(resourceUrl.includes('amp;pipeline=terrain-rgb'), `includes pipeline=terrain-rgb in ${resourceUrl}`);
49
+ assert.ok(resourceUrl.includes('.png'), `includes .png in ${resourceUrl}`);
50
+ });
51
+
30
52
  it('should default to the aerial layer', async (t) => {
31
53
  t.mock.method(Env, 'get', (arg: string) => {
32
54
  if (arg === Env.PublicUrlBase) return 'https://tiles.test';
@@ -68,8 +90,6 @@ describe('WMTSRouting', () => {
68
90
 
69
91
  config.put({ ...TileSetAerial, id: 'ts_all', name: 'all', layers: [] } as ConfigTileSetRaster);
70
92
 
71
- config.createVirtualTileSets;
72
-
73
93
  const req = mockUrlRequest(
74
94
  '/v1/tiles/all/WMTSCapabilities.xml',
75
95
  `format=png&api=${Api.key}&config=s3://linz-basemaps/config.json`,
@@ -6,7 +6,7 @@ import { ConfigLoader } from '../util/config.loader.js';
6
6
  import { Etag } from '../util/etag.js';
7
7
  import { NotFound, NotModified } from '../util/response.js';
8
8
 
9
- async function sendJson(req: LambdaHttpRequest, toSend: unknown): Promise<LambdaHttpResponse> {
9
+ function sendJson(req: LambdaHttpRequest, toSend: unknown): LambdaHttpResponse {
10
10
  const data = Buffer.from(JSON.stringify(toSend));
11
11
 
12
12
  const cacheKey = Etag.key(data);
@@ -3,6 +3,6 @@ import { HttpHeader, LambdaHttpResponse } from '@linzjs/lambda';
3
3
  const OkResponse = new LambdaHttpResponse(200, 'ok');
4
4
  OkResponse.header(HttpHeader.CacheControl, 'no-store');
5
5
 
6
- export async function pingGet(): Promise<LambdaHttpResponse> {
7
- return OkResponse;
6
+ export function pingGet(): Promise<LambdaHttpResponse> {
7
+ return Promise.resolve(OkResponse);
8
8
  }
@@ -1,6 +1,7 @@
1
1
  import { promisify } from 'node:util';
2
2
  import { gunzip } from 'node:zlib';
3
3
 
4
+ import { TileSetType } from '@basemaps/config';
4
5
  import { GoogleTms, LocationUrl, LonLatZoom, TileMatrixSets } from '@basemaps/geo';
5
6
  import { Env, fsa, getPreviewQuery } from '@basemaps/shared';
6
7
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
@@ -95,7 +96,7 @@ export async function previewIndexGet(req: LambdaHttpRequest<PreviewIndexGet>):
95
96
  const tileSet = await config.TileSet.get(config.TileSet.id(query.style));
96
97
  req.timer.end('tileset:load');
97
98
  if (tileSet == null) return loadAndServeIndexHtml(req, loc);
98
- if (tileSet.type !== 'raster') return loadAndServeIndexHtml(req, loc);
99
+ if (tileSet.type !== TileSetType.Raster) return loadAndServeIndexHtml(req, loc);
99
100
 
100
101
  let tileMatrix = TileMatrixSets.find(query.tileMatrix);
101
102
  if (tileMatrix == null) tileMatrix = GoogleTms;
@@ -1,4 +1,4 @@
1
- import { ConfigTileSetRaster, ConfigTileSetRasterOutput } from '@basemaps/config';
1
+ import { ConfigTileSetRaster, ConfigTileSetRasterOutput, TileSetType } from '@basemaps/config';
2
2
  import { Bounds, LatLon, Projection, TileMatrixSet } from '@basemaps/geo';
3
3
  import { CompositionTiff, TileMakerContext, Tiler } from '@basemaps/tiler';
4
4
  import { SharpOverlay, TileMakerSharp } from '@basemaps/tiler-sharp';
@@ -62,7 +62,7 @@ export async function tilePreviewGet(req: LambdaHttpRequest<PreviewGet>): Promis
62
62
  req.timer.end('tileset:load');
63
63
  if (tileSet == null) return new LambdaHttpResponse(404, 'Tileset not found');
64
64
  // Only raster previews are supported
65
- if (tileSet.type !== 'raster') return new LambdaHttpResponse(404, 'Preview invalid tile set type');
65
+ if (tileSet.type !== TileSetType.Raster) return new LambdaHttpResponse(404, 'Preview invalid tile set type');
66
66
 
67
67
  const pipeline = req.query.get('pipeline');
68
68
 
@@ -161,6 +161,8 @@ export async function renderPreview(req: LambdaHttpRequest, ctx: PreviewRenderCo
161
161
 
162
162
  // Load all the tiff tiles and resize/them into the correct locations
163
163
  req.timer.start('compose:overlay');
164
+ // Remove with typescript >=5.5.0
165
+
164
166
  const overlays = (await Promise.all(
165
167
  compositions.map((comp) => {
166
168
  if (tileContext.pipeline) return TilerSharp.composeTilePipeline(comp, tileContext);
@@ -10,7 +10,7 @@ interface SpriteGet {
10
10
  };
11
11
  }
12
12
 
13
- const Extensions = new Map();
13
+ const Extensions = new Map<string, string>();
14
14
  Extensions.set('.png', 'image/png');
15
15
  Extensions.set('.json', 'application/json');
16
16
 
@@ -1,4 +1,4 @@
1
- import { ConfigTileSetRaster, Layer, Sources, StyleJson, TileSetType } from '@basemaps/config';
1
+ import { ConfigId, ConfigPrefix, ConfigTileSetRaster, Layer, Sources, StyleJson, TileSetType } from '@basemaps/config';
2
2
  import { GoogleTms, TileMatrixSet, TileMatrixSets } from '@basemaps/geo';
3
3
  import { Env, toQueryString } from '@basemaps/shared';
4
4
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
@@ -44,10 +44,12 @@ export function convertStyleJson(
44
44
  config: string | null,
45
45
  layers?: Layer[],
46
46
  ): StyleJson {
47
- const sources: Sources = JSON.parse(JSON.stringify(style.sources));
47
+ const sources = JSON.parse(JSON.stringify(style.sources)) as Sources;
48
48
  for (const [key, value] of Object.entries(sources)) {
49
49
  if (value.type === 'vector') {
50
- if (tileMatrix !== GoogleTms) throw new Error(`TileMatrix is not supported for the vector source ${value.url}.`);
50
+ if (tileMatrix !== GoogleTms) {
51
+ throw new LambdaHttpResponse(400, `TileMatrix is not supported for the vector source ${value.url}.`);
52
+ }
51
53
  value.url = convertRelativeUrl(value.url, tileMatrix, apiKey, config);
52
54
  } else if ((value.type === 'raster' || value.type === 'raster-dem') && Array.isArray(value.tiles)) {
53
55
  for (let i = 0; i < value.tiles.length; i++) {
@@ -78,11 +80,41 @@ export interface StyleGet {
78
80
  };
79
81
  }
80
82
 
83
+ function setStyleTerrain(style: StyleJson, terrain: string): void {
84
+ const source = Object.keys(style.sources).find((s) => s === terrain);
85
+ if (source == null) throw new LambdaHttpResponse(400, `Terrain: ${terrain} is not exists in the style source.`);
86
+ style.terrain = {
87
+ source,
88
+ exaggeration: 1.2,
89
+ };
90
+ }
91
+
92
+ async function ensureTerrain(
93
+ req: LambdaHttpRequest<StyleGet>,
94
+ tileMatrix: TileMatrixSet,
95
+ apiKey: string,
96
+ style: StyleJson,
97
+ ): Promise<void> {
98
+ const config = await ConfigLoader.load(req);
99
+ const terrain = await config.TileSet.get('ts_elevation');
100
+ if (terrain) {
101
+ const configLocation = ConfigLoader.extract(req);
102
+ const elevationQuery = toQueryString({ config: configLocation, api: apiKey, pipeline: 'terrain-rgb' });
103
+ style.sources['LINZ-Terrain'] = {
104
+ type: 'raster-dem',
105
+ tileSize: 256,
106
+ maxzoom: 18,
107
+ tiles: [convertRelativeUrl(`/v1/tiles/elevation/${tileMatrix.identifier}/{z}/{x}/{y}.png${elevationQuery}`)],
108
+ };
109
+ }
110
+ }
111
+
81
112
  export async function tileSetToStyle(
82
113
  req: LambdaHttpRequest<StyleGet>,
83
114
  tileSet: ConfigTileSetRaster,
84
115
  tileMatrix: TileMatrixSet,
85
116
  apiKey: string,
117
+ terrain?: string,
86
118
  ): Promise<LambdaHttpResponse> {
87
119
  const [tileFormat] = Validate.getRequestedFormats(req) ?? ['webp'];
88
120
  if (tileFormat == null) return new LambdaHttpResponse(400, 'Invalid image format');
@@ -98,11 +130,20 @@ export async function tileSetToStyle(
98
130
  `/v1/tiles/${tileSet.name}/${tileMatrix.identifier}/{z}/{x}/{y}.${tileFormat}${query}`;
99
131
 
100
132
  const styleId = `basemaps-${tileSet.name}`;
101
- const style = {
133
+ const style: StyleJson = {
134
+ id: ConfigId.prefix(ConfigPrefix.Style, tileSet.name),
135
+ name: tileSet.name,
102
136
  version: 8,
103
137
  sources: { [styleId]: { type: 'raster', tiles: [tileUrl], tileSize: 256 } },
104
138
  layers: [{ id: styleId, type: 'raster', source: styleId }],
105
139
  };
140
+
141
+ // Ensure elevation for individual tilesets
142
+ await ensureTerrain(req, tileMatrix, apiKey, style);
143
+
144
+ // Add terrain in style
145
+ if (terrain) setStyleTerrain(style, terrain);
146
+
106
147
  const data = Buffer.from(JSON.stringify(style));
107
148
 
108
149
  const cacheKey = Etag.key(data);
@@ -121,6 +162,7 @@ export async function tileSetOutputToStyle(
121
162
  tileSet: ConfigTileSetRaster,
122
163
  tileMatrix: TileMatrixSet,
123
164
  apiKey: string,
165
+ terrain?: string,
124
166
  ): Promise<LambdaHttpResponse> {
125
167
  const configLocation = ConfigLoader.extract(req);
126
168
  const query = toQueryString({ config: configLocation, api: apiKey });
@@ -171,19 +213,31 @@ export async function tileSetOutputToStyle(
171
213
  }
172
214
  }
173
215
 
174
- const style = { version: 8, sources, layers };
216
+ const style: StyleJson = {
217
+ id: ConfigId.prefix(ConfigPrefix.Style, tileSet.name),
218
+ name: tileSet.name,
219
+ version: 8,
220
+ sources,
221
+ layers,
222
+ };
223
+
224
+ // Ensure elevation for style json config
225
+ await ensureTerrain(req, tileMatrix, apiKey, style);
226
+
227
+ // Add terrain in style
228
+ if (terrain) setStyleTerrain(style, terrain);
175
229
 
176
230
  const data = Buffer.from(JSON.stringify(style));
177
231
 
178
232
  const cacheKey = Etag.key(data);
179
- if (Etag.isNotModified(req, cacheKey)) return NotModified();
233
+ if (Etag.isNotModified(req, cacheKey)) return Promise.resolve(NotModified());
180
234
 
181
235
  const response = new LambdaHttpResponse(200, 'ok');
182
236
  response.header(HttpHeader.ETag, cacheKey);
183
237
  response.header(HttpHeader.CacheControl, 'no-store');
184
238
  response.buffer(data, 'application/json');
185
239
  req.set('bytes', data.byteLength);
186
- return response;
240
+ return Promise.resolve(response);
187
241
  }
188
242
 
189
243
  export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<LambdaHttpResponse> {
@@ -193,6 +247,7 @@ export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<La
193
247
  const excluded = new Set(excludeLayers.map((l) => l.toLowerCase()));
194
248
  const tileMatrix = TileMatrixSets.find(req.query.get('tileMatrix') ?? GoogleTms.identifier);
195
249
  if (tileMatrix == null) return new LambdaHttpResponse(400, 'Invalid tile matrix');
250
+ const terrain = req.query.get('terrain') ?? undefined;
196
251
 
197
252
  // Get style Config from db
198
253
  const config = await ConfigLoader.load(req);
@@ -203,8 +258,8 @@ export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<La
203
258
  const tileSet = await config.TileSet.get(config.TileSet.id(styleName));
204
259
  if (tileSet == null) return NotFound();
205
260
  if (tileSet.type !== TileSetType.Raster) return NotFound();
206
- if (tileSet.outputs) return tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey);
207
- else return tileSetToStyle(req, tileSet, tileMatrix, apiKey);
261
+ if (tileSet.outputs) return await tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey, terrain);
262
+ else return await tileSetToStyle(req, tileSet, tileMatrix, apiKey, terrain);
208
263
  }
209
264
 
210
265
  // Prepare sources and add linz source
@@ -215,6 +270,14 @@ export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<La
215
270
  ConfigLoader.extract(req),
216
271
  styleConfig.style.layers.filter((f) => !excluded.has(f.id.toLowerCase())),
217
272
  );
273
+
274
+ // Ensure elevation for style json config
275
+ // TODO: We should remove this after adding terrain source into style configs. PR-916
276
+ await ensureTerrain(req, tileMatrix, apiKey, style);
277
+
278
+ // Add terrain in style
279
+ if (terrain) setStyleTerrain(style, terrain);
280
+
218
281
  const data = Buffer.from(JSON.stringify(style));
219
282
 
220
283
  const cacheKey = Etag.key(data);
@@ -69,6 +69,7 @@ export async function wmtsCapabilitiesGet(req: LambdaHttpRequest<WmtsCapabilitie
69
69
  imagery,
70
70
  formats: Validate.getRequestedFormats(req) ?? [],
71
71
  layers: req.params.tileMatrix == null ? tileSet.layers : undefined,
72
+ pipeline: req.query.get('pipeline'),
72
73
  });
73
74
 
74
75
  const xml = wmts.toXml();
@@ -95,19 +95,21 @@ export const TileXyzRaster = {
95
95
  toLoad.push(
96
96
  LoadingQueue((): Promise<CloudArchive | null> => {
97
97
  if (assetPath.pathname.endsWith('.tar.co')) {
98
- return CoSources.getCotar(assetPath).catch((error) => {
99
- req.log.warn({ error, tiff: assetPath }, 'Load:Cotar:Failed');
98
+ return CoSources.getCotar(assetPath).catch((err: unknown) => {
99
+ req.log.warn({ err, tiff: assetPath }, 'Load:Cotar:Failed');
100
100
  return null;
101
101
  });
102
102
  }
103
- return CoSources.getCog(assetPath).catch((error) => {
104
- req.log.warn({ error, tiff: assetPath }, 'Load:Tiff:Failed');
103
+ return CoSources.getCog(assetPath).catch((err: unknown) => {
104
+ req.log.warn({ err, tiff: assetPath }, 'Load:Tiff:Failed');
105
105
  return null;
106
106
  });
107
107
  }),
108
108
  );
109
109
  }
110
110
 
111
+ // Remove with typescript >=5.5.0
112
+
111
113
  return (await Promise.all(toLoad)).filter((f) => f != null) as CloudArchive[];
112
114
  },
113
115
 
@@ -128,7 +130,7 @@ export const TileXyzRaster = {
128
130
  const assets = await TileXyzRaster.loadAssets(req, assetPaths);
129
131
 
130
132
  const tiler = new Tiler(xyz.tileMatrix);
131
- const layers = await tiler.tile(assets, xyz.tile.x, xyz.tile.y, xyz.tile.z);
133
+ const layers = tiler.tile(assets, xyz.tile.x, xyz.tile.y, xyz.tile.z);
132
134
 
133
135
  const format = getImageFormat(xyz.tileType);
134
136
  if (format == null) return new LambdaHttpResponse(400, 'Invalid image format: ' + xyz.tileType);
@@ -1,6 +1,6 @@
1
1
  import { HttpHeader, LambdaHttpResponse } from '@linzjs/lambda';
2
2
 
3
- export async function versionGet(): Promise<LambdaHttpResponse> {
3
+ export function versionGet(): Promise<LambdaHttpResponse> {
4
4
  const response = new LambdaHttpResponse(200, 'ok');
5
5
  response.header(HttpHeader.CacheControl, 'no-store');
6
6
  response.json({
@@ -20,5 +20,5 @@ export async function versionGet(): Promise<LambdaHttpResponse> {
20
20
  */
21
21
  buildId: process.env['BUILD_ID'],
22
22
  });
23
- return response;
23
+ return Promise.resolve(response);
24
24
  }