Skip to content

Commit

Permalink
feat(cli): make cog with aligned level, stop using MaxPixelWitdh to c…
Browse files Browse the repository at this point in the history
…alculate zoom levels. BM-703 (#2571)

* Add minZoom to the make cog cli

* Covert zoom level

* Calucation cog zoom by aligned levels

* Fix the tests.

* test commits

* Revert "test commits"

This reverts commit be5cbdc.

* Remove the MaxImagePixelWidth constant
  • Loading branch information
Wentao-Kuang authored Nov 14, 2022
1 parent 7c21ca0 commit 3a15f37
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 50 deletions.
17 changes: 11 additions & 6 deletions packages/cli/src/cli/cogify/action.make.cog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class CommandMakeCog extends CommandLineAction {
private target: CommandLineStringParameter;
private cutline: CommandLineStringParameter;
private blend: CommandLineIntegerParameter;
private maxPixelWidth: CommandLineIntegerParameter;
private alignedLevel: CommandLineIntegerParameter;
private output: CommandLineStringParameter;
private aws: CommandLineFlagParameter;

Expand Down Expand Up @@ -85,10 +85,10 @@ export class CommandMakeCog extends CommandLineAction {
description: 'Cutline blend',
required: false,
});
this.maxPixelWidth = this.defineIntegerParameter({
argumentName: 'MAX_PIXEL_WIDTH',
parameterLongName: '--max-pixel',
description: 'Maximum Pixel Width for the cogs',
this.alignedLevel = this.defineIntegerParameter({
argumentName: 'ALIGNED_LEVEL',
parameterLongName: '--aligned-level',
description: 'Aligned level between resolution and cog',
required: false,
});
this.output = this.defineStringParameter({
Expand Down Expand Up @@ -185,7 +185,12 @@ export class CommandMakeCog extends CommandLineAction {

const ctx: JobCreationContext = {
imageryName,
override: { id, projection: Epsg.Nztm2000, resampling, maxImageSize: this.maxPixelWidth.value },
override: {
id,
projection: Epsg.Nztm2000,
resampling,
alignedLevel: this.alignedLevel.value,
},
outputLocation: this.aws.value
? await this.findLocation(`s3://${bucket}/`)
: { type: 'local' as const, path: '.' },
Expand Down
68 changes: 42 additions & 26 deletions packages/cli/src/cog/__tests__/cutline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MultiPolygon } from '@linzjs/geojson';
import o from 'ospec';
import path from 'path';
import url from 'url';
import { AlignedLevel } from '../constants.js';
import { Cutline, polyContainsBounds } from '../cutline.js';
import { SourceMetadata } from '../types.js';
import { SourceTiffTestHelper } from './source.tiff.testhelper.js';
Expand Down Expand Up @@ -163,11 +164,14 @@ o.spec('cutline', () => {

o('full-extent 3857', () => {
const cutline = new Cutline(GoogleTms);
const covering = cutline.optimizeCovering({
projection: EpsgCode.Google,
bounds: [{ ...GoogleTms.extent, name: 'gebco' }],
resZoom: 5,
} as SourceMetadata);
const covering = cutline.optimizeCovering(
{
projection: EpsgCode.Google,
bounds: [{ ...GoogleTms.extent, name: 'gebco' }],
resZoom: 5,
} as SourceMetadata,
4,
);

o(round(covering, 4)).deepEquals([
{
Expand Down Expand Up @@ -207,7 +211,7 @@ o.spec('cutline', () => {
const covering = cutline.optimizeCovering({
projection: EpsgCode.Nztm2000,
bounds,
resZoom: 14,
resZoom: 7 + AlignedLevel,
} as SourceMetadata);

o(covering[4]).deepEquals({
Expand Down Expand Up @@ -253,11 +257,14 @@ o.spec('cutline', () => {
const bounds = [tiff1, tiff2];
const cutline = new Cutline(GoogleTms, await Cutline.loadCutline(testDir + '/kapiti.geojson'), 500);

const covering = cutline.optimizeCovering({
projection: EpsgCode.Nztm2000,
bounds,
resZoom: 22,
} as SourceMetadata);
const covering = cutline.optimizeCovering(
{
projection: EpsgCode.Nztm2000,
bounds,
resZoom: 22,
} as SourceMetadata,
8,
);

o(round(cutline.clipPoly, 4)).deepEquals([
[
Expand Down Expand Up @@ -298,11 +305,14 @@ o.spec('cutline', () => {
},
];

const covering = cutline.optimizeCovering({
projection: EpsgCode.Nztm2000,
bounds,
resZoom: 22,
} as SourceMetadata);
const covering = cutline.optimizeCovering(
{
projection: EpsgCode.Nztm2000,
bounds,
resZoom: 22,
} as SourceMetadata,
8,
);

o(round(cutline.clipPoly, 4)).deepEquals([
[
Expand All @@ -328,22 +338,28 @@ o.spec('cutline', () => {
o('low res', () => {
const cutline = new Cutline(GoogleTms);

const covering = cutline.optimizeCovering({
projection: EpsgCode.Nztm2000,
bounds,
resZoom: 13,
} as SourceMetadata);
const covering = cutline.optimizeCovering(
{
projection: EpsgCode.Nztm2000,
bounds,
resZoom: 13,
} as SourceMetadata,
9,
);

o(covering.length).equals(covering.length);
o(covering.map((c) => c.name)).deepEquals(['8-252-159', '8-252-160']);
});

o('hi res', () => {
const covering2 = new Cutline(GoogleTms).optimizeCovering({
projection: EpsgCode.Nztm2000,
bounds,
resZoom: 18,
} as SourceMetadata);
const covering2 = new Cutline(GoogleTms).optimizeCovering(
{
projection: EpsgCode.Nztm2000,
bounds,
resZoom: 18,
} as SourceMetadata,
8,
);

o(covering2.length).equals(covering2.length);

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/cog/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,11 @@ export class CogBuilder {
* @param tiffs list of source imagery to be converted
* @returns List of Tile bounds covering tiffs
*/
async build(tiffs: ChunkSource[], cutline: Cutline, maxImageSize?: number): Promise<CogBuilderMetadata> {
async build(tiffs: ChunkSource[], cutline: Cutline, alignedLevel?: number): Promise<CogBuilderMetadata> {
const metadata = await this.bounds(tiffs);
// Ensure that the projection definition is loaded
await ProjectionLoader.load(metadata.projection);
const files = cutline.optimizeCovering(metadata, maxImageSize);
const files = cutline.optimizeCovering(metadata, alignedLevel);
let union: Bounds | null = null;
for (const bounds of files) {
if (union == null) union = Bounds.fromJson(bounds);
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/cog/cog.stac.job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ export interface JobCreationContext {
projection?: Epsg;

/**
* Override Maximum Image Pixel Size
* Override Default aligned zoom level
*/
maxImageSize?: number;
alignedLevel?: number;

/**
* Resampling method
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/cog/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** Default Maximum desired image size */
export const MaxImagePixelWidth = 256000;
/** Default Aligned levels between resolution zoom level and cog zoom level */
export const AlignedLevel = 6;

/** When a tile has at least this much covering merge it up to parent */
export const CoveringFraction = 0.25;
15 changes: 4 additions & 11 deletions packages/cli/src/cog/cutline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
union,
} from '@linzjs/geojson';
import { FeatureCollection } from 'geojson';
import { CoveringFraction, MaxImagePixelWidth } from './constants.js';
import { AlignedLevel, CoveringFraction } from './constants.js';
import { CogJob, FeatureCollectionWithCrs, SourceMetadata } from './types.js';

/** Padding to always apply to image boundies */
Expand Down Expand Up @@ -155,7 +155,7 @@ export class Cutline {
* Generate an optimized WebMercator tile cover for the supplied source images
* @param sourceMetadata contains images bounds and projection info
*/
optimizeCovering(sourceMetadata: SourceMetadata, maxImageSize: number = MaxImagePixelWidth): NamedBounds[] {
optimizeCovering(sourceMetadata: SourceMetadata, alignedLevel: number = AlignedLevel): NamedBounds[] {
if (this.oneCogCovering) {
const extent = this.tileMatrix.extent.toJson();
return [{ ...extent, name: '0-0-0' }];
Expand All @@ -164,15 +164,8 @@ export class Cutline {

const { resZoom } = sourceMetadata;

// Look for the biggest tile size we are allowed to create.
let minZ = resZoom - 1;
while (
minZ > 0 &&
Projection.getImagePixelWidth(this.tileMatrix, { x: 0, y: 0, z: minZ }, resZoom) < maxImageSize
) {
--minZ;
}
minZ = Math.max(1, minZ + 1);
// Fix the cog Minimum Zoom by the aligned level
const minZ = Math.max(0, resZoom - alignedLevel);

let tiles: Tile[] = [];

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/cog/job.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const CogJobFactory = {
);

const builder = new CogBuilder(ctx.tileMatrix, maxConcurrency, logger, ctx.override?.projection);
const metadata = await builder.build(tiffSource, cutline, ctx.override?.maxImageSize);
const metadata = await builder.build(tiffSource, cutline, ctx.override?.alignedLevel);

if (cutline.clipPoly.length === 0) {
// no cutline needed for this imagery set
Expand Down

0 comments on commit 3a15f37

Please sign in to comment.