Skip to content

Commit

Permalink
feat(tiler-sharp): allow outputs to customise how output is compressed (
Browse files Browse the repository at this point in the history
#3126)

#### Motivation

TerrainRGB needs to be lossless compressed, this adds the abilities for
pipelines to customise how they create the output eg resizeKernels and
lossless parameters

#### Modification

allows outputs to define resize kernels

#### Checklist

_If not applicable, provide explanation of why._

- [ ] Tests updated
- [ ] Docs updated
- [ ] Issue linked in Title
  • Loading branch information
blacha authored Feb 8, 2024
1 parent 87f152b commit f13b8fb
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 18 deletions.
23 changes: 15 additions & 8 deletions packages/config-loader/src/json/tiff.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ export async function initConfigFromUrls(
targets: URL[],
concurrency = 25,
log?: LogType,
): Promise<{ tileSet: ConfigTileSetRaster; imagery: ConfigImageryTiff[] }> {
): Promise<{ tileSet: ConfigTileSetRaster; tileSets: ConfigTileSetRaster[]; imagery: ConfigImageryTiff[] }> {
const q = pLimit(concurrency);

const imageryConfig: Promise<ConfigImageryTiff>[] = [];
Expand All @@ -451,23 +451,29 @@ export async function initConfigFromUrls(
type: TileSetType.Raster,
layers: [],
outputs: [
{
title: 'TerrainRGB',
name: 'terrain-rgb',
pipeline: [{ type: 'terrain-rgb' }],
output: {
type: 'webp',
lossless: true,
background: { r: 1, g: 134, b: 160, alpha: 1 },
resizeKernel: { in: 'nearest', out: 'nearest' },
},
},
{
title: 'Color ramp',
name: 'color-ramp',
pipeline: [{ type: 'color-ramp' }],
output: { type: 'webp' },
},
{
title: 'TerrainRGB',
name: 'terrain-rgb',
pipeline: [{ type: 'terrain-rgb' }],
output: { type: 'webp', lossless: true },
},
],
};

provider.put(aerialTileSet);
const configs = await Promise.all(imageryConfig);
const tileSets = [aerialTileSet];
for (const cfg of configs) {
if (isRgbOrRgba(cfg)) {
let existingLayer = aerialTileSet.layers.find((l) => l.title === cfg.title);
Expand All @@ -484,10 +490,11 @@ export async function initConfigFromUrls(
}
existingLayer[cfg.projection] = cfg.id;
provider.put(elevationTileSet);
tileSets.push(elevationTileSet);
}
}
// FIXME: this should return all the tile sets that were created
return { tileSet: aerialTileSet, imagery: configs };
return { tileSet: aerialTileSet, tileSets, imagery: configs };
}

/**
Expand Down
7 changes: 7 additions & 0 deletions packages/config/src/config/tile.set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ export interface ConfigTileSetRasterOutput {
* {@link ConfigTileSetRaster.background} if not defined
*/
background?: { r: number; g: number; b: number; alpha: number };

/**
* When scaling tiles in the rendering process what kernel to use
*
* will fall back to {@link ConfigTileSetRaster.background} if not defined
*/
resizeKernel?: { in: TileResizeKernel; out: TileResizeKernel };
};
}

Expand Down
10 changes: 6 additions & 4 deletions packages/lambda-tiler/src/routes/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,14 @@ export async function renderPreview(req: LambdaHttpRequest, ctx: PreviewRenderCo
compositions.push(...result);
}

const tileOutput = ctx.output;
const tileContext = {
layers: compositions,
format: ctx.output.output.type,
lossless: ctx.output.output.lossless,
background: ctx.output.output.background ?? ctx.tileSet.background ?? DefaultBackground,
resizeKernel: DefaultResizeKernel,
pipeline: tileOutput.pipeline,
format: tileOutput.output.type,
lossless: tileOutput.output.lossless,
background: tileOutput.output.background ?? ctx.tileSet.background ?? DefaultBackground,
resizeKernel: tileOutput.output.resizeKernel ?? ctx.tileSet.resizeKernel ?? DefaultResizeKernel,
};

// Load all the tiff tiles and resize/them into the correct locations
Expand Down
2 changes: 1 addition & 1 deletion packages/lambda-tiler/src/routes/tile.xyz.raster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const TileXyzRaster = {
format: tileOutput.output.type,
lossless: tileOutput.output.lossless,
background: tileOutput.output.background ?? tileSet.background ?? DefaultBackground,
resizeKernel: tileSet.resizeKernel ?? DefaultResizeKernel,
resizeKernel: tileOutput.output.resizeKernel ?? tileSet.resizeKernel ?? DefaultResizeKernel,
metrics: req.timer,
});

Expand Down
8 changes: 7 additions & 1 deletion packages/server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ export async function loadConfig(opts: ServerOptions, logger: LogType): Promise<
if ('paths' in opts) {
const mem = new ConfigProviderMemory();
const ret = await initConfigFromUrls(mem, opts.paths);
logger.info({ tileSet: ret.tileSet.name, layers: ret.tileSet.layers.length }, 'TileSet:Loaded');
for (const ts of ret.tileSets) {
logger.info(
{ tileSet: ts.name, layers: ts.layers.length, outputs: ts.outputs?.map((f) => f.name) },
'TileSet:Loaded',
);
}

for (const im of ret.imagery) {
logger.info(
{ imagery: im.uri, title: im.title, tileMatrix: im.tileMatrix, files: im.files.length },
Expand Down
16 changes: 12 additions & 4 deletions packages/tiler-sharp/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,29 @@ export class TileMakerSharp implements TileMaker {
toImage(format: ImageFormat, pipeline: Sharp.Sharp, lossless?: boolean): Promise<Buffer> {
switch (format) {
case 'jpeg':
if (lossless) throw new Error('lossless jpeg is not defined');
return pipeline.jpeg().toBuffer();
case 'png':
return pipeline.png().toBuffer();
case 'webp':
return pipeline.webp({ lossless }).toBuffer();
if (lossless) return pipeline.webp({ lossless: true, quality: 100, alphaQuality: 100 }).toBuffer();
return pipeline.webp().toBuffer();
case 'avif':
if (lossless) throw new Error('lossless avif is not defined');
return pipeline.avif().toBuffer();
default:
throw new Error(`Invalid image format "${format}"`);
}
}

private async getImageBuffer(layers: SharpOverlay[], format: ImageFormat, background: Sharp.RGBA): Promise<Buffer> {
private async getImageBuffer(
layers: SharpOverlay[],
format: ImageFormat,
background: Sharp.RGBA,
lossless?: boolean,
): Promise<Buffer> {
if (layers.length === 0) return this.getEmptyImage(format, background);
return this.toImage(format, this.createImage(background).composite(layers));
return this.toImage(format, this.createImage(background).composite(layers), lossless);
}

public async compose(ctx: TileMakerContext): Promise<{ buffer: Buffer; metrics: Metrics; layers: number }> {
Expand All @@ -104,7 +112,7 @@ export class TileMakerSharp implements TileMaker {
metrics.end('compose:overlay');

metrics.start('compose:compress');
const buffer = await this.getImageBuffer(overlays, ctx.format, ctx.background);
const buffer = await this.getImageBuffer(overlays, ctx.format, ctx.background, ctx.lossless);
metrics.end('compose:compress');

return { buffer, metrics, layers: overlays.length };
Expand Down

0 comments on commit f13b8fb

Please sign in to comment.