Skip to content

Commit d1f5c65

Browse files
committed
Pass backgroud as an parameter for create cog.
1 parent 330249f commit d1f5c65

File tree

3 files changed

+58
-20
lines changed

3 files changed

+58
-20
lines changed

packages/cogify/src/cogify/cli/cli.cog.ts

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Projection, ProjectionLoader, TileId, TileMatrixSet, TileMatrixSets } f
33
import { fsa, LogType, stringToUrlFolder, Tiff } from '@basemaps/shared';
44
import { CliId, CliInfo } from '@basemaps/shared/build/cli/info.js';
55
import { Metrics } from '@linzjs/metrics';
6-
import { command, flag, number, option, optional, restPositionals } from 'cmd-ts';
6+
import { command, flag, number, option, optional, restPositionals, string } from 'cmd-ts';
77
import { mkdir, rm } from 'fs/promises';
88
import { tmpdir } from 'os';
99
import pLimit from 'p-limit';
@@ -18,12 +18,22 @@ import { getLogger, logArguments } from '../../log.js';
1818
import { gdalBuildCog, gdalBuildVrt, gdalBuildVrtWarp, gdalCreate } from '../gdal.command.js';
1919
import { GdalRunner } from '../gdal.runner.js';
2020
import { Url, UrlArrayJsonFile } from '../parsers.js';
21-
import { CogifyCreationOptions, CogifyStacItem, getCutline, getSources } from '../stac.js';
21+
import { Background, CogifyCreationOptions, CogifyStacItem, getCutline, getSources } from '../stac.js';
2222

2323
function extractSourceFiles(item: CogifyStacItem, baseUrl: URL): URL[] {
2424
return item.links.filter((link) => link.rel === 'linz_basemaps:source').map((link) => new URL(link.href, baseUrl));
2525
}
2626

27+
function parseBackgroud(background: string): Background {
28+
const parts = background.split(',').map((f) => {
29+
const value = parseInt(f);
30+
if (value < 0 || value > 255) throw new Error('Invalid rgba number');
31+
return value;
32+
});
33+
if (parts.length !== 4) throw new Error('Invalid background format');
34+
return { r: parts[0], g: parts[1], b: parts[2], alpha: parts[3] };
35+
}
36+
2737
const Collections = new Map<string, Promise<StacCollection>>();
2838

