@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
@@ -78,7 +78,148 @@ export const Imagery3857: ConfigImagery = {
78
78
  width: 51977.179234057665,
79
79
  height: 30574.81131407339,
80
80
  },
81
- files: [],
81
+ files: [
82
+ {
83
+ x: 19461478.89763212,
84
+ y: -4591419.415033963,
85
+ width: 305.7481131407044,
86
+ height: 305.7481131407044,
87
+ name: '17-129188-80552',
88
+ },
89
+ {
90
+ x: 19502754.89290612,
91
+ y: -4603343.591446448,
92
+ width: 305.7481131407044,
93
+ height: 305.7481131407044,
94
+ name: '17-129323-80591',
95
+ },
96
+ {
97
+ x: 19504283.633471873,
98
+ y: -4608847.057483015,
99
+ width: 611.4962262814096,
100
+ height: 611.4962262814096,
101
+ name: '16-64664-40304',
102
+ },
103
+ {
104
+ x: 19509175.60328212,
105
+ y: -4605178.080125325,
106
+ width: 611.4962262814096,
107
+ height: 611.4962262814096,
108
+ name: '16-64672-40298',
109
+ },
110
+ {
111
+ x: 19509175.60328212,
112
+ y: -4604566.583899044,
113
+ width: 611.4962262814096,
114
+ height: 611.4962262814096,
115
+ name: '16-64672-40297',
116
+ },
117
+ {
118
+ x: 19459032.912726905,
119
+ y: -4582552.719752827,
120
+ width: 1222.9924525628148,
121
+ height: 1222.9924525628148,
122
+ name: '15-32295-20130',
123
+ },
124
+ {
125
+ x: 19460255.905179467,
126
+ y: -4592336.659373329,
127
+ width: 1222.9924525628148,
128
+ height: 1222.9924525628148,
129
+ name: '15-32296-20138',
130
+ },
131
+ {
132
+ x: 19501837.648566607,
133
+ y: -4609458.55370921,
134
+ width: 1222.9924525628148,
135
+ height: 1222.9924525628148,
136
+ name: '15-32330-20152',
137
+ },
138
+ {
139
+ x: 19503060.64101917,
140
+ y: -4609458.55370921,
141
+ width: 1222.9924525628148,
142
+ height: 1222.9924525628148,
143
+ name: '15-32331-20152',
144
+ },
145
+ {
146
+ x: 19503060.64101917,
147
+ y: -4603343.591446393,
148
+ width: 1222.9924525628148,
149
+ height: 1222.9924525628148,
150
+ name: '15-32331-20147',
151
+ },
152
+ {
153
+ x: 19457809.920274343,
154
+ y: -4593559.651825892,
155
+ width: 2445.9849051256297,
156
+ height: 2445.9849051256297,
157
+ name: '14-16147-10069',
158
+ },
159
+ {
160
+ x: 19457809.920274343,
161
+ y: -4591113.666920764,
162
+ width: 2445.9849051256297,
163
+ height: 2445.9849051256297,
164
+ name: '14-16147-10068',
165
+ },
166
+ {
167
+ x: 19457809.920274343,
168
+ y: -4588667.682015641,
169
+ width: 2445.9849051256297,
170
+ height: 2445.9849051256297,
171
+ name: '14-16147-10067',
172
+ },
173
+ {
174
+ x: 19457809.920274343,
175
+ y: -4581329.727300262,
176
+ width: 2445.9849051256297,
177
+ height: 2445.9849051256297,
178
+ name: '14-16147-10064',
179
+ },
180
+ {
181
+ x: 19460255.905179467,
182
+ y: -4591113.666920764,
183
+ width: 2445.9849051256297,
184
+ height: 2445.9849051256297,
185
+ name: '14-16148-10068',
186
+ },
187
+ {
188
+ x: 19460255.905179586,
189
+ y: -4588667.682015713,
190
+ width: 4891.969810251274,
191
+ height: 4891.969810251274,
192
+ name: '13-8074-5033',
193
+ },
194
+ {
195
+ x: 19460255.905179586,
196
+ y: -4583775.712205462,
197
+ width: 4891.969810251274,
198
+ height: 4891.969810251274,
199
+ name: '13-8074-5032',
200
+ },
201
+ {
202
+ x: 19499391.663661595,
203
+ y: -4608235.561256718,
204
+ width: 4891.969810251274,
205
+ height: 4891.969810251274,
206
+ name: '13-8082-5037',
207
+ },
208
+ {
209
+ x: 19504283.63347185,
210
+ y: -4608235.561256718,
211
+ width: 4891.969810251274,
212
+ height: 4891.969810251274,
213
+ name: '13-8083-5037',
214
+ },
215
+ {
216
+ x: 19504283.63347185,
217
+ y: -4603343.591446467,
218
+ width: 4891.969810251274,
219
+ height: 4891.969810251274,
220
+ name: '13-8083-5036',
221
+ },
222
+ ],
82
223
  };
