From d3fb0ee62823fa5c100cdd903da85d159d67b1ba Mon Sep 17 00:00:00 2001
From: Matthias Betz <matthias.betz@hft-stuttgart.de>
Date: Wed, 3 Mar 2021 10:51:12 +0100
Subject: [PATCH] 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.<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 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<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 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 <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 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<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 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<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 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<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 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 <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());
+	}
+}
-- 
GitLab