Skip to content

Commit

Permalink
feat(basemaps): Update elevation config to insert the source is 2193 …
Browse files Browse the repository at this point in the history
…layer. BM-1125 (#1139)

#### Motivation
We got several problems and new features for creating pull request for
elevation configs in Basemaps.
1. 2193 link is missing (this is just the source nz-elevation S3 path)
2. category is included but isn’t included in any of the other
configurations
3. minZoom is missing
4. Stop standerize the layer name for elevation config.

#### Modification
1. get the source url from collection.json and put in the elevation
config as 2193 layer.
2. category was only populate to aerial raster imagery config layers. I
have separated all three type of configs and stop populate category for
elevation and vector config layers.
3. Add default `minZoom:9` for elevation.

#### Checklist

_If not applicable, provide explanation of why._

- [ ] Tests updated - Tested with Local debug run
- [ ] Docs updated
- [x] Issue linked in Title
  • Loading branch information
Wentao-Kuang authored Nov 21, 2024
1 parent 0f65f3e commit 37f9030
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 68 deletions.
68 changes: 64 additions & 4 deletions src/commands/basemaps-github/create-pr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,17 @@ export function parseTargetUrl(target: string, offset: 0 | 1): targetInfo {
}
}

/**
* Pass Raster target location with the following format
* s3://linz-basemaps-staging/2193/west-coast_rural_2015-16_0-3m/01F6P21PNQC7D67W5SHQF806Z3/
* s3://linz-basemaps-staging/3857/west-coast_rural_2015-16_0-3m/01ED83TT0ZHKXTPFXEGFJHP2M5/
*/
async function parseRasterTargetInfo(
target: string,
elevation: boolean,
individual: boolean,
): Promise<{ name: string; title: string; epsg: EpsgCode; region: string | undefined }> {
logger.info({ target }, 'CreatePR: Get the layer information from target');
const { bucket, epsg, name } = parseTargetUrl(target, elevation ? 1 : 0);
const { bucket, epsg, name } = parseTargetUrl(target, 0);

assertValidBucket(bucket, validTargetBuckets);

Expand Down Expand Up @@ -132,6 +136,53 @@ async function parseVectorTargetInfo(target: string): Promise<{ name: string; ti
return { name: name, epsg: epsg.code, title };
}

/**
* Pass Elevation target location with the following format and add source location into layer
* s3://linz-basemaps/elevation/3857/kapiti-coast_2021_dem_1m/01HZ5W74E8B1DF2B0MDSKSTSTV/
*/
async function parseElevationTargetInfo(
target: string,
individual: boolean,
): Promise<{ name: string; title: string; epsg: EpsgCode; region: string | undefined; source: string }> {
logger.info({ target }, 'CreatePR: Get the layer information from target');
const { bucket, epsg, name } = parseTargetUrl(target, 1);

assertValidBucket(bucket, validTargetBuckets);

const collectionPath = fsa.join(target, 'collection.json');
const collection = await fsa.readJson<StacCollection>(collectionPath);
if (collection == null) throw new Error(`Failed to get target collection json from ${collectionPath}.`);
const title = collection.title;
if (title == null) throw new Error(`Failed to get imagery title from collection.json.`);

// Validate the source location
const source = collection.links.find((f) => f.rel === 'linz_basemaps:source_collection')?.href;
if (source == null) throw new Error(`Failed to get source url from collection.json.`);
const sourceUrl = new URL(source);
const sourceBucket = sourceUrl.hostname;
assertValidBucket(sourceBucket, validSourceBuckets);

// Try to get the region for individual layers
let region;
if (individual) {
logger.info({ source }, 'CreatePR: Get region for individual imagery');
const regionValue = sourceUrl.pathname.split('/')[1];
if (regionValue) region = regionValue;
else {
logger.warn({ source }, 'CreatePR: Failed to find region and use individual instead.');
region = 'individual';
}
}

return {
name, //TODO: We not standardize elevation layer name for now. And will update all others to match this in future. BM-1133
epsg: epsg.code,
title,
region,
source: source.replace('collection.json', ''),
};
}

export const CommandCreatePRArgs = {
verbose,
target: option({
Expand Down Expand Up @@ -202,9 +253,18 @@ export const basemapsCreatePullRequest = command({
layer.title = info.title;
layer[info.epsg] = target;
}
} else if (configType === ConfigType.Elevation) {
for (const target of targets) {
const info = await parseElevationTargetInfo(target, args.individual);
layer.name = info.name;
layer.title = info.title;
layer[2193] = info.source;
layer[info.epsg] = target;
region = info.region;
}
} else {
for (const target of targets) {
const info = await parseRasterTargetInfo(target, category === Category.Elevation, args.individual);
const info = await parseRasterTargetInfo(target, args.individual);
layer.name = info.name;
layer.title = info.title;
layer[info.epsg] = target;
Expand All @@ -221,7 +281,7 @@ export const basemapsCreatePullRequest = command({
} else if (configType === ConfigType.Raster) {
await git.updateRasterTileSet(layer.name, layer, category, args.individual, region);
} else if (configType === ConfigType.Elevation) {
await git.updateElevationTileSet(layer.name, layer, category, args.individual, region);
await git.updateElevationTileSet(layer.name, layer, args.individual, region);
} else throw new Error(`Invalid Config File target: ${configType}`);
},
});
147 changes: 83 additions & 64 deletions src/commands/basemaps-github/make.cog.github.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DefaultColorRampOutput, DefaultTerrainRgbOutput } from '@basemaps/config';
import { DefaultColorRampOutput, DefaultTerrainRgbOutput, standardizeLayerName } from '@basemaps/config';
import {
ConfigLayer,
ConfigTileSet,
Expand Down Expand Up @@ -32,7 +32,7 @@ export const DefaultCategorySetting: Record<Category, CategorySetting> = {
[Category.Urban]: { minZoom: 14 },
[Category.Rural]: { minZoom: 13 },
[Category.Satellite]: { minZoom: 5 },
[Category.Elevation]: {},
[Category.Elevation]: { minZoom: 9 },
[Category.Scanned]: { minZoom: 0, maxZoom: 32 },
[Category.Other]: {},
[Category.Event]: {},
Expand Down Expand Up @@ -125,7 +125,6 @@ export class MakeCogGithub {
async updateElevationTileSet(
filename: string,
layer: ConfigLayer,
category: Category,
individual: boolean,
region: string | undefined,
): Promise<void> {
Expand All @@ -137,17 +136,7 @@ export class MakeCogGithub {
logger.info({ imagery: this.imagery }, 'GitHub: Get the master TileSet config file');
if (individual) {
if (region == null) region = 'individual';
// Prepare new standalone tileset config
const targetLayer = { ...layer, category, minZoom: 0, maxZoom: 32 };
const tileSet: ConfigTileSetRaster = {
type: TileSetType.Raster,
id: `ts_${layer.name}`,
name: layer.name,
title: layer.title,
category,
layers: [targetLayer],
outputs: [DefaultTerrainRgbOutput, DefaultColorRampOutput],
};
const tileSet = await this.prepareElevationTileSetConfig(layer);
const tileSetPath = fsa.joinAll('config', 'tileset', region, 'elevation', `${layer.name}.json`);
// Github create pull request
await this.createTileSetPullRequest(gh, branch, title, tileSetPath, tileSet);
Expand All @@ -157,14 +146,59 @@ export class MakeCogGithub {
const tileSetContent = await gh.getContent(tileSetPath);
if (tileSetContent == null) throw new Error(`Failed to get config elevation from config repo.`);
const tileSet = JSON.parse(tileSetContent) as ConfigTileSetRaster;
const newTileSet = await this.prepareElevationTileSetConfig(layer, tileSet);
// Github create pull request
await this.createTileSetPullRequest(gh, branch, title, tileSetPath, newTileSet);
}
}

// Just insert the new elevation at the bottom of config
tileSet.layers.push(layer);
/**
* Prepare and create pull request for the aerial tileset config
*/
async updateVectorTileSet(filename: string, layer: ConfigLayer, individual: boolean): Promise<void> {
const gh = new GithubApi(this.repository);
const branch = `feat/bot-config-vector-${this.imagery}${this.ticketBranchSuffix}`;
const title = `config(vector): Update the ${this.imagery} to ${filename} config file.`;

// Prepare new vector tileset config
logger.info({ imagery: this.imagery }, 'GitHub: Get the master TileSet config file');
if (individual) {
const tileSetPath = fsa.joinAll('config', 'tileset', `${filename}.json`);
const newTileSet = await this.prepareVectorTileSetConfig(layer, undefined);
// Github create pull request
await this.createTileSetPullRequest(gh, branch, title, tileSetPath, tileSet);
await this.createTileSetPullRequest(gh, branch, title, tileSetPath, newTileSet);
} else {
const tileSetPath = fsa.joinAll('config', 'tileset', `topographic.json`);
const tileSetContent = await gh.getContent(tileSetPath);
if (tileSetContent == null) throw new Error(`Failed to get config topographic from config repo.`);
// update the existing tileset
const existingTileSet = JSON.parse(tileSetContent) as ConfigTileSetVector;
const newTileSet = await this.prepareVectorTileSetConfig(layer, existingTileSet);
// Github create pull request
await this.createTileSetPullRequest(gh, branch, title, tileSetPath, newTileSet);
}
}

/**
* Create pull request for a tileset config
*/
async createTileSetPullRequest(
gh: GithubApi,
branch: string,
title: string,
tileSetPath: string,
newTileSet: ConfigTileSet | undefined,
): Promise<void> {
// skip pull request tileset prepare failure.
if (newTileSet == null) throw new Error(`Failed to prepare new tileSet for ${tileSetPath}.`);

// Github
const content = await prettyPrint(JSON.stringify(newTileSet, null, 2), ConfigPrettierFormat);
const file = { path: tileSetPath, content };
// Github create pull request
await gh.createPullRequest(branch, title, botEmail, [file]);
}

/**
* Set the default setting for the category
*/
Expand Down Expand Up @@ -230,53 +264,6 @@ export class MakeCogGithub {
return tileSet;
}

/**
* Prepare and create pull request for the aerial tileset config
*/
async updateVectorTileSet(filename: string, layer: ConfigLayer, individual: boolean): Promise<void> {
const gh = new GithubApi(this.repository);
const branch = `feat/bot-config-vector-${this.imagery}${this.ticketBranchSuffix}`;
const title = `config(vector): Update the ${this.imagery} to ${filename} config file.`;

// Prepare new vector tileset config
logger.info({ imagery: this.imagery }, 'GitHub: Get the master TileSet config file');
if (individual) {
const tileSetPath = fsa.joinAll('config', 'tileset', `${filename}.json`);
const newTileSet = await this.prepareVectorTileSetConfig(layer, undefined);
// Github create pull request
await this.createTileSetPullRequest(gh, branch, title, tileSetPath, newTileSet);
} else {
const tileSetPath = fsa.joinAll('config', 'tileset', `topographic.json`);
const tileSetContent = await gh.getContent(tileSetPath);
if (tileSetContent == null) throw new Error(`Failed to get config topographic from config repo.`);
// update the existing tileset
const existingTileSet = JSON.parse(tileSetContent) as ConfigTileSetVector;
const newTileSet = await this.prepareVectorTileSetConfig(layer, existingTileSet);
// Github create pull request
await this.createTileSetPullRequest(gh, branch, title, tileSetPath, newTileSet);
}
}

/**
* Create pull request for a tileset config
*/
async createTileSetPullRequest(
gh: GithubApi,
branch: string,
title: string,
tileSetPath: string,
newTileSet: ConfigTileSet | undefined,
): Promise<void> {
// skip pull request tileset prepare failure.
if (newTileSet == null) throw new Error(`Failed to prepare new tileSet for ${tileSetPath}.`);

// Github
const content = await prettyPrint(JSON.stringify(newTileSet, null, 2), ConfigPrettierFormat);
const file = { path: tileSetPath, content };
// Github create pull request
await gh.createPullRequest(branch, title, botEmail, [file]);
}

/**
* Prepare raster tileSet config json
*/
Expand All @@ -302,4 +289,36 @@ export class MakeCogGithub {
}
return tileSet;
}

/**
* Prepare raster tileSet config json
*/
async prepareElevationTileSetConfig(layer: ConfigLayer, tileSet?: ConfigTileSetRaster): Promise<ConfigTileSetRaster> {
if (tileSet == null) {
// Prepare individual elevation tileset config
const targetLayer = { ...layer, Category: Category.Elevation, minZoom: 0, maxZoom: 32 };
return {
type: TileSetType.Raster,
id: `ts_${layer.name}`,
name: layer.name,
title: layer.title,
category: Category.Elevation,
layers: [targetLayer],
outputs: [DefaultTerrainRgbOutput, DefaultColorRampOutput],
};
}

this.setDefaultConfig(layer, Category.Elevation);

// Prepare elevation tileset config
for (let i = 0; i < tileSet.layers.length; i++) {
const name = tileSet.layers[i]?.name;
if (name != null && standardizeLayerName(name) === standardizeLayerName(layer.name)) {
tileSet.layers[i] = layer;
return tileSet;
}
}
tileSet.layers.push(layer);
return tileSet;
}
}

0 comments on commit 37f9030

Please sign in to comment.