@basemaps/lambda-tiler 6.39.0 → 6.41.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 (140) hide show
  1. package/CHANGELOG.md +51 -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 +105 -79
  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 +36 -0
  13. package/build/cli/render.tile.js.map +1 -0
  14. package/build/routes/__tests__/attribution.test.js +62 -2
  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 +11 -0
  19. package/build/routes/attribution.d.ts.map +1 -1
  20. package/build/routes/attribution.js +32 -28
  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 +12 -7
  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/util/validate.d.ts.map +1 -1
  46. package/build/util/validate.js +4 -3
  47. package/build/util/validate.js.map +1 -1
  48. package/build/wmts.capability.d.ts +61 -28
  49. package/build/wmts.capability.d.ts.map +1 -1
  50. package/build/wmts.capability.js +175 -99
  51. package/build/wmts.capability.js.map +1 -1
  52. package/dist/index.js +110 -82
  53. package/dist/node_modules/.package-lock.json +17 -17
  54. package/dist/node_modules/detect-libc/README.md +4 -1
  55. package/dist/node_modules/detect-libc/index.d.ts +3 -0
  56. package/dist/node_modules/detect-libc/lib/detect-libc.js +105 -4
  57. package/dist/node_modules/detect-libc/lib/filesystem.js +41 -0
  58. package/dist/node_modules/detect-libc/lib/process.js +3 -0
  59. package/dist/node_modules/detect-libc/package.json +7 -3
  60. package/dist/node_modules/minimist/.eslintrc +25 -50
  61. package/dist/node_modules/minimist/CHANGELOG.md +87 -1
  62. package/dist/node_modules/minimist/README.md +14 -10
  63. package/dist/node_modules/minimist/example/parse.js +2 -0
  64. package/dist/node_modules/minimist/index.js +256 -242
  65. package/dist/node_modules/minimist/package.json +73 -73
  66. package/dist/node_modules/minimist/test/all_bool.js +26 -24
  67. package/dist/node_modules/minimist/test/bool.js +146 -147
  68. package/dist/node_modules/minimist/test/dash.js +33 -21
  69. package/dist/node_modules/minimist/test/default_bool.js +26 -24
  70. package/dist/node_modules/minimist/test/dotted.js +13 -11
  71. package/dist/node_modules/minimist/test/kv_short.js +26 -10
  72. package/dist/node_modules/minimist/test/long.js +28 -26
  73. package/dist/node_modules/minimist/test/num.js +30 -28
  74. package/dist/node_modules/minimist/test/parse.js +169 -157
  75. package/dist/node_modules/minimist/test/parse_modified.js +7 -5
  76. package/dist/node_modules/minimist/test/proto.js +41 -37
  77. package/dist/node_modules/minimist/test/short.js +57 -55
  78. package/dist/node_modules/minimist/test/stop_early.js +10 -8
  79. package/dist/node_modules/minimist/test/unknown.js +83 -81
  80. package/dist/node_modules/minimist/test/whitespace.js +6 -4
  81. package/dist/node_modules/node-abi/.circleci/config.yml +2 -2
  82. package/dist/node_modules/node-abi/.github/CODEOWNERS +1 -0
  83. package/dist/node_modules/node-abi/.github/workflows/semantic.yml +26 -0
  84. package/dist/node_modules/node-abi/.github/workflows/update-abi.yml +5 -4
  85. package/dist/node_modules/node-abi/README.md +5 -3
  86. package/dist/node_modules/node-abi/abi_registry.json +32 -1
  87. package/dist/node_modules/node-abi/package.json +4 -4
  88. package/dist/node_modules/node-abi/scripts/update-abi-registry.js +2 -2
  89. package/dist/node_modules/readable-stream/README.md +1 -1
  90. package/dist/node_modules/readable-stream/lib/_stream_duplex.js +12 -25
  91. package/dist/node_modules/readable-stream/lib/_stream_passthrough.js +2 -4
  92. package/dist/node_modules/readable-stream/lib/_stream_readable.js +176 -273
  93. package/dist/node_modules/readable-stream/lib/_stream_transform.js +26 -37
  94. package/dist/node_modules/readable-stream/lib/_stream_writable.js +118 -174
  95. package/dist/node_modules/readable-stream/lib/internal/streams/async_iterator.js +10 -37
  96. package/dist/node_modules/readable-stream/lib/internal/streams/buffer_list.js +20 -47
  97. package/dist/node_modules/readable-stream/lib/internal/streams/destroy.js +8 -17
  98. package/dist/node_modules/readable-stream/lib/internal/streams/end-of-stream.js +1 -19
  99. package/dist/node_modules/readable-stream/lib/internal/streams/from.js +12 -24
  100. package/dist/node_modules/readable-stream/lib/internal/streams/pipeline.js +5 -16
  101. package/dist/node_modules/readable-stream/lib/internal/streams/state.js +2 -7
  102. package/dist/node_modules/readable-stream/package.json +1 -1
  103. package/dist/node_modules/semver/README.md +70 -1
  104. package/dist/node_modules/semver/bin/semver.js +16 -2
  105. package/dist/node_modules/semver/classes/comparator.js +39 -34
  106. package/dist/node_modules/semver/classes/range.js +45 -28
  107. package/dist/node_modules/semver/classes/semver.js +32 -17
  108. package/dist/node_modules/semver/functions/coerce.js +1 -1
  109. package/dist/node_modules/semver/functions/diff.js +58 -16
  110. package/dist/node_modules/semver/functions/inc.js +3 -2
  111. package/dist/node_modules/semver/functions/parse.js +5 -22
  112. package/dist/node_modules/semver/index.js +1 -0
  113. package/dist/node_modules/semver/internal/constants.js +20 -2
  114. package/dist/node_modules/semver/internal/parse-options.js +14 -10
  115. package/dist/node_modules/semver/internal/re.js +34 -4
  116. package/dist/node_modules/semver/package.json +8 -7
  117. package/dist/node_modules/semver/ranges/intersects.js +1 -1
  118. package/dist/node_modules/semver/ranges/subset.js +6 -3
  119. package/dist/package-lock.json +18 -349
  120. package/dist/package.json +1 -2
  121. package/package.json +9 -10
  122. package/src/__tests__/config.data.ts +142 -1
  123. package/src/__tests__/wmts.capability.test.ts +117 -79
  124. package/src/__tests__/xyz.util.ts +6 -2
  125. package/src/cli/render.tile.ts +41 -0
  126. package/src/routes/__tests__/attribution.test.ts +64 -2
  127. package/src/routes/__tests__/wmts.test.ts +70 -9
  128. package/src/routes/attribution.ts +28 -28
  129. package/src/routes/tile.json.ts +2 -1
  130. package/src/routes/tile.style.json.ts +2 -1
  131. package/src/routes/tile.wmts.ts +13 -6
  132. package/src/routes/tile.xyz.raster.ts +4 -1
  133. package/src/util/__test__/filter.test.ts +80 -0
  134. package/src/util/config.loader.ts +1 -2
  135. package/src/util/filter.ts +60 -0
  136. package/src/util/validate.ts +4 -3
  137. package/src/wmts.capability.ts +216 -123
  138. package/tsconfig.tsbuildinfo +1 -1
  139. package/test-dump.js +0 -6
  140. package/test-imagery.js +0 -3
