@basemaps/lambda-tiler 7.4.0 → 7.6.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 (99) hide show
  1. package/CHANGELOG.md +36 -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.d.ts +0 -2
  24. package/build/routes/__tests__/memory.fs.js +1 -1
  25. package/build/routes/__tests__/memory.fs.js.map +1 -1
  26. package/build/routes/__tests__/preview.index.test.js +1 -1
  27. package/build/routes/__tests__/preview.index.test.js.map +1 -1
  28. package/build/routes/__tests__/tile.json.test.js.map +1 -1
  29. package/build/routes/__tests__/tile.style.json.test.js +144 -11
  30. package/build/routes/__tests__/tile.style.json.test.js.map +1 -1
  31. package/build/routes/__tests__/wmts.test.js +18 -2
  32. package/build/routes/__tests__/wmts.test.js.map +1 -1
  33. package/build/routes/attribution.d.ts +0 -1
  34. package/build/routes/config.js +1 -1
  35. package/build/routes/config.js.map +1 -1
  36. package/build/routes/health.d.ts +0 -1
  37. package/build/routes/ping.js +2 -2
  38. package/build/routes/ping.js.map +1 -1
  39. package/build/routes/preview.index.js +2 -1
  40. package/build/routes/preview.index.js.map +1 -1
  41. package/build/routes/preview.js +5 -3
  42. package/build/routes/preview.js.map +1 -1
  43. package/build/routes/sprites.js.map +1 -1
  44. package/build/routes/tile.style.json.d.ts +2 -2
  45. package/build/routes/tile.style.json.js +61 -10
  46. package/build/routes/tile.style.json.js.map +1 -1
  47. package/build/routes/tile.wmts.js +1 -0
  48. package/build/routes/tile.wmts.js.map +1 -1
  49. package/build/routes/tile.xyz.raster.d.ts +0 -1
  50. package/build/routes/tile.xyz.raster.js +6 -5
  51. package/build/routes/tile.xyz.raster.js.map +1 -1
  52. package/build/routes/version.js +2 -2
  53. package/build/routes/version.js.map +1 -1
  54. package/build/util/__test__/config.loader.test.js +12 -12
  55. package/build/util/__test__/config.loader.test.js.map +1 -1
  56. package/build/util/assets.provider.d.ts +0 -2
  57. package/build/util/config.cache.d.ts +0 -1
  58. package/build/util/config.cache.js.map +1 -1
  59. package/build/util/cotar.serve.d.ts +0 -2
  60. package/build/util/source.cache.d.ts +0 -1
  61. package/build/util/source.cache.js +5 -4
  62. package/build/util/source.cache.js.map +1 -1
  63. package/build/util/validate.js +1 -1
  64. package/build/util/validate.js.map +1 -1
  65. package/build/wmts.capability.d.ts +8 -4
  66. package/build/wmts.capability.js +18 -8
  67. package/build/wmts.capability.js.map +1 -1
  68. package/package.json +10 -10
  69. package/src/__tests__/config.data.ts +31 -2
  70. package/src/__tests__/index.test.ts +2 -3
  71. package/src/__tests__/tile.style.json.test.ts +2 -5
  72. package/src/__tests__/wmts.capability.test.ts +13 -10
  73. package/src/__tests__/xyz.util.ts +4 -4
  74. package/src/cli/render.preview.ts +5 -2
  75. package/src/cli/render.tile.ts +1 -1
  76. package/src/routes/__tests__/attribution.test.ts +7 -7
  77. package/src/routes/__tests__/health.test.ts +3 -3
  78. package/src/routes/__tests__/imagery.test.ts +1 -1
  79. package/src/routes/__tests__/memory.fs.ts +2 -2
  80. package/src/routes/__tests__/preview.index.test.ts +3 -3
  81. package/src/routes/__tests__/tile.json.test.ts +1 -1
  82. package/src/routes/__tests__/tile.style.json.test.ts +175 -15
  83. package/src/routes/__tests__/wmts.test.ts +23 -3
  84. package/src/routes/config.ts +1 -1
  85. package/src/routes/ping.ts +2 -2
  86. package/src/routes/preview.index.ts +2 -1
  87. package/src/routes/preview.ts +7 -5
  88. package/src/routes/sprites.ts +1 -1
  89. package/src/routes/tile.style.json.ts +73 -9
  90. package/src/routes/tile.wmts.ts +1 -0
  91. package/src/routes/tile.xyz.raster.ts +8 -6
  92. package/src/routes/version.ts +2 -2
  93. package/src/util/__test__/config.loader.test.ts +17 -20
  94. package/src/util/config.cache.ts +2 -1
  95. package/src/util/source.cache.ts +6 -4
  96. package/src/util/validate.ts +1 -1
  97. package/src/wmts.capability.ts +17 -9
  98. package/tsconfig.json +10 -10
  99. 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,7 +1,7 @@
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
- import { SharpOverlay, TileMakerSharp } from '@basemaps/tiler-sharp';
4
+ import { TileMakerSharp } from '@basemaps/tiler-sharp';
5
5
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
6
6
  import sharp from 'sharp';
