From 180fa11356fdc4dcd41a4e622cd6fd9367c7ada7 Mon Sep 17 00:00:00 2001 From: Wouter ten Bosch Date: Mon, 3 Jun 2024 14:03:31 +0200 Subject: [PATCH] AER-2975 Add error on too many buildings --- .../overheid/aerius/gml/base/GMLHelper.java | 6 ++++ .../aerius/importer/ImaerImporter.java | 15 +++++---- .../test/TestValidationAndEmissionHelper.java | 6 ++++ .../aerius/shared/domain/ops/OPSLimits.java | 5 +++ .../domain/v2/building/BuildingLimits.java | 2 ++ .../v2/characteristics/adms/ADMSLimits.java | 7 ++++ .../exception/ImaerExceptionReason.java | 7 ++++ .../aerius/validation/BuildingValidator.java | 13 +++++++- .../ScenarioSituationValidator.java | 6 ++-- .../aerius/validation/ValidationHelper.java | 6 +++- .../validation/BuildingValidatorTest.java | 33 +++++++++++++++---- 11 files changed, 89 insertions(+), 17 deletions(-) diff --git a/source/imaer-gml/src/main/java/nl/overheid/aerius/gml/base/GMLHelper.java b/source/imaer-gml/src/main/java/nl/overheid/aerius/gml/base/GMLHelper.java index b0e31268..4346e2f9 100644 --- a/source/imaer-gml/src/main/java/nl/overheid/aerius/gml/base/GMLHelper.java +++ b/source/imaer-gml/src/main/java/nl/overheid/aerius/gml/base/GMLHelper.java @@ -20,6 +20,7 @@ import nl.overheid.aerius.gml.base.source.ship.v31.GMLInlandShippingSupplier; import nl.overheid.aerius.shared.domain.geo.ReceptorGridSettings; +import nl.overheid.aerius.shared.domain.v2.building.BuildingLimits; import nl.overheid.aerius.shared.domain.v2.source.EmissionSourceFeature; import nl.overheid.aerius.shared.exception.AeriusException; import nl.overheid.aerius.shared.geometry.EmissionSourceLimits; @@ -57,4 +58,9 @@ default EmissionSourceLimits getEmissionSourceGeometryLimits() { // Added default for backward compatibility. Will be removed when used in Calculator. return null; } + + /** + * @return The building limits. + */ + BuildingLimits getBuildingLimits(); } diff --git a/source/imaer-gml/src/main/java/nl/overheid/aerius/importer/ImaerImporter.java b/source/imaer-gml/src/main/java/nl/overheid/aerius/importer/ImaerImporter.java index d066b283..36201706 100644 --- a/source/imaer-gml/src/main/java/nl/overheid/aerius/importer/ImaerImporter.java +++ b/source/imaer-gml/src/main/java/nl/overheid/aerius/importer/ImaerImporter.java @@ -43,6 +43,7 @@ import nl.overheid.aerius.shared.domain.Theme; import nl.overheid.aerius.shared.domain.scenario.SituationType; import nl.overheid.aerius.shared.domain.v2.building.BuildingFeature; +import nl.overheid.aerius.shared.domain.v2.building.BuildingLimits; import nl.overheid.aerius.shared.domain.v2.cimlk.CIMLKCorrection; import nl.overheid.aerius.shared.domain.v2.cimlk.CIMLKDispersionLineFeature; import nl.overheid.aerius.shared.domain.v2.cimlk.CIMLKMeasureFeature; @@ -75,12 +76,14 @@ public class ImaerImporter { private final GMLReaderFactory factory; private final EPSG epsg; - private final EmissionSourceLimits limits; + private final EmissionSourceLimits sourceLimits; + private final BuildingLimits buildingLimits; public ImaerImporter(final GMLHelper gmlHelper) throws AeriusException { factory = GMLReaderFactory.getFactory(gmlHelper); epsg = gmlHelper.getReceptorGridSettings().getEPSG(); - limits = gmlHelper.getEmissionSourceGeometryLimits(); + sourceLimits = gmlHelper.getEmissionSourceGeometryLimits(); + buildingLimits = gmlHelper.getBuildingLimits(); } /** @@ -140,7 +143,7 @@ public void importStream(final InputStream inputStream, final Set addCimlkMeasures(reader, importOptions, result, situation); addCimlkDispersionLines(reader, importOptions, situation); addCimlkCorrections(reader, importOptions, situation); - addBuildings(reader, importOptions, result, situation); + addBuildings(reader, importOptions, result, situation, buildingLimits); addDefinitions(reader, importOptions, result, situation); setCrs(result); @@ -189,7 +192,7 @@ private void addEmissionSources(final GMLReader reader, final Set EmissionSourceValidator.validateSourcesWithEmissions(sources, result.getExceptions(), result.getWarnings()); } if (ImportOption.WARNING_ON_GEOMETRY_LIMITS.in(importOptions)) { - result.getWarnings().addAll(EmissionSourceLimitValidator.checkGeometries(sources, limits)); + result.getWarnings().addAll(EmissionSourceLimitValidator.checkGeometries(sources, sourceLimits)); } result.getSituation().getEmissionSourcesList().addAll(sources); } @@ -342,11 +345,11 @@ private static void addCimlkCorrections(final GMLReader reader, final Set importOptions, final ImportParcel result, - final ScenarioSituation situation) { + final ScenarioSituation situation, final BuildingLimits limits) { if (ImportOption.INCLUDE_SOURCES.in(importOptions)) { final List buildings = reader.getBuildings(); if (ImportOption.VALIDATE_SOURCES.in(importOptions)) { - BuildingValidator.validateBuildings(buildings, result.getExceptions(), result.getWarnings()); + BuildingValidator.validateBuildings(limits, buildings, result.getExceptions(), result.getWarnings()); } situation.getBuildingsList().addAll(buildings); } diff --git a/source/imaer-gml/src/test/java/nl/overheid/aerius/test/TestValidationAndEmissionHelper.java b/source/imaer-gml/src/test/java/nl/overheid/aerius/test/TestValidationAndEmissionHelper.java index 9adc9521..554ea53a 100644 --- a/source/imaer-gml/src/test/java/nl/overheid/aerius/test/TestValidationAndEmissionHelper.java +++ b/source/imaer-gml/src/test/java/nl/overheid/aerius/test/TestValidationAndEmissionHelper.java @@ -31,8 +31,10 @@ import nl.overheid.aerius.gml.base.conversion.PlanConversion; import nl.overheid.aerius.shared.domain.Substance; import nl.overheid.aerius.shared.domain.ops.DiurnalVariation; +import nl.overheid.aerius.shared.domain.v2.building.BuildingLimits; import nl.overheid.aerius.shared.domain.v2.characteristics.HeatContentType; import nl.overheid.aerius.shared.domain.v2.characteristics.OPSSourceCharacteristics; +import nl.overheid.aerius.shared.domain.v2.characteristics.adms.ADMSLimits; import nl.overheid.aerius.shared.domain.v2.geojson.Geometry; import nl.overheid.aerius.shared.domain.v2.source.road.RoadStandardEmissionFactorsKey; import nl.overheid.aerius.shared.domain.v2.source.road.RoadStandardsInterpolationValues; @@ -1139,4 +1141,8 @@ private Optional roadStandard(final RoadStandardEmissionFac } } + @Override + public BuildingLimits buildingLimits() { + return ADMSLimits.INSTANCE; + } } diff --git a/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/ops/OPSLimits.java b/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/ops/OPSLimits.java index 126bcb58..9e15662d 100644 --- a/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/ops/OPSLimits.java +++ b/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/ops/OPSLimits.java @@ -484,4 +484,9 @@ public double buildingLengthMinimum() { public double buildingLengthMaximum() { return SCOPE_BUILDING_LENGTH_MAXIMUM; } + + @Override + public Integer buildingMaximumPerSituation() { + return null; + } } diff --git a/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/v2/building/BuildingLimits.java b/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/v2/building/BuildingLimits.java index 91f650e5..601dc832 100644 --- a/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/v2/building/BuildingLimits.java +++ b/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/v2/building/BuildingLimits.java @@ -43,4 +43,6 @@ default boolean isCircularBuildingSupported() { double buildingLengthMinimum(); double buildingLengthMaximum(); + + Integer buildingMaximumPerSituation(); } diff --git a/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/v2/characteristics/adms/ADMSLimits.java b/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/v2/characteristics/adms/ADMSLimits.java index 57a264c6..80db4129 100644 --- a/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/v2/characteristics/adms/ADMSLimits.java +++ b/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/domain/v2/characteristics/adms/ADMSLimits.java @@ -124,6 +124,8 @@ public final class ADMSLimits implements BuildingLimits { public static final boolean SPATIALLY_VARYING_ROUGHNESS_DEFAULT = true; public static final boolean ADMS_COMPLEX_TERRAIN_DEFAULT = false; + private static final int ADMS_MAX_BUILDINGS_PER_SITUATION = 50; + public static final ADMSLimits INSTANCE = new ADMSLimits(); private ADMSLimits() { @@ -174,4 +176,9 @@ public double buildingLengthMinimum() { public double buildingLengthMaximum() { return BUILDING_LENGTH_MAXIMUM; } + + @Override + public Integer buildingMaximumPerSituation() { + return ADMS_MAX_BUILDINGS_PER_SITUATION; + } } diff --git a/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/exception/ImaerExceptionReason.java b/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/exception/ImaerExceptionReason.java index 6923b005..e17ffa1f 100644 --- a/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/exception/ImaerExceptionReason.java +++ b/source/imaer-shared/src/main/java/nl/overheid/aerius/shared/exception/ImaerExceptionReason.java @@ -641,6 +641,13 @@ public enum ImaerExceptionReason implements Reason { */ CIRCULAR_BUILDING_INCORRECT_DIAMETER(5243), + /** + * There are more buildings in a situation than the maximum + * + * @param 0 The maximum number of allowed buildings + */ + TOO_MANY_BUILDINGS_IN_SITUATION(5244), + /** * Value is <= 0. * diff --git a/source/imaer-util/src/main/java/nl/overheid/aerius/validation/BuildingValidator.java b/source/imaer-util/src/main/java/nl/overheid/aerius/validation/BuildingValidator.java index e0d658ef..b0a02054 100644 --- a/source/imaer-util/src/main/java/nl/overheid/aerius/validation/BuildingValidator.java +++ b/source/imaer-util/src/main/java/nl/overheid/aerius/validation/BuildingValidator.java @@ -20,6 +20,7 @@ import nl.overheid.aerius.shared.domain.v2.building.Building; import nl.overheid.aerius.shared.domain.v2.building.BuildingFeature; +import nl.overheid.aerius.shared.domain.v2.building.BuildingLimits; import nl.overheid.aerius.shared.domain.v2.geojson.Point; import nl.overheid.aerius.shared.domain.v2.geojson.Polygon; import nl.overheid.aerius.shared.exception.AeriusException; @@ -31,13 +32,23 @@ public final class BuildingValidator { // Util class } - public static void validateBuildings(final List buildings, final List errors, + public static void validateBuildings(final BuildingLimits limits, final List buildings, final List errors, final List warnings) { + checkBuildingCount(limits, buildings, errors); checkBuildingGeometry(buildings, errors); checkBuildingHeight(buildings, errors, warnings); checkBuildingDiameter(buildings, errors); } + private static void checkBuildingCount(final BuildingLimits limits, final List buildings, final List errors) { + if (limits.buildingMaximumPerSituation() == null) { + return; + } + if (buildings.size() > limits.buildingMaximumPerSituation()) { + errors.add(new AeriusException(ImaerExceptionReason.TOO_MANY_BUILDINGS_IN_SITUATION, String.valueOf(limits.buildingMaximumPerSituation()))); + } + } + private static void checkBuildingGeometry(final List buildings, final List errors) { for (final BuildingFeature feature : buildings) { // Only polygon and point geometries are supported. diff --git a/source/imaer-util/src/main/java/nl/overheid/aerius/validation/ScenarioSituationValidator.java b/source/imaer-util/src/main/java/nl/overheid/aerius/validation/ScenarioSituationValidator.java index 3a2930de..aead3ef8 100644 --- a/source/imaer-util/src/main/java/nl/overheid/aerius/validation/ScenarioSituationValidator.java +++ b/source/imaer-util/src/main/java/nl/overheid/aerius/validation/ScenarioSituationValidator.java @@ -44,7 +44,7 @@ private ScenarioSituationValidator() { */ public static void validateSituation(final ScenarioSituation situation, final ValidationHelper validationHelper) throws AeriusException { validateSources(situation.getEmissionSourcesList(), validationHelper); - validateBuildings(situation.getBuildingsList()); + validateBuildings(situation.getBuildingsList(), validationHelper); validateCimlkMeasures(situation.getCimlkMeasuresList()); validateDefinitions(situation.getDefinitions()); } @@ -57,9 +57,9 @@ public static void validateSources(final List sources, fi } } - public static void validateBuildings(final List buildings) throws AeriusException { + public static void validateBuildings(final List buildings, final ValidationHelper validationHelper) throws AeriusException { final List errors = new ArrayList<>(); - BuildingValidator.validateBuildings(buildings, errors, new ArrayList<>()); + BuildingValidator.validateBuildings(validationHelper.buildingLimits(), buildings, errors, new ArrayList<>()); if (!errors.isEmpty()) { throw errors.get(0); } diff --git a/source/imaer-util/src/main/java/nl/overheid/aerius/validation/ValidationHelper.java b/source/imaer-util/src/main/java/nl/overheid/aerius/validation/ValidationHelper.java index b3f59865..12910d78 100644 --- a/source/imaer-util/src/main/java/nl/overheid/aerius/validation/ValidationHelper.java +++ b/source/imaer-util/src/main/java/nl/overheid/aerius/validation/ValidationHelper.java @@ -16,8 +16,10 @@ */ package nl.overheid.aerius.validation; +import nl.overheid.aerius.shared.domain.v2.building.BuildingLimits; + /** - * Interface to help with validating sources. + * Interface to help with validating sources and buildings. */ public interface ValidationHelper { @@ -40,4 +42,6 @@ default FarmAnimalHousingValidationHelper farmAnimalHousingValidation() { InlandShippingValidationHelper inlandShippingValidation(); MaritimeShippingValidationHelper maritimeShippingValidation(); + + BuildingLimits buildingLimits(); } diff --git a/source/imaer-util/src/test/java/nl/overheid/aerius/validation/BuildingValidatorTest.java b/source/imaer-util/src/test/java/nl/overheid/aerius/validation/BuildingValidatorTest.java index 486c8099..1bfe7e83 100644 --- a/source/imaer-util/src/test/java/nl/overheid/aerius/validation/BuildingValidatorTest.java +++ b/source/imaer-util/src/test/java/nl/overheid/aerius/validation/BuildingValidatorTest.java @@ -21,11 +21,14 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.IntStream; import org.junit.jupiter.api.Test; import nl.overheid.aerius.shared.domain.v2.building.Building; import nl.overheid.aerius.shared.domain.v2.building.BuildingFeature; +import nl.overheid.aerius.shared.domain.v2.building.BuildingLimits; +import nl.overheid.aerius.shared.domain.v2.characteristics.adms.ADMSLimits; import nl.overheid.aerius.shared.domain.v2.geojson.LineString; import nl.overheid.aerius.shared.domain.v2.geojson.Point; import nl.overheid.aerius.shared.domain.v2.geojson.Polygon; @@ -34,6 +37,7 @@ class BuildingValidatorTest { + private static final BuildingLimits BUILDING_LIMITS = ADMSLimits.INSTANCE; private static final String BUILDING_ID = "OurBuildingId"; private static final String BUILDING_LABEL = "One More Building"; @@ -44,7 +48,7 @@ void testValidBuilding() { final List errors = new ArrayList<>(); final List warnings = new ArrayList<>(); - BuildingValidator.validateBuildings(List.of(building), errors, warnings); + BuildingValidator.validateBuildings(BUILDING_LIMITS, List.of(building), errors, warnings); assertEquals(List.of(), errors, "No errors"); assertEquals(List.of(), warnings, "No warnings"); @@ -58,7 +62,7 @@ void testBuildingLineStringGeometry() { final List errors = new ArrayList<>(); final List warnings = new ArrayList<>(); - BuildingValidator.validateBuildings(List.of(building), errors, warnings); + BuildingValidator.validateBuildings(BUILDING_LIMITS, List.of(building), errors, warnings); assertEquals(1, errors.size(), "Number of errors"); assertEquals(ImaerExceptionReason.GML_GEOMETRY_NOT_PERMITTED, errors.get(0).getReason(), "Error reason"); @@ -73,7 +77,7 @@ void testBuildingZeroHeight() { final List errors = new ArrayList<>(); final List warnings = new ArrayList<>(); - BuildingValidator.validateBuildings(List.of(building), errors, warnings); + BuildingValidator.validateBuildings(BUILDING_LIMITS, List.of(building), errors, warnings); assertEquals(List.of(), errors, "No errors"); assertEquals(1, warnings.size(), "Number of warnings"); @@ -88,7 +92,7 @@ void testBuildingNegativeHeight() { final List errors = new ArrayList<>(); final List warnings = new ArrayList<>(); - BuildingValidator.validateBuildings(List.of(building), errors, warnings); + BuildingValidator.validateBuildings(BUILDING_LIMITS, List.of(building), errors, warnings); assertEquals(1, errors.size(), "Number of errors"); assertEquals(ImaerExceptionReason.BUILDING_HEIGHT_TOO_LOW, errors.get(0).getReason(), "Error reason"); @@ -105,7 +109,7 @@ void testValidCircularBuilding() { final List errors = new ArrayList<>(); final List warnings = new ArrayList<>(); - BuildingValidator.validateBuildings(List.of(building), errors, warnings); + BuildingValidator.validateBuildings(BUILDING_LIMITS, List.of(building), errors, warnings); assertEquals(List.of(), errors, "No errors"); assertEquals(List.of(), warnings, "No warnings"); @@ -120,7 +124,7 @@ void testCircularBuildingIncorrectDiameter() { final List errors = new ArrayList<>(); final List warnings = new ArrayList<>(); - BuildingValidator.validateBuildings(List.of(building), errors, warnings); + BuildingValidator.validateBuildings(BUILDING_LIMITS, List.of(building), errors, warnings); assertEquals(1, errors.size(), "Number of errors"); assertEquals(ImaerExceptionReason.CIRCULAR_BUILDING_INCORRECT_DIAMETER, errors.get(0).getReason(), "Error reason"); @@ -128,6 +132,23 @@ void testCircularBuildingIncorrectDiameter() { assertEquals(List.of(), warnings, "No warnings"); } + @Test + void testTooManyBuildings() { + final BuildingFeature building = createBuilding(1); + building.setGeometry(new Point(0, 0)); + building.getProperties().setDiameter(1); + + final List errors = new ArrayList<>(); + final List warnings = new ArrayList<>(); + + BuildingValidator.validateBuildings(BUILDING_LIMITS, IntStream.range(1, 80).mapToObj(s -> building).toList(), errors, warnings); + + assertEquals(1, errors.size(), "Number of errors"); + assertEquals(ImaerExceptionReason.TOO_MANY_BUILDINGS_IN_SITUATION, errors.get(0).getReason(), "Error reason"); + assertArrayEquals(new Object[] {"50"}, errors.get(0).getArgs(), "Arguments"); + assertEquals(List.of(), warnings, "No warnings"); + } + private static BuildingFeature createBuilding(final double height) { final BuildingFeature feature = new BuildingFeature(); feature.setGeometry(new Polygon());