From d6384b86464a431672dd5805a20a3d7c0617dd00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daute=20Rodr=C3=ADguez=20Rodr=C3=ADguez?= <94375891+aciddaute@users.noreply.github.com> Date: Wed, 25 May 2022 14:07:58 +0100 Subject: [PATCH 1/7] feat: set proper errors when a job fails (#1108) --- ...-legacy-project-import-piece-as-failed.command.ts | 1 - ...-legacy-project-import-piece-as-failed.handler.ts | 2 -- .../legacy-project-import-component.ts | 4 +--- .../legacy-project-import/legacy-project-import.ts | 3 +-- .../import-legacy-project-piece.events-handler.ts | 9 ++++++--- .../infra/legacy-project-import.infra.module.ts | 2 ++ api/apps/api/src/modules/queue-api-events/adapter.ts | 12 ++++++++---- 7 files changed, 18 insertions(+), 15 deletions(-) diff --git a/api/apps/api/src/modules/legacy-project-import/application/mark-legacy-project-import-piece-as-failed.command.ts b/api/apps/api/src/modules/legacy-project-import/application/mark-legacy-project-import-piece-as-failed.command.ts index e536ff8dab..a964f62d0d 100644 --- a/api/apps/api/src/modules/legacy-project-import/application/mark-legacy-project-import-piece-as-failed.command.ts +++ b/api/apps/api/src/modules/legacy-project-import/application/mark-legacy-project-import-piece-as-failed.command.ts @@ -7,7 +7,6 @@ export class MarkLegacyProjectImportPieceAsFailed extends Command { public readonly projectId: ResourceId, public readonly legacyProjectImportComponentId: LegacyProjectImportComponentId, public readonly errors: string[] = [], - public readonly warnings: string[] = [], ) { super(); } diff --git a/api/apps/api/src/modules/legacy-project-import/application/mark-legacy-project-import-piece-as-failed.handler.ts b/api/apps/api/src/modules/legacy-project-import/application/mark-legacy-project-import-piece-as-failed.handler.ts index 0183b5d00c..ef983feefa 100644 --- a/api/apps/api/src/modules/legacy-project-import/application/mark-legacy-project-import-piece-as-failed.handler.ts +++ b/api/apps/api/src/modules/legacy-project-import/application/mark-legacy-project-import-piece-as-failed.handler.ts @@ -40,7 +40,6 @@ export class MarkLegacyProjectImportPieceAsFailedHandler errors, legacyProjectImportComponentId, projectId, - warnings, }: MarkLegacyProjectImportPieceAsFailed): Promise { const result = await this.legacyProjectImportRepository.transaction( async (repo) => { @@ -61,7 +60,6 @@ export class MarkLegacyProjectImportPieceAsFailedHandler const result = aggregate.markPieceAsFailed( legacyProjectImportComponentId, errors, - warnings, ); if (isLeft(result)) { diff --git a/api/apps/api/src/modules/legacy-project-import/domain/legacy-project-import/legacy-project-import-component.ts b/api/apps/api/src/modules/legacy-project-import/domain/legacy-project-import/legacy-project-import-component.ts index df2d8140ea..6fcbcf5a97 100644 --- a/api/apps/api/src/modules/legacy-project-import/domain/legacy-project-import/legacy-project-import-component.ts +++ b/api/apps/api/src/modules/legacy-project-import/domain/legacy-project-import/legacy-project-import-component.ts @@ -1,4 +1,3 @@ -import { ArchiveLocation } from '@marxan/cloning/domain'; import { LegacyProjectImportPiece, LegacyProjectImportPieceOrderResolver, @@ -49,10 +48,9 @@ export class LegacyProjectImportComponent { this.warnings.push(...warnings); } - markAsFailed(errors: string[] = [], warnings: string[] = []) { + markAsFailed(errors: string[] = []) { this.status = this.status.markAsFailed(); this.errors.push(...errors); - this.warnings.push(...warnings); } toSnapshot(): LegacyProjectImportComponentSnapshot { diff --git a/api/apps/api/src/modules/legacy-project-import/domain/legacy-project-import/legacy-project-import.ts b/api/apps/api/src/modules/legacy-project-import/domain/legacy-project-import/legacy-project-import.ts index 2af3cdb4b6..027ba8dadd 100644 --- a/api/apps/api/src/modules/legacy-project-import/domain/legacy-project-import/legacy-project-import.ts +++ b/api/apps/api/src/modules/legacy-project-import/domain/legacy-project-import/legacy-project-import.ts @@ -190,14 +190,13 @@ export class LegacyProjectImport extends AggregateRoot { markPieceAsFailed( pieceId: LegacyProjectImportComponentId, errors: string[] = [], - warnings: string[] = [], ): Either { const piece = this.pieces.find((pc) => pc.id.value === pieceId.value); if (!piece) return left(legacyProjectImportComponentNotFound); if (piece.hasFailed()) return left(legacyProjectImportComponentAlreadyFailed); - piece.markAsFailed(errors, warnings); + piece.markAsFailed(errors); const hasThisBatchFinished = this.hasBatchFinished(piece.order); const hasThisBatchFailed = this.hasBatchFailed(piece.order); diff --git a/api/apps/api/src/modules/legacy-project-import/infra/import-legacy-project-piece.events-handler.ts b/api/apps/api/src/modules/legacy-project-import/infra/import-legacy-project-piece.events-handler.ts index 27b47900f8..e7666ebfa9 100644 --- a/api/apps/api/src/modules/legacy-project-import/infra/import-legacy-project-piece.events-handler.ts +++ b/api/apps/api/src/modules/legacy-project-import/infra/import-legacy-project-piece.events-handler.ts @@ -99,13 +99,16 @@ export class ImportLegacyProjectPieceEventsHandler private async failed(event: EventData) { const { pieceId, projectId } = await event.data; + const result = await event.result; + const errors: string[] = []; + + if (typeof result === 'string') errors.push(result); + await this.commandBus.execute( new MarkLegacyProjectImportPieceAsFailed( new ResourceId(projectId), new LegacyProjectImportComponentId(pieceId), - // TODO Obtain actual errors and/or warnings - [], - [], + errors, ), ); } diff --git a/api/apps/api/src/modules/legacy-project-import/infra/legacy-project-import.infra.module.ts b/api/apps/api/src/modules/legacy-project-import/infra/legacy-project-import.infra.module.ts index 6f8b66724e..939cc8c239 100644 --- a/api/apps/api/src/modules/legacy-project-import/infra/legacy-project-import.infra.module.ts +++ b/api/apps/api/src/modules/legacy-project-import/infra/legacy-project-import.infra.module.ts @@ -13,6 +13,7 @@ import { import { ScheduleDbCleanupForFailedLegacyProjectImportHandler } from './schedule-db-cleanup-for-failed-legacy-project-import.handler'; import { LegacyProjectImportRepositoryModule } from './legacy-project-import.repository.module'; import { ScheduleLegacyProjectImportPieceHandler } from './schedule-legacy-project-import-piece.handler'; +import { ImportLegacyProjectPieceEventsHandler } from './import-legacy-project-piece.events-handler'; @Module({ imports: [ @@ -30,6 +31,7 @@ import { ScheduleLegacyProjectImportPieceHandler } from './schedule-legacy-proje ScheduleDbCleanupForFailedLegacyProjectImportHandler, LegacyProjectImportPieceRequestedSaga, ScheduleLegacyProjectImportPieceHandler, + ImportLegacyProjectPieceEventsHandler, { provide: Logger, useClass: Logger, diff --git a/api/apps/api/src/modules/queue-api-events/adapter.ts b/api/apps/api/src/modules/queue-api-events/adapter.ts index ad03385d82..eb5d7ebfdc 100644 --- a/api/apps/api/src/modules/queue-api-events/adapter.ts +++ b/api/apps/api/src/modules/queue-api-events/adapter.ts @@ -50,8 +50,8 @@ export class QueueEventsAdapter< queueEvents.on(`completed`, ({ jobId }, eventId) => this.handleFinished(jobId, eventId), ); - queueEvents.on(`failed`, ({ jobId }, eventId) => - this.handleFailed(jobId, eventId), + queueEvents.on(`failed`, ({ jobId, failedReason }, eventId) => + this.handleFailed(jobId, eventId, failedReason), ); } @@ -84,7 +84,11 @@ export class QueueEventsAdapter< }); } - private async handleFailed(jobId: string, eventId: string) { + private async handleFailed( + jobId: string, + eventId: string, + failedReason: string, + ) { const lazyDataGetter = this.createLazyDataGetter(); const eventDto = await this.eventFactory.createFailedEvent({ jobId, @@ -107,7 +111,7 @@ export class QueueEventsAdapter< return lazyDataGetter(jobId); }, get result() { - return Promise.resolve(undefined); + return Promise.resolve(failedReason); }, }); } From cc3d2fb3553dd038e08c9598fb48d77f2531a5fa Mon Sep 17 00:00:00 2001 From: Alicia Date: Thu, 26 May 2022 13:46:58 +0200 Subject: [PATCH 2/7] Fix Bbox and grid creation to ensure antimeridian is taken into account --- .../api/test/fixtures/test-init-apidb.sql | 5 ++ ...ModifyBboxCalculationTakingAntimeridian.ts | 72 +++++++++++++++++++ .../planning-units/planning-units.job.ts | 28 +++++--- .../planning-units/planning-units.service.ts | 17 +++-- .../geoprocessing/src/utils/bbox.utils.ts | 16 +++++ 5 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 api/apps/geoprocessing/src/migrations/geoprocessing/1653565019335-ModifyBboxCalculationTakingAntimeridian.ts diff --git a/api/apps/api/test/fixtures/test-init-apidb.sql b/api/apps/api/test/fixtures/test-init-apidb.sql index 2ad36ab144..f56e5f1aab 100644 --- a/api/apps/api/test/fixtures/test-init-apidb.sql +++ b/api/apps/api/test/fixtures/test-init-apidb.sql @@ -5,3 +5,8 @@ VALUES ('bb@example.com', 'b', 'b', 'User B B', true, false, crypt('bbuserpassword', gen_salt('bf'))), ('cc@example.com', 'c', 'c', 'User C C', true, false, crypt('ccuserpassword', gen_salt('bf'))), ('dd@example.com', 'd', 'd', 'User D D', true, false, crypt('dduserpassword', gen_salt('bf'))); + +INSERT INTO organizations (name, created_by) +VALUES + ('Example Org 1', (SELECT id FROM users WHERE email = 'aa@example.com')), + ('Example Org 2', (SELECT id FROM users WHERE email = 'aa@example.com')); diff --git a/api/apps/geoprocessing/src/migrations/geoprocessing/1653565019335-ModifyBboxCalculationTakingAntimeridian.ts b/api/apps/geoprocessing/src/migrations/geoprocessing/1653565019335-ModifyBboxCalculationTakingAntimeridian.ts new file mode 100644 index 0000000000..20cb17820d --- /dev/null +++ b/api/apps/geoprocessing/src/migrations/geoprocessing/1653565019335-ModifyBboxCalculationTakingAntimeridian.ts @@ -0,0 +1,72 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ModifyBboxCalculationTakingAntimeridian1653565019335 + implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE OR REPLACE FUNCTION mx_bbox2json(geom geometry) + returns jsonb + language plpgsql + as + $function$ + DECLARE + -- variable declaration + both_hemispheres RECORD; + BEGIN + -- logic https://github.com/mapbox/carmen/blob/03fac2d7397ecdfcb4f0828fcfd9d8a54c845f21/lib/util/bbox.js#L59 + -- json form of the bbox should be in Nominatim bbox [xmin, xmax, ymin, ymax] [W, E, S, N]. + execute 'with region as (select st_intersection($1, geom) as the_geom, + st_intersects($1, geom) intersects, pos + from (values (ST_MakeEnvelope(-180, -90, 0, 90, 4326), ''west''), + (ST_MakeEnvelope(0, -90, 180, 90, 4326), ''east'')) as t(geom, pos)), + data as (select ST_XMax(the_geom), ST_XMin(the_geom), + ST_YMax(the_geom),ST_YMin(the_geom), pos, intersects, + ST_XMax(the_geom) + ABS(lag(ST_XMin(the_geom), 1) OVER ()) > + (180 - ST_XMin(the_geom)) + (180 - ABS(lag(ST_XMax(the_geom), 1) OVER ())) as pm_am + from region) + select bool_and(intersects) and bool_and(pm_am) result, + jsonb_build_array(max(st_xmax), min(st_xmin), max(st_ymax), min(st_ymin)) if_false, + jsonb_build_array(min(st_xmax), max(st_xmin), max(st_ymax), min(st_ymin))if_true from data;' + into both_hemispheres + using geom; + if both_hemispheres.result then + return both_hemispheres.if_true; + else + return both_hemispheres.if_false; + end if; + end; + $function$; + + CREATE OR REPLACE FUNCTION public.tr_getbbox() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + BEGIN + NEW.bbox := mx_bbox2json(NEW.the_geom); + RETURN NEW; + END; + $function$; + + UPDATE admin_regions SET id = id; + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE OR REPLACE FUNCTION public.tr_getbbox() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + BEGIN + NEW.bbox := jsonb_build_array(ST_XMax(NEW.the_geom), ST_XMin(NEW.the_geom), + ST_YMax(NEW.the_geom), ST_YMin(NEW.the_geom)); + RETURN NEW; + END; + $function$; + + Drop FUNCTION mx_bbox2json(geom geometry); + + UPDATE admin_regions SET id = id; + `); + } +} diff --git a/api/apps/geoprocessing/src/modules/planning-units/planning-units.job.ts b/api/apps/geoprocessing/src/modules/planning-units/planning-units.job.ts index ae89b36c28..23bcd07763 100644 --- a/api/apps/geoprocessing/src/modules/planning-units/planning-units.job.ts +++ b/api/apps/geoprocessing/src/modules/planning-units/planning-units.job.ts @@ -115,11 +115,16 @@ export class PlanningUnitsJobProcessor { SELECT ST_Transform(the_geom, 3410) as geom FROM planning_areas WHERE id = '${data.planningAreaId}' - ), grid AS ( - SELECT (${gridFn}(${size}, geom)).* - FROM region + ), + bboxes as (select * from (values (st_transform(ST_MakeEnvelope(-180, -90, 0, 90, 4326), 3410), 'west'), + (st_transform(ST_MakeEnvelope(0, -90, 180, 90, 4326),3410), 'east')) as t(geom, pos)), + grid AS ( + SELECT ST_ClipByBox2D((${gridFn}(${size}, st_intersection(region.geom, bboxes.geom))).geom, + st_transform(ST_MakeEnvelope(-180, -90, 180, 90, 4326), 3410)) as geom + FROM region, bboxes ) - SELECT grid.geom +SELECT +grid.geom FROM grid, region WHERE ST_Intersects(grid.geom, region.geom) `; @@ -145,11 +150,16 @@ export class PlanningUnitsJobProcessor { SELECT ST_Transform(the_geom, 3410) as geom FROM admin_regions WHERE ${whereConditions.join(' AND ')} - ), grid AS ( - SELECT (${gridFn}(${size}, geom)).* - FROM region + ), + bboxes as (select * from (values (st_transform(ST_MakeEnvelope(-180, -90, 0, 90, 4326), 3410), 'west'), + (st_transform(ST_MakeEnvelope(0, -90, 180, 90, 4326),3410), 'east')) as t(geom, pos)), + grid AS ( + SELECT ST_ClipByBox2D((${gridFn}(${size}, st_intersection(region.geom, bboxes.geom))).geom, + st_transform(ST_MakeEnvelope(-180, -90, 180, 90, 4326), 3410)) as geom + FROM region, bboxes ) - SELECT grid.geom + SELECT + grid.geom FROM grid, region WHERE ST_Intersects(grid.geom, region.geom) `; @@ -196,7 +206,7 @@ export class PlanningUnitsJobProcessor { const geometries: { id: string }[] = await em.query( ` INSERT INTO planning_units_geom (the_geom, type, size) - SELECT st_transform(geom, 4326) AS the_geom, $1::planning_unit_grid_shape AS type, $2 AS size + SELECT ST_MakeValid(st_transform(geom, 4326)) AS the_geom, $1::planning_unit_grid_shape AS type, $2 AS size FROM (${subquery}) grid ON CONFLICT (the_geom_hash, type) DO UPDATE SET type = $1::planning_unit_grid_shape RETURNING id diff --git a/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts b/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts index 4a801950a6..07628601f8 100644 --- a/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts +++ b/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts @@ -1,11 +1,11 @@ import { TileService } from '@marxan-geoprocessing/modules/tile/tile.service'; -import { nominatim2bbox } from '@marxan-geoprocessing/utils/bbox.utils'; +import { nominatim2bbox, antimeridianBbox } from '@marxan-geoprocessing/utils/bbox.utils'; import { PlanningUnitGridShape } from '@marxan/scenarios-planning-unit'; import { TileRequest } from '@marxan/tiles'; import { Inject, Injectable, Logger } from '@nestjs/common'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsArray, IsIn, IsNumber, IsOptional, IsString } from 'class-validator'; +import { IsArray, IsIn, IsNumber, IsOptional} from 'class-validator'; import { BBox } from 'geojson'; import { calculateGridSize, @@ -110,6 +110,7 @@ export class PlanningUnitsService { * Because we want to reduce the overhead for the db if an uncontroled area requests * a large area. * If so the shape we are getting is down the optimal to visualize it as points + * */ const query = ratioPixelExtent < 8 && !filters?.bbox @@ -122,14 +123,20 @@ export class PlanningUnitsService { } /** * @param filters including only bounding box of the area where the grids would be generated + * */ private buildPlanningUnitsWhereQuery(filters?: PlanningUnitsFilters): string { let whereQuery = ``; if (filters?.bbox) { - whereQuery = `st_intersects(ST_Transform(ST_MakeEnvelope(${nominatim2bbox( - filters.bbox, - )}, 4326), 3857) ,the_geom)`; + const {westBbox, eastBbox} = antimeridianBbox(nominatim2bbox( + filters.bbox + )); + whereQuery = `st_intersects(ST_Transform(st_intersection(ST_MakeEnvelope(${eastBbox}, 4326), + ST_MakeEnvelope(0, -90, 180, 90, 4326)), 3857), the_geom) + OR + st_intersects(ST_Transform(st_intersection(ST_MakeEnvelope(${westBbox}, 4326), + ST_MakeEnvelope(-180, -90, 0, 90, 4326)), 3857), the_geom)`; } return whereQuery; } diff --git a/api/apps/geoprocessing/src/utils/bbox.utils.ts b/api/apps/geoprocessing/src/utils/bbox.utils.ts index d95e062f1a..591d64aa48 100644 --- a/api/apps/geoprocessing/src/utils/bbox.utils.ts +++ b/api/apps/geoprocessing/src/utils/bbox.utils.ts @@ -3,6 +3,7 @@ import { BBox } from 'geojson'; * Utility functions related to lower-level interaction with bbox operations. * * @debt This should be moved to a self-standing + * @debt there is also a mismatch in terms of the bbox creation [xmax,xmin,ymax,ymin] vs [xmin,xmax,ymin,ymax] */ /** @@ -16,3 +17,18 @@ export function bbox2Nominatim(bbox: BBox): BBox { export function nominatim2bbox(nominatim: BBox): BBox { return [nominatim[0], nominatim[2], nominatim[1], nominatim[3]]; } + +/** + * Check of antimeridian and division of bbox + * to Nominatim bbox [xmin, xmax, ymin, ymax]. + * @param bbox + * @returns {BBox, BBox} west and east bbox split + * + */ +export function antimeridianBbox(bbox: BBox): {westBbox: BBox, eastBbox: BBox} { + if (bbox[2] > bbox[0]) { + return {westBbox: [bbox[0], bbox[1], bbox[2] - 360, bbox[3]], + eastBbox: [bbox[0] + 360, bbox[1], bbox[2], bbox[3]]}; + } + return {westBbox: bbox, eastBbox: bbox} +} From 45f2009a67413214b104f4a64a7994c436d8c410 Mon Sep 17 00:00:00 2001 From: Alicia Date: Thu, 26 May 2022 15:24:55 +0200 Subject: [PATCH 3/7] fix tile selection for Protected areas based on bbox --- .../protected-areas/protected-areas-tiles.service.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts b/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts index da02e94f3c..ae463731ef 100644 --- a/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts +++ b/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Brackets, Repository } from 'typeorm'; -import { nominatim2bbox } from '@marxan-geoprocessing/utils/bbox.utils'; +import { nominatim2bbox, antimeridianBbox } from '@marxan-geoprocessing/utils/bbox.utils'; import { TileService } from '@marxan-geoprocessing/modules/tile/tile.service'; import { ProtectedArea } from '@marxan/protected-areas'; @@ -73,10 +73,16 @@ export class ProtectedAreasTilesService { } if (bbox) { + const {westBbox, eastBbox} = antimeridianBbox(nominatim2bbox( + bbox + )); subQuery.andWhere( - `st_intersects(ST_MakeEnvelope(:...bbox, 4326), the_geom)`, + `(st_intersects(ST_MakeEnvelope(:...westBbox, 4326), the_geom) + or + st_intersects(ST_MakeEnvelope(:...eastBbox, 4326), the_geom))`, { - bbox: nominatim2bbox(bbox), + westBbox: westBbox, + eastBbox: eastBbox, }, ); } From a0a8afe7ae21853e086d2a3e31ff6b6a211e4c03 Mon Sep 17 00:00:00 2001 From: Alicia Date: Thu, 26 May 2022 16:22:35 +0200 Subject: [PATCH 4/7] fixed bbox filtering based on antimeridian fix --- .../geo-feature-property-sets.service.ts | 36 +++++++++++-------- .../geo-features/geo-features.service.ts | 26 +++++++++----- .../src/modules/features/features.service.ts | 16 +++++++-- .../planning-units/planning-units.job.ts | 4 +-- 4 files changed, 54 insertions(+), 28 deletions(-) diff --git a/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts b/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts index f60e8a5fad..94ae26843c 100644 --- a/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts +++ b/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts @@ -10,6 +10,7 @@ import { GeoFeature } from './geo-feature.api.entity'; import { GeoFeaturePropertySet } from './geo-feature.geo.entity'; import { DbConnections } from '@marxan-api/ormconfig.connections'; import { BBox } from 'geojson'; +import { antimeridianBbox, nominatim2bbox } from '@marxan-geoprocessing/utils/bbox.utils'; @Injectable() export class GeoFeaturePropertySetService { @@ -31,20 +32,27 @@ export class GeoFeaturePropertySetService { .distinct(true) .where(`propertySets.featureId IN (:...ids)`, { ids: geoFeatureIds }); - if (withinBBox) { - query.andWhere( - `st_intersects( - st_makeenvelope(:xmin, :ymin, :xmax, :ymax, 4326), - "propertySets".bbox - )`, - { - xmin: withinBBox[1], - ymin: withinBBox[3], - xmax: withinBBox[0], - ymax: withinBBox[2], - }, - ); - } + if (withinBBox) { + const {westBbox, eastBbox} = antimeridianBbox( + [withinBBox[1],withinBBox[3],withinBBox[0],withinBBox[2]] + ); + query.andWhere( + `(st_intersects( + st_intersection(st_makeenvelope(:...westBbox, 4326), + ST_MakeEnvelope(0, -90, 180, 90, 4326)), + "propertySets".bbox) + or + st_intersects( + st_intersection(st_makeenvelope(:...eastBbox, 4326), + ST_MakeEnvelope(-180, -90, 0, 90, 4326)), + "propertySets".bbox))`, + { + westBbox: westBbox, + eastBbox: eastBbox, + } + , + ); + } return query.getMany(); } diff --git a/api/apps/api/src/modules/geo-features/geo-features.service.ts b/api/apps/api/src/modules/geo-features/geo-features.service.ts index ff50fcac0f..6b402afbd2 100644 --- a/api/apps/api/src/modules/geo-features/geo-features.service.ts +++ b/api/apps/api/src/modules/geo-features/geo-features.service.ts @@ -28,6 +28,7 @@ import { DbConnections } from '@marxan-api/ormconfig.connections'; import { v4 } from 'uuid'; import { UploadShapefileDTO } from '../projects/dto/upload-shapefile.dto'; import { GeoFeaturesRequestInfo } from './geo-features-request-info'; +import { antimeridianBbox } from '@marxan-geoprocessing/utils/bbox.utils'; const geoFeatureFilterKeyNames = [ 'featureClassName', @@ -167,21 +168,28 @@ export class GeoFeaturesService extends AppBaseService< * */ if (projectId && info?.params?.bbox) { + const {westBbox, eastBbox} = antimeridianBbox( + [info.params.bbox[1],info.params.bbox[3], + info.params.bbox[0],info.params.bbox[2]] + ); const geoFeaturesWithinProjectBbox = await this.geoFeaturesGeometriesRepository .createQueryBuilder('geoFeatureGeometries') .select('"geoFeatureGeometries"."feature_id"', 'featureId') .distinctOn(['"geoFeatureGeometries"."feature_id"']) .where( - `st_intersects( - st_makeenvelope(:xmin, :ymin, :xmax, :ymax, 4326), + `(st_intersects( + st_intersection(st_makeenvelope(:...westBbox, 4326), + ST_MakeEnvelope(0, -90, 180, 90, 4326)), "geoFeatureGeometries".the_geom - )`, - { - xmin: info.params.bbox[1], - ymin: info.params.bbox[3], - xmax: info.params.bbox[0], - ymax: info.params.bbox[2], - }, + ) or st_intersects( + st_intersection(st_makeenvelope(:...eastBbox, 4326), + ST_MakeEnvelope(-180, -90, 0, 90, 4326)), + "geoFeatureGeometries".the_geom + ))`, + { + westBbox: westBbox, + eastBbox: eastBbox, + }, ) .getRawMany() .then((result) => result.map((i) => i.featureId)) diff --git a/api/apps/geoprocessing/src/modules/features/features.service.ts b/api/apps/geoprocessing/src/modules/features/features.service.ts index cdea6f09dc..76d7864610 100644 --- a/api/apps/geoprocessing/src/modules/features/features.service.ts +++ b/api/apps/geoprocessing/src/modules/features/features.service.ts @@ -7,7 +7,7 @@ import { IsArray, IsNumber, IsString, IsOptional } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { BBox } from 'geojson'; -import { nominatim2bbox } from '@marxan-geoprocessing/utils/bbox.utils'; +import { antimeridianBbox, nominatim2bbox } from '@marxan-geoprocessing/utils/bbox.utils'; import { TileRequest } from '@marxan/tiles'; @@ -49,9 +49,19 @@ export class FeatureService { let whereQuery = `feature_id = '${id}'`; if (bbox) { - whereQuery += `AND st_intersects(ST_MakeEnvelope(${nominatim2bbox( + const {westBbox, eastBbox} = antimeridianBbox(nominatim2bbox( bbox, - )}, 4326), the_geom)`; + )); + whereQuery += `AND + (st_intersects( + st_intersection(st_makeenvelope(${eastBbox}, 4326), + ST_MakeEnvelope(0, -90, 180, 90, 4326)), + the_geom + ) or st_intersects( + st_intersection(st_makeenvelope(${westBbox}, 4326), + ST_MakeEnvelope(-180, -90, 0, 90, 4326)), + the_geom + ))`; } return whereQuery; } diff --git a/api/apps/geoprocessing/src/modules/planning-units/planning-units.job.ts b/api/apps/geoprocessing/src/modules/planning-units/planning-units.job.ts index 23bcd07763..161f2e56ae 100644 --- a/api/apps/geoprocessing/src/modules/planning-units/planning-units.job.ts +++ b/api/apps/geoprocessing/src/modules/planning-units/planning-units.job.ts @@ -123,7 +123,7 @@ export class PlanningUnitsJobProcessor { st_transform(ST_MakeEnvelope(-180, -90, 180, 90, 4326), 3410)) as geom FROM region, bboxes ) -SELECT +SELECT distinct grid.geom FROM grid, region WHERE ST_Intersects(grid.geom, region.geom) @@ -158,7 +158,7 @@ grid.geom st_transform(ST_MakeEnvelope(-180, -90, 180, 90, 4326), 3410)) as geom FROM region, bboxes ) - SELECT + SELECT distinct grid.geom FROM grid, region WHERE ST_Intersects(grid.geom, region.geom) From 4e9f7b4a7bf7925a10dbc9f35ba27f3408abe399 Mon Sep 17 00:00:00 2001 From: Alicia Date: Thu, 26 May 2022 16:57:50 +0200 Subject: [PATCH 5/7] format and moved bbox utils from geoprocessing to libs --- .../adapters/memory-export.repository.ts | 2 +- .../import-scenario.handler.spec.ts | 2 +- .../geo-feature-property-sets.service.ts | 30 ++++++++++--------- .../geo-features/geo-features.service.ts | 20 +++++++------ api/apps/api/src/utils/json.type.ts | 2 +- .../admin-areas/admin-areas.service.ts | 2 +- .../src/modules/features/features.service.ts | 6 ++-- .../planning-area-tiles.service.ts | 2 +- .../planning-units/planning-units.service.ts | 10 +++---- .../protected-areas-tiles.service.ts | 6 ++-- .../comparison-difference-tile.service.ts | 1 - ...-config.project-piece-exporter.e2e-spec.ts | 2 +- ...protected-areas.piece-exporter.e2e-spec.ts | 4 +-- .../utils/src/geo/bbox.ts} | 12 +++++--- 14 files changed, 52 insertions(+), 49 deletions(-) rename api/{apps/geoprocessing/src/utils/bbox.utils.ts => libs/utils/src/geo/bbox.ts} (75%) diff --git a/api/apps/api/src/modules/clone/export/adapters/memory-export.repository.ts b/api/apps/api/src/modules/clone/export/adapters/memory-export.repository.ts index cf38d676a3..b640298469 100644 --- a/api/apps/api/src/modules/clone/export/adapters/memory-export.repository.ts +++ b/api/apps/api/src/modules/clone/export/adapters/memory-export.repository.ts @@ -28,7 +28,7 @@ export class MemoryExportRepo implements ExportRepository { async findLatestExportsFor( projectId: string, - limit: number = 5, + limit = 5, options?: { isStandalone?: boolean; isFinished?: boolean; diff --git a/api/apps/api/src/modules/clone/import/application/import-scenario.handler.spec.ts b/api/apps/api/src/modules/clone/import/application/import-scenario.handler.spec.ts index 832e6541e3..3ee191788b 100644 --- a/api/apps/api/src/modules/clone/import/application/import-scenario.handler.spec.ts +++ b/api/apps/api/src/modules/clone/import/application/import-scenario.handler.spec.ts @@ -187,7 +187,7 @@ class FakeScenarioRepository { class FakeExportRepository implements ExportRepository { public returnUnfinishedExport = false; - public importResourceId: string = ''; + public importResourceId = ''; async save(exportInstance: Export): Promise> { throw new Error('Method not implemented.'); diff --git a/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts b/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts index 94ae26843c..b4f1013c0d 100644 --- a/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts +++ b/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts @@ -10,7 +10,7 @@ import { GeoFeature } from './geo-feature.api.entity'; import { GeoFeaturePropertySet } from './geo-feature.geo.entity'; import { DbConnections } from '@marxan-api/ormconfig.connections'; import { BBox } from 'geojson'; -import { antimeridianBbox, nominatim2bbox } from '@marxan-geoprocessing/utils/bbox.utils'; +import { antimeridianBbox } from '@marxan/utils/geo/bbox'; @Injectable() export class GeoFeaturePropertySetService { @@ -32,12 +32,15 @@ export class GeoFeaturePropertySetService { .distinct(true) .where(`propertySets.featureId IN (:...ids)`, { ids: geoFeatureIds }); - if (withinBBox) { - const {westBbox, eastBbox} = antimeridianBbox( - [withinBBox[1],withinBBox[3],withinBBox[0],withinBBox[2]] - ); - query.andWhere( - `(st_intersects( + if (withinBBox) { + const { westBbox, eastBbox } = antimeridianBbox([ + withinBBox[1], + withinBBox[3], + withinBBox[0], + withinBBox[2], + ]); + query.andWhere( + `(st_intersects( st_intersection(st_makeenvelope(:...westBbox, 4326), ST_MakeEnvelope(0, -90, 180, 90, 4326)), "propertySets".bbox) @@ -46,13 +49,12 @@ export class GeoFeaturePropertySetService { st_intersection(st_makeenvelope(:...eastBbox, 4326), ST_MakeEnvelope(-180, -90, 0, 90, 4326)), "propertySets".bbox))`, - { - westBbox: westBbox, - eastBbox: eastBbox, - } - , - ); - } + { + westBbox: westBbox, + eastBbox: eastBbox, + }, + ); + } return query.getMany(); } diff --git a/api/apps/api/src/modules/geo-features/geo-features.service.ts b/api/apps/api/src/modules/geo-features/geo-features.service.ts index 6b402afbd2..cda7da0843 100644 --- a/api/apps/api/src/modules/geo-features/geo-features.service.ts +++ b/api/apps/api/src/modules/geo-features/geo-features.service.ts @@ -28,7 +28,7 @@ import { DbConnections } from '@marxan-api/ormconfig.connections'; import { v4 } from 'uuid'; import { UploadShapefileDTO } from '../projects/dto/upload-shapefile.dto'; import { GeoFeaturesRequestInfo } from './geo-features-request-info'; -import { antimeridianBbox } from '@marxan-geoprocessing/utils/bbox.utils'; +import { antimeridianBbox } from '@marxan/utils/geo/bbox'; const geoFeatureFilterKeyNames = [ 'featureClassName', @@ -168,10 +168,12 @@ export class GeoFeaturesService extends AppBaseService< * */ if (projectId && info?.params?.bbox) { - const {westBbox, eastBbox} = antimeridianBbox( - [info.params.bbox[1],info.params.bbox[3], - info.params.bbox[0],info.params.bbox[2]] - ); + const { westBbox, eastBbox } = antimeridianBbox([ + info.params.bbox[1], + info.params.bbox[3], + info.params.bbox[0], + info.params.bbox[2], + ]); const geoFeaturesWithinProjectBbox = await this.geoFeaturesGeometriesRepository .createQueryBuilder('geoFeatureGeometries') .select('"geoFeatureGeometries"."feature_id"', 'featureId') @@ -186,10 +188,10 @@ export class GeoFeaturesService extends AppBaseService< ST_MakeEnvelope(-180, -90, 0, 90, 4326)), "geoFeatureGeometries".the_geom ))`, - { - westBbox: westBbox, - eastBbox: eastBbox, - }, + { + westBbox: westBbox, + eastBbox: eastBbox, + }, ) .getRawMany() .then((result) => result.map((i) => i.featureId)) diff --git a/api/apps/api/src/utils/json.type.ts b/api/apps/api/src/utils/json.type.ts index f053b32aac..b1a68ab31d 100644 --- a/api/apps/api/src/utils/json.type.ts +++ b/api/apps/api/src/utils/json.type.ts @@ -4,4 +4,4 @@ export interface JSONObject { [x: string]: JSONValue; } -export interface JSONArray extends Array {} +export type JSONArray = Array; diff --git a/api/apps/geoprocessing/src/modules/admin-areas/admin-areas.service.ts b/api/apps/geoprocessing/src/modules/admin-areas/admin-areas.service.ts index 9f8c3eee91..912760bc00 100644 --- a/api/apps/geoprocessing/src/modules/admin-areas/admin-areas.service.ts +++ b/api/apps/geoprocessing/src/modules/admin-areas/admin-areas.service.ts @@ -15,7 +15,7 @@ import { import { Transform } from 'class-transformer'; import { BBox } from 'geojson'; import { AdminArea } from '@marxan/admin-regions'; -import { nominatim2bbox } from '@marxan-geoprocessing/utils/bbox.utils'; +import { nominatim2bbox } from '@marxan/utils/geo/bbox'; import { TileRequest } from '@marxan/tiles'; export class TileSpecification extends TileRequest { diff --git a/api/apps/geoprocessing/src/modules/features/features.service.ts b/api/apps/geoprocessing/src/modules/features/features.service.ts index 76d7864610..84ca4f414d 100644 --- a/api/apps/geoprocessing/src/modules/features/features.service.ts +++ b/api/apps/geoprocessing/src/modules/features/features.service.ts @@ -7,7 +7,7 @@ import { IsArray, IsNumber, IsString, IsOptional } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { BBox } from 'geojson'; -import { antimeridianBbox, nominatim2bbox } from '@marxan-geoprocessing/utils/bbox.utils'; +import { antimeridianBbox, nominatim2bbox } from '@marxan/utils/geo/bbox'; import { TileRequest } from '@marxan/tiles'; @@ -49,9 +49,7 @@ export class FeatureService { let whereQuery = `feature_id = '${id}'`; if (bbox) { - const {westBbox, eastBbox} = antimeridianBbox(nominatim2bbox( - bbox, - )); + const { westBbox, eastBbox } = antimeridianBbox(nominatim2bbox(bbox)); whereQuery += `AND (st_intersects( st_intersection(st_makeenvelope(${eastBbox}, 4326), diff --git a/api/apps/geoprocessing/src/modules/planning-area/planning-area-tiles/planning-area-tiles.service.ts b/api/apps/geoprocessing/src/modules/planning-area/planning-area-tiles/planning-area-tiles.service.ts index b86868c169..07f5a8270c 100644 --- a/api/apps/geoprocessing/src/modules/planning-area/planning-area-tiles/planning-area-tiles.service.ts +++ b/api/apps/geoprocessing/src/modules/planning-area/planning-area-tiles/planning-area-tiles.service.ts @@ -27,7 +27,7 @@ export class PlanningAreaTilesService { /** * @todo this generation query is a bit... */ - let whereQuery = `project_id = '${planningAreaId}'`; + const whereQuery = `project_id = '${planningAreaId}'`; return whereQuery; } diff --git a/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts b/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts index 07628601f8..20456cd27c 100644 --- a/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts +++ b/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts @@ -1,11 +1,11 @@ import { TileService } from '@marxan-geoprocessing/modules/tile/tile.service'; -import { nominatim2bbox, antimeridianBbox } from '@marxan-geoprocessing/utils/bbox.utils'; +import { nominatim2bbox, antimeridianBbox } from '@marxan/utils/geo/bbox'; import { PlanningUnitGridShape } from '@marxan/scenarios-planning-unit'; import { TileRequest } from '@marxan/tiles'; import { Inject, Injectable, Logger } from '@nestjs/common'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsArray, IsIn, IsNumber, IsOptional} from 'class-validator'; +import { IsArray, IsIn, IsNumber, IsOptional } from 'class-validator'; import { BBox } from 'geojson'; import { calculateGridSize, @@ -129,9 +129,9 @@ export class PlanningUnitsService { let whereQuery = ``; if (filters?.bbox) { - const {westBbox, eastBbox} = antimeridianBbox(nominatim2bbox( - filters.bbox - )); + const { westBbox, eastBbox } = antimeridianBbox( + nominatim2bbox(filters.bbox), + ); whereQuery = `st_intersects(ST_Transform(st_intersection(ST_MakeEnvelope(${eastBbox}, 4326), ST_MakeEnvelope(0, -90, 180, 90, 4326)), 3857), the_geom) OR diff --git a/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts b/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts index ae463731ef..3df0138796 100644 --- a/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts +++ b/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Brackets, Repository } from 'typeorm'; -import { nominatim2bbox, antimeridianBbox } from '@marxan-geoprocessing/utils/bbox.utils'; +import { nominatim2bbox, antimeridianBbox } from '@marxan/utils/geo/bbox'; import { TileService } from '@marxan-geoprocessing/modules/tile/tile.service'; import { ProtectedArea } from '@marxan/protected-areas'; @@ -73,9 +73,7 @@ export class ProtectedAreasTilesService { } if (bbox) { - const {westBbox, eastBbox} = antimeridianBbox(nominatim2bbox( - bbox - )); + const { westBbox, eastBbox } = antimeridianBbox(nominatim2bbox(bbox)); subQuery.andWhere( `(st_intersects(ST_MakeEnvelope(:...westBbox, 4326), the_geom) or diff --git a/api/apps/geoprocessing/src/modules/scenarios/comparison-difference-tile/comparison-difference-tile.service.ts b/api/apps/geoprocessing/src/modules/scenarios/comparison-difference-tile/comparison-difference-tile.service.ts index 4c4dc4b28a..169c7ae2d4 100644 --- a/api/apps/geoprocessing/src/modules/scenarios/comparison-difference-tile/comparison-difference-tile.service.ts +++ b/api/apps/geoprocessing/src/modules/scenarios/comparison-difference-tile/comparison-difference-tile.service.ts @@ -2,7 +2,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { nominatim2bbox } from '@marxan-geoprocessing/utils/bbox.utils'; import { TileService } from '@marxan-geoprocessing/modules/tile/tile.service'; import { ScenariosPuPaDataGeo } from '@marxan/scenarios-planning-unit'; diff --git a/api/apps/geoprocessing/test/integration/cloning/piece-exporters/export-config.project-piece-exporter.e2e-spec.ts b/api/apps/geoprocessing/test/integration/cloning/piece-exporters/export-config.project-piece-exporter.e2e-spec.ts index 89cd488328..69523c6606 100644 --- a/api/apps/geoprocessing/test/integration/cloning/piece-exporters/export-config.project-piece-exporter.e2e-spec.ts +++ b/api/apps/geoprocessing/test/integration/cloning/piece-exporters/export-config.project-piece-exporter.e2e-spec.ts @@ -96,7 +96,7 @@ const getFixtures = async () => { const getExpectedContent = ( options: FixtureOptions, ): ProjectExportConfigContent => { - let scenarios: Record = {}; + const scenarios: Record = {}; if (options.projectWithScenario) scenarios[scenarioId] = [ClonePiece.ScenarioMetadata]; return { diff --git a/api/apps/geoprocessing/test/integration/cloning/piece-exporters/scenario-protected-areas.piece-exporter.e2e-spec.ts b/api/apps/geoprocessing/test/integration/cloning/piece-exporters/scenario-protected-areas.piece-exporter.e2e-spec.ts index 57ba0b3a26..f1dbd1e2b3 100644 --- a/api/apps/geoprocessing/test/integration/cloning/piece-exporters/scenario-protected-areas.piece-exporter.e2e-spec.ts +++ b/api/apps/geoprocessing/test/integration/cloning/piece-exporters/scenario-protected-areas.piece-exporter.e2e-spec.ts @@ -85,8 +85,8 @@ const getFixtures = async () => { const projectId = v4(); const scenarioId = v4(); let customProtectedAreaId: string = v4(); - let commonProtectedAreasWdpaids: number[] = []; - let commonProtectedAreasIds: string[] = []; + const commonProtectedAreasWdpaids: number[] = []; + const commonProtectedAreasIds: string[] = []; const organizationId = v4(); const sut = sandbox.get(ScenarioProtectedAreasPieceExporter); const apiEntityManager: EntityManager = sandbox.get( diff --git a/api/apps/geoprocessing/src/utils/bbox.utils.ts b/api/libs/utils/src/geo/bbox.ts similarity index 75% rename from api/apps/geoprocessing/src/utils/bbox.utils.ts rename to api/libs/utils/src/geo/bbox.ts index 591d64aa48..3ac95c6fc8 100644 --- a/api/apps/geoprocessing/src/utils/bbox.utils.ts +++ b/api/libs/utils/src/geo/bbox.ts @@ -25,10 +25,14 @@ export function nominatim2bbox(nominatim: BBox): BBox { * @returns {BBox, BBox} west and east bbox split * */ -export function antimeridianBbox(bbox: BBox): {westBbox: BBox, eastBbox: BBox} { +export function antimeridianBbox( + bbox: BBox, +): { westBbox: BBox; eastBbox: BBox } { if (bbox[2] > bbox[0]) { - return {westBbox: [bbox[0], bbox[1], bbox[2] - 360, bbox[3]], - eastBbox: [bbox[0] + 360, bbox[1], bbox[2], bbox[3]]}; + return { + westBbox: [bbox[0], bbox[1], bbox[2] - 360, bbox[3]], + eastBbox: [bbox[0] + 360, bbox[1], bbox[2], bbox[3]], + }; } - return {westBbox: bbox, eastBbox: bbox} + return { westBbox: bbox, eastBbox: bbox }; } From 2e5b37c61b8f5110a0a12d2a41af7008e5756b2a Mon Sep 17 00:00:00 2001 From: Alicia Date: Tue, 31 May 2022 11:44:43 +0200 Subject: [PATCH 6/7] changed how bbox functions are imported and exported in the lib --- .../modules/geo-features/geo-feature-property-sets.service.ts | 2 +- .../src/modules/admin-areas/admin-areas.service.ts | 2 +- api/apps/geoprocessing/src/modules/features/features.service.ts | 2 +- .../src/modules/planning-units/planning-units.service.ts | 2 +- .../modules/protected-areas/protected-areas-tiles.service.ts | 2 +- api/libs/utils/src/geo/index.ts | 1 + 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts b/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts index b4f1013c0d..76d02dd730 100644 --- a/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts +++ b/api/apps/api/src/modules/geo-features/geo-feature-property-sets.service.ts @@ -10,7 +10,7 @@ import { GeoFeature } from './geo-feature.api.entity'; import { GeoFeaturePropertySet } from './geo-feature.geo.entity'; import { DbConnections } from '@marxan-api/ormconfig.connections'; import { BBox } from 'geojson'; -import { antimeridianBbox } from '@marxan/utils/geo/bbox'; +import { antimeridianBbox } from '@marxan/utils/geo'; @Injectable() export class GeoFeaturePropertySetService { diff --git a/api/apps/geoprocessing/src/modules/admin-areas/admin-areas.service.ts b/api/apps/geoprocessing/src/modules/admin-areas/admin-areas.service.ts index 912760bc00..b900e77a27 100644 --- a/api/apps/geoprocessing/src/modules/admin-areas/admin-areas.service.ts +++ b/api/apps/geoprocessing/src/modules/admin-areas/admin-areas.service.ts @@ -15,7 +15,7 @@ import { import { Transform } from 'class-transformer'; import { BBox } from 'geojson'; import { AdminArea } from '@marxan/admin-regions'; -import { nominatim2bbox } from '@marxan/utils/geo/bbox'; +import { nominatim2bbox } from '@marxan/utils/geo'; import { TileRequest } from '@marxan/tiles'; export class TileSpecification extends TileRequest { diff --git a/api/apps/geoprocessing/src/modules/features/features.service.ts b/api/apps/geoprocessing/src/modules/features/features.service.ts index 84ca4f414d..c5c8b40ceb 100644 --- a/api/apps/geoprocessing/src/modules/features/features.service.ts +++ b/api/apps/geoprocessing/src/modules/features/features.service.ts @@ -7,7 +7,7 @@ import { IsArray, IsNumber, IsString, IsOptional } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { BBox } from 'geojson'; -import { antimeridianBbox, nominatim2bbox } from '@marxan/utils/geo/bbox'; +import { antimeridianBbox, nominatim2bbox } from '@marxan/utils/geo'; import { TileRequest } from '@marxan/tiles'; diff --git a/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts b/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts index 20456cd27c..e460240fe9 100644 --- a/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts +++ b/api/apps/geoprocessing/src/modules/planning-units/planning-units.service.ts @@ -1,5 +1,5 @@ import { TileService } from '@marxan-geoprocessing/modules/tile/tile.service'; -import { nominatim2bbox, antimeridianBbox } from '@marxan/utils/geo/bbox'; +import { nominatim2bbox, antimeridianBbox } from '@marxan/utils/geo'; import { PlanningUnitGridShape } from '@marxan/scenarios-planning-unit'; import { TileRequest } from '@marxan/tiles'; import { Inject, Injectable, Logger } from '@nestjs/common'; diff --git a/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts b/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts index 3df0138796..0c0f9b6bc9 100644 --- a/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts +++ b/api/apps/geoprocessing/src/modules/protected-areas/protected-areas-tiles.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Brackets, Repository } from 'typeorm'; -import { nominatim2bbox, antimeridianBbox } from '@marxan/utils/geo/bbox'; +import { nominatim2bbox, antimeridianBbox } from '@marxan/utils/geo'; import { TileService } from '@marxan-geoprocessing/modules/tile/tile.service'; import { ProtectedArea } from '@marxan/protected-areas'; diff --git a/api/libs/utils/src/geo/index.ts b/api/libs/utils/src/geo/index.ts index e98f71dfa1..a2e8c31b45 100644 --- a/api/libs/utils/src/geo/index.ts +++ b/api/libs/utils/src/geo/index.ts @@ -1,3 +1,4 @@ export { defaultSrid } from './spatial-data-format'; export { isFeatureCollection } from './is-feature-collection'; export { decodeMvt } from './decode-mvt'; +export { bbox2Nominatim, nominatim2bbox, antimeridianBbox } from './bbox'; From 20610a64c2f532aedc47d00dad3afba21c3ba7b4 Mon Sep 17 00:00:00 2001 From: Alicia Date: Tue, 31 May 2022 17:36:11 +0200 Subject: [PATCH 7/7] fixed filter --- .../modules/geo-features/geo-features.service.ts | 13 ++++--------- .../src/modules/features/features.service.ts | 4 +++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/api/apps/api/src/modules/geo-features/geo-features.service.ts b/api/apps/api/src/modules/geo-features/geo-features.service.ts index cda7da0843..5a433c6794 100644 --- a/api/apps/api/src/modules/geo-features/geo-features.service.ts +++ b/api/apps/api/src/modules/geo-features/geo-features.service.ts @@ -28,7 +28,7 @@ import { DbConnections } from '@marxan-api/ormconfig.connections'; import { v4 } from 'uuid'; import { UploadShapefileDTO } from '../projects/dto/upload-shapefile.dto'; import { GeoFeaturesRequestInfo } from './geo-features-request-info'; -import { antimeridianBbox } from '@marxan/utils/geo/bbox'; +import { antimeridianBbox, nominatim2bbox } from '@marxan/utils/geo'; const geoFeatureFilterKeyNames = [ 'featureClassName', @@ -168,23 +168,18 @@ export class GeoFeaturesService extends AppBaseService< * */ if (projectId && info?.params?.bbox) { - const { westBbox, eastBbox } = antimeridianBbox([ - info.params.bbox[1], - info.params.bbox[3], - info.params.bbox[0], - info.params.bbox[2], - ]); + const { westBbox, eastBbox } = antimeridianBbox(nominatim2bbox(info.params.bbox)); const geoFeaturesWithinProjectBbox = await this.geoFeaturesGeometriesRepository .createQueryBuilder('geoFeatureGeometries') .select('"geoFeatureGeometries"."feature_id"', 'featureId') .distinctOn(['"geoFeatureGeometries"."feature_id"']) .where( `(st_intersects( - st_intersection(st_makeenvelope(:...westBbox, 4326), + st_intersection(st_makeenvelope(:...eastBbox, 4326), ST_MakeEnvelope(0, -90, 180, 90, 4326)), "geoFeatureGeometries".the_geom ) or st_intersects( - st_intersection(st_makeenvelope(:...eastBbox, 4326), + st_intersection(st_makeenvelope(:...westBbox, 4326), ST_MakeEnvelope(-180, -90, 0, 90, 4326)), "geoFeatureGeometries".the_geom ))`, diff --git a/api/apps/geoprocessing/src/modules/features/features.service.ts b/api/apps/geoprocessing/src/modules/features/features.service.ts index c5c8b40ceb..b6939d2d1a 100644 --- a/api/apps/geoprocessing/src/modules/features/features.service.ts +++ b/api/apps/geoprocessing/src/modules/features/features.service.ts @@ -66,6 +66,7 @@ export class FeatureService { /** * @todo get attributes from Entity, based on user selection + * @todo simplification level based on zoom level */ public findTile( tileSpecification: TileSpecification, @@ -73,7 +74,7 @@ export class FeatureService { ): Promise { const { z, x, y, id } = tileSpecification; const attributes = 'feature_id, properties'; - const table = `(select (st_dump(the_geom)).geom as the_geom, properties, feature_id from "${this.featuresRepository.metadata.tableName}")`; + const table = `(select ST_RemoveRepeatedPoints((st_dump(the_geom)).geom, 0.1) as the_geom, properties, feature_id from "${this.featuresRepository.metadata.tableName}")`; const customQuery = this.buildFeaturesWhereQuery(id, bbox); return this.tileService.getTile({ z, @@ -84,4 +85,5 @@ export class FeatureService { attributes, }); } + }