Commit 1da24add authored by Matthias Betz's avatar Matthias Betz
Browse files

Merge branch 'CheckEngineRework' into 'master'

Check engine rework

See merge request betzms/citydoctor2!4
parents 09470a4d cd494d75
Pipeline #2115 passed with stage
in 2 minutes and 53 seconds
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.check;
public class GlobalParameters {
private GlobalParameters() {
}
public static final String SCHEMATRON_PATH = "schematronPath";
public static final String MIN_VERTEX_DISTANCE = "minVertexDistance";
public static final String NUMBER_OF_ROUNDING_PLACES = "numberOfRoundingPlaces";
}
......@@ -29,7 +29,7 @@ import java.util.Map;
* @author Matthias Betz
*
*/
public class CheckConfiguration implements Serializable {
public class RequirementConfiguration implements Serializable {
private static final long serialVersionUID = -1258195428669813888L;
......@@ -59,10 +59,7 @@ public class CheckConfiguration implements Serializable {
}
return parameters;
}
/**
* @param parameters Sets the parameters of this check.
*/
public void setParameters(Map<String, String> parameters) {
this.parameters = parameters;
}
......
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.check;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import org.citygml4j.factory.GMLGeometryFactory;
import org.citygml4j.model.citygml.core.CityModel;
import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import de.hft.stuttgart.citydoctor2.checks.SvrlContentHandler;
import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.LandObject;
import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject;
import de.hft.stuttgart.citydoctor2.datastructure.Vegetation;
import de.hft.stuttgart.citydoctor2.datastructure.WaterObject;
import de.hft.stuttgart.citydoctor2.parser.CityGmlConsumer;
import de.hft.stuttgart.citydoctor2.reporting.XmlStreamReporter;
import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfStreamReporter;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.citydoctor2.utils.QualityADEUtils;
import de.hft.stuttgart.quality.model.Validation;
import de.hft.stuttgart.quality.model.jaxb.ErrorStatistics;
import de.hft.stuttgart.quality.model.jaxb.FeatureStatistics;
import de.hft.stuttgart.quality.model.jaxb.Statistics;
public class StreamCityGmlConsumer implements CityGmlConsumer {
private Checker c;
private XmlStreamReporter xmlReporter;
private PdfStreamReporter pdfReporter;
private SvrlContentHandler handler;
private Map<ErrorId, AtomicInteger> errorCount;
private GMLGeometryFactory gmlFactory;
private ValidationConfiguration config;
private Statistics statistics;
private FeatureStatistics buildingStatistics;
private FeatureStatistics bridgeStatistics;
private FeatureStatistics transportationStatistics;
private FeatureStatistics vegetationStatistics;
private FeatureStatistics landStatistics;
private FeatureStatistics waterStatistics;
private Validation val;
private FeatureCheckedListener l;
public StreamCityGmlConsumer(Checker c, XmlStreamReporter xmlReporter, PdfStreamReporter pdfReporter,
SvrlContentHandler handler, ValidationConfiguration config, FeatureCheckedListener l) {
this.c = c;
this.xmlReporter = xmlReporter;
this.pdfReporter = pdfReporter;
this.handler = handler;
this.config = config;
this.l = l;
errorCount = new HashMap<>();
gmlFactory = new GMLGeometryFactory();
val = new Validation();
val.setValidationDate(ZonedDateTime.now());
val.setValidationSoftware("CityDoctor " + Localization.getText(Localization.VERSION));
statistics = new Statistics();
buildingStatistics = new FeatureStatistics();
statistics.setNumErrorBuildings(buildingStatistics);
bridgeStatistics = new FeatureStatistics();
statistics.setNumErrorBridgeObjects(bridgeStatistics);
transportationStatistics = new FeatureStatistics();
statistics.setNumErrorTransportation(transportationStatistics);
vegetationStatistics = new FeatureStatistics();
statistics.setNumErrorVegetation(vegetationStatistics);
landStatistics = new FeatureStatistics();
statistics.setNumErrorLandObjects(landStatistics);
waterStatistics = new FeatureStatistics();
statistics.setNumErrorWaterObjects(waterStatistics);
}
@Override
public void accept(CityObject co) {
c.checkFeature(xmlReporter, pdfReporter, co);
if (handler != null) {
List<SchematronError> errors = handler.getFeatureErrors().get(co.getGmlId().getGmlString());
if (errors != null) {
Checker.handleSchematronErrorsForCityObject(errors, co);
}
}
// remove existing quality ade datastructure if existing
QualityADEUtils.removeValidationResult(co);
// store quality ade datastructures in cityobject
QualityADEUtils.writeQualityAde(co);
// recreate geometry
co.reCreateGeometries(gmlFactory, config.getParserConfiguration());
// store result in statistics
applyToStatistics(buildingStatistics, bridgeStatistics, transportationStatistics, vegetationStatistics,
landStatistics, waterStatistics, co);
// add errors to statistics
List<CheckError> errorList = new ArrayList<>();
co.collectContainedErrors(errorList);
Set<CheckError> errors = new HashSet<>(errorList);
for (CheckError e : errors) {
errorCount.compute(e.getErrorId(), (k, v) -> {
if (v == null) {
return new AtomicInteger(1);
}
v.incrementAndGet();
return v;
});
}
if (l != null) {
l.featureChecked(co);
}
}
@Override
public void accept(CityModel cm) {
QualityADEUtils.removeValidation(cm);
for (Entry<ErrorId, AtomicInteger> e : errorCount.entrySet()) {
ErrorStatistics stats = new ErrorStatistics();
stats.setAmount(e.getValue().get());
stats.setName(QualityADEUtils.mapErrorIdToAdeId(e.getKey()));
statistics.getErrorStatistics().add(stats);
}
val.setStatistics(statistics);
val.setValidationPlan(c.createValidationPlan());
cm.addGenericApplicationPropertyOfCityModel(val);
}
private static void applyToStatistics(FeatureStatistics buildingStatistics, FeatureStatistics bridgeStatistics,
FeatureStatistics transportationStatistics, FeatureStatistics vegetationStatistics,
FeatureStatistics landStatistics, FeatureStatistics waterStatistics, CityObject co) {
if (co.isValidated()) {
if (co instanceof Building) {
countForFeatureStatistics(buildingStatistics, co);
} else if (co instanceof TransportationObject) {
countForFeatureStatistics(transportationStatistics, co);
} else if (co instanceof BridgeObject) {
countForFeatureStatistics(bridgeStatistics, co);
} else if (co instanceof WaterObject) {
countForFeatureStatistics(waterStatistics, co);
} else if (co instanceof LandObject) {
countForFeatureStatistics(landStatistics, co);
} else if (co instanceof Vegetation) {
countForFeatureStatistics(vegetationStatistics, co);
}
}
}
private static void countForFeatureStatistics(FeatureStatistics featureStatistics, CityObject co) {
featureStatistics.setNumChecked(featureStatistics.getNumChecked() + 1);
if (co.containsAnyError()) {
featureStatistics.setNumErrors(featureStatistics.getNumErrors() + 1);
}
}
}
......@@ -28,6 +28,7 @@ import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -40,7 +41,6 @@ import org.yaml.snakeyaml.representer.Representer;
import de.hft.stuttgart.citydoctor2.checks.CheckPrototype;
import de.hft.stuttgart.citydoctor2.checks.Checks;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.Localization;
/**
* The validation configuration class represented in the yaml configuration
......@@ -52,19 +52,17 @@ import de.hft.stuttgart.citydoctor2.utils.Localization;
*/
public class ValidationConfiguration implements Serializable {
private static final String NUMBER_OF_ROUNDING_PLACES_DEFAULT = "8";
private static final String MIN_VERTEX_DISTANCE_DEFAULT = "0.0001";
private static final long serialVersionUID = -8020055032177740646L;
private static final Logger logger = LogManager.getLogger(ValidationConfiguration.class);
private int numberOfRoundingPlaces = 8;
private double minVertexDistance = 0.0001;
private String schematronFilePath = null;
private Map<String, String> globalParameters;
private boolean xmlValidation = false;
private boolean useStreaming = false;
private FilterConfiguration filter;
private Map<CheckId, CheckConfiguration> checks;
private Map<String, RequirementConfiguration> requirements;
private ParserConfiguration parserConfig;
......@@ -81,19 +79,20 @@ public class ValidationConfiguration implements Serializable {
public static ValidationConfiguration loadStandardValidationConfig() {
ValidationConfiguration config = new ValidationConfiguration();
config.checks = new HashMap<>();
config.requirements = new HashMap<>();
for (CheckPrototype c : Checks.getCheckPrototypes()) {
CheckConfiguration cConfig = new CheckConfiguration();
cConfig.setEnabled(true);
if (!c.getDefaultParameter().isEmpty()) {
Map<String, String> paramMap = new HashMap<>();
for (DefaultParameter param : c.getDefaultParameter()) {
paramMap.put(param.getName(), param.getDefaultValue());
for (Requirement req : c.checksRequirements()) {
RequirementConfiguration reqConfig = new RequirementConfiguration();
reqConfig.setEnabled(true);
for (DefaultParameter param : req.getDefaultParameter()) {
reqConfig.getParameters().put(param.getName(), param.getValue());
}
cConfig.setParameters(paramMap);
config.requirements.put(req.getId(), reqConfig);
}
config.checks.put(c.getCheckId(), cConfig);
}
config.globalParameters = new HashMap<>();
config.globalParameters.put(GlobalParameters.NUMBER_OF_ROUNDING_PLACES, NUMBER_OF_ROUNDING_PLACES_DEFAULT);
config.globalParameters.put(GlobalParameters.MIN_VERTEX_DISTANCE, MIN_VERTEX_DISTANCE_DEFAULT);
return config;
}
......@@ -117,14 +116,6 @@ public class ValidationConfiguration implements Serializable {
this.xmlValidation = xmlValidation;
}
public String getSchematronFilePath() {
return schematronFilePath;
}
public void setSchematronFilePath(String schematronFilePath) {
this.schematronFilePath = schematronFilePath;
}
public boolean isUseStreaming() {
return useStreaming;
}
......@@ -141,88 +132,67 @@ public class ValidationConfiguration implements Serializable {
this.filter = filter;
}
public Map<CheckId, CheckConfiguration> getChecks() {
if (checks == null) {
checks = new HashMap<>();
public Map<String, RequirementConfiguration> getRequirements() {
if (requirements == null) {
requirements = new HashMap<>();
}
return checks;
return requirements;
}
public void setChecks(Map<CheckId, CheckConfiguration> checks) {
this.checks = checks;
public Map<String, String> getGlobalParameters() {
if (globalParameters == null) {
globalParameters = new HashMap<>();
}
return globalParameters;
}
public void setGlobalParameters(Map<String, String> globalParameters) {
this.globalParameters = globalParameters;
}
/**
* Validates the configuration, adds all missing checks as enabled, reenables
* checks that are necessary to perform other checks
* Validates the configuration, adds all missing requirements as enabled.
*
* @return true if it reenabled disabled checks, false otherwise
*/
public boolean validateConfiguration() {
for (CheckPrototype c : Checks.getCheckPrototypes()) {
// enable all checks that are missing in the validation config
checks.computeIfAbsent(c.getCheckId(), id -> {
CheckConfiguration cConfig = new CheckConfiguration();
cConfig.setEnabled(true);
return cConfig;
});
}
boolean reenabledChecks = reenableNecessaryChecks();
if (schematronFilePath != null && !schematronFilePath.isEmpty()) {
File f = new File(schematronFilePath);
if (!f.exists() || !f.isFile()) {
schematronFilePath = null;
if (logger.isWarnEnabled()) {
logger.warn(Localization.getText("ValidationConfiguration.missingSchematron"), f.getAbsolutePath());
public void validateConfiguration() {
logger.trace("Validating configuration");
Map<String, Requirement> availableRequirements = Checks.getAvailableRequirements();
for (Entry<String, RequirementConfiguration> entry : requirements.entrySet()) {
Requirement req = availableRequirements.get(entry.getKey());
if (req != null) {
for (DefaultParameter param : req.getDefaultParameter()) {
RequirementConfiguration config = entry.getValue();
config.getParameters().computeIfAbsent(param.getName(), k -> param.getValue());
}
}
}
return reenabledChecks;
}
private boolean reenableNecessaryChecks() {
boolean reenabledChecks = false;
for (java.util.Map.Entry<CheckId, CheckConfiguration> e : checks.entrySet()) {
if (!e.getValue().isEnabled()) {
continue;
}
CheckPrototype c = Checks.getCheckPrototypeForId(e.getKey());
for (CheckId dep : c.getDependencies()) {
CheckConfiguration checkConfig = checks.get(dep);
if (!checkConfig.isEnabled()) {
checkConfig.setEnabled(true);
reenabledChecks = true;
if (logger.isWarnEnabled()) {
logger.warn(Localization.getText("ValidationConfiguration.reenable"), dep, c.getCheckId());
}
for (Requirement req : availableRequirements.values()) {
requirements.computeIfAbsent(req.getId(), id -> {
RequirementConfiguration cConfig = new RequirementConfiguration();
cConfig.setEnabled(true);
for (DefaultParameter param : req.getDefaultParameter()) {
cConfig.getParameters().put(param.getName(), param.getValue());
}
}
insertMissingParametersWithDefaultParameters(e, c);
}
return reenabledChecks;
}
private void insertMissingParametersWithDefaultParameters(java.util.Map.Entry<CheckId, CheckConfiguration> e,
CheckPrototype c) {
for (DefaultParameter param : c.getDefaultParameter()) {
String configuredValue = e.getValue().getParameters().get(param.getName());
if (configuredValue == null) {
e.getValue().getParameters().put(param.getName(), param.getDefaultValue());
}
return cConfig;
});
}
globalParameters.computeIfAbsent(GlobalParameters.MIN_VERTEX_DISTANCE, k -> MIN_VERTEX_DISTANCE_DEFAULT);
globalParameters.computeIfAbsent(GlobalParameters.NUMBER_OF_ROUNDING_PLACES,
k -> NUMBER_OF_ROUNDING_PLACES_DEFAULT);
}
public int getNumberOfRoundingPlaces() {
return numberOfRoundingPlaces;
return Integer.parseInt(globalParameters.get(GlobalParameters.NUMBER_OF_ROUNDING_PLACES));
}
public void setNumberOfRoundingPlaces(int numberOfRoundingPlaces) {
this.numberOfRoundingPlaces = numberOfRoundingPlaces;
public void setNumberOfRoundingPlacesInGlobalParameters(int numberOfRoundingPlaces) {
globalParameters.put(GlobalParameters.NUMBER_OF_ROUNDING_PLACES, Integer.toString(numberOfRoundingPlaces));
}
public ParserConfiguration getParserConfiguration() {
if (parserConfig == null) {
parserConfig = new ParserConfiguration(numberOfRoundingPlaces, xmlValidation);
parserConfig = new ParserConfiguration(getNumberOfRoundingPlaces(), xmlValidation);
}
return parserConfig;
}
......@@ -231,11 +201,31 @@ public class ValidationConfiguration implements Serializable {
this.parserConfig = parserConfig;
}
public void setMinVertexDistance(double minVertexDistance) {
this.minVertexDistance = minVertexDistance;
public void setMinVertexDistanceInGlobalParameters(double minVertexDistance) {
globalParameters.put(GlobalParameters.MIN_VERTEX_DISTANCE, Double.toString(minVertexDistance));
}
public double getMinVertexDistance() {
return minVertexDistance;
return Double.parseDouble(globalParameters.get(GlobalParameters.MIN_VERTEX_DISTANCE));
}
public String getSchematronFilePath() {
return globalParameters.get(GlobalParameters.SCHEMATRON_PATH);
}
public String getNumberOfRoundingPlacesAsString() {
return globalParameters.get(GlobalParameters.NUMBER_OF_ROUNDING_PLACES);
}
public String getMinVertexDistanceAsString() {
return globalParameters.get(GlobalParameters.MIN_VERTEX_DISTANCE);
}
public void setSchematronFilePathInGlobalParameters(String string) {
globalParameters.put(GlobalParameters.SCHEMATRON_PATH, string);
}
public void setRequirements(Map<String, RequirementConfiguration> requirements) {
this.requirements = requirements;
}
}
......@@ -20,6 +20,7 @@ package de.hft.stuttgart.citydoctor2.checks;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -28,9 +29,9 @@ import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.DefaultParameter;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding;
......@@ -68,6 +69,11 @@ public class CheckContainer extends Check {
public CheckContainer(Check check) {
this.check = check;
}
@Override
public Set<Requirement> appliesToRequirements() {
return check.appliesToRequirements();
}
@Override
public void init(Map<String, String> parameters, ParserConfiguration config) {
......@@ -98,11 +104,6 @@ public class CheckContainer extends Check {
c.addCheckResult(cr);
}
@Override
public List<DefaultParameter> getDefaultParameter() {
return check.getDefaultParameter();
}
@Override
public List<CheckId> getDependencies() {
return check.getDependencies();
......@@ -244,7 +245,7 @@ public class CheckContainer extends Check {
}
@Override
public CheckType getType() {
public RequirementType getType() {
return check.getType();
}
......
......@@ -19,11 +19,12 @@
package de.hft.stuttgart.citydoctor2.checks;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.DefaultParameter;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
/**
* This class is for having a read only access to a check for accessing meta
......@@ -58,12 +59,12 @@ public class CheckPrototype {
return c.getDependencies();
}
public List<DefaultParameter> getDefaultParameter() {
return c.getDefaultParameter();
public RequirementType getType() {
return c.getType();
}
public CheckType getType() {
return c.getType();
public Set<Requirement> checksRequirements() {
return c.appliesToRequirements();
}
}
......@@ -29,15 +29,15 @@ import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.checks.geometry.AllPolygonsWrongOrientationCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.DuplicatePointsCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.FaceOutCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.HoleOutsideCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.InteriorDisconnectedCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.ManifoldVertexCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.MultipleConnectedComponentCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.NestedRingsCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.NonManifoldEdgeCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.NumPointsCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.PlanarCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.PolygonIntersectingRingsCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.PolygonSameOrientationCheck;
......@@ -46,6 +46,7 @@ import de.hft.stuttgart.citydoctor2.checks.geometry.RingNotClosedCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.RingSelfIntCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.SolidNotClosedCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.SolidSelfIntCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.TooFewPointsCheck;
import de.hft.stuttgart.citydoctor2.checks.geometry.TooFewPolygonsCheck;
import de.hft.stuttgart.citydoctor2.checks.semantics.IsCeilingCheck;
import de.hft.stuttgart.citydoctor2.checks.semantics.IsFloorCheck;
......@@ -62,11 +63,15 @@ import de.hft.stuttgart.citydoctor2.utils.Localization;
*
*/
public class Checks {
public static final double MIN_VERTEX_DISTANCE_DEFAULT = 0.0001;
private static final Logger logger = LogManager.getLogger(Checks.class);
private static List<CheckPrototype> checkPrototypes;
private static Map<CheckId, CheckPrototype> prototypeMap;
private static Map<String, Requirement> availableRequirements;
private Map<CheckId, Check> checkMap;
......@@ -75,21 +80,26 @@ public class Checks {
prototypeMap = new HashMap<>();
// add new checks here
publish(new NumPointsCheck());
// ring checks
publish(new TooFewPointsCheck());
publish(new RingNotClosedCheck());
publish(new DuplicatePointsCheck());
publish(new RingSelfIntCheck());
// polygon checks
publish(new PlanarCheck());
publish(new PolygonSameOrientationCheck());
publish(new HoleOutsideCheck());
publish(new NestedRingsCheck());
publish(new PolygonIntersectingRingsCheck());
publish(new InteriorDisconnectedCheck());
// solid checks
publish(new MultipleConnectedComponentCheck());
publish(new SolidNotClosedCheck());
publish(new NonManifoldEdgeCheck());
publish(new PolygonWrongOrientationCheck());
publish(new FaceOutCheck());
publish(new AllPolygonsWrongOrientationCheck());
publish(new TooFewPolygonsCheck());
publish(new ManifoldVertexCheck());
publish(new SolidSelfIntCheck());
......@@ -121,6 +131,30 @@ public class Checks {
}
}
private static Map<String, Requirement> collectRequirements() {
Map<String, Requirement> requirements = new HashMap<>();
for (CheckPrototype proto : checkPrototypes) {
for (Requirement req : proto.checksRequirements()) {
requirements.put(req.getId(), req);
}
}
return requirements;
}
/**
* Gets all requirements for which there are checks that can verify the
* requirements. This is dependent on the available checks as only the checks
* know which requirements they check.
*
* @return a set of available requirements
*/
public static Map<String, Requirement> getAvailableRequirements() {
if (availableRequirements == null) {
availableRequirements = collectRequirements();
}
return availableRequirements;
}
private static void publish(Check check) {
CheckPrototype prototype = new CheckPrototype(check);
checkPrototypes.add(prototype);
......
......@@ -21,14 +21,17 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
......@@ -38,11 +41,12 @@ import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
/**
* Checks whether all polygons have the wrong orientation.
*
* @author Matthias Betz - 71bema1mst@hft-stuttgart.de
* @author Matthias Betz
*
*/
public class FaceOutCheck extends Check {
public class AllPolygonsWrongOrientationCheck extends Check {
private static final List<CheckId> dependencies;
......@@ -171,15 +175,20 @@ public class FaceOutCheck extends Check {
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION);
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
public Check createNewInstance() {
return new FaceOutCheck();
return new AllPolygonsWrongOrientationCheck();
}
@Override
......
......@@ -22,16 +22,19 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError;
import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
......@@ -44,7 +47,7 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
* <dd>
* <dd>CP_CLOSE</dd></dd>
*
* @author Matthias Betz - 12bema1bif@hft-stuttgart.de
* @author Matthias Betz
*
*/
public class DuplicatePointsCheck extends Check {
......@@ -68,9 +71,7 @@ public class DuplicatePointsCheck extends Check {
@Override
public void init(Map<String, String> params, ParserConfiguration config) {
String epsilonString = params.get(EPSILON_NAME);
if (epsilonString == null) {
epsilon = 0.0001;
} else {
if (epsilonString != null) {
epsilon = Double.parseDouble(epsilonString);
}
}
......@@ -79,6 +80,11 @@ public class DuplicatePointsCheck extends Check {
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.asSet(Requirement.R_GE_R_CONSECUTIVE_POINTS_SAME, Requirement.R_GE_R_SELF_INTERSECTION);
}
@Override
public void check(LinearRing lr) {
......@@ -99,8 +105,8 @@ public class DuplicatePointsCheck extends Check {
// ignore last point, because last point = first, but this is allowed
for (int i = 0; i < pointList.size() - 2; i++) {
Vertex point1 = pointList.get(i);
for (int j = i + 2; j < pointList.size() - 1; j++) {
Vertex point1 = pointList.get(i);
Vertex point2 = pointList.get(j);
if (point1.equalsWithEpsilon(point2, epsilon)) {
// non consecutive points same
......@@ -116,8 +122,8 @@ public class DuplicatePointsCheck extends Check {
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -21,14 +21,17 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.PolygonHoleOutsideError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
......@@ -79,10 +82,15 @@ public class HoleOutsideCheck extends Check {
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_P_HOLE_OUTSIDE);
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -23,14 +23,17 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
......@@ -38,12 +41,18 @@ import de.hft.stuttgart.citydoctor2.math.Segment3d;
import de.hft.stuttgart.citydoctor2.math.graph.CycleNode;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
/**
* Checks if a polygon is split up into multiple polygons by interior rings
*
* @author Matthias Betz
*
*/
public class InteriorDisconnectedCheck extends Check {
private static final String EPSILON_NAME = "minVertexDistance";
private static final List<CheckId> dependencies;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
......@@ -53,19 +62,17 @@ public class InteriorDisconnectedCheck extends Check {
deps.add(CheckId.C_GE_P_NON_PLANAR);
dependencies = Collections.unmodifiableList(deps);
}
private double epsilon = 0.0001;
@Override
public void init(Map<String, String> params, ParserConfiguration config) {
String epsilonString = params.get(EPSILON_NAME);
if (epsilonString == null) {
epsilon = 0.0001;
} else {
if (epsilonString != null) {
epsilon = Double.parseDouble(epsilonString);
}
}
@Override
public void check(Polygon p) {
if (p.getInnerRings().isEmpty()) {
......@@ -88,7 +95,7 @@ public class InteriorDisconnectedCheck extends Check {
}
}
}
for (CycleNode node : nodeMap.values()) {
if (node.hasMoreThan2CycleDepth(null)) {
// found ring in graph with more than 1 node
......@@ -127,7 +134,7 @@ public class InteriorDisconnectedCheck extends Check {
p.addCheckResult(cr);
return true;
}
}
}
}
......@@ -152,15 +159,20 @@ public class InteriorDisconnectedCheck extends Check {
nodeMap.put(lr, node);
}
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_P_INTERIOR_DISCONNECTED);
}
@Override
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -21,14 +21,17 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldVertexError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
......@@ -40,7 +43,7 @@ import de.hft.stuttgart.citydoctor2.math.graph.PolygonGraph;
* Checks if the polygons adjacent to a vertex are attached via edges or are
* they discontinuous.
*
* @author Matthias Betz - 12bema1bif@hft-stuttgart.de
* @author Matthias Betz
*
*/
public class ManifoldVertexCheck extends Check {
......@@ -92,10 +95,15 @@ public class ManifoldVertexCheck extends Check {
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_S_NON_MANIFOLD_VERTEX);
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -21,14 +21,17 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.MultipleConnectedComponentsError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
......@@ -38,7 +41,7 @@ import de.hft.stuttgart.citydoctor2.math.graph.PolygonGraph;
/**
* Checks if all parts of a solid are connected or not.
*
* @author Matthias Betz - 12bema1bif@hft-stuttgart.de
* @author Matthias Betz
*
*/
public class MultipleConnectedComponentCheck extends Check {
......@@ -86,10 +89,15 @@ public class MultipleConnectedComponentCheck extends Check {
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_S_MULTIPLE_CONNECTED_COMPONENTS);
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -21,20 +21,30 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.NestedRingError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
/**
* Checks whether a inner ring is completely contained in another inner ring
* within the same polygon
*
* @author Matthias Betz
*
*/
public class NestedRingsCheck extends Check {
private static final List<CheckId> dependencies;
static {
......@@ -47,7 +57,7 @@ public class NestedRingsCheck extends Check {
deps.add(CheckId.C_GE_P_ORIENTATION_RINGS_SAME);
dependencies = Collections.unmodifiableList(deps);
}
@Override
public void check(Polygon p) {
for (LinearRing interiorRing : p.getInnerRings()) {
......@@ -85,10 +95,15 @@ public class NestedRingsCheck extends Check {
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_P_INNER_RINGS_NESTED);
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -21,14 +21,17 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldEdgeError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
......@@ -37,7 +40,9 @@ import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
* This class detects half edges with more than two neighbors, i.e.
* non-2-manifold geometries
*
* @author dwagner, alam
* @author dwagner
* @author alam
* @author Matthias Betz
*/
public class NonManifoldEdgeCheck extends Check {
......@@ -80,6 +85,11 @@ public class NonManifoldEdgeCheck extends Check {
}
g.addCheckResult(cr);
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_S_NON_MANIFOLD_EDGE);
}
@Override
public List<CheckId> getDependencies() {
......@@ -87,8 +97,8 @@ public class NonManifoldEdgeCheck extends Check {
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -22,15 +22,16 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.DefaultParameter;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.Unit;
import de.hft.stuttgart.citydoctor2.check.error.NullAreaError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Line3d;
......@@ -45,7 +46,6 @@ public class NullAreaCheck extends Check {
private static final String DELTA_NAME = "delta";
private static final List<CheckId> dependencies;
private static final List<DefaultParameter> defaultParameters;
static {
ArrayList<CheckId> deps = new ArrayList<>();
......@@ -53,10 +53,6 @@ public class NullAreaCheck extends Check {
deps.add(CheckId.C_GE_R_NOT_CLOSED);
deps.add(CheckId.C_GE_R_DUPLICATE_POINT);
dependencies = Collections.unmodifiableList(deps);
ArrayList<DefaultParameter> defParameters = new ArrayList<>();
defParameters.add(new DefaultParameter(DELTA_NAME, "0.0001", Unit.SQUARE_METER));
defaultParameters = Collections.unmodifiableList(defParameters);
}
private double delta = 0.0001;
......@@ -68,11 +64,6 @@ public class NullAreaCheck extends Check {
}
}
@Override
public List<DefaultParameter> getDefaultParameter() {
return defaultParameters;
}
@Override
public void check(LinearRing lr) {
boolean areaIsNull = checkWithDistanceToRegressionLine(lr, delta);
......@@ -106,6 +97,11 @@ public class NullAreaCheck extends Check {
}
return area < 0.0001;
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_R_NULL_AREA);
}
@Override
public List<CheckId> getDependencies() {
......@@ -113,8 +109,8 @@ public class NullAreaCheck extends Check {
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
......
......@@ -22,23 +22,26 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import Jama.EigenvalueDecomposition;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.DefaultParameter;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.Unit;
import de.hft.stuttgart.citydoctor2.check.error.DegeneratedPolygonError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonNormalsDeviation;
import de.hft.stuttgart.citydoctor2.check.error.DegeneratedPolygonError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.CovarianceMatrix;
import de.hft.stuttgart.citydoctor2.math.Matrix3x3d;
import de.hft.stuttgart.citydoctor2.math.OrthogonalRegressionPlane;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
......@@ -48,7 +51,8 @@ import de.hft.stuttgart.citydoctor2.tesselation.JoglTesselator;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
/**
* Check class to check for planarity issues
* Check class to check for planarity issues as well as degenerated polygons.
* Checks for regression plane and normal issues.
*
* @author Matthias Betz
*
......@@ -62,7 +66,6 @@ public class PlanarCheck extends Check {
private static final String DEGENERATED_POLYGON_TOLERANCE = "degeneratedPolygonTolerance";
private static final List<CheckId> dependencies;
private static final List<DefaultParameter> defaultParameters;
static {
ArrayList<CheckId> deps = new ArrayList<>(4);
......@@ -71,14 +74,6 @@ public class PlanarCheck extends Check {
deps.add(CheckId.C_GE_R_DUPLICATE_POINT);
deps.add(CheckId.C_GE_R_SELF_INTERSECTION);
dependencies = Collections.unmodifiableList(deps);
ArrayList<DefaultParameter> defParameters = new ArrayList<>(3);
defParameters.add(new DefaultParameter(TYPE, DISTANCE, Unit.NONE));
defParameters.add(new DefaultParameter(DISTANCE_TOLERANCE, "0.01", Unit.METER));
defParameters.add(new DefaultParameter(ANGLE_TOLERANCE, "1", Unit.DEGREE));
defParameters.add(new DefaultParameter(DEGENERATED_POLYGON_TOLERANCE, "0.00000", Unit.METER));
defaultParameters = Collections.unmodifiableList(defParameters);
}
private String planarCheckType = DISTANCE;
......@@ -91,6 +86,8 @@ public class PlanarCheck extends Check {
public void init(Map<String, String> parameters, ParserConfiguration config) {
if (parameters.containsKey(TYPE)) {
planarCheckType = parameters.get(TYPE).toLowerCase();
} else {
throw new IllegalStateException("Parameter " + TYPE + " is missing from parameters");
}
if (parameters.containsKey(ANGLE_TOLERANCE)) {
rad = Math.toRadians(Double.parseDouble(parameters.get(ANGLE_TOLERANCE)));
......@@ -103,11 +100,6 @@ public class PlanarCheck extends Check {
}
}
@Override
public List<DefaultParameter> getDefaultParameter() {
return defaultParameters;
}
@Override
public void check(Polygon p) {
if (DISTANCE.equals(planarCheckType)) {
......@@ -118,8 +110,7 @@ public class PlanarCheck extends Check {
ArrayList<Vertex> vertices = collectVertices(p);
Vector3d centroid = CovarianceMatrix.getCentroid(vertices);
EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid);
Vector3d eigenvalues = OrthogonalRegressionPlane.getEigenvalues(ed);
if (checkEigenvalues(p, eigenvalues)) {
if (checkEigenvalues(p, vertices, ed)) {
// found tiny edge error, abort further checking
return;
}
......@@ -181,13 +172,13 @@ public class PlanarCheck extends Check {
ArrayList<Vertex> vertices = collectVertices(p);
Vector3d centroid = CovarianceMatrix.getCentroid(vertices);
EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid);
Vector3d eigenvalues = OrthogonalRegressionPlane.getEigenvalues(ed);
if (checkEigenvalues(p, eigenvalues)) {
if (checkEigenvalues(p, vertices, ed)) {
// found tiny edge error, abort further checking
return;
}
Vector3d eigenvalues = OrthogonalRegressionPlane.getEigenvalues(ed);
Plane plane = OrthogonalRegressionPlane.calculatePlane(centroid, ed, eigenvalues);
for (Vertex v : vertices) {
double distance = plane.getDistance(v);
......@@ -220,13 +211,25 @@ public class PlanarCheck extends Check {
return vertices;
}
private boolean checkEigenvalues(Polygon p, Vector3d eigenvalues) {
private boolean checkEigenvalues(Polygon p, List<Vertex> points, EigenvalueDecomposition ed) {
Matrix3x3d mat = new Matrix3x3d(ed.getV().getArray());
List<Vector3d> rotatedVertices = new ArrayList<>();
for (Vertex v : points) {
rotatedVertices.add(mat.mult(v));
}
BoundingBox bbox = BoundingBox.ofPoints(rotatedVertices);
int nrOfEigenvaluesBelowTolerance = 0;
for (double d : eigenvalues.getCoordinates()) {
if (d <= degeneratedPolygonTolerance) {
nrOfEigenvaluesBelowTolerance++;
}
if (bbox.getWidth() < degeneratedPolygonTolerance) {
nrOfEigenvaluesBelowTolerance++;
}
if (bbox.getHeight() < degeneratedPolygonTolerance) {
nrOfEigenvaluesBelowTolerance++;
}
if (bbox.getDepth() < degeneratedPolygonTolerance) {
nrOfEigenvaluesBelowTolerance++;
}
if (nrOfEigenvaluesBelowTolerance >= 2) {
CheckError err = new DegeneratedPolygonError(p);
p.addCheckResult(new CheckResult(this, ResultStatus.ERROR, err));
......@@ -241,8 +244,13 @@ public class PlanarCheck extends Check {
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_P_NON_PLANAR);
}
@Override
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -21,14 +21,17 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.PolygonIntersectingRingsError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Polygon2d;
......@@ -38,11 +41,16 @@ import de.hft.stuttgart.citydoctor2.math.Vector2d;
import de.hft.stuttgart.citydoctor2.utils.Pair;
import de.hft.stuttgart.citydoctor2.utils.SerializablePair;
/**
* Checks whether linear rings within a polygon are intersecting with each other
*
* @author Matthias Betz
*
*/
public class PolygonIntersectingRingsCheck extends Check {
private static final List<CheckId> dependencies;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
......@@ -54,7 +62,7 @@ public class PolygonIntersectingRingsCheck extends Check {
deps.add(CheckId.C_GE_P_INNER_RINGS_NESTED);
dependencies = Collections.unmodifiableList(deps);
}
@Override
public void check(Polygon p) {
if (p.getInnerRings().isEmpty()) {
......@@ -73,7 +81,7 @@ public class PolygonIntersectingRingsCheck extends Check {
}
p.addCheckResult(cr);
}
public SerializablePair<LinearRing, LinearRing> interiorRingsIntersectWithExterior(Polygon p) {
List<Pair<LinearRing, List<Segment2d>>> ringSegments = projectTo2D(p);
for (int i = 0; i < ringSegments.size() - 1; i++) {
......@@ -124,8 +132,13 @@ public class PolygonIntersectingRingsCheck extends Check {
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_P_INTERSECTING_RINGS);
}
@Override
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -21,22 +21,33 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.PolygonSameOrientationError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.checks.util.Orientation;
import de.hft.stuttgart.citydoctor2.checks.util.RingOrientationUtil;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Polygon2d;
import de.hft.stuttgart.citydoctor2.math.Ring2d;
/**
* This class checks whether a linear ring and the exterior ring of a polygon
* have the same orientation. A inner ring must be oriented in the opposite
* direction than the exterior ring.
*
* @author Matthias Betz
*
*/
public class PolygonSameOrientationCheck extends Check {
private static final List<CheckId> dependencies;
static {
......@@ -48,7 +59,7 @@ public class PolygonSameOrientationCheck extends Check {
deps.add(CheckId.C_GE_P_NON_PLANAR);
dependencies = Collections.unmodifiableList(deps);
}
@Override
public void check(Polygon p) {
if (p.getInnerRings().isEmpty()) {
......@@ -67,7 +78,7 @@ public class PolygonSameOrientationCheck extends Check {
return;
}
}
// no error found, or the method would have terminated previously
CheckResult cr = new CheckResult(this, ResultStatus.OK, null);
p.addCheckResult(cr);
......@@ -80,8 +91,13 @@ public class PolygonSameOrientationCheck extends Check {
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_P_ORIENTATION_RINGS_SAME);
}
@Override
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
......@@ -21,28 +21,32 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWrongOrientationError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
/**
* This class checks the orientation of each face of a solid
* @author Matthias Betz - 12bema1bif@hft-stuttgart.de
* Checks whether polygons share a edge that is walked more than one time in the
* same direction by two or more polygons indicating that a polygon is oriented wrong.
*
* @author Matthias Betz
*
*/
public class PolygonWrongOrientationCheck extends Check {
private static final List<CheckId> dependencies;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
......@@ -57,19 +61,19 @@ public class PolygonWrongOrientationCheck extends Check {
deps.add(CheckId.C_GE_S_NON_MANIFOLD_EDGE);
dependencies = Collections.unmodifiableList(deps);
}
@Override
public void check(Geometry g) {
// only for solids
if (g.getType() != GeometryType.SOLID) {
return;
}
List<Edge> faultyEdges = new ArrayList<>();
for (Edge edge : g.getEdges()) {
if (edge.getNumberOfHalfEdges() != edge.getNumberOppositeHalfEdges()) {
faultyEdges.add(edge);
}
}
}
CheckResult cr;
if (faultyEdges.isEmpty()) {
......@@ -80,15 +84,20 @@ public class PolygonWrongOrientationCheck extends Check {
}
g.addCheckResult(cr);
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_GE_S_POLYGON_WRONG_ORIENTATION);
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
public RequirementType getType() {
return RequirementType.GEOMETRY;
}
@Override
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment