Commit d3fb0ee6 authored by Matthias Betz's avatar Matthias Betz
Browse files

fixing some linting issues

added test for degenerated polygon
renaming degenerated polygon error
parent 09470a4d
Pipeline #2012 canceled with stage
......@@ -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())) {
......
......@@ -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;
......
......@@ -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
......
......@@ -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 {
......
......@@ -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) {
......
/*-
* 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);
}
}
}
......@@ -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());
......
......@@ -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
......
......@@ -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) {