@@ -6,17 +6,19 @@ import {
6
6
  Bounds,
7
7
  GoogleTms,
8
8
  NamedBounds,
9
+ Projection,
9
10
  Stac,
10
11
  StacExtent,
11
12
  StacProvider,
12
13
  TileMatrixSet,
13
14
  } from '@basemaps/geo';
14
- import { extractYearRangeFromName, Projection, titleizeImageryName } from '@basemaps/shared';
15
15
  import { BBox, MultiPolygon, multiPolygonToWgs84, Pair, union, Wgs84 } from '@linzjs/geojson';
16
+ import { extractYearRangeFromName, extractYearRangeFromTitle } from '@basemaps/shared';
16
17
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
17
18
  import { ConfigLoader } from '../util/config.loader.js';
18
19
 
19
20
  import { Etag } from '../util/etag.js';
21
+ import { filterLayers, yearRangeToInterval } from '../util/filter.js';
20
22
  import { NotFound, NotModified } from '../util/response.js';
21
23
  import { Validate } from '../util/validate.js';
22
24
 
@@ -44,23 +46,19 @@ function roundPair(p: Pair): Pair {
44
46
  * @param files in target projection
45
47
  * @return MultiPolygon in WGS84
46
48
  */
47
- function createCoordinates(bbox: BBox, files: NamedBounds[], proj: Projection): MultiPolygon {
49
+ export function createCoordinates(bbox: BBox, files: NamedBounds[], proj: Projection): MultiPolygon {
48
50
  if (Wgs84.delta(bbox[0], bbox[2]) <= 0) {
49
51
  // This bounds spans more than half the globe which multiPolygonToWgs84 can't handle; just
50
52
  // return bbox as polygon
51
53
  return Wgs84.bboxToMultiPolygon(bbox);
52
54
  }
53
55
 
54
- let coordinates: MultiPolygon = [];
55
-
56
+ const polygons: MultiPolygon = [];
56
57
  // merge imagery bounds
57
- for (const image of files) {
58
- const poly = [Bounds.fromJson(image).pad(SmoothPadding).toPolygon()] as MultiPolygon;
59
- coordinates = union(coordinates, poly);
60
- }
58
+ for (const image of files) polygons.push(Bounds.fromJson(image).pad(SmoothPadding).toPolygon());
59
+ const coordinates = union(polygons);
61
60
 
62
61
  const roundToWgs84 = (p: number[]): number[] => roundPair(proj.toWgs84(p) as Pair);
63
-
64
62
  return multiPolygonToWgs84(coordinates, roundToWgs84);
65
63
  }
66
64
 
@@ -86,30 +84,28 @@ async function tileSetAttribution(
86
84
 
87
85
  const config = await ConfigLoader.load(req);
88
86
  const imagery = await getAllImagery(config, tileSet.layers, [tileMatrix.projection]);
87
+ const filteredLayers = filterLayers(req, tileSet.layers);
89
88
 
90
89
  const host = await config.Provider.get(config.Provider.id('linz'));
91
90
 
92
- for (const layer of tileSet.layers) {
91
+ for (const layer of filteredLayers) {
93
92
  const imgId = layer[proj.epsg.code];
94
93
  if (imgId == null) continue;
95
94
  const im = imagery.get(imgId);
96
95
  if (im == null) continue;
96
+ const title = im.title;
97
+ const years = extractYearRangeFromTitle(im.title) ?? extractYearRangeFromName(im.name);
98
+ if (years == null) continue;
99
+ const interval = yearRangeToInterval(years);
97
100
 
98
101
  const bbox = proj.boundsToWgs84BoundingBox(im.bounds).map(roundNumber) as BBox;
99
102
 
100
- const years = extractYearRangeFromName(im.name);
101
- if (years[0] === -1) {
102
- req.log.debug({ imagery: im.name }, 'Attribution:DefaultYear');
103
- // Put it in the future so people know its a "fake" date
104
- years[0] = new Date().getUTCFullYear() + 1;
105
- years[1] = years[0] + 1;
106
- }
107
-
108
- const interval = [years.map((y) => `${y}-01-01T00:00:00Z`) as [string, string]];
103
+ const extent: StacExtent = {
104
+ spatial: { bbox: [bbox] },
105
+ temporal: { interval: [[interval[0].toISOString(), interval[1].toISOString()]] },
106
+ };
109
107
 
110
- const extent: StacExtent = { spatial: { bbox: [bbox] }, temporal: { interval } };
111
-
112
- items.push({
108
+ const item: AttributionItem = {
113
109
  type: 'Feature',
114
110
  stac_version: Stac.Version,
115
111
  id: imgId + '_item',
@@ -119,22 +115,25 @@ async function tileSetAttribution(
119
115
  bbox,
120
116
  geometry: { type: 'MultiPolygon', coordinates: createCoordinates(bbox, im.files, proj) },
121
117
  properties: {
122
- title: im.title ?? titleizeImageryName(im.name),
118
+ title,
123
119
  category: im.category,
124
120
  datetime: null,
125
- start_datetime: interval[0][0],
126
- end_datetime: interval[0][1],
121
+ start_datetime: interval[0].toISOString(),
122
+ end_datetime: interval[1].toISOString(),
127
123
  },
128
- });
124
+ };
125
+
126
+ items.push(item);
129
127
 
130
- const zoomMin = TileMatrixSet.convertZoomLevel(layer.minZoom ? layer.minZoom : 0, GoogleTms, tileMatrix, true);
128
+ const minZoom = layer.disabled ? 32 : layer.minZoom;
129
+ const zoomMin = TileMatrixSet.convertZoomLevel(minZoom ? minZoom : 0, GoogleTms, tileMatrix, true);
131
130
  const zoomMax = TileMatrixSet.convertZoomLevel(layer.maxZoom ? layer.maxZoom : 32, GoogleTms, tileMatrix, true);
132
131
  cols.push({
133
132
  stac_version: Stac.Version,
134
133
  license: Stac.License,
135
134
  id: im.id,
136
135
  providers: getHost(host),
137
- title: im.title ?? titleizeImageryName(im.name),
136
+ title,
138
137
  description: 'No description',
139
138
  extent,
140
139
  links: [],
@@ -142,6 +141,7 @@ async function tileSetAttribution(
142
141
  'linz:category': im.category,
143
142
  'linz:zoom': { min: zoomMin, max: zoomMax },
144
143
  'linz:priority': [1000 + tileSet.layers.indexOf(layer)],
144
+ 'linz:disabled': layer.disabled ? true : false,
145
145
  },
146
146
  });
147
147
  }
@@ -2,6 +2,7 @@ import { GoogleTms, TileJson, TileMatrixSet } from '@basemaps/geo';
2
2
  import { Env, toQueryString } from '@basemaps/shared';
3
3
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
4
  import { ConfigLoader } from '../util/config.loader.js';
5
+ import { getFilters } from '../util/filter.js';
5
6
  import { NotFound } from '../util/response.js';
6
7
  import { Validate } from '../util/validate.js';
7
8
 
@@ -31,7 +32,7 @@ export async function tileJsonGet(req: LambdaHttpRequest<TileJsonGet>): Promise<
31
32
 
32
33
  const configLocation = ConfigLoader.extract(req);
33
34
 
34
- const query = toQueryString({ api: apiKey, config: configLocation });
35
+ const query = toQueryString({ api: apiKey, config: configLocation, ...getFilters(req) });
35
36
 
36
37
  const tileUrl =
37
38
  [host, 'v1', 'tiles', tileSet.name, tileMatrix.identifier, '{z}', '{x}', '{y}'].join('/') + `.${format[0]}${query}`;
@@ -8,6 +8,7 @@ import { Validate } from '../util/validate.js';
8
8
  import { Etag } from '../util/etag.js';
9
9
  import { ConfigLoader } from '../util/config.loader.js';
10
10
  import { GoogleTms, ImageFormat, TileMatrixSets } from '@basemaps/geo';
11
+ import { getFilters } from '../util/filter.js';
11
12
 
12
13
  /**
13
14
  * Convert relative URLS into a full hostname url
@@ -73,7 +74,7 @@ export async function tileSetToStyle(
73
74
  if (tileFormat == null) return new LambdaHttpResponse(400, 'Invalid image format');
74
75
 
75
76
  const configLocation = ConfigLoader.extract(req);
76
- const query = toQueryString({ config: configLocation, api: apiKey });
77
+ const query = toQueryString({ config: configLocation, api: apiKey, ...getFilters(req) });
77
78
 
78
79
  const tileUrl = fsa.join(
79
80
  Env.get(Env.PublicUrlBase) ?? '',
@@ -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: {
@@ -54,17 +55,23 @@ export async function wmtsCapabilitiesGet(req: LambdaHttpRequest<WmtsCapabilitie
54
55
  );
55
56
  req.timer.end('imagery:load');
56
57
 
57
- const xml = new WmtsCapabilities({
58
+ const wmts = new WmtsCapabilities({
58
59
  httpBase: host,
60
+ apiKey,
61
+ config: ConfigLoader.extract(req),
62
+ filters: getFilters(req),
63
+ });
64
+
65
+ wmts.fromParams({
59
66
  provider: provider ?? undefined,
60
67
  tileSet,
61
68
  tileMatrix,
62
- isIndividualLayers: req.params.tileMatrix == null,
63
69
  imagery,
64
- apiKey,
65
- config: ConfigLoader.extract(req),
66
- formats: Validate.getRequestedFormats(req),
67
- }).toXml();
70
+ formats: Validate.getRequestedFormats(req) ?? [],
71
+ layers: req.params.tileMatrix == null ? filterLayers(req, tileSet.layers) : undefined,
72
+ });
73
+
74
+ const xml = wmts.toXml();
68
75
  if (xml == null) return NotFound();
69
76
 
70
77
  const data = Buffer.from(xml);
@@ -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
+ }
@@ -1,5 +1,5 @@
1
- import { ImageFormat, TileMatrixSet, TileMatrixSets, VectorFormat } from '@basemaps/geo';
2
- import { Const, isValidApiKey, Projection } from '@basemaps/shared';
1
+ import { ImageFormat, Projection, TileMatrixSet, TileMatrixSets, VectorFormat } from '@basemaps/geo';
2
+ import { Const, isValidApiKey, truncateApiKey } from '@basemaps/shared';
3
3
  import { getImageFormat } from '@basemaps/tiler';
4
4
  import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
5
5
  import { TileXyzGet } from '../routes/tile.xyz';
@@ -25,7 +25,8 @@ export const Validate = {
25
25
  const valid = isValidApiKey(apiKey);
26
26
 
27
27
  if (!valid.valid) throw new LambdaHttpResponse(400, 'API Key Invalid: ' + valid.message);
28
- req.set('api', apiKey);
28
+ // Truncate the API Key so we are not logging the full key
29
+ req.set('api', truncateApiKey(apiKey));
29
30
  return apiKey as string;
30
31
  },
31
32