@basemaps/lambda-tiler 8.11.1 → 8.12.2

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 (36) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/build/__tests__/config.data.d.ts +2 -0
  3. package/build/__tests__/config.data.js +38 -2
  4. package/build/__tests__/config.data.js.map +1 -1
  5. package/build/__tests__/wmts.capability.test.js +29 -4
  6. package/build/__tests__/wmts.capability.test.js.map +1 -1
  7. package/build/routes/__tests__/tile.style.json.test.js +56 -0
  8. package/build/routes/__tests__/tile.style.json.test.js.map +1 -1
  9. package/build/routes/__tests__/wmts.test.js +6 -3
  10. package/build/routes/__tests__/wmts.test.js.map +1 -1
  11. package/build/routes/__tests__/xyz.test.js +1 -1
  12. package/build/routes/__tests__/xyz.test.js.map +1 -1
  13. package/build/routes/preview.js +4 -14
  14. package/build/routes/preview.js.map +1 -1
  15. package/build/routes/tile.style.json.js +20 -14
  16. package/build/routes/tile.style.json.js.map +1 -1
  17. package/build/routes/tile.xyz.raster.js +7 -12
  18. package/build/routes/tile.xyz.raster.js.map +1 -1
  19. package/build/util/validate.d.ts +13 -2
  20. package/build/util/validate.js +53 -35
  21. package/build/util/validate.js.map +1 -1
  22. package/build/wmts.capability.d.ts +6 -6
  23. package/build/wmts.capability.js +45 -22
  24. package/build/wmts.capability.js.map +1 -1
  25. package/package.json +7 -7
  26. package/src/__tests__/config.data.ts +40 -2
  27. package/src/__tests__/wmts.capability.test.ts +57 -4
  28. package/src/routes/__tests__/tile.style.json.test.ts +76 -0
  29. package/src/routes/__tests__/wmts.test.ts +14 -3
  30. package/src/routes/__tests__/xyz.test.ts +1 -1
  31. package/src/routes/preview.ts +4 -16
  32. package/src/routes/tile.style.json.ts +23 -14
  33. package/src/routes/tile.xyz.raster.ts +7 -11
  34. package/src/util/validate.ts +60 -34
  35. package/src/wmts.capability.ts +57 -24
  36. package/tsconfig.tsbuildinfo +1 -1
@@ -16,7 +16,7 @@ export interface TileXyz {
16
16
  /** Output tile format */
17
17
  tileType: string;
18
18
  /** Optional processing pipeline to use */
19
- pipeline?: string | null;
19
+ pipeline?: string;
20
20
  }
21
21
 
