@basemaps/lambda-tiler 6.16.0 → 6.19.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.
@@ -137,7 +137,7 @@ cs.get.hsl = function (string) {
137
137
  return null;
138
138
  }
139
139
 
140
- var hsl = /^hsla?\(\s*([+-]?(?:\d{0,3}\.)?\d+)(?:deg)?\s*,?\s*([+-]?[\d\.]+)%\s*,?\s*([+-]?[\d\.]+)%\s*(?:[,|\/]\s*([+-]?[\d\.]+)\s*)?\)$/;
140
+ var hsl = /^hsla?\(\s*([+-]?(?:\d{0,3}\.)?\d+)(?:deg)?\s*,?\s*([+-]?[\d\.]+)%\s*,?\s*([+-]?[\d\.]+)%\s*(?:[,|\/]\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
141
141
  var match = string.match(hsl);
142
142
 
143
143
  if (match) {
@@ -158,7 +158,7 @@ cs.get.hwb = function (string) {
158
158
  return null;
159
159
  }
160
160
 
161
- var hwb = /^hwb\(\s*([+-]?\d{0,3}(?:\.\d+)?)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/;
161
+ var hwb = /^hwb\(\s*([+-]?\d{0,3}(?:\.\d+)?)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
162
162
  var match = string.match(hwb);
163
163
 
164
164
  if (match) {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "_from": "color-string@^1.6.0",
3
- "_id": "color-string@1.8.2",
3
+ "_id": "color-string@1.9.0",
4
4
  "_inBundle": false,
5
- "_integrity": "sha512-w5ZkKRdLsc5NOYsmnpS2DpyRW71npwZGwbRpLrJTuqjfTs2Bhrba7UiV59IX9siBlCPl2pne5NtiwnVWUzvYFA==",
5
+ "_integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
6
6
  "_location": "/color-string",
7
7
  "_phantomChildren": {},
8
8
  "_requested": {
@@ -18,8 +18,8 @@
18
18
  "_requiredBy": [
19
19
  "/color"
20
20
  ],
21
- "_resolved": "https://registry.npmjs.org/color-string/-/color-string-1.8.2.tgz",
22
- "_shasum": "08bd49fa5f3889c27b0c670052ed746dd7a671de",
21
+ "_resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
22
+ "_shasum": "63b6ebd1bec11999d1df3a79a7569451ac2be8aa",
23
23
  "_spec": "color-string@^1.6.0",
24
24
  "_where": "/home/runner/work/basemaps/basemaps/packages/lambda-tiler/dist/node_modules/color",
25
25
  "author": {
@@ -71,7 +71,7 @@
71
71
  "pretest": "xo",
72
72
  "test": "node test/basic.js"
73
73
  },
74
- "version": "1.8.2",
74
+ "version": "1.9.0",
75
75
  "xo": {
76
76
  "rules": {
77
77
  "no-cond-assign": 0,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basemaps/lambda-tiler",
3
- "version": "6.16.0",
3
+ "version": "6.19.0",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
@@ -92,9 +92,9 @@
92
92
  "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
93
93
  },
94
94
  "color-string": {
95
- "version": "1.8.2",
96
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.8.2.tgz",
97
- "integrity": "sha512-w5ZkKRdLsc5NOYsmnpS2DpyRW71npwZGwbRpLrJTuqjfTs2Bhrba7UiV59IX9siBlCPl2pne5NtiwnVWUzvYFA==",
95
+ "version": "1.9.0",
96
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
97
+ "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
98
98
  "requires": {
99
99
  "color-name": "^1.0.0",
100
100
  "simple-swizzle": "^0.2.2"
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basemaps/lambda-tiler",
3
- "version": "6.16.0",
3
+ "version": "6.19.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/linz/basemaps.git",
@@ -21,12 +21,12 @@
21
21
  "types": "./build/index.d.ts",
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "@basemaps/config": "^6.10.0",
24
+ "@basemaps/config": "^6.18.0",
25
25
  "@basemaps/geo": "^6.10.0",
26
26
  "@basemaps/lambda": "^6.7.0",
27
- "@basemaps/shared": "^6.12.0",
27
+ "@basemaps/shared": "^6.18.0",
28
28
  "@basemaps/tiler": "^6.11.0",
29
- "@basemaps/tiler-sharp": "^6.11.0",
29
+ "@basemaps/tiler-sharp": "^6.16.1",
30
30
  "@chunkd/fs": "^7.3.1",
31
31
  "@cogeotiff/core": "^6.0.2",
32
32
  "@cotar/core": "^5.0.1",
@@ -51,7 +51,7 @@
51
51
  "bundle": "./bundle.sh"
52
52
  },
53
53
  "devDependencies": {
54
- "@basemaps/attribution": "^6.10.0",
54
+ "@basemaps/attribution": "^6.17.0",
55
55
  "@types/aws-lambda": "^8.10.75",
56
56
  "@types/express": "^4.17.11",
57
57
  "@types/node": "^14.11.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basemaps/lambda-tiler",
3
- "version": "6.16.0",
3
+ "version": "6.19.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/linz/basemaps.git",
@@ -22,12 +22,12 @@
22
22
  "types": "./build/index.d.ts",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@basemaps/config": "^6.10.0",
25
+ "@basemaps/config": "^6.18.0",
26
26
  "@basemaps/geo": "^6.10.0",
27
27
  "@basemaps/lambda": "^6.7.0",
28
- "@basemaps/shared": "^6.12.0",
28
+ "@basemaps/shared": "^6.18.0",
29
29
  "@basemaps/tiler": "^6.11.0",
30
- "@basemaps/tiler-sharp": "^6.11.0",
30
+ "@basemaps/tiler-sharp": "^6.16.1",
31
31
  "@chunkd/fs": "^7.3.1",
32
32
  "@cogeotiff/core": "^6.0.2",
33
33
  "@cotar/core": "^5.0.1",
@@ -52,7 +52,7 @@
52
52
  "bundle": "./bundle.sh"
53
53
  },
54
54
  "devDependencies": {
55
- "@basemaps/attribution": "^6.10.0",
55
+ "@basemaps/attribution": "^6.17.0",
56
56
  "@types/aws-lambda": "^8.10.75",
57
57
  "@types/express": "^4.17.11",
58
58
  "@types/node": "^14.11.2",
@@ -61,5 +61,5 @@
61
61
  "express": "^4.17.1",
62
62
  "pretty-json-log": "^1.0.0"
63
63
  },
64
- "gitHead": "75bda15568c94ebe89af4c179c0e80226bfabd3f"
64
+ "gitHead": "87d778480368585d0f4a5a7546a2858882d8ab39"
65
65
  }
@@ -209,7 +209,7 @@ o.spec('LambdaXyz', () => {
209
209
  o('should serve tile json for tile_set', async () => {
210
210
  process.env[Env.PublicUrlBase] = 'https://tiles.test';
211
211
 
212
- const request = mockRequest('/v1/tiles/topolike/Google/tile.json', 'get', apiKeyHeader);
212
+ const request = mockRequest('/v1/tiles/topographic/Google/tile.json', 'get', apiKeyHeader);
213
213
 
214
214
  const res = await handleRequest(request);
215
215
  o(res.status).equals(200);
@@ -218,7 +218,7 @@ o.spec('LambdaXyz', () => {
218
218
 
219
219
  const body = Buffer.from(res.body ?? '', 'base64').toString();
220
220
  o(JSON.parse(body)).deepEquals({
221
- tiles: [`https://tiles.test/v1/tiles/topolike/Google/{z}/{x}/{y}.pbf?api=${apiKey}`],
221
+ tiles: [`https://tiles.test/v1/tiles/topographic/Google/{z}/{x}/{y}.pbf?api=${apiKey}`],
222
222
  minzoom: 0,
223
223
  maxzoom: 15,
224
224
  format: 'pbf',
@@ -236,7 +236,7 @@ o.spec('LambdaXyz', () => {
236
236
  o('should not found style json', async () => {
237
237
  process.env[Env.PublicUrlBase] = 'https://tiles.test';
238
238
 
239
- const request = mockRequest('/v1/tiles/topolike/Google/style/topolike.json', 'get', apiKeyHeader);
239
+ const request = mockRequest('/v1/tiles/topographic/Google/style/topographic.json', 'get', apiKeyHeader);
240
240
 
241
241
  sandbox.stub(Config.Style, 'get').resolves(null);
242
242
 
@@ -248,24 +248,24 @@ o.spec('LambdaXyz', () => {
248
248
  const host = 'https://tiles.test';
249
249
  process.env[Env.PublicUrlBase] = host;
250
250
 
251
- const request = mockRequest('/v1/tiles/topolike/Google/style/topolike.json', 'get', apiKeyHeader);
251
+ const request = mockRequest('/v1/tiles/topographic/Google/style/topographic.json', 'get', apiKeyHeader);
252
252
 
253
253
  const fakeStyle: StyleJson = {
254
254
  version: 8,
255
255
  id: 'test',
256
- name: 'topolike',
256
+ name: 'topographic',
257
257
  sources: {
258
258
  basemaps_vector: {
259
259
  type: 'vector',
260
- url: `${host}/vector`,
260
+ url: `/vector`,
261
261
  },
262
262
  basemaps_raster: {
263
263
  type: 'raster',
264
- tiles: [`${host}/raster`],
264
+ tiles: [`/raster`],
265
265
  },
266
266
  basemaps_raster_encode: {
267
267
  type: 'raster',
268
- tiles: [`${host}/raster/{z}/{x}/{y}.webp`], // Shouldn't encode the {}
268
+ tiles: [`/raster/{z}/{x}/{y}.webp`], // Shouldn't encode the {}
269
269
  },
270
270
  test_vector: {
271
271
  type: 'vector',
@@ -295,8 +295,8 @@ o.spec('LambdaXyz', () => {
295
295
  };
296
296
 
297
297
  const fakeRecord = {
298
- id: 'st_topolike_production',
299
- name: 'topolike',
298
+ id: 'st_topographic_production',
299
+ name: 'topographic',
300
300
  style: fakeStyle,
301
301
  };
302
302
 
@@ -305,7 +305,7 @@ o.spec('LambdaXyz', () => {
305
305
  const res = await handleRequest(request);
306
306
  o(res.status).equals(200);
307
307
  o(res.header('content-type')).equals('application/json');
308
- o(res.header('cache-control')).equals('max-age=120');
308
+ o(res.header('cache-control')).equals('no-store');
309
309
 
310
310
  const body = Buffer.from(res.body ?? '', 'base64').toString();
311
311
  fakeStyle.sources.basemaps_vector = {
package/src/index.ts CHANGED
@@ -4,6 +4,8 @@ import { Ping, Version } from './routes/api.js';
4
4
  import { Health } from './routes/health.js';
5
5
  import { Tiles } from './routes/tile.js';
6
6
  import { Router } from './router.js';
7
+ import { createHash } from 'crypto';
8
+ import { Imagery } from './routes/imagery.js';
7
9
 
8
10
  const app = new Router();
9
11
 
@@ -11,6 +13,7 @@ app.get('ping', Ping);
11
13
  app.get('health', Health);
12
14
  app.get('version', Version);
13
15
  app.get('tiles', Tiles);
16
+ app.get('imagery', Imagery);
14
17
 
15
18
  let slowTimer: NodeJS.Timer | null = null;
16
19
  export async function handleRequest(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
@@ -21,6 +24,11 @@ export async function handleRequest(req: LambdaHttpRequest): Promise<LambdaHttpR
21
24
 
22
25
  req.set('name', 'LambdaTiler');
23
26
  try {
27
+ const apiKey = Router.apiKey(req);
28
+ if (apiKey != null) {
29
+ const apiKeyHash = createHash('sha256').update(apiKey).digest('base64');
30
+ req.set('api', apiKeyHash);
31
+ }
24
32
  return await app.handle(req);
25
33
  } finally {
26
34
  if (slowTimer) clearTimeout(slowTimer);
@@ -0,0 +1,50 @@
1
+ import { Config } from '@basemaps/config';
2
+ import { fsa } from '@basemaps/shared';
3
+ import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
+ import { createHash } from 'crypto';
5
+ import { promisify } from 'util';
6
+ import { gzip } from 'zlib';
7
+ import { Router } from '../router.js';
8
+ import { TileEtag } from './tile.etag.js';
9
+ import { NotModified } from './tile.js';
10
+
11
+ const gzipP = promisify(gzip);
12
+
13
+ function isAllowedFile(f: string): boolean {
14
+ if (f.endsWith('.geojson')) return true;
15
+ if (f.endsWith('.json')) return true;
16
+ return false;
17
+ }
18
+
19
+ /**
20
+ * Get metadata around the imagery such as the source bounding box or the bounding box of the COGS
21
+ * - /v1/imagery/:imageryId/source.geojson
22
+ * - /v1/imagery/:imageryId/covering.geojson
23
+ */
24
+ export async function Imagery(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
25
+ const { rest } = Router.action(req);
26
+ const [imageryId, requestType] = rest;
27
+ if (!isAllowedFile(requestType)) return new LambdaHttpResponse(404, 'Not found');
28
+
29
+ const imagery = await Config.Imagery.get(imageryId);
30
+ if (imagery == null) return new LambdaHttpResponse(404, 'Not found');
31
+
32
+ const targetPath = fsa.join(imagery.uri, requestType);
33
+
34
+ try {
35
+ const buf = await fsa.read(targetPath);
36
+ const cacheKey = createHash('sha256').update(buf).digest('base64');
37
+
38
+ if (TileEtag.isNotModified(req, cacheKey)) return NotModified;
39
+
40
+ const response = new LambdaHttpResponse(200, 'ok');
41
+ response.header(HttpHeader.ETag, cacheKey);
42
+ response.header(HttpHeader.ContentEncoding, 'gzip');
43
+ response.buffer(await gzipP(buf), 'application/json');
44
+ req.set('bytes', buf.byteLength);
45
+ return response;
46
+ } catch (e) {
47
+ req.log.warn({ targetPath }, 'ImageryMetadata:Failed');
48
+ return new LambdaHttpResponse(404, 'Not found');
49
+ }
50
+ }
@@ -13,6 +13,7 @@ import { TileEtag } from './tile.etag.js';
13
13
  import { Router } from '../router.js';
14
14
  import { ValidateTilePath } from '../validate.js';
15
15
  import { URL } from 'url';
16
+ import { fsa } from '@chunkd/fs';
16
17
 
17
18
  export const TileComposer = new TileMakerSharp(256);
18
19
 
@@ -132,16 +133,16 @@ export const TileRoute = {
132
133
  const sources: Sources = {};
133
134
  for (const [key, value] of Object.entries(style.sources)) {
134
135
  if (value.type === 'vector') {
135
- if (value.url.includes(host)) {
136
- const url = new URL(value.url);
136
+ if (value.url.startsWith('/')) {
137
+ const url = new URL(fsa.join(host, value.url));
137
138
  url.searchParams.set('api', apiKey);
138
139
  value.url = url.toString().replace(/%7B/g, '{').replace(/%7D/g, '}');
139
140
  }
140
141
  } else if (value.type === 'raster' && Array.isArray(value.tiles)) {
141
142
  for (let i = 0; i < value.tiles.length; i++) {
142
143
  const tile = value.tiles[i];
143
- if (tile.includes(host)) {
144
- const url = new URL(tile);
144
+ if (tile.startsWith('/')) {
145
+ const url = new URL(fsa.join(host, tile));
145
146
  url.searchParams.set('api', apiKey);
146
147
  value.tiles[i] = url.toString().replace(/%7B/g, '{').replace(/%7D/g, '}');
147
148
  }
@@ -173,7 +174,7 @@ export const TileRoute = {
173
174
 
174
175
  const response = new LambdaHttpResponse(200, 'ok');
175
176
  response.header(HttpHeader.ETag, cacheKey);
176
- response.header(HttpHeader.CacheControl, 'max-age=120');
177
+ response.header(HttpHeader.CacheControl, 'no-store');
177
178
  response.buffer(data, 'application/json');
178
179
  req.set('bytes', data.byteLength);
179
180
  return response;
@@ -183,10 +184,6 @@ export async function Tiles(req: LambdaHttpRequest): Promise<LambdaHttpResponse>
183
184
  const { rest } = Router.action(req);
184
185
  if (rest.length < 1) return NotFound;
185
186
  const apiKey = Router.apiKey(req);
186
- const apiKeyHash = createHash('sha256')
187
- .update(apiKey ?? '')
188
- .digest('base64');
189
- req.set('api', apiKeyHash);
190
187
  if (!isValidApiKey(apiKey)) return new LambdaHttpResponse(400, 'Invalid API Key');
191
188
 
192
189
  const fileName = rest[rest.length - 1].toLowerCase();