7
7
 
@@ -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,12 +161,14 @@ 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
- const overlays = (await Promise.all(
164
+ // Remove with typescript >=5.5.0
165
+
166
+ const overlays = await Promise.all(
165
167
  compositions.map((comp) => {
166
168
  if (tileContext.pipeline) return TilerSharp.composeTilePipeline(comp, tileContext);
167
169
  return TilerSharp.composeTileTiff(comp, tileContext.resizeKernel);
168
170
  }),
169
- ).then((items) => items.filter((f) => f != null))) as SharpOverlay[];
171
+ ).then((items) => items.filter((f) => f != null));
170
172
  req.timer.end('compose:overlay');
171
173
 
172
174
  // Create the output image and render all the individual pieces into them
@@ -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++) {
@@ -68,6 +70,7 @@ export function convertStyleJson(
68
70
  if (style.metadata) styleJson.metadata = style.metadata;
69
71
  if (style.glyphs) styleJson.glyphs = convertRelativeUrl(style.glyphs, undefined, undefined, config);
70
72
  if (style.sprite) styleJson.sprite = convertRelativeUrl(style.sprite, undefined, undefined, config);
73
+ if (style.sky) styleJson.sky = style.sky;
71
74
 
72
75
  return styleJson;
73
76
  }
@@ -78,11 +81,41 @@ export interface StyleGet {
78
81
  };
79
82
  }
80
83
 
