@basemaps/lambda-tiler 7.9.0 → 7.10.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 +13 -0
- package/build/__tests__/tile.style.json.test.js +13 -7
- package/build/__tests__/tile.style.json.test.js.map +1 -1
- package/build/routes/__tests__/health.test.js +40 -20
- package/build/routes/__tests__/health.test.js.map +1 -1
- package/build/routes/__tests__/tile.style.json.test.js +57 -0
- package/build/routes/__tests__/tile.style.json.test.js.map +1 -1
- package/build/routes/__tests__/xyz.test.js +13 -0
- package/build/routes/__tests__/xyz.test.js.map +1 -1
- package/build/routes/health.d.ts +17 -0
- package/build/routes/health.js +119 -21
- package/build/routes/health.js.map +1 -1
- package/build/routes/tile.style.json.d.ts +35 -13
- package/build/routes/tile.style.json.js +108 -123
- package/build/routes/tile.style.json.js.map +1 -1
- package/build/routes/tile.xyz.vector.js +9 -9
- package/build/routes/tile.xyz.vector.js.map +1 -1
- package/build/util/__test__/nztm.style.test.d.ts +1 -0
- package/build/util/__test__/nztm.style.test.js +87 -0
- package/build/util/__test__/nztm.style.test.js.map +1 -0
- package/build/util/nztm.style.d.ts +12 -0
- package/build/util/nztm.style.js +45 -0
- package/build/util/nztm.style.js.map +1 -0
- package/package.json +7 -6
- package/src/__tests__/tile.style.json.test.ts +16 -7
- package/src/routes/__tests__/health.test.ts +46 -22
- package/src/routes/__tests__/tile.style.json.test.ts +60 -0
- package/src/routes/__tests__/xyz.test.ts +18 -0
- package/src/routes/health.ts +129 -21
- package/src/routes/tile.style.json.ts +131 -145
- package/src/routes/tile.xyz.vector.ts +10 -6
- package/src/util/__test__/nztm.style.test.ts +100 -0
- package/src/util/nztm.style.ts +44 -0
- package/tsconfig.tsbuildinfo +1 -1
package/build/routes/health.js
CHANGED
|
@@ -1,13 +1,47 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
|
+
import { TileSetType } from '@basemaps/config';
|
|
2
3
|
import { GoogleTms, Nztm2000QuadTms } from '@basemaps/geo';
|
|
3
4
|
import { HttpHeader, LambdaHttpResponse } from '@linzjs/lambda';
|
|
5
|
+
import { VectorTile } from '@mapbox/vector-tile';
|
|
6
|
+
import Protobuf from 'pbf';
|
|
4
7
|
import PixelMatch from 'pixelmatch';
|
|
5
8
|
import Sharp from 'sharp';
|
|
9
|
+
import { gunzipSync } from 'zlib';
|
|
6
10
|
import { ConfigLoader } from '../util/config.loader.js';
|
|
11
|
+
import { isGzip } from '../util/cotar.serve.js';
|
|
7
12
|
import { TileXyzRaster } from './tile.xyz.raster.js';
|
|
13
|
+
import { tileXyzVector } from './tile.xyz.vector.js';
|
|
8
14
|
export const TestTiles = [
|
|
9
15
|
{ tileSet: 'health', tileMatrix: GoogleTms, tileType: 'png', tile: { x: 252, y: 156, z: 8 } },
|
|
10
16
|
{ tileSet: 'health', tileMatrix: Nztm2000QuadTms, tileType: 'png', tile: { x: 30, y: 33, z: 6 } },
|
|
17
|
+
{
|
|
18
|
+
tileSet: 'topographic',
|
|
19
|
+
tileMatrix: GoogleTms,
|
|
20
|
+
tileType: 'pbf',
|
|
21
|
+
tile: { x: 1009, y: 641, z: 10 },
|
|
22
|
+
testFeatures: [
|
|
23
|
+
{ layer: 'aeroway', key: 'name', value: 'Wellington Airport' },
|
|
24
|
+
{ layer: 'place', key: 'name', value: 'Wellington' },
|
|
25
|
+
{ layer: 'coastline', key: 'class', value: 'coastline' },
|
|
26
|
+
{ layer: 'landcover', key: 'class', value: 'grass' },
|
|
27
|
+
{ layer: 'poi', key: 'name', value: 'Seatoun Wharf' },
|
|
28
|
+
{ layer: 'transportation', key: 'name', value: 'Mt Victoria Tunnel' },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
tileSet: 'topographic',
|
|
33
|
+
tileMatrix: GoogleTms,
|
|
34
|
+
tileType: 'pbf',
|
|
35
|
+
tile: { x: 62, y: 40, z: 6 },
|
|
36
|
+
testFeatures: [
|
|
37
|
+
{ layer: 'landuse', key: 'name', value: 'Queenstown' },
|
|
38
|
+
{ layer: 'place', key: 'name', value: 'Christchurch' },
|
|
39
|
+
{ layer: 'water', key: 'name', value: 'Tasman Lake' },
|
|
40
|
+
{ layer: 'coastline', key: 'class', value: 'coastline' },
|
|
41
|
+
{ layer: 'landcover', key: 'class', value: 'wood' },
|
|
42
|
+
{ layer: 'transportation', key: 'name', value: 'STATE HIGHWAY 6' },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
11
45
|
];
|
|
12
46
|
const TileSize = 256;
|
|
13
47
|
export async function getTestBuffer(test) {
|
|
@@ -27,6 +61,80 @@ export async function updateExpectedTile(test, newTileData, difference) {
|
|
|
27
61
|
.toBuffer();
|
|
28
62
|
await fs.promises.writeFile(`${expectedFileName}.diff.png`, imgPng);
|
|
29
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Compare and validate the raster test tile from server with pixel match
|
|
66
|
+
*/
|
|
67
|
+
async function validateRasterTile(tileSet, test, req) {
|
|
68
|
+
// Get the parse response tile to raw buffer
|
|
69
|
+
const response = await TileXyzRaster.tile(req, tileSet, test);
|
|
70
|
+
if (response.status !== 200)
|
|
71
|
+
throw new LambdaHttpResponse(500, response.statusDescription);
|
|
72
|
+
if (!Buffer.isBuffer(response._body))
|
|
73
|
+
throw new LambdaHttpResponse(500, 'Not a Buffer response content.');
|
|
74
|
+
const resImgBuffer = await Sharp(response._body).raw().toBuffer();
|
|
75
|
+
// Get test tile to compare
|
|
76
|
+
const testBuffer = await getTestBuffer(test);
|
|
77
|
+
test.buf = testBuffer;
|
|
78
|
+
const testImgBuffer = await Sharp(testBuffer).raw().toBuffer();
|
|
79
|
+
const outputBuffer = Buffer.alloc(testImgBuffer.length);
|
|
80
|
+
const missMatchedPixels = PixelMatch(testImgBuffer, resImgBuffer, outputBuffer, TileSize, TileSize);
|
|
81
|
+
if (missMatchedPixels) {
|
|
82
|
+
/** Uncomment this to overwite the expected files */
|
|
83
|
+
// await updateExpectedTile(test, response._body as Buffer, outputBuffer);
|
|
84
|
+
req.log.error({ missMatchedPixels, projection: test.tileMatrix.identifier, xyz: test.tile }, 'Health:MissMatch');
|
|
85
|
+
throw new LambdaHttpResponse(500, 'TileSet does not match.');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function checkFeatureExists(tile, testFeature) {
|
|
89
|
+
const layer = tile.layers[testFeature.layer];
|
|
90
|
+
for (let i = 0; i < layer.length; i++) {
|
|
91
|
+
const feature = layer.feature(i);
|
|
92
|
+
if (feature.properties[testFeature.key] === testFeature.value)
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Fetch vector tile and decode into mapbox VectorTile
|
|
99
|
+
*/
|
|
100
|
+
export const VectorTileProvider = {
|
|
101
|
+
async getVectorTile(tileSet, test, req) {
|
|
102
|
+
// Get the parse response tile to raw buffer
|
|
103
|
+
const response = await tileXyzVector.tile(req, tileSet, test);
|
|
104
|
+
if (response.status !== 200)
|
|
105
|
+
throw new LambdaHttpResponse(500, response.statusDescription);
|
|
106
|
+
if (!Buffer.isBuffer(response._body))
|
|
107
|
+
throw new LambdaHttpResponse(500, 'Not a Buffer response content.');
|
|
108
|
+
const buffer = isGzip(response._body) ? gunzipSync(response._body) : response._body;
|
|
109
|
+
return new VectorTile(new Protobuf(buffer));
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Check the existence of a feature property in side the vector tile
|
|
114
|
+
*
|
|
115
|
+
* @throws LambdaHttpResponse if any test feature not found from vector tile
|
|
116
|
+
*/
|
|
117
|
+
function featureCheck(tile, testTile) {
|
|
118
|
+
const testTileName = `${testTile.tileSet}-${testTile.tile.x}/${testTile.tile.y}/z${testTile.tile.z}`;
|
|
119
|
+
if (testTile.testFeatures == null) {
|
|
120
|
+
throw new LambdaHttpResponse(500, `No test feature found from testTile: ${testTileName}`);
|
|
121
|
+
}
|
|
122
|
+
for (const testFeature of testTile.testFeatures) {
|
|
123
|
+
if (!checkFeatureExists(tile, testFeature)) {
|
|
124
|
+
throw new LambdaHttpResponse(500, `Failed to validate tile: ${testTileName} for layer: ${testFeature.layer}.`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Health check the test vector tiles that contains all the expected features.
|
|
130
|
+
*
|
|
131
|
+
* @throws LambdaHttpResponse if test tiles not returned or features not exists
|
|
132
|
+
*/
|
|
133
|
+
async function validateVectorTile(tileSet, test, req) {
|
|
134
|
+
// Get the parse response tile to raw buffer
|
|
135
|
+
const tile = await VectorTileProvider.getVectorTile(tileSet, test, req);
|
|
136
|
+
featureCheck(tile, test);
|
|
137
|
+
}
|
|
30
138
|
/**
|
|
31
139
|
* Health request get health TileSets and validate with test TileSets
|
|
32
140
|
* - Valid response from get heath tile request
|
|
@@ -36,28 +144,18 @@ export async function updateExpectedTile(test, newTileData, difference) {
|
|
|
36
144
|
*/
|
|
37
145
|
export async function healthGet(req) {
|
|
38
146
|
const config = await ConfigLoader.load(req);
|
|
39
|
-
const tileSet = await config.TileSet.get(config.TileSet.id('health'));
|
|
40
|
-
if (tileSet == null)
|
|
41
|
-
throw new LambdaHttpResponse(500, 'TileSet: "health" not found');
|
|
42
147
|
for (const test of TestTiles) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const outputBuffer = Buffer.alloc(testImgBuffer.length);
|
|
55
|
-
const missMatchedPixels = PixelMatch(testImgBuffer, resImgBuffer, outputBuffer, TileSize, TileSize);
|
|
56
|
-
if (missMatchedPixels) {
|
|
57
|
-
/** Uncomment this to overwite the expected files */
|
|
58
|
-
// await updateExpectedTile(test, response._body as Buffer, outputBuffer);
|
|
59
|
-
req.log.error({ missMatchedPixels, projection: test.tileMatrix.identifier, xyz: test.tile }, 'Health:MissMatch');
|
|
60
|
-
return new LambdaHttpResponse(500, 'TileSet does not match.');
|
|
148
|
+
const tileSet = await config.TileSet.get(config.TileSet.id(test.tileSet));
|
|
149
|
+
if (tileSet == null)
|
|
150
|
+
throw new LambdaHttpResponse(500, `TileSet: ${test.tileSet} not found`);
|
|
151
|
+
if (tileSet.type === TileSetType.Raster) {
|
|
152
|
+
await validateRasterTile(tileSet, test, req);
|
|
153
|
+
}
|
|
154
|
+
else if (tileSet.type === TileSetType.Vector) {
|
|
155
|
+
await validateVectorTile(tileSet, test, req);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
throw new LambdaHttpResponse(500, `Invalid TileSet type for tileSet ${test.tileSet}`);
|
|
61
159
|
}
|
|
62
160
|
}
|
|
63
161
|
// Return Ok response when all health test passed.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,31 +1,53 @@
|
|
|
1
|
-
import { ConfigTileSetRaster,
|
|
1
|
+
import { ConfigTileSetRaster, StyleJson } from '@basemaps/config';
|
|
2
2
|
import { TileMatrixSet } from '@basemaps/geo';
|
|
3
3
|
import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
|
|
4
4
|
/**
|
|
5
|
-
* Convert relative
|
|
5
|
+
* Convert relative URL into a full hostname URL, converting {tileMatrix} into the provided tileMatrix
|
|
6
|
+
*
|
|
7
|
+
* Will also add query parameters of apiKey and configuration if provided
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* convertRelativeUrl("/v1/tiles/aerial/{tileMatrix}/{z}/{x}/{y}.webp", NZTM2000Quad)
|
|
12
|
+
* "https://basemaps.linz.govt.nz/v1/tiles/aerial/NZTM2000Quad/{z}/{x}/{y}.webp?api=c..."
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
6
15
|
* @param url possible url to update
|
|
7
16
|
* @param apiKey ApiKey to append with ?api= if required
|
|
8
|
-
* @
|
|
17
|
+
* @param tileMatrix replace {tileMatrix} with the tile matrix
|
|
18
|
+
*
|
|
19
|
+
* @returns Updated URL or empty string if url is empty
|
|
9
20
|
*/
|
|
10
21
|
export declare function convertRelativeUrl(url?: string, tileMatrix?: TileMatrixSet, apiKey?: string, config?: string | null): string;
|
|
11
22
|
/**
|
|
12
|
-
*
|
|
23
|
+
* Update the style JSON to have absolute urls to the current host and API Keys where required
|
|
24
|
+
*
|
|
13
25
|
* @param style style to update
|
|
26
|
+
* @param tileMatrix convert the tile matrix to the target tile matrix
|
|
14
27
|
* @param apiKey api key to inject
|
|
15
|
-
* @
|
|
28
|
+
* @param config optional configuration url to use
|
|
29
|
+
* @param layers replace the layers in the style json
|
|
30
|
+
* @returns new style JSON
|
|
16
31
|
*/
|
|
17
|
-
export declare function
|
|
18
|
-
export interface StyleGet {
|
|
19
|
-
Params: {
|
|
20
|
-
styleName: string;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
32
|
+
export declare function setStyleUrls(style: StyleJson, tileMatrix: TileMatrixSet, apiKey: string, config: string | null): void;
|
|
23
33
|
export interface StyleConfig {
|
|
24
34
|
/** Name of the terrain layer */
|
|
25
35
|
terrain?: string | null;
|
|
26
36
|
/** Combine layer with the labels layer */
|
|
27
37
|
labels: boolean;
|
|
28
38
|
}
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Generate a StyleJSON from a tileset
|
|
41
|
+
* @returns
|
|
42
|
+
*/
|
|
43
|
+
export declare function tileSetToStyle(req: LambdaHttpRequest<StyleGet>, tileSet: ConfigTileSetRaster, tileMatrix: TileMatrixSet, apiKey: string): StyleJson;
|
|
44
|
+
/**
|
|
45
|
+
* generate a style from a tile set which has a output
|
|
46
|
+
*/
|
|
47
|
+
export declare function tileSetOutputToStyle(req: LambdaHttpRequest<StyleGet>, tileSet: ConfigTileSetRaster, tileMatrix: TileMatrixSet, apiKey: string): StyleJson;
|
|
48
|
+
export interface StyleGet {
|
|
49
|
+
Params: {
|
|
50
|
+
styleName: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
31
53
|
export declare function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<LambdaHttpResponse>;
|
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
import { ConfigId, ConfigPrefix, TileSetType } from '@basemaps/config';
|
|
1
|
+
import { ConfigId, ConfigPrefix, TileSetType, } from '@basemaps/config';
|
|
2
2
|
import { DefaultExaggeration } from '@basemaps/config/build/config/vector.style.js';
|
|
3
|
-
import { GoogleTms, TileMatrixSets } from '@basemaps/geo';
|
|
3
|
+
import { GoogleTms, Nztm2000QuadTms, TileMatrixSets } from '@basemaps/geo';
|
|
4
4
|
import { Env, toQueryString } from '@basemaps/shared';
|
|
5
5
|
import { HttpHeader, LambdaHttpResponse } from '@linzjs/lambda';
|
|
6
6
|
import { URL } from 'url';
|
|
7
7
|
import { ConfigLoader } from '../util/config.loader.js';
|
|
8
8
|
import { Etag } from '../util/etag.js';
|
|
9
|
+
import { convertStyleToNztmStyle } from '../util/nztm.style.js';
|
|
9
10
|
import { NotFound, NotModified } from '../util/response.js';
|
|
10
11
|
import { Validate } from '../util/validate.js';
|
|
11
12
|
/**
|
|
12
|
-
* Convert relative
|
|
13
|
+
* Convert relative URL into a full hostname URL, converting {tileMatrix} into the provided tileMatrix
|
|
14
|
+
*
|
|
15
|
+
* Will also add query parameters of apiKey and configuration if provided
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* convertRelativeUrl("/v1/tiles/aerial/{tileMatrix}/{z}/{x}/{y}.webp", NZTM2000Quad)
|
|
20
|
+
* "https://basemaps.linz.govt.nz/v1/tiles/aerial/NZTM2000Quad/{z}/{x}/{y}.webp?api=c..."
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
13
23
|
* @param url possible url to update
|
|
14
24
|
* @param apiKey ApiKey to append with ?api= if required
|
|
15
|
-
* @
|
|
25
|
+
* @param tileMatrix replace {tileMatrix} with the tile matrix
|
|
26
|
+
*
|
|
27
|
+
* @returns Updated URL or empty string if url is empty
|
|
16
28
|
*/
|
|
17
29
|
export function convertRelativeUrl(url, tileMatrix, apiKey, config) {
|
|
18
30
|
if (url == null)
|
|
@@ -30,14 +42,17 @@ export function convertRelativeUrl(url, tileMatrix, apiKey, config) {
|
|
|
30
42
|
return fullUrl.toString().replace(/%7B/g, '{').replace(/%7D/g, '}');
|
|
31
43
|
}
|
|
32
44
|
/**
|
|
33
|
-
*
|
|
45
|
+
* Update the style JSON to have absolute urls to the current host and API Keys where required
|
|
46
|
+
*
|
|
34
47
|
* @param style style to update
|
|
48
|
+
* @param tileMatrix convert the tile matrix to the target tile matrix
|
|
35
49
|
* @param apiKey api key to inject
|
|
36
|
-
* @
|
|
50
|
+
* @param config optional configuration url to use
|
|
51
|
+
* @param layers replace the layers in the style json
|
|
52
|
+
* @returns new style JSON
|
|
37
53
|
*/
|
|
38
|
-
export function
|
|
39
|
-
const
|
|
40
|
-
for (const [key, value] of Object.entries(sources)) {
|
|
54
|
+
export function setStyleUrls(style, tileMatrix, apiKey, config) {
|
|
55
|
+
for (const [key, value] of Object.entries(style.sources ?? {})) {
|
|
41
56
|
if (value.type === 'vector') {
|
|
42
57
|
value.url = convertRelativeUrl(value.url, tileMatrix, apiKey, config);
|
|
43
58
|
}
|
|
@@ -46,36 +61,28 @@ export function convertStyleJson(style, tileMatrix, apiKey, config, layers) {
|
|
|
46
61
|
value.tiles[i] = convertRelativeUrl(value.tiles[i], tileMatrix, apiKey, config);
|
|
47
62
|
}
|
|
48
63
|
}
|
|
49
|
-
sources[key] = value;
|
|
64
|
+
style.sources[key] = value;
|
|
50
65
|
}
|
|
51
|
-
const styleJson = {
|
|
52
|
-
version: 8,
|
|
53
|
-
id: style.id,
|
|
54
|
-
name: style.name,
|
|
55
|
-
sources,
|
|
56
|
-
layers: layers ? layers : style.layers,
|
|
57
|
-
};
|
|
58
|
-
if (style.metadata)
|
|
59
|
-
styleJson.metadata = style.metadata;
|
|
60
66
|
if (style.glyphs)
|
|
61
|
-
|
|
67
|
+
style.glyphs = convertRelativeUrl(style.glyphs, undefined, undefined, config);
|
|
62
68
|
if (style.sprite)
|
|
63
|
-
|
|
64
|
-
if (style.sky)
|
|
65
|
-
styleJson.sky = style.sky;
|
|
66
|
-
if (style.terrain)
|
|
67
|
-
styleJson.terrain = style.terrain;
|
|
68
|
-
return styleJson;
|
|
69
|
+
style.sprite = convertRelativeUrl(style.sprite, undefined, undefined, config);
|
|
69
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Turn on the terrain setting in the style json
|
|
73
|
+
*/
|
|
70
74
|
function setStyleTerrain(style, terrain, tileMatrix) {
|
|
71
75
|
const source = Object.keys(style.sources).find((s) => s === terrain);
|
|
72
76
|
if (source == null)
|
|
73
|
-
throw new LambdaHttpResponse(400, `Terrain: ${terrain}
|
|
77
|
+
throw new LambdaHttpResponse(400, `Terrain: ${terrain} does not exists in the style source.`);
|
|
74
78
|
style.terrain = {
|
|
75
79
|
source,
|
|
76
80
|
exaggeration: DefaultExaggeration[tileMatrix.identifier] ?? DefaultExaggeration[GoogleTms.identifier],
|
|
77
81
|
};
|
|
78
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Merge the "labels" layer into the style json
|
|
85
|
+
*/
|
|
79
86
|
async function setStyleLabels(req, style) {
|
|
80
87
|
const config = await ConfigLoader.load(req);
|
|
81
88
|
const labels = await config.Style.get('labels');
|
|
@@ -100,6 +107,9 @@ async function setStyleLabels(req, style) {
|
|
|
100
107
|
Object.assign(style.sources, labels.style.sources);
|
|
101
108
|
style.layers = style.layers.concat(labels.style.layers);
|
|
102
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Ensure that a "LINZ-Terrain" layer is force added into the output styleJSON source
|
|
112
|
+
*/
|
|
103
113
|
async function ensureTerrain(req, tileMatrix, apiKey, style) {
|
|
104
114
|
const config = await ConfigLoader.load(req);
|
|
105
115
|
const terrain = await config.TileSet.get('elevation');
|
|
@@ -110,14 +120,21 @@ async function ensureTerrain(req, tileMatrix, apiKey, style) {
|
|
|
110
120
|
style.sources['LINZ-Terrain'] = {
|
|
111
121
|
type: 'raster-dem',
|
|
112
122
|
tileSize: 256,
|
|
113
|
-
maxzoom: 18,
|
|
123
|
+
maxzoom: 18, // TODO: this should be configurable based on the elevation layer
|
|
114
124
|
tiles: [convertRelativeUrl(`/v1/tiles/elevation/${tileMatrix.identifier}/{z}/{x}/{y}.png${elevationQuery}`)],
|
|
115
125
|
};
|
|
116
126
|
}
|
|
117
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Generate a StyleJSON from a tileset
|
|
129
|
+
* @returns
|
|
130
|
+
*/
|
|
131
|
+
export function tileSetToStyle(req, tileSet, tileMatrix, apiKey) {
|
|
132
|
+
// If the style has outputs defined it has a different process for generating the stylejson
|
|
133
|
+
if (tileSet.outputs)
|
|
134
|
+
return tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey);
|
|
118
135
|
const [tileFormat] = Validate.getRequestedFormats(req) ?? ['webp'];
|
|
119
136
|
if (tileFormat == null)
|
|
120
|
-
|
|
137
|
+
throw new LambdaHttpResponse(400, 'Invalid image format');
|
|
121
138
|
const pipeline = Validate.pipeline(tileSet, tileFormat, req.query.get('pipeline'));
|
|
122
139
|
const pipelineName = pipeline?.name === 'rgba' ? undefined : pipeline?.name;
|
|
123
140
|
const configLocation = ConfigLoader.extract(req);
|
|
@@ -125,140 +142,108 @@ export async function tileSetToStyle(req, tileSet, tileMatrix, apiKey, cfg) {
|
|
|
125
142
|
const tileUrl = (Env.get(Env.PublicUrlBase) ?? '') +
|
|
126
143
|
`/v1/tiles/${tileSet.name}/${tileMatrix.identifier}/{z}/{x}/{y}.${tileFormat}${query}`;
|
|
127
144
|
const styleId = `basemaps-${tileSet.name}`;
|
|
128
|
-
|
|
145
|
+
return {
|
|
129
146
|
id: ConfigId.prefix(ConfigPrefix.Style, tileSet.name),
|
|
130
147
|
name: tileSet.name,
|
|
131
148
|
version: 8,
|
|
132
149
|
sources: { [styleId]: { type: 'raster', tiles: [tileUrl], tileSize: 256 } },
|
|
133
150
|
layers: [{ id: styleId, type: 'raster', source: styleId }],
|
|
134
151
|
};
|
|
135
|
-
// Ensure elevation for individual tilesets
|
|
136
|
-
await ensureTerrain(req, tileMatrix, apiKey, style);
|
|
137
|
-
// Add terrain in style
|
|
138
|
-
if (cfg.terrain)
|
|
139
|
-
setStyleTerrain(style, cfg.terrain, tileMatrix);
|
|
140
|
-
if (cfg.labels)
|
|
141
|
-
await setStyleLabels(req, style);
|
|
142
|
-
const data = Buffer.from(JSON.stringify(convertStyleJson(style, tileMatrix, apiKey, configLocation)));
|
|
143
|
-
const cacheKey = Etag.key(data);
|
|
144
|
-
if (Etag.isNotModified(req, cacheKey))
|
|
145
|
-
return NotModified();
|
|
146
|
-
const response = new LambdaHttpResponse(200, 'ok');
|
|
147
|
-
response.header(HttpHeader.ETag, cacheKey);
|
|
148
|
-
response.header(HttpHeader.CacheControl, 'no-store');
|
|
149
|
-
response.buffer(data, 'application/json');
|
|
150
|
-
req.set('bytes', data.byteLength);
|
|
151
|
-
return response;
|
|
152
152
|
}
|
|
153
|
-
|
|
153
|
+
/**
|
|
154
|
+
* generate a style from a tile set which has a output
|
|
155
|
+
*/
|
|
156
|
+
export function tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey) {
|
|
157
|
+
if (tileSet.outputs == null)
|
|
158
|
+
throw new LambdaHttpResponse(400, 'TileSet does not have any outputs to generate');
|
|
154
159
|
const configLocation = ConfigLoader.extract(req);
|
|
155
|
-
const query = toQueryString({ config: configLocation, api: apiKey });
|
|
156
160
|
const styleId = `basemaps-${tileSet.name}`;
|
|
157
161
|
const sources = {};
|
|
158
162
|
const layers = [];
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
sources[`${styleId}-${output.name}-dem`] = {
|
|
173
|
-
type: 'raster-dem',
|
|
174
|
-
tiles: [tileUrl + `&pipeline=${output.name}`],
|
|
175
|
-
tileSize: 256,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
// Add raster source other outputs
|
|
180
|
-
sources[`${styleId}-${output.name}`] = {
|
|
181
|
-
type: 'raster',
|
|
182
|
-
tiles: [tileUrl + `&pipeline=${output.name}`],
|
|
183
|
-
tileSize: 256,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
163
|
+
for (const output of tileSet.outputs) {
|
|
164
|
+
const format = output.format?.[0] ?? 'webp';
|
|
165
|
+
const urlBase = Env.get(Env.PublicUrlBase) ?? '';
|
|
166
|
+
const query = toQueryString({ config: configLocation, api: apiKey, pipeline: output.name });
|
|
167
|
+
const tileUrl = `${urlBase}/v1/tiles/${tileSet.name}/${tileMatrix.identifier}/{z}/{x}/{y}.${format}${query}`;
|
|
168
|
+
if (output.name === 'terrain-rgb') {
|
|
169
|
+
// Add both raster source and dem raster source for terrain-rgb output
|
|
170
|
+
sources[`${styleId}-${output.name}`] = { type: 'raster', tiles: [tileUrl], tileSize: 256 };
|
|
171
|
+
sources[`${styleId}-${output.name}-dem`] = { type: 'raster-dem', tiles: [tileUrl], tileSize: 256 };
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// Add raster source other outputs
|
|
175
|
+
sources[`${styleId}-${output.name}`] = { type: 'raster', tiles: [tileUrl], tileSize: 256 };
|
|
186
176
|
}
|
|
187
177
|
}
|
|
188
178
|
// Add first raster source as default layer
|
|
189
179
|
for (const source of Object.keys(sources)) {
|
|
190
180
|
if (sources[source].type === 'raster') {
|
|
191
|
-
layers.push({
|
|
192
|
-
id: styleId,
|
|
193
|
-
type: 'raster',
|
|
194
|
-
source,
|
|
195
|
-
});
|
|
181
|
+
layers.push({ id: styleId, type: 'raster', source });
|
|
196
182
|
break;
|
|
197
183
|
}
|
|
198
184
|
}
|
|
199
|
-
|
|
185
|
+
return {
|
|
200
186
|
id: ConfigId.prefix(ConfigPrefix.Style, tileSet.name),
|
|
201
187
|
name: tileSet.name,
|
|
202
188
|
version: 8,
|
|
203
189
|
sources,
|
|
204
190
|
layers,
|
|
205
191
|
};
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if (
|
|
210
|
-
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
response.header(HttpHeader.ETag, cacheKey);
|
|
219
|
-
response.header(HttpHeader.CacheControl, 'no-store');
|
|
220
|
-
response.buffer(data, 'application/json');
|
|
221
|
-
req.set('bytes', data.byteLength);
|
|
222
|
-
return Promise.resolve(response);
|
|
192
|
+
}
|
|
193
|
+
async function generateStyleFromTileSet(req, config, tileSetName, tileMatrix, apiKey) {
|
|
194
|
+
const tileSet = await config.TileSet.get(tileSetName);
|
|
195
|
+
if (tileSet == null)
|
|
196
|
+
throw NotFound();
|
|
197
|
+
if (tileSet.type !== TileSetType.Raster) {
|
|
198
|
+
throw new LambdaHttpResponse(400, 'Only raster tile sets can generate style JSON');
|
|
199
|
+
}
|
|
200
|
+
if (tileSet.outputs)
|
|
201
|
+
return tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey);
|
|
202
|
+
else
|
|
203
|
+
return tileSetToStyle(req, tileSet, tileMatrix, apiKey);
|
|
223
204
|
}
|
|
224
205
|
export async function styleJsonGet(req) {
|
|
225
206
|
const apiKey = Validate.apiKey(req);
|
|
226
207
|
const styleName = req.params.styleName;
|
|
227
|
-
const excludeLayers = req.query.getAll('exclude');
|
|
228
|
-
const excluded = new Set(excludeLayers.map((l) => l.toLowerCase()));
|
|
229
208
|
const tileMatrix = TileMatrixSets.find(req.query.get('tileMatrix') ?? GoogleTms.identifier);
|
|
230
209
|
if (tileMatrix == null)
|
|
231
210
|
return new LambdaHttpResponse(400, 'Invalid tile matrix');
|
|
211
|
+
// Remove layers from the output style json
|
|
212
|
+
const excludeLayers = req.query.getAll('exclude');
|
|
213
|
+
const excluded = new Set(excludeLayers.map((l) => l.toLowerCase()));
|
|
214
|
+
if (excluded.size > 0)
|
|
215
|
+
req.set('excludedLayers', [...excluded]);
|
|
216
|
+
/**
|
|
217
|
+
* Configuration options used for the landing page:
|
|
218
|
+
* "terrain" - force add a terrain layer
|
|
219
|
+
* "labels" - merge the labels style with the current style
|
|
220
|
+
*
|
|
221
|
+
* TODO: (2024-08) this is not a very scalable way of configuring styles, it would be good to provide a styleJSON merge
|
|
222
|
+
*/
|
|
232
223
|
const terrain = req.query.get('terrain') ?? undefined;
|
|
233
224
|
const labels = Boolean(req.query.get('labels') ?? false);
|
|
225
|
+
req.set('styleConfig', { terrain, labels });
|
|
234
226
|
// Get style Config from db
|
|
235
227
|
const config = await ConfigLoader.load(req);
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
if (styleConfig == null) {
|
|
240
|
-
// Were we given a tileset name instead, generated
|
|
241
|
-
const tileSet = await config.TileSet.get(config.TileSet.id(styleName));
|
|
242
|
-
if (tileSet == null)
|
|
243
|
-
return NotFound();
|
|
244
|
-
if (tileSet.type !== TileSetType.Raster)
|
|
245
|
-
return NotFound();
|
|
246
|
-
if (tileSet.outputs)
|
|
247
|
-
return await tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey, { terrain, labels });
|
|
248
|
-
else
|
|
249
|
-
return await tileSetToStyle(req, tileSet, tileMatrix, apiKey, { terrain, labels });
|
|
250
|
-
}
|
|
251
|
-
// Prepare sources and add linz source
|
|
252
|
-
const style = convertStyleJson(styleConfig.style, tileMatrix, apiKey, ConfigLoader.extract(req), styleConfig.style.layers.filter((f) => !excluded.has(f.id.toLowerCase())));
|
|
228
|
+
const styleConfig = await config.Style.get(styleName);
|
|
229
|
+
const styleSource = styleConfig?.style ?? (await generateStyleFromTileSet(req, config, styleName, tileMatrix, apiKey));
|
|
230
|
+
const targetStyle = structuredClone(styleSource);
|
|
253
231
|
// Ensure elevation for style json config
|
|
254
232
|
// TODO: We should remove this after adding terrain source into style configs. PR-916
|
|
255
|
-
await ensureTerrain(req, tileMatrix, apiKey,
|
|
233
|
+
await ensureTerrain(req, tileMatrix, apiKey, targetStyle);
|
|
256
234
|
// Add terrain in style
|
|
257
235
|
if (terrain)
|
|
258
|
-
setStyleTerrain(
|
|
236
|
+
setStyleTerrain(targetStyle, terrain, tileMatrix);
|
|
259
237
|
if (labels)
|
|
260
|
-
await setStyleLabels(req,
|
|
261
|
-
|
|
238
|
+
await setStyleLabels(req, targetStyle);
|
|
239
|
+
// convert sources to full URLS and convert style between projections
|
|
240
|
+
setStyleUrls(targetStyle, tileMatrix, apiKey, ConfigLoader.extract(req));
|
|
241
|
+
if (tileMatrix.identifier === Nztm2000QuadTms.identifier)
|
|
242
|
+
convertStyleToNztmStyle(targetStyle, false);
|
|
243
|
+
// filter out any excluded layers
|
|
244
|
+
if (excluded.size > 0)
|
|
245
|
+
targetStyle.layers = targetStyle.layers.filter((f) => !excluded.has(f.id.toLowerCase()));
|
|
246
|
+
const data = Buffer.from(JSON.stringify(targetStyle));
|
|
262
247
|
const cacheKey = Etag.key(data);
|
|
263
248
|
if (Etag.isNotModified(req, cacheKey))
|
|
264
249
|
return NotModified();
|