@basemaps/lambda-tiler 6.39.0 → 6.40.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 (112) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/build/__tests__/config.data.d.ts.map +1 -1
  3. package/build/__tests__/config.data.js +142 -1
  4. package/build/__tests__/config.data.js.map +1 -1
  5. package/build/__tests__/wmts.capability.test.js +9 -16
  6. package/build/__tests__/wmts.capability.test.js.map +1 -1
  7. package/build/__tests__/xyz.util.d.ts.map +1 -1
  8. package/build/__tests__/xyz.util.js +6 -2
  9. package/build/__tests__/xyz.util.js.map +1 -1
  10. package/build/cli/render.tile.d.ts +2 -0
  11. package/build/cli/render.tile.d.ts.map +1 -0
  12. package/build/cli/render.tile.js +33 -0
  13. package/build/cli/render.tile.js.map +1 -0
  14. package/build/routes/__tests__/attribution.test.js +63 -3
  15. package/build/routes/__tests__/attribution.test.js.map +1 -1
  16. package/build/routes/__tests__/wmts.test.js +50 -8
  17. package/build/routes/__tests__/wmts.test.js.map +1 -1
  18. package/build/routes/attribution.d.ts +12 -0
  19. package/build/routes/attribution.d.ts.map +1 -1
  20. package/build/routes/attribution.js +29 -27
  21. package/build/routes/attribution.js.map +1 -1
  22. package/build/routes/tile.json.d.ts.map +1 -1
  23. package/build/routes/tile.json.js +2 -1
  24. package/build/routes/tile.json.js.map +1 -1
  25. package/build/routes/tile.style.json.d.ts.map +1 -1
  26. package/build/routes/tile.style.json.js +2 -1
  27. package/build/routes/tile.style.json.js.map +1 -1
  28. package/build/routes/tile.wmts.d.ts.map +1 -1
  29. package/build/routes/tile.wmts.js +3 -1
  30. package/build/routes/tile.wmts.js.map +1 -1
  31. package/build/routes/tile.xyz.raster.d.ts.map +1 -1
  32. package/build/routes/tile.xyz.raster.js +5 -1
  33. package/build/routes/tile.xyz.raster.js.map +1 -1
  34. package/build/util/__test__/filter.test.d.ts +2 -0
  35. package/build/util/__test__/filter.test.d.ts.map +1 -0
  36. package/build/util/__test__/filter.test.js +64 -0
  37. package/build/util/__test__/filter.test.js.map +1 -0
  38. package/build/util/config.loader.d.ts.map +1 -1
  39. package/build/util/config.loader.js +2 -3
  40. package/build/util/config.loader.js.map +1 -1
  41. package/build/util/filter.d.ts +15 -0
  42. package/build/util/filter.d.ts.map +1 -0
  43. package/build/util/filter.js +59 -0
  44. package/build/util/filter.js.map +1 -0
  45. package/build/wmts.capability.d.ts +8 -5
  46. package/build/wmts.capability.d.ts.map +1 -1
  47. package/build/wmts.capability.js +17 -15
  48. package/build/wmts.capability.js.map +1 -1
  49. package/dist/index.js +58 -58
  50. package/dist/node_modules/.package-lock.json +11 -11
  51. package/dist/node_modules/minimist/.eslintrc +25 -50
  52. package/dist/node_modules/minimist/CHANGELOG.md +87 -1
  53. package/dist/node_modules/minimist/README.md +14 -10
  54. package/dist/node_modules/minimist/example/parse.js +2 -0
  55. package/dist/node_modules/minimist/index.js +256 -242
  56. package/dist/node_modules/minimist/package.json +73 -73
  57. package/dist/node_modules/minimist/test/all_bool.js +26 -24
  58. package/dist/node_modules/minimist/test/bool.js +146 -147
  59. package/dist/node_modules/minimist/test/dash.js +33 -21
  60. package/dist/node_modules/minimist/test/default_bool.js +26 -24
  61. package/dist/node_modules/minimist/test/dotted.js +13 -11
  62. package/dist/node_modules/minimist/test/kv_short.js +26 -10
  63. package/dist/node_modules/minimist/test/long.js +28 -26
  64. package/dist/node_modules/minimist/test/num.js +30 -28
  65. package/dist/node_modules/minimist/test/parse.js +169 -157
  66. package/dist/node_modules/minimist/test/parse_modified.js +7 -5
  67. package/dist/node_modules/minimist/test/proto.js +41 -37
  68. package/dist/node_modules/minimist/test/short.js +57 -55
  69. package/dist/node_modules/minimist/test/stop_early.js +10 -8
  70. package/dist/node_modules/minimist/test/unknown.js +83 -81
  71. package/dist/node_modules/minimist/test/whitespace.js +6 -4
  72. package/dist/node_modules/node-abi/.circleci/config.yml +2 -2
  73. package/dist/node_modules/node-abi/.github/CODEOWNERS +1 -0
  74. package/dist/node_modules/node-abi/.github/workflows/update-abi.yml +5 -4
  75. package/dist/node_modules/node-abi/README.md +5 -3
  76. package/dist/node_modules/node-abi/abi_registry.json +8 -1
  77. package/dist/node_modules/node-abi/package.json +4 -4
  78. package/dist/node_modules/readable-stream/README.md +1 -1
  79. package/dist/node_modules/readable-stream/lib/_stream_duplex.js +12 -25
  80. package/dist/node_modules/readable-stream/lib/_stream_passthrough.js +2 -4
  81. package/dist/node_modules/readable-stream/lib/_stream_readable.js +176 -273
  82. package/dist/node_modules/readable-stream/lib/_stream_transform.js +26 -37
  83. package/dist/node_modules/readable-stream/lib/_stream_writable.js +118 -174
  84. package/dist/node_modules/readable-stream/lib/internal/streams/async_iterator.js +10 -37
  85. package/dist/node_modules/readable-stream/lib/internal/streams/buffer_list.js +20 -47
  86. package/dist/node_modules/readable-stream/lib/internal/streams/destroy.js +8 -17
  87. package/dist/node_modules/readable-stream/lib/internal/streams/end-of-stream.js +1 -19
  88. package/dist/node_modules/readable-stream/lib/internal/streams/from.js +12 -24
  89. package/dist/node_modules/readable-stream/lib/internal/streams/pipeline.js +5 -16
  90. package/dist/node_modules/readable-stream/lib/internal/streams/state.js +2 -7
  91. package/dist/node_modules/readable-stream/package.json +1 -1
  92. package/dist/package-lock.json +12 -343
  93. package/dist/package.json +1 -1
  94. package/package.json +8 -8
  95. package/src/__tests__/config.data.ts +142 -1
  96. package/src/__tests__/wmts.capability.test.ts +9 -16
  97. package/src/__tests__/xyz.util.ts +6 -2
  98. package/src/cli/render.tile.ts +38 -0
  99. package/src/routes/__tests__/attribution.test.ts +65 -3
  100. package/src/routes/__tests__/wmts.test.ts +70 -9
  101. package/src/routes/attribution.ts +24 -28
  102. package/src/routes/tile.json.ts +2 -1
  103. package/src/routes/tile.style.json.ts +2 -1
  104. package/src/routes/tile.wmts.ts +3 -1
  105. package/src/routes/tile.xyz.raster.ts +4 -1
  106. package/src/util/__test__/filter.test.ts +80 -0
  107. package/src/util/config.loader.ts +1 -2
  108. package/src/util/filter.ts +60 -0
  109. package/src/wmts.capability.ts +19 -14
  110. package/tsconfig.tsbuildinfo +1 -1
  111. package/test-dump.js +0 -6
  112. package/test-imagery.js +0 -3
