@basemaps/lambda-tiler 7.7.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 +32 -0
- package/build/__tests__/config.data.js +3 -3
- package/build/__tests__/config.data.js.map +1 -1
- package/build/__tests__/tile.style.json.test.js +13 -12
- 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 +81 -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 +36 -8
- package/build/routes/tile.style.json.js +144 -128
- package/build/routes/tile.style.json.js.map +1 -1
- package/build/routes/tile.xyz.raster.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__/cache.test.d.ts +1 -0
- package/build/util/__test__/cache.test.js +29 -0
- package/build/util/__test__/cache.test.js.map +1 -0
- 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/build/util/source.cache.d.ts +2 -8
- package/build/util/source.cache.js +6 -24
- package/build/util/source.cache.js.map +1 -1
- package/build/util/swapping.lru.d.ts +1 -0
- package/build/util/swapping.lru.js +4 -0
- package/build/util/swapping.lru.js.map +1 -1
- package/package.json +7 -6
- package/src/__tests__/config.data.ts +3 -3
- package/src/__tests__/tile.style.json.test.ts +16 -14
- package/src/routes/__tests__/health.test.ts +46 -22
- package/src/routes/__tests__/tile.style.json.test.ts +91 -0
- package/src/routes/__tests__/xyz.test.ts +18 -0
- package/src/routes/health.ts +129 -21
- package/src/routes/tile.style.json.ts +172 -149
- package/src/routes/tile.xyz.raster.ts +0 -1
- package/src/routes/tile.xyz.vector.ts +10 -6
- package/src/util/__test__/cache.test.ts +36 -0
- package/src/util/__test__/nztm.style.test.ts +100 -0
- package/src/util/nztm.style.ts +44 -0
- package/src/util/source.cache.ts +10 -20
- package/src/util/swapping.lru.ts +5 -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,25 +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
|
|
32
|
+
export declare function setStyleUrls(style: StyleJson, tileMatrix: TileMatrixSet, apiKey: string, config: string | null): void;
|
|
33
|
+
export interface StyleConfig {
|
|
34
|
+
/** Name of the terrain layer */
|
|
35
|
+
terrain?: string | null;
|
|
36
|
+
/** Combine layer with the labels layer */
|
|
37
|
+
labels: boolean;
|
|
38
|
+
}
|
|
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;
|
|
18
48
|
export interface StyleGet {
|
|
19
49
|
Params: {
|
|
20
50
|
styleName: string;
|
|
21
51
|
};
|
|
22
52
|
}
|
|
23
|
-
export declare function tileSetToStyle(req: LambdaHttpRequest<StyleGet>, tileSet: ConfigTileSetRaster, tileMatrix: TileMatrixSet, apiKey: string, terrain?: string): Promise<LambdaHttpResponse>;
|
|
24
|
-
export declare function tileSetOutputToStyle(req: LambdaHttpRequest<StyleGet>, tileSet: ConfigTileSetRaster, tileMatrix: TileMatrixSet, apiKey: string, terrain?: string): Promise<LambdaHttpResponse>;
|
|
25
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,18 +42,18 @@ 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
|
-
if (tileMatrix !== GoogleTms) {
|
|
43
|
-
throw new LambdaHttpResponse(400, `TileMatrix is not supported for the vector source ${value.url}.`);
|
|
44
|
-
}
|
|
45
57
|
value.url = convertRelativeUrl(value.url, tileMatrix, apiKey, config);
|
|
46
58
|
}
|
|
47
59
|
else if ((value.type === 'raster' || value.type === 'raster-dem') && Array.isArray(value.tiles)) {
|
|
@@ -49,52 +61,80 @@ export function convertStyleJson(style, tileMatrix, apiKey, config, layers) {
|
|
|
49
61
|
value.tiles[i] = convertRelativeUrl(value.tiles[i], tileMatrix, apiKey, config);
|
|
50
62
|
}
|
|
51
63
|
}
|
|
52
|
-
sources[key] = value;
|
|
64
|
+
style.sources[key] = value;
|
|
53
65
|
}
|
|
54
|
-
const styleJson = {
|
|
55
|
-
version: 8,
|
|
56
|
-
id: style.id,
|
|
57
|
-
name: style.name,
|
|
58
|
-
sources,
|
|
59
|
-
layers: layers ? layers : style.layers,
|
|
60
|
-
};
|
|
61
|
-
if (style.metadata)
|
|
62
|
-
styleJson.metadata = style.metadata;
|
|
63
66
|
if (style.glyphs)
|
|
64
|
-
|
|
67
|
+
style.glyphs = convertRelativeUrl(style.glyphs, undefined, undefined, config);
|
|
65
68
|
if (style.sprite)
|
|
66
|
-
|
|
67
|
-
if (style.sky)
|
|
68
|
-
styleJson.sky = style.sky;
|
|
69
|
-
return styleJson;
|
|
69
|
+
style.sprite = convertRelativeUrl(style.sprite, undefined, undefined, config);
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Turn on the terrain setting in the style json
|
|
73
|
+
*/
|
|
71
74
|
function setStyleTerrain(style, terrain, tileMatrix) {
|
|
72
75
|
const source = Object.keys(style.sources).find((s) => s === terrain);
|
|
73
76
|
if (source == null)
|
|
74
|
-
throw new LambdaHttpResponse(400, `Terrain: ${terrain}
|
|
77
|
+
throw new LambdaHttpResponse(400, `Terrain: ${terrain} does not exists in the style source.`);
|
|
75
78
|
style.terrain = {
|
|
76
79
|
source,
|
|
77
80
|
exaggeration: DefaultExaggeration[tileMatrix.identifier] ?? DefaultExaggeration[GoogleTms.identifier],
|
|
78
81
|
};
|
|
79
82
|
}
|
|
80
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Merge the "labels" layer into the style json
|
|
85
|
+
*/
|
|
86
|
+
async function setStyleLabels(req, style) {
|
|
81
87
|
const config = await ConfigLoader.load(req);
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
const labels = await config.Style.get('labels');
|
|
89
|
+
if (labels == null) {
|
|
90
|
+
req.log.warn('LabelsStyle:Missing');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const layerId = new Set();
|
|
94
|
+
for (const l of style.layers)
|
|
95
|
+
layerId.add(l.id);
|
|
96
|
+
for (const newLayers of labels.style.layers) {
|
|
97
|
+
if (layerId.has(newLayers.id)) {
|
|
98
|
+
throw new LambdaHttpResponse(400, 'Cannot merge styles with duplicate layerIds: ' + newLayers.id);
|
|
99
|
+
}
|
|
92
100
|
}
|
|
101
|
+
if (style.glyphs == null)
|
|
102
|
+
style.glyphs = labels.style.glyphs;
|
|
103
|
+
if (style.sprite == null)
|
|
104
|
+
style.sprite = labels.style.sprite;
|
|
105
|
+
if (style.sky == null)
|
|
106
|
+
style.sky = labels.style.sky;
|
|
107
|
+
Object.assign(style.sources, labels.style.sources);
|
|
108
|
+
style.layers = style.layers.concat(labels.style.layers);
|
|
93
109
|
}
|
|
94
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Ensure that a "LINZ-Terrain" layer is force added into the output styleJSON source
|
|
112
|
+
*/
|
|
113
|
+
async function ensureTerrain(req, tileMatrix, apiKey, style) {
|
|
114
|
+
const config = await ConfigLoader.load(req);
|
|
115
|
+
const terrain = await config.TileSet.get('elevation');
|
|
116
|
+
if (terrain == null)
|
|
117
|
+
return;
|
|
118
|
+
const configLocation = ConfigLoader.extract(req);
|
|
119
|
+
const elevationQuery = toQueryString({ config: configLocation, api: apiKey, pipeline: 'terrain-rgb' });
|
|
120
|
+
style.sources['LINZ-Terrain'] = {
|
|
121
|
+
type: 'raster-dem',
|
|
122
|
+
tileSize: 256,
|
|
123
|
+
maxzoom: 18, // TODO: this should be configurable based on the elevation layer
|
|
124
|
+
tiles: [convertRelativeUrl(`/v1/tiles/elevation/${tileMatrix.identifier}/{z}/{x}/{y}.png${elevationQuery}`)],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
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);
|
|
95
135
|
const [tileFormat] = Validate.getRequestedFormats(req) ?? ['webp'];
|
|
96
136
|
if (tileFormat == null)
|
|
97
|
-
|
|
137
|
+
throw new LambdaHttpResponse(400, 'Invalid image format');
|
|
98
138
|
const pipeline = Validate.pipeline(tileSet, tileFormat, req.query.get('pipeline'));
|
|
99
139
|
const pipelineName = pipeline?.name === 'rgba' ? undefined : pipeline?.name;
|
|
100
140
|
const configLocation = ConfigLoader.extract(req);
|
|
@@ -102,132 +142,108 @@ export async function tileSetToStyle(req, tileSet, tileMatrix, apiKey, terrain)
|
|
|
102
142
|
const tileUrl = (Env.get(Env.PublicUrlBase) ?? '') +
|
|
103
143
|
`/v1/tiles/${tileSet.name}/${tileMatrix.identifier}/{z}/{x}/{y}.${tileFormat}${query}`;
|
|
104
144
|
const styleId = `basemaps-${tileSet.name}`;
|
|
105
|
-
|
|
145
|
+
return {
|
|
106
146
|
id: ConfigId.prefix(ConfigPrefix.Style, tileSet.name),
|
|
107
147
|
name: tileSet.name,
|
|
108
148
|
version: 8,
|
|
109
149
|
sources: { [styleId]: { type: 'raster', tiles: [tileUrl], tileSize: 256 } },
|
|
110
150
|
layers: [{ id: styleId, type: 'raster', source: styleId }],
|
|
111
151
|
};
|
|
112
|
-
// Ensure elevation for individual tilesets
|
|
113
|
-
await ensureTerrain(req, tileMatrix, apiKey, style);
|
|
114
|
-
// Add terrain in style
|
|
115
|
-
if (terrain)
|
|
116
|
-
setStyleTerrain(style, terrain, tileMatrix);
|
|
117
|
-
const data = Buffer.from(JSON.stringify(style));
|
|
118
|
-
const cacheKey = Etag.key(data);
|
|
119
|
-
if (Etag.isNotModified(req, cacheKey))
|
|
120
|
-
return NotModified();
|
|
121
|
-
const response = new LambdaHttpResponse(200, 'ok');
|
|
122
|
-
response.header(HttpHeader.ETag, cacheKey);
|
|
123
|
-
response.header(HttpHeader.CacheControl, 'no-store');
|
|
124
|
-
response.buffer(data, 'application/json');
|
|
125
|
-
req.set('bytes', data.byteLength);
|
|
126
|
-
return response;
|
|
127
152
|
}
|
|
128
|
-
|
|
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');
|
|
129
159
|
const configLocation = ConfigLoader.extract(req);
|
|
130
|
-
const query = toQueryString({ config: configLocation, api: apiKey });
|
|
131
160
|
const styleId = `basemaps-${tileSet.name}`;
|
|
132
161
|
const sources = {};
|
|
133
162
|
const layers = [];
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
sources[`${styleId}-${output.name}-dem`] = {
|
|
148
|
-
type: 'raster-dem',
|
|
149
|
-
tiles: [tileUrl + `&pipeline=${output.name}`],
|
|
150
|
-
tileSize: 256,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
// Add raster source other outputs
|
|
155
|
-
sources[`${styleId}-${output.name}`] = {
|
|
156
|
-
type: 'raster',
|
|
157
|
-
tiles: [tileUrl + `&pipeline=${output.name}`],
|
|
158
|
-
tileSize: 256,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
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 };
|
|
161
176
|
}
|
|
162
177
|
}
|
|
163
178
|
// Add first raster source as default layer
|
|
164
179
|
for (const source of Object.keys(sources)) {
|
|
165
180
|
if (sources[source].type === 'raster') {
|
|
166
|
-
layers.push({
|
|
167
|
-
id: styleId,
|
|
168
|
-
type: 'raster',
|
|
169
|
-
source,
|
|
170
|
-
});
|
|
181
|
+
layers.push({ id: styleId, type: 'raster', source });
|
|
171
182
|
break;
|
|
172
183
|
}
|
|
173
184
|
}
|
|
174
|
-
|
|
185
|
+
return {
|
|
175
186
|
id: ConfigId.prefix(ConfigPrefix.Style, tileSet.name),
|
|
176
187
|
name: tileSet.name,
|
|
177
188
|
version: 8,
|
|
178
189
|
sources,
|
|
179
190
|
layers,
|
|
180
191
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
response.buffer(data, 'application/json');
|
|
194
|
-
req.set('bytes', data.byteLength);
|
|
195
|
-
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);
|
|
196
204
|
}
|
|
197
205
|
export async function styleJsonGet(req) {
|
|
198
206
|
const apiKey = Validate.apiKey(req);
|
|
199
207
|
const styleName = req.params.styleName;
|
|
200
|
-
const excludeLayers = req.query.getAll('exclude');
|
|
201
|
-
const excluded = new Set(excludeLayers.map((l) => l.toLowerCase()));
|
|
202
208
|
const tileMatrix = TileMatrixSets.find(req.query.get('tileMatrix') ?? GoogleTms.identifier);
|
|
203
209
|
if (tileMatrix == null)
|
|
204
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
|
+
*/
|
|
205
223
|
const terrain = req.query.get('terrain') ?? undefined;
|
|
224
|
+
const labels = Boolean(req.query.get('labels') ?? false);
|
|
225
|
+
req.set('styleConfig', { terrain, labels });
|
|
206
226
|
// Get style Config from db
|
|
207
227
|
const config = await ConfigLoader.load(req);
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
// Were we given a tileset name instead, generated
|
|
212
|
-
const tileSet = await config.TileSet.get(config.TileSet.id(styleName));
|
|
213
|
-
if (tileSet == null)
|
|
214
|
-
return NotFound();
|
|
215
|
-
if (tileSet.type !== TileSetType.Raster)
|
|
216
|
-
return NotFound();
|
|
217
|
-
if (tileSet.outputs)
|
|
218
|
-
return await tileSetOutputToStyle(req, tileSet, tileMatrix, apiKey, terrain);
|
|
219
|
-
else
|
|
220
|
-
return await tileSetToStyle(req, tileSet, tileMatrix, apiKey, terrain);
|
|
221
|
-
}
|
|
222
|
-
// Prepare sources and add linz source
|
|
223
|
-
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);
|
|
224
231
|
// Ensure elevation for style json config
|
|
225
232
|
// TODO: We should remove this after adding terrain source into style configs. PR-916
|
|
226
|
-
await ensureTerrain(req, tileMatrix, apiKey,
|
|
233
|
+
await ensureTerrain(req, tileMatrix, apiKey, targetStyle);
|
|
227
234
|
// Add terrain in style
|
|
228
235
|
if (terrain)
|
|
229
|
-
setStyleTerrain(
|
|
230
|
-
|
|
236
|
+
setStyleTerrain(targetStyle, terrain, tileMatrix);
|
|
237
|
+
if (labels)
|
|
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));
|
|
231
247
|
const cacheKey = Etag.key(data);
|
|
232
248
|
if (Etag.isNotModified(req, cacheKey))
|
|
233
249
|
return NotModified();
|