84
+ function setStyleTerrain(style: StyleJson, terrain: string): void {
85
+ const source = Object.keys(style.sources).find((s) => s === terrain);
86
+ if (source == null) throw new LambdaHttpResponse(400, `Terrain: ${terrain} is not exists in the style source.`);
87
+ style.terrain = {
88
+ source,
89
+ exaggeration: 1.2,
90
+ };
91
+ }
92
+
93
+ async function ensureTerrain(
94
+ req: LambdaHttpRequest<StyleGet>,
95
+ tileMatrix: TileMatrixSet,
96
+ apiKey: string,
97
+ style: StyleJson,
98
+ ): Promise<void> {
99
+ const config = await ConfigLoader.load(req);
100
+ const terrain = await config.TileSet.get('ts_elevation');
101
+ if (terrain) {
102
+ const configLocation = ConfigLoader.extract(req);
103
+ const elevationQuery = toQueryString({ config: configLocation, api: apiKey, pipeline: 'terrain-rgb' });
104
+ style.sources['LINZ-Terrain'] = {
105
+ type: 'raster-dem',
106
+ tileSize: 256,
107
+ maxzoom: 18,
108
+ tiles: [convertRelativeUrl(`/v1/tiles/elevation/${tileMatrix.identifier}/{z}/{x}/{y}.png${elevationQuery}`)],
109
+ };
110
+ }
111
+ }
112
+
81
113
  export async function tileSetToStyle(
82
114
  req: LambdaHttpRequest<StyleGet>,
83
115
  tileSet: ConfigTileSetRaster,
84
116
  tileMatrix: TileMatrixSet,
85
117
  apiKey: string,
118
+ terrain?: string,
86
119
  ): Promise<LambdaHttpResponse> {
87
120
  const [tileFormat] = Validate.getRequestedFormats(req) ?? ['webp'];
88
121
  if (tileFormat == null) return new LambdaHttpResponse(400, 'Invalid image format');
@@ -98,11 +131,20 @@ export async function tileSetToStyle(
98
131
  `/v1/tiles/${tileSet.name}/${tileMatrix.identifier}/{z}/{x}/{y}.${tileFormat}${query}`;
99
132
 
100
133
  const styleId = `basemaps-${tileSet.name}`;
101
- const style = {
134
+ const style: StyleJson = {
135
+ id: ConfigId.prefix(ConfigPrefix.Style, tileSet.name),
136
+ name: tileSet.name,
102
137
  version: 8,
103
138
  sources: { [styleId]: { type: 'raster', tiles: [tileUrl], tileSize: 256 } },
104
139
  layers: [{ id: styleId, type: 'raster', source: styleId }],
105
140
  };
141
+
142
+ // Ensure elevation for individual tilesets
143
+ await ensureTerrain(req, tileMatrix, apiKey, style);
144
+
145
+ // Add terrain in style
146
+ if (terrain) setStyleTerrain(style, terrain);
147
+
106
148
  const data = Buffer.from(JSON.stringify(style));
107
149
 
108
150
  const cacheKey = Etag.key(data);
@@ -121,6 +163,7 @@ export async function tileSetOutputToStyle(
121
163
  tileSet: ConfigTileSetRaster,
122
164
  tileMatrix: TileMatrixSet,
123
165
  apiKey: string,
166
+ terrain?: string,
124
167
  ): Promise<LambdaHttpResponse> {
125
168
  const configLocation = ConfigLoader.extract(req);
126
169
  const query = toQueryString({ config: configLocation, api: apiKey });
@@ -171,19 +214,31 @@ export async function tileSetOutputToStyle(
171
214
  }
172
215
  }
173
216
 
174
- const style = { version: 8, sources, layers };
217
+ const style: StyleJson = {
218
+ id: ConfigId.prefix(ConfigPrefix.Style, tileSet.name),
219
+ name: tileSet.name,
220
+ version: 8,
221
+ sources,
222
+ layers,
223
+ };
224
+
225
+ // Ensure elevation for style json config
226
+ await ensureTerrain(req, tileMatrix, apiKey, style);
227
+
228
+ // Add terrain in style
229
+ if (terrain) setStyleTerrain(style, terrain);
175
230
 
176
231
  const data = Buffer.from(JSON.stringify(style));
177
232
 
178
233
  const cacheKey = Etag.key(data);
179
- if (Etag.isNotModified(req, cacheKey)) return NotModified();
234
+ if (Etag.isNotModified(req, cacheKey)) return Promise.resolve(NotModified());
180
235
 
181
236
  const response = new LambdaHttpResponse(200, 'ok');
182
237
  response.header(HttpHeader.ETag, cacheKey);
183
238
  response.header(HttpHeader.CacheControl, 'no-store');
184
239
  response.buffer(data, 'application/json');
185
240
  req.set('bytes', data.byteLength);
186
- return response;
241
+ return Promise.resolve(response);
187
242
  }
188
243
 
189
244
  export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<LambdaHttpResponse> {
@@ -193,6 +248,7 @@ export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<La
193
248
  const excluded = new Set(excludeLayers.map((l) => l.toLowerCase()));
194
249
  const tileMatrix = TileMatrixSets.find(req.query.get('tileMatrix') ?? GoogleTms.identifier);
195
250
  if (tileMatrix == null) return new LambdaHttpResponse(400, 'Invalid tile matrix');
251
+ const terrain = req.query.get('terrain') ?? undefined;
196
252
 
197
253
  // Get style Config from db
198
254
  const config = await ConfigLoader.load(req);
@@ -203,8 +259,8 @@ export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<La
203
259
  const tileSet = await config.TileSet.get(config.TileSet.id(styleName));
204
260
  if (tileSet == null) return NotFound();
205
261
  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);
262
+ if (tileSet.outputs) return await tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey, terrain);
263
+ else return await tileSetToStyle(req, tileSet, tileMatrix, apiKey, terrain);
208
264
  }
209
265
 
210
266
  // Prepare sources and add linz source
@@ -215,6 +271,14 @@ export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<La
215
271
  ConfigLoader.extract(req),
216
272
  styleConfig.style.layers.filter((f) => !excluded.has(f.id.toLowerCase())),
217
273
  );
274
+
275
+ // Ensure elevation for style json config
276
+ // TODO: We should remove this after adding terrain source into style configs. PR-916
277
+ await ensureTerrain(req, tileMatrix, apiKey, style);
278
+
279
+ // Add terrain in style
280
+ if (terrain) setStyleTerrain(style, terrain);
281
+
218
282
  const data = Buffer.from(JSON.stringify(style));
219
283
 
220
284
  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,20 +95,22 @@ 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
- return (await Promise.all(toLoad)).filter((f) => f != null) as CloudArchive[];
111
+ // Remove with typescript >=5.5.0
112
+
113
+ return (await Promise.all(toLoad)).filter((f) => f != null);
112
114
  },
113
115
 
114
116
  async getAssetsForTile(req: LambdaHttpRequest, tileSet: ConfigTileSetRaster, xyz: TileXyz): Promise<URL[]> {
@@ -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
  }