From d3fb0ee62823fa5c100cdd903da85d159d67b1ba Mon Sep 17 00:00:00 2001 From: Matthias Betz Date: Wed, 3 Mar 2021 10:51:12 +0100 Subject: [PATCH 1/5] fixing some linting issues added test for degenerated polygon renaming degenerated polygon error --- .../stuttgart/citydoctor2/check/Check.java | 23 +-- .../stuttgart/citydoctor2/check/ErrorId.java | 2 +- .../check/error/DegeneratedPolygonError.java | 2 +- .../math/OrthogonalRegressionPlane.java | 4 +- .../stuttgart/citydoctor2/check/Checker.java | 135 +------------ .../check/StreamCityGmlConsumer.java | 186 ++++++++++++++++++ .../stuttgart/citydoctor2/checks/Checks.java | 7 + .../checks/geometry/DuplicatePointsCheck.java | 6 +- .../checks/geometry/PlanarCheck.java | 12 +- .../checks/geometry/RingSelfIntCheck.java | 21 +- .../geometry/DegeneratedPolygonCheckTest.java | 102 ++++++++++ 11 files changed, 338 insertions(+), 162 deletions(-) create mode 100644 CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/StreamCityGmlConsumer.java create mode 100644 CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java index 2610cc1..e421951 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java @@ -46,15 +46,16 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; * The general check class containing the methods which will be called by check * engine. To create a new check override one or more check(...) methods and * implement the {@link Check#getApplicableToClasses()} method by returning a - * list of classes you wish to check. CheckResult objects can be attached to - * every Checkable. If the check has parameters override the - * {@link Check#init(Map, ParserConfiguration)} method to get the value of a - * parameter if a user has specified one in the validation plan. It will be - * contained in the Map as a string. If you have parameters you will need to - * override the {@link Check#getDefaultParameter()} method as well to declare - * which parameters you have, which name and default value they have. The name - * will be the key for the Map in the init method previously mentioned. If your - * check has dependencies you can declare them in the + * list of classes you wish to check.
+ * CheckResult objects can be attached to every Checkable. If the check has + * parameters override the {@link Check#init(Map, ParserConfiguration)} method + * to get the value of a parameter if a user has specified one in the validation + * plan. It will be contained in the Map as a string. If you have parameters you + * will need to override the {@link Check#getDefaultParameter()} method as well + * to declare which parameters you have, which name and default value they have. + * The name will be the key for the Map in the init method previously + * mentioned.
+ * If your check has dependencies you can declare them in the * {@link Check#getDependencies()} method. Be sure not to create cyclic * dependencies as that would result in undefined behavior. * @@ -66,7 +67,7 @@ public abstract class Check { private List> applicableToClasses = new ArrayList<>(2); @SuppressWarnings("unchecked") - public Check() { + protected Check() { Method[] declaredMethods = getClass().getDeclaredMethods(); for (Method m : declaredMethods) { if ("check".equals(m.getName())) { @@ -145,7 +146,7 @@ public abstract class Check { } return true; } - + private boolean canBeApplied(Checkable c) { for (Class checkableClass : getApplicableToClasses()) { if (checkableClass.isAssignableFrom(c.getCheckClass())) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorId.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorId.java index b3a179f..44049e0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorId.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorId.java @@ -63,7 +63,7 @@ public class ErrorId implements Serializable { public static final ErrorId SEM_BS_NOT_GROUND = new ErrorId("SEM_BS_NOT_GROUND"); public static final ErrorId SEM_SCHEMATRON_ERROR = new ErrorId("SEM_SCHEMATRON_ERROR"); public static final ErrorId SEM_BS_UNFRAGMENTED = new ErrorId("SEM_BS_UNFRAGMENTED"); - public static final ErrorId GE_P_TINY_EDGE = new ErrorId("GE_P_TINY_EDGE"); + public static final ErrorId GE_P_DEGENERATED_POLYGON = new ErrorId("GE_P_DEGENERATED_POLYGON"); private String name; diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/DegeneratedPolygonError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/DegeneratedPolygonError.java index a9671a2..708b6a7 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/DegeneratedPolygonError.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/DegeneratedPolygonError.java @@ -64,7 +64,7 @@ public class DegeneratedPolygonError implements CheckError { @Override public ErrorId getErrorId() { - return ErrorId.GE_P_TINY_EDGE; + return ErrorId.GE_P_DEGENERATED_POLYGON; } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/OrthogonalRegressionPlane.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/OrthogonalRegressionPlane.java index 724d731..2375019 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/OrthogonalRegressionPlane.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/OrthogonalRegressionPlane.java @@ -55,10 +55,10 @@ public class OrthogonalRegressionPlane { double eig2 = eigenvalues.getZ(); Matrix v = ed.getV(); Matrix eigenVector; - if (eig0 < eig1 && eig0 < eig2) { + if (eig0 <= eig1 && eig0 <= eig2) { // the first eigenvalue is the lowest eigenVector = v.getMatrix(0, 2, 0, 0); - } else if (eig1 < eig0 && eig1 < eig2) { + } else if (eig1 <= eig0 && eig1 <= eig2) { // the second eigenvalue is the lowest eigenVector = v.getMatrix(0, 2, 1, 1); } else { diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 6ca48f1..b439cdb 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -23,7 +23,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -33,7 +32,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; import javax.xml.XMLConstants; @@ -47,8 +45,6 @@ import javax.xml.transform.stream.StreamSource; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.citygml4j.factory.GMLGeometryFactory; -import org.citygml4j.model.citygml.core.CityModel; import org.w3c.dom.Document; import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError; @@ -60,15 +56,9 @@ import de.hft.stuttgart.citydoctor2.checks.CheckPrototype; import de.hft.stuttgart.citydoctor2.checks.Checks; 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.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; -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.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; @@ -81,15 +71,10 @@ import de.hft.stuttgart.citydoctor2.reporting.XmlValidationReporter; import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfReporter; 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.Checking; -import de.hft.stuttgart.quality.model.jaxb.ErrorStatistics; -import de.hft.stuttgart.quality.model.jaxb.FeatureStatistics; import de.hft.stuttgart.quality.model.jaxb.Parameter; import de.hft.stuttgart.quality.model.jaxb.Requirement; import de.hft.stuttgart.quality.model.jaxb.RequirementId; -import de.hft.stuttgart.quality.model.jaxb.Statistics; import de.hft.stuttgart.quality.model.jaxb.TopLevelFeatureType; import de.hft.stuttgart.quality.model.jaxb.ValidationPlan; import net.sf.saxon.s9api.DOMDestination; @@ -235,7 +220,7 @@ public class Checker { }); } - private static void handleSchematronErrorsForCityObject(List v, CityObject co) { + static void handleSchematronErrorsForCityObject(List v, CityObject co) { int count = 0; for (SchematronError se : v) { CheckError err; @@ -252,7 +237,7 @@ public class Checker { } } - private ValidationPlan createValidationPlan() { + ValidationPlan createValidationPlan() { ValidationPlan plan = new ValidationPlan(); List filter = createFilter(); @@ -475,6 +460,8 @@ public class Checker { expandTransformer.setDestination(xslt2Transformer); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); Document doc = factory.newDocumentBuilder().newDocument(); DOMDestination domDestination = new DOMDestination(doc); @@ -724,97 +711,18 @@ public class Checker { BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) { Checker c = new Checker(config, null); String fileName = inputFile.getName(); - + // create reporter if available XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName); PdfStreamReporter pdfReporter = getPdfReporter(config, logoLocation, pdfBos, fileName); - - // create quality ade structures - Validation val = new Validation(); - val.setValidationDate(ZonedDateTime.now()); - val.setValidationSoftware("CityDoctor " + Localization.getText(Localization.VERSION)); - Statistics statistics = new Statistics(); - FeatureStatistics buildingStatistics = new FeatureStatistics(); - statistics.setNumErrorBuildings(buildingStatistics); - FeatureStatistics bridgeStatistics = new FeatureStatistics(); - statistics.setNumErrorBridgeObjects(bridgeStatistics); - FeatureStatistics transportationStatistics = new FeatureStatistics(); - statistics.setNumErrorTransportation(transportationStatistics); - FeatureStatistics vegetationStatistics = new FeatureStatistics(); - statistics.setNumErrorVegetation(vegetationStatistics); - FeatureStatistics landStatistics = new FeatureStatistics(); - statistics.setNumErrorLandObjects(landStatistics); - FeatureStatistics waterStatistics = new FeatureStatistics(); - statistics.setNumErrorWaterObjects(waterStatistics); - - // map for counting individual error counts - Map errorCount = new HashMap<>(); - GMLGeometryFactory gmlFactory = new GMLGeometryFactory(); - + // execute schematron first SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, inputFile); - - CityGmlConsumer con = new CityGmlConsumer() { - @Override - public void accept(CityObject co) { - c.checkFeature(xmlReporter, pdfReporter, co); - - if (handler != null) { - List errors = handler.getFeatureErrors().get(co.getGmlId().getGmlString()); - if (errors != null) { - 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 errorList = new ArrayList<>(); - co.collectContainedErrors(errorList); - Set 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 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()); + CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, l); - cm.addGenericApplicationPropertyOfCityModel(val); - } - }; - // parse and validate CityGmlParser.streamCityGml(inputFile, config.getParserConfiguration(), con, outputFile); - + // write reports if available writeReport(xmlReporter, handler); writeReport(pdfReporter, handler); @@ -822,33 +730,6 @@ public class Checker { logger.error(Localization.getText("Checker.failReports"), e); } } - - 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); - } - } private static XmlStreamReporter getXmlReporter(ValidationConfiguration config, BufferedOutputStream xmlBos, String fileName) { diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/StreamCityGmlConsumer.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/StreamCityGmlConsumer.java new file mode 100644 index 0000000..d960e92 --- /dev/null +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/StreamCityGmlConsumer.java @@ -0,0 +1,186 @@ +/*- + * 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 . + */ +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 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 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 errorList = new ArrayList<>(); + co.collectContainedErrors(errorList); + Set 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 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); + } + } + +} diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/Checks.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/Checks.java index cbae54f..61af958 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/Checks.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/Checks.java @@ -62,6 +62,8 @@ 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); @@ -75,16 +77,21 @@ public class Checks { prototypeMap = new HashMap<>(); // add new checks here + // ring checks publish(new NumPointsCheck()); 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()); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/DuplicatePointsCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/DuplicatePointsCheck.java index d564b83..3e8e616 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/DuplicatePointsCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/DuplicatePointsCheck.java @@ -68,9 +68,7 @@ public class DuplicatePointsCheck extends Check { @Override public void init(Map params, ParserConfiguration config) { String epsilonString = params.get(EPSILON_NAME); - if (epsilonString == null) { - epsilon = 0.0001; - } else { + if (epsilonString != null) { epsilon = Double.parseDouble(epsilonString); } } @@ -99,8 +97,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 diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java index 8135788..9f63a86 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java @@ -115,10 +115,7 @@ public class PlanarCheck extends Check { } else if ("angle".equals(planarCheckType)) { // check for tiny edge as well // store all used points in temporary list - ArrayList vertices = collectVertices(p); - Vector3d centroid = CovarianceMatrix.getCentroid(vertices); - EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid); - Vector3d eigenvalues = OrthogonalRegressionPlane.getEigenvalues(ed); + Vector3d eigenvalues = calculatedEigenvalues(p); if (checkEigenvalues(p, eigenvalues)) { // found tiny edge error, abort further checking return; @@ -133,6 +130,13 @@ public class PlanarCheck extends Check { } } + private Vector3d calculatedEigenvalues(Polygon p) { + ArrayList vertices = collectVertices(p); + Vector3d centroid = CovarianceMatrix.getCentroid(vertices); + EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid); + return OrthogonalRegressionPlane.getEigenvalues(ed); + } + private void planarNormalDeviation(Polygon p) { TesselatedPolygon tp = JoglTesselator.tesselatePolygon(p); ArrayList normals = new ArrayList<>(); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java index a3e49eb..f65b0f8 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java @@ -31,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckType; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError; import de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError; +import de.hft.stuttgart.citydoctor2.checks.Checks; import de.hft.stuttgart.citydoctor2.datastructure.Edge; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; @@ -40,13 +41,11 @@ import de.hft.stuttgart.citydoctor2.math.Segment3d; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; public class RingSelfIntCheck extends Check { - + private static final String EPSILON_NAME = "minVertexDistance"; - private static final List dependencies; - static { ArrayList deps = new ArrayList<>(); deps.add(CheckId.C_GE_R_TOO_FEW_POINTS); @@ -55,14 +54,12 @@ public class RingSelfIntCheck extends Check { dependencies = Collections.unmodifiableList(deps); } - private double epsilon = 0.0001; + private double epsilon = Checks.MIN_VERTEX_DISTANCE_DEFAULT; @Override public void init(Map parameters, ParserConfiguration config) { String epsilonString = parameters.get(EPSILON_NAME); - if (epsilonString == null) { - epsilon = 0.0001; - } else { + if (epsilonString != null) { epsilon = Double.parseDouble(epsilonString); } } @@ -74,16 +71,16 @@ public class RingSelfIntCheck extends Check { private void checkRingJava(LinearRing lr) { List edges = getEdgesForRing(lr); - + for (Edge e : edges) { if (checkForPointsTouchingEdge(lr, e)) { return; - } + } } - + for (int i = 0; i < edges.size() - 1; i++) { Edge e1 = edges.get(i); - + for (int j = i + 1; j < edges.size(); j++) { Edge e2 = edges.get(j); if (e1.getConnectionPoint(e2) != null) { @@ -101,7 +98,7 @@ public class RingSelfIntCheck extends Check { } } } - + // no errors detected CheckResult cr = new CheckResult(this, ResultStatus.OK, null); lr.addCheckResult(cr); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java new file mode 100644 index 0000000..d15aed0 --- /dev/null +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java @@ -0,0 +1,102 @@ +/*- + * 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 . + */ +package de.hft.stuttgart.citydoctor2.checks.geometry; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.check.Checker; +import de.hft.stuttgart.citydoctor2.check.ErrorId; +import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; +import de.hft.stuttgart.citydoctor2.datastructure.Geometry; +import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; +import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; +import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType; +import de.hft.stuttgart.citydoctor2.datastructure.Lod; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; + +public class DegeneratedPolygonCheckTest { + + + @Test + public void testDegeneratedPolygon() { + Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD1); + ConcretePolygon polygon = new ConcretePolygon(); + geom.getPolygons().add(polygon); + polygon.setParent(geom); + LinearRing lr = new LinearRing(LinearRingType.EXTERIOR); + polygon.setExteriorRing(lr); + Vertex v1 = new Vertex(427583.301, 6003502.571, 9.711); + lr.getVertices().add(v1); + Vertex v2 = new Vertex(427583.304, 6003502.574, 9.713); + lr.getVertices().add(v2); + Vertex v3 = new Vertex(427583.304, 6003502.574, 4.097); + lr.getVertices().add(v3); + Vertex v4 = new Vertex(427583.301, 6003502.571, 4.097); + lr.getVertices().add(v4); + lr.getVertices().add(v1); + geom.updateEdgesAndVertices(); + + Building b = new Building(); + b.addGeometry(geom); + + ParserConfiguration config = new ParserConfiguration(8, false); + CityDoctorModel model = new CityDoctorModel(config, new File("")); + model.addBuilding(b); + // model +// Edge [from=Vertex [x=427583.301, y=6003502.571, z=9.711], to=Vertex [x=427583.304, y=6003502.574, z=9.713]], +// Edge [from=Vertex [x=427583.304, y=6003502.574, z=9.713], to=Vertex [x=427583.304, y=6003502.574, z=4.097]], +// Edge [from=Vertex [x=427583.304, y=6003502.574, z=4.097], to=Vertex [x=427583.301, y=6003502.571, z=4.097]], +// Edge [from=Vertex [x=427583.301, y=6003502.571, z=4.097], to=Vertex [x=427583.301, y=6003502.571, z=9.711]]] + + // test +// Edge [from=Vertex [x=427583.301, y=6003502.571, z=9.711], to=Vertex [x=427583.304, y=6003502.574, z=9.713]], +// Edge [from=Vertex [x=427583.304, y=6003502.574, z=9.713], to=Vertex [x=427583.304, y=6003502.574, z=4.097]], +// Edge [from=Vertex [x=427583.304, y=6003502.574, z=4.097], to=Vertex [x=427583.301, y=6003502.571, z=4.097]], +// Edge [from=Vertex [x=427583.301, y=6003502.571, z=4.097], to=Vertex [x=427583.301, y=6003502.571, z=9.711]]] + +// Segment3d [pointA=Vertex [x=427583.301, y=6003502.571, z=9.711], pointB=Vertex [x=427583.304, y=6003502.574, z=9.713]] +// Vertex [x=427583.301, y=6003502.571, z=9.711] + + ValidationConfiguration valConfig = ValidationConfiguration.loadStandardValidationConfig(); + Map parameters = new HashMap<>(); + parameters.put("degeneratedPolygonTolerance", "0.0001"); + valConfig.getChecks().get(CheckId.C_GE_P_NON_PLANAR).setParameters(parameters); + Checker c = new Checker(valConfig, model); + c.runChecks(); + + List errors = new ArrayList<>(); + b.collectContainedErrors(errors); + CheckError checkError = errors.get(0); + assertEquals(ErrorId.GE_P_DEGENERATED_POLYGON, checkError.getErrorId()); + } +} -- GitLab From ce3562cfd9c1c0e04db3f85e38f04347a8102144 Mon Sep 17 00:00:00 2001 From: Matthias Betz Date: Wed, 3 Mar 2021 11:21:06 +0100 Subject: [PATCH 2/5] fixing more lint issues Adding event handler so logging is not different --- .../citydoctor2/reporting/pdf/PdfReport.java | 22 +++++++++++++++++-- .../citydoctor2/reporting/pdf/PdfUtils.java | 16 +++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfReport.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfReport.java index b80ffa6..3ddd133 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfReport.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfReport.java @@ -41,6 +41,10 @@ import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; +import org.apache.fop.events.EventFormatter; +import org.apache.fop.events.model.EventSeverity; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.xmlgraphics.util.MimeConstants; import org.jdom2.Document; import org.jdom2.Element; @@ -57,6 +61,8 @@ import de.hft.stuttgart.citydoctor2.checkresult.utility.CheckReportWriteExceptio */ public class PdfReport { + private static final Logger logger = LogManager.getLogger(PdfReport.class); + private static final String FRONT_PAGE = "front-page"; private static final String REGION_BODY = "region-body"; private static final String MASTER_NAME = "master-name"; @@ -160,16 +166,28 @@ public class PdfReport { try { FOUserAgent userAgent = FOP_FACTORY.newFOUserAgent(); + userAgent.getEventBroadcaster().addEventListener(event -> { + EventSeverity severity = event.getSeverity(); + String msg = EventFormatter.format(event); + if (severity == EventSeverity.ERROR) { + logger.error(msg); + } else if (severity == EventSeverity.FATAL) { + logger.fatal(msg); + } else if (severity == EventSeverity.INFO) { + logger.info(msg); + } else if (severity == EventSeverity.WARN) { + logger.warn(msg); + } + }); Fop fop = FOP_FACTORY.newFop(MimeConstants.MIME_PDF, userAgent, outFile); - TransformerFactory factory = TransformerFactory.newInstance(); // deactivate external dtds because of security issues factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + // identity transformer Transformer transformer = factory.newTransformer(); DOMOutputter domOutputter = new DOMOutputter(); - Source src = new DOMSource(domOutputter.output(doc)); Result res = new SAXResult(fop.getDefaultHandler()); transformer.transform(src, res); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfUtils.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfUtils.java index c78f67f..af2ffe9 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfUtils.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfUtils.java @@ -29,6 +29,10 @@ import org.jdom2.Namespace; */ public class PdfUtils { + private static final String HEIGHT_STRING = "height"; + + private static final String WIDTH_STRING = "width"; + private static final Namespace svgNs = Namespace.getNamespace("svg", "http://www.w3.org/2000/svg"); private static final int WIDTH = 450; @@ -46,8 +50,8 @@ public class PdfUtils { String val2LengthString = "" + val2Length; Element svgElement = new Element("svg", svgNs); - svgElement.setAttribute("width", "450px"); - svgElement.setAttribute("height", "30px"); + svgElement.setAttribute(WIDTH_STRING, "450px"); + svgElement.setAttribute(HEIGHT_STRING, "30px"); if (val1 > 0) { Element gVal1Style = new Element("g", svgNs); @@ -58,8 +62,8 @@ public class PdfUtils { gVal1Style.addContent(val1Rect); val1Rect.setAttribute("x", "0"); val1Rect.setAttribute("y", "0"); - val1Rect.setAttribute("width", val1LengthString); - val1Rect.setAttribute("height", "30"); + val1Rect.setAttribute(WIDTH_STRING, val1LengthString); + val1Rect.setAttribute(HEIGHT_STRING, "30"); Element val1Text = new Element("text", svgNs); svgElement.addContent(val1Text); val1Text.setAttribute("x", "5"); @@ -76,8 +80,8 @@ public class PdfUtils { gVal2Style.addContent(val2Rect); val2Rect.setAttribute("x", val1LengthString); val2Rect.setAttribute("y", "0"); - val2Rect.setAttribute("width", val2LengthString); - val2Rect.setAttribute("height", "30"); + val2Rect.setAttribute(WIDTH_STRING, val2LengthString); + val2Rect.setAttribute(HEIGHT_STRING, "30"); Element val2Text = new Element("text", svgNs); svgElement.addContent(val2Text); String val2String = "" + val2; -- GitLab From 355777853c36e4409453b59d7b46c66363389176 Mon Sep 17 00:00:00 2001 From: Matthias Betz Date: Thu, 4 Mar 2021 14:04:05 +0100 Subject: [PATCH 3/5] added missing citygml4j ade 0.1.1 --- .../0.1.1/citygml4j-quality-ade-0.1.1.jar | Bin 0 -> 90083 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 CityDoctorParent/non-maven-libs/de/hft/stuttgart/citygml4j-quality-ade/0.1.1/citygml4j-quality-ade-0.1.1.jar diff --git a/CityDoctorParent/non-maven-libs/de/hft/stuttgart/citygml4j-quality-ade/0.1.1/citygml4j-quality-ade-0.1.1.jar b/CityDoctorParent/non-maven-libs/de/hft/stuttgart/citygml4j-quality-ade/0.1.1/citygml4j-quality-ade-0.1.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..4e0c6e12eadce2e60a5b3ec19eaa1eca90211a8d GIT binary patch literal 90083 zcmb5V19WAY#SZhwryKG*|Cj|I<{?_9ox38olZJ-(n&gaeebz?=e=?M_pdRQ zzlj=iR;^m~RjJ5BK*EFl+ol<=tM=~~|8ao(dsdXx5M`8Al44f*4;U<%#$VVaP?pB- z@9UtyJLdm{DT*q|N=d3~FeysiD^5=-$TKo6Ajvb*%}&oXsj)1x?;p7^$jy#3$TNz; z!u%}JO2MY@r|{~?kz;q2bIYZx#Y|F@uljo*t%f15rru8$2NgMiR|(%6XLGHzys@4&zW{;3cUf4u{m|Cjs!CjjOj04oc(e*r@K zZ;-2-yPKOOz~x`T@&0dcXLo>|wVU_9utM>##Owhsu2uj$J9C$Rq2cSl)Zhp-xBC~u z|JwY&3)=v^O#g)j;(uk{-Oc)cT9^5!K%xAV{jY4Z2Y8q}{0~F_3?}(M!9a7SzcFO` zzlGHO0-_nTxpWH|PpE_O5T|BJK%w7Mh1hD^}mo!ln zTUq#EVEsm5U_yWKe--{)+Woi7|Cu8HBi5?_$Qn_gx#E8%0F#*=z}2-yz}p1#=<(Vw zn`Od+WaaM~+poK?4ov7E-#MU51>0?hw;O^K8KT; zBW{QBUw7ZAr~9{ZC?04&-}+a+;=M)&_?K0I1wr1$?^gvEB7RK#w86PcOURj51oBF@ zIq}N1Rv(X{DmR_-l>NMd8v}{8UvMH~YB|lS4boLz-Vr zTz4GZh!|^D5wa2g%Z65)nuQ~=5P*oqrZKG2NI}JWKx=cFlgu_lai0H{WL`S_npfiMfjy#G#Dz`fz zr^SCDx_8V?tVB~L>TQ%y7!&Wj?@dO>JNQ~V1 zL1n4iWbDS6!Xw056RS`PEjbVcwZIUM8SXE=`ZB@(O*F^d-@4b8cwO6tR#XBU=z(aP`LKeZt<7tlS3CM(NCI z$YV4|b)<2tV;bXFv_#NFWjRQJ&%y5JXi-(G-rQb-GidpH>NtqYoZ(_}B%p)Q2Fpq@ zr*VP^4OMsipypA19Jnwp@|gN6Z2{Xg34sF{SBk}bBuN2Fj&JY1;}*(h4Mw-${wiAW z!CtFVKO=K>Co8(HdQO!x)xEU0Q4@nDuL>%JRwh#AAe@&|(x|+cYLG#8983UMu`WU-*KLbF6)g@Jz;z zN(dD?MXQDNQ$1vx&R9fud0VGCB+c9Q{b5+~`|WG`H*ujl0u**KTR#97jdw&5x*+vQ z{N#Bm7pDxkb})J-20=|wkXYBjnI6XNYHfK{St7-(h>l6}rqXt2lFO7rA-#OGr`Sy= z!Ih3uUd3-I-vO|oHWJOCHh?mH^9tT3tPXk7Ic)eDYSa>{L}RV?AX>;`oJ`3EPN8Q| zqkO7UQ@$5h8Q_Ofa_a$~ZuN^?gemuG?>ujzNc82SQK|r zMiWrfM>k2JNp`5pY$W;{oS0E}Yc0TNJCUWW`eo5XA1M6>?bqd z=|LJIwbgrpx|(U2XUGTiVU!6;&GJHbgQzb00~FZ-z9aceDuKF1GG*aR&W{pKHe1s% zNb>265-V91|3{YX9}KuT)ppCQs3|GCEQeceL7Br1er|fEcF%N~1^!`~^0bCFn4eit z-y_h;`c8&CUFBSBPwcYnC2g!Y3&l;*EZmeDD6-I85aXvS9Sa_WSxaGX=gLN6Y;<_G zvGqe(Pg%=mSj3do6>y-)u|7<6Dy6L&u%>B5@l|6MaYsuv__R<5OOT^&W{errdQB=8 zamp0+0)r;0%4dz~`LfgW6E|m!hZr5v_9DifWE_AZt5IDTd60$do_Hkj@^zsbwKs>qvgVyibCTW!A^Sq1QJyNZJ5Itv?a30Rdov_on&JG8|_1LBCDDSzJOgFa@E zni-7H;GyvqC}8!~eM*U3vS+Qsg9~kREmK4?Tw|7TO}wT>8zK6!9+BQ?C)vwyEVz#kay* zsZM^cP+wWAIa)Eo*@ulLtzLz#>NV^j*?%j#-zp2YaqMrSI;l6V3n81N;y)jRB>S@@ z%8A10e9 zuNUqiZrmY_5@9QO9FiNjWl39IKZFycNhaE)OX0(d}0}GKCn}pPpbu(#$dX~3m z76HR+Keg%fxWljpM$1e@7L@AM=0KMTTW*s4gcq}*OIm;@wsL70g$>$kE0Z5tVnTf@ zZ;_Sjs!M%eW>~1x8Kp8==1!>+S?NJe%5`l`2${ll$@=bXUd2=BP_s&N6L(Cx>>3Po z=1vbFh^On@5XF~V&Vgfd~^ocD4uTBm)jT@A+)2YL{ObiJl8z4g6 z9QxLImek{FT2tpdglKbTq2uv&D54JZ8n;M<3Y{Z#ecDid7dvHxwvORY*b!?&UyKE2 z0}xrrEfEhB6)4&eguYo=?y4iJk={9MUMZk8QCJQJkp;z z2Tz*QtR+0c<+n4l7(6D#fSw)0JC)?ivlL1KSI&{&tl1Mkd}W(|cMQK*eiMI-5e-3U z9}cbz)iYp991#ZrbT6>K0dEOzBN&K}&Ws8tP4m4Ju>W+twp%5RIF4KcZy~kwF=2qW zDPDTma}j$}Al=&>3b7wvMsK3n0l*NPVbRUINs#0%wQPv_ExNF;Zg&zNILPRDiU)se z9F*|f6!Wq!ja?tL1|*CK)_E`n<^*D(QMuow1;*%RFq(O~-FLfE)pjNIU8J$bFBstj zq>d1XrAkO(2bg_!)9xk>Z~gF+eltYBo&*Wr_A&^ehMV6OddXvdnw*f|Hc|*BjtGzR zQ7kY-E00+Ics6*`$2N{&HqQxmHNX9Oo5~QUqWPxno9c5D&kIZ#`P5lmV>l-W>%|IJ zN10f?uCpTA&h!-g@f@fA>)S2vB?|xBs06lj<`l}s{=anrUuI6Wx1e?36ndS(yeGaR zlkyYBA#Nm|o+SB5$@qlj`_MJ;;Yib5KV&vuBoONUAm2JjA>u{euvkyZZ6nhl3}%zIvE|v+L$2c-AI%V4jZCGCPQqwRmyt8&*h6rBHGdQKBIQ(#&x>c|0`yKKyrRW$vce)cmhbsIM+OudK`Yg@-!* zU}GZLf~z)`D@@jZ+H9@=pa&>NN^Q7Z31sR#TA8)j+0(SE3fr}sr>&SvLwar@JAs$B zau0f5**!*CKdQ&TxYg zWCh-`TjU*yklg4!6?-^mz-P$j3?TNWx(WOeTQ&{*xESf)cAdQRx0ZpXh6L2%& z?`5CU56l^(JEhr63(_4kf}iE%jyt zCo22nY-H=+bl4aqGPVl8v8QcEp~hW5Lt)CNjd&!@f}X-Cn^a+&7P~P46P6rqB@Hczc$3LM+5u_XqvRSM z`+`}N^=f+5rJoLyNh^^2HRwh!IzO_ksP#7k0!$}7(FfmbwQ zu8njcuY#+Kw0pQ)v+u#L$^Xs3vZW&aWPJ_o6PnyWD>G~{AP9oI$Hj7N`mi$^68zW38@OhweZfr`9^(QcdCPF#Q~9 zZgU`irbjVtm)VejH-~H{KqxsrfQBu^E0=Ujrv}6#*Yda2}sg5?Y29VJ(d<=% zV&y2BHaH18izG6^j6Vuhe)1WCg$ii_v58|sbh@D4oHPF(KBV3?5=i5h{M`lXPK1BCF z*8|f^K6P|BP7sy@{!aovY;zPXeWV3bY4y)~C~U({BX^XM1GEcQdU&Wz5E z9*Vl2688zq-i;B=V;>y7uk*85h05Qp3suod09)&rb&E|booG5GJN~~IV!jO@nvdvZ zp>RYGmdUj;3n;_UkQ(RiqSgX2Yv>G^c+1A)D&z3j;)cKAMK|b-%JS%rvgI%xGBBh& zlM*u@k{K`@s?{Vq^VTKY0tF;Tmu}qf&KrzITTYS};$zvk4)ECWLM^A-q2`jE35;kC zDZMIVv_YC|M!{R=+uymzy)drCa2H#d?x46J*XZ-;9L?-|+Vmp5ia(3U%NuK{cr6mw zs)7a{XB1OlHzOlG7RPUW7P@EIo=<<<9Djp&^rsY1pw3BaFO`w1I8q76cTc=r!lr7~ znoltlr=Pb@DkcQ9Gk&W;XpV;oKA?F6gB2E|YJVBU0Be8wf{_|@&4QLuIFR|UHlmS! z!i>-aN31ZHlQGnmSD}RzB5vMS4 z*_D(@9rRTw1JQ~*w_7b+KZrWhfgA1|no>o|jwnmcj3nG&`3szu>J;rff)XWy_7DwE z?2Qcoei_Y!QA3RvXEj4Z$VeOS8)^s~Ob}OD3 zC#_9H*GvL2-+FSt5nqP1i6hZ8bwfPz6JHsUGkDC+2_d~aakp5df9l3o-q8yoh3Zfi^eN3Vp zxqS{I26CrPwE8Pq(28L+&eK5Qf|ns!@QEkUh^^pAb>cP#2}CevJynt?^mG{N5fE2P zEQD-Tp_age-7dtw0j`ehM2ZLFDx}GTI|qr;30VN<*C6To7sny*8`c~YBe|o1fL~iA?NvTlm3(7N}nB!EWtjb z(`Tkw7MCG8sv2q4EZI@;#d?*4gmlOFE8cX|<)+_N4J!t3i#?&ghb!}$6= zCX>zqU>AXCz;mni$S*Dk<~Ow|18ufhD2pvDe1zhDjbC+>q1YRQiKY3PIC&FpAs*74 zMA{s7P8LL5f*eLMB=TgB#G>L$#56gpHEIXR=&-Lt)acSK!ZA>>V|P*H(L4rCzSy85 z_y4HGE7ss?tWX{m(&_!G1*_xyd(JUi0Z#+~)gGv$yAumCxX1lq1u4QrLSYtZ(?kLwjI`uEo<(Qy06p$GFNe&_pIs`$aCDY<)Ca=@sK7?ZN6|(Mdxi@}~zdsApP? zdL~sP!)D$<@7N*qp-o+A=1hygC7Grrlt6ByCQHTbpETT=F+^WETT=?)cvuJ4{Zyt5 z4t0AIhIL{o0gIa>Chp9k!RozgbL)JJX6KqJ6*~8X5BQRE+ST*@npmq<`{Y)Jc0+u% z06($1UWtt-wxcupl$B~CUDf?*UDnuBIv zACYQC@ypavuY=W1$|n>V(uJrUa%|Dk|UP_bFp_I^iNlBkzMrab8N-6dssf<)&t+# zgD=TwezeTsy;KK#^DJ+*t;=);BdZ$E6D4-K8XIhC9(iSUw6y$tIS3qjj3%1aJw*8k zj18$8J7tomVp#rY`3N*5(OULQ(=u5InhORMt1jQMA0pM5$Y^nF)tFQd79nYM`ws)7 zSHDk`Fht7niYANkLyOBLlbXvQ7)Yrxt(6!Q4yGCyl@6{Le5i%mDyZFiO_J*BX2`1< zCdBUmP&*YD!zD^lNKdCYPW@zGBeUE#tARd;_Dk9aOt7fI?Ca2TTcekI zb=Ci1V>uk%Y13_w9$nJC@eNpu%TyMYEVH<5I=O5{lqm6Yo;9yzEm>(>%G7h0{I>tj zFY}t4_0Xl!o;~$VqkWBHWu}P@J8{`Gi7%=oj!X7}Aay;R!iO``AxWoNwuG^S^p=cV zVT94?t8BM%@^c_OirTG;9S5=60RYP*yQq*rC{BZh6l+wSLyi_7L(v9PdWD9H6H_s^ zuw)dU99}n24->yaNq@uLIzt#$6NL3FtY7 z-V~;O8uz2PWmN?EV-YK#Bpy*%bIDf8Y?4DP5aTVE%tP-a+@S&P_gmVb^kgO-IcfXU z|9)ulw&B#R7CZveJ(aD`J1g=0*kV$5M8cX&cb3%IrEwl7N+t^24S&a#nTC~-9g$S> z#B1az(=inUGK73^v@4bmSdk)$ue9r^ccpyc&RrED9h?dIU_l^h)Hp33b9;VyyoA$ffb6Exlox)nCV-y^VbUf)$VH) zD(v(kjb$WrqQVIrWn(*(h&pvSQ=w1tsBkSJIy0C#)W7K-eh`vbG+r+GlnqU<@Z#m_ zFkINE7wysXshui zsT{nq}yG!_Yb%K`1Z@rfh>EDBwHNlnqe20Ff#+ZhyLNMuE%AJ-STpm!etC- zeUa;?b5`+AxARwSdhx>&lEKxOrD~ZYZJAT#t1uuU((tb=edcW6N%*mn!1ewDIfKD?)`?@MNiDfSfr>e>BRnLBLaZO2&e`CYJ?`147Oy!L&%1n zPoFCO={c|Lg>aO)xXn)z%eRL2s~ha=coPd{?s$ubH>P}eo{~wQl1-h4@$qLz z{J!yn;EVZ+KQH~<2>y=oX)cm~@C*6@%HLYV_y7&!^{cn31eBLBZxFHML4Cd2;0d(|4icoD9k<#|(E*Dg)+bjKSVm=mTwO1s#&tTOMCJ@p5ko4Ye3@Q+E`po(8HU!jg0_0zfr-m(rIXo9 z;oSM)Ezg|!)CUd0!913JTfA?E`d8Z3VsvI z+6sO%%K@3MU)A?m9&3a4HUQxFIQzd9-usv172b!Jm7#)2a_9AbVn!GwTH-{V3~Ka} zUA)zZpj8oRMTtqeX-c3aBTKq94Z+nktMEjk?_3szpG>6pTokpP4E>IJ`N6FIj=8Xh zjDL^A+DFw%ZyHD>;9H)JD}x**>P8EFe}q~V#&!g(;#VKaz@OD%E{n=Mo5HLl287qv zRPf(Bny-*wHsoFlFi4?M%4N`*S1F(!d8`l2`9#4N4O>aZG!7~CE@fX|U>0Y(uyUqV zIxA?>nRXL-uxbk*Z;lweXV~JnKAP1TPIC?*YqM#yx*ARxjF^(CCud|$H)al|t7Eaw zAb-nn2Wz?ltb$+k6ML9+5;^>|6o1}`eY1s527WSV3_JT;CX(4)+|W$L zns!L*I;2#X+pwz3XXsT$99}v_bjW3%qq?U4mf!|Cw6NZhPHey3HQ9SB}XGu6>3dwQpvQo z4j?RWnO9LMSJ~XZs8h4KqpMTXgE09C0dp!M8Rr_3?O2V-q+{px zdl4#HO#?j}mo`Y=-E}^F^}U>^^S-1w!ib%J#~Wd`dsUDOm82WOTU)GJuF|j>16evp zzgoGid=uNk&+ftC!llbP64%yD*6LHW)giW^K6v9(E}kQ@L9&94%^aVNFmrg~w%UEN zP45zQt-XK77wDB*yEyA;d2^cRMAGxKdQ}jcv0dz5+=g=2TW#^_MuKb`=yA`jURk?9 zhRoh>w2|G3dV(=xY!I8CJf)-^XAY|`sd(X$%u-!8&@0o+i z)#h2szkgtRZ=qB%fgzEhDh{T2E?#F+!HH?(1|wFdSnI%C*hLmmcNnYRM9^#{J{~eC z#n>!R#cn`ZisvB>FdR4ROJpiFjH-~n0hUoz`=4Sgi^(2FH&pzza>x3uu>^`81rA%ld#?E4_HmOjDCM#hw8r-XJV;VzCTHqY;B zF|vj8eiM?*s-1(wj`m=TJ`Qi8#TA%t=IEH!1CAVAaz43pW7DcSH2$^ zRT!cE~jQ_lqTkO)KH=o;$la|&EjF#_w^g}?I zGRk1z2pnql>bM|q>`^@%3`{h2Vn#SGD{Z?ADla$5C}1>#+~0Byr6?@k1tp=ciY=Cc zF~Px@x2|<9U+=8Sq&`#y?ycccnhYNs*fz=*Q~O2msfa+&3DQZqz17VG;SCKrvu7ra zUAWn@o{LDodX=J5k-KtMl?B1ZRs8PYdewvgt{lwueTUI70(_Y&5aSp9eI7HDeD|A7%^j zZzQL1I6R*)u>!W=$EG&2sh0$aIhn(jMRZ!tZ5b!xVt7mCQAgSTghPx!amRcd53+LCn4%`nNcIc=({O{%um8fd~ACW!?igP!X6Pq zYwlf~i#a2<;&^!fk>%ReMPV$eXc zMq84+s(I4<11Ut>>#69Y$C#V(%F8>;p zpK+9Z{YuSW$7c9#3XvjtC?cQdQhAMPZJ^JIrjwoN#0mjrqPv>Y+;*Nc$VJ zaWRZDKn2(D~Cls4PVKX)=hU zu7RUK)w+grp~^7^plgp@4_|9K7q`n|aYyTGI9InTVg;bKRfFO|$eP=gz*VBDa_o|Mxfw~@u5^CTJ-bH`y9an+AA^$-kd~&On zi;~pC621$0NaiWn><^2Y*T^p5=gMv;^}8DNrO>wLZQXt_<3A;<-@F^g$ogQj~ zY)eXnL-2d`W^e#VY8h06`^qa z!Q7}?IgSNVbGBR4$dhF}UyJXpKly3eLrS_jd~9P7My1V5OcB;nVoW`YOOSpflZfD# z!&xlq%(&k`zyQfqu;9qwM-4FgA3m90QPc?Qq-pGR!NsJy z`zWc&Zwox5$)RXN4EdCD0nii(>|tL^&9HPcSO)KsmKG+07!Yx-y`kkE(S#9hRMYxG zDL>uvpxofZNTRP-bxG3oq?BHFco2TFO${-Z`(tl$KQdpSc5>K|p3;fMCsAbMg*ZTm>%MC(dB7GL^V^Ou)pV??8FStQ9}#== z)a$dT76r)4fq$5r&EY2SeZJ0Pdy)*4@kJ%PO>VGf2X|L9IpVD;c$ zor!qiHSlLC>Mw#@=%QsS?8m5C61!t`Rr#Rut_5iQA+r1?B=Hq~8X?RodzUaZ?XJ=} zeJK#17gw4*C*f$6yU4$%|K5^<=?TIxM?;>>&qpuwG(|6%D^ zKadpGHHo-%w_OBRe-X`cv4do8XI?}SUCg=J?q}Dq+ZA)3TGe)~D1N*;hJUF2J9XF+ zD1gsiGv&S_!ft=Bq01bvui^U@nP<9UcWwNQ3CSHEVzAZY_6m$x!yv$E zQZq)5z~cC7vze9@CTeBI=QVgeF*fzq_#f0IV#@V8Gr7@vjlpCl0ygG-N3LZ&4JLwL zJZGH1|C{ib1I`=-L`ZIokntp_h zt0d!P7C8wKw4ohSA3LW=oW0qvZV#-29c2zEb|M5VlxdXAu6n7)aY?gvcS+(V3@2{% zN;!TTmGmcLx+a@ebR!>h96*>5*f|-b;h$vm0z|ZGGv3b>j>Gjs$vm{|=#0g>zFnTq zl~BJ?^=ePct$c*#xpqzv8PwR9V$qFJ6A;w$Iky=8+4|iQMth(^XqkcTO7z~%cExLm zBdboXk1S61nsv`*kjtF5cgf>;=y02&z9NB9_aKbxo_Gy6yXLFcukI)SYy2~%hS9Kc zeYAory;g1Hg9i-YgYDR_Nf;!qNf=&YN2a9aOsAyoOhsG`@--*`k=3X+BBxikL#9`? z1%GsSyp%;y%M8v4k_u3;usIZY)F6maIc|nSq*bY*I{l!R#DizeZ5Ndr4g#;I zEN$q=rYuH3+xYjR^-!9X{W0HW`MfV0r_85=a}nWQjXy4k$NdQ9D<>96=i1dKuOLP? zN-w~ELY$a*Vxv~#14$DNk$yzTgo}ERFIp$;L|y}%EHzBAAzb`;z3ZrVr7jPoC3+!n?+w4bxDa6a8 zu3H;GxfVz8Mb4&?|FT>xdVx&J5LU~B+nNZa-*0<|@}ajVSQ}##Bph*^_7LdN(v+KYRT~2(IG@mFU_&9Voh+WNn0U@}IEds-?ZXk= zx{6y+Kw+DAnaL&?3OFHVJ>ks9P*i5Ytxz*xm^ME02LSiQ{7NnG-Kjh9kI9CA1;z~v z!yXDfWKAdD9ZvizU_~4&r>lq7O|xO~A}di~_<)WLz@6#KDcBPx^|NKzRnXZGW~BiJ zdk|^yjg74w-^CdhIX#X6p8rP#As#7FC7HBT5=5xfegGLI7nC%uPLT<1L6CU|>-X}O zQAA&aQ($cm8BwQ?qdh^5fqEm#st^T#bD$b9*4kTp!jFBY0}B3N)hQ5x_Iubu#=g=u zR%-)JoG1<{7XE~4@xG2&IpvCk@SH^cGBmYHgL2A%kZgD|z16hx!U1NdfF?{E@_EtK z@)SRkD(!sqC}KYmW+Xpt-Z$&J&S;DDuHw!Bwe>Pt%$8d=8#5Xk4B+pjR|mc3O$7-{ z0xRHxkF{zsvZAV$6r$T=L;UPgj?4w{tti;p8 zq&4jJd^*l+kG%H{IP z+zI9E(+muOIoE~xL1R4fuk(y_+2|16sRnyg5WZQ+<vLP|rsx;CWccH+FdY ze|us@2DZ>%QUrF?DeOG`mZMTX74Q<9BKY{91~%>ncvTD8EHboV&o$v!Y@4UMMVqFw zS&(+4e%MQ`x8ecyTbk{^Y;dh1$_hnX10sU%c}O(GsGxx`nLc%h#{UBGAa3|H?9uM%Lil*0fj@%7*Z?YCAZ{a?-^99 z%CL`1G!KPPB*)7|x~u&G2oD&D1vDwrhrOxjl<(2mk{Tg$*0%GgTma7Qp|` z;g$G1Z6sS&$s&hg23N`=$8@w1p&(|c(oK=Sa+qaXN$On{-#r}J)1^y0ybFDBKHBa? zk?Wz51+yGc+>Zwli4C#YOr9{&69be_0TSffCJ-j8^gR_T4GDHD!B31mK--{DwUW0g z>XzRA5o8OItMVk9^2cpmdFJ~LODcS9>xjf&-&bOhj5PI}rmw^jbhyy0kEm*;w>Qn( z3+5vo%{&+3((ON=Pm!!m<2amYz#&qr4J(|SR0`*_G%AYG`hXsCL*+v53+@6Vf*htOKWT+7dU(>rgt zm+ll_p*&wBDIw;$@n5b(Liy>dzwV3ub|Y~pl;|9j<~|dI`bvj{F{n~kfKT{r-|Q!4 zk1P*lkAAU@Fb_v7q_`@ksM_Cf6~4(Abr%nHEqAvObgBaPsPFbhu_;M=l&MbCVP7?( zG6 zH@brnjuiS?nhK%JEwc+{<;zO)P&qJ7*_qbNMcDazFZrl1b6~A_W z_X-H_8zWFW)9RinaTpWMTFz}>E4dmK&Rp)P$X@nq-z(X?@lW;^6-;b8rJsaqCkL?o4LzB*r!~L zEj0{LR6z(*`LrW4?b}hwVX(of0vC*k1S^hlakUh4T}h+~u;}cahD?><;HQ48TM7ulYQ;7bUa&@nU;5CH;b4mo+MIE3p*tvE)7M|n~nu#-4VCB zrCBt$z{*iZIp=S54Alo~gA?rkrNpM$)uk#&P$RbE4|-*AYH4kaV|0yxBzj22S8Lm) zgC9Rt?epJ41YMaOv(M9#**gx~ljsYnm^~Y;{KvOMxh?0SJ5?44iAu|NlG3r@5iO9qhjYGrGq5yko_2?2$a$9n7W&aa(CA+F`$$QMhBy(d^U*?K zX-#KHZU+bncS@r42(fZzN^m3ewYLk^?j3kF+xd zH+ySBoH~vW)z@sKk7^3yKX9`P{oPH@7M3>UjHz_XiiY<3dlS@yx%7to8wf6&@sA+g z3X&PrQPO{4dWG9DZq(IR;PFaaqKUywkxRpV73G(B|BkY}AhNHQ-#Ve(C!I)KF`@F% zgUL>_7^^QK4IB`i?26?dc5Y)h$(0%>z7xk(*PI7JiCIk0+4|$;1;0AEBc69hhRV&rn zaoN&9{fJA}_$Vb!0V5UB!*?P~64&Q$ZaiOg2XTzZ!h0CP4}~&RSF!y7{ydz0PO{vW zoJ1^v$UOjWKk#k`tdrBW+VWTC8a=;VUbXFAZ8WX!eZIfo1akDon__RklC_tT;EZ7Y z0Bon-=d#?n^i~(3QhIA4>$BPw;|?KTjWp0}|8`a$Sc8{?f5NrU@3gJb()83XGZWt4 zF*W&(hYmI70Jnmzxngt9ei{(q|1rm$2`|1ppBu)sJ?oGQsB^0jQtv3;gl0xqYmP-6 z>Zq~KCL3c(HK=4$z{%;Hb?OCfzWR5mF6~6=HMv<|SpC$Y{N0R#Tt1@SG?Ku$@^{E7 za-VSldr-x2Ai$>TW^RK{Ha1@*H zPAXWIvPTZw(b1yqs;n|@sG-q<#C+@-YYxI}VRrp8Cj+Bp%>|Fgs=h}ttuJ^(E$k#L z7Yd;E^;XWg+X@N=&?jyL;A5rglp~)0*XU2D4LQnW@@bh34m;(3m02yW@QveH)Ry>O zG4i36#tY9Uxp(am>Sh2<%=z(hG~Sxywc28`F5$2DeIysbh~rJo|+8b4s zk{lS~sq!k>=ZA_v^_MCQUww--4iGT5h0|V

Dx8uLp=J?$&&ZYcte-3X4tT-oNgU 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 U0t2H3{6nUMfob9g))g@S2bnQ{v;Y7A literal 0 HcmV?d00001 -- GitLab From 85cf93b6d43ec6e1e67fbaf9843b4aecc267ddfa Mon Sep 17 00:00:00 2001 From: Matthias Betz Date: Fri, 12 Mar 2021 11:13:41 +0100 Subject: [PATCH 4/5] reworked check engine now working on requirements --- .../stuttgart/citydoctor2/check/Check.java | 5 +- .../stuttgart/citydoctor2/check/CheckId.java | 15 +- .../citydoctor2/check/Checkable.java | 2 +- .../citydoctor2/check/DefaultParameter.java | 20 ++- .../stuttgart/citydoctor2/check/ErrorId.java | 13 +- .../citydoctor2/check/Requirement.java | 136 ++++++++++++++ .../{CheckType.java => RequirementType.java} | 2 +- .../check/error/NotCeilingError.java | 2 +- .../check/error/NotFloorError.java | 2 +- .../check/error/NotGroundError.java | 2 +- .../citydoctor2/check/error/NotWallError.java | 2 +- .../check/error/SchematronError.java | 2 +- .../check/error/SurfaceUnfragmentedError.java | 2 +- .../datastructure/BoundingBox.java | 4 + .../citydoctor2/math/CovarianceMatrix.java | 6 + .../citydoctor2/math/Matrix3x3d.java | 2 +- .../utils/BoundingBoxCalculator.java | 54 +++++- .../stuttgart/citydoctor2/check/Checker.java | 134 ++++++++------ .../citydoctor2/check/GlobalParameters.java | 31 ++++ ...ion.java => RequirementConfiguration.java} | 7 +- .../check/ValidationConfiguration.java | 166 ++++++++---------- .../citydoctor2/checks/CheckContainer.java | 11 +- .../citydoctor2/checks/CheckPrototype.java | 10 +- .../stuttgart/citydoctor2/checks/Checks.java | 35 +++- ... => AllPolygonsWrongOrientationCheck.java} | 21 ++- .../checks/geometry/DuplicatePointsCheck.java | 16 +- .../checks/geometry/HoleOutsideCheck.java | 14 +- .../geometry/InteriorDisconnectedCheck.java | 42 +++-- .../checks/geometry/ManifoldVertexCheck.java | 16 +- .../MultipleConnectedComponentCheck.java | 16 +- .../checks/geometry/NestedRingsCheck.java | 25 ++- .../checks/geometry/NonManifoldEdgeCheck.java | 18 +- .../checks/geometry/NullAreaCheck.java | 14 +- .../checks/geometry/PlanarCheck.java | 72 +++++--- .../PolygonIntersectingRingsCheck.java | 27 ++- .../geometry/PolygonSameOrientationCheck.java | 28 ++- .../PolygonWrongOrientationCheck.java | 33 ++-- .../checks/geometry/RingNotClosedCheck.java | 18 +- .../checks/geometry/RingSelfIntCheck.java | 21 ++- .../checks/geometry/SolidNotClosedCheck.java | 19 +- .../checks/geometry/SolidSelfIntCheck.java | 14 +- ...ointsCheck.java => TooFewPointsCheck.java} | 23 ++- .../checks/geometry/TooFewPolygonsCheck.java | 19 +- .../semantics/GroundSurfaceUnfragmented.java | 16 +- .../checks/semantics/IsCeilingCheck.java | 16 +- .../checks/semantics/IsFloorCheck.java | 16 +- .../checks/semantics/IsGroundCheck.java | 16 +- .../checks/semantics/IsWallCheck.java | 16 +- .../RoofSurfaceUnfragmentedCheck.java | 16 +- .../checks/util/CollectionUtils.java | 43 +++++ .../reporting/XmlStreamReporter.java | 7 +- .../reporting/pdf/PdfStreamReporter.java | 6 +- .../citydoctor2/check/CheckerTest.java | 4 +- .../check/ValidationConfigurationTest.java | 21 +-- .../checks/CheckContainerTest.java | 12 +- .../geometry/DegeneratedPolygonCheckTest.java | 37 ++-- .../checks/geometry/FaceOutCheckTest.java | 4 +- .../checks/geometry/NumPointsCheckTest.java | 4 +- .../systemtest/NonManifoldEdgeSystemTest.java | 5 +- .../citydoctor2/systemtest/PlanarTest.java | 13 +- .../citydoctor2/systemtest/TestUtil.java | 13 +- .../src/test/resources/testConfig.yml | 50 +++--- .../test/resources/testConfigWithExclude.yml | 46 ++--- .../test/resources/testConfigWithFilter.yml | 47 ++--- .../test/resources/testConfigWithInclude.yml | 41 ++--- .../resources/testConfigWithStreaming.yml | 43 ++--- 66 files changed, 1093 insertions(+), 520 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Requirement.java rename CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/{CheckType.java => RequirementType.java} (97%) create mode 100644 CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/GlobalParameters.java rename CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/{CheckConfiguration.java => RequirementConfiguration.java} (93%) rename CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/{FaceOutCheck.java => AllPolygonsWrongOrientationCheck.java} (91%) rename CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/{NumPointsCheck.java => TooFewPointsCheck.java} (83%) create mode 100644 CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/CollectionUtils.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java index e421951..20db528 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java @@ -23,6 +23,7 @@ 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.error.DependenciesNotMetError; import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; @@ -98,6 +99,8 @@ public abstract class Check { public List getDependencies() { return Collections.emptyList(); } + + public abstract Set appliesToRequirements(); /** * Getter for the check id. @@ -111,7 +114,7 @@ public abstract class Check { * * @return the check type */ - public abstract CheckType getType(); + public abstract RequirementType getType(); /** * Checks whether the check can be executed on this checkable, meaning the diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckId.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckId.java index 1fac338..4dec1f1 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckId.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckId.java @@ -44,14 +44,13 @@ public class CheckId implements Serializable { public static final CheckId C_GE_P_HOLE_OUTSIDE = new CheckId("C_GE_P_HOLE_OUTSIDE"); public static final CheckId IS_CEILING = new CheckId("IS_CEILING"); public static final CheckId C_GE_P_INNER_RINGS_NESTED = new CheckId("C_GE_P_INNER_RINGS_NESTED"); - public static final CheckId C_SEM_BS_NOT_CEILING = new CheckId("C_SEM_BS_NOT_CEILING"); - public static final CheckId C_SEM_BS_NOT_FLOOR = new CheckId("C_SEM_BS_NOT_FLOOR"); - public static final CheckId C_SEM_BS_NOT_GROUND = new CheckId("C_SEM_BS_NOT_GROUND"); - public static final CheckId C_SEM_F_MISSING_ID = new CheckId("C_SEM_F_MISSING_ID"); - public static final CheckId C_SEM_BS_GROUND_NOT_FRAGMENTED = new CheckId("C_SEM_BS_GROUND_NOT_FRAGMENTED"); - public static final CheckId C_SEM_BS_IS_WALL = new CheckId("C_SEM_BS_IS_WALL"); - public static final CheckId C_SEM_SCHEMATRON = new CheckId("C_SEM_SCHEMATRON"); - public static final CheckId C_SEM_BS_ROOF_NOT_FRAGMENTED = new CheckId("C_SEM_BS_ROOF_NOT_FRAGMENTED"); + public static final CheckId C_SE_BS_IS_CEILING = new CheckId("C_SE_BS_IS_CEILING"); + public static final CheckId C_SE_BS_IS_FLOOR = new CheckId("C_SE_BS_IS_FLOOR"); + public static final CheckId C_SE_BS_IS_GROUND = new CheckId("C_SE_BS_IS_GROUND"); + public static final CheckId C_SE_BS_GROUND_NOT_FRAGMENTED = new CheckId("C_SE_BS_GROUND_NOT_FRAGMENTED"); + public static final CheckId C_SE_BS_IS_WALL = new CheckId("C_SE_BS_IS_WALL"); + public static final CheckId C_SE_SCHEMATRON = new CheckId("C_SE_SCHEMATRON"); + public static final CheckId C_SE_BS_ROOF_UNFRAGMENTED = new CheckId("C_SE_BS_ROOF_UNFRAGMENTED"); public static final CheckId C_GE_S_NOT_CLOSED = new CheckId("C_GE_S_NOT_CLOSED"); public static final CheckId C_GE_P_ORIENTATION_RINGS_SAME = new CheckId("C_GE_P_ORIENTATION_RINGS_SAME"); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java index e281a64..005c4b3 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java @@ -31,7 +31,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.GmlId; /** * Interface to indicate that this object can be checked by Checks. * - * @author Matthias Betz - 12bema1bif@hft-stuttgart.de + * @author Matthias Betz * */ public abstract class Checkable implements Serializable { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/DefaultParameter.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/DefaultParameter.java index 0c48ec1..2bb9aee 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/DefaultParameter.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/DefaultParameter.java @@ -18,6 +18,8 @@ */ package de.hft.stuttgart.citydoctor2.check; +import java.io.Serializable; + /** * Describes a parameter for a check including the default value as a String. * All values are a String as they are passed to the check as a Map. + */ +package de.hft.stuttgart.citydoctor2.check; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Requirement implements Serializable { + + private static final long serialVersionUID = -590639811553512803L; + + private static final String DISTANCE_TOLERANCE = "distanceTolerance"; + private static final String ANGLE_TOLERANCE = "angleTolerance"; + private static final String TYPE_STRING = "type"; + private static final String DEGENERATED_POLYGON_TOLERANCE = "degeneratedPolygonTolerance"; + private static final String LOWER_ANGLE_NAME = "lowerAngle"; + private static final String UPPER_ANGLE_NAME = "upperAngle"; + + public static final Requirement R_GE_R_TOO_FEW_POINTS = new Requirement("R_GE_R_TOO_FEW_POINTS", RequirementType.GEOMETRY); + public static final Requirement R_GE_R_NOT_CLOSED = new Requirement("R_GE_R_NOT_CLOSED", RequirementType.GEOMETRY); + public static final Requirement R_GE_R_CONSECUTIVE_POINTS_SAME = new Requirement("R_GE_R_CONSECUTIVE_POINTS_SAME", RequirementType.GEOMETRY); + public static final Requirement R_GE_R_SELF_INTERSECTION = new Requirement("R_GE_R_SELF_INTERSECTION", RequirementType.GEOMETRY); + public static final Requirement R_GE_P_NON_PLANAR = new Requirement("R_GE_P_NON_PLANAR", RequirementType.GEOMETRY); + public static final Requirement R_GE_P_INTERIOR_DISCONNECTED = new Requirement("R_GE_P_INTERIOR_DISCONNECTED", RequirementType.GEOMETRY); + public static final Requirement R_GE_P_INTERSECTING_RINGS = new Requirement("R_GE_P_INTERSECTING_RINGS", RequirementType.GEOMETRY); + public static final Requirement R_GE_P_HOLE_OUTSIDE = new Requirement("R_GE_P_HOLE_OUTSIDE", RequirementType.GEOMETRY); + public static final Requirement R_GE_P_ORIENTATION_RINGS_SAME = new Requirement("R_GE_P_ORIENTATION_RINGS_SAME", RequirementType.GEOMETRY); + public static final Requirement R_GE_P_INNER_RINGS_NESTED = new Requirement("R_GE_P_INNER_RINGS_NESTED", RequirementType.GEOMETRY); + public static final Requirement R_GE_S_TOO_FEW_POLYGONS = new Requirement("R_GE_S_TOO_FEW_POLYGONS", RequirementType.GEOMETRY); + public static final Requirement R_GE_S_NOT_CLOSED = new Requirement("R_GE_S_NOT_CLOSED", RequirementType.GEOMETRY); + public static final Requirement R_GE_S_NON_MANIFOLD_EDGE = new Requirement("R_GE_S_NON_MANIFOLD_EDGE", RequirementType.GEOMETRY); + public static final Requirement R_GE_S_POLYGON_WRONG_ORIENTATION = new Requirement( + "R_GE_S_POLYGON_WRONG_ORIENTATION", RequirementType.GEOMETRY); + public static final Requirement R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION = new Requirement( + "R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION", RequirementType.GEOMETRY); + public static final Requirement R_GE_S_NON_MANIFOLD_VERTEX = new Requirement("R_GE_S_NON_MANIFOLD_VERTEX", RequirementType.GEOMETRY); + public static final Requirement R_GE_S_SELF_INTERSECTION = new Requirement("R_GE_S_SELF_INTERSECTION", RequirementType.GEOMETRY); + public static final Requirement R_GE_S_MULTIPLE_CONNECTED_COMPONENTS = new Requirement( + "R_GE_S_MULTIPLE_CONNECTED_COMPONENTS", RequirementType.GEOMETRY); + public static final Requirement R_SE_ATTRIBUTES_EXISTING = new Requirement("R_SE_ATTRIBUTES_EXISTING", RequirementType.SEMANTIC); + public static final Requirement R_SE_ATTRIBUTES_CORRECT = new Requirement("R_SE_ATTRIBUTES_CORRECT", RequirementType.SEMANTIC); + public static final Requirement R_GE_R_NULL_AREA = new Requirement("R_GE_R_NULL_AREA", RequirementType.GEOMETRY); + + public static final Requirement R_SE_BS_GROUND_UNFRAGMENTED = new Requirement("R_SE_BS_GROUND_UNFRAGMENTED", RequirementType.SEMANTIC); + public static final Requirement R_SE_BS_ROOF_UNFRAGMENTED = new Requirement("R_SE_BS_GROUND_UNFRAGMENTED", RequirementType.SEMANTIC); + public static final Requirement R_SE_BS_IS_CEILING = new Requirement("R_SE_BS_IS_CEILING", RequirementType.SEMANTIC); + public static final Requirement R_SE_BS_IS_FLOOR = new Requirement("R_SE_BS_IS_FLOOR", RequirementType.SEMANTIC); + public static final Requirement R_SE_BS_IS_WALL = new Requirement("R_SE_BS_IS_WALL", RequirementType.SEMANTIC); + public static final Requirement R_SE_BS_IS_GROUND = new Requirement("R_SE_BS_IS_GROUND", RequirementType.SEMANTIC); + + static { + // fill requirements with default parameters + ArrayList defaultParameters = new ArrayList<>(); + defaultParameters.add(new DefaultParameter(TYPE_STRING, "distance", Unit.NONE)); + defaultParameters.add(new DefaultParameter(DISTANCE_TOLERANCE, "0.01", Unit.METER)); + defaultParameters.add(new DefaultParameter(ANGLE_TOLERANCE, "1", Unit.DEGREE)); + defaultParameters.add(new DefaultParameter(DEGENERATED_POLYGON_TOLERANCE, "0.00000", Unit.METER)); + R_GE_P_NON_PLANAR.parameters = Collections.unmodifiableList(defaultParameters); + + defaultParameters = new ArrayList<>(); + defaultParameters.add(new DefaultParameter(LOWER_ANGLE_NAME, "45", Unit.DEGREE)); + defaultParameters.add(new DefaultParameter(UPPER_ANGLE_NAME, "135", Unit.DEGREE)); + R_SE_BS_IS_WALL.parameters = Collections.unmodifiableList(defaultParameters); + } + + private String id; + private RequirementType type; + private List parameters; + + public Requirement(String id, RequirementType type) { + this.id = id; + this.type = type; + parameters = Collections.emptyList(); + } + + public String getId() { + return id; + } + + public List getDefaultParameter() { + return parameters; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Requirement other = (Requirement) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + + @Override + public String toString() { + return id; + } + + public RequirementType getType() { + return type; + } + +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckType.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/RequirementType.java similarity index 97% rename from CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckType.java rename to CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/RequirementType.java index b8f2afd..46d63f3 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckType.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/RequirementType.java @@ -26,7 +26,7 @@ package de.hft.stuttgart.citydoctor2.check; * @author Matthias Betz * */ -public enum CheckType { +public enum RequirementType { GEOMETRY, SEMANTIC diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotCeilingError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotCeilingError.java index 4ed6bea..699ad8b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotCeilingError.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotCeilingError.java @@ -79,7 +79,7 @@ public class NotCeilingError implements CheckError { @Override public ErrorId getErrorId() { - return ErrorId.SEM_BS_NOT_CEILING; + return ErrorId.SE_BS_NOT_CEILING; } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotFloorError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotFloorError.java index 1b8198f..7f4aea9 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotFloorError.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotFloorError.java @@ -77,7 +77,7 @@ public class NotFloorError implements CheckError { @Override public ErrorId getErrorId() { - return ErrorId.SEM_BS_NOT_FLOOR; + return ErrorId.SE_BS_NOT_FLOOR; } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotGroundError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotGroundError.java index b810a6f..90395bc 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotGroundError.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotGroundError.java @@ -79,7 +79,7 @@ public class NotGroundError implements CheckError { @Override public ErrorId getErrorId() { - return ErrorId.SEM_BS_NOT_GROUND; + return ErrorId.SE_BS_NOT_GROUND; } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotWallError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotWallError.java index 44a3efa..6eb0835 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotWallError.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/NotWallError.java @@ -82,7 +82,7 @@ public class NotWallError implements CheckError { @Override public ErrorId getErrorId() { - return ErrorId.SEM_BS_NOT_WALL; + return ErrorId.SE_BS_NOT_WALL; } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java index f127027..cdc72e8 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java @@ -94,7 +94,7 @@ public class SchematronError implements CheckError { @Override public ErrorId getErrorId() { - return ErrorId.SEM_SCHEMATRON_ERROR; + return ErrorId.SE_SCHEMATRON_ERROR; } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SurfaceUnfragmentedError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SurfaceUnfragmentedError.java index 55d02c6..16ceb6b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SurfaceUnfragmentedError.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SurfaceUnfragmentedError.java @@ -77,7 +77,7 @@ public class SurfaceUnfragmentedError implements CheckError { @Override public ErrorId getErrorId() { - return ErrorId.SEM_BS_UNFRAGMENTED; + return ErrorId.SE_BS_UNFRAGMENTED; } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java index 13889c2..357f39d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java @@ -42,6 +42,10 @@ public class BoundingBox { public static BoundingBox of(List polygons) { return BoundingBoxCalculator.calculateBoundingBox(polygons); } + + public static BoundingBox ofPoints(List points) { + return BoundingBoxCalculator.calculateBoundingBoxFromPoints(points); + } /** * Creates an axis aligned bounding box of the whole model. diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/CovarianceMatrix.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/CovarianceMatrix.java index f6df612..0c58931 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/CovarianceMatrix.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/CovarianceMatrix.java @@ -56,6 +56,12 @@ public class CovarianceMatrix { covValues[1][2] += ydiff * zdiff; covValues[2][2] += zdiff * zdiff; } + covValues[0][0] /= vertices.size(); + covValues[0][1] /= vertices.size(); + covValues[0][2] /= vertices.size(); + covValues[1][1] /= vertices.size(); + covValues[1][2] /= vertices.size(); + covValues[2][2] /= vertices.size(); // the covariance matrix is symmetric, so we can fill in the remaining values covValues[1][0] = covValues[0][1]; covValues[2][0] = covValues[0][2]; diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Matrix3x3d.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Matrix3x3d.java index c112ee3..5d75193 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Matrix3x3d.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Matrix3x3d.java @@ -135,7 +135,7 @@ public class Matrix3x3d { /** * Multiplies this matrix with a 3-dim vector. * - * @param other the vector + * @param the multiplied vector * @return the result stored in a new vector */ public Vector3d mult(Vector3d other) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java index 5ff2bf8..2d50bf6 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java @@ -63,17 +63,20 @@ public class BoundingBoxCalculator { for (Vertex v : p.getExteriorRing().getVertices()) { if (v.getX() < lowX) { lowX = v.getX(); - } else if (v.getX() > highX) { + } + if (v.getX() > highX) { highX = v.getX(); } if (v.getY() < lowY) { lowY = v.getY(); - } else if (v.getY() > highY) { + } + if (v.getY() > highY) { highY = v.getY(); } if (v.getZ() < lowZ) { lowZ = v.getZ(); - } else if (v.getZ() > highZ) { + } + if (v.getZ() > highZ) { highZ = v.getZ(); } } @@ -92,7 +95,7 @@ public class BoundingBoxCalculator { */ public static BoundingBox calculateBoundingBox(CityDoctorModel model) { Vector3d low = new Vector3d(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); - Vector3d high = new Vector3d(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE); + Vector3d high = new Vector3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); findMinMax(low, high, model.getBuildings()); findMinMax(low, high, model.getBridges()); @@ -107,6 +110,40 @@ public class BoundingBoxCalculator { return BoundingBox.of(result); } + public static BoundingBox calculateBoundingBoxFromPoints(List points) { + double lowX = Double.MAX_VALUE; + double highX = Double.NEGATIVE_INFINITY; + double lowY = Double.MAX_VALUE; + double highY = Double.NEGATIVE_INFINITY; + double lowZ = Double.MAX_VALUE; + double highZ = Double.NEGATIVE_INFINITY; + // only need to check exterior rings + for (Vector3d v : points) { + if (v.getX() < lowX) { + lowX = v.getX(); + } + if (v.getX() > highX) { + highX = v.getX(); + } + if (v.getY() < lowY) { + lowY = v.getY(); + } + if (v.getY() > highY) { + highY = v.getY(); + } + if (v.getZ() < lowZ) { + lowZ = v.getZ(); + } + if (v.getZ() > highZ) { + highZ = v.getZ(); + } + } + Vector3d[] result = new Vector3d[2]; + result[0] = new Vector3d(lowX, lowY, lowZ); + result[1] = new Vector3d(highX, highY, highZ); + return BoundingBox.of(result); + } + private static void findMinMax(Vector3d low, Vector3d high, List features) { for (CityObject co : features) { findMinMax(low, high, co); @@ -121,17 +158,20 @@ public class BoundingBoxCalculator { for (Vertex v : geom.getVertices()) { if (v.getX() < low.getX()) { low.setX(v.getX()); - } else if (v.getX() > high.getX()) { + } + if (v.getX() > high.getX()) { high.setX(v.getX()); } if (v.getY() < low.getY()) { low.setY(v.getY()); - } else if (v.getY() > high.getY()) { + } + if (v.getY() > high.getY()) { high.setY(v.getY()); } if (v.getZ() < low.getZ()) { low.setZ(v.getZ()); - } else if (v.getZ() > high.getZ()) { + } + if (v.getZ() > high.getZ()) { high.setZ(v.getZ()); } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index b439cdb..c24700d 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -241,7 +241,7 @@ public class Checker { ValidationPlan plan = new ValidationPlan(); List filter = createFilter(); - for (Entry e : config.getChecks().entrySet()) { + for (Entry e : config.getRequirements().entrySet()) { RequirementId reqId = mapToRequirement(e.getKey()); if (reqId == null) { continue; @@ -250,12 +250,12 @@ public class Checker { req.setName(reqId); req.setEnabled(e.getValue().isEnabled()); plan.getRequirements().add(req); - CheckPrototype proto = Checks.getCheckPrototypeForId(e.getKey()); Map parameters = e.getValue().getParameters(); + Map reqs = Checks.getAvailableRequirements(); if (parameters != null) { for (Entry param : parameters.entrySet()) { Parameter p = new Parameter(); - DefaultParameter defaultP = getDefaultParameter(param.getKey(), proto); + DefaultParameter defaultP = getDefaultParameter(param.getKey(), reqs); if (defaultP != null) { p.setUom(defaultP.getUnitType().getGmlRepresentation()); } @@ -292,10 +292,14 @@ public class Checker { return plan; } - private DefaultParameter getDefaultParameter(String key, CheckPrototype proto) { - for (DefaultParameter param : proto.getDefaultParameter()) { - if (param.getName().equals(key)) { - return param; + private DefaultParameter getDefaultParameter(String key, + Map reqs) { + de.hft.stuttgart.citydoctor2.check.Requirement requirement = reqs.get(key); + if (requirement != null) { + for (DefaultParameter param : requirement.getDefaultParameter()) { + if (param.getName().equals(key)) { + return param; + } } } return null; @@ -382,43 +386,10 @@ public class Checker { } } - private RequirementId mapToRequirement(CheckId key) { - switch (key.getName()) { - case "C_GE_R_TOO_FEW_POINTS": - return RequirementId.R_GE_R_TOO_FEW_POINTS; - case "C_GE_R_NOT_CLOSED": - return RequirementId.R_GE_R_NOT_CLOSED; - case "C_GE_R_DUPLICATE_POINT": - return RequirementId.R_GE_R_CONSECUTIVE_POINTS_SAME; - case "C_GE_R_SELF_INTERSECTION": - return RequirementId.R_GE_R_SELF_INTERSECTION; - case "C_GE_P_INTERIOR_DISCONNECTED": - return RequirementId.R_GE_P_INTERIOR_DISCONNECTED; - case "C_GE_P_INTERSECTING_RINGS": - return RequirementId.R_GE_P_INTERSECTING_RINGS; - case "C_GE_P_NON_PLANAR": - return RequirementId.R_GE_P_NON_PLANAR; - case "C_GE_S_TOO_FEW_POLYGONS": - return RequirementId.R_GE_S_TOO_FEW_POLYGONS; - case "C_GE_S_NON_MANIFOLD_EDGE": - return RequirementId.R_GE_S_NON_MANIFOLD_EDGE; - case "C_GE_S_POLYGON_WRONG_ORIENTATION": - return RequirementId.R_GE_S_POLYGON_WRONG_ORIENTATION; - case "C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION": - return RequirementId.R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION; - case "C_GE_S_NON_MANIFOLD_VERTEX": - return RequirementId.R_GE_S_NON_MANIFOLD_VERTEX; - case "C_GE_S_SELF_INTERSECTION": - return RequirementId.R_GE_S_SELF_INTERSECTION; - case "C_GE_P_HOLE_OUTSIDE": - return RequirementId.R_GE_P_HOLE_OUTSIDE; - case "C_GE_P_INNER_RINGS_NESTED": - return RequirementId.R_GE_P_INNER_RINGS_NESTED; - case "C_GE_S_NOT_CLOSED": - return RequirementId.R_GE_S_NOT_CLOSED; - case "C_GE_S_MULTIPLE_CONNECTED_COMPONENTS": - return RequirementId.R_GE_S_MULTIPLE_CONNECTED_COMPONENTS; - default: + private RequirementId mapToRequirement(String requirementName) { + try { + return RequirementId.valueOf(requirementName); + } catch (IllegalArgumentException e) { return null; } } @@ -557,20 +528,71 @@ public class Checker { } private List collectEnabledChecksAndInit(ParserConfiguration parserConfig, ValidationConfiguration config) { - List checks = new ArrayList<>(); - for (Entry e : config.getChecks().entrySet()) { - if (e.getValue().isEnabled()) { - Check c = checkConfig.getCheckForId(e.getKey()); - Map parameters = new HashMap<>(); - parameters.putAll(e.getValue().getParameters()); - parameters.put("numberOfRoundingPlaces", "" + config.getNumberOfRoundingPlaces()); - parameters.put("minVertexDistance", "" + config.getMinVertexDistance()); - // initialize checks with parameters - c.init(parameters, parserConfig); - checks.add(c); + Set enabledCheck = new HashSet<>(); + Map> parameterMap = new HashMap<>(); + for (Entry e : config.getRequirements().entrySet()) { + de.hft.stuttgart.citydoctor2.check.Requirement req = Checks.getAvailableRequirements().get(e.getKey()); + if (req == null) { + logger.warn("Could not find any check that satisfies requirement {}, it will not be checked", + e.getKey()); + } else { + if (e.getValue().isEnabled()) { + // this requirement is enabled + for (CheckPrototype proto : Checks.getCheckPrototypes()) { + if (proto.checksRequirements().contains(req)) { + // this requirement is checked by this check + // put all requirement parameter in the map + parameterMap.compute(proto.getCheckId(), (k, v) -> { + if (v == null) { + v = new HashMap<>(); + v.put(GlobalParameters.NUMBER_OF_ROUNDING_PLACES, + config.getNumberOfRoundingPlacesAsString()); + v.put(GlobalParameters.MIN_VERTEX_DISTANCE, config.getMinVertexDistanceAsString()); + } + v.putAll(e.getValue().getParameters()); + return v; + }); + enabledCheck.add(proto.getCheckId()); + collectDependencyChecks(proto, enabledCheck); + } + } + } + } + } + fillParameterMapsWithDefaultParameter(enabledCheck, parameterMap); + ArrayList checkList = new ArrayList<>(); + for (CheckId id : enabledCheck) { + Check c = checkConfig.getCheckForId(id); + c.init(parameterMap.get(id), parserConfig); + checkList.add(c); + } + return checkList; + } + + private void fillParameterMapsWithDefaultParameter(Set enabledCheck, + Map> parameterMap) { + for (CheckId id : enabledCheck) { + CheckPrototype proto = Checks.getCheckPrototypeForId(id); + Map map = parameterMap.computeIfAbsent(id, k -> new HashMap<>()); + for (de.hft.stuttgart.citydoctor2.check.Requirement req : proto.checksRequirements()) { + if (proto.checksRequirements().contains(req)) { + for (DefaultParameter param : req.getDefaultParameter()) { + map.computeIfAbsent(param.getName(), k -> param.getValue()); + } + } + } + } + } + + private void collectDependencyChecks(CheckPrototype proto, Set enabledChecks) { + enabledChecks.addAll(proto.getDependencies()); + for (CheckId id : proto.getDependencies()) { + if (enabledChecks.contains(id)) { + continue; } + CheckPrototype depProto = Checks.getCheckPrototypeForId(id); + collectDependencyChecks(depProto, enabledChecks); } - return checks; } private void checkCityModel(CityDoctorModel model, ProgressListener l) { diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/GlobalParameters.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/GlobalParameters.java new file mode 100644 index 0000000..74e197d --- /dev/null +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/GlobalParameters.java @@ -0,0 +1,31 @@ +/*- + * 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 . + */ +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"; + +} diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckConfiguration.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/RequirementConfiguration.java similarity index 93% rename from CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckConfiguration.java rename to CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/RequirementConfiguration.java index 142a9b1..4ae0c72 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckConfiguration.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/RequirementConfiguration.java @@ -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 parameters) { this.parameters = parameters; } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/ValidationConfiguration.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/ValidationConfiguration.java index 35fbe42..75b0da9 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/ValidationConfiguration.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/ValidationConfiguration.java @@ -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 globalParameters; private boolean xmlValidation = false; private boolean useStreaming = false; private FilterConfiguration filter; - private Map checks; + private Map 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 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 getChecks() { - if (checks == null) { - checks = new HashMap<>(); + public Map getRequirements() { + if (requirements == null) { + requirements = new HashMap<>(); } - return checks; + return requirements; } - public void setChecks(Map checks) { - this.checks = checks; + public Map getGlobalParameters() { + if (globalParameters == null) { + globalParameters = new HashMap<>(); + } + return globalParameters; + } + + public void setGlobalParameters(Map 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 availableRequirements = Checks.getAvailableRequirements(); + + for (Entry 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 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 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 requirements) { + this.requirements = requirements; } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java index e60d8ec..eb9ee05 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java @@ -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,10 @@ 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.DefaultParameter; +import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError; import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; @@ -68,6 +70,11 @@ public class CheckContainer extends Check { public CheckContainer(Check check) { this.check = check; } + + @Override + public Set appliesToRequirements() { + return check.appliesToRequirements(); + } @Override public void init(Map parameters, ParserConfiguration config) { @@ -244,7 +251,7 @@ public class CheckContainer extends Check { } @Override - public CheckType getType() { + public RequirementType getType() { return check.getType(); } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckPrototype.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckPrototype.java index 9676b59..975e959 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckPrototype.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckPrototype.java @@ -19,11 +19,13 @@ 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.RequirementType; import de.hft.stuttgart.citydoctor2.check.DefaultParameter; +import de.hft.stuttgart.citydoctor2.check.Requirement; /** * This class is for having a read only access to a check for accessing meta @@ -62,8 +64,12 @@ public class CheckPrototype { return c.getDefaultParameter(); } - public CheckType getType() { + public RequirementType getType() { return c.getType(); } + public Set checksRequirements() { + return c.appliesToRequirements(); + } + } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/Checks.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/Checks.java index 61af958..813170a 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/Checks.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/Checks.java @@ -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; @@ -69,6 +70,8 @@ public class Checks { private static List checkPrototypes; private static Map prototypeMap; + private static Map availableRequirements; + private Map checkMap; @@ -78,7 +81,7 @@ public class Checks { // add new checks here // ring checks - publish(new NumPointsCheck()); + publish(new TooFewPointsCheck()); publish(new RingNotClosedCheck()); publish(new DuplicatePointsCheck()); publish(new RingSelfIntCheck()); @@ -96,7 +99,7 @@ public class Checks { publish(new SolidNotClosedCheck()); publish(new NonManifoldEdgeCheck()); publish(new PolygonWrongOrientationCheck()); - publish(new FaceOutCheck()); + publish(new AllPolygonsWrongOrientationCheck()); publish(new TooFewPolygonsCheck()); publish(new ManifoldVertexCheck()); publish(new SolidSelfIntCheck()); @@ -128,6 +131,30 @@ public class Checks { } } + private static Map collectRequirements() { + Map 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 getAvailableRequirements() { + if (availableRequirements == null) { + availableRequirements = collectRequirements(); + } + return availableRequirements; + } + private static void publish(Check check) { CheckPrototype prototype = new CheckPrototype(check); checkPrototypes.add(prototype); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/FaceOutCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/AllPolygonsWrongOrientationCheck.java similarity index 91% rename from CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/FaceOutCheck.java rename to CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/AllPolygonsWrongOrientationCheck.java index 00799cd..17d3b1a 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/FaceOutCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/AllPolygonsWrongOrientationCheck.java @@ -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 dependencies; @@ -171,15 +175,20 @@ public class FaceOutCheck extends Check { public List getDependencies() { return dependencies; } + + @Override + public Set 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 diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/DuplicatePointsCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/DuplicatePointsCheck.java index 3e8e616..991e7f2 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/DuplicatePointsCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/DuplicatePointsCheck.java @@ -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; *

*
CP_CLOSE
* - * @author Matthias Betz - 12bema1bif@hft-stuttgart.de + * @author Matthias Betz * */ public class DuplicatePointsCheck extends Check { @@ -77,6 +80,11 @@ public class DuplicatePointsCheck extends Check { public List getDependencies() { return dependencies; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.asSet(Requirement.R_GE_R_CONSECUTIVE_POINTS_SAME, Requirement.R_GE_R_SELF_INTERSECTION); + } @Override public void check(LinearRing lr) { @@ -114,8 +122,8 @@ public class DuplicatePointsCheck extends Check { } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/HoleOutsideCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/HoleOutsideCheck.java index 68f60a1..8bb90e5 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/HoleOutsideCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/HoleOutsideCheck.java @@ -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 getDependencies() { return dependencies; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_P_HOLE_OUTSIDE); + } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/InteriorDisconnectedCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/InteriorDisconnectedCheck.java index 95c3e4d..99fa9f4 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/InteriorDisconnectedCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/InteriorDisconnectedCheck.java @@ -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 dependencies; - + static { ArrayList 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 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 getDependencies() { return dependencies; } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_P_INTERIOR_DISCONNECTED); + } + + @Override + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/ManifoldVertexCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/ManifoldVertexCheck.java index 5a2c1c9..5e97cd5 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/ManifoldVertexCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/ManifoldVertexCheck.java @@ -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 getDependencies() { return dependencies; } + + @Override + public Set 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 diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/MultipleConnectedComponentCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/MultipleConnectedComponentCheck.java index 0caf422..3d87992 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/MultipleConnectedComponentCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/MultipleConnectedComponentCheck.java @@ -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 getDependencies() { return dependencies; } + + @Override + public Set 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 diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NestedRingsCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NestedRingsCheck.java index 7d5f9cc..fed3f15 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NestedRingsCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NestedRingsCheck.java @@ -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 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 getDependencies() { return dependencies; } + + @Override + public Set 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 diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NonManifoldEdgeCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NonManifoldEdgeCheck.java index 2f634d4..9d4261c 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NonManifoldEdgeCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NonManifoldEdgeCheck.java @@ -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 appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_S_NON_MANIFOLD_EDGE); + } @Override public List getDependencies() { @@ -87,8 +97,8 @@ public class NonManifoldEdgeCheck extends Check { } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NullAreaCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NullAreaCheck.java index 8f3a2c4..3b5a6fb 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NullAreaCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NullAreaCheck.java @@ -22,15 +22,18 @@ 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.RequirementType; import de.hft.stuttgart.citydoctor2.check.DefaultParameter; +import de.hft.stuttgart.citydoctor2.check.Requirement; 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; @@ -106,6 +109,11 @@ public class NullAreaCheck extends Check { } return area < 0.0001; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_R_NULL_AREA); + } @Override public List getDependencies() { @@ -113,8 +121,8 @@ public class NullAreaCheck extends Check { } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public RequirementType getType() { + return RequirementType.GEOMETRY; } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java index 9f63a86..46b42ff 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java @@ -22,23 +22,28 @@ 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.RequirementType; import de.hft.stuttgart.citydoctor2.check.DefaultParameter; +import de.hft.stuttgart.citydoctor2.check.Requirement; 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,18 +53,19 @@ 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 * */ public class PlanarCheck extends Check { - private static final String DISTANCE = "distance"; - private static final String DISTANCE_TOLERANCE = "distanceTolerance"; - private static final String ANGLE_TOLERANCE = "angleTolerance"; - private static final String TYPE = "type"; - private static final String DEGENERATED_POLYGON_TOLERANCE = "degeneratedPolygonTolerance"; + public static final String DISTANCE = "distance"; + public static final String DISTANCE_TOLERANCE = "distanceTolerance"; + public static final String ANGLE_TOLERANCE = "angleTolerance"; + public static final String TYPE = "type"; + public static final String DEGENERATED_POLYGON_TOLERANCE = "degeneratedPolygonTolerance"; private static final List dependencies; private static final List defaultParameters; @@ -91,6 +97,8 @@ public class PlanarCheck extends Check { public void init(Map 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))); @@ -115,8 +123,10 @@ public class PlanarCheck extends Check { } else if ("angle".equals(planarCheckType)) { // check for tiny edge as well // store all used points in temporary list - Vector3d eigenvalues = calculatedEigenvalues(p); - if (checkEigenvalues(p, eigenvalues)) { + ArrayList vertices = collectVertices(p); + Vector3d centroid = CovarianceMatrix.getCentroid(vertices); + EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid); + if (checkEigenvalues(p, vertices, ed)) { // found tiny edge error, abort further checking return; } @@ -130,13 +140,6 @@ public class PlanarCheck extends Check { } } - private Vector3d calculatedEigenvalues(Polygon p) { - ArrayList vertices = collectVertices(p); - Vector3d centroid = CovarianceMatrix.getCentroid(vertices); - EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid); - return OrthogonalRegressionPlane.getEigenvalues(ed); - } - private void planarNormalDeviation(Polygon p) { TesselatedPolygon tp = JoglTesselator.tesselatePolygon(p); ArrayList normals = new ArrayList<>(); @@ -185,13 +188,13 @@ public class PlanarCheck extends Check { ArrayList 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); @@ -224,13 +227,25 @@ public class PlanarCheck extends Check { return vertices; } - private boolean checkEigenvalues(Polygon p, Vector3d eigenvalues) { + private boolean checkEigenvalues(Polygon p, List points, EigenvalueDecomposition ed) { + Matrix3x3d mat = new Matrix3x3d(ed.getV().getArray()); + List 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)); @@ -245,8 +260,13 @@ public class PlanarCheck extends Check { } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_P_NON_PLANAR); + } + + @Override + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonIntersectingRingsCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonIntersectingRingsCheck.java index 9eb7c24..0e2e834 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonIntersectingRingsCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonIntersectingRingsCheck.java @@ -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 dependencies; - + static { ArrayList 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 interiorRingsIntersectWithExterior(Polygon p) { List>> 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 appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_P_INTERSECTING_RINGS); + } + + @Override + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonSameOrientationCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonSameOrientationCheck.java index 7257585..778ec21 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonSameOrientationCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonSameOrientationCheck.java @@ -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 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 appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_P_ORIENTATION_RINGS_SAME); + } + + @Override + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonWrongOrientationCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonWrongOrientationCheck.java index c0e8fd9..38c9629 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonWrongOrientationCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PolygonWrongOrientationCheck.java @@ -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 dependencies; - + static { ArrayList 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 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 getDependencies() { return dependencies; } - + + @Override + public Set 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 diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingNotClosedCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingNotClosedCheck.java index 156af28..b1948f7 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingNotClosedCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingNotClosedCheck.java @@ -21,22 +21,25 @@ 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.RingNotClosedError; +import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.Vertex; /** - * CP_CLOSE checks the closeness a linear ring. A linear ring must end where it + * C_GE_R_NOT_CLOSED checks the closeness a linear ring. A linear ring must end where it * started. * - * @author Matthias Betz - 12bema1bif@hft-stuttgart.de + * @author Matthias Betz * */ public class RingNotClosedCheck extends Check { @@ -69,8 +72,13 @@ public class RingNotClosedCheck extends Check { } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public RequirementType getType() { + return RequirementType.GEOMETRY; + } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_R_NOT_CLOSED); } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java index f65b0f8..0598121 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingSelfIntCheck.java @@ -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.Requirement; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError; import de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError; import de.hft.stuttgart.citydoctor2.checks.Checks; +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.LinearRing; @@ -40,6 +43,13 @@ import de.hft.stuttgart.citydoctor2.math.DistanceResult; import de.hft.stuttgart.citydoctor2.math.Segment3d; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +/** + * Checks whether a ring self intersects. Also checks if a point is too close to + * an edge of the ring. + * + * @author Matthias Betz + * + */ public class RingSelfIntCheck extends Check { private static final String EPSILON_NAME = "minVertexDistance"; @@ -138,8 +148,13 @@ public class RingSelfIntCheck extends Check { } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_R_SELF_INTERSECTION); + } + + @Override + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidNotClosedCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidNotClosedCheck.java index 2239d94..fb282bb 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidNotClosedCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidNotClosedCheck.java @@ -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.SolidNotClosedError; +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,8 +40,9 @@ import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; * This class detects half edges without neighbor, i.e. holes in solid * geometries * - * @author dwagner, alam - * @author Matthias Betz - 12bema1bif@hft-stuttgart.de + * @author dwagner + * @author alam + * @author Matthias Betz */ public class SolidNotClosedCheck extends Check { @@ -87,10 +91,15 @@ public class SolidNotClosedCheck extends Check { public List getDependencies() { return dependencies; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_S_NOT_CLOSED); + } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java index ed5f7d0..67f16dd 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java @@ -21,15 +21,18 @@ 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.GeometrySelfIntersection; +import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError; +import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; @@ -98,10 +101,15 @@ public class SolidSelfIntCheck extends Check { public List getDependencies() { return dependencies; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_S_SELF_INTERSECTION); + } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NumPointsCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/TooFewPointsCheck.java similarity index 83% rename from CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NumPointsCheck.java rename to CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/TooFewPointsCheck.java index f2e7cbe..1eb348c 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NumPointsCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/TooFewPointsCheck.java @@ -28,23 +28,25 @@ 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.RingTooFewPointsError; +import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.Vertex; /** - * CP_NUMPOINTS checks the minimum number of point in a linear ring. At least + * C_GE_R_TOO_FEW_POINTS checks the minimum number of point in a linear ring. At least * three vertices are minimum for a linear ring.
*
* Dependency:
*
- *
none
+ *
C_GE_R_NOT_CLOSED
* - * @author Matthias Betz - 12bema1bif@hft-stuttgart.de + * @author Matthias Betz */ -public class NumPointsCheck extends Check { +public class TooFewPointsCheck extends Check { private static final List dependencies; @@ -95,15 +97,20 @@ public class NumPointsCheck extends Check { public List getDependencies() { return dependencies; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_R_TOO_FEW_POINTS); + } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override public Check createNewInstance() { - return new NumPointsCheck(); + return new TooFewPointsCheck(); } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/TooFewPolygonsCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/TooFewPolygonsCheck.java index 9365e87..670e009 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/TooFewPolygonsCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/TooFewPolygonsCheck.java @@ -21,21 +21,24 @@ 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.TooFewPolygonsError; +import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; /** - * This class checks minimum number of planes for a solid + * This class checks minimum number of polygons for a solid * - * @author Matthias Betz 12bema1bif@hft-stuttgart.de + * @author Matthias Betz */ public class TooFewPolygonsCheck extends Check { @@ -59,7 +62,6 @@ public class TooFewPolygonsCheck extends Check { * not. * * @param toBeCheckedGeometry The ViewableGeometry that has to be checked. - * @return Vector of CDErrors */ @Override public void check(Geometry g) { @@ -81,10 +83,15 @@ public class TooFewPolygonsCheck extends Check { public List getDependencies() { return dependencies; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_GE_S_TOO_FEW_POLYGONS); + } @Override - public CheckType getType() { - return CheckType.GEOMETRY; + public RequirementType getType() { + return RequirementType.GEOMETRY; } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/GroundSurfaceUnfragmented.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/GroundSurfaceUnfragmented.java index 55bcad7..6ff7b13 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/GroundSurfaceUnfragmented.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/GroundSurfaceUnfragmented.java @@ -21,12 +21,15 @@ package de.hft.stuttgart.citydoctor2.checks.semantics; 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.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.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.checks.util.UnfragmentedCheck; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; @@ -66,10 +69,15 @@ public class GroundSurfaceUnfragmented extends Check { public List getDependencies() { return dependencies; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_SE_BS_GROUND_UNFRAGMENTED); + } @Override - public CheckType getType() { - return CheckType.SEMANTIC; + public RequirementType getType() { + return RequirementType.SEMANTIC; } @Override @@ -79,7 +87,7 @@ public class GroundSurfaceUnfragmented extends Check { @Override public CheckId getCheckId() { - return CheckId.C_SEM_BS_GROUND_NOT_FRAGMENTED; + return CheckId.C_SE_BS_GROUND_NOT_FRAGMENTED; } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsCeilingCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsCeilingCheck.java index c825c04..67df459 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsCeilingCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsCeilingCheck.java @@ -21,13 +21,16 @@ package de.hft.stuttgart.citydoctor2.checks.semantics; 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.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.NotCeilingError; +import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; @@ -80,10 +83,15 @@ public class IsCeilingCheck extends Check { public List getDependencies() { return dependencies; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_SE_BS_IS_CEILING); + } @Override - public CheckType getType() { - return CheckType.SEMANTIC; + public RequirementType getType() { + return RequirementType.SEMANTIC; } @Override @@ -93,7 +101,7 @@ public class IsCeilingCheck extends Check { @Override public CheckId getCheckId() { - return CheckId.C_SEM_BS_NOT_CEILING; + return CheckId.C_SE_BS_IS_CEILING; } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsFloorCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsFloorCheck.java index e2e0017..5e55082 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsFloorCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsFloorCheck.java @@ -21,13 +21,16 @@ package de.hft.stuttgart.citydoctor2.checks.semantics; 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.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.NotFloorError; +import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; @@ -83,8 +86,13 @@ public class IsFloorCheck extends Check { } @Override - public CheckType getType() { - return CheckType.SEMANTIC; + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_SE_BS_IS_FLOOR); + } + + @Override + public RequirementType getType() { + return RequirementType.SEMANTIC; } @Override @@ -94,7 +102,7 @@ public class IsFloorCheck extends Check { @Override public CheckId getCheckId() { - return CheckId.C_SEM_BS_NOT_FLOOR; + return CheckId.C_SE_BS_IS_FLOOR; } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsGroundCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsGroundCheck.java index 0d4e46f..89a1b19 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsGroundCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsGroundCheck.java @@ -21,13 +21,16 @@ package de.hft.stuttgart.citydoctor2.checks.semantics; 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.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.NotGroundError; +import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; @@ -80,10 +83,15 @@ public class IsGroundCheck extends Check { public List getDependencies() { return dependencies; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_SE_BS_IS_GROUND); + } @Override - public CheckType getType() { - return CheckType.SEMANTIC; + public RequirementType getType() { + return RequirementType.SEMANTIC; } @Override @@ -93,6 +101,6 @@ public class IsGroundCheck extends Check { @Override public CheckId getCheckId() { - return CheckId.C_SEM_BS_NOT_GROUND; + return CheckId.C_SE_BS_IS_GROUND; } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsWallCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsWallCheck.java index 85437c5..4cfb380 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsWallCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsWallCheck.java @@ -22,15 +22,18 @@ 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.RequirementType; import de.hft.stuttgart.citydoctor2.check.DefaultParameter; +import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.Unit; import de.hft.stuttgart.citydoctor2.check.error.NotWallError; +import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; @@ -132,14 +135,19 @@ public class IsWallCheck extends Check { return dependencies; } + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_SE_BS_IS_WALL); + } + @Override public List getDefaultParameter() { return defaultParameters; } @Override - public CheckType getType() { - return CheckType.SEMANTIC; + public RequirementType getType() { + return RequirementType.SEMANTIC; } @Override @@ -149,7 +157,7 @@ public class IsWallCheck extends Check { @Override public CheckId getCheckId() { - return CheckId.C_SEM_BS_IS_WALL; + return CheckId.C_SE_BS_IS_WALL; } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/RoofSurfaceUnfragmentedCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/RoofSurfaceUnfragmentedCheck.java index 692ed2e..e2e535f 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/RoofSurfaceUnfragmentedCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/RoofSurfaceUnfragmentedCheck.java @@ -22,13 +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.RequirementType; import de.hft.stuttgart.citydoctor2.check.DefaultParameter; +import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.Unit; +import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.checks.util.UnfragmentedCheck; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; @@ -91,10 +94,15 @@ public class RoofSurfaceUnfragmentedCheck extends Check { public List getDefaultParameter() { return defaultParameters; } + + @Override + public Set appliesToRequirements() { + return CollectionUtils.singletonSet(Requirement.R_SE_BS_ROOF_UNFRAGMENTED); + } @Override - public CheckType getType() { - return CheckType.SEMANTIC; + public RequirementType getType() { + return RequirementType.SEMANTIC; } @Override @@ -104,7 +112,7 @@ public class RoofSurfaceUnfragmentedCheck extends Check { @Override public CheckId getCheckId() { - return CheckId.C_SEM_BS_ROOF_NOT_FRAGMENTED; + return CheckId.C_SE_BS_ROOF_UNFRAGMENTED; } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/CollectionUtils.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/CollectionUtils.java new file mode 100644 index 0000000..cdbe466 --- /dev/null +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/CollectionUtils.java @@ -0,0 +1,43 @@ +/*- + * 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 . + */ +package de.hft.stuttgart.citydoctor2.checks.util; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class CollectionUtils { + + private CollectionUtils() { + } + + public static Set singletonSet(T v) { + HashSet set = new HashSet<>(2); + set.add(v); + return set; + } + + @SafeVarargs + public static Set asSet(T... values) { + HashSet set = new HashSet<>((int) (values.length * 1.25)); + set.addAll(Arrays.asList(values)); + return set; + } + +} diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/XmlStreamReporter.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/XmlStreamReporter.java index 1deaad7..82fd142 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/XmlStreamReporter.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/XmlStreamReporter.java @@ -32,10 +32,9 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import de.hft.stuttgart.citydoctor2.check.CheckConfiguration; import de.hft.stuttgart.citydoctor2.check.CheckError; -import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.ErrorType; +import de.hft.stuttgart.citydoctor2.check.RequirementConfiguration; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.checkresult.CheckReport; import de.hft.stuttgart.citydoctor2.checkresult.Environment; @@ -115,10 +114,10 @@ public class XmlStreamReporter implements StreamReporter { plan.setNumberOfRoundingPlaces(config.getNumberOfRoundingPlaces()); List checkConfigs = new ArrayList<>(); plan.setChecks(checkConfigs); - for (Entry e : config.getChecks().entrySet()) { + for (Entry e : config.getRequirements().entrySet()) { PlanCheck checkConfig = new PlanCheck(); checkConfigs.add(checkConfig); - checkConfig.setName(e.getKey().toString()); + checkConfig.setName(e.getKey()); if (e.getValue().isEnabled()) { checkConfig.setActivated(true); Map checkParams = e.getValue().getParameters(); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfStreamReporter.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfStreamReporter.java index bfd739e..ebde6ff 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfStreamReporter.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfStreamReporter.java @@ -32,7 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import de.hft.stuttgart.citydoctor2.check.CheckConfiguration; +import de.hft.stuttgart.citydoctor2.check.RequirementConfiguration; import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.CheckResult; @@ -133,8 +133,8 @@ public class PdfStreamReporter implements StreamReporter { if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) { vp.addTextElement("Schematron file: " + config.getSchematronFilePath()); } - for (Entry e : config.getChecks().entrySet()) { - String text = e.getKey().toString(); + for (Entry e : config.getRequirements().entrySet()) { + String text = e.getKey(); String color; if (e.getValue().isEnabled()) { text = text + " = enabled"; diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java index 3d2b9d6..9191678 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java @@ -51,8 +51,8 @@ public class CheckerTest { @Test public void testSchematron() throws CityGmlParseException, InvalidGmlFileException { ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); - config.getChecks().get(CheckId.C_SEM_BS_ROOF_NOT_FRAGMENTED).setEnabled(false); - config.setSchematronFilePath("src/test/resources/schematronTest.xml"); + config.getRequirements().get(Requirement.R_SE_BS_ROOF_UNFRAGMENTED.toString()).setEnabled(false); + config.setSchematronFilePathInGlobalParameters("src/test/resources/schematronTest.xml"); CityDoctorModel model = CityGmlParser.parseCityGmlFile( "src/test/resources/SimpleSolid_SrefBS_SchematronTest.gml", config.getParserConfiguration()); Checker checker = new Checker(config, model); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/ValidationConfigurationTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/ValidationConfigurationTest.java index 45bfce2..c3f420e 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/ValidationConfigurationTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/ValidationConfigurationTest.java @@ -25,6 +25,7 @@ import java.io.FileNotFoundException; import org.junit.Test; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; +import de.hft.stuttgart.quality.model.jaxb.RequirementId; public class ValidationConfigurationTest { @@ -33,33 +34,33 @@ public class ValidationConfigurationTest { String file = "src/test/resources/testConfig.yml"; ValidationConfiguration config = ValidationConfiguration.loadValidationConfig(file); assertEquals(8, config.getNumberOfRoundingPlaces()); - assertTrue(config.getChecks().containsKey(CheckId.C_GE_R_TOO_FEW_POINTS)); - assertTrue(config.getChecks().get(CheckId.C_GE_P_NON_PLANAR).isEnabled()); + assertTrue(config.getRequirements().containsKey(RequirementId.R_GE_R_TOO_FEW_POINTS.toString())); + assertFalse(config.getRequirements().get(RequirementId.R_GE_P_NON_PLANAR.toString()).isEnabled()); assertNull(config.getFilter()); } - + @Test public void testLoadingConfigWithFilter() throws FileNotFoundException { String file = "src/test/resources/testConfigWithFilter.yml"; ValidationConfiguration config = ValidationConfiguration.loadValidationConfig(file); assertEquals(8, config.getNumberOfRoundingPlaces()); - assertTrue(config.getChecks().containsKey(CheckId.C_GE_R_TOO_FEW_POINTS)); - assertTrue(config.getChecks().get(CheckId.C_GE_P_NON_PLANAR).isEnabled()); - + assertTrue(config.getRequirements().containsKey(RequirementId.R_GE_R_TOO_FEW_POINTS.toString())); + assertFalse(config.getRequirements().get(RequirementId.R_GE_P_NON_PLANAR.toString()).isEnabled()); + FilterConfiguration filterConfig = config.getFilter(); assertNotNull(filterConfig); - + ExcludeFilterConfiguration excludeFilter = filterConfig.getExclude(); assertNotNull(excludeFilter); - + assertTrue(excludeFilter.getIds().contains("UUID-8972-kghf-asgv")); assertTrue(excludeFilter.getIds().contains("UUID.*")); assertTrue(excludeFilter.getTypes().contains(FeatureType.BUILDING)); assertTrue(excludeFilter.getTypes().contains(FeatureType.VEGETATION)); - + IncludeFilterConfiguration includeFilter = filterConfig.getInclude(); assertNotNull(includeFilter); - + assertTrue(includeFilter.getIds().contains(".*")); assertTrue(includeFilter.getTypes().contains(FeatureType.BUILDING)); assertTrue(includeFilter.getTypes().contains(FeatureType.VEGETATION)); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/CheckContainerTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/CheckContainerTest.java index be6ff0a..6a08997 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/CheckContainerTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/CheckContainerTest.java @@ -20,13 +20,16 @@ package de.hft.stuttgart.citydoctor2.checks; import static org.junit.Assert.assertEquals; +import java.util.Set; + import org.junit.Test; 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.RequirementType; import de.hft.stuttgart.citydoctor2.check.ErrorId; +import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; import de.hft.stuttgart.citydoctor2.datastructure.Building; @@ -43,7 +46,7 @@ public class CheckContainerTest { } @Override - public CheckType getType() { + public RequirementType getType() { return null; } @@ -56,6 +59,11 @@ public class CheckContainerTest { public CheckId getCheckId() { return CheckId.C_GE_P_HOLE_OUTSIDE; } + + @Override + public Set appliesToRequirements() { + return null; + } }; CheckContainer cc = new CheckContainer(c); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java index d15aed0..a2fcbe4 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java @@ -18,7 +18,7 @@ */ package de.hft.stuttgart.citydoctor2.checks.geometry; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.io.File; import java.util.ArrayList; @@ -29,9 +29,7 @@ import java.util.Map; import org.junit.Test; import de.hft.stuttgart.citydoctor2.check.CheckError; -import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.Checker; -import de.hft.stuttgart.citydoctor2.check.ErrorId; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; @@ -43,10 +41,10 @@ import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType; import de.hft.stuttgart.citydoctor2.datastructure.Lod; import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.quality.model.jaxb.RequirementId; public class DegeneratedPolygonCheckTest { - - + @Test public void testDegeneratedPolygon() { Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD1); @@ -65,38 +63,25 @@ public class DegeneratedPolygonCheckTest { lr.getVertices().add(v4); lr.getVertices().add(v1); geom.updateEdgesAndVertices(); - + Building b = new Building(); b.addGeometry(geom); - + ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = new CityDoctorModel(config, new File("")); model.addBuilding(b); - // model -// Edge [from=Vertex [x=427583.301, y=6003502.571, z=9.711], to=Vertex [x=427583.304, y=6003502.574, z=9.713]], -// Edge [from=Vertex [x=427583.304, y=6003502.574, z=9.713], to=Vertex [x=427583.304, y=6003502.574, z=4.097]], -// Edge [from=Vertex [x=427583.304, y=6003502.574, z=4.097], to=Vertex [x=427583.301, y=6003502.571, z=4.097]], -// Edge [from=Vertex [x=427583.301, y=6003502.571, z=4.097], to=Vertex [x=427583.301, y=6003502.571, z=9.711]]] - // test -// Edge [from=Vertex [x=427583.301, y=6003502.571, z=9.711], to=Vertex [x=427583.304, y=6003502.574, z=9.713]], -// Edge [from=Vertex [x=427583.304, y=6003502.574, z=9.713], to=Vertex [x=427583.304, y=6003502.574, z=4.097]], -// Edge [from=Vertex [x=427583.304, y=6003502.574, z=4.097], to=Vertex [x=427583.301, y=6003502.571, z=4.097]], -// Edge [from=Vertex [x=427583.301, y=6003502.571, z=4.097], to=Vertex [x=427583.301, y=6003502.571, z=9.711]]] - -// Segment3d [pointA=Vertex [x=427583.301, y=6003502.571, z=9.711], pointB=Vertex [x=427583.304, y=6003502.574, z=9.713]] -// Vertex [x=427583.301, y=6003502.571, z=9.711] - ValidationConfiguration valConfig = ValidationConfiguration.loadStandardValidationConfig(); + valConfig.setMinVertexDistanceInGlobalParameters(0.004); Map parameters = new HashMap<>(); - parameters.put("degeneratedPolygonTolerance", "0.0001"); - valConfig.getChecks().get(CheckId.C_GE_P_NON_PLANAR).setParameters(parameters); + parameters.put("degeneratedPolygonTolerance", "0.004"); + valConfig.getRequirements().get(RequirementId.R_GE_P_NON_PLANAR.toString()).getParameters().putAll(parameters); Checker c = new Checker(valConfig, model); c.runChecks(); - + List errors = new ArrayList<>(); b.collectContainedErrors(errors); - CheckError checkError = errors.get(0); - assertEquals(ErrorId.GE_P_DEGENERATED_POLYGON, checkError.getErrorId()); + assertTrue(errors.isEmpty()); } + } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/FaceOutCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/FaceOutCheckTest.java index 96ea542..68ecbbc 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/FaceOutCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/FaceOutCheckTest.java @@ -107,7 +107,7 @@ public class FaceOutCheckTest { public void testGoodGeometry() { Geometry geom = createGoodGeometry(); - FaceOutCheck foc = new FaceOutCheck(); + AllPolygonsWrongOrientationCheck foc = new AllPolygonsWrongOrientationCheck(); foc.check(geom); Assert.assertEquals(ResultStatus.OK, geom.getCheckResult(foc).getResultStatus()); @@ -117,7 +117,7 @@ public class FaceOutCheckTest { public void testBadGeometry() { Geometry geom = createBadGeometry(); - FaceOutCheck foc = new FaceOutCheck(); + AllPolygonsWrongOrientationCheck foc = new AllPolygonsWrongOrientationCheck(); foc.check(geom); Assert.assertEquals(ResultStatus.ERROR, geom.getCheckResult(foc).getResultStatus()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/NumPointsCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/NumPointsCheckTest.java index b7a0b9b..a9ab3bb 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/NumPointsCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/NumPointsCheckTest.java @@ -53,7 +53,7 @@ public class NumPointsCheckTest { lr.addVertex(v2); lr.addVertex(v0); - NumPointsCheck check = new NumPointsCheck(); + TooFewPointsCheck check = new TooFewPointsCheck(); ParserConfiguration config = new ParserConfiguration(8, false); check.init(Collections.emptyMap(), config); check.check(lr); @@ -79,7 +79,7 @@ public class NumPointsCheckTest { lr.addVertex(v3); lr.addVertex(v0); - NumPointsCheck check = new NumPointsCheck(); + TooFewPointsCheck check = new TooFewPointsCheck(); ParserConfiguration config = new ParserConfiguration(8, false); check.init(Collections.emptyMap(), config); check.check(lr); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldEdgeSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldEdgeSystemTest.java index f5a5885..a704453 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldEdgeSystemTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldEdgeSystemTest.java @@ -34,6 +34,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import de.hft.stuttgart.quality.model.jaxb.RequirementId; /** * @@ -52,10 +53,10 @@ public class NonManifoldEdgeSystemTest { @Test public void testNonManifoldEdge2() throws CityGmlParseException, IOException, InvalidGmlFileException { - Map> paramMap = new HashMap<>(); + Map> paramMap = new HashMap<>(); Map parameter = new HashMap<>(); parameter.put("distanceTolerance", "0.1"); - paramMap.put(CheckId.C_GE_P_NON_PLANAR, parameter); + paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0004-T0001.gml", paramMap); Geometry g = c.getBuildings().get(0).getGeometries().get(0); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java index 2419973..0378718 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java @@ -37,6 +37,7 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.quality.model.jaxb.RequirementId; /** * @@ -113,10 +114,10 @@ public class PlanarTest { @Test public void testPlanarPolygon4() throws CityGmlParseException, IOException, InvalidGmlFileException { - Map> paramMap = new HashMap<>(); + Map> paramMap = new HashMap<>(); Map parameter = new HashMap<>(); parameter.put("distanceTolerance", "0.01"); - paramMap.put(CheckId.C_GE_P_NON_PLANAR, parameter); + paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0002-T0001.gml", paramMap); Polygon p = TestUtil.getPolygonById("_Simple_BD.1_PG.2", c); CheckResult cr = p.getCheckResult(CheckId.C_GE_P_NON_PLANAR); @@ -126,11 +127,11 @@ public class PlanarTest { @Test public void testPlanarPolygon5() throws CityGmlParseException, IOException, InvalidGmlFileException { - Map> paramMap = new HashMap<>(); + Map> paramMap = new HashMap<>(); Map parameter = new HashMap<>(); parameter.put("type", "distance"); parameter.put("distanceTolerance", "0.5"); - paramMap.put(CheckId.C_GE_P_NON_PLANAR, parameter); + paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0002-T0001.gml", paramMap); Polygon p = TestUtil.getPolygonById("_Simple_BD.1_PG.2", c); CheckResult cr = p.getCheckResult(CheckId.C_GE_P_NON_PLANAR); @@ -139,10 +140,10 @@ public class PlanarTest { @Test public void testPlanarPolygon6() throws CityGmlParseException, IOException, InvalidGmlFileException { - Map> paramMap = new HashMap<>(); + Map> paramMap = new HashMap<>(); Map parameter = new HashMap<>(); parameter.put("type", "both"); - paramMap.put(CheckId.C_GE_P_NON_PLANAR, parameter); + paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0002-T0002.gml", paramMap); Polygon p = TestUtil.getPolygonById("_Simple_BD.1_PG.1", c); CheckResult cr = p.getCheckResult(CheckId.C_GE_P_NON_PLANAR); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TestUtil.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TestUtil.java index 30d0cc6..da96f4f 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TestUtil.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TestUtil.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; @@ -52,7 +51,7 @@ public class TestUtil { public static CityDoctorModel loadAndCheckCityModel(String path, int numberOfRoundingPlaces) throws CityGmlParseException, IOException, InvalidGmlFileException { ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); - config.setNumberOfRoundingPlaces(numberOfRoundingPlaces); + config.setNumberOfRoundingPlacesInGlobalParameters(numberOfRoundingPlaces); CityDoctorModel m = CityGmlParser.parseCityGmlFile(path, config.getParserConfiguration()); Checker c = new Checker(config, m); c.runChecks(); @@ -63,8 +62,8 @@ public class TestUtil { public static CityDoctorModel loadAndCheckCityModel(String path, int numberOfRoundingPlaces, double minVertexDistance) throws CityGmlParseException, IOException, InvalidGmlFileException { ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); - config.setNumberOfRoundingPlaces(numberOfRoundingPlaces); - config.setMinVertexDistance(minVertexDistance); + config.setNumberOfRoundingPlacesInGlobalParameters(numberOfRoundingPlaces); + config.setMinVertexDistanceInGlobalParameters(minVertexDistance); CityDoctorModel m = CityGmlParser.parseCityGmlFile(path, config.getParserConfiguration()); Checker c = new Checker(config, m); c.runChecks(); @@ -72,11 +71,11 @@ public class TestUtil { } - public static CityDoctorModel loadAndCheckCityModel(String path, Map> paramMap) + public static CityDoctorModel loadAndCheckCityModel(String path, Map> paramMap) throws CityGmlParseException, IOException, InvalidGmlFileException { ValidationConfiguration valConfig = ValidationConfiguration.loadStandardValidationConfig(); - for (Entry> e : paramMap.entrySet()) { - valConfig.getChecks().get(e.getKey()).setParameters(e.getValue()); + for (Entry> e : paramMap.entrySet()) { + valConfig.getRequirements().get(e.getKey()).getParameters().putAll(e.getValue()); } CityDoctorModel m = CityGmlParser.parseCityGmlFile(path, valConfig.getParserConfiguration()); Checker c = new Checker(valConfig, m); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfig.yml b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfig.yml index 0c64010..a761170 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfig.yml +++ b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfig.yml @@ -1,45 +1,53 @@ -numberOfRoundingPlaces: 8 +globalParameters: + numberOfRoundingPlaces: 8 + # in m + minVertexDistance: 0.0001 + schematronFilePath: '' useStreaming: false xmlValidation: false -checks: - C_GE_R_TOO_FEW_POINTS: +requirements: + R_GE_R_TOO_FEW_POINTS: enabled: true - C_GE_R_NOT_CLOSED: + R_GE_R_NOT_CLOSED: enabled: true - C_GE_R_DUPLICATE_POINT: + R_GE_R_CONSECUTIVE_POINTS_SAME: enabled: true - C_GE_R_SELF_INTERSECTION: + R_GE_R_SELF_INTERSECTION: enabled: true - C_GE_S_MULTIPLE_CONNECTED_COMPONENTS: + R_GE_S_MULTIPLE_CONNECTED_COMPONENTS: enabled: true - C_GE_P_INTERIOR_DISCONNECTED: + R_GE_P_INTERIOR_DISCONNECTED: enabled: true - C_GE_P_INTERSECTING_RINGS: + R_GE_P_INTERSECTING_RINGS: enabled: true - C_GE_P_NON_PLANAR: + R_GE_P_NON_PLANAR: enabled: false parameters: # one of ("distance", "angle", "both") type: distance + # in m distanceTolerance: 0.01 - angleTolerance: 0.1 - C_GE_P_HOLE_OUTSIDE: + # in degree + angleTolerance: 1 + # in m + degeneratedPolygonTolerance: 0 + R_GE_P_HOLE_OUTSIDE: enabled: true - C_GE_P_ORIENTATION_RINGS_SAME: + R_GE_P_ORIENTATION_RINGS_SAME: enabled: true - C_GE_P_INNER_RINGS_NESTED: + R_GE_P_INNER_RINGS_NESTED: enabled: true - C_GE_S_TOO_FEW_POLYGONS: + R_GE_S_TOO_FEW_POLYGONS: enabled: true - C_GE_S_NOT_CLOSED: + R_GE_S_NOT_CLOSED: enabled: true - C_GE_S_NON_MANIFOLD_EDGE: + R_GE_S_NON_MANIFOLD_EDGE: enabled: true - C_GE_S_POLYGON_WRONG_ORIENTATION: + R_GE_S_POLYGON_WRONG_ORIENTATION: enabled: true - C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: + R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: enabled: true - C_GE_S_NON_MANIFOLD_VERTEX: + R_GE_S_NON_MANIFOLD_VERTEX: enabled: true - C_GE_S_SELF_INTERSECTION: + R_GE_S_SELF_INTERSECTION: enabled: true \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithExclude.yml b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithExclude.yml index c279b3e..3e052a7 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithExclude.yml +++ b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithExclude.yml @@ -1,4 +1,6 @@ -numberOfRoundingPlaces: 8 +globalParameters: + numberOfRoundingPlaces: 8 + minVertexDistance: 0.0001 filter: exclude: # available types: BUILDING, VEGETATION, TRANSPORTATION, BRIDGE, LAND, WATER @@ -6,45 +8,47 @@ filter: - BUILDING # exlude matching ids (Regex) ids: -checks: - C_GE_R_TOO_FEW_POINTS: +requirements: + R_GE_R_TOO_FEW_POINTS: enabled: true - C_GE_R_NOT_CLOSED: + R_GE_R_NOT_CLOSED: enabled: true - C_GE_R_DUPLICATE_POINT: + R_GE_R_CONSECUTIVE_POINTS_SAME: enabled: true - C_GE_R_SELF_INTERSECTION: + R_GE_R_SELF_INTERSECTION: enabled: true - C_GE_S_MULTIPLE_CONNECTED_COMPONENTS: + R_GE_S_MULTIPLE_CONNECTED_COMPONENTS: enabled: true - C_GE_P_INTERIOR_DISCONNECTED: + R_GE_P_INTERIOR_DISCONNECTED: enabled: true - C_GE_P_INTERSECTING_RINGS: + R_GE_P_INTERSECTING_RINGS: enabled: true - C_GE_P_NON_PLANAR: + R_GE_P_NON_PLANAR: enabled: false parameters: # one of ("distance", "angle", "both") type: distance + # in m distanceTolerance: 0.01 - angleTolerance: 0.1 - C_GE_P_HOLE_OUTSIDE: + # in degree + angleTolerance: 1 + R_GE_P_HOLE_OUTSIDE: enabled: true - C_GE_P_ORIENTATION_RINGS_SAME: + R_GE_P_ORIENTATION_RINGS_SAME: enabled: true - C_GE_P_INNER_RINGS_NESTED: + R_GE_P_INNER_RINGS_NESTED: enabled: true - C_GE_S_TOO_FEW_POLYGONS: + R_GE_S_TOO_FEW_POLYGONS: enabled: true - C_GE_S_NOT_CLOSED: + R_GE_S_NOT_CLOSED: enabled: true - C_GE_S_NON_MANIFOLD_EDGE: + R_GE_S_NON_MANIFOLD_EDGE: enabled: true - C_GE_S_POLYGON_WRONG_ORIENTATION: + R_GE_S_POLYGON_WRONG_ORIENTATION: enabled: true - C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: + R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: enabled: true - C_GE_S_NON_MANIFOLD_VERTEX: + R_GE_S_NON_MANIFOLD_VERTEX: enabled: true - C_GE_S_SELF_INTERSECTION: + R_GE_S_SELF_INTERSECTION: enabled: true \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithFilter.yml b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithFilter.yml index afd1032..0b082f4 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithFilter.yml +++ b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithFilter.yml @@ -1,4 +1,7 @@ -numberOfRoundingPlaces: 8 +globalParameters: + numberOfRoundingPlaces: 8 + # in m + minVertexDistance: 0.0001 filter: include: # available types: BUILDING, VEGETATION, TRANSPORTATION, BRIDGE, LAND, WATER @@ -27,45 +30,47 @@ filter: - UUID-8972-kghf-asgv - UUID-567-asdf-GEGH - UUID.* -checks: - C_GE_R_TOO_FEW_POINTS: +requirements: + R_GE_R_TOO_FEW_POINTS: enabled: true - C_GE_R_NOT_CLOSED: + R_GE_R_NOT_CLOSED: enabled: true - C_GE_R_DUPLICATE_POINT: + R_GE_R_CONSECUTIVE_POINTS_SAME: enabled: true - C_GE_R_SELF_INTERSECTION: + R_GE_R_SELF_INTERSECTION: enabled: true - C_GE_S_MULTIPLE_CONNECTED_COMPONENTS: + R_GE_S_MULTIPLE_CONNECTED_COMPONENTS: enabled: true - C_GE_P_INTERIOR_DISCONNECTED: + R_GE_P_INTERIOR_DISCONNECTED: enabled: true - C_GE_P_INTERSECTING_RINGS: + R_GE_P_INTERSECTING_RINGS: enabled: true - C_GE_P_NON_PLANAR: + R_GE_P_NON_PLANAR: enabled: false parameters: # one of ("distance", "angle", "both") type: distance + # in m distanceTolerance: 0.01 - angleTolerance: 0.1 - C_GE_P_HOLE_OUTSIDE: + # in degree + angleTolerance: 1 + R_GE_P_HOLE_OUTSIDE: enabled: true - C_GE_P_ORIENTATION_RINGS_SAME: + R_GE_P_ORIENTATION_RINGS_SAME: enabled: true - C_GE_P_INNER_RINGS_NESTED: + R_GE_P_INNER_RINGS_NESTED: enabled: true - C_GE_S_TOO_FEW_POLYGONS: + R_GE_S_TOO_FEW_POLYGONS: enabled: true - C_GE_S_NOT_CLOSED: + R_GE_S_NOT_CLOSED: enabled: true - C_GE_S_NON_MANIFOLD_EDGE: + R_GE_S_NON_MANIFOLD_EDGE: enabled: true - C_GE_S_POLYGON_WRONG_ORIENTATION: + R_GE_S_POLYGON_WRONG_ORIENTATION: enabled: true - C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: + R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: enabled: true - C_GE_S_NON_MANIFOLD_VERTEX: + R_GE_S_NON_MANIFOLD_VERTEX: enabled: true - C_GE_S_SELF_INTERSECTION: + R_GE_S_SELF_INTERSECTION: enabled: true \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithInclude.yml b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithInclude.yml index 2f8c5af..6f49da4 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithInclude.yml +++ b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithInclude.yml @@ -1,48 +1,49 @@ -numberOfRoundingPlaces: 8 +globalParameters: + numberOfRoundingPlaces: 8 filter: include: # available types: BUILDING, VEGETATION, TRANSPORTATION, BRIDGE, LAND, WATER types: - TRANSPORTATION -checks: - C_GE_R_TOO_FEW_POINTS: +requirements: + R_GE_R_TOO_FEW_POINTS: enabled: true - C_GE_R_NOT_CLOSED: + R_GE_R_NOT_CLOSED: enabled: true - C_GE_R_DUPLICATE_POINT: + R_GE_R_CONSECUTIVE_POINTS_SAME: enabled: true - C_GE_R_SELF_INTERSECTION: + R_GE_R_SELF_INTERSECTION: enabled: true - C_GE_S_MULTIPLE_CONNECTED_COMPONENTS: + R_GE_S_MULTIPLE_CONNECTED_COMPONENTS: enabled: true - C_GE_P_INTERIOR_DISCONNECTED: + R_GE_P_INTERIOR_DISCONNECTED: enabled: true - C_GE_P_INTERSECTING_RINGS: + R_GE_P_INTERSECTING_RINGS: enabled: true - C_GE_P_NON_PLANAR: + R_GE_P_NON_PLANAR: enabled: false parameters: # one of ("distance", "angle", "both") type: distance distanceTolerance: 0.01 angleTolerance: 0.1 - C_GE_P_HOLE_OUTSIDE: + R_GE_P_HOLE_OUTSIDE: enabled: true - C_GE_P_ORIENTATION_RINGS_SAME: + R_GE_P_ORIENTATION_RINGS_SAME: enabled: true - C_GE_P_INNER_RINGS_NESTED: + R_GE_P_INNER_RINGS_NESTED: enabled: true - C_GE_S_TOO_FEW_POLYGONS: + R_GE_S_TOO_FEW_POLYGONS: enabled: true - C_GE_S_NOT_CLOSED: + R_GE_S_NOT_CLOSED: enabled: true - C_GE_S_NON_MANIFOLD_EDGE: + R_GE_S_NON_MANIFOLD_EDGE: enabled: true - C_GE_S_POLYGON_WRONG_ORIENTATION: + R_GE_S_POLYGON_WRONG_ORIENTATION: enabled: true - C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: + R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: enabled: true - C_GE_S_NON_MANIFOLD_VERTEX: + R_GE_S_NON_MANIFOLD_VERTEX: enabled: true - C_GE_S_SELF_INTERSECTION: + R_GE_S_SELF_INTERSECTION: enabled: true \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithStreaming.yml b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithStreaming.yml index ea10094..311740e 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithStreaming.yml +++ b/CityDoctorParent/CityDoctorValidation/src/test/resources/testConfigWithStreaming.yml @@ -1,45 +1,46 @@ -numberOfRoundingPlaces: 8 +globalParameters: + numberOfRoundingPlaces: 8 + schematronFilePath: src/test/resources/schematronTest.xml useStreaming: true -schematronFilePath: src/test/resources/schematronTest.xml -checks: - C_GE_R_TOO_FEW_POINTS: +requirements: + R_GE_R_TOO_FEW_POINTS: enabled: true - C_GE_R_NOT_CLOSED: + R_GE_R_NOT_CLOSED: enabled: true - C_GE_R_DUPLICATE_POINT: + R_GE_R_CONSECUTIVE_POINTS_SAME: enabled: true - C_GE_R_SELF_INTERSECTION: + R_GE_R_SELF_INTERSECTION: enabled: true - C_GE_S_MULTIPLE_CONNECTED_COMPONENTS: + R_GE_S_MULTIPLE_CONNECTED_COMPONENTS: enabled: true - C_GE_P_INTERIOR_DISCONNECTED: + R_GE_P_INTERIOR_DISCONNECTED: enabled: true - C_GE_P_INTERSECTING_RINGS: + R_GE_P_INTERSECTING_RINGS: enabled: true - C_GE_P_NON_PLANAR: + R_GE_P_NON_PLANAR: enabled: false parameters: # one of ("distance", "angle", "both") type: distance distanceTolerance: 0.01 angleTolerance: 0.1 - C_GE_P_HOLE_OUTSIDE: + R_GE_P_HOLE_OUTSIDE: enabled: true - C_GE_P_ORIENTATION_RINGS_SAME: + R_GE_P_ORIENTATION_RINGS_SAME: enabled: true - C_GE_P_INNER_RINGS_NESTED: + R_GE_P_INNER_RINGS_NESTED: enabled: true - C_GE_S_TOO_FEW_POLYGONS: + R_GE_S_TOO_FEW_POLYGONS: enabled: true - C_GE_S_NOT_CLOSED: + R_GE_S_NOT_CLOSED: enabled: true - C_GE_S_NON_MANIFOLD_EDGE: + R_GE_S_NON_MANIFOLD_EDGE: enabled: true - C_GE_S_POLYGON_WRONG_ORIENTATION: + R_GE_S_POLYGON_WRONG_ORIENTATION: enabled: true - C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: + R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: enabled: true - C_GE_S_NON_MANIFOLD_VERTEX: + R_GE_S_NON_MANIFOLD_VERTEX: enabled: true - C_GE_S_SELF_INTERSECTION: + R_GE_S_SELF_INTERSECTION: enabled: true \ No newline at end of file -- GitLab From cd494d75fda0b64732a3f683828c173577ce5076 Mon Sep 17 00:00:00 2001 From: Matthias Betz Date: Fri, 12 Mar 2021 11:24:16 +0100 Subject: [PATCH 5/5] removing unused interface methods --- .../stuttgart/citydoctor2/check/Check.java | 12 +------- .../citydoctor2/check/Requirement.java | 5 ++++ .../citydoctor2/checks/CheckContainer.java | 8 +----- .../citydoctor2/checks/CheckPrototype.java | 7 +---- .../checks/geometry/NullAreaCheck.java | 14 +--------- .../checks/geometry/PlanarCheck.java | 28 ++++--------------- .../checks/semantics/IsWallCheck.java | 14 +--------- .../RoofSurfaceUnfragmentedCheck.java | 14 +--------- 8 files changed, 17 insertions(+), 85 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java index 20db528..85cd9cf 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java @@ -310,7 +310,7 @@ public abstract class Check { * * @param params the parameter map containing the parameters for the check in * String form. The key should be the same String provided by the - * {@link Check#getDefaultParameter()} method + * {@link Requirement#getDefaultParameter()} method * @param config sometimes there are global parameters which can be used by * checks. Those are be stored in this container */ @@ -318,16 +318,6 @@ public abstract class Check { } - /** - * This methods gives checks the possibility to declare default parameters, used - * primarily in the GUI. - * - * @return a list of default parameters, not null - */ - public List getDefaultParameter() { - return Collections.emptyList(); - } - @Override public String toString() { return "Check [id=" + getCheckId() + "]"; diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Requirement.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Requirement.java index 2368be5..e78c29c 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Requirement.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Requirement.java @@ -33,6 +33,7 @@ public class Requirement implements Serializable { private static final String DEGENERATED_POLYGON_TOLERANCE = "degeneratedPolygonTolerance"; private static final String LOWER_ANGLE_NAME = "lowerAngle"; private static final String UPPER_ANGLE_NAME = "upperAngle"; + private static final String MAX_ANGLE_DEVIATION = "maxAngleDeviation"; public static final Requirement R_GE_R_TOO_FEW_POINTS = new Requirement("R_GE_R_TOO_FEW_POINTS", RequirementType.GEOMETRY); public static final Requirement R_GE_R_NOT_CLOSED = new Requirement("R_GE_R_NOT_CLOSED", RequirementType.GEOMETRY); @@ -79,6 +80,10 @@ public class Requirement implements Serializable { defaultParameters.add(new DefaultParameter(LOWER_ANGLE_NAME, "45", Unit.DEGREE)); defaultParameters.add(new DefaultParameter(UPPER_ANGLE_NAME, "135", Unit.DEGREE)); R_SE_BS_IS_WALL.parameters = Collections.unmodifiableList(defaultParameters); + + defaultParameters.add(new DefaultParameter(MAX_ANGLE_DEVIATION, "1", Unit.DEGREE)); + R_SE_BS_ROOF_UNFRAGMENTED.parameters = Collections.unmodifiableList(defaultParameters); + } private String id; diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java index eb9ee05..8040f6b 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java @@ -29,10 +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.RequirementType; 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; @@ -105,11 +104,6 @@ public class CheckContainer extends Check { c.addCheckResult(cr); } - @Override - public List getDefaultParameter() { - return check.getDefaultParameter(); - } - @Override public List getDependencies() { return check.getDependencies(); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckPrototype.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckPrototype.java index 975e959..4529905 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckPrototype.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckPrototype.java @@ -23,9 +23,8 @@ import java.util.Set; import de.hft.stuttgart.citydoctor2.check.Check; import de.hft.stuttgart.citydoctor2.check.CheckId; -import de.hft.stuttgart.citydoctor2.check.RequirementType; -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 @@ -60,10 +59,6 @@ public class CheckPrototype { return c.getDependencies(); } - public List getDefaultParameter() { - return c.getDefaultParameter(); - } - public RequirementType getType() { return c.getType(); } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NullAreaCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NullAreaCheck.java index 3b5a6fb..fdba67d 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NullAreaCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/NullAreaCheck.java @@ -27,11 +27,9 @@ 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.RequirementType; -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; @@ -48,7 +46,6 @@ public class NullAreaCheck extends Check { private static final String DELTA_NAME = "delta"; private static final List dependencies; - private static final List defaultParameters; static { ArrayList deps = new ArrayList<>(); @@ -56,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 defParameters = new ArrayList<>(); - defParameters.add(new DefaultParameter(DELTA_NAME, "0.0001", Unit.SQUARE_METER)); - defaultParameters = Collections.unmodifiableList(defParameters); } private double delta = 0.0001; @@ -71,11 +64,6 @@ public class NullAreaCheck extends Check { } } - @Override - public List getDefaultParameter() { - return defaultParameters; - } - @Override public void check(LinearRing lr) { boolean areaIsNull = checkWithDistanceToRegressionLine(lr, delta); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java index 46b42ff..f0b1d3e 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java @@ -29,11 +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.RequirementType; -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; @@ -61,14 +59,13 @@ import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon; */ public class PlanarCheck extends Check { - public static final String DISTANCE = "distance"; - public static final String DISTANCE_TOLERANCE = "distanceTolerance"; - public static final String ANGLE_TOLERANCE = "angleTolerance"; - public static final String TYPE = "type"; - public static final String DEGENERATED_POLYGON_TOLERANCE = "degeneratedPolygonTolerance"; + private static final String DISTANCE = "distance"; + private static final String DISTANCE_TOLERANCE = "distanceTolerance"; + private static final String ANGLE_TOLERANCE = "angleTolerance"; + private static final String TYPE = "type"; + private static final String DEGENERATED_POLYGON_TOLERANCE = "degeneratedPolygonTolerance"; private static final List dependencies; - private static final List defaultParameters; static { ArrayList deps = new ArrayList<>(4); @@ -77,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 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; @@ -111,11 +100,6 @@ public class PlanarCheck extends Check { } } - @Override - public List getDefaultParameter() { - return defaultParameters; - } - @Override public void check(Polygon p) { if (DISTANCE.equals(planarCheckType)) { diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsWallCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsWallCheck.java index 4cfb380..dfcfedd 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsWallCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/IsWallCheck.java @@ -27,11 +27,9 @@ 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.RequirementType; -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.NotWallError; import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; @@ -55,7 +53,6 @@ public class IsWallCheck extends Check { private static final String UPPER_ANGLE_NAME = "upperAngle"; private static final List dependencies; - private static final List defaultParameters; static { ArrayList deps = new ArrayList<>(); @@ -66,10 +63,6 @@ public class IsWallCheck extends Check { deps.add(CheckId.C_GE_P_NON_PLANAR); dependencies = Collections.unmodifiableList(deps); - ArrayList defParameters = new ArrayList<>(3); - defParameters.add(new DefaultParameter(LOWER_ANGLE_NAME, "45", Unit.DEGREE)); - defParameters.add(new DefaultParameter(UPPER_ANGLE_NAME, "135", Unit.DEGREE)); - defaultParameters = Collections.unmodifiableList(defParameters); } private static final Vector3d Z_AXIS = new Vector3d(0, 0, 1); @@ -140,11 +133,6 @@ public class IsWallCheck extends Check { return CollectionUtils.singletonSet(Requirement.R_SE_BS_IS_WALL); } - @Override - public List getDefaultParameter() { - return defaultParameters; - } - @Override public RequirementType getType() { return RequirementType.SEMANTIC; diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/RoofSurfaceUnfragmentedCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/RoofSurfaceUnfragmentedCheck.java index e2e535f..4996b70 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/RoofSurfaceUnfragmentedCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/RoofSurfaceUnfragmentedCheck.java @@ -27,10 +27,8 @@ 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.RequirementType; -import de.hft.stuttgart.citydoctor2.check.DefaultParameter; import de.hft.stuttgart.citydoctor2.check.Requirement; -import de.hft.stuttgart.citydoctor2.check.Unit; +import de.hft.stuttgart.citydoctor2.check.RequirementType; import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.checks.util.UnfragmentedCheck; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; @@ -46,17 +44,12 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; public class RoofSurfaceUnfragmentedCheck extends Check { private static final List dependencies; - private static final List defaultParameters; private static final String MAX_ANGLE_DEVIATION = "maxAngleDeviation"; private double maxAngleDeviation = Math.toRadians(1); static { - ArrayList defParameters = new ArrayList<>(3); - defParameters.add(new DefaultParameter(MAX_ANGLE_DEVIATION, "1", Unit.DEGREE)); - defaultParameters = Collections.unmodifiableList(defParameters); - ArrayList deps = new ArrayList<>(); deps.add(CheckId.C_GE_R_TOO_FEW_POINTS); deps.add(CheckId.C_GE_R_NOT_CLOSED); @@ -90,11 +83,6 @@ public class RoofSurfaceUnfragmentedCheck extends Check { return dependencies; } - @Override - public List getDefaultParameter() { - return defaultParameters; - } - @Override public Set appliesToRequirements() { return CollectionUtils.singletonSet(Requirement.R_SE_BS_ROOF_UNFRAGMENTED); -- GitLab