Skip to content

Commit

Permalink
fix(cogify): resolve conversations
Browse files Browse the repository at this point in the history
  • Loading branch information
tawera-manaena committed Dec 19, 2024
1 parent cb8d975 commit 6b3b67f
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 43 deletions.
10 changes: 5 additions & 5 deletions packages/cogify/src/cogify/cli/cli.cog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,24 +332,24 @@ async function createCog(ctx: CogCreationContext): Promise<URL> {
await new GdalRunner(vrtWarpCommand).run(logger);

if (options.background == null) {
// Create the COG from the warped vrt without background
// Create the COG from the warped vrt without a forced background
const cogCreateCommand = gdalBuildCog(new URL(`${tileId}.tiff`, ctx.tempFolder), vrtWarpCommand.output, options);
await new GdalRunner(cogCreateCommand).run(logger);
return cogCreateCommand.output;
}

// Create a tiff with background to fill the empty space in the target cog
const gdalCreateCommand = gdalCreate(new URL(`${tileId}-bg.tiff`, ctx.tempFolder), options);
// Create a colored background tiff to fill the empty space in the target cog
const gdalCreateCommand = gdalCreate(new URL(`${tileId}-bg.tiff`, ctx.tempFolder), options.background, options);
await new GdalRunner(gdalCreateCommand).run(logger);

// Create a vrt layering with the background tiff
// Create a vrt with the background tiff behind the source file 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 merged Vrt with background
// Create the COG from the merged vrt with a forced background
const cogCreateCommand = gdalBuildCog(new URL(`${tileId}.tiff`, ctx.tempFolder), vrtMergeCommand.output, options);
await new GdalRunner(cogCreateCommand).run(logger);
return cogCreateCommand.output;
Expand Down
22 changes: 5 additions & 17 deletions packages/cogify/src/cogify/cli/cli.cover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { CutlineOptimizer } from '../../cutline.js';
import { getLogger, logArguments } from '../../log.js';
import { Presets } from '../../preset.js';
import { createTileCover, TileCoverContext } from '../../tile.cover.js';
import { Url, UrlFolder } from '../parsers.js';
import { Background, createFileStats } from '../stac.js';
import { Rgba, Url, UrlFolder } from '../parsers.js';
import { createFileStats } from '../stac.js';

const SupportedTileMatrix = [GoogleTms, Nztm2000QuadTms];

Expand All @@ -23,17 +23,6 @@ export function gsdToMeter(gsd: number): number {
return parseFloat(gsd.toFixed(3));
}

// parse background color from string "r,g,b,a"
function parseBackgroud(background: string): Background {
const parts = background.split(',').map((f) => {
const value = parseInt(f);
if (value < 0 || value > 255) throw new Error('Invalid rgba number');
return value;
});
if (parts.length !== 4) throw new Error('Invalid background format');
return { r: parts[0], g: parts[1], b: parts[2], alpha: parts[3] };
}

export const BasemapsCogifyCoverCommand = command({
name: 'cogify-cover',
version: CliInfo.version,
Expand Down Expand Up @@ -79,9 +68,9 @@ export const BasemapsCogifyCoverCommand = command({
description: 'Define the name of the output imagery',
}),
background: option({
type: optional(string),
type: optional(Rgba),
long: 'background',
description: 'Background rbga color to fill empty space in the COG' + 'Format: "r,g,b,a" eg "255,0,0,0.5"',
description: 'Replace all transparent COG pixels with this RGBA hexstring color',
}),
},
async handler(args) {
Expand All @@ -90,7 +79,6 @@ export const BasemapsCogifyCoverCommand = command({

const mem = new ConfigProviderMemory();
metrics.start('imagery:load');
const background = args.background ? parseBackgroud(args.background) : undefined;
const cfg = await initConfigFromUrls(mem, args.paths, args.name);
const imageryLoadTime = metrics.end('imagery:load');
if (cfg.imagery.length === 0) throw new Error('No imagery found');
Expand All @@ -117,7 +105,7 @@ export const BasemapsCogifyCoverCommand = command({
metrics,
cutline,
preset: args.preset,
background,
background: args.background,
targetZoomOffset: args.baseZoomOffset,
};

Expand Down
29 changes: 23 additions & 6 deletions packages/cogify/src/cogify/gdal.command.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RGBA } from '@basemaps/config/src/color.js';
import { Epsg, EpsgCode, TileMatrixSets } from '@basemaps/geo';
import { urlToString } from '@basemaps/shared';

Expand Down Expand Up @@ -98,23 +99,39 @@ export function gdalBuildCog(targetTiff: URL, sourceVrt: URL, opt: CogifyCreatio
};
}

export function gdalCreate(targetTiff: URL, opt: CogifyCreationOptions): GdalCommand {
/**
* Creates an empty tiff where all pixel values are set to the given color.
* Used to force a background so that there are no empty pixels in the final COG.
*
* @param targetTiff the file path and name for the created tiff
* @param color the color to set all pixel values
* @param opt a CogifyCreationOptions object
*
* @returns a 'gdal_create' GdalCommand object
*/
export function gdalCreate(targetTiff: URL, color: RGBA, 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(opt.tile);
const bg = opt.background;
if (bg == null) throw new Error('Background color is required');
const bounds = tileMatrix.tileToSourceBounds(cfg.tile);
const pixelScale = tileMatrix.pixelScale(cfg.zoomLevel);
const size = Math.round(bounds.width / pixelScale);

// if the value of 'size' is not a power of 2
if ((Math.log(size) / Math.log(size)) % 1 !== 0) {
throw new Error('Size did not compute to a power of 2');
}

return {
command: 'gdal_create',
output: targetTiff,
args: [
['-of', 'GTiff'],
['-outsize', 4096, 4096],
['-outsize', size, size], // set the size to match that of the final COG
['-bands', '4'],
['-burn', `${bg.r} ${bg.g} ${bg.b} ${bg.alpha}`], // this is the color
['-burn', `${color.r} ${color.g} ${color.b} ${color.alpha}`], // set all pixel values to the given color
['-a_srs', tileMatrix.projection.toEpsgString()],
['-a_ullr', bounds.x, bounds.bottom, bounds.right, bounds.y],
['-co', 'COMPRESS=LZW'],
Expand Down
12 changes: 12 additions & 0 deletions packages/cogify/src/cogify/parsers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { pathToFileURL } from 'node:url';

import { parseRgba, RGBA } from '@basemaps/config/src/color.js';
import { fsa } from '@basemaps/shared';
import { Type } from 'cmd-ts';

/**
* Parse a input parameter as a URL.
*
* If it looks like a file path, it will be converted using `pathToFileURL`.
**/
export const Rgba: Type<string, RGBA> = {
from(str) {
return Promise.resolve(parseRgba(str));
},
};

/**
* Parse a input parameter as a URL.
*
Expand Down
13 changes: 3 additions & 10 deletions packages/cogify/src/cogify/stac.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { createHash } from 'node:crypto';

import { RGBA } from '@basemaps/config/src/color.js';
import { Tile } from '@basemaps/geo';
import { StacCollection, StacItem, StacLink } from 'stac-ts';
export interface Background {
r: number;
g: number;
b: number;
alpha: number;
}

export interface CogifyCreationOptions {
/** Preset GDAL config to use */
Expand Down Expand Up @@ -63,10 +58,8 @@ export interface CogifyCreationOptions {
*/
overviewResampling?: GdalResampling;

/**
* Background to fill the cog empty space with alpha 0
*/
background?: Background;
/** Color with which to replace all transparent COG pixels */
background?: RGBA;
}

export type GdalResampling = 'nearest' | 'bilinear' | 'cubic' | 'cubicspline' | 'lanczos' | 'average' | 'mode';
Expand Down
8 changes: 4 additions & 4 deletions packages/cogify/src/tile.cover.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RGBA } from '@basemaps/config/src/color.js';
import { ConfigImageryTiff } from '@basemaps/config-loader';
import { BoundingBox, Bounds, EpsgCode, Projection, ProjectionLoader, TileId, TileMatrixSet } from '@basemaps/geo';
import { fsa, LogType, urlToString } from '@basemaps/shared';
Expand All @@ -8,7 +9,6 @@ import { GeoJSONPolygon } from 'stac-ts/src/types/geojson.js';

import { createCovering } from './cogify/covering.js';
import {
Background,
CogifyLinkCutline,
CogifyLinkSource,
CogifyStacCollection,
Expand All @@ -33,8 +33,8 @@ export interface TileCoverContext {
logger?: LogType;
/** GDAL configuration preset */
preset: string;
/** Optional configuration for adding background for the target cog */
background?: Background;
/** Optional color with which to replace all transparent COG pixels */
background?: RGBA;
/**
* Override the base zoom to store the output COGS as
*/
Expand Down Expand Up @@ -183,7 +183,7 @@ export async function createTileCover(ctx: TileCoverContext): Promise<TileCoverR
assets: {},
};

// Add background if exists
// Add the background color if it exists
if (ctx.background) item.properties['linz_basemaps:options'].background = ctx.background;

// Add the source imagery as a STAC Link
Expand Down
9 changes: 8 additions & 1 deletion packages/config/src/color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ export function parseHex(str: string): number {
return val;
}

export interface RGBA {
r: number;
g: number;
b: number;
alpha: number;
}

/**
* Parse a hexstring into RGBA
*
* Defaults to 0 if missing values
* @param str string to parse
*/
export function parseRgba(str: string): { r: number; g: number; b: number; alpha: number } {
export function parseRgba(str: string): RGBA {
if (str.startsWith('0x')) str = str.slice(2);
else if (str.startsWith('#')) str = str.slice(1);
if (str.length !== 6 && str.length !== 8) {
Expand Down

0 comments on commit 6b3b67f

Please sign in to comment.