@@ -8,6 +8,7 @@ import { Validate } from '../util/validate.js';
8
8
  import { WmtsCapabilities } from '../wmts.capability.js';
9
9
  import { Etag } from '../util/etag.js';
10
10
  import { ConfigLoader } from '../util/config.loader.js';
11
+ import { filterLayers, getFilters } from '../util/filter.js';
11
12
 
12
13
  export interface WmtsCapabilitiesGet {
13
14
  Params: {
@@ -59,11 +60,12 @@ export async function wmtsCapabilitiesGet(req: LambdaHttpRequest<WmtsCapabilitie
59
60
  provider: provider ?? undefined,
60
61
  tileSet,
61
62
  tileMatrix,
62
- isIndividualLayers: req.params.tileMatrix == null,
63
63
  imagery,
64
64
  apiKey,
65
65
  config: ConfigLoader.extract(req),
66
66
  formats: Validate.getRequestedFormats(req),
67
+ layers: req.params.tileMatrix == null ? filterLayers(req, tileSet.layers) : null,
68
+ filters: getFilters(req),
67
69
  }).toXml();
68
70
  if (xml == null) return NotFound();
69
71
 
@@ -9,6 +9,7 @@ import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambd
9
9
  import pLimit from 'p-limit';
10
10
  import { ConfigLoader } from '../util/config.loader.js';
11
11
  import { Etag } from '../util/etag.js';
12
+ import { filterLayers } from '../util/filter.js';
12
13
  import { NotFound, NotModified } from '../util/response.js';
13
14
  import { CoSources } from '../util/source.cache.js';
14
15
  import { TileXyz } from '../util/validate.js';
@@ -32,13 +33,15 @@ export const TileXyzRaster = {
32
33
  async getAssetsForTile(req: LambdaHttpRequest, tileSet: ConfigTileSetRaster, xyz: TileXyz): Promise<string[]> {
33
34
  const config = await ConfigLoader.load(req);
34
35
  const imagery = await getAllImagery(config, tileSet.layers, [xyz.tileMatrix.projection]);
36
+ const filteredLayers = filterLayers(req, tileSet.layers);
35
37
 
36
38
  const output: string[] = [];
37
39
  const tileBounds = xyz.tileMatrix.tileToSourceBounds(xyz.tile);
38
40
 
39
41
  // All zoom level config is stored as Google zoom levels
40
42
  const filterZoom = TileMatrixSet.convertZoomLevel(xyz.tile.z, xyz.tileMatrix, TileMatrixSets.get(Epsg.Google));
41
- for (const layer of tileSet.layers) {
43
+ for (const layer of filteredLayers) {
44
+ if (layer.disabled) continue;
42
45
  if (layer.maxZoom != null && filterZoom > layer.maxZoom) continue;
43
46
  if (layer.minZoom != null && filterZoom < layer.minZoom) continue;
44
47
 
@@ -0,0 +1,80 @@
1
+ import { ConfigLayer } from '@basemaps/config';
2
+ import o from 'ospec';
3
+ import { mockUrlRequest } from '../../__tests__/xyz.util.js';
4
+ import { filterLayers } from '../filter.js';
5
+
6
+ o.spec('filterLayers', () => {
7
+ const sourceLayers: ConfigLayer[] = [
8
+ {
9
+ name: 'waikato-0_625m-snc12836-2004',
10
+ title: 'Waikato 0.625m SNC12836 (2004-2008)',
11
+ },
12
+ {
13
+ name: 'hawkes-bay--manawat-whanganui-0_75m-snc30001-2002',
14
+ title: 'Hawkes Bay / Manawatū-Whanganui 0.75m SNC30001 (2002)',
15
+ },
16
+ {
17
+ name: 'canterbury-0_75m-snc25054-2000-2001',
18
+ title: 'Canterbury 0.75m SNC25054 (2000-2001)',
19
+ },
20
+ {
21
+ name: 'otago-0_375m-sn3806-1975',
22
+ title: 'Otago 0.375m SN3806 (1975)',
23
+ },
24
+ ];
25
+
26
+ o('should not filter with empty parameters', () => {
27
+ const layers = filterLayers(mockUrlRequest('/foo/bar,js'), sourceLayers);
28
+ o(layers).deepEquals(sourceLayers);
29
+ });
30
+
31
+ o('should filter date[after]', () => {
32
+ const dateAfter = '2003-12-31T23:59:59.999';
33
+ const layers = filterLayers(mockUrlRequest('/foo/bar,js', `?date[after]=${dateAfter}`), sourceLayers);
34
+ o(layers).deepEquals([sourceLayers[0]]);
35
+ });
36
+
37
+ o('should filter date[before]', () => {
38
+ const dateBefore = '2003-01-01T00:00:00.000Z';
39
+ const layers = filterLayers(mockUrlRequest('/foo/bar,js', `?date[before]=${dateBefore}`), sourceLayers);
40
+ o(layers).deepEquals(sourceLayers.slice(1));
41
+ });
42
+
43
+ o('should filter date[before] in between years', () => {
44
+ const dateBefore = '2026-01-01T00:00:00.000Z';
45
+ const layer = [{ name: '', title: 'Waikato 0.625m SNC12836 (2020-2028)' }];
46
+ const layers = filterLayers(mockUrlRequest('/foo/bar,js', `?date[before]=${dateBefore}`), layer);
47
+ o(layers).deepEquals(layer);
48
+ });
49
+
50
+ o('should filter date[after] in between years', () => {
51
+ const dateAfter = '2026-12-31T23:59:59.999Z';
52
+ const layer = [{ name: '', title: 'Waikato 0.625m SNC12836 (2020-2028)' }];
53
+ const layers = filterLayers(mockUrlRequest('/foo/bar,js', `?date[after]=${dateAfter}`), layer);
54
+ o(layers).deepEquals(layer);
55
+ });
56
+
57
+ o('should filter date[after] and date[before] in between years', () => {
58
+ const dateAfter = '2026-12-31T23:59:59.999Z';
59
+ const dateBefore = '2028-01-01T00:00:00.000Z';
60
+
61
+ const layer = [{ name: '', title: 'Waikato 0.625m SNC12836 (2020-2028)' }];
62
+ const layers = filterLayers(
63
+ mockUrlRequest('/foo/bar,js', `?date[after]=${dateAfter}&date[before]=${dateBefore}`),
64
+ layer,
65
+ );
66
+ o(layers).deepEquals(layer);
67
+ });
68
+
69
+ o('should filter date[after] and date[before] in between years with single year', () => {
70
+ const dateAfter = '2026-12-31T23:59:59.999Z';
71
+ const dateBefore = '2028-01-01T00:00:00.000Z';
72
+
73
+ const layer = [{ name: '', title: 'Waikato 0.625m SNC12836 (2028)' }];
74
+ const layers = filterLayers(
75
+ mockUrlRequest('/foo/bar,js', `?date[after]=${dateAfter}&date[before]=${dateBefore}`),
76
+ layer,
77
+ );
78
+ o(layers).deepEquals(layer);
79
+ });
80
+ });
@@ -15,8 +15,7 @@ export class ConfigLoader {
15
15
  const config = getDefaultConfig();
16
16
  if (config.assets == null) {
17
17
  const cb = await config.ConfigBundle.get(config.ConfigBundle.id('latest'));
18
- if (cb == null) throw new LambdaHttpResponse(400, 'Unable to get lastest config bundle for asset.');
19
- config.assets = cb.assets;
18
+ if (cb) config.assets = cb.assets;
20
19
  }
21
20
  return config;
22
21
  }
@@ -0,0 +1,60 @@
1
+ import { ConfigLayer } from '@basemaps/config';
2
+ import { extractYearRangeFromTitle } from '@basemaps/shared';
3
+ import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
+
5
+ export const FilterNames = {
6
+ DateBefore: 'date[before]',
7
+ DateAfter: 'date[after]',
8
+ };
9
+
10
+ export function getFilters(req: LambdaHttpRequest): Record<string, string | undefined> {
11
+ return {
12
+ [FilterNames.DateBefore]: req.query.get(FilterNames.DateBefore) ?? undefined,
13
+ [FilterNames.DateAfter]: req.query.get(FilterNames.DateAfter) ?? undefined,
14
+ };
15
+ }
16
+ /**
17
+ * Convert the year range into full ISO date year range
18
+ *
19
+ * Expand to the full year of jan 1st 00:00 -> Dec 31st 23:59
20
+ */
21
+ export function yearRangeToInterval(x: [number] | [number, number]): [Date, Date] {
22
+ if (x.length === 1) return [new Date(`${x[0]}-01-01T00:00:00.000Z`), new Date(`${x[0]}-12-31T23:59:59.999Z`)];
23
+ return [new Date(`${x[0]}-01-01T00:00:00.000Z`), new Date(`${x[1]}-12-31T23:59:59.999Z`)];
24
+ }
25
+
26
+ function parseDateAsIso(s: string | null): Date | null {
27
+ if (s == null) return null;
28
+ const date = new Date(s);
29
+ if (isNaN(date.getTime())) throw new LambdaHttpResponse(400, `Invalid date format: "${s}"`);
30
+ return date;
31
+ }
32
+
33
+ export function filterLayers(req: LambdaHttpRequest, layers: ConfigLayer[]): ConfigLayer[] {
34
+ const dateAfterQuery = req.query.get(FilterNames.DateAfter);
35
+ const dateBeforeQuery = req.query.get(FilterNames.DateBefore);
36
+
37
+ if (dateAfterQuery == null && dateBeforeQuery == null) return layers;
38
+ const dateAfter = parseDateAsIso(dateAfterQuery);
39
+ const dateBefore = parseDateAsIso(dateBeforeQuery);
40
+
41
+ const filtered = layers.filter((l) => {
42
+ if (l.title == null) return false;
43
+ const yearRange = extractYearRangeFromTitle(l.title);
44
+ if (yearRange == null) return false;
45
+
46
+ const ranges = yearRangeToInterval(yearRange);
47
+ const startYear = ranges[0];
48
+ const endYear = ranges[1];
49
+ return (dateAfter == null || endYear >= dateAfter) && (dateBefore == null || startYear <= dateBefore);
50
+ });
51
+
52
+ // Trace that layers have been filtered
53
+ req.set('layerFilter', {
54
+ [FilterNames.DateBefore]: dateBefore?.toISOString(),
55
+ [FilterNames.DateAfter]: dateAfter?.toISOString(),
56
+ layerCount: layers.length,
57
+ filterCount: filtered.length,
58
+ });
59
+ return filtered;
60
+ }
@@ -28,8 +28,6 @@ export interface WmtsCapabilitiesParams {
28
28
  tileSet: ConfigTileSet;
29
29
  /** List of tile matrixes to output */
30
30
  tileMatrix: TileMatrixSet[];
31
- /** Should WMTS Layers be created for each imagery set inside this tileSet */
32
- isIndividualLayers: boolean;
33
31
  /** All the imagery used by the tileSet and tileMatrixes */
34
32
  imagery: Map<string, ConfigImagery>;
35
33
  /** API key to append to all resource urls */
@@ -38,6 +36,10 @@ export interface WmtsCapabilitiesParams {
38
36
  formats?: ImageFormat[] | null;
39
37
  /** Config location */
40
38
  config?: string | null;
39
+ /** Specific layers to add to the WMTS */
40
+ layers?: ConfigLayer[] | null;
41
+ /** Specific DateRange filter for the wmts layers */
42
+ filters?: Record<string, string | undefined>;
41
43
  }
42
44
 
43
45
  /** Number of decimal places to use in lat lng */
@@ -62,21 +64,23 @@ export class WmtsCapabilities {
62
64
  tileMatrixSets = new Map<string, TileMatrixSet>();
63
65
  imagery: Map<string, ConfigImagery>;
64
66
  formats: ImageFormat[];
65
- isIndividualLayers = false;
67
+ filters?: Record<string, string | undefined>;
66
68
 
67
69
  minZoom = 0;
68
70
  maxZoom = 32;
71
+ layers: ConfigLayer[] | null | undefined;
69
72
 
70
73
  constructor(params: WmtsCapabilitiesParams) {
71
74
  this.httpBase = params.httpBase;
72
75
  this.provider = params.provider;
73
76
  this.tileSet = params.tileSet;
74
77
  this.config = params.config;
75
- this.isIndividualLayers = params.isIndividualLayers;
76
78
  for (const tms of params.tileMatrix) this.tileMatrixSets.set(tms.identifier, tms);
77
79
  this.apiKey = params.apiKey;
78
80
  this.formats = params.formats ?? ImageFormatOrder;
79
81
  this.imagery = params.imagery;
82
+ this.layers = params.layers;
83
+ this.filters = params.filters;
80
84
  }
81
85
 
82
86
  buildWgs84BoundingBox(tms: TileMatrixSet, layers: Bounds[]): VNodeElement {
@@ -159,8 +163,9 @@ export class WmtsCapabilities {
159
163
  ];
160
164
  }
161
165
 
162
- buildTileUrl(tileSetId: string, suffix: string): string {
163
- const query = toQueryString({ api: this.apiKey, config: this.config });
166
+ buildTileUrl(tileSetId: string, suffix: string, addFilter = false): string {
167
+ let query = { api: this.apiKey, config: this.config };
168
+ if (addFilter) query = { api: this.apiKey, config: this.config, ...this.filters };
164
169
 
165
170
  return [
166
171
  this.httpBase,
@@ -170,15 +175,15 @@ export class WmtsCapabilities {
170
175
  '{TileMatrixSet}',
171
176
  '{TileMatrix}',
172
177
  '{TileCol}',
173
- `{TileRow}.${suffix}${query}`,
178
+ `{TileRow}.${suffix}${toQueryString(query)}`,
174
179
  ].join('/');
175
180
  }
176
181
 
177
- buildResourceUrl(tileSetId: string, suffix: string): VNodeElement {
182
+ buildResourceUrl(tileSetId: string, suffix: string, addFilter = false): VNodeElement {
178
183
  return V('ResourceURL', {
179
184
  format: 'image/' + suffix,
180
185
  resourceType: 'tile',
181
- template: this.buildTileUrl(tileSetId, suffix),
186
+ template: this.buildTileUrl(tileSetId, suffix, addFilter),
182
187
  });
183
188
  }
184
189
 
@@ -202,7 +207,7 @@ export class WmtsCapabilities {
202
207
  if (firstImg == null) return null;
203
208
 
204
209
  return V('Layer', [
205
- V('ows:Title', layer.title ?? layerNameId),
210
+ V('ows:Title', layer.title),
206
211
  V('ows:Abstract', ''),
207
212
  V('ows:Identifier', layerNameId),
208
213
  this.buildKeywords(firstImg),
@@ -241,7 +246,7 @@ export class WmtsCapabilities {
241
246
  }
242
247
 
243
248
  return V('Layer', [
244
- V('ows:Title', layer.title ?? layerNameId),
249
+ V('ows:Title', layer.title),
245
250
  V('ows:Abstract', layer.description ?? ''),
246
251
  V('ows:Identifier', layerNameId),
247
252
  this.buildKeywords(layer),
@@ -250,7 +255,7 @@ export class WmtsCapabilities {
250
255
  this.buildStyle(),
251
256
  ...this.formats.map((fmt) => V('Format', 'image/' + fmt)),
252
257
  ...matrixSetNodes,
253
- ...this.formats.map((fmt) => this.buildResourceUrl(layerNameId, fmt)),
258
+ ...this.formats.map((fmt) => this.buildResourceUrl(layerNameId, fmt, true)),
254
259
  ]);
255
260
  }
256
261
 
@@ -287,10 +292,10 @@ export class WmtsCapabilities {
287
292
  const layers: (VNodeElement | null)[] = [];
288
293
  layers.push(this.buildLayer(this.tileSet));
289
294
 
290
- if (this.isIndividualLayers) {
295
+ if (this.layers) {
291
296
  const layerByName = new Map<string, ConfigLayer>();
292
297
  // Dedupe the layers by unique name
293
- for (const img of this.tileSet.layers) layerByName.set(standardizeLayerName(img.name), img);
298
+ for (const img of this.layers) layerByName.set(standardizeLayerName(img.name), img);
294
299
  const orderedLayers = Array.from(layerByName.values()).sort((a, b) =>
295
300
  (a.title ?? a.name).localeCompare(b.title ?? b.name),
296
301
  );