@basemaps/lambda-tiler 6.34.0 → 6.35.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 (124) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/build/__tests__/config.data.d.ts +2 -1
  3. package/build/__tests__/config.data.d.ts.map +1 -1
  4. package/build/__tests__/config.data.js +14 -1
  5. package/build/__tests__/config.data.js.map +1 -1
  6. package/build/__tests__/tile.style.json.test.js +17 -3
  7. package/build/__tests__/tile.style.json.test.js.map +1 -1
  8. package/build/__tests__/wmts.capability.test.js +17 -0
  9. package/build/__tests__/wmts.capability.test.js.map +1 -1
  10. package/build/arcgis/__tests__/arcgis.style.json.test.js +18 -15
  11. package/build/arcgis/__tests__/arcgis.style.json.test.js.map +1 -1
  12. package/build/arcgis/__tests__/vector.tiler.server.test.js +14 -7
  13. package/build/arcgis/__tests__/vector.tiler.server.test.js.map +1 -1
  14. package/build/arcgis/arcgis.style.json.d.ts.map +1 -1
  15. package/build/arcgis/arcgis.style.json.js +6 -4
  16. package/build/arcgis/arcgis.style.json.js.map +1 -1
  17. package/build/arcgis/vector.tile.server.d.ts.map +1 -1
  18. package/build/arcgis/vector.tile.server.js +4 -2
  19. package/build/arcgis/vector.tile.server.js.map +1 -1
  20. package/build/index.d.ts +1 -3
  21. package/build/index.d.ts.map +1 -1
  22. package/build/index.js +7 -8
  23. package/build/index.js.map +1 -1
  24. package/build/routes/__tests__/attribution.test.js +3 -2
  25. package/build/routes/__tests__/attribution.test.js.map +1 -1
  26. package/build/routes/__tests__/fonts.test.js +36 -7
  27. package/build/routes/__tests__/fonts.test.js.map +1 -1
  28. package/build/routes/__tests__/health.test.js +4 -2
  29. package/build/routes/__tests__/health.test.js.map +1 -1
  30. package/build/routes/__tests__/sprites.test.js +3 -4
  31. package/build/routes/__tests__/sprites.test.js.map +1 -1
  32. package/build/routes/__tests__/tile.json.test.js +24 -2
  33. package/build/routes/__tests__/tile.json.test.js.map +1 -1
  34. package/build/routes/__tests__/tile.style.json.test.js +58 -7
  35. package/build/routes/__tests__/tile.style.json.test.js.map +1 -1
  36. package/build/routes/__tests__/wmts.test.js +17 -14
  37. package/build/routes/__tests__/wmts.test.js.map +1 -1
  38. package/build/routes/__tests__/xyz.test.js +4 -2
  39. package/build/routes/__tests__/xyz.test.js.map +1 -1
  40. package/build/routes/attribution.d.ts.map +1 -1
  41. package/build/routes/attribution.js +20 -10
  42. package/build/routes/attribution.js.map +1 -1
  43. package/build/routes/config.d.ts +22 -0
  44. package/build/routes/config.d.ts.map +1 -0
  45. package/build/routes/config.js +63 -0
  46. package/build/routes/config.js.map +1 -0
  47. package/build/routes/health.d.ts.map +1 -1
  48. package/build/routes/health.js +3 -2
  49. package/build/routes/health.js.map +1 -1
  50. package/build/routes/imagery.d.ts.map +1 -1
  51. package/build/routes/imagery.js +3 -2
  52. package/build/routes/imagery.js.map +1 -1
  53. package/build/routes/tile.json.d.ts.map +1 -1
  54. package/build/routes/tile.json.js +7 -4
  55. package/build/routes/tile.json.js.map +1 -1
  56. package/build/routes/tile.style.json.d.ts +4 -3
  57. package/build/routes/tile.style.json.d.ts.map +1 -1
  58. package/build/routes/tile.style.json.js +53 -12
  59. package/build/routes/tile.style.json.js.map +1 -1
  60. package/build/routes/tile.wmts.d.ts.map +1 -1
  61. package/build/routes/tile.wmts.js +7 -4
  62. package/build/routes/tile.wmts.js.map +1 -1
  63. package/build/routes/tile.xyz.d.ts.map +1 -1
  64. package/build/routes/tile.xyz.js +4 -2
  65. package/build/routes/tile.xyz.js.map +1 -1
  66. package/build/routes/tile.xyz.raster.d.ts.map +1 -1
  67. package/build/routes/tile.xyz.raster.js +4 -2
  68. package/build/routes/tile.xyz.raster.js.map +1 -1
  69. package/build/util/__test__/config.loader.test.d.ts +2 -0
  70. package/build/util/__test__/config.loader.test.d.ts.map +1 -0
  71. package/build/util/__test__/config.loader.test.js +79 -0
  72. package/build/util/__test__/config.loader.test.js.map +1 -0
  73. package/build/util/assets.provider.d.ts +1 -4
  74. package/build/util/assets.provider.d.ts.map +1 -1
  75. package/build/util/assets.provider.js +23 -10
  76. package/build/util/assets.provider.js.map +1 -1
  77. package/build/util/config.cache.d.ts +3 -3
  78. package/build/util/config.cache.d.ts.map +1 -1
  79. package/build/util/config.cache.js +14 -15
  80. package/build/util/config.cache.js.map +1 -1
  81. package/build/util/config.loader.d.ts +10 -0
  82. package/build/util/config.loader.d.ts.map +1 -0
  83. package/build/util/config.loader.js +47 -0
  84. package/build/util/config.loader.js.map +1 -0
  85. package/build/wmts.capability.d.ts +3 -1
  86. package/build/wmts.capability.d.ts.map +1 -1
  87. package/build/wmts.capability.js +5 -15
  88. package/build/wmts.capability.js.map +1 -1
  89. package/dist/index.js +66 -66
  90. package/dist/node_modules/.package-lock.json +1 -1
  91. package/dist/package-lock.json +2 -2
  92. package/dist/package.json +1 -1
  93. package/package.json +8 -9
  94. package/src/__tests__/config.data.ts +25 -1
  95. package/src/__tests__/tile.style.json.test.ts +19 -3
  96. package/src/__tests__/wmts.capability.test.ts +21 -0
  97. package/src/arcgis/__tests__/arcgis.style.json.test.ts +21 -17
  98. package/src/arcgis/__tests__/vector.tiler.server.test.ts +17 -8
  99. package/src/arcgis/arcgis.style.json.ts +6 -4
  100. package/src/arcgis/vector.tile.server.ts +5 -2
  101. package/src/index.ts +9 -10
  102. package/src/routes/__tests__/attribution.test.ts +4 -2
  103. package/src/routes/__tests__/fonts.test.ts +44 -7
  104. package/src/routes/__tests__/health.test.ts +4 -2
  105. package/src/routes/__tests__/sprites.test.ts +3 -4
  106. package/src/routes/__tests__/tile.json.test.ts +30 -2
  107. package/src/routes/__tests__/tile.style.json.test.ts +68 -9
  108. package/src/routes/__tests__/wmts.test.ts +23 -17
  109. package/src/routes/__tests__/xyz.test.ts +4 -2
  110. package/src/routes/attribution.ts +23 -8
  111. package/src/routes/config.ts +83 -0
  112. package/src/routes/health.ts +4 -2
  113. package/src/routes/imagery.ts +3 -2
  114. package/src/routes/tile.json.ts +10 -4
  115. package/src/routes/tile.style.json.ts +58 -12
  116. package/src/routes/tile.wmts.ts +9 -4
  117. package/src/routes/tile.xyz.raster.ts +4 -2
  118. package/src/routes/tile.xyz.ts +5 -2
  119. package/src/util/__test__/config.loader.test.ts +116 -0
  120. package/src/util/assets.provider.ts +11 -12
  121. package/src/util/config.cache.ts +18 -18
  122. package/src/util/config.loader.ts +50 -0
  123. package/src/wmts.capability.ts +9 -15
  124. package/tsconfig.tsbuildinfo +1 -1
