Skip to content

Commit

Permalink
AER-2977 Geometry validation for ADMS + max vertices (#290)
Browse files Browse the repository at this point in the history
That is, when characteristics dictate certain source types, the geometry should match it.
Also added a test on maximum vertices in the case of a polygon (area or surface).
  • Loading branch information
BertScholten authored Jul 10, 2024
1 parent 5ab7f89 commit e93823e
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,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;

public static final int MAX_POLYGON_CONVEX_VERTICES = 50;

private static final int ADMS_MAX_BUILDINGS_PER_SITUATION = 50;

public static final ADMSLimits INSTANCE = new ADMSLimits();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,12 @@ public enum ImaerExceptionReason implements Reason {
* @param 3 the system definition code of the lodging that was converted.
*/
GML_CONVERTED_LODGING_TO_CUSTOM(5265),
/**
* Geometry contains too many vertices.
*
* @param 0 the ID of the source with too many vertices.
*/
GEOMETRY_TOO_MANY_VERTICES(5271),


// Cohesion (between files) errors.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@
import nl.overheid.aerius.shared.domain.v2.characteristics.ADMSSourceCharacteristics;
import nl.overheid.aerius.shared.domain.v2.characteristics.adms.ADMSLimits;
import nl.overheid.aerius.shared.domain.v2.characteristics.adms.SourceType;
import nl.overheid.aerius.shared.domain.v2.geojson.Geometry;
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;
import nl.overheid.aerius.shared.exception.AeriusException;
import nl.overheid.aerius.shared.exception.ImaerExceptionReason;
import nl.overheid.aerius.util.GeometryUtil;

/**
* Class to validate ADMS source characteristics
Expand All @@ -34,19 +39,18 @@ class ADMSCharacteristicsValidator extends CharacteristicsValidator<ADMSSourceCh
}

@Override
boolean validate(final ADMSSourceCharacteristics characteristics) {
boolean valid = true;
if (characteristics.getSourceType() != SourceType.VOLUME && characteristics.getSourceType() != SourceType.ROAD) {
valid = validateADMSHeatContent(characteristics);
}
boolean validate(final ADMSSourceCharacteristics characteristics, final Geometry sourceGeometry) {
boolean valid = validateGeometry(characteristics, sourceGeometry);
valid = validateADMSHeatContent(characteristics) && valid;
validateADMSVolumeHeight(characteristics);
return valid;
}

private boolean validateADMSHeatContent(final ADMSSourceCharacteristics characteristics) {
boolean valid = true;
if (characteristics.getSpecificHeatCapacity() < ADMSLimits.SOURCE_SPECIFIC_HEAT_CAPACITY_MINIMUM
|| characteristics.getSpecificHeatCapacity() > ADMSLimits.SOURCE_SPECIFIC_HEAT_CAPACITY_MAXIMUM) {
if (expectValidHeatContent(characteristics.getSourceType()) &&
(characteristics.getSpecificHeatCapacity() < ADMSLimits.SOURCE_SPECIFIC_HEAT_CAPACITY_MINIMUM
|| characteristics.getSpecificHeatCapacity() > ADMSLimits.SOURCE_SPECIFIC_HEAT_CAPACITY_MAXIMUM)) {
errors.add(new AeriusException(ImaerExceptionReason.HEAT_CAPACITY_OUT_OF_RANGE, sourceId,
String.valueOf(characteristics.getSpecificHeatCapacity()), String.valueOf(ADMSLimits.SOURCE_SPECIFIC_HEAT_CAPACITY_MINIMUM),
String.valueOf(ADMSLimits.SOURCE_SPECIFIC_HEAT_CAPACITY_MAXIMUM)));
Expand All @@ -55,6 +59,10 @@ private boolean validateADMSHeatContent(final ADMSSourceCharacteristics characte
return valid;
}

private boolean expectValidHeatContent(final SourceType sourceType) {
return sourceType != SourceType.VOLUME && sourceType != SourceType.ROAD;
}

/**
* Warn if the height is larger than half the vertical height (or 2 * height is larger than the vertical dimension).
*
Expand All @@ -65,4 +73,64 @@ private void validateADMSVolumeHeight(final ADMSSourceCharacteristics characteri
warnings.add(new AeriusException(ImaerExceptionReason.SOURCE_VOLUME_FLOATING, sourceId));
}
}

private boolean validateGeometry(final ADMSSourceCharacteristics characteristics, final Geometry sourceGeometry) {
return switch (characteristics.getSourceType()) {
case POINT, JET -> validPointGeometry(sourceGeometry);
case LINE, ROAD -> validLineGeometry(sourceGeometry);
case AREA, VOLUME -> validPolygonGeometry(sourceGeometry);
};
}

private boolean validPointGeometry(final Geometry sourceGeometry) {
boolean valid = true;
if (!(sourceGeometry instanceof Point)) {
errors.add(new AeriusException(ImaerExceptionReason.GML_GEOMETRY_NOT_PERMITTED, sourceId));
valid = false;
}
return valid;
}

private boolean validLineGeometry(final Geometry sourceGeometry) {
boolean valid = true;
if (!(sourceGeometry instanceof LineString)) {
errors.add(new AeriusException(ImaerExceptionReason.GML_GEOMETRY_NOT_PERMITTED, sourceId));
valid = false;
}
return valid;
}

private boolean validPolygonGeometry(final Geometry sourceGeometry) {
boolean valid = true;
if (sourceGeometry instanceof final Polygon polygon) {
valid = validPolygon(polygon);
} else {
errors.add(new AeriusException(ImaerExceptionReason.GML_GEOMETRY_NOT_PERMITTED, sourceId));
valid = false;
}
return valid;
}

private boolean validPolygon(final Polygon polygon) {
boolean valid = true;
// Quick check on nr of vertices. Convex hull always has same or less number of vertices as the original polygon.
// So if original already has less than maximum, the convex hull will also be less.
// + 1 as the begin and end point should be the same and only unique points should be considered anyway.
if (polygon.getCoordinates()[0].length > ADMSLimits.MAX_POLYGON_CONVEX_VERTICES + 1) {
try {
// Convert to convex hull and check nr of vertices again. If this is above the maximum, then it's invalid.
// SRID doesn't matter too much in this case.
final Polygon convexHull = GeometryUtil.toConvexHull(polygon, 0);
if (convexHull.getCoordinates()[0].length > ADMSLimits.MAX_POLYGON_CONVEX_VERTICES + 1) {
errors.add(new AeriusException(ImaerExceptionReason.GEOMETRY_TOO_MANY_VERTICES, sourceId));
valid = false;
}
} catch (final AeriusException e) {
errors.add(e);
valid = false;
}
}
return valid;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.List;

import nl.overheid.aerius.shared.domain.v2.characteristics.SourceCharacteristics;
import nl.overheid.aerius.shared.domain.v2.geojson.Geometry;
import nl.overheid.aerius.shared.exception.AeriusException;

/**
Expand All @@ -41,8 +42,9 @@ abstract class CharacteristicsValidator<T extends SourceCharacteristics> {
* If no validation errors should return true.
*
* @param characteristics characteristics to validate
* @param sourceGeometry the geometry of the associated source
* @return true if no validation errors
*/
abstract boolean validate(final T characteristics);
abstract boolean validate(final T characteristics, final Geometry sourceGeometry);

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import nl.overheid.aerius.shared.domain.ops.OPSLimits;
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.geojson.Geometry;
import nl.overheid.aerius.shared.exception.AeriusException;
import nl.overheid.aerius.shared.exception.ImaerExceptionReason;

Expand All @@ -34,7 +35,7 @@ class OPSCharacteristicsValidator extends CharacteristicsValidator<OPSSourceChar
}

@Override
boolean validate(final OPSSourceCharacteristics characteristics) {
boolean validate(final OPSSourceCharacteristics characteristics, final Geometry sourceGeometry) {
boolean valid = true;
if (characteristics.getHeatContentType() == HeatContentType.NOT_FORCED) {
valid = validateOPSForcedHeatContent(characteristics);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ abstract class SourceValidator<T extends EmissionSource> {
*/
final boolean validate(final T source, final IsFeature feature) {
boolean valid = validateGeometry(feature.getGeometry());
valid = validateCharacteristics(source.getCharacteristics(), source.getGmlId()) && valid;
valid = validateCharacteristics(source.getCharacteristics(), feature.getGeometry(), source.getGmlId()) && valid;
valid = validate(source) && valid;
return valid;
}
Expand All @@ -69,11 +69,11 @@ protected boolean validateGeometry(final Geometry geometry) {
return true;
}

protected boolean validateCharacteristics(final SourceCharacteristics characteristics, final String sourceId) {
if (characteristics instanceof OPSSourceCharacteristics) {
return new OPSCharacteristicsValidator(errors, warnings, sourceId).validate((OPSSourceCharacteristics) characteristics);
} else if (characteristics instanceof ADMSSourceCharacteristics) {
return new ADMSCharacteristicsValidator(errors, warnings, sourceId).validate((ADMSSourceCharacteristics) characteristics);
protected boolean validateCharacteristics(final SourceCharacteristics characteristics, final Geometry sourceGeometry, final String sourceId) {
if (characteristics instanceof final OPSSourceCharacteristics opsChars) {
return new OPSCharacteristicsValidator(errors, warnings, sourceId).validate(opsChars, sourceGeometry);
} else if (characteristics instanceof final ADMSSourceCharacteristics admsChars) {
return new ADMSCharacteristicsValidator(errors, warnings, sourceId).validate(admsChars, sourceGeometry);
} else {
return true;
}
Expand Down
Loading

0 comments on commit e93823e

Please sign in to comment.