Skip to content

Commit

Permalink
fix(FeatureAmounts): Adds FeatureAmount calculation to feature shapef…
Browse files Browse the repository at this point in the history
…ile upload time
  • Loading branch information
KevSanchez committed Oct 27, 2023
1 parent 0ba7e0a commit 10bcb85
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 25 deletions.
2 changes: 2 additions & 0 deletions api/apps/api/src/modules/geo-features/geo-features.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { UploadedFeatureAmount } from '@marxan-api/modules/geo-features/import/f
import { FeatureAmountUploadService } from '@marxan-api/modules/geo-features/import/features-amounts-upload.service';
import { ApiEventsModule } from '@marxan-api/modules/api-events';
import { FeatureImportEventsService } from '@marxan-api/modules/geo-features/import/feature-import.events';
import { FeatureAmountsPerPlanningUnitModule } from '@marxan/feature-amounts-per-planning-unit';

@Module({
imports: [
Expand All @@ -44,6 +45,7 @@ import { FeatureImportEventsService } from '@marxan-api/modules/geo-features/imp
forwardRef(() => ScenarioFeaturesModule),
ApiEventsModule,
GeoFeatureTagsModule,
FeatureAmountsPerPlanningUnitModule.for(DbConnections.geoprocessingDB),
],
providers: [
GeoFeaturesService,
Expand Down
43 changes: 43 additions & 0 deletions api/apps/api/src/modules/geo-features/geo-features.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ import {
} from '@marxan-api/modules/geo-feature-tags/geo-feature-tags.service';
import { FeatureAmountUploadService } from '@marxan-api/modules/geo-features/import/features-amounts-upload.service';
import { isNil } from 'lodash';
import {
FeatureAmountsPerPlanningUnitEntity,
FeatureAmountsPerPlanningUnitService,
} from '@marxan/feature-amounts-per-planning-unit';
import { ComputeFeatureAmountPerPlanningUnit } from '@marxan/feature-amounts-per-planning-unit/feature-amounts-per-planning-units.service';

const geoFeatureFilterKeyNames = [
'featureClassName',
Expand Down Expand Up @@ -119,6 +124,7 @@ export class GeoFeaturesService extends AppBaseService<
private readonly scenarioFeaturesService: ScenarioFeaturesService,
private readonly projectAclService: ProjectAclService,
private readonly featureAmountUploads: FeatureAmountUploadService,
private readonly featureAmountsPerPlanningUnitService: FeatureAmountsPerPlanningUnitService,
) {
super(
geoFeaturesRepository,
Expand Down Expand Up @@ -411,8 +417,23 @@ export class GeoFeaturesService extends AppBaseService<
);
}

const computedFeatureAmounts =
await this.featureAmountsPerPlanningUnitService.computeMarxanAmountPerPlanningUnit(
geoFeature.id,
projectId,
geoQueryRunner.manager,
);

await this.saveFeatureAmountPerPlanningUnit(
geoQueryRunner.manager,
projectId,
computedFeatureAmounts,
);

await apiQueryRunner.commitTransaction();
await geoQueryRunner.commitTransaction();

await this.saveAmountRangeForFeatures([geoFeature.id]);
} catch (err) {
await apiQueryRunner.rollbackTransaction();
await geoQueryRunner.rollbackTransaction();
Expand All @@ -431,6 +452,24 @@ export class GeoFeaturesService extends AppBaseService<
return right(geoFeature);
}

private async saveFeatureAmountPerPlanningUnit(
geoEntityManager: EntityManager,
projectId: string,
featureAmounts: ComputeFeatureAmountPerPlanningUnit[],
): Promise<void> {
const repo = geoEntityManager.getRepository(
FeatureAmountsPerPlanningUnitEntity,
);
await repo.save(
featureAmounts.map(({ amount, projectPuId, featureId }) => ({
projectId,
featureId,
amount,
projectPuId,
})),
);
}

public async updateFeatureForProject(
userId: string,
featureId: string,
Expand Down Expand Up @@ -854,6 +893,10 @@ export class GeoFeaturesService extends AppBaseService<
.groupBy('fappu.feature_id')
.getRawMany();

if (minAndMaxAmountsForFeatures.length === 0) {
return;
}

const minMaxSqlValueStringForFeatures = minAndMaxAmountsForFeatures
.map(
(feature) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class ComputeArea {
const amountPerPlanningUnitOfFeature =
await this.featureAmountsPerPlanningUnit.computeMarxanAmountPerPlanningUnit(
featureId,
scenarioId,
projectId,
);

return this.featureAmountsPerPlanningUnitRepo.saveAmountPerPlanningUnitAndFeature(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,48 +41,51 @@ export class FeatureAmountsPerPlanningUnitService {

public async computeMarxanAmountPerPlanningUnit(
featureId: string,
scenarioId: string,
projectId: string,
geoEntityManager?: EntityManager,
): Promise<ComputeFeatureAmountPerPlanningUnit[]> {
/**
* @TODO further performance savings: limiting scans to planning_units_geom
* by partition (we need to get the grid shape from the parent project); use
* && operator instead of st_intersects() for bbox-based calculation of
* intersections.
*/
geoEntityManager = geoEntityManager
? geoEntityManager
: this.geoEntityManager;

const rows: {
featureid: string;
puid: number;
projectpuid: string;
amount: number;
}[] = await this.geoEntityManager.query(
}[] = await geoEntityManager.query(
`
WITH all_amount_per_planning_unit as
( select
$2 as featureId,
pu.puid as puid,
pu.id as projectpuid,
ST_Area(ST_Transform(ST_Intersection(species.the_geom, pu.the_geom),3410)) as amount
from
(
select
st_union(the_geom) as the_geom
from scenario_features_preparation sfp
inner join features_data fd on sfp.feature_class_id = fd.id where sfp.scenario_id = $1
AND sfp.api_feature_id = $2
group by sfp.api_feature_id
) species,
(
select the_geom, ppu.puid as puid, ppu.id as id, spd.scenario_id
from planning_units_geom pug
inner join projects_pu ppu on pug.id = ppu.geom_id
inner join scenarios_pu_data spd on ppu.id = spd.project_pu_id
where spd.scenario_id = $1 order by ppu.puid asc
) pu
where species.the_geom && pu.the_geom
$2 as featureId,
pu.puid as puid,
pu.id as projectpuid,
ST_Area(ST_Transform(ST_Intersection(species.the_geom, pu.the_geom),3410)) as amount
from
(
select st_union(the_geom) as the_geom
from features_data fd
where fd.feature_id = $2
group by fd.feature_id
) species,
(
select the_geom, ppu.puid as puid, ppu.id as id
from planning_units_geom pug
inner join projects_pu ppu on pug.id = ppu.geom_id
where ppu.project_id = $1
order by ppu.puid asc
) pu
where species.the_geom && pu.the_geom
)
select * from all_amount_per_planning_unit where amount > 0 order by puid;
`,
[scenarioId, featureId],
[projectId, featureId],
);

return rows.map(({ featureid, projectpuid, puid, amount }) => ({
Expand Down

0 comments on commit 10bcb85

Please sign in to comment.