@@ -1,24 +1,29 @@
1
- import { Config, StyleJson } from '@basemaps/config';
1
+ import { ConfigProviderMemory, StyleJson } from '@basemaps/config';
2
2
  import { Env } from '@basemaps/shared';
3
3
  import o from 'ospec';
4
4
  import { createSandbox } from 'sinon';
5
5
  import { handler } from '../../index.js';
6
- import { Api, mockRequest } from '../../__tests__/xyz.util.js';
6
+ import { ConfigLoader } from '../../util/config.loader.js';
7
+ import { FakeData } from '../../__tests__/config.data.js';
8
+ import { Api, mockRequest, mockUrlRequest } from '../../__tests__/xyz.util.js';
7
9
 
8
10
  o.spec('/v1/styles', () => {
9
11
  const host = 'https://tiles.test';
12
+ const config = new ConfigProviderMemory();
10
13
  const sandbox = createSandbox();
11
14
 
12
15
  o.before(() => {
13
16
  process.env[Env.PublicUrlBase] = host;
14
17
  });
15
- // o.beforeEach(() => {});
16
- o.afterEach(() => sandbox.restore());
18
+ o.beforeEach(() => {
19
+ sandbox.stub(ConfigLoader, 'getDefaultConfig').resolves(config);
20
+ });
21
+ o.afterEach(() => {
22
+ sandbox.restore();
23
+ config.objects.clear();
24
+ });
17
25
  o('should not found style json', async () => {
18
26
  const request = mockRequest('/v1/tiles/topographic/Google/style/topographic.json', 'get', Api.header);
19
-
20
- sandbox.stub(Config.Style, 'get').resolves(null);
21
-
22
27
  const res = await handler.router.handle(request);
23
28
  o(res.status).equals(404);
24
29
  });
@@ -71,12 +76,12 @@ o.spec('/v1/styles', () => {
71
76
  };
72
77
 
73
78
  const fakeRecord = {
74
- id: 'st_topographic_production',
79
+ id: 'st_topographic',
75
80
  name: 'topographic',
76
81
  style: fakeStyle,
77
82
  };
78
83
 
79
- sandbox.stub(Config.Style, 'get').resolves(fakeRecord as any);
84
+ config.put(fakeRecord);
80
85
 
81
86
  const res = await handler.router.handle(request);
82
87
  o(res.status).equals(200);
@@ -102,4 +107,58 @@ o.spec('/v1/styles', () => {
102
107
 
103
108
  o(JSON.parse(body)).deepEquals(fakeStyle);
104
109
  });
110
+
111
+ o('should create raster styles', async () => {
112
+ const request = mockUrlRequest('/v1/styles/aerial.json', '', Api.header);
113
+ const tileSet = FakeData.tileSetRaster('aerial');
114
+ config.put(tileSet);
115
+ const res = await handler.router.handle(request);
116
+ o(res.status).equals(200);
117
+
118
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
119
+
120
+ o(body.version).equals(8);
121
+ o(body.sources['basemaps-aerial'].type).deepEquals('raster');
122
+ o(body.sources['basemaps-aerial'].tiles).deepEquals([
123
+ `https://tiles.test/v1/tiles/aerial/WebMercatorQuad/{z}/{x}/{y}.webp?api=${Api.key}`,
124
+ ]);
125
+ o(body.sources['basemaps-aerial'].tileSize).deepEquals(256);
126
+ o(body.layers).deepEquals([{ id: 'basemaps-aerial', type: 'raster', source: 'basemaps-aerial' }]);
127
+ });
128
+
129
+ o('should support parameters', async () => {
130
+ const request = mockUrlRequest('/v1/styles/aerial.json', '?tileMatrix=NZTM2000Quad&format=jpg', Api.header);
131
+ const tileSet = FakeData.tileSetRaster('aerial');
132
+ config.put(tileSet);
133
+ const res = await handler.router.handle(request);
134
+ o(res.status).equals(200);
135
+
136
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
137
+
138
+ o(body.version).equals(8);
139
+ o(body.sources['basemaps-aerial'].type).deepEquals('raster');
140
+ o(body.sources['basemaps-aerial'].tiles).deepEquals([
141
+ `https://tiles.test/v1/tiles/aerial/NZTM2000Quad/{z}/{x}/{y}.jpeg?api=${Api.key}`,
142
+ ]);
143
+ o(body.sources['basemaps-aerial'].tileSize).deepEquals(256);
144
+ o(body.layers).deepEquals([{ id: 'basemaps-aerial', type: 'raster', source: 'basemaps-aerial' }]);
145
+ });
146
+
147
+ o('should create raster styles from custom config', async () => {
148
+ const configId = FakeData.bundle([FakeData.tileSetRaster('aerial')]);
149
+ const request = mockUrlRequest('/v1/styles/aerial.json', `?config=${configId}`, Api.header);
150
+
151
+ const res = await handler.router.handle(request);
152
+ o(res.status).equals(200);
153
+
154
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
155
+
156
+ o(body.version).equals(8);
157
+ o(body.sources['basemaps-aerial'].type).deepEquals('raster');
158
+ o(body.sources['basemaps-aerial'].tiles).deepEquals([
159
+ `https://tiles.test/v1/tiles/aerial/WebMercatorQuad/{z}/{x}/{y}.webp?api=${Api.key}&config=${configId}`,
160
+ ]);
161
+ o(body.sources['basemaps-aerial'].tileSize).deepEquals(256);
162
+ o(body.layers).deepEquals([{ id: 'basemaps-aerial', type: 'raster', source: 'basemaps-aerial' }]);
163
+ });
105
164
  });
@@ -1,13 +1,21 @@
1
- import { Config } from '@basemaps/shared';
1
+ import { ConfigProviderMemory } from '@basemaps/config';
2
2
  import o from 'ospec';
3
3
  import { createSandbox } from 'sinon';
4
4
  import { handler } from '../../index.js';
5
+ import { ConfigLoader } from '../../util/config.loader.js';
5
6
  import { Imagery2193, Imagery3857, Provider, TileSetAerial } from '../../__tests__/config.data.js';
6
7
  import { Api, mockUrlRequest } from '../../__tests__/xyz.util.js';
7
8
 
8
9
  o.spec('WMTSRouting', () => {
9
10
  const sandbox = createSandbox();
11
+ const config = new ConfigProviderMemory();
12
+
13
+ o.before(() => {
14
+ sandbox.stub(ConfigLoader, 'load').resolves(config);
15
+ });
16
+
10
17
  o.afterEach(() => {
18
+ config.objects.clear();
11
19
  sandbox.restore();
12
20
  });
13
21
 
@@ -16,25 +24,17 @@ o.spec('WMTSRouting', () => {
16
24
  imagery.set(Imagery3857.id, Imagery3857);
17
25
  imagery.set(Imagery2193.id, Imagery2193);
18
26
 
19
- const tileSetStub = sandbox.stub(Config.TileSet, 'get').returns(Promise.resolve(TileSetAerial));
20
- const imageryStub = sandbox.stub(Config.Imagery, 'getAll').returns(Promise.resolve(imagery));
21
- const providerStub = sandbox.stub(Config.Provider, 'get').returns(Promise.resolve(Provider));
27
+ config.put(TileSetAerial);
28
+ config.put(Imagery2193);
29
+ config.put(Imagery3857);
30
+ config.put(Provider);
22
31
 
23
- const req = mockUrlRequest('/v1/tiles/WMTSCapabilities.xml', `format=png&api=${Api.key}`);
32
+ const req = mockUrlRequest(
33
+ '/v1/tiles/WMTSCapabilities.xml',
34
+ `format=png&api=${Api.key}&config=s3://linz-basemaps/config.json`,
35
+ );
24
36
  const res = await handler.router.handle(req);
25
37
 
26
- o(tileSetStub.calledOnce).equals(true);
27
- o(tileSetStub.args[0][0]).equals('ts_aerial');
28
-
29
- o(providerStub.calledOnce).equals(true);
30
- o(providerStub.args[0][0]).equals('pv_linz');
31
-
32
- o(imageryStub.calledOnce).equals(true);
33
- o([...imageryStub.args[0][0].values()]).deepEquals([
34
- 'im_01FYWKATAEK2ZTJQ2PX44Y0XNT',
35
- 'im_01FYWKAJ86W9P7RWM1VB62KD0H',
36
- ]);
37
-
38
38
  o(res.status).equals(200);
39
39
  const lines = Buffer.from(res.body, 'base64').toString().split('\n');
40
40
 
@@ -46,5 +46,11 @@ o.spec('WMTSRouting', () => {
46
46
  '<ows:Title>Google Maps Compatible for the World</ows:Title>',
47
47
  '<ows:Title>LINZ NZTM2000 Map Tile Grid V2</ows:Title>',
48
48
  ]);
49
+
50
+ const resourceURLs = lines.filter((f) => f.includes('<ResourceURL')).map((f) => f.trim());
51
+ o(resourceURLs).deepEquals([
52
+ '<ResourceURL format="image/png" resourceType="tile" template="https://tiles.test/v1/tiles/aerial/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png?api=d01f7w7rnhdzg0p7fyrc9v9ard1&amp;config=Q5pC4UjWdtFLU1CYtLcRSmB49RekgDgMa5EGJnB2M" />',
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
+ ]);
49
55
  });
50
56
  });
@@ -1,9 +1,10 @@
1
- import { Config, ConfigProviderMemory } from '@basemaps/config';
1
+ import { ConfigProviderMemory } from '@basemaps/config';
2
2
  import { LogConfig } from '@basemaps/shared';
3
3
  import { round } from '@basemaps/test/build/rounding.js';
4
4
  import o from 'ospec';
5
5
  import sinon from 'sinon';
6
6
  import { handler } from '../../index.js';
7
+ import { ConfigLoader } from '../../util/config.loader.js';
7
8
  import { Etag } from '../../util/etag.js';
8
9
  import { FakeData } from '../../__tests__/config.data.js';
9
10
  import { Api, mockRequest } from '../../__tests__/xyz.util.js';
@@ -16,7 +17,7 @@ o.spec('/v1/tiles', () => {
16
17
 
17
18
  o.beforeEach(() => {
18
19
  LogConfig.get().level = 'silent';
19
- Config.setConfigProvider(config);
20
+ sandbox.stub(ConfigLoader, 'getDefaultConfig').resolves(config);
20
21
  config.objects.clear();
21
22
 
22
23
  for (const tileSetName of TileSetNames) config.put(FakeData.tileSetRaster(tileSetName));
@@ -25,6 +26,7 @@ o.spec('/v1/tiles', () => {
25
26
  });
26
27
 
27
28
  o.afterEach(() => {
29
+ config.objects.clear();
28
30
  sandbox.restore();
29
31
  });
30
32
 
@@ -1,4 +1,4 @@
1
- import { ConfigTileSet, TileSetType } from '@basemaps/config';
1
+ import { ConfigProvider, ConfigTileSet, getAllImagery, TileSetType } from '@basemaps/config';
2
2
  import {
3
3
  AttributionCollection,
4
4
  AttributionItem,
@@ -8,11 +8,13 @@ import {
8
8
  NamedBounds,
9
9
  Stac,
10
10
  StacExtent,
11
+ StacProvider,
11
12
  TileMatrixSet,
12
13
  } from '@basemaps/geo';
13
- import { Config, extractYearRangeFromName, Projection, titleizeImageryName } from '@basemaps/shared';
14
+ import { extractYearRangeFromName, Projection, titleizeImageryName } from '@basemaps/shared';
14
15
  import { BBox, MultiPolygon, multiPolygonToWgs84, Pair, union, Wgs84 } from '@linzjs/geojson';
15
16
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
17
+ import { ConfigLoader } from '../util/config.loader.js';
16
18
 
17
19
  import { Etag } from '../util/etag.js';
18
20
  import { NotFound, NotModified } from '../util/response.js';
@@ -62,6 +64,11 @@ function createCoordinates(bbox: BBox, files: NamedBounds[], proj: Projection):
62
64
  return multiPolygonToWgs84(coordinates, roundToWgs84);
63
65
  }
64
66
 
67
+ function getHost(host: ConfigProvider | null): StacProvider[] | undefined {
68
+ if (host == null) return undefined;
69
+ return [{ name: host.serviceProvider.name, url: host.serviceProvider.site, roles: ['host'] }];
70
+ }
71
+
65
72
  /**
66
73
  * Build a Single File STAC for the given TileSet.
67
74
  *
@@ -77,10 +84,10 @@ async function tileSetAttribution(
77
84
  const cols: AttributionCollection[] = [];
78
85
  const items: AttributionItem[] = [];
79
86
 
80
- const imagery = await Config.getAllImagery(tileSet.layers, [tileMatrix.projection]);
87
+ const config = await ConfigLoader.load(req);
88
+ const imagery = await getAllImagery(config, tileSet.layers, [tileMatrix.projection]);
81
89
 
82
- const host = await Config.Provider.get(Config.Provider.id('linz'));
83
- if (host == null) return null;
90
+ const host = await config.Provider.get(config.Provider.id('linz'));
84
91
 
85
92
  for (const layer of tileSet.layers) {
86
93
  const imgId = layer[proj.epsg.code];
@@ -91,7 +98,13 @@ async function tileSetAttribution(
91
98
  const bbox = proj.boundsToWgs84BoundingBox(im.bounds).map(roundNumber) as BBox;
92
99
 
93
100
  const years = extractYearRangeFromName(im.name);
94
- if (years[0] === -1) throw new Error('Missing date in imagery name: ' + 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
+
95
108
  const interval = [years.map((y) => `${y}-01-01T00:00:00Z`) as [string, string]];
96
109
 
97
110
  const extent: StacExtent = { spatial: { bbox: [bbox] }, temporal: { interval } };
@@ -120,7 +133,7 @@ async function tileSetAttribution(
120
133
  stac_version: Stac.Version,
121
134
  license: Stac.License,
122
135
  id: im.id,
123
- providers: [{ name: host.serviceProvider.name, url: host.serviceProvider.site, roles: ['host'] }],
136
+ providers: getHost(host),
124
137
  title: im.title ?? titleizeImageryName(im.name),
125
138
  description: 'No description',
126
139
  extent,
@@ -159,8 +172,10 @@ export async function tileAttributionGet(req: LambdaHttpRequest<TileAttributionG
159
172
  const tileMatrix = Validate.getTileMatrixSet(req.params.tileMatrix);
160
173
  if (tileMatrix == null) throw new LambdaHttpResponse(404, 'Tile Matrix not found');
161
174
 
175
+ const config = await ConfigLoader.load(req);
176
+
162
177
  req.timer.start('tileset:load');
163
- const tileSet = await Config.TileSet.get(Config.TileSet.id(req.params.tileSet));
178
+ const tileSet = await config.TileSet.get(config.TileSet.id(req.params.tileSet));
164
179
  req.timer.end('tileset:load');
165
180
  if (tileSet == null || tileSet.type === TileSetType.Vector) return NotFound();
166
181
 
@@ -0,0 +1,83 @@
1
+ import { standardizeLayerName } from '@basemaps/config';
2
+ import { GoogleTms, TileMatrixSets } from '@basemaps/geo';
3
+ import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
+ import { ConfigLoader } from '../util/config.loader.js';
5
+ import { Etag } from '../util/etag.js';
6
+ import { NotFound, NotModified } from '../util/response.js';
7
+
8
+ async function sendJson(req: LambdaHttpRequest, toSend: unknown): Promise<LambdaHttpResponse> {
9
+ const data = Buffer.from(JSON.stringify(toSend));
10
+
11
+ const cacheKey = Etag.key(data);
12
+ if (Etag.isNotModified(req, cacheKey)) return NotModified();
13
+
14
+ const response = new LambdaHttpResponse(200, 'ok');
15
+ response.header(HttpHeader.ETag, cacheKey);
16
+ response.header(HttpHeader.CacheControl, 'no-store');
17
+ response.buffer(data, 'application/json');
18
+ req.set('bytes', data.byteLength);
19
+ return response;
20
+ }
21
+
22
+ interface ConfigTileSetGet {
23
+ Params: {
24
+ tileSet: string;
25
+ };
26
+ }
27
+
28
+ export async function configTileSetGet(req: LambdaHttpRequest<ConfigTileSetGet>): Promise<LambdaHttpResponse> {
29
+ const config = await ConfigLoader.load(req);
30
+
31
+ req.timer.start('tileset:load');
32
+ const tileSet = await config.TileSet.get(config.TileSet.id(req.params.tileSet));
33
+ req.timer.end('tileset:load');
34
+ if (tileSet == null) return NotFound();
35
+
36
+ return sendJson(req, tileSet);
37
+ }
38
+
39
+ interface ConfigImageryGet {
40
+ Params: {
41
+ tileSet: string;
42
+ imageryId: string;
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Load the imagery configuration by either name or id
48
+ *
49
+ * @param req
50
+ * @returns
51
+ */
52
+ export async function configImageryGet(req: LambdaHttpRequest<ConfigImageryGet>): Promise<LambdaHttpResponse> {
53
+ const config = await ConfigLoader.load(req);
54
+
55
+ req.timer.start('tileset:load');
56
+ const tileSet = await config.TileSet.get(config.TileSet.id(req.params.tileSet));
57
+ req.timer.end('tileset:load');
58
+ if (tileSet == null) return NotFound();
59
+
60
+ req.timer.start('imagery:load');
61
+ let imagery = await config.Imagery.get(config.Imagery.id(req.params.imageryId));
62
+ req.timer.end('imagery:load');
63
+
64
+ if (imagery == null) {
65
+ const imageryLayer = tileSet.layers.find(
66
+ (f) => f.name === req.params.imageryId || standardizeLayerName(f.name) === req.params.imageryId,
67
+ );
68
+ if (imageryLayer == null) return NotFound();
69
+
70
+ const tileMatrix = TileMatrixSets.find(req.query.get('tileMatrix') ?? GoogleTms.identifier);
71
+ if (tileMatrix == null) return NotFound();
72
+
73
+ const imageryId = imageryLayer[tileMatrix.projection.code];
74
+ if (imageryId == null) return NotFound();
75
+
76
+ req.timer.start('imagery:load:sub');
77
+ imagery = await config.Imagery.get(config.Imagery.id(imageryId));
78
+ req.timer.end('imagery:load:sub');
79
+ }
80
+
81
+ if (imagery == null) return NotFound();
82
+ return sendJson(req, imagery);
83
+ }
@@ -1,4 +1,4 @@
1
- import { Config, ConfigTileSetRaster } from '@basemaps/config';
1
+ import { ConfigTileSetRaster } from '@basemaps/config';
2
2
  import { GoogleTms, ImageFormat, Nztm2000QuadTms } from '@basemaps/geo';
3
3
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
4
  import * as fs from 'fs';
@@ -6,6 +6,7 @@ import * as path from 'path';
6
6
  import PixelMatch from 'pixelmatch';
7
7
  import Sharp from 'sharp';
8
8
  import url from 'url';
9
+ import { ConfigLoader } from '../util/config.loader.js';
9
10
  import { TileXyz } from '../util/validate.js';
10
11
  import { TileXyzRaster } from './tile.xyz.raster.js';
11
12
 
@@ -53,7 +54,8 @@ export async function updateExpectedTile(test: TestTile, newTileData: Buffer, di
53
54
  * @throws LambdaHttpResponse for failure health test
54
55
  */
55
56
  export async function healthGet(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
56
- const tileSet = await Config.TileSet.get(Config.TileSet.id('health'));
57
+ const config = await ConfigLoader.load(req);
58
+ const tileSet = await config.TileSet.get(config.TileSet.id('health'));
57
59
  if (tileSet == null) throw new LambdaHttpResponse(500, 'TileSet: "health" not found');
58
60
  for (const test of TestTiles) {
59
61
  // Get the parse response tile to raw buffer
@@ -1,8 +1,8 @@
1
- import { Config } from '@basemaps/config';
2
1
  import { fsa } from '@basemaps/shared';
3
2
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
3
  import { promisify } from 'util';
5
4
  import { gzip } from 'zlib';
5
+ import { ConfigLoader } from '../util/config.loader.js';
6
6
  import { isGzip } from '../util/cotar.serve.js';
7
7
  import { Etag } from '../util/etag.js';
8
8
  import { NotFound, NotModified } from '../util/response.js';
@@ -34,7 +34,8 @@ export async function imageryGet(req: LambdaHttpRequest<ImageryGet>): Promise<La
34
34
  const requestedFile = req.params.fileName;
35
35
  if (!isAllowedFile(requestedFile)) return NotFound();
36
36
 
37
- const imagery = await Config.Imagery.get(Config.Imagery.id(req.params.imageryId));
37
+ const config = await ConfigLoader.load(req);
38
+ const imagery = await config.Imagery.get(config.Imagery.id(req.params.imageryId));
38
39
  if (imagery == null) return NotFound();
39
40
 
40
41
  const targetPath = fsa.join(imagery.uri, requestedFile);
@@ -1,6 +1,7 @@
1
1
  import { GoogleTms, TileJson, TileMatrixSet } from '@basemaps/geo';
2
- import { Config, Env } from '@basemaps/shared';
2
+ import { Env, toQueryString } from '@basemaps/shared';
3
3
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
+ import { ConfigLoader } from '../util/config.loader.js';
4
5
  import { NotFound } from '../util/response.js';
5
6
  import { Validate } from '../util/validate.js';
6
7
 
@@ -17,8 +18,10 @@ export async function tileJsonGet(req: LambdaHttpRequest<TileJsonGet>): Promise<
17
18
 
18
19
  const apiKey = Validate.apiKey(req);
19
20
 
21
+ const config = await ConfigLoader.load(req);
22
+
20
23
  req.timer.start('tileset:load');
21
- const tileSet = await Config.TileSet.get(Config.TileSet.id(req.params.tileSet));
24
+ const tileSet = await config.TileSet.get(config.TileSet.id(req.params.tileSet));
22
25
  req.timer.end('tileset:load');
23
26
  if (tileSet == null) return NotFound();
24
27
 
@@ -26,9 +29,12 @@ export async function tileJsonGet(req: LambdaHttpRequest<TileJsonGet>): Promise<
26
29
 
27
30
  const host = Env.get(Env.PublicUrlBase) ?? '';
28
31
 
32
+ const configLocation = ConfigLoader.extract(req);
33
+
34
+ const query = toQueryString({ api: apiKey, config: configLocation });
35
+
29
36
  const tileUrl =
30
- [host, 'v1', 'tiles', tileSet.name, tileMatrix.identifier, '{z}', '{x}', '{y}'].join('/') +
31
- `.${format[0]}?api=${apiKey}`;
37
+ [host, 'v1', 'tiles', tileSet.name, tileMatrix.identifier, '{z}', '{x}', '{y}'].join('/') + `.${format[0]}${query}`;
32
38
 
33
39
  const tileJson: TileJson = { tiles: [tileUrl], tilejson: '3.0.0' };
34
40
  const maxZoom = TileMatrixSet.convertZoomLevel(tileSet.maxZoom ?? 30, GoogleTms, tileMatrix, true);
@@ -1,11 +1,13 @@
1
- import { Sources, StyleJson } from '@basemaps/config';
2
- import { Config, Env } from '@basemaps/shared';
1
+ import { ConfigTileSetRaster, Sources, StyleJson, TileSetType } from '@basemaps/config';
2
+ import { Env, toQueryString } from '@basemaps/shared';
3
3
  import { fsa } from '@chunkd/fs';
4
4
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
5
5
  import { URL } from 'url';
6
6
  import { NotFound, NotModified } from '../util/response.js';
7
7
  import { Validate } from '../util/validate.js';
8
8
  import { Etag } from '../util/etag.js';
9
+ import { ConfigLoader } from '../util/config.loader.js';
10
+ import { GoogleTms, ImageFormat, TileMatrixSets } from '@basemaps/geo';
9
11
 
10
12
  /**
11
13
  * Convert relative URLS into a full hostname url
@@ -13,12 +15,13 @@ import { Etag } from '../util/etag.js';
13
15
  * @param apiKey ApiKey to append with ?api= if required
14
16
  * @returns Updated Url or empty string if url is empty
15
17
  */
16
- export function convertRelativeUrl(url?: string, apiKey?: string): string {
18
+ export function convertRelativeUrl(url?: string, apiKey?: string, config?: string | null): string {
17
19
  if (url == null) return '';
18
20
  const host = Env.get(Env.PublicUrlBase) ?? '';
19
21
  if (!url.startsWith('/')) return url; // Not relative ignore
20
22
  const fullUrl = new URL(fsa.join(host, url));
21
23
  if (apiKey) fullUrl.searchParams.set('api', apiKey);
24
+ if (config) fullUrl.searchParams.set('config', config);
22
25
  return fullUrl.toString().replace(/%7B/g, '{').replace(/%7D/g, '}');
23
26
  }
24
27
 
@@ -28,14 +31,14 @@ export function convertRelativeUrl(url?: string, apiKey?: string): string {
28
31
  * @param apiKey api key to inject
29
32
  * @returns new stylejson
30
33
  */
31
- export function convertStyleJson(style: StyleJson, apiKey: string): StyleJson {
34
+ export function convertStyleJson(style: StyleJson, apiKey: string, config: string | null): StyleJson {
32
35
  const sources: Sources = JSON.parse(JSON.stringify(style.sources));
33
36
  for (const [key, value] of Object.entries(sources)) {
34
37
  if (value.type === 'vector') {
35
- value.url = convertRelativeUrl(value.url, apiKey);
38
+ value.url = convertRelativeUrl(value.url, apiKey, config);
36
39
  } else if (value.type === 'raster' && Array.isArray(value.tiles)) {
37
40
  for (let i = 0; i < value.tiles.length; i++) {
38
- value.tiles[i] = convertRelativeUrl(value.tiles[i], apiKey);
41
+ value.tiles[i] = convertRelativeUrl(value.tiles[i], apiKey, config);
39
42
  }
40
43
  }
41
44
  sources[key] = value;
@@ -48,8 +51,8 @@ export function convertStyleJson(style: StyleJson, apiKey: string): StyleJson {
48
51
  sources,
49
52
  layers: style.layers,
50
53
  metadata: style.metadata ?? {},
51
- glyphs: convertRelativeUrl(style.glyphs),
52
- sprite: convertRelativeUrl(style.sprite),
54
+ glyphs: convertRelativeUrl(style.glyphs, undefined, config),
55
+ sprite: convertRelativeUrl(style.sprite, undefined, config),
53
56
  } as StyleJson;
54
57
  }
55
58
 
@@ -59,17 +62,60 @@ export interface StyleGet {
59
62
  };
60
63
  }
61
64
 
65
+ export async function tileSetToStyle(
66
+ req: LambdaHttpRequest<StyleGet>,
67
+ tileSet: ConfigTileSetRaster,
68
+ apiKey: string,
69
+ ): Promise<LambdaHttpResponse> {
70
+ const tileMatrix = TileMatrixSets.find(req.query.get('tileMatrix') ?? GoogleTms.identifier);
71
+ if (tileMatrix == null) return new LambdaHttpResponse(400, 'Invalid tile matrix');
72
+ const [tileFormat] = Validate.getRequestedFormats(req) ?? [ImageFormat.Webp];
73
+ if (tileFormat == null) return new LambdaHttpResponse(400, 'Invalid image format');
74
+
75
+ const configLocation = ConfigLoader.extract(req);
76
+ const query = toQueryString({ config: configLocation, api: apiKey });
77
+
78
+ const tileUrl = fsa.join(
79
+ Env.get(Env.PublicUrlBase) ?? '',
80
+ `/v1/tiles/${tileSet.name}/${tileMatrix.identifier}/{z}/{x}/{y}.${tileFormat}${query}`,
81
+ );
82
+ const styleId = `basemaps-${tileSet.name}`;
83
+ const style = {
84
+ version: 8,
85
+ sources: { [styleId]: { type: 'raster', tiles: [tileUrl], tileSize: 256 } },
86
+ layers: [{ id: styleId, type: 'raster', source: styleId }],
87
+ };
88
+ const data = Buffer.from(JSON.stringify(style));
89
+
90
+ const cacheKey = Etag.key(data);
91
+ if (Etag.isNotModified(req, cacheKey)) return NotModified();
92
+
93
+ const response = new LambdaHttpResponse(200, 'ok');
94
+ response.header(HttpHeader.ETag, cacheKey);
95
+ response.header(HttpHeader.CacheControl, 'no-store');
96
+ response.buffer(data, 'application/json');
97
+ req.set('bytes', data.byteLength);
98
+ return response;
99
+ }
100
+
62
101
  export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<LambdaHttpResponse> {
63
102
  const apiKey = Validate.apiKey(req);
64
103
  const styleName = req.params.styleName;
65
104
 
66
105
  // Get style Config from db
67
- const dbId = Config.Style.id(styleName);
68
- const styleConfig = await Config.Style.get(dbId);
69
- if (styleConfig == null) return NotFound();
106
+ const config = await ConfigLoader.load(req);
107
+ const dbId = config.Style.id(styleName);
108
+ const styleConfig = await config.Style.get(dbId);
109
+ if (styleConfig == null) {
110
+ // Were we given a tileset name instead, generated
111
+ const tileSet = await config.TileSet.get(config.TileSet.id(styleName));
112
+ if (tileSet == null) return NotFound();
113
+ if (tileSet.type !== TileSetType.Raster) return NotFound();
114
+ return tileSetToStyle(req, tileSet, apiKey);
115
+ }
70
116
 
71
117
  // Prepare sources and add linz source
72
- const style = convertStyleJson(styleConfig.style, apiKey);
118
+ const style = convertStyleJson(styleConfig.style, apiKey, ConfigLoader.extract(req));
73
119
  const data = Buffer.from(JSON.stringify(style));
74
120
 
75
121
  const cacheKey = Etag.key(data);
@@ -1,4 +1,4 @@
1
- import { Config, TileSetType } from '@basemaps/config';
1
+ import { getAllImagery, TileSetType } from '@basemaps/config';
2
2
  import { GoogleTms, Nztm2000QuadTms, TileMatrixSet } from '@basemaps/geo';
3
3
  import { Env } from '@basemaps/shared';
4
4
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
@@ -7,6 +7,7 @@ import { NotFound, NotModified } from '../util/response.js';
7
7
  import { Validate } from '../util/validate.js';
8
8
  import { WmtsCapabilities } from '../wmts.capability.js';
9
9
  import { Etag } from '../util/etag.js';
10
+ import { ConfigLoader } from '../util/config.loader.js';
10
11
 
11
12
  export interface WmtsCapabilitiesGet {
12
13
  Params: {
@@ -36,15 +37,18 @@ export async function wmtsCapabilitiesGet(req: LambdaHttpRequest<WmtsCapabilitie
36
37
 
37
38
  const host = Env.get(Env.PublicUrlBase) ?? '';
38
39
 
40
+ const config = await ConfigLoader.load(req);
41
+
39
42
  req.timer.start('tileset:load');
40
- const tileSet = await Config.TileSet.get(Config.TileSet.id(tileSetName ?? 'aerial'));
43
+ const tileSet = await config.TileSet.get(config.TileSet.id(tileSetName ?? 'aerial'));
41
44
  req.timer.end('tileset:load');
42
45
  if (tileSet == null || tileSet.type !== TileSetType.Raster) return NotFound();
43
46
 
44
- const provider = await Config.Provider.get(Config.Provider.id('linz'));
47
+ const provider = await config.Provider.get(config.Provider.id('linz'));
45
48
 
46
49
  req.timer.start('imagery:load');
47
- const imagery = await Config.getAllImagery(
50
+ const imagery = await getAllImagery(
51
+ config,
48
52
  tileSet.layers,
49
53
  tileMatrix.map((tms) => tms.projection),
50
54
  );
@@ -58,6 +62,7 @@ export async function wmtsCapabilitiesGet(req: LambdaHttpRequest<WmtsCapabilitie
58
62
  isIndividualLayers: req.params.tileMatrix == null,
59
63
  imagery,
60
64
  apiKey,
65
+ config: ConfigLoader.extract(req),
61
66
  formats: Validate.getRequestedFormats(req),
62
67
  }).toXml();
63
68
  if (xml == null) return NotFound();
@@ -1,4 +1,4 @@
1
- import { Config, ConfigTileSetRaster } from '@basemaps/config';
1
+ import { getAllImagery, ConfigTileSetRaster } from '@basemaps/config';
2
2
  import { Bounds, Epsg, TileMatrixSet, TileMatrixSets, VectorFormat } from '@basemaps/geo';
3
3
  import { Env, fsa } from '@basemaps/shared';
4
4
  import { Tiler } from '@basemaps/tiler';
@@ -6,6 +6,7 @@ import { TileMakerSharp } from '@basemaps/tiler-sharp';
6
6
  import { CogTiff } from '@cogeotiff/core';
7
7
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
8
8
  import pLimit from 'p-limit';
9
+ import { ConfigLoader } from '../util/config.loader.js';
9
10
  import { Etag } from '../util/etag.js';
10
11
  import { NotFound, NotModified } from '../util/response.js';
11
12
  import { CoSources } from '../util/source.cache.js';
@@ -26,7 +27,8 @@ const DefaultBackground = { r: 0, g: 0, b: 0, alpha: 0 };
26
27
 
27
28
  export const TileXyzRaster = {
28
29
  async getTiffsForTile(req: LambdaHttpRequest, tileSet: ConfigTileSetRaster, xyz: TileXyz): Promise<string[]> {
29
- const imagery = await Config.getAllImagery(tileSet.layers, [xyz.tileMatrix.projection]);
30
+ const config = await ConfigLoader.load(req);
31
+ const imagery = await getAllImagery(config, tileSet.layers, [xyz.tileMatrix.projection]);
30
32
 
31
33
  const output: string[] = [];
32
34
  const tileBounds = xyz.tileMatrix.tileToSourceBounds(xyz.tile);
@@ -1,5 +1,6 @@
1
- import { Config, TileSetType } from '@basemaps/config';
1
+ import { TileSetType } from '@basemaps/config';
2
2
  import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
3
+ import { ConfigLoader } from '../util/config.loader.js';
3
4
  import { NotFound } from '../util/response.js';
4
5
  import { Validate } from '../util/validate.js';
5
6
  import { TileXyzRaster } from './tile.xyz.raster.js';
@@ -29,8 +30,10 @@ export interface TileXyzGet {
29
30
  export async function tileXyzGet(req: LambdaHttpRequest<TileXyzGet>): Promise<LambdaHttpResponse> {
30
31
  const xyzData = Validate.xyz(req);
31
32
 
33
+ const config = await ConfigLoader.load(req);
34
+
32
35
  req.timer.start('tileset:load');
33
- const tileSet = await Config.TileSet.get(Config.TileSet.id(xyzData.tileSet));
36
+ const tileSet = await config.TileSet.get(config.TileSet.id(xyzData.tileSet));
34
37
  req.timer.end('tileset:load');
35
38
  if (tileSet == null) return NotFound();
36
39