From 229021de6397a5177eb79b1c4f9af5fc487cb102 Mon Sep 17 00:00:00 2001 From: Tawera Manaena Date: Thu, 12 Dec 2024 13:33:52 +1300 Subject: [PATCH] feat(cogify): force a background color for transparent pixels --- packages/cogify/src/cogify/cli/cli.cog.ts | 17 +++++++++++--- packages/cogify/src/cogify/gdal.command.ts | 26 ++++++++++++++++++++++ packages/cogify/src/cogify/gdal.runner.ts | 2 +- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/packages/cogify/src/cogify/cli/cli.cog.ts b/packages/cogify/src/cogify/cli/cli.cog.ts index 6a80fe0b89..e803432733 100644 --- a/packages/cogify/src/cogify/cli/cli.cog.ts +++ b/packages/cogify/src/cogify/cli/cli.cog.ts @@ -15,7 +15,7 @@ import { CutlineOptimizer } from '../../cutline.js'; import { SourceDownloader } from '../../download.js'; import { HashTransform } from '../../hash.stream.js'; import { getLogger, logArguments } from '../../log.js'; -import { gdalBuildCog, gdalBuildVrt, gdalBuildVrtWarp } from '../gdal.command.js'; +import { gdalBuildCog, gdalBuildVrt, gdalBuildVrtWarp, gdalCreate } from '../gdal.command.js'; import { GdalRunner } from '../gdal.runner.js'; import { Url, UrlArrayJsonFile } from '../parsers.js'; import { CogifyCreationOptions, CogifyStacItem, getCutline, getSources } from '../stac.js'; @@ -332,10 +332,21 @@ async function createCog(ctx: CogCreationContext): Promise { ); await new GdalRunner(vrtWarpCommand).run(logger); - logger?.debug({ tileId }, 'Cog:Create:Tiff'); + // Create a tiff file covering the whole world in sea blue + const gdalCreateCommand = gdalCreate(new URL(`${tileId}-bg.tiff`, ctx.tempFolder), options); + await new GdalRunner(gdalCreateCommand).run(logger); + + // Create a vrt layering with the sea blue behind the warp VRT + const vrtMergeCommand = gdalBuildVrt(new URL(`${tileId}-merged.vrt`, ctx.tempFolder), [ + gdalCreateCommand.output, + vrtWarpCommand.output, + ]); + await new GdalRunner(vrtMergeCommand).run(logger); + // Create the COG from the warped vrt - const cogCreateCommand = gdalBuildCog(new URL(`${tileId}.tiff`, ctx.tempFolder), vrtWarpCommand.output, options); + const cogCreateCommand = gdalBuildCog(new URL(`${tileId}.tiff`, ctx.tempFolder), vrtMergeCommand.output, options); await new GdalRunner(cogCreateCommand).run(logger); + return cogCreateCommand.output; } diff --git a/packages/cogify/src/cogify/gdal.command.ts b/packages/cogify/src/cogify/gdal.command.ts index 68653d4e2e..04c30acb83 100644 --- a/packages/cogify/src/cogify/gdal.command.ts +++ b/packages/cogify/src/cogify/gdal.command.ts @@ -97,3 +97,29 @@ export function gdalBuildCog(targetTiff: URL, sourceVrt: URL, opt: CogifyCreatio .map(String), }; } + +export function gdalCreate(targetTiff: URL, opt: CogifyCreationOptions): GdalCommand { + const cfg = { ...Presets[opt.preset], ...opt }; + const tileMatrix = TileMatrixSets.find(cfg.tileMatrix); + if (tileMatrix == null) throw new Error('Unable to find tileMatrix: ' + cfg.tileMatrix); + + const bounds = tileMatrix.tileToSourceBounds({ x: 0, y: 0, z: 0 }); + + return { + command: 'gdal_create', + output: targetTiff, + args: [ + ['-of', 'GTiff'], + ['-outsize', 20, 20], + ['-bands', '4'], + ['-burn', '208 231 244 255'], // this is the color + ['-a_srs', tileMatrix.projection.toEpsgString()], + ['-a_ullr', bounds.x, bounds.bottom, bounds.right, bounds.y], + ['-co', 'COMPRESS=LZW'], + urlToString(targetTiff), + ] + .filter((f) => f != null) + .flat() + .map(String), + }; +} diff --git a/packages/cogify/src/cogify/gdal.runner.ts b/packages/cogify/src/cogify/gdal.runner.ts index 7d59770b2d..74dfddd610 100644 --- a/packages/cogify/src/cogify/gdal.runner.ts +++ b/packages/cogify/src/cogify/gdal.runner.ts @@ -8,7 +8,7 @@ export interface GdalCommand { /** Output file location */ output: URL; /** GDAL command to use */ - command: 'gdalwarp' | 'gdalbuildvrt' | 'gdal_translate'; + command: 'gdal_create' | 'gdalwarp' | 'gdalbuildvrt' | 'gdal_translate'; /** GDAL arguments to use */ args: string[]; }