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 2610cc122d0829cb17f7750ce902053d89e23022..e421951eb4f7dbc6ff8c94d12ef4a5791f8389c7 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.<br>
+ * 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.<br>
+ * 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<Class<Checkable>> 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<Checkable> 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 b3a179f8a939d70d2622c9a081a8a22778394290..44049e0d366ff714a8f25d3d964ad0e628bd8b47 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 a9671a2564ac1a435689421c0480627e463039f5..708b6a7f3f7d6e3cd361910c4cbe60a11f610989 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 724d731697f1d6ac501727dcae51d77b13785d3b..237501906236333c00e8bfa400c8321aa05f4324 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 6ca48f1d1affa1444ba7ee4a0f803252d85bf941..b439cdb377f94f849371dd8d33e1c0e446ea6879 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<SchematronError> v, CityObject co) {
+ static void handleSchematronErrorsForCityObject(List<SchematronError> 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<Checking> 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<ErrorId, AtomicInteger> 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<SchematronError> 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<CheckError> errorList = new ArrayList<>();
- co.collectContainedErrors(errorList);
- Set<CheckError> errors = new HashSet<>(errorList);
- for (CheckError e : errors) {
- errorCount.compute(e.getErrorId(), (k, v) -> {
- if (v == null) {
- return new AtomicInteger(1);
- }
- v.incrementAndGet();
- return v;
- });
- }
-
- if (l != null) {
- l.featureChecked(co);
- }
- }
-
- @Override
- public void accept(CityModel cm) {
- QualityADEUtils.removeValidation(cm);
- for (Entry<ErrorId, AtomicInteger> e : errorCount.entrySet()) {
- ErrorStatistics stats = new ErrorStatistics();
- stats.setAmount(e.getValue().get());
- stats.setName(QualityADEUtils.mapErrorIdToAdeId(e.getKey()));
- statistics.getErrorStatistics().add(stats);
- }
- val.setStatistics(statistics);
- val.setValidationPlan(c.createValidationPlan());
+ 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 0000000000000000000000000000000000000000..d960e927b35f9fa8be9ec3f3ed935658902cf2ef
--- /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 <https://www.gnu.org/licenses/>.
+ */
+package de.hft.stuttgart.citydoctor2.check;
+
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.citygml4j.factory.GMLGeometryFactory;
+import org.citygml4j.model.citygml.core.CityModel;
+
+import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
+import de.hft.stuttgart.citydoctor2.checks.SvrlContentHandler;
+import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener;
+import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
+import de.hft.stuttgart.citydoctor2.datastructure.Building;
+import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
+import de.hft.stuttgart.citydoctor2.datastructure.LandObject;
+import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject;
+import de.hft.stuttgart.citydoctor2.datastructure.Vegetation;
+import de.hft.stuttgart.citydoctor2.datastructure.WaterObject;
+import de.hft.stuttgart.citydoctor2.parser.CityGmlConsumer;
+import de.hft.stuttgart.citydoctor2.reporting.XmlStreamReporter;
+import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfStreamReporter;
+import de.hft.stuttgart.citydoctor2.utils.Localization;
+import de.hft.stuttgart.citydoctor2.utils.QualityADEUtils;
+import de.hft.stuttgart.quality.model.Validation;
+import de.hft.stuttgart.quality.model.jaxb.ErrorStatistics;
+import de.hft.stuttgart.quality.model.jaxb.FeatureStatistics;
+import de.hft.stuttgart.quality.model.jaxb.Statistics;
+
+public class StreamCityGmlConsumer implements CityGmlConsumer {
+
+ private Checker c;
+ private XmlStreamReporter xmlReporter;
+ private PdfStreamReporter pdfReporter;
+ private SvrlContentHandler handler;
+ private Map<ErrorId, AtomicInteger> errorCount;
+ private GMLGeometryFactory gmlFactory;
+ private ValidationConfiguration config;
+ private Statistics statistics;
+ private FeatureStatistics buildingStatistics;
+ private FeatureStatistics bridgeStatistics;
+ private FeatureStatistics transportationStatistics;
+ private FeatureStatistics vegetationStatistics;
+ private FeatureStatistics landStatistics;
+ private FeatureStatistics waterStatistics;
+ private Validation val;
+ private FeatureCheckedListener l;
+
+ public StreamCityGmlConsumer(Checker c, XmlStreamReporter xmlReporter, PdfStreamReporter pdfReporter,
+ SvrlContentHandler handler, ValidationConfiguration config, FeatureCheckedListener l) {
+ this.c = c;
+ this.xmlReporter = xmlReporter;
+ this.pdfReporter = pdfReporter;
+ this.handler = handler;
+ this.config = config;
+ this.l = l;
+ errorCount = new HashMap<>();
+ gmlFactory = new GMLGeometryFactory();
+
+ val = new Validation();
+ val.setValidationDate(ZonedDateTime.now());
+ val.setValidationSoftware("CityDoctor " + Localization.getText(Localization.VERSION));
+ statistics = new Statistics();
+ buildingStatistics = new FeatureStatistics();
+ statistics.setNumErrorBuildings(buildingStatistics);
+ bridgeStatistics = new FeatureStatistics();
+ statistics.setNumErrorBridgeObjects(bridgeStatistics);
+ transportationStatistics = new FeatureStatistics();
+ statistics.setNumErrorTransportation(transportationStatistics);
+ vegetationStatistics = new FeatureStatistics();
+ statistics.setNumErrorVegetation(vegetationStatistics);
+ landStatistics = new FeatureStatistics();
+ statistics.setNumErrorLandObjects(landStatistics);
+ waterStatistics = new FeatureStatistics();
+ statistics.setNumErrorWaterObjects(waterStatistics);
+
+ }
+
+ @Override
+ public void accept(CityObject co) {
+ c.checkFeature(xmlReporter, pdfReporter, co);
+
+ if (handler != null) {
+ List<SchematronError> errors = handler.getFeatureErrors().get(co.getGmlId().getGmlString());
+ if (errors != null) {
+ Checker.handleSchematronErrorsForCityObject(errors, co);
+ }
+ }
+
+ // remove existing quality ade datastructure if existing
+ QualityADEUtils.removeValidationResult(co);
+ // store quality ade datastructures in cityobject
+ QualityADEUtils.writeQualityAde(co);
+ // recreate geometry
+ co.reCreateGeometries(gmlFactory, config.getParserConfiguration());
+
+ // store result in statistics
+ applyToStatistics(buildingStatistics, bridgeStatistics, transportationStatistics, vegetationStatistics,
+ landStatistics, waterStatistics, co);
+
+ // add errors to statistics
+ List<CheckError> errorList = new ArrayList<>();
+ co.collectContainedErrors(errorList);
+ Set<CheckError> errors = new HashSet<>(errorList);
+ for (CheckError e : errors) {
+ errorCount.compute(e.getErrorId(), (k, v) -> {
+ if (v == null) {
+ return new AtomicInteger(1);
+ }
+ v.incrementAndGet();
+ return v;
+ });
+ }
+
+ if (l != null) {
+ l.featureChecked(co);
+ }
+ }
+
+ @Override
+ public void accept(CityModel cm) {
+ QualityADEUtils.removeValidation(cm);
+ for (Entry<ErrorId, AtomicInteger> e : errorCount.entrySet()) {
+ ErrorStatistics stats = new ErrorStatistics();
+ stats.setAmount(e.getValue().get());
+ stats.setName(QualityADEUtils.mapErrorIdToAdeId(e.getKey()));
+ statistics.getErrorStatistics().add(stats);
+ }
+ val.setStatistics(statistics);
+ val.setValidationPlan(c.createValidationPlan());
+
+ cm.addGenericApplicationPropertyOfCityModel(val);
+ }
+
+ private static void applyToStatistics(FeatureStatistics buildingStatistics, FeatureStatistics bridgeStatistics,
+ FeatureStatistics transportationStatistics, FeatureStatistics vegetationStatistics,
+ FeatureStatistics landStatistics, FeatureStatistics waterStatistics, CityObject co) {
+ if (co.isValidated()) {
+ if (co instanceof Building) {
+ countForFeatureStatistics(buildingStatistics, co);
+ } else if (co instanceof TransportationObject) {
+ countForFeatureStatistics(transportationStatistics, co);
+ } else if (co instanceof BridgeObject) {
+ countForFeatureStatistics(bridgeStatistics, co);
+ } else if (co instanceof WaterObject) {
+ countForFeatureStatistics(waterStatistics, co);
+ } else if (co instanceof LandObject) {
+ countForFeatureStatistics(landStatistics, co);
+ } else if (co instanceof Vegetation) {
+ countForFeatureStatistics(vegetationStatistics, co);
+ }
+ }
+ }
+
+ private static void countForFeatureStatistics(FeatureStatistics featureStatistics, CityObject co) {
+ featureStatistics.setNumChecked(featureStatistics.getNumChecked() + 1);
+ if (co.containsAnyError()) {
+ featureStatistics.setNumErrors(featureStatistics.getNumErrors() + 1);
+ }
+ }
+
+}
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 cbae54fd03436c3afc344c491fe7a0880a9224a1..61af95865b8f245cf3a697b75011c38e5b4db286 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 d564b8365d7611e56aa2127d526100f61bac4f9b..3e8e6167a19298ec24b7a320784670ed2f716eb1 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<String, String> params, ParserConfiguration config) {
String epsilonString = params.get(EPSILON_NAME);
- if (epsilonString == null) {
- epsilon = 0.0001;
- } else {
+ if (epsilonString != null) {
epsilon = Double.parseDouble(epsilonString);
}
}
@@ -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 8135788c0ee199a28751bd8b985ef4df2e2f89c4..9f63a86fd58173d7655607329c3ca9b0aaa8b938 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<Vertex> 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<Vertex> 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<Vector3d> 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 a3e49ebfdba85d019a323cde98a61845b57e2498..f65b0f87170a1ce766aabf89dd7a275749806b64 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<CheckId> dependencies;
-
static {
ArrayList<CheckId> 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<String, String> 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<Edge> 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 0000000000000000000000000000000000000000..d15aed03f645172c65af5b74ba3fbf6a4d8d09f0
--- /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 <https://www.gnu.org/licenses/>.
+ */
+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<String, String> 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<CheckError> errors = new ArrayList<>();
+ b.collectContainedErrors(errors);
+ CheckError checkError = errors.get(0);
+ assertEquals(ErrorId.GE_P_DEGENERATED_POLYGON, checkError.getErrorId());
+ }
+}