Skip to content

Commit

Permalink
feat(cogify): set zoom offsets for cogify to create smaller output fi…
Browse files Browse the repository at this point in the history
…les (#3293)

### Motivation

for some datasets we are overzooming them by a signficiant amount to
align them to a output tile matrix zoom level.

For example a 1M DEM will be overzoomed to z18 0.59m, z17 is 1.19M it is
closer to the 1M but is slightly lower quality.

By adding `--base-zoom-offset=-1` we can force cogify to underzoom some
datasets where to save storage space.

This can also be used with more zoom offsets to create signficantly
smaller output files for a sample dataset `whanganui_2022_0.075m` which
by default creats 20GB of COGs at z21.

By using `-3` or `-4` can greatly reduce the size of the dataset

```
114M    z17_whanganui_2022_0.075m (-4)
477M    z18_whanganui_2022_0.075m (-3)
```
Reducing Webp quality from 90% to 80% quality can additionally reduce
the output file size even further. ~200MB for z18.


### Modifications


Adds a configuration option `--base-zoom-offset` to allow configuration
of the output COGs base zoom level.
Adds a preset webp80 to reduce the quality target for webp from 90% to
80%

### Verification

<!-- TODO: Say how you tested your changes. -->

I have manually created many testing datasets with the new cogify to
validate file sizes and image quality.
  • Loading branch information
blacha authored Jun 24, 2024
1 parent 591e42d commit 259e4f4
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 20 deletions.
12 changes: 7 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 4 additions & 14 deletions packages/cogify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,22 @@
},
"scripts": {
"build": "tsc",
"bundle": "../../scripts/bundle.mjs package.json",
"test": "node --test"
},
"bundle": [
{
"entry": "src/bin.ts",
"minify": false,
"outfile": "dist/index.cjs",
"external": [
"sharp",
"pino-pretty"
]
}
],
"type": "module",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"devDependencies": {
"dependencies": {
"@basemaps/cli": "^7.4.0",
"@basemaps/config": "^7.4.0",
"@basemaps/config-loader": "^7.4.0",
"@basemaps/geo": "^7.4.0",
"@basemaps/shared": "^7.4.0",
"cmd-ts": "^0.12.1",
"p-limit": "^4.0.0",
"p-limit": "^4.0.0"
},
"devDependencies": {
"stac-ts": "^1.0.0"
},
"publishConfig": {
Expand Down
7 changes: 7 additions & 0 deletions packages/cogify/src/cogify/cli/cli.cover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ export const BasemapsCogifyCoverCommand = command({
long: 'tile-matrix',
description: `Output TileMatrix to use [${SupportedTileMatrix.map((f) => f.identifier).join(', ')}]`,
}),
baseZoomOffset: option({
type: optional(number),
long: 'base-zoom-offset',
description:
'Adjust the base zoom level of the output COGS, "-1" reduce the target output resolution by one zoom level',
}),
},
async handler(args) {
const metrics = new Metrics();
Expand Down Expand Up @@ -79,6 +85,7 @@ export const BasemapsCogifyCoverCommand = command({
metrics,
cutline,
preset: args.preset,
targetZoomOffset: args.baseZoomOffset,
};

const res = await createTileCover(ctx);
Expand Down
12 changes: 12 additions & 0 deletions packages/cogify/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ const webP: Preset = {
},
};

const webP80: Preset = {
name: 'webp_80',
options: {
blockSize: CogifyDefaults.blockSize,
compression: CogifyDefaults.compression,
quality: 80,
warpResampling: CogifyDefaults.warpResampling,
overviewResampling: CogifyDefaults.overviewResampling,
},
};

const lerc1mm: Preset = {
name: 'lerc_1mm',
options: {
Expand Down Expand Up @@ -60,6 +71,7 @@ const lzw: Preset = {

export const Presets = {
[webP.name]: webP,
[webP80.name]: webP80,
[lerc1mm.name]: lerc1mm,
[lerc10mm.name]: lerc10mm,
[lzw.name]: lzw,
Expand Down
25 changes: 24 additions & 1 deletion packages/cogify/src/tile.cover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export interface TileCoverContext {
logger?: LogType;
/** GDAL configuration preset */
preset: string;
/**
* Override the base zoom to store the output COGS as
*/
targetZoomOffset?: number;
}
export interface TileCoverResult {
/** Stac collection for the imagery */
Expand All @@ -51,12 +55,31 @@ function getDateTime(ctx: TileCoverContext): { start: string | null; end: string
return { start: null, end: null };
}

/**
* Find a zoom level in the target tile matrix that is at least as good as resolution as `resolution`
*
* for some imagery sets this can mean a large difference in the source to target for example 1M resolution source imagery
* is resampled to 0.59m (z18) rather than the closer 1.19m (z17) `targetZoomOffset` can adjust the resolution
* to a nearer value using `-1` will convert a 1M target from 0.59m (z18) to 1.19m (z17)
*
* This can greatly reduce the size of output tiles
*
* @param tileMatrix target tile tile matrix to use
* @param resolution target resolution
* @param targetZoomOffset override the target base zoom, for instance -1 turns a z18 into z17
* @returns
*/
function getTargetBaseZoom(tileMatrix: TileMatrixSet, resolution: number, targetZoomOffset?: number): number {
if (targetZoomOffset == null) return Projection.getTiffResZoom(tileMatrix, resolution);
return Projection.getTiffResZoom(tileMatrix, resolution) + targetZoomOffset;
}

export async function createTileCover(ctx: TileCoverContext): Promise<TileCoverResult> {
// Ensure we have the projection loaded for the source imagery
await ProjectionLoader.load(ctx.imagery.projection);

// Find the zoom level that is at least as good as the source imagery
const targetBaseZoom = Projection.getTiffResZoom(ctx.tileMatrix, ctx.imagery.gsd);
const targetBaseZoom = getTargetBaseZoom(ctx.tileMatrix, ctx.imagery.gsd, ctx.targetZoomOffset);

// The base zoom is 256x256 pixels at its resolution, we are trying to find a image that is <32k pixels wide/high
// zooming out 7 levels converts a 256x256 image into 32k x 32k image
Expand Down

0 comments on commit 259e4f4

Please sign in to comment.