From 5d6adb0fc5c036005305206f4a4b8170215ae965 Mon Sep 17 00:00:00 2001 From: alexeh Date: Tue, 27 Feb 2024 09:23:46 +0300 Subject: [PATCH] Fallback to intersection when no adminregion found by district name --- .../eudr/eudr.dto-processor.service.ts | 79 +++++++++++++++++++ .../import-data/eudr/eudr.import.service.ts | 7 +- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/api/src/modules/import-data/eudr/eudr.dto-processor.service.ts b/api/src/modules/import-data/eudr/eudr.dto-processor.service.ts index aa3dde870..df36b2adc 100644 --- a/api/src/modules/import-data/eudr/eudr.dto-processor.service.ts +++ b/api/src/modules/import-data/eudr/eudr.dto-processor.service.ts @@ -18,6 +18,8 @@ import { GeoRegion } from 'modules/geo-regions/geo-region.entity'; // @ts-ignore import * as wellknown from 'wellknown'; import { DataSource, QueryRunner, Repository } from 'typeorm'; +import { GeoCodingError } from 'modules/geo-coding/errors/geo-coding.error'; +import { AdminRegion } from 'modules/admin-regions/admin-region.entity'; /** * @debt: Define a more accurate DTO / Interface / Class for API-DB trades @@ -87,6 +89,7 @@ export class EUDRDTOProcessor { geoRegion.totalArea = row.total_area_ha; geoRegion.theGeom = wellknown.parse(row.geometry) as unknown as JSON; geoRegion.isCreatedByUser = true; + geoRegion.name = row.plot_name; const foundGeoRegion: GeoRegion | null = await geoRegionRepository.findOne({ where: { name: geoRegion.name }, @@ -108,6 +111,13 @@ export class EUDRDTOProcessor { sourcingLocation.producer = savedSupplier; sourcingLocation.geoRegion = savedGeoRegion; sourcingLocation.sourcingRecords = []; + sourcingLocation.adminRegionId = row.sourcing_district + ? await this.getAdminRegionByAddress( + queryRunner, + row.sourcing_district, + row.geometry, + ) + : (null as unknown as string); for (const key in row) { const sourcingRecord: SourcingRecord = new SourcingRecord(); @@ -126,6 +136,8 @@ export class EUDRDTOProcessor { const saved: SourcingLocation[] = await queryRunner.manager .getRepository(SourcingLocation) .save(sourcingLocations); + + await queryRunner.commitTransaction(); return { sourcingLocations: saved, }; @@ -137,6 +149,73 @@ export class EUDRDTOProcessor { } } + private async getAdminRegionByAddress( + queryRunner: QueryRunner, + name: string, + geom: string, + ): Promise { + const adminRegion: AdminRegion | null = await queryRunner.manager + .getRepository(AdminRegion) + .findOne({ where: { name: name, level: 1 } }); + if (!adminRegion) { + this.logger.warn( + `No admin region found for the provided address: ${name}`, + ); + return this.getAdminRegionByIntersection(queryRunner, geom); + } + return adminRegion.id; + } + + // TODO: temporal method to determine the most accurate admin region. For now we only consider Level 0 + // as Country and Level 1 as district + + private async getAdminRegionByIntersection( + queryRunner: QueryRunner, + geometry: string, + ): Promise { + this.logger.log(`Intersecting EUDR geometry...`); + + const adminRegions: any = await queryRunner.manager.query( + ` + WITH intersections AS ( + SELECT + ar.id, + ar.name, + ar."geoRegionId", + gr."theGeom", + ar.level, + ST_Area(ST_Intersection(gr."theGeom", ST_GeomFromEWKT('SRID=4326;${geometry}'))) AS intersection_area + FROM admin_region ar + JOIN geo_region gr ON ar."geoRegionId" = gr.id + WHERE + ST_Intersects(gr."theGeom", ST_GeomFromEWKT('SRID=4326;${geometry}')) + AND ar.level IN (0, 1) + ), + max_intersection_by_level AS ( + SELECT + level, + MAX(intersection_area) AS max_area + FROM intersections + GROUP BY level + ) + SELECT i.* + FROM intersections i + JOIN max_intersection_by_level m ON i.level = m.level AND i.intersection_area = m.max_area; + `, + ); + if (!adminRegions.length) { + throw new GeoCodingError( + `No admin region found for the provided geometry`, + ); + } + + const level1AdminRegionid: string = adminRegions.find( + (ar: any) => ar.level === 1, + ).id; + this.logger.log('Admin region found'); + return level1AdminRegionid; + } + private async validateCleanData(nonEmptyData: SourcingData[]): Promise { const excelErrors: { line: number; diff --git a/api/src/modules/import-data/eudr/eudr.import.service.ts b/api/src/modules/import-data/eudr/eudr.import.service.ts index d8d7bde16..61c9ed7ca 100644 --- a/api/src/modules/import-data/eudr/eudr.import.service.ts +++ b/api/src/modules/import-data/eudr/eudr.import.service.ts @@ -62,6 +62,9 @@ export class EudrImportService { await this.cleanDataBeforeImport(); + // TODO: Check what do we need to do with indicators and specially materials: + // Do we need to ingest new materials? Activate some through the import? Activate all? + const { sourcingLocations } = await this.dtoProcessor.save( parsedEudrData.Data, ); @@ -123,9 +126,9 @@ export class EudrImportService { await this.sourcingLocationService.clearTable(); await this.sourcingRecordService.clearTable(); await this.geoRegionsService.deleteGeoRegionsCreatedByUser(); - } catch ({ message }) { + } catch (e: any) { throw new Error( - `Database could not been cleaned before loading new dataset: ${message}`, + `Database could not been cleaned before loading new dataset: ${e.message}`, ); } }