2939
export interface CogItem {
@@ -72,6 +82,11 @@ export const BasemapsCogifyCreateCommand = command({
7282
defaultValue: () => 4,
7383
defaultValueIsSerializable: true,
7484
}),
85+
background: option({
86+
type: optional(string),
87+
long: 'background',
88+
description: 'Background rbga color to fill empty space in the COG' + 'Format: "r,g,b,a" eg "255,0,0,0.5"',
89+
}),
7590
docker: flag({ long: 'docker', description: 'Run GDAL inside docker container' }),
7691
fromFile: option({
7792
type: optional(UrlArrayJsonFile),
@@ -87,7 +102,7 @@ export const BasemapsCogifyCreateCommand = command({
87102
const logger = getLogger(this, args);
88103

89104
if (args.docker) process.env['GDAL_DOCKER'] = '1';
90-
105+
const background = args.background ? parseBackgroud(args.background) : undefined;
91106
const paths = args.fromFile != null ? args.path.concat(args.fromFile) : args.path;
92107

93108
const toCreate = await Promise.all(paths.map(async (p) => loadItem(p, logger)));
@@ -158,6 +173,9 @@ export const BasemapsCogifyCreateCommand = command({
158173
const options = item.properties['linz_basemaps:options'];
159174
const tileId = TileId.fromTile(options.tile);
160175

176+
// Forces background if defined.
177+
if (background) options.background = background;
178+
161179
// Location to where the tiff should be stored
162180
const tiffPath = new URL(tileId + '.tiff', url);
163181
const itemStacPath = new URL(tileId + '.json', url);
@@ -332,22 +350,28 @@ async function createCog(ctx: CogCreationContext): Promise<URL> {
332350
);
333351
await new GdalRunner(vrtWarpCommand).run(logger);
334352

335-
// Create a tiff file covering the whole world in sea blue
336-
const gdalCreateCommand = gdalCreate(new URL(`${tileId}-bg.tiff`, ctx.tempFolder), options);
337-
await new GdalRunner(gdalCreateCommand).run(logger);
338-
339-
// Create a vrt layering with the sea blue behind the warp VRT
340-
const vrtMergeCommand = gdalBuildVrt(new URL(`${tileId}-merged.vrt`, ctx.tempFolder), [
341-
gdalCreateCommand.output,
342-
vrtWarpCommand.output,
343-
]);
344-
await new GdalRunner(vrtMergeCommand).run(logger);
345-
346-
// Create the COG from the warped vrt
347-
const cogCreateCommand = gdalBuildCog(new URL(`${tileId}.tiff`, ctx.tempFolder), vrtMergeCommand.output, options);
348-
await new GdalRunner(cogCreateCommand).run(logger);
349-
350-
return cogCreateCommand.output;
353+
if (options.background) {
354+
// Create a tiff with background to fill the empty space in the target cog
355+
const gdalCreateCommand = gdalCreate(new URL(`${tileId}-bg.tiff`, ctx.tempFolder), options);
356+
await new GdalRunner(gdalCreateCommand).run(logger);
357+
358+
// Create a vrt layering with the backgroud tiff
359+
const vrtMergeCommand = gdalBuildVrt(new URL(`${tileId}-merged.vrt`, ctx.tempFolder), [
360+
gdalCreateCommand.output,
361+
vrtWarpCommand.output,
362+
]);
363+
await new GdalRunner(vrtMergeCommand).run(logger);
364+
365+
// Create the COG from the merged Vrt with background
366+
const cogCreateCommand = gdalBuildCog(new URL(`${tileId}.tiff`, ctx.tempFolder), vrtMergeCommand.output, options);
367+
await new GdalRunner(cogCreateCommand).run(logger);
368+
return cogCreateCommand.output;
369+
} else {
370+
// Create the COG from the warped vrt without background
371+
const cogCreateCommand = gdalBuildCog(new URL(`${tileId}.tiff`, ctx.tempFolder), vrtWarpCommand.output, options);
372+
await new GdalRunner(cogCreateCommand).run(logger);
373+
return cogCreateCommand.output;
374+
}
351375
}
352376

353377
/**

packages/cogify/src/cogify/gdal.command.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ export function gdalCreate(targetTiff: URL, opt: CogifyCreationOptions): GdalCom
104104
if (tileMatrix == null) throw new Error('Unable to find tileMatrix: ' + cfg.tileMatrix);
105105

106106
const bounds = tileMatrix.tileToSourceBounds({ x: 0, y: 0, z: 0 });
107+
const bg = opt.background;
108+
if (bg == null) throw new Error('Background color is required');
107109

108110
return {
109111
command: 'gdal_create',
@@ -112,7 +114,7 @@ export function gdalCreate(targetTiff: URL, opt: CogifyCreationOptions): GdalCom
112114
['-of', 'GTiff'],
113115
['-outsize', 20, 20],
114116
['-bands', '4'],
115-
['-burn', '208 231 244 255'], // this is the color
117+
['-burn', `${bg.r} ${bg.g} ${bg.b} ${bg.alpha}`], // this is the color
116118
['-a_srs', tileMatrix.projection.toEpsgString()],
117119
['-a_ullr', bounds.x, bounds.bottom, bounds.right, bounds.y],
118120
['-co', 'COMPRESS=LZW'],

packages/cogify/src/cogify/stac.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import { createHash } from 'node:crypto';
22

33
import { Tile } from '@basemaps/geo';
44
import { StacCollection, StacItem, StacLink } from 'stac-ts';
5+
export interface Background {
6+
r: number;
7+
g: number;
8+
b: number;
9+
alpha: number;
10+
}
511

612
export interface CogifyCreationOptions {
713
/** Preset GDAL config to use */
@@ -56,7 +62,13 @@ export interface CogifyCreationOptions {
5662
* @default 'lanczos'
5763
*/
5864
overviewResampling?: GdalResampling;
65+
66+
/**
67+
* Background to fill the cog empty space with alpha 0
68+
*/
69+
background?: Background;
5970
}
71+
6072
export type GdalResampling = 'nearest' | 'bilinear' | 'cubic' | 'cubicspline' | 'lanczos' | 'average' | 'mode';
6173

6274
export type CogifyStacCollection = StacCollection;

0 commit comments

Comments
 (0)