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()); + } +}