Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: scenario planning units data piece exporter #912

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const getFixtures = async () => {
const pieces = [
ClonePiece.ScenarioMetadata,
ClonePiece.ScenarioProtectedAreas,
ClonePiece.ScenarioRunResults,
ClonePiece.ScenarioPlanningUnitsData,
];
if (!projectExport) pieces.push(ClonePiece.ExportConfig);
return pieces;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class ExportResourcePiecesAdapter implements ExportResourcePieces {
const pieces: ExportComponent[] = [
ExportComponent.newOne(id, ClonePiece.ScenarioMetadata),
ExportComponent.newOne(id, ClonePiece.ScenarioProtectedAreas),
ExportComponent.newOne(id, ClonePiece.ScenarioRunResults),
ExportComponent.newOne(id, ClonePiece.ScenarioPlanningUnitsData),
];

if (kind === ResourceKind.Scenario) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import { PlanningUnitsGridGeojsonPieceExporter } from './planning-units-grid-geo
import { PlanningUnitsGridPieceExporter } from './planning-units-grid.piece-exporter';
import { ProjectCustomProtectedAreasPieceExporter } from './project-custom-protected-areas.piece-exporter';
import { ProjectMetadataPieceExporter } from './project-metadata.piece-exporter';
import { ScenarioRunResultsPieceExporter } from './scenario-run-results.piece-exporter';
import { ScenarioMetadataPieceExporter } from './scenario-metadata.piece-exporter';
import { ScenarioPlanningUnitsDataPieceExporter } from './scenario-planning-units-data.piece-exporter';
import { ScenarioProtectedAreasPieceExporter } from './scenario-protected-areas.piece-exporter';
import { ScenarioRunResultsPieceExporter } from './scenario-run-results.piece-exporter';

@Module({
imports: [
Expand All @@ -34,6 +35,7 @@ import { ScenarioProtectedAreasPieceExporter } from './scenario-protected-areas.
ScenarioMetadataPieceExporter,
ScenarioProtectedAreasPieceExporter,
ScenarioRunResultsPieceExporter,
ScenarioPlanningUnitsDataPieceExporter,
Logger,
],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import {
PieceExportProvider,
} from '../pieces/export-piece-processor';

interface PlanningAreaGeojsonSelectResult {
type PlanningAreaGeojsonSelectResult = {
geojson: string;
}
};

@Injectable()
@PieceExportProvider()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import {
PieceExportProvider,
} from '../pieces/export-piece-processor';

interface PlanningAreaSelectResult {
type PlanningAreaSelectResult = {
ewkb: Buffer;
}
};

interface ProjectSelectResult {
type ProjectSelectResult = {
planning_unit_grid_shape: PlanningUnitGridShape;
planning_unit_area_km2: number;
}
};

@Injectable()
@PieceExportProvider()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import {
PieceExportProvider,
} from '../pieces/export-piece-processor';

interface QueryResult {
type QueryResult = {
country_id: string;
admin_area_l1_id?: string;
admin_area_l2_id?: string;
planning_unit_grid_shape: PlanningUnitGridShape;
planning_unit_area_km2: number;
bbox: number[];
}
};

@Injectable()
@PieceExportProvider()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import {
PieceExportProvider,
} from '../pieces/export-piece-processor';

interface ProjectSelectResult {
type ProjectSelectResult = {
id: string;
bbox: BBox;
}
};

@Injectable()
@PieceExportProvider()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import {
PieceExportProvider,
} from '../pieces/export-piece-processor';

interface ProjectSelectResult {
type ProjectSelectResult = {
id: string;
bbox: BBox;
}
};

@Injectable()
@PieceExportProvider()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import { ClonePieceUrisResolver } from '@marxan/cloning/infrastructure/clone-pie
import { ProjectCustomProtectedAreasContent } from '@marxan/cloning/infrastructure/clone-piece-data/project-custom-protected-areas';
import { ProtectedArea } from '@marxan/protected-areas';

interface ProjectCustomProtectedAreasSelectResult {
type ProjectCustomProtectedAreasSelectResult = {
fullName: string;
ewkb: Buffer;
}
};

@Injectable()
@PieceExportProvider()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import {
PieceExportProvider,
} from '../pieces/export-piece-processor';

interface SelectScenarioResult {
type SelectScenarioResult = {
name: string;
description?: string;
blm?: number;
number_of_runs?: number;
metadata?: Scenario['metadata'];
}
};

@Injectable()
@PieceExportProvider()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { geoprocessingConnections } from '@marxan-geoprocessing/ormconfig';
import { ClonePiece, ExportJobInput, ExportJobOutput } from '@marxan/cloning';
import { ClonePieceUrisResolver } from '@marxan/cloning/infrastructure/clone-piece-data';
import { ScenarioPlanningUnitsDataContent } from '@marxan/cloning/infrastructure/clone-piece-data/scenario-planning-units-data';
import { FileRepository } from '@marxan/files-repository';
import { Injectable, Logger } from '@nestjs/common';
import { InjectEntityManager } from '@nestjs/typeorm';
import { isLeft } from 'fp-ts/Either';
import { Readable } from 'stream';
import { EntityManager } from 'typeorm';
import {
ExportPieceProcessor,
PieceExportProvider,
} from '../pieces/export-piece-processor';

type SelectResult = {
lockin_status?: 1 | 2;
xloc?: number;
yloc?: number;
protected_area?: number;
protected_by_default: boolean;
feature_list: string[];
puid: number;
cost: number;
};

@Injectable()
@PieceExportProvider()
export class ScenarioPlanningUnitsDataPieceExporter
implements ExportPieceProcessor {
constructor(
private readonly fileRepository: FileRepository,
@InjectEntityManager(geoprocessingConnections.default)
private readonly entityManager: EntityManager,
private readonly logger: Logger,
) {
this.logger.setContext(ScenarioPlanningUnitsDataPieceExporter.name);
}

isSupported(piece: ClonePiece): boolean {
return piece === ClonePiece.ScenarioPlanningUnitsData;
}

async run(input: ExportJobInput): Promise<ExportJobOutput> {
const result: SelectResult[] = await this.entityManager
.createQueryBuilder()
.select('*')
.from(
(qb) =>
qb
.select('*')
.from('scenarios_pu_data', 'inner_spd')
.where('scenario_id = :scenarioId', {
scenarioId: input.resourceId,
}),
'spd',
)
.innerJoin('projects_pu', 'ppu', 'ppu.id = spd.project_pu_id')
.innerJoin(
'scenarios_pu_cost_data',
'spcd',
'spd.id = spcd.scenarios_pu_data_id',
)
.execute();

const fileContent: ScenarioPlanningUnitsDataContent = {
planningUnitsData: result.map((row) => ({
cost: row.cost,
featureList: row.feature_list,
protectedArea: row.protected_area,
protectedByDefault: row.protected_by_default,
puid: row.puid,
lockinStatus: row.lockin_status,
xloc: row.xloc,
yloc: row.yloc,
})),
};

const outputFile = await this.fileRepository.save(
Readable.from(JSON.stringify(fileContent)),
`json`,
);

if (isLeft(outputFile)) {
const errorMessage = `${ScenarioPlanningUnitsDataPieceExporter.name} - Scenario - couldn't save file - ${outputFile.left.description}`;
this.logger.error(errorMessage);
throw new Error(errorMessage);
}

return {
...input,
uris: ClonePieceUrisResolver.resolveFor(
ClonePiece.ScenarioPlanningUnitsData,
outputFile.right,
{
kind: input.resourceKind,
scenarioId: input.resourceId,
},
),
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ import {
PieceExportProvider,
} from '../pieces/export-piece-processor';

interface SelectScenarioResult {
type SelectScenarioResult = {
id: string;
protectedAreasIds?: string[];
wdpaThreshold?: number;
}
};

interface CustomProtectedArea {
type CustomProtectedArea = {
ewkb: Buffer;
name: string;
}
};

interface WdpaProtectedArea {
type WdpaProtectedArea = {
ewkb: Buffer;
wdpaid: number;
}
};

type SelectWdpaResult = WdpaProtectedArea | CustomProtectedArea;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { PlanningUnitGridShape } from '../../../../scenarios-planning-unit/src';

export interface PlanningAreaCustomContent {
export type PlanningAreaCustomContent = {
planningAreaGeom: number[];
puGridShape: PlanningUnitGridShape;
puAreaKm2: number;
}
};

export const planningAreaCustomRelativePath = 'planning-area.json';
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { PlanningUnitGridShape } from '../../../../scenarios-planning-unit/src';

export interface PlanningAreaGadmContent {
export type PlanningAreaGadmContent = {
country?: string;
l1?: string;
l2?: string;
puGridShape: PlanningUnitGridShape;
planningUnitAreakm2: number;
bbox: number[];
}
};

export const planningAreaGadmRelativePath = 'planning-area.json';
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export const projectCustomProtectedAreasRelativePath =
'project-custom-protected-areas.json';

export interface ProjectCustomProtectedAreasContent {
export type ProjectCustomProtectedAreasContent = {
fullName: string;
ewkb: number[];
}
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export interface ProjectMetadataContent {
export type ProjectMetadataContent = {
name: string;
description?: string;
}
};

export const projectMetadataRelativePath = 'project-metadata.json';
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Scenario } from '@marxan-api/modules/scenarios/scenario.api.entity';

export interface ScenarioMetadataContent {
export type ScenarioMetadataContent = {
name: string;
description?: string;
numberOfRuns?: number;
blm?: number;
metadata?: Scenario['metadata'];
}
};

export const scenarioMetadataRelativePath = {
scenarioImport: `scenario-metadata.json`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,18 @@ export const scenarioPlanningUnitsDataRelativePath = {
projectImport: (oldScenarioId: string) =>
`scenarios/${oldScenarioId}/scenario-pu-data.json`,
};

type PlanningUnitData = {
puid: number;
cost: number;
lockinStatus?: 1 | 2;
xloc?: number;
yloc?: number;
protectedArea?: number;
protectedByDefault: boolean;
featureList: string[];
};

export type ScenarioPlanningUnitsDataContent = {
planningUnitsData: PlanningUnitData[];
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ export const scenarioProtectedAreasRelativePath = {
`scenarios/${oldScenarioId}/scenario-protected-areas.json`,
};

export interface ScenarioProtectedAreasContent {
export type ScenarioProtectedAreasContent = {
wdpa: number[];
customProtectedAreas: {
name: string;
geom: number[];
}[];
threshold?: number;
}
};