From af6f1936d3c8ca99c57d68d5823fa4ae55e74968 Mon Sep 17 00:00:00 2001 From: Geoff Jacobsen Date: Wed, 12 Aug 2020 21:20:53 +1200 Subject: [PATCH] fix(tiler): Use nearest smoothing when down sizing Use lanczos3 when up sizing. This is a fix to reduce artefacts when rendering the topo-50 nztm layer. Allow supplying source order when creating CogJob; needed when ordering is important or a source directory contains more than one imagery set as in the case of topo-50. Acked-by: Geoff Jacobsen --- packages/cli/src/cli/cogify/action.job.ts | 22 +++++++++++++++++-- .../src/__test__/tile.creation.test.ts | 2 +- packages/tiler-sharp/src/index.ts | 9 ++++++-- packages/tiler/src/__test__/tiler.test.ts | 2 +- packages/tiler/src/raster.ts | 2 +- packages/tiler/src/tiler.ts | 2 +- 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/cli/cogify/action.job.ts b/packages/cli/src/cli/cogify/action.job.ts index 3a1570d18..92cacf06e 100644 --- a/packages/cli/src/cli/cogify/action.job.ts +++ b/packages/cli/src/cli/cogify/action.job.ts @@ -1,5 +1,5 @@ import { Epsg, EpsgCode } from '@basemaps/geo'; -import { FileConfig, FileOperator, ProjectionTileMatrixSet } from '@basemaps/shared'; +import { FileConfig, FileOperator, ProjectionTileMatrixSet, FileConfigPath } from '@basemaps/shared'; import { CommandLineAction, CommandLineFlagParameter, @@ -52,6 +52,7 @@ export class ActionJobCreate extends CommandLineAction { private sourceProjection: CommandLineIntegerParameter; private targetProjection: CommandLineIntegerParameter; private oneCog: CommandLineFlagParameter; + private fileList: CommandLineStringParameter; public constructor() { super({ @@ -80,7 +81,7 @@ export class ActionJobCreate extends CommandLineAction { } async onExecute(): Promise { - const source = this.fsConfig(this.source); + const source: FileConfig | FileConfigPath = this.fsConfig(this.source); const output = this.fsConfig(this.output); let cutline = undefined; @@ -104,6 +105,16 @@ export class ActionJobCreate extends CommandLineAction { }; } + const fileListPath = this.fileList?.value; + if (fileListPath != null) { + const fileData = await FileOperator.create(fileListPath).read(fileListPath); + (source as FileConfigPath).files = fileData + .toString() + .trim() + .split('\n') + .map((fn) => source.path + '/' + fn); + } + const ctx: JobCreationContext = { source, output, @@ -197,5 +208,12 @@ export class ActionJobCreate extends CommandLineAction { description: 'ignore target projection window and just produce one big COG.', required: false, }); + + this.fileList = this.defineStringParameter({ + argumentName: 'FILE_LIST', + parameterLongName: '--filelist', + description: 'supply a list of files to use as source imagery', + required: false, + }); } } diff --git a/packages/tiler-sharp/src/__test__/tile.creation.test.ts b/packages/tiler-sharp/src/__test__/tile.creation.test.ts index 674471bc8..9c478ae98 100644 --- a/packages/tiler-sharp/src/__test__/tile.creation.test.ts +++ b/packages/tiler-sharp/src/__test__/tile.creation.test.ts @@ -40,7 +40,7 @@ o.spec('TileCreation', () => { const topLeft = layer0.find((f) => f.source.x == 0 && f.source.y == 0); o(topLeft?.tiff.source.name).equals(tiff.source.name); - o(topLeft?.resize).deepEquals({ width: 32, height: 32 }); + o(topLeft?.resize).deepEquals({ width: 32, height: 32, downsize: false }); o(topLeft?.x).equals(64); o(topLeft?.y).equals(64); }); diff --git a/packages/tiler-sharp/src/index.ts b/packages/tiler-sharp/src/index.ts index f42b0458a..1d9224567 100644 --- a/packages/tiler-sharp/src/index.ts +++ b/packages/tiler-sharp/src/index.ts @@ -7,7 +7,8 @@ function notEmpty(value: T | null | undefined): value is T { } export type SharpOverlay = { input: string | Buffer } & Sharp.OverlayOptions; -const SharpScaleOptions = { fit: Sharp.fit.cover }; +const SharpScaleOptionsDownsize = { fit: Sharp.fit.cover, kernel: Sharp.kernel.nearest }; +const SharpScaleOptionsUpsize = { fit: Sharp.fit.cover, kernel: Sharp.kernel.lanczos3 }; export class TileMakerSharp implements TileMaker { static readonly MaxImageSize = 256 * 2 ** 15; @@ -89,7 +90,11 @@ export class TileMakerSharp implements TileMaker { } if (composition.resize) { - sharp.resize(composition.resize.width, composition.resize.height, SharpScaleOptions); + sharp.resize( + composition.resize.width, + composition.resize.height, + composition.resize.downsize ? SharpScaleOptionsDownsize : SharpScaleOptionsUpsize, + ); } if (composition.crop) { diff --git a/packages/tiler/src/__test__/tiler.test.ts b/packages/tiler/src/__test__/tiler.test.ts index 343d3c16c..87fd7b3f0 100644 --- a/packages/tiler/src/__test__/tiler.test.ts +++ b/packages/tiler/src/__test__/tiler.test.ts @@ -63,7 +63,7 @@ o.spec('tiler.test', () => { y: 0, x: 64, extract: { width: 512, height: 387 }, - resize: { width: 256, height: 194 }, + resize: { width: 256, height: 194, downsize: true }, crop, }); diff --git a/packages/tiler/src/raster.ts b/packages/tiler/src/raster.ts index e59893a13..ceeebddc4 100644 --- a/packages/tiler/src/raster.ts +++ b/packages/tiler/src/raster.ts @@ -28,7 +28,7 @@ export interface Composition { /** Crop the initial bounds */ extract?: Size; /** Resize the image */ - resize?: Size; + resize?: Size & { downsize: boolean }; /** Crop after the resize */ crop?: Bounds; } diff --git a/packages/tiler/src/tiler.ts b/packages/tiler/src/tiler.ts index 97f9cbb54..91fd20905 100644 --- a/packages/tiler/src/tiler.ts +++ b/packages/tiler/src/tiler.ts @@ -110,7 +110,7 @@ export class Tiler { // Often COG tiles do not align to the same size as XYZ Tiles // This will scale the COG tile to the same size as a XYZ if (source.width != target.width || source.height != target.height) { - composition.resize = { width: target.width, height: target.height }; + composition.resize = { width: target.width, height: target.height, downsize: source.width > target.width }; } // If the output XYZ tile needs a piece of a COG tile, extract the speicific