83
224
 
84
225
  export const Provider: ConfigProvider = {
@@ -30,7 +30,6 @@ o.spec('WmtsCapabilities', () => {
30
30
  imagery: allImagery,
31
31
  apiKey,
32
32
  formats: [ImageFormat.Avif],
33
- isIndividualLayers: false,
34
33
  }).toVNode();
35
34
 
36
35
  const urls = tags(wmts, 'ResourceURL');
@@ -51,7 +50,6 @@ o.spec('WmtsCapabilities', () => {
51
50
  apiKey,
52
51
  config: 's3://linz-basemaps/config.json',
53
52
  formats: [ImageFormat.Avif],
54
- isIndividualLayers: false,
55
53
  }).toVNode();
56
54
 
57
55
  const urls = tags(wmts, 'ResourceURL');
@@ -71,7 +69,6 @@ o.spec('WmtsCapabilities', () => {
71
69
  imagery: allImagery,
72
70
  apiKey,
73
71
  formats: [ImageFormat.Avif],
74
- isIndividualLayers: false,
75
72
  }).toXml();
76
73
 
77
74
  o(xml.split('\n')[0]).deepEquals('<?xml version="1.0" encoding="utf-8"?>');
@@ -90,7 +87,6 @@ o.spec('WmtsCapabilities', () => {
90
87
  imagery: allImagery,
91
88
  apiKey,
92
89
  formats: [ImageFormat.Avif],
93
- isIndividualLayers: false,
94
90
  }).toVNode();
95
91
 
96
92
  const urls = tags(wmts, 'ResourceURL');
@@ -142,7 +138,7 @@ o.spec('WmtsCapabilities', () => {
142
138
  tileSet,
143
139
  imagery,
144
140
  apiKey,
145
- isIndividualLayers: true,
141
+ layers: tileSet.layers,
146
142
  }).toVNode();
147
143
 
148
144
  const layers = tags(wmts, 'Layer').map((c) => c.find('ows:Title')?.textContent);
@@ -161,7 +157,6 @@ o.spec('WmtsCapabilities', () => {
161
157
  tileSet: TileSetAerial,
162
158
  imagery,
163
159
  apiKey,
164
- isIndividualLayers: false,
165
160
  });
166
161
 
167
162
  const raw = wmts.toVNode();
@@ -213,7 +208,6 @@ o.spec('WmtsCapabilities', () => {
213
208
  tileSet: TileSetAerial,
214
209
  imagery: allImagery,
215
210
  apiKey,
216
- isIndividualLayers: false,
217
211
  }).toVNode();
218
212
  const layer = raw.find('Contents', 'Layer');
219
213
 
@@ -260,7 +254,7 @@ o.spec('WmtsCapabilities', () => {
260
254
  tileSet: TileSetAerial,
261
255
  imagery: imagery,
262
256
  formats: [ImageFormat.Png],
263
- isIndividualLayers: true,
257
+ layers: TileSetAerial.layers,
264
258
  }).toVNode();
265
259
 
266
260
  const tms = raw?.find('TileMatrixSet', 'ows:Identifier');
@@ -299,7 +293,6 @@ o.spec('WmtsCapabilities', () => {
299
293
  tileSet: TileSetAerial,
300
294
  imagery: imagery,
301
295
  formats: [ImageFormat.Png],
302
- isIndividualLayers: false,
303
296
  }).toVNode();
304
297
 
305
298
  const layers = tags(raw, 'Layer');
@@ -361,7 +354,7 @@ o.spec('WmtsCapabilities', () => {
361
354
  tileSet: TileSetAerial,
362
355
  imagery: imagery,
363
356
  formats: [ImageFormat.Png],
364
- isIndividualLayers: true,
357
+ layers: TileSetAerial.layers,
365
358
  }).toVNode();
366
359
 
367
360
  const layers = tags(raw, 'Layer');
@@ -375,7 +368,7 @@ o.spec('WmtsCapabilities', () => {
375
368
  tileSet: TileSetAerial,
376
369
  imagery: imagery,
377
370
  formats: [ImageFormat.Png],
378
- isIndividualLayers: true,
371
+ layers: TileSetAerial.layers,
379
372
  }).toVNode();
380
373
 
381
374
  const layersB = tags(rawB, 'Layer');
@@ -397,8 +390,8 @@ o.spec('WmtsCapabilities', () => {
397
390
 
398
391
  const tileSet = { ...TileSetAerial };
399
392
  tileSet.layers = [
400
- { 3857: imageTopLeft.id, name: 'a_top_left' },
401
- { 3857: imageBottomRight.id, name: 'b_bottom_right' },
393
+ { 3857: imageTopLeft.id, name: 'a_top_left', title: 'A Top Left' },
394
+ { 3857: imageBottomRight.id, name: 'b_bottom_right', title: 'B Bottom Right' },
402
395
  ];
403
396
 
404
397
  const raw = new WmtsCapabilities({
@@ -408,7 +401,7 @@ o.spec('WmtsCapabilities', () => {
408
401
  tileSet,
409
402
  imagery,
410
403
  formats: [ImageFormat.Png],
411
- isIndividualLayers: true,
404
+ layers: tileSet.layers,
412
405
  }).toVNode();
413
406
 
414
407
  const boundingBox = tags(raw, 'ows:WGS84BoundingBox').map((c) =>
@@ -438,7 +431,7 @@ o.spec('WmtsCapabilities', () => {
438
431
  imagery.set(imageBottomRight.id, imageBottomRight);
439
432
 
440
433
  const tileSet = { ...TileSetAerial };
441
- tileSet.layers = [{ 3857: imageBottomRight.id, name: 'b_bottom_right' }];
434
+ tileSet.layers = [{ 3857: imageBottomRight.id, name: 'b_bottom_right', title: 'B Bottom Right' }];
442
435
 
443
436
  const raw = new WmtsCapabilities({
444
437
  httpBase: 'https://basemaps.test',
@@ -447,7 +440,7 @@ o.spec('WmtsCapabilities', () => {
447
440
  tileSet,
448
441
  imagery,
449
442
  formats: [ImageFormat.Png],
450
- isIndividualLayers: true,
443
+ layers: tileSet.layers,
451
444
  }).toVNode();
452
445
 
453
446
  const boundingBox = tags(raw, 'ows:WGS84BoundingBox').map((c) =>
@@ -3,6 +3,8 @@ import { LambdaAlbRequest, LambdaHttpRequest, LambdaUrlRequest } from '@linzjs/l
3
3
  import { Context } from 'aws-lambda';
4
4
 
5
5
  export function mockRequest(path: string, method = 'get', headers: Record<string, string> = {}): LambdaHttpRequest {
6
+ const log = LogConfig.get();
7
+ log.level = 'silent';
6
8
  return new LambdaAlbRequest(
7
9
  {
8
10
  requestContext: null as any,
@@ -13,7 +15,7 @@ export function mockRequest(path: string, method = 'get', headers: Record<string
13
15
  isBase64Encoded: false,
14
16
  },
15
17
  {} as Context,
16
- LogConfig.get(),
18
+ log,
17
19
  );
18
20
  }
19
21
 
@@ -23,6 +25,8 @@ export function mockUrlRequest(
23
25
  headers: Record<string, unknown> = {},
24
26
  method?: string,
25
27
  ): LambdaHttpRequest {
28
+ const log = LogConfig.get();
29
+ log.level = 'silent';
26
30
  return new LambdaUrlRequest(
27
31
  {
28
32
  requestContext: { http: { method: method ? method.toUpperCase() : 'GET' } },
@@ -32,7 +36,7 @@ export function mockUrlRequest(
32
36
  isBase64Encoded: false,
33
37
  } as any,
34
38
  {} as Context,
35
- LogConfig.get(),
39
+ log,
36
40
  );
37
41
  }
38
42
 
@@ -0,0 +1,38 @@
1
+ import { ConfigProviderMemory } from '@basemaps/config';
2
+ import { initConfigFromPaths } from '@basemaps/config/build/json/tiff.config.js';
3
+ import { ImageFormat, Nztm2000QuadTms } from '@basemaps/geo';
4
+ import { LogConfig, setDefaultConfig } from '@basemaps/shared';
5
+ import { fsa } from '@chunkd/fs';
6
+ import { LambdaHttpRequest, LambdaUrlRequest, UrlEvent } from '@linzjs/lambda';
7
+ import { Context } from 'aws-lambda';
8
+ import { TileXyzRaster } from '../routes/tile.xyz.raster.js';
9
+
10
+ const target = `/home/blacha/tmp/basemaps`;
11
+ const tile = { z: 18, x: 126359, y: 137603 };
12
+ const tileMatrix = Nztm2000QuadTms;
13
+
14
+ async function main(): Promise<void> {
15
+ const log = LogConfig.get();
16
+ const provider = new ConfigProviderMemory();
17
+ setDefaultConfig(provider);
18
+ const { tileSet, imagery } = await initConfigFromPaths(provider, [target]);
19
+
20
+ if (tileSet.layers.length === 0) throw new Error('No imagery found in path: ' + target);
21
+ log.info({ tileSet: tileSet.name, layers: tileSet.layers.length }, 'TileSet:Loaded');
22
+ for (const im of imagery) {
23
+ log.info({ imagery: im.uri, title: im.title, tileMatrix: im.tileMatrix, files: im.files.length }, 'Imagery:Loaded');
24
+ }
25
+ const request = new LambdaUrlRequest({ headers: {} } as UrlEvent, {} as Context, log) as LambdaHttpRequest;
26
+
27
+ const res = await TileXyzRaster.tile(request, tileSet, {
28
+ tile,
29
+ tileMatrix,
30
+ tileSet: tileSet.id,
31
+ tileType: ImageFormat.Png,
32
+ });
33
+
34
+ await fsa.write(`./${tile.z}_${tile.x}_${tile.y}.png`, Buffer.from(res.body, 'base64'));
35
+ log.info({ path: `./${tile.z}_${tile.x}_${tile.y}.png` }, 'Tile:Write');
36
+ }
37
+
38
+ main();
@@ -1,7 +1,8 @@
1
1
  import { Attribution } from '@basemaps/attribution';
2
2
  import { ConfigProviderMemory } from '@basemaps/config';
3
- import { Nztm2000QuadTms } from '@basemaps/geo';
4
- import { LogConfig } from '@basemaps/shared';
3
+ import { GoogleTms, Nztm2000QuadTms } from '@basemaps/geo';
4
+ import { LogConfig, Projection } from '@basemaps/shared';
5
+ import { BBox } from '@linzjs/geojson';
5
6
  import { HttpHeader } from '@linzjs/lambda';
6
7
  import o from 'ospec';
7
8
  import sinon from 'sinon';
@@ -9,6 +10,7 @@ import { handler } from '../../index.js';
9
10
  import { ConfigLoader } from '../../util/config.loader.js';
10
11
  import { FakeData, Imagery2193, Imagery3857, Provider, TileSetAerial } from '../../__tests__/config.data.js';
11
12
  import { mockUrlRequest } from '../../__tests__/xyz.util.js';
13
+ import { createCoordinates } from '../attribution.js';
12
14
 
13
15
  // const ExpectedJson = {
14
16
  // id: 'aerial_WebMercatorQuad',
@@ -332,7 +334,7 @@ o.spec('/v1/attribution', () => {
332
334
  });
333
335
 
334
336
  o.spec('ImageryRules', () => {
335
- const fakeLayer = { [2193]: Imagery2193.id, name: 'image', minZoom: 9, maxZoom: 16 };
337
+ const fakeLayer = { [2193]: Imagery2193.id, name: 'image', minZoom: 9, maxZoom: 16, title: 'Image' };
336
338
  const ts = FakeData.tileSetRaster('fake');
337
339
 
338
340
  o.beforeEach(() => {
@@ -387,4 +389,64 @@ o.spec('/v1/attribution', () => {
387
389
  o(output.collections[0].summaries['linz:zoom']).deepEquals({ min: 0, max: Nztm2000QuadTms.maxZoom });
388
390
  });
389
391
  });
392
+
393
+ o('should create valid coordinates', async () => {
394
+ //bbox: BBox, files: NamedBounds[], proj: Projection
395
+ const bbox = [174.79248047, -38.21228805, 175.25939941, -37.99616268] as BBox;
396
+ const proj = Projection.get(GoogleTms);
397
+ const coordinates = createCoordinates(bbox, Imagery3857.files, proj);
398
+ console.log(JSON.stringify(coordinates));
399
+ o(coordinates).deepEquals([
400
+ [
401
+ [
402
+ [174.79247149, -38.09998972],
403
+ [174.81446211, -38.09998972],
404
+ [174.81446211, -38.09134368],
405
+ [174.82544844, -38.09134368],
406
+ [174.82544844, -38.08485848],
407
+ [174.82819502, -38.08485848],
408
+ [174.82819502, -38.08269662],
409
+ [174.83643476, -38.08269662],
410
+ [174.83643476, -38.06539942],
411
+ [174.85840742, -38.06539942],
412
+ [174.85840742, -37.9961556],
413
+ [174.81446211, -37.9961556],
414
+ [174.81446211, -37.9961556],
415
+ [174.79247149, -37.9961556],
416
+ [174.79247149, -38.01348331],
417
+ [174.80345781, -38.01348331],
418
+ [174.80345781, -38.02213855],
419
+ [174.81444414, -38.02213855],
420
+ [174.81444414, -38.04808399],
421
+ [174.79247149, -38.04808399],
422
+ [174.79247149, -38.09998972],
423
+ ],
424
+ ],
425
+ [
426
+ [
427
+ [175.16600664, -38.20366238],
428
+ [175.1879793, -38.20366238],
429
+ [175.1879793, -38.21229511],
430
+ [175.20996992, -38.21229511],
431
+ [175.20996992, -38.20797887],
432
+ [175.21546308, -38.20797887],
433
+ [175.21546308, -38.20366238],
434
+ [175.25391523, -38.20366238],
435
+ [175.25391523, -38.18207606],
436
+ [175.2594084, -38.18207606],
437
+ [175.2594084, -38.17342562],
438
+ [175.25391523, -38.17342562],
439
+ [175.25391523, -38.13454951],
440
+ [175.20995195, -38.13454951],
441
+ [175.20995195, -38.16046922],
442
+ [175.19896563, -38.16046922],
443
+ [175.19896563, -38.16694771],
444
+ [175.19621904, -38.16694771],
445
+ [175.19621904, -38.16910707],
446
+ [175.16600664, -38.16910707],
447
+ [175.16600664, -38.20366238],
448
+ ],
449
+ ],
450
+ ]);
451
+ });
390
452
  });
@@ -9,18 +9,11 @@ import { Api, mockUrlRequest } from '../../__tests__/xyz.util.js';
9
9
  o.spec('WMTSRouting', () => {
10
10
  const sandbox = createSandbox();
11
11
  const config = new ConfigProviderMemory();
12
+ const imagery = new Map();
12
13
 
13
- o.before(() => {
14
+ o.beforeEach(() => {
14
15
  sandbox.stub(ConfigLoader, 'load').resolves(config);
15
- });
16
16
 
17
- o.afterEach(() => {
18
- config.objects.clear();
19
- sandbox.restore();
20
- });
21
-
22
- o('should default to the aerial layer', async () => {
23
- const imagery = new Map();
24
17
  imagery.set(Imagery3857.id, Imagery3857);
25
18
  imagery.set(Imagery2193.id, Imagery2193);
26
19
 
@@ -28,7 +21,14 @@ o.spec('WMTSRouting', () => {
28
21
  config.put(Imagery2193);
29
22
  config.put(Imagery3857);
30
23
  config.put(Provider);
24
+ });
31
25
 
26
+ o.afterEach(() => {
27
+ config.objects.clear();
28
+ sandbox.restore();
29
+ });
30
+
31
+ o('should default to the aerial layer', async () => {
32
32
  const req = mockUrlRequest(
33
33
  '/v1/tiles/WMTSCapabilities.xml',
34
34
  `format=png&api=${Api.key}&config=s3://linz-basemaps/config.json`,
@@ -53,4 +53,65 @@ o.spec('WMTSRouting', () => {
53
53
  '<ResourceURL format="image/png" resourceType="tile" template="https://tiles.test/v1/tiles/ōtorohanga-urban-2021-0.1m/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png?api=d01f7w7rnhdzg0p7fyrc9v9ard1&amp;config=Q5pC4UjWdtFLU1CYtLcRSmB49RekgDgMa5EGJnB2M" />',
54
54
  ]);
55
55
  });
56
+
57
+ o('should filter out date[after] by year', async () => {
58
+ const req = mockUrlRequest(
59
+ '/v1/tiles/WMTSCapabilities.xml',
60
+ `format=png&api=${Api.key}&config=s3://linz-basemaps/config.json&date[after]=2022`,
61
+ );
62
+ const res = await handler.router.handle(req);
63
+
64
+ o(res.status).equals(200);
65
+ const lines = Buffer.from(res.body, 'base64').toString().split('\n');
66
+ const titles = lines.filter((f) => f.startsWith(' <ows:Title>')).map((f) => f.trim());
67
+
68
+ o(titles).deepEquals([
69
+ '<ows:Title>Aerial Imagery</ows:Title>',
70
+ '<ows:Title>Google Maps Compatible for the World</ows:Title>',
71
+ '<ows:Title>LINZ NZTM2000 Map Tile Grid V2</ows:Title>',
72
+ ]);
73
+ });
74
+
75
+ o('should filter out date[before] by year', async () => {
76
+ const req = mockUrlRequest(
77
+ '/v1/tiles/WMTSCapabilities.xml',
78
+ `format=png&api=${Api.key}&config=s3://linz-basemaps/config.json&date[before]=2020`,
79
+ );
80
+ const res = await handler.router.handle(req);
81
+
82
+ o(res.status).equals(200);
83
+ const lines = Buffer.from(res.body, 'base64').toString().split('\n');
84
+ const titles = lines.filter((f) => f.startsWith(' <ows:Title>')).map((f) => f.trim());
85
+
86
+ o(titles).deepEquals([
87
+ '<ows:Title>Aerial Imagery</ows:Title>',
88
+ '<ows:Title>Google Maps Compatible for the World</ows:Title>',
89
+ '<ows:Title>LINZ NZTM2000 Map Tile Grid V2</ows:Title>',
90
+ ]);
91
+ });
92
+
93
+ o('should filter inclusive date[before] by year', async () => {
94
+ const req = mockUrlRequest(
95
+ '/v1/tiles/WMTSCapabilities.xml',
96
+ `format=png&api=${Api.key}&config=s3://linz-basemaps/config.json&date[before]=2021`,
97
+ );
98
+ const res = await handler.router.handle(req);
99
+
100
+ o(res.status).equals(200);
101
+ const lines = Buffer.from(res.body, 'base64').toString().split('\n');
102
+ const titles = lines.filter((f) => f.startsWith(' <ows:Title>')).map((f) => f.trim());
103
+
104
+ o(titles).deepEquals([
105
+ '<ows:Title>Aerial Imagery</ows:Title>',
106
+ '<ows:Title>Ōtorohanga 0.1m Urban Aerial Photos (2021)</ows:Title>',
107
+ '<ows:Title>Google Maps Compatible for the World</ows:Title>',
108
+ '<ows:Title>LINZ NZTM2000 Map Tile Grid V2</ows:Title>',
109
+ ]);
110
+
111
+ const resourceURLs = lines.filter((f) => f.includes('<ResourceURL')).map((f) => f.trim());
112
+ o(resourceURLs).deepEquals([
113
+ '<ResourceURL format="image/png" resourceType="tile" template="https://tiles.test/v1/tiles/aerial/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png?api=d01f7w7rnhdzg0p7fyrc9v9ard1&amp;config=Q5pC4UjWdtFLU1CYtLcRSmB49RekgDgMa5EGJnB2M&amp;date%5Bbefore%5D=2021" />',
114
+ '<ResourceURL format="image/png" resourceType="tile" template="https://tiles.test/v1/tiles/ōtorohanga-urban-2021-0.1m/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png?api=d01f7w7rnhdzg0p7fyrc9v9ard1&amp;config=Q5pC4UjWdtFLU1CYtLcRSmB49RekgDgMa5EGJnB2M" />',
115
+ ]);
116
+ });
56
117
  });
@@ -11,12 +11,13 @@ import {
11
11
  StacProvider,
12
12
  TileMatrixSet,
13
13
  } from '@basemaps/geo';
14
- import { extractYearRangeFromName, Projection, titleizeImageryName } from '@basemaps/shared';
14
+ import { extractYearRangeFromName, extractYearRangeFromTitle, Projection } from '@basemaps/shared';
15
15
  import { BBox, MultiPolygon, multiPolygonToWgs84, Pair, union, Wgs84 } from '@linzjs/geojson';
16
16
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
17
17
  import { ConfigLoader } from '../util/config.loader.js';
18
18
 
19
19
  import { Etag } from '../util/etag.js';
20
+ import { filterLayers, yearRangeToInterval } from '../util/filter.js';
20
21
  import { NotFound, NotModified } from '../util/response.js';
21
22
  import { Validate } from '../util/validate.js';
22
23
 
@@ -44,23 +45,19 @@ function roundPair(p: Pair): Pair {
44
45
  * @param files in target projection
45
46
  * @return MultiPolygon in WGS84
46
47
  */
47
- function createCoordinates(bbox: BBox, files: NamedBounds[], proj: Projection): MultiPolygon {
48
+ export function createCoordinates(bbox: BBox, files: NamedBounds[], proj: Projection): MultiPolygon {
48
49
  if (Wgs84.delta(bbox[0], bbox[2]) <= 0) {
49
50
  // This bounds spans more than half the globe which multiPolygonToWgs84 can't handle; just
50
51
  // return bbox as polygon
51
52
  return Wgs84.bboxToMultiPolygon(bbox);
52
53
  }
53
54
 
54
- let coordinates: MultiPolygon = [];
55
-
55
+ const polygons: MultiPolygon = [];
56
56
  // 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
- }
57
+ for (const image of files) polygons.push(Bounds.fromJson(image).pad(SmoothPadding).toPolygon());
58
+ const coordinates = union(polygons);
61
59
 
62
60
  const roundToWgs84 = (p: number[]): number[] => roundPair(proj.toWgs84(p) as Pair);
63
-
64
61
  return multiPolygonToWgs84(coordinates, roundToWgs84);
65
62
  }
66
63
 
@@ -86,30 +83,23 @@ async function tileSetAttribution(
86
83
 
87
84
  const config = await ConfigLoader.load(req);
88
85
  const imagery = await getAllImagery(config, tileSet.layers, [tileMatrix.projection]);
86
+ const filteredLayers = filterLayers(req, tileSet.layers);
89
87
 
90
88
  const host = await config.Provider.get(config.Provider.id('linz'));
91
89
 
92
- for (const layer of tileSet.layers) {
90
+ for (const layer of filteredLayers) {
91
+ if (layer.disabled) continue;
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
97
 
98
98
  const bbox = proj.boundsToWgs84BoundingBox(im.bounds).map(roundNumber) as BBox;
99
99
 
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]];
109
-
110
- const extent: StacExtent = { spatial: { bbox: [bbox] }, temporal: { interval } };
100
+ const extent: StacExtent = { spatial: { bbox: [bbox] } };
111
101
 
112
- items.push({
102
+ const item: AttributionItem = {
113
103
  type: 'Feature',
114
104
  stac_version: Stac.Version,
115
105
  id: imgId + '_item',
@@ -119,13 +109,19 @@ async function tileSetAttribution(
119
109
  bbox,
120
110
  geometry: { type: 'MultiPolygon', coordinates: createCoordinates(bbox, im.files, proj) },
121
111
  properties: {
122
- title: im.title ?? titleizeImageryName(im.name),
112
+ title,
123
113
  category: im.category,
124
- datetime: null,
125
- start_datetime: interval[0][0],
126
- end_datetime: interval[0][1],
127
114
  },
128
- });
115
+ };
116
+ const years = extractYearRangeFromTitle(im.title) ?? extractYearRangeFromName(im.name);
117
+ if (years) {
118
+ const interval = yearRangeToInterval(years);
119
+ extent.temporal = { interval: [[interval[0].toISOString(), interval[1].toISOString()]] };
120
+ item.properties.datetime = null;
121
+ item.properties.start_datetime = interval[0].toISOString();
122
+ item.properties.end_datetime = interval[1].toISOString();
123
+ }
124
+ items.push(item);
129
125
 
130
126
  const zoomMin = TileMatrixSet.convertZoomLevel(layer.minZoom ? layer.minZoom : 0, GoogleTms, tileMatrix, true);
131
127
  const zoomMax = TileMatrixSet.convertZoomLevel(layer.maxZoom ? layer.maxZoom : 32, GoogleTms, tileMatrix, true);
@@ -134,7 +130,7 @@ async function tileSetAttribution(
134
130
  license: Stac.License,
135
131
  id: im.id,
136
132
  providers: getHost(host),
137
- title: im.title ?? titleizeImageryName(im.name),
133
+ title,
138
134
  description: 'No description',
139
135
  extent,
140
136
  links: [],
@@ -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) ?? '',