22
22
  export interface TileMatrixRequest {
@@ -118,15 +118,15 @@ export const Validate = {
118
118
  if (isNaN(x) || x < 0 || x > zoom.matrixWidth) throw new LambdaHttpResponse(404, `X not found: ${x}`);
119
119
  if (isNaN(y) || y < 0 || y > zoom.matrixHeight) throw new LambdaHttpResponse(404, `Y not found: ${y}`);
120
120
 
121
- const pipeline = req.query.get('pipeline');
122
- if (pipeline) req.set('pipeline', pipeline);
121
+ const pipeline = req.query.get('pipeline') ?? undefined;
122
+ req.set('pipeline', pipeline);
123
123
 
124
124
  const xyzData = {
125
125
  tile: { x, y, z },
126
126
  tileSet: req.params.tileSet,
127
127
  tileMatrix,
128
128
  tileType: req.params.tileType,
129
- pipeline: req.query.get('pipeline'),
129
+ pipeline,
130
130
  };
131
131
  req.set('xyz', xyzData.tile);
132
132
 
@@ -137,42 +137,68 @@ export const Validate = {
137
137
  },
138
138
 
139
139
  /**
140
- * Lookup the raster configuration pipeline for a output tile type
140
+ * Get the pipeline to use for a imagery set
141
141
  *
142
- * Defaults to standard image format output if no outputs are defined on the tileset
142
+ * @param tileSet
143
+ * @param pipeline pipeline parameter if it exists
144
+ * @returns 'rgba' for any pipeline without outputs, otherwise the provided pipeline or default output
143
145
  */
144
- pipeline(tileSet: ConfigTileSetRaster, tileType: string, pipeline?: string | null): ConfigTileSetRasterOutput | null {
145
- // If there is only one pipeline force the use of it
146
- if (tileSet.outputs?.length === 1 && pipeline == null) pipeline = tileSet.outputs[0].name;
147
-
148
- if (pipeline != null && pipeline !== 'rgba') {
149
- if (tileSet.outputs == null) throw new LambdaHttpResponse(404, 'TileSet has no pipelines');
150
- const output = tileSet.outputs.find((f) => f.name === pipeline);
151
- if (output == null) throw new LambdaHttpResponse(404, `TileSet has no pipeline named "${pipeline}"`);
152
-
153
- const validFormats = output.format ?? ['webp', 'png', 'jpeg', 'avif'];
154
- if (!validFormats.includes(tileType as ImageFormat)) {
155
- throw new LambdaHttpResponse(400, `TileSet pipeline "${pipeline}" cannot be output as ${tileType}`);
156
- }
157
- return output;
158
- }
146
+ pipelineName(tileSet: ConfigTileSetRaster, pipeline?: string | null): ConfigTileSetRasterOutput {
147
+ if (pipeline == null && tileSet.outputs) {
148
+ // If no pipeline is specified find the default pipeline
149
+ const defaultOutput = tileSet.outputs.find((f) => f.default === true);
150
+ if (defaultOutput) return defaultOutput;
159
151
 
160
- // If the tileset has multiple pipelines defined the user MUST specify which one
161
- if (tileSet.outputs) {
152
+ // If there is only one pipeline force the use of it
153
+ if (tileSet.outputs.length === 1) return tileSet.outputs[0];
154
+
155
+ // No default pipeline, and multiple pipelines exist one must be chosen
162
156
  throw new LambdaHttpResponse(404, 'TileSet needs pipeline: ' + tileSet.outputs.map((f) => f.name).join(', '));
163
157
  }
164
158
 
165
- // Generate a default RGBA configuration
166
- const img = getImageFormat(tileType ?? 'webp');
167
- if (img == null) return null;
168
- return {
169
- title: `RGBA ${tileType}`,
170
- name: 'rgba',
171
- output: {
172
- type: [img],
173
- lossless: img === 'png' ? true : false,
159
+ // No pipeline and no outputs default is RGBA
160
+ if (pipeline == null || pipeline === 'rgba') {
161
+ return {
162
+ title: `RGBA`,
163
+ name: 'rgba',
174
164
  background: tileSet.background,
175
- },
176
- } as ConfigTileSetRasterOutput;
165
+ };
166
+ }
167
+
168
+ // Pipeline defined and pipeline not found
169
+ if (tileSet.outputs == null) throw new LambdaHttpResponse(404, `TileSet has no pipeline named "${pipeline}"`);
170
+
171
+ const output = tileSet.outputs.find((f) => f.name === pipeline);
172
+ if (output == null) throw new LambdaHttpResponse(404, `TileSet has no pipeline named "${pipeline}"`);
173
+ return output;
174
+ },
175
+
176
+ /**
177
+ * Lookup the raster configuration pipeline for a output tile type
178
+ *
179
+ * Defaults to standard image format output if no outputs are defined on the tileset
180
+ */
181
+ pipeline(
182
+ tileSet: ConfigTileSetRaster,
183
+ imageFormat?: string | null,
184
+ pipelineName?: string | null,
185
+ ): { output: ConfigTileSetRasterOutput; format: ImageFormat } {
186
+ const output = Validate.pipelineName(tileSet, pipelineName);
187
+
188
+ // Failed to parse the chosen image format
189
+ const chosenFormat = getImageFormat(imageFormat);
190
+ if (imageFormat != null && chosenFormat == null) {
191
+ throw new LambdaHttpResponse(400, `TileSet pipeline "${output.name}" cannot be output as ${imageFormat}`);
192
+ }
193
+
194
+ // No requirement on image formats
195
+ if (output.format == null) return { output, format: chosenFormat ?? 'webp' };
196
+ if (chosenFormat == null) return { output, format: output.format[0] };
197
+
198
+ // Validate selected format works as expected
199
+ if (!output.format.includes(chosenFormat)) {
200
+ throw new LambdaHttpResponse(400, `TileSet pipeline "${output.name}" cannot be output as ${imageFormat}`);
201
+ }
202
+ return { output, format: chosenFormat };
177
203
  },
178
204
  };
@@ -1,4 +1,11 @@
1
- import { ConfigImagery, ConfigLayer, ConfigTileSet, standardizeLayerName } from '@basemaps/config';
1
+ import {
2
+ ConfigImagery,
3
+ ConfigLayer,
4
+ ConfigTileSet,
5
+ ConfigTileSetRasterOutput,
6
+ standardizeLayerName,
7
+ TileSetType,
8
+ } from '@basemaps/config';
2
9
  import { BoundingBox, Bounds, GoogleTms, ImageFormat, Projection, TileMatrixSet, WmtsProvider } from '@basemaps/geo';
3
10
  import { toQueryString, V, VNodeElement } from '@basemaps/shared';
4
11
  import { ImageFormatOrder } from '@basemaps/tiler';
@@ -74,7 +81,13 @@ export class WmtsBuilder {
74
81
  for (const format of formats) this.formats.push(format);
75
82
  }
76
83
 
77
- getFormats(): ImageFormat[] {
84
+ getFormats(restrictTo?: ImageFormat[]): ImageFormat[] {
85
+ if (restrictTo) {
86
+ if (this.formats.length === 0) return restrictTo;
87
+ const filtered = this.formats.filter((f) => restrictTo.includes(f));
88
+ if (filtered.length === 0) return restrictTo;
89
+ return filtered;
90
+ }
78
91
  if (this.formats.length) return this.formats;
79
92
  return ImageFormatOrder;
80
93
  }
@@ -154,17 +167,17 @@ export class WmtsBuilder {
154
167
  return V('Style', { isDefault: 'true' }, [V('ows:Title', 'Default Style'), V('ows:Identifier', 'default')]);
155
168
  }
156
169
 
157
- buildResourceUrl(tileSetId: string, suffix: string): VNodeElement {
170
+ buildResourceUrl(tileSetId: string, suffix: string, pipeline?: string): VNodeElement {
158
171
  return V('ResourceURL', {
159
172
  format: 'image/' + suffix,
160
173
  resourceType: 'tile',
161
- template: this.buildTileUrl(tileSetId, suffix),
174
+ template: this.buildTileUrl(tileSetId, suffix, pipeline),
162
175
  });
163
176
  }
164
177
 
165
- buildTileUrl(tileSetId: string, suffix: string): string {
178
+ buildTileUrl(tileSetId: string, suffix: string, pipeline?: string): string {
166
179
  // TODO this should restrict the output formats to supported formats in pipelines
167
- const query = { api: this.apiKey, config: this.config, pipeline: this.pipeline };
180
+ const query = { api: this.apiKey, config: this.config, pipeline };
168
181
 
169
182
  return [
170
183
  this.httpBase,
@@ -178,10 +191,6 @@ export class WmtsBuilder {
178
191
  ].join('/');
179
192
  }
180
193
 
181
- buildFormats(): VNodeElement[] {
182
- return this.getFormats().map((fmt) => V('Format', 'image/' + fmt));
183
- }
184
-
185
194
  buildTileMatrixLink(tileSet: ConfigTileSet): VNodeElement[] {
186
195
  const matrixSetNodes: VNodeElement[] = [];
187
196
  for (const tms of this.tileMatrixSets.values()) {
@@ -319,7 +328,7 @@ export class WmtsCapabilities extends WmtsBuilder {
319
328
  ];
320
329
  }
321
330
 
322
- toLayerVNode(tileSet: ConfigTileSet): VNodeElement {
331
+ toLayerVNode(tileSet: ConfigTileSet): VNodeElement[] {
323
332
  const matrixSets = this.getMatrixSets(tileSet);
324
333
  const matrixSetList = [...matrixSets.values()];
325
334
  const firstMatrix = matrixSetList[0];
@@ -334,19 +343,43 @@ export class WmtsCapabilities extends WmtsBuilder {
334
343
  bounds.push(Bounds.fromJson(img.bounds));
335
344
  }
336
345
 
346
+ const layers: VNodeElement[] = [];
347
+
348
+ const pipelines = this.getPipelines(tileSet, this.pipeline);
349
+
337
350
  const layerNameId = standardizeLayerName(tileSet.name);
338
- return V('Layer', [
339
- V('ows:Title', tileSet.title),
340
- V('ows:Abstract', tileSet.description ?? ''),
341
- V('ows:Identifier', layerNameId),
342
- this.buildKeywords(tileSet),
343
- ...[...matrixSets.values()].map((tms) => this.buildBoundingBoxFromImagery(tms, tileSet.layers)),
344
- this.buildWgs84BoundingBox(webMercatorOrFirst, bounds),
345
- this.buildStyle(),
346
- ...this.buildFormats(),
347
- ...this.buildTileMatrixLink(tileSet),
348
- ...this.getFormats().map((fmt) => this.buildResourceUrl(layerNameId, fmt)),
349
- ]);
351
+
352
+ for (const pipeline of pipelines) {
353
+ const formats = this.getFormats(pipeline.format);
354
+ const layerId = pipeline.default ? layerNameId : `${layerNameId}_${pipeline.name}`;
355
+ const layerTitle = pipeline.default ? tileSet.title : `${tileSet.title} ${pipeline.title}`;
356
+
357
+ const layer = V('Layer', [
358
+ V('ows:Title', layerTitle),
359
+ V('ows:Abstract', tileSet.description ?? ''),
360
+ V('ows:Identifier', layerId),
361
+ this.buildKeywords(tileSet),
362
+ ...[...matrixSets.values()].map((tms) => this.buildBoundingBoxFromImagery(tms, tileSet.layers)),
363
+ this.buildWgs84BoundingBox(webMercatorOrFirst, bounds),
364
+ this.buildStyle(),
365
+ ...formats.map((fmt) => V('Format', 'image/' + fmt)),
366
+ ...this.buildTileMatrixLink(tileSet),
367
+ ...formats.map((fmt) => this.buildResourceUrl(layerNameId, fmt, pipeline.default ? undefined : pipeline.name)),
368
+ ]);
369
+ layers.push(layer);
370
+ }
371
+
372
+ return layers;
373
+ }
374
+
375
+ getPipelines(tileSet: ConfigTileSet, pipeline?: string): ConfigTileSetRasterOutput[] {
376
+ if (tileSet.type !== TileSetType.Raster) return [];
377
+
378
+ if (tileSet.outputs == null) return [{ name: 'rgba', title: 'RGBA', default: true }];
379
+
380
+ if (pipeline) return tileSet.outputs.filter((f) => f.name === pipeline);
381
+
382
+ return tileSet.outputs;
350
383
  }
351
384
 
352
385
  toAllImageryLayersVNode(configLayers?: ConfigLayer[]): VNodeElement[] {
@@ -393,7 +426,7 @@ export class WmtsCapabilities extends WmtsBuilder {
393
426
 
394
427
  // Build TileSet Layer VNodes
395
428
  const layers: VNodeElement[] = [];
396
- layers.push(this.toLayerVNode(this.tileSet));
429
+ layers.push(...this.toLayerVNode(this.tileSet));
397
430
  const contents = layers.concat(this.toAllImageryLayersVNode(this.configLayers));
398
431
 
399
432
  // Build TileMatrix Sets vNodes