@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.
- package/CHANGELOG.md +20 -0
- package/build/__tests__/config.data.d.ts +2 -0
- package/build/__tests__/config.data.js +24 -1
- package/build/__tests__/config.data.js.map +1 -1
- package/build/__tests__/index.test.js +1 -2
- package/build/__tests__/index.test.js.map +1 -1
- package/build/__tests__/tile.style.json.test.js +2 -2
- package/build/__tests__/tile.style.json.test.js.map +1 -1
- package/build/__tests__/wmts.capability.test.js +5 -2
- package/build/__tests__/wmts.capability.test.js.map +1 -1
- package/build/__tests__/xyz.util.js.map +1 -1
- package/build/cli/render.preview.js +5 -2
- package/build/cli/render.preview.js.map +1 -1
- package/build/cli/render.tile.js +1 -1
- package/build/cli/render.tile.js.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/routes/__tests__/attribution.test.js +1 -1
- package/build/routes/__tests__/attribution.test.js.map +1 -1
- package/build/routes/__tests__/health.test.js +1 -1
- package/build/routes/__tests__/health.test.js.map +1 -1
- package/build/routes/__tests__/imagery.test.js.map +1 -1
- package/build/routes/__tests__/memory.fs.js +1 -1
- package/build/routes/__tests__/memory.fs.js.map +1 -1
- package/build/routes/__tests__/preview.index.test.js +1 -1
- package/build/routes/__tests__/preview.index.test.js.map +1 -1
- package/build/routes/__tests__/tile.json.test.js.map +1 -1
- package/build/routes/__tests__/tile.style.json.test.js +144 -11
- package/build/routes/__tests__/tile.style.json.test.js.map +1 -1
- package/build/routes/__tests__/wmts.test.js +18 -2
- package/build/routes/__tests__/wmts.test.js.map +1 -1
- package/build/routes/config.js +1 -1
- package/build/routes/config.js.map +1 -1
- package/build/routes/ping.js +2 -2
- package/build/routes/ping.js.map +1 -1
- package/build/routes/preview.index.js +2 -1
- package/build/routes/preview.index.js.map +1 -1
- package/build/routes/preview.js +3 -1
- package/build/routes/preview.js.map +1 -1
- package/build/routes/sprites.js.map +1 -1
- package/build/routes/tile.style.json.d.ts +2 -2
- package/build/routes/tile.style.json.js +59 -10
- package/build/routes/tile.style.json.js.map +1 -1
- package/build/routes/tile.wmts.js +1 -0
- package/build/routes/tile.wmts.js.map +1 -1
- package/build/routes/tile.xyz.raster.js +6 -5
- package/build/routes/tile.xyz.raster.js.map +1 -1
- package/build/routes/version.js +2 -2
- package/build/routes/version.js.map +1 -1
- package/build/util/__test__/config.loader.test.js +12 -12
- package/build/util/__test__/config.loader.test.js.map +1 -1
- package/build/util/config.cache.js.map +1 -1
- package/build/util/source.cache.js +5 -4
- package/build/util/source.cache.js.map +1 -1
- package/build/util/validate.js +1 -1
- package/build/util/validate.js.map +1 -1
- package/build/wmts.capability.d.ts +8 -4
- package/build/wmts.capability.js +18 -8
- package/build/wmts.capability.js.map +1 -1
- package/package.json +10 -10
- package/src/__tests__/config.data.ts +31 -2
- package/src/__tests__/index.test.ts +2 -3
- package/src/__tests__/tile.style.json.test.ts +2 -5
- package/src/__tests__/wmts.capability.test.ts +13 -10
- package/src/__tests__/xyz.util.ts +4 -4
- package/src/cli/render.preview.ts +5 -2
- package/src/cli/render.tile.ts +1 -1
- package/src/routes/__tests__/attribution.test.ts +7 -7
- package/src/routes/__tests__/health.test.ts +3 -3
- package/src/routes/__tests__/imagery.test.ts +1 -1
- package/src/routes/__tests__/memory.fs.ts +2 -2
- package/src/routes/__tests__/preview.index.test.ts +3 -3
- package/src/routes/__tests__/tile.json.test.ts +1 -1
- package/src/routes/__tests__/tile.style.json.test.ts +175 -15
- package/src/routes/__tests__/wmts.test.ts +23 -3
- package/src/routes/config.ts +1 -1
- package/src/routes/ping.ts +2 -2
- package/src/routes/preview.index.ts +2 -1
- package/src/routes/preview.ts +4 -2
- package/src/routes/sprites.ts +1 -1
- package/src/routes/tile.style.json.ts +72 -9
- package/src/routes/tile.wmts.ts +1 -0
- package/src/routes/tile.xyz.raster.ts +7 -5
- package/src/routes/version.ts +2 -2
- package/src/util/__test__/config.loader.test.ts +17 -20
- package/src/util/config.cache.ts +2 -1
- package/src/util/source.cache.ts +6 -4
- package/src/util/validate.ts +1 -1
- package/src/wmts.capability.ts +17 -9
- package/tsconfig.json +10 -10
- 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
|
-
|
|
181
|
-
|
|
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(
|
|
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
|
-
|
|
199
|
-
|
|
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(
|
|
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.
|
|
216
|
-
assert.deepEqual(
|
|
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(
|
|
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`,
|
package/src/routes/config.ts
CHANGED
|
@@ -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
|
-
|
|
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);
|
package/src/routes/ping.ts
CHANGED
|
@@ -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
|
|
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 !==
|
|
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;
|
package/src/routes/preview.ts
CHANGED
|
@@ -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 !==
|
|
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);
|
package/src/routes/sprites.ts
CHANGED
|
@@ -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
|
|
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)
|
|
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 = {
|
|
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);
|
package/src/routes/tile.wmts.ts
CHANGED
|
@@ -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((
|
|
99
|
-
req.log.warn({
|
|
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((
|
|
104
|
-
req.log.warn({
|
|
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 =
|
|
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);
|
package/src/routes/version.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HttpHeader, LambdaHttpResponse } from '@linzjs/lambda';
|
|
2
2
|
|
|
3
|
-
export
|
|
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
|
}
|