From d460254600f27876bdd5cd484a1b7e58b5a9d984 Mon Sep 17 00:00:00 2001 From: Blayne Chard Date: Thu, 18 Jun 2020 12:03:38 +1200 Subject: [PATCH] feat: support split overview/warp resampling GDAL now supports multiple resampling methods, use bilinear for warping and lanczos for overviews see https://github.com/OSGeo/gdal/issues/2671 --- packages/cli/src/cli/cogify/action.job.ts | 16 +--------------- packages/cli/src/cog/__test__/cog.test.ts | 6 ++++-- packages/cli/src/cog/cog.vrt.ts | 2 +- packages/cli/src/cog/job.ts | 7 +++++-- packages/cli/src/cog/types.ts | 2 +- packages/cli/src/gdal/__test__/gdal.test.ts | 2 +- packages/cli/src/gdal/gdal.config.ts | 21 +++++++++++++++++---- packages/cli/src/gdal/gdal.ts | 7 +++++-- 8 files changed, 35 insertions(+), 28 deletions(-) diff --git a/packages/cli/src/cli/cogify/action.job.ts b/packages/cli/src/cli/cogify/action.job.ts index 5e54111b7..9df6cf143 100644 --- a/packages/cli/src/cli/cogify/action.job.ts +++ b/packages/cli/src/cli/cogify/action.job.ts @@ -7,7 +7,7 @@ import { CommandLineStringParameter, } from '@rushstack/ts-command-line'; import { CogJobFactory, JobCreationContext, MaxConcurrencyDefault } from '../../cog/job'; -import { GdalCogBuilderDefaults, GdalResamplingOptions } from '../../gdal/gdal.config'; +import { GdalCogBuilderDefaults } from '../../gdal/gdal.config'; import { CliId } from '../base.cli'; export class CLiInputData { @@ -43,7 +43,6 @@ export class ActionJobCreate extends CommandLineAction { private source: CLiInputData; private output: CLiInputData; private maxConcurrency: CommandLineIntegerParameter; - private resampling: CommandLineStringParameter; private cutline: CommandLineStringParameter; private cutlineBlend: CommandLineIntegerParameter; private overrideId: CommandLineStringParameter; @@ -90,11 +89,6 @@ export class ActionJobCreate extends CommandLineAction { const targetProjection = ProjectionTileMatrixSet.tryGet(this.targetProjection?.value); if (targetProjection == null) throw new Error('Invalid target-projection'); - const resampling = - this.resampling?.value == null - ? GdalCogBuilderDefaults.resampling - : GdalResamplingOptions[this.resampling?.value]; - const ctx: JobCreationContext = { source, output, @@ -105,7 +99,6 @@ export class ActionJobCreate extends CommandLineAction { quality: this.quality?.value ?? GdalCogBuilderDefaults.quality, id: this.overrideId?.value ?? CliId, projection: Epsg.tryGet(this.sourceProjection?.value), - resampling, }, batch: this.submitBatch?.value, }; @@ -126,13 +119,6 @@ export class ActionJobCreate extends CommandLineAction { required: false, }); - this.resampling = this.defineStringParameter({ - argumentName: 'RESAMPLING', - parameterLongName: '--resampling', - description: 'Resampling method to use', - required: false, - }); - this.cutline = this.defineStringParameter({ argumentName: 'CUTLINE', parameterLongName: '--cutline', diff --git a/packages/cli/src/cog/__test__/cog.test.ts b/packages/cli/src/cog/__test__/cog.test.ts index fa276638c..3b93428ee 100644 --- a/packages/cli/src/cog/__test__/cog.test.ts +++ b/packages/cli/src/cog/__test__/cog.test.ts @@ -42,7 +42,7 @@ o.spec('cog', () => { compression: 'webp', tilingScheme: TilingScheme.Google, projection: Epsg.Google, - resampling: 'bilinear', + resampling: { warp: 'bilinear', overview: 'lanczos' }, blockSize: 512, targetRes: 19.1093, quality: 90, @@ -64,7 +64,9 @@ o.spec('cog', () => { '-co', 'BLOCKSIZE=512', '-co', - 'RESAMPLING=bilinear', + 'WARP_RESAMPLING=bilinear', + '-co', + 'OVERVIEW_RESAMPLING=lanczos', '-co', 'COMPRESS=webp', '-co', diff --git a/packages/cli/src/cog/cog.vrt.ts b/packages/cli/src/cog/cog.vrt.ts index 40cd77d3f..cfcd9b9fc 100644 --- a/packages/cli/src/cog/cog.vrt.ts +++ b/packages/cli/src/cog/cog.vrt.ts @@ -70,7 +70,7 @@ async function buildWarpVrt( warpOpts.push('-srcnodata', String(job.output.nodata), '-dstnodata', String(job.output.nodata)); } if (job.output.resampling) { - warpOpts.push('-r', job.output.resampling); + warpOpts.push('-r', job.output.resampling.warp); } logger.debug({ warpOpts: warpOpts.join(' ') }, 'gdalwarp'); diff --git a/packages/cli/src/cog/job.ts b/packages/cli/src/cog/job.ts index e607f0d66..d2f06cad3 100644 --- a/packages/cli/src/cog/job.ts +++ b/packages/cli/src/cog/job.ts @@ -63,7 +63,10 @@ export interface JobCreationContext { * Resampling method * @Default GdalCogBuilderDefaults.resampling */ - resampling?: GdalCogBuilderOptionsResampling; + resampling?: { + warp: GdalCogBuilderOptionsResampling; + overview: GdalCogBuilderOptionsResampling; + }; }; /** @@ -164,7 +167,7 @@ export const CogJobFactory = { projection: ctx.targetProjection.tms.projection.code, output: { ...output, - resampling: ctx.override?.resampling ?? GdalCogBuilderDefaults.resampling, + resampling: GdalCogBuilderDefaults.resampling, quality: ctx.override?.quality ?? GdalCogBuilderDefaults.quality, cutline: ctx.cutline, nodata: metadata.nodata, diff --git a/packages/cli/src/cog/types.ts b/packages/cli/src/cog/types.ts index 7377d9b3d..148a87d97 100644 --- a/packages/cli/src/cog/types.ts +++ b/packages/cli/src/cog/types.ts @@ -33,7 +33,7 @@ export interface CogJob { /** Folder/S3 bucket to store the output */ output: { - resampling: GdalCogBuilderOptionsResampling; + resampling: { warp: GdalCogBuilderOptionsResampling; overview: GdalCogBuilderOptionsResampling }; nodata?: number; /** * Quality level to use diff --git a/packages/cli/src/gdal/__test__/gdal.test.ts b/packages/cli/src/gdal/__test__/gdal.test.ts index 2b0247093..2b8713fcc 100644 --- a/packages/cli/src/gdal/__test__/gdal.test.ts +++ b/packages/cli/src/gdal/__test__/gdal.test.ts @@ -9,7 +9,7 @@ o.spec('GdalCogBuilder', () => { o(builder.config.bbox).equals(undefined); o(builder.config.compression).equals('webp'); - o(builder.config.resampling).equals('bilinear'); + o(builder.config.resampling).deepEquals({ warp: 'bilinear', overview: 'lanczos' }); o(builder.config.blockSize).equals(512); o(builder.config.alignmentLevels).equals(1); diff --git a/packages/cli/src/gdal/gdal.config.ts b/packages/cli/src/gdal/gdal.config.ts index 887bd969c..f6c3f11ff 100644 --- a/packages/cli/src/gdal/gdal.config.ts +++ b/packages/cli/src/gdal/gdal.config.ts @@ -37,10 +37,20 @@ export interface GdalCogBuilderOptions { compression: 'webp' | 'jpeg'; /** - * Resampling method to use - * @default 'bilinear' + * Resampling methods to use */ - resampling: GdalCogBuilderOptionsResampling; + resampling: { + /** + * Resampling for warping + * @default 'bilinear' + */ + warp: GdalCogBuilderOptionsResampling; + /** + * Resampling for overview + * @default 'lanczos' + */ + overview: GdalCogBuilderOptionsResampling; + }; /** * Output tile size * @default 512 @@ -56,7 +66,10 @@ export interface GdalCogBuilderOptions { } export const GdalCogBuilderDefaults: GdalCogBuilderOptions = { - resampling: 'bilinear', + resampling: { + warp: 'bilinear', + overview: 'lanczos', + }, compression: 'webp', tilingScheme: TilingScheme.Google, projection: Epsg.Google, diff --git a/packages/cli/src/gdal/gdal.ts b/packages/cli/src/gdal/gdal.ts index 2c93e6fb8..fb02c6945 100644 --- a/packages/cli/src/gdal/gdal.ts +++ b/packages/cli/src/gdal/gdal.ts @@ -106,9 +106,11 @@ export class GdalCogBuilder { // User configured output block size '-co', `BLOCKSIZE=${this.config.blockSize}`, - // User configured resampling method + // Configured resampling methods '-co', - `RESAMPLING=${this.config.resampling}`, + `WARP_RESAMPLING=${this.config.resampling.warp}`, + '-co', + `OVERVIEW_RESAMPLING=${this.config.resampling.overview}`, // User configured compression '-co', `COMPRESS=${this.config.compression}`, @@ -121,6 +123,7 @@ export class GdalCogBuilder { // most of the imagery contains a lot of empty tiles, no need to output them '-co', `SPARSE_OK=YES`, + // Force a target resolution to be better than the imagery not worse '-tr', tr, tr,