@basemaps/lambda-tiler 6.33.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.
- package/CHANGELOG.md +47 -0
- package/build/__tests__/config.data.d.ts +2 -1
- package/build/__tests__/config.data.d.ts.map +1 -1
- package/build/__tests__/config.data.js +14 -1
- package/build/__tests__/config.data.js.map +1 -1
- package/build/__tests__/tile.style.json.test.js +17 -3
- package/build/__tests__/tile.style.json.test.js.map +1 -1
- package/build/__tests__/wmts.capability.test.js +17 -0
- package/build/__tests__/wmts.capability.test.js.map +1 -1
- package/build/__tests__/xyz.util.d.ts +1 -1
- package/build/__tests__/xyz.util.d.ts.map +1 -1
- package/build/__tests__/xyz.util.js +2 -2
- package/build/__tests__/xyz.util.js.map +1 -1
- package/build/arcgis/__tests__/arcgis.style.json.test.d.ts +2 -0
- package/build/arcgis/__tests__/arcgis.style.json.test.d.ts.map +1 -0
- package/build/arcgis/__tests__/arcgis.style.json.test.js +131 -0
- package/build/arcgis/__tests__/arcgis.style.json.test.js.map +1 -0
- package/build/arcgis/__tests__/vector.tiler.server.test.d.ts +2 -0
- package/build/arcgis/__tests__/vector.tiler.server.test.d.ts.map +1 -0
- package/build/arcgis/__tests__/vector.tiler.server.test.js +54 -0
- package/build/arcgis/__tests__/vector.tiler.server.test.js.map +1 -0
- package/build/arcgis/arcgis.info.d.ts +3 -0
- package/build/arcgis/arcgis.info.d.ts.map +1 -0
- package/build/arcgis/arcgis.info.js +25 -0
- package/build/arcgis/arcgis.info.js.map +1 -0
- package/build/arcgis/arcgis.style.json.d.ts +9 -0
- package/build/arcgis/arcgis.style.json.d.ts.map +1 -0
- package/build/arcgis/arcgis.style.json.js +75 -0
- package/build/arcgis/arcgis.style.json.js.map +1 -0
- package/build/arcgis/vector.tile.server.d.ts +8 -0
- package/build/arcgis/vector.tile.server.d.ts.map +1 -0
- package/build/arcgis/vector.tile.server.js +73 -0
- package/build/arcgis/vector.tile.server.js.map +1 -0
- package/build/index.d.ts +1 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +15 -4
- package/build/index.js.map +1 -1
- package/build/routes/__tests__/attribution.test.js +3 -2
- package/build/routes/__tests__/attribution.test.js.map +1 -1
- package/build/routes/__tests__/fonts.test.js +46 -29
- package/build/routes/__tests__/fonts.test.js.map +1 -1
- package/build/routes/__tests__/health.test.js +4 -2
- package/build/routes/__tests__/health.test.js.map +1 -1
- package/build/routes/__tests__/sprites.test.js +7 -2
- package/build/routes/__tests__/sprites.test.js.map +1 -1
- package/build/routes/__tests__/tile.json.test.js +24 -2
- package/build/routes/__tests__/tile.json.test.js.map +1 -1
- package/build/routes/__tests__/tile.style.json.test.js +58 -7
- package/build/routes/__tests__/tile.style.json.test.js.map +1 -1
- package/build/routes/__tests__/wmts.test.js +17 -14
- package/build/routes/__tests__/wmts.test.js.map +1 -1
- package/build/routes/__tests__/xyz.test.js +4 -2
- package/build/routes/__tests__/xyz.test.js.map +1 -1
- package/build/routes/attribution.d.ts.map +1 -1
- package/build/routes/attribution.js +20 -10
- package/build/routes/attribution.js.map +1 -1
- package/build/routes/config.d.ts +22 -0
- package/build/routes/config.d.ts.map +1 -0
- package/build/routes/config.js +63 -0
- package/build/routes/config.js.map +1 -0
- package/build/routes/fonts.d.ts +0 -2
- package/build/routes/fonts.d.ts.map +1 -1
- package/build/routes/fonts.js +3 -66
- package/build/routes/fonts.js.map +1 -1
- package/build/routes/health.d.ts.map +1 -1
- package/build/routes/health.js +3 -2
- package/build/routes/health.js.map +1 -1
- package/build/routes/imagery.d.ts.map +1 -1
- package/build/routes/imagery.js +3 -2
- package/build/routes/imagery.js.map +1 -1
- package/build/routes/sprites.d.ts.map +1 -1
- package/build/routes/sprites.js +3 -29
- package/build/routes/sprites.js.map +1 -1
- package/build/routes/tile.json.d.ts.map +1 -1
- package/build/routes/tile.json.js +7 -4
- package/build/routes/tile.json.js.map +1 -1
- package/build/routes/tile.style.json.d.ts +4 -3
- package/build/routes/tile.style.json.d.ts.map +1 -1
- package/build/routes/tile.style.json.js +53 -12
- package/build/routes/tile.style.json.js.map +1 -1
- package/build/routes/tile.wmts.d.ts.map +1 -1
- package/build/routes/tile.wmts.js +7 -4
- package/build/routes/tile.wmts.js.map +1 -1
- package/build/routes/tile.xyz.d.ts.map +1 -1
- package/build/routes/tile.xyz.js +4 -2
- package/build/routes/tile.xyz.js.map +1 -1
- package/build/routes/tile.xyz.raster.d.ts.map +1 -1
- package/build/routes/tile.xyz.raster.js +4 -2
- package/build/routes/tile.xyz.raster.js.map +1 -1
- package/build/util/__test__/config.loader.test.d.ts +2 -0
- package/build/util/__test__/config.loader.test.d.ts.map +1 -0
- package/build/util/__test__/config.loader.test.js +79 -0
- package/build/util/__test__/config.loader.test.js.map +1 -0
- package/build/util/assets.provider.d.ts +24 -0
- package/build/util/assets.provider.d.ts.map +1 -0
- package/build/util/assets.provider.js +69 -0
- package/build/util/assets.provider.js.map +1 -0
- package/build/util/config.cache.d.ts +16 -0
- package/build/util/config.cache.d.ts.map +1 -0
- package/build/util/config.cache.js +40 -0
- package/build/util/config.cache.js.map +1 -0
- package/build/util/config.loader.d.ts +10 -0
- package/build/util/config.loader.d.ts.map +1 -0
- package/build/util/config.loader.js +47 -0
- package/build/util/config.loader.js.map +1 -0
- package/build/util/response.d.ts +2 -1
- package/build/util/response.d.ts.map +1 -1
- package/build/util/response.js +1 -0
- package/build/util/response.js.map +1 -1
- package/build/wmts.capability.d.ts +3 -1
- package/build/wmts.capability.d.ts.map +1 -1
- package/build/wmts.capability.js +5 -15
- package/build/wmts.capability.js.map +1 -1
- package/dist/index.js +68 -68
- package/dist/node_modules/.package-lock.json +4 -4
- package/dist/node_modules/node-abi/abi_registry.json +8 -1
- package/dist/node_modules/node-abi/package.json +1 -1
- package/dist/package-lock.json +8 -8
- package/dist/package.json +1 -1
- package/package.json +8 -9
- package/src/__tests__/config.data.ts +25 -1
- package/src/__tests__/tile.style.json.test.ts +19 -3
- package/src/__tests__/wmts.capability.test.ts +21 -0
- package/src/__tests__/xyz.util.ts +7 -2
- package/src/arcgis/__tests__/arcgis.style.json.test.ts +157 -0
- package/src/arcgis/__tests__/vector.tiler.server.test.ts +70 -0
- package/src/arcgis/arcgis.info.ts +26 -0
- package/src/arcgis/arcgis.style.json.ts +83 -0
- package/src/arcgis/vector.tile.server.ts +81 -0
- package/src/index.ts +18 -5
- package/src/routes/__tests__/attribution.test.ts +4 -2
- package/src/routes/__tests__/fonts.test.ts +54 -32
- package/src/routes/__tests__/health.test.ts +4 -2
- package/src/routes/__tests__/sprites.test.ts +7 -3
- package/src/routes/__tests__/tile.json.test.ts +30 -2
- package/src/routes/__tests__/tile.style.json.test.ts +68 -9
- package/src/routes/__tests__/wmts.test.ts +23 -17
- package/src/routes/__tests__/xyz.test.ts +4 -2
- package/src/routes/attribution.ts +23 -8
- package/src/routes/config.ts +83 -0
- package/src/routes/fonts.ts +4 -64
- package/src/routes/health.ts +4 -2
- package/src/routes/imagery.ts +3 -2
- package/src/routes/sprites.ts +4 -27
- package/src/routes/tile.json.ts +10 -4
- package/src/routes/tile.style.json.ts +58 -12
- package/src/routes/tile.wmts.ts +9 -4
- package/src/routes/tile.xyz.raster.ts +4 -2
- package/src/routes/tile.xyz.ts +5 -2
- package/src/util/__test__/config.loader.test.ts +116 -0
- package/src/util/assets.provider.ts +66 -0
- package/src/util/config.cache.ts +44 -0
- package/src/util/config.loader.ts +50 -0
- package/src/util/response.ts +3 -1
- package/src/wmts.capability.ts +9 -15
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -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 {
|
|
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
|
|
87
|
+
const config = await ConfigLoader.load(req);
|
|
88
|
+
const imagery = await getAllImagery(config, tileSet.layers, [tileMatrix.projection]);
|
|
81
89
|
|
|
82
|
-
const host = await
|
|
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)
|
|
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:
|
|
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
|
|
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
|
+
}
|
package/src/routes/fonts.ts
CHANGED
|
@@ -1,76 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { fsa } from '@chunkd/fs';
|
|
3
|
-
import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
|
|
1
|
+
import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
|
|
4
2
|
import path from 'path';
|
|
5
|
-
import {
|
|
6
|
-
import { Etag } from '../util/etag.js';
|
|
7
|
-
import { NotFound, NotModified } from '../util/response.js';
|
|
3
|
+
import { assetProvider } from '../util/assets.provider.js';
|
|
8
4
|
|
|
9
5
|
interface FontGet {
|
|
10
6
|
Params: { fontStack: string; range: string };
|
|
11
7
|
}
|
|
12
8
|
|
|
13
9
|
export async function fontGet(req: LambdaHttpRequest<FontGet>): Promise<LambdaHttpResponse> {
|
|
14
|
-
const assetLocation = Env.get(Env.AssetLocation);
|
|
15
|
-
if (assetLocation == null) return NotFound();
|
|
16
|
-
|
|
17
10
|
const targetFile = path.join('fonts', req.params.fontStack, req.params.range) + '.pbf';
|
|
18
|
-
|
|
19
|
-
return serveFromCotar(req, assetLocation, targetFile, 'application/x-protobuf');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const filePath = fsa.join(assetLocation, targetFile);
|
|
24
|
-
const buf = await fsa.read(filePath);
|
|
25
|
-
|
|
26
|
-
const cacheKey = Etag.key(buf);
|
|
27
|
-
if (Etag.isNotModified(req, cacheKey)) return NotModified();
|
|
28
|
-
|
|
29
|
-
const response = LambdaHttpResponse.ok().buffer(buf, 'application/x-protobuf');
|
|
30
|
-
response.header(HttpHeader.ETag, cacheKey);
|
|
31
|
-
response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
|
|
32
|
-
if (isGzip(buf)) response.header(HttpHeader.ContentEncoding, 'gzip');
|
|
33
|
-
return response;
|
|
34
|
-
} catch (e: any) {
|
|
35
|
-
if (e.code === 404) return NotFound();
|
|
36
|
-
throw e;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Get the unique name of folders is a path that contain .pbf files */
|
|
41
|
-
export async function getFonts(fontPath: string): Promise<string[]> {
|
|
42
|
-
const fonts = new Set<string>();
|
|
43
|
-
|
|
44
|
-
// TODO use {recursive: false}
|
|
45
|
-
for await (const font of fsa.list(fontPath)) {
|
|
46
|
-
if (!font.endsWith('.pbf')) continue;
|
|
47
|
-
const dirName = path.basename(path.dirname(font)); // TODO this only works for /a/b.pbf and not /a/b/c.pbf
|
|
48
|
-
if (dirName.includes('/')) continue;
|
|
49
|
-
fonts.add(dirName);
|
|
50
|
-
}
|
|
51
|
-
// Ensure the fonts are alphabetical
|
|
52
|
-
return [...fonts].sort();
|
|
11
|
+
return assetProvider.serve(req, targetFile, 'application/x-protobuf');
|
|
53
12
|
}
|
|
54
13
|
|
|
55
14
|
export async function fontList(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
|
|
56
|
-
|
|
57
|
-
if (assetLocation == null) return NotFound();
|
|
58
|
-
|
|
59
|
-
if (assetLocation.endsWith('.tar.co')) return serveFromCotar(req, assetLocation, 'fonts.json', 'application/json');
|
|
60
|
-
|
|
61
|
-
try {
|
|
62
|
-
const filePath = fsa.join(assetLocation, '/fonts');
|
|
63
|
-
const fonts = await getFonts(filePath);
|
|
64
|
-
|
|
65
|
-
const cacheKey = Etag.key(fonts);
|
|
66
|
-
if (Etag.isNotModified(req, cacheKey)) return NotModified();
|
|
67
|
-
|
|
68
|
-
const response = LambdaHttpResponse.ok().buffer(JSON.stringify(fonts), 'application/json');
|
|
69
|
-
response.header(HttpHeader.ETag, cacheKey);
|
|
70
|
-
response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
|
|
71
|
-
return response;
|
|
72
|
-
} catch (e: any) {
|
|
73
|
-
if (e.code === 404) return NotFound();
|
|
74
|
-
throw e;
|
|
75
|
-
}
|
|
15
|
+
return assetProvider.serve(req, 'fonts.json', 'application/json');
|
|
76
16
|
}
|
package/src/routes/health.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
package/src/routes/imagery.ts
CHANGED
|
@@ -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
|
|
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);
|
package/src/routes/sprites.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { Env } from '@basemaps/shared';
|
|
2
1
|
import { fsa } from '@chunkd/fs';
|
|
3
2
|
import path from 'path';
|
|
4
|
-
import {
|
|
5
|
-
import { NotFound
|
|
6
|
-
import {
|
|
7
|
-
import { Etag } from '../util/etag.js';
|
|
3
|
+
import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
|
|
4
|
+
import { NotFound } from '../util/response.js';
|
|
5
|
+
import { assetProvider } from '../util/assets.provider.js';
|
|
8
6
|
|
|
9
7
|
interface SpriteGet {
|
|
10
8
|
Params: {
|
|
@@ -17,31 +15,10 @@ Extensions.set('.png', 'image/png');
|
|
|
17
15
|
Extensions.set('.json', 'application/json');
|
|
18
16
|
|
|
19
17
|
export async function spriteGet(req: LambdaHttpRequest<SpriteGet>): Promise<LambdaHttpResponse> {
|
|
20
|
-
const assetLocation = Env.get(Env.AssetLocation);
|
|
21
|
-
if (assetLocation == null) return NotFound();
|
|
22
|
-
|
|
23
18
|
const extension = path.extname(req.params.spriteName);
|
|
24
19
|
const mimeType = Extensions.get(extension);
|
|
25
20
|
if (mimeType == null) return NotFound();
|
|
26
21
|
|
|
27
22
|
const targetFile = fsa.join('sprites', req.params.spriteName);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
const filePath = fsa.join(assetLocation, targetFile);
|
|
32
|
-
req.set('target', filePath);
|
|
33
|
-
|
|
34
|
-
const buf = await fsa.read(filePath);
|
|
35
|
-
const cacheKey = Etag.key(buf);
|
|
36
|
-
if (Etag.isNotModified(req, cacheKey)) return NotModified();
|
|
37
|
-
|
|
38
|
-
const response = LambdaHttpResponse.ok().buffer(buf, mimeType);
|
|
39
|
-
response.header(HttpHeader.ETag, cacheKey);
|
|
40
|
-
response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
|
|
41
|
-
if (isGzip(buf)) response.header(HttpHeader.ContentEncoding, 'gzip');
|
|
42
|
-
return response;
|
|
43
|
-
} catch (e: any) {
|
|
44
|
-
if (e.code === 404) return NotFound();
|
|
45
|
-
throw e;
|
|
46
|
-
}
|
|
23
|
+
return assetProvider.serve(req, targetFile, mimeType);
|
|
47
24
|
}
|
package/src/routes/tile.json.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GoogleTms, TileJson, TileMatrixSet } from '@basemaps/geo';
|
|
2
|
-
import {
|
|
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
|
|
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 {
|
|
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
|
|
68
|
-
const
|
|
69
|
-
|
|
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);
|
package/src/routes/tile.wmts.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
|
47
|
+
const provider = await config.Provider.get(config.Provider.id('linz'));
|
|
45
48
|
|
|
46
49
|
req.timer.start('imagery:load');
|
|
47
|
-
const imagery = await
|
|
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 {
|
|
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
|
|
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);
|
package/src/routes/tile.xyz.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
|