Commit b8bac3e6 authored by Luna Riegel's avatar Luna Riegel
Browse files

Merge branch 'dev' into 'master'

CityDoctor Version 3.18.2

See merge request !40
parents 947020ff 4b27a7e4
Pipeline #12385 passed with stage
in 2 minutes and 4 seconds
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>de.hft.stuttgart</groupId> <groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId> <artifactId>CityDoctorParent</artifactId>
<version>3.18.1</version> <version>3.18.2</version>
</parent> </parent>
<artifactId>CityDoctorValidation</artifactId> <artifactId>CityDoctorValidation</artifactId>
<name>CityDoctorValidation</name> <name>CityDoctorValidation</name>
......
#!/bin/sh #!/bin/sh
./runtime/bin/java -classpath app/*:plugin/* de.hft.stuttgart.citydoctor2.CityDoctorValidation -in REKaiserwall.gml -config testConfigWithStreaming.yml -xmlReport output.xml ./runtime/bin/java -classpath app/*:plugin/* de.hft.stuttgart.citydoctor2.CityDoctorValidation -in REKaiserwall.gml -config testConfigWithStreaming.yml -xmlReport output.xml -db_location "${TMPDIR:-/tmp}"
\ No newline at end of file \ No newline at end of file
java -classpath app/*;plugin/* de.hft.stuttgart.citydoctor2.CityDoctorValidation -in REKaiserwall.gml -config testConfigWithStreaming.yml -xmlReport output.xml java -classpath app/*;plugin/* de.hft.stuttgart.citydoctor2.CityDoctorValidation -in REKaiserwall.gml -config testConfigWithStreaming.yml -xmlReport output.xml -db_location %TEMP%/citydoctor/
pause pause
\ No newline at end of file
#!/bin/sh #!/bin/sh
java -classpath app/*:plugin/* de.hft.stuttgart.citydoctor2.CityDoctorValidation -in REKaiserwall.gml -config testConfigWithStreaming.yml -xmlReport output.xml java -classpath app/*:plugin/* de.hft.stuttgart.citydoctor2.CityDoctorValidation -in REKaiserwall.gml -config testConfigWithStreaming.yml -xmlReport output.xml -db_location #!/bin/sh
\ No newline at end of file ./runtime/bin/java -classpath app/*:plugin/* de.hft.stuttgart.citydoctor2.CityDoctorValidation -in REKaiserwall.gml -config testConfigWithStreaming.yml -xmlReport output.xml -db_location "${TMPDIR:-/tmp}"
\ No newline at end of file
"runtime/bin/java.exe" -classpath app/*;plugin/* de.hft.stuttgart.citydoctor2.CityDoctorValidation -in REKaiserwall.gml -config testConfigWithStreaming.yml -xmlReport output.xml "runtime/bin/java.exe" -classpath app/*;plugin/* de.hft.stuttgart.citydoctor2.CityDoctorValidation -in REKaiserwall.gml -config testConfigWithStreaming.yml -xmlReport output.xml -db_location %TEMP%/citydoctor/
pause pause
\ No newline at end of file
...@@ -23,6 +23,7 @@ import java.io.FileNotFoundException; ...@@ -23,6 +23,7 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import de.hft.stuttgart.citydoctor2.database.DatabaseSettings;
import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.utils.Localization;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
...@@ -70,6 +71,8 @@ public class CityDoctorValidation { ...@@ -70,6 +71,8 @@ public class CityDoctorValidation {
String xmlOutput = getXmlOutput(argParser); String xmlOutput = getXmlOutput(argParser);
String pdfOutput = getPdfOutput(argParser); String pdfOutput = getPdfOutput(argParser);
String outputFile = getOutputFile(argParser, true); String outputFile = getOutputFile(argParser, true);
lookForDbConfigFileParameter(argParser);
lookForDbLocationParameter(argParser);
ValidationConfiguration config = getValidationConfig(argParser); ValidationConfiguration config = getValidationConfig(argParser);
startValidationProcess(inputFile, xmlOutput, pdfOutput, config, outputFile); startValidationProcess(inputFile, xmlOutput, pdfOutput, config, outputFile);
...@@ -81,18 +84,42 @@ public class CityDoctorValidation { ...@@ -81,18 +84,42 @@ public class CityDoctorValidation {
if (optional) { if (optional) {
return null; return null;
} }
logger.error(Localization.getText("CityDoctorValidation.noOutputFile")); logger.fatal(Localization.getText("CityDoctorValidation.noOutputFile"));
System.exit(11); System.exit(11);
} }
List<String> outFiles = argParser.getValues("out"); List<String> outFiles = argParser.getValues("out");
if (outFiles.size() != 1) { if (outFiles.size() != 1) {
logger.error(Localization.getText("CityDoctorValidation.notExactlyOneOutputFile")); logger.fatal(Localization.getText("CityDoctorValidation.notExactlyOneOutputFile"));
System.exit(12); System.exit(12);
} }
outputFile = outFiles.get(0); outputFile = outFiles.get(0);
return outputFile; return outputFile;
} }
private static void lookForDbConfigFileParameter(ArgumentParser argParser) {
if (!argParser.containsOption("db_config")) {
return;
}
List<String> dbConfigFiles = argParser.getValues("db_config");
if (dbConfigFiles.size() == 1) {
DatabaseSettings.loadPropertiesFromFile(new File(dbConfigFiles.get(0)));
} else if (dbConfigFiles.size() > 1) {
logger.warn("More than one DB config file provided! Using standard config instead.");
}
}
private static void lookForDbLocationParameter(ArgumentParser argParser) {
if (!argParser.containsOption("db_location")) {
return;
}
List<String> dbLocations = argParser.getValues("db_location");
if (dbLocations.size() == 1) {
DatabaseSettings.setDBLocation(dbLocations.get(0));
} else if (dbLocations.size() > 1) {
logger.warn("More than one DB location provided! Using standard location instead.");
}
}
/** /**
* Validates the cityGML input file with a standard validation configuration. * Validates the cityGML input file with a standard validation configuration.
* *
...@@ -133,7 +160,7 @@ public class CityDoctorValidation { ...@@ -133,7 +160,7 @@ public class CityDoctorValidation {
config = ValidationConfiguration.loadStandardValidationConfig(); config = ValidationConfiguration.loadStandardValidationConfig();
config.setUseStreaming(true); config.setUseStreaming(true);
} else { } else {
config = ValidationConfiguration.loadValidationConfig(validationConfigFile.getAbsolutePath()); config = ValidationConfiguration.loadValidationConfigFile(validationConfigFile.getAbsolutePath());
} }
String xmlOutputPath = null; String xmlOutputPath = null;
if (xmlOutput != null) { if (xmlOutput != null) {
...@@ -202,7 +229,7 @@ public class CityDoctorValidation { ...@@ -202,7 +229,7 @@ public class CityDoctorValidation {
if (argParser.containsOption("pdfreport")) { if (argParser.containsOption("pdfreport")) {
List<String> reportFiles = argParser.getValues("pdfreport"); List<String> reportFiles = argParser.getValues("pdfreport");
if (reportFiles.size() != 1) { if (reportFiles.size() != 1) {
logger.error(Localization.getText("CityDoctorValidation.notExactlyOnePDFOutput")); logger.fatal(Localization.getText("CityDoctorValidation.notExactlyOnePDFOutput"));
System.exit(6); System.exit(6);
} }
return reportFiles.get(0); return reportFiles.get(0);
...@@ -214,7 +241,7 @@ public class CityDoctorValidation { ...@@ -214,7 +241,7 @@ public class CityDoctorValidation {
if (argParser.containsOption("xmlreport")) { if (argParser.containsOption("xmlreport")) {
List<String> reportFiles = argParser.getValues("xmlreport"); List<String> reportFiles = argParser.getValues("xmlreport");
if (reportFiles.size() != 1) { if (reportFiles.size() != 1) {
logger.error(Localization.getText("CityDoctorValidation.notExactlyOneXMLOutput")); logger.fatal(Localization.getText("CityDoctorValidation.notExactlyOneXMLOutput"));
System.exit(5); System.exit(5);
} }
return reportFiles.get(0); return reportFiles.get(0);
...@@ -231,16 +258,16 @@ public class CityDoctorValidation { ...@@ -231,16 +258,16 @@ public class CityDoctorValidation {
if (argParser.containsOption("config")) { if (argParser.containsOption("config")) {
List<String> configFiles = argParser.getValues("config"); List<String> configFiles = argParser.getValues("config");
if (configFiles.size() != 1) { if (configFiles.size() != 1) {
logger.error(Localization.getText("CityDoctorValidation.notExactlyOneConfigFile")); logger.fatal(Localization.getText("CityDoctorValidation.notExactlyOneConfigFile"));
System.exit(4); System.exit(4);
} }
return ValidationConfiguration.loadValidationConfig(configFiles.get(0)); return ValidationConfiguration.loadValidationConfigFile(configFiles.get(0));
} else { } else {
if (optional) { if (optional) {
logger.warn(Localization.getText("CityDoctorValidation.loadingDefaultConfig")); logger.warn(Localization.getText("CityDoctorValidation.loadingDefaultConfig"));
return ValidationConfiguration.loadStandardValidationConfig(); return ValidationConfiguration.loadStandardValidationConfig();
} else { } else {
logger.error(Localization.getText("CityDoctorValidation.noConfig")); logger.fatal(Localization.getText("CityDoctorValidation.noConfig"));
System.exit(7); System.exit(7);
return null; return null;
} }
...@@ -252,7 +279,7 @@ public class CityDoctorValidation { ...@@ -252,7 +279,7 @@ public class CityDoctorValidation {
if (argParser.containsOption("out")) { if (argParser.containsOption("out")) {
List<String> outFiles = argParser.getValues("out"); List<String> outFiles = argParser.getValues("out");
if (outFiles.size() != 1) { if (outFiles.size() != 1) {
logger.error(Localization.getText("CityDoctorValidation.multipleOutputFiles")); logger.fatal(Localization.getText("CityDoctorValidation.multipleOutputFiles"));
System.exit(3); System.exit(3);
} }
return outFiles.get(0); return outFiles.get(0);
...@@ -271,12 +298,12 @@ public class CityDoctorValidation { ...@@ -271,12 +298,12 @@ public class CityDoctorValidation {
if (optional) { if (optional) {
return null; return null;
} }
logger.error(Localization.getText("CityDoctorValidation.noInputFile")); logger.fatal(Localization.getText("CityDoctorValidation.noInputFile"));
System.exit(1); System.exit(1);
} }
List<String> inFiles = argParser.getValues("in"); List<String> inFiles = argParser.getValues("in");
if (inFiles.size() != 1) { if (inFiles.size() != 1) {
logger.error(Localization.getText("CityDoctorValidation.notExactlyOneInputFile")); logger.fatal(Localization.getText("CityDoctorValidation.notExactlyOneInputFile"));
System.exit(2); System.exit(2);
} }
inputFile = inFiles.get(0); inputFile = inFiles.get(0);
......
...@@ -2,6 +2,7 @@ package de.hft.stuttgart.citydoctor2; ...@@ -2,6 +2,7 @@ package de.hft.stuttgart.citydoctor2;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.database.DatabaseSettings;
import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
...@@ -26,20 +27,30 @@ public class CityDoctorValidationCLI implements Runnable { ...@@ -26,20 +27,30 @@ public class CityDoctorValidationCLI implements Runnable {
private File config; private File config;
@Option(names = {"-x" , "--xml" , "--xmlReport"}, @Option(names = {"-x" , "--xml" , "--xmlReport"},
description = "Create a XML validation report at target location", description = "Creates a XML validation report at target location",
defaultValue = Option.NULL_VALUE) defaultValue = Option.NULL_VALUE)
private String xmlReport; private String xmlReport;
@Option(names = {"-p" , "--pdf" , "--pdfReport"}, @Option(names = {"-p" , "--pdf" , "--pdfReport"},
description = "Create a PDF validation report at target location", description = "Creates a PDF validation report at target location",
defaultValue = Option.NULL_VALUE) defaultValue = Option.NULL_VALUE)
private String pdfReport; private String pdfReport;
@Option(names = {"-o" , "--out" , "--output"}, @Option(names = {"-o" , "--out" , "--output"},
description = "Create a validated copy of the CityGML input with QualityADE at target location.", description = "Creates a validated copy of the CityGML input with QualityADE at target location.",
defaultValue = Option.NULL_VALUE) defaultValue = Option.NULL_VALUE)
private String output; private String output;
@Option(names = {"--db_location", "--db_loc"},
description = "Creates the embedded database file at target location instead of CityDoctor's working directory.",
defaultValue = Option.NULL_VALUE)
private String dbLocation;
@Option(names = {"-db", "--db_config", "--db_settings"},
description = "Filepath to a .properties file for overriding the default configuration of the embedded database",
defaultValue = Option.NULL_VALUE)
private String dbConfig;
@Override @Override
public void run(){ public void run(){
try { try {
...@@ -47,7 +58,13 @@ public class CityDoctorValidationCLI implements Runnable { ...@@ -47,7 +58,13 @@ public class CityDoctorValidationCLI implements Runnable {
if(config == null){ if(config == null){
valConfig=ValidationConfiguration.loadStandardValidationConfig(); valConfig=ValidationConfiguration.loadStandardValidationConfig();
} else{ } else{
valConfig=ValidationConfiguration.loadValidationConfig(config.getAbsolutePath()); valConfig=ValidationConfiguration.loadValidationConfigFile(config.getAbsolutePath());
}
if(dbConfig != null){
DatabaseSettings.loadPropertiesFromFile(new File(dbConfig));
}
if (dbLocation != null) {
DatabaseSettings.setDBLocation(dbLocation);
} }
CityDoctorValidation.startValidationProcess(input, xmlReport, pdfReport, valConfig,output); CityDoctorValidation.startValidationProcess(input, xmlReport, pdfReport, valConfig,output);
} catch (CityDoctorWriteException | CityGmlParseException | IOException | InvalidGmlFileException e) { } catch (CityDoctorWriteException | CityGmlParseException | IOException | InvalidGmlFileException e) {
......
...@@ -18,10 +18,55 @@ ...@@ -18,10 +18,55 @@
*/ */
package de.hft.stuttgart.citydoctor2.check; package de.hft.stuttgart.citydoctor2.check;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import javax.xml.XMLConstants;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError; import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError; import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError; import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError;
import de.hft.stuttgart.citydoctor2.check.error.SchematronError; import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import de.hft.stuttgart.citydoctor2.check.error.XMLValidationError;
import de.hft.stuttgart.citydoctor2.checkresult.utility.CheckReportWriteException; import de.hft.stuttgart.citydoctor2.checkresult.utility.CheckReportWriteException;
import de.hft.stuttgart.citydoctor2.checks.CheckContainer; import de.hft.stuttgart.citydoctor2.checks.CheckContainer;
import de.hft.stuttgart.citydoctor2.checks.CheckPrototype; import de.hft.stuttgart.citydoctor2.checks.CheckPrototype;
...@@ -29,13 +74,10 @@ import de.hft.stuttgart.citydoctor2.checks.Checks; ...@@ -29,13 +74,10 @@ import de.hft.stuttgart.citydoctor2.checks.Checks;
import de.hft.stuttgart.citydoctor2.checks.SvrlContentHandler; import de.hft.stuttgart.citydoctor2.checks.SvrlContentHandler;
import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener; import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener;
import de.hft.stuttgart.citydoctor2.database.CityObjectCache; import de.hft.stuttgart.citydoctor2.database.CityObjectCache;
import de.hft.stuttgart.citydoctor2.database.CityObjectCache;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.parser.CityGmlConsumer; import de.hft.stuttgart.citydoctor2.parser.CityGmlConsumer;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
...@@ -59,40 +101,6 @@ import de.hft.stuttgart.quality.model.properties.RequirementProperty; ...@@ -59,40 +101,6 @@ import de.hft.stuttgart.quality.model.properties.RequirementProperty;
import de.hft.stuttgart.quality.model.types.Checking; import de.hft.stuttgart.quality.model.types.Checking;
import de.hft.stuttgart.quality.model.types.Parameter; import de.hft.stuttgart.quality.model.types.Parameter;
import de.hft.stuttgart.quality.model.types.ValidationPlan; import de.hft.stuttgart.quality.model.types.ValidationPlan;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.xml.XMLConstants;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/** /**
* The main container class for checking. It contains the logic for validation, * The main container class for checking. It contains the logic for validation,
...@@ -142,8 +150,8 @@ public class Checker { ...@@ -142,8 +150,8 @@ public class Checker {
return; return;
} }
File xmlFile = new File(xmlOutput); File xmlFile = new File(xmlOutput);
if (xmlFile.getParentFile() != null) { if (xmlFile.getParentFile() != null && xmlFile.getParentFile().mkdirs()) {
xmlFile.getParentFile().mkdirs(); logger.trace("Xml dir created");
} }
Reporter reporter = new XmlValidationReporter(); Reporter reporter = new XmlValidationReporter();
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(xmlFile.getAbsolutePath()))) { try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(xmlFile.getAbsolutePath()))) {
...@@ -158,9 +166,10 @@ public class Checker { ...@@ -158,9 +166,10 @@ public class Checker {
return; return;
} }
File pdfFile = new File(pdfOutput); File pdfFile = new File(pdfOutput);
if (pdfFile.getParentFile() != null) { if (pdfFile.getParentFile() != null && pdfFile.getParentFile().mkdirs()){
pdfFile.getParentFile().mkdirs(); logger.trace("Pdf dir created");
} }
Reporter reporter = new PdfReporter(); Reporter reporter = new PdfReporter();
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(pdfFile.getAbsolutePath()))) { try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(pdfFile.getAbsolutePath()))) {
reporter.writeReport(checkConfig, bos, model, config); reporter.writeReport(checkConfig, bos, model, config);
...@@ -235,7 +244,6 @@ public class Checker { ...@@ -235,7 +244,6 @@ public class Checker {
private void handleSchematronResults(SvrlContentHandler handler) { private void handleSchematronResults(SvrlContentHandler handler) {
CityObjectCache cache = model.getCache(); CityObjectCache cache = model.getCache();
handleSchematronErrorsGlobal(handler.getGeneralErrors()); handleSchematronErrorsGlobal(handler.getGeneralErrors());
Map<String, CityObject> featureMap = new HashMap<>();
boolean onlySchematron = execLayers.isEmpty(); boolean onlySchematron = execLayers.isEmpty();
if (onlySchematron) { if (onlySchematron) {
CheckableUtilsVisitor visitor = new CheckableUtilsVisitor() { CheckableUtilsVisitor visitor = new CheckableUtilsVisitor() {
...@@ -244,16 +252,11 @@ public class Checker { ...@@ -244,16 +252,11 @@ public class Checker {
checkable.setValidated(true); checkable.setValidated(true);
} }
}; };
model.createFeatureStream().forEach(f -> { model.createFeatureStream().forEach(co -> co.accept(visitor));
featureMap.put(f.getGmlId().getGmlString(), f);
f.accept(visitor);
});
} else {
model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f));
} }
handler.getFeatureErrors().forEach((k, v) -> { handler.getFeatureErrors().forEach((k, v) -> {
String trimmedId = k.trim(); String trimmedId = k.trim();
CityObject co = cache.get(new GmlId(k)); CityObject co = cache.get(new GmlId(trimmedId));
if (co == null) { if (co == null) {
// gml id reported by schematron was not found, add to general errors // gml id reported by schematron was not found, add to general errors
handleSchematronErrorsGlobal(v); handleSchematronErrorsGlobal(v);
...@@ -633,7 +636,7 @@ public class Checker { ...@@ -633,7 +636,7 @@ public class Checker {
ArrayList<Check> checkList = new ArrayList<>(); ArrayList<Check> checkList = new ArrayList<>();
for (CheckId id : enabledCheck) { for (CheckId id : enabledCheck) {
Check c = checkConfig.getCheckForId(id); Check c = checkConfig.getCheckForId(id);
c.init(parameterMap.get(id), parserConfig); c.init(parameterMap, parserConfig);
checkList.add(c); checkList.add(c);
} }
return checkList; return checkList;
...@@ -649,7 +652,6 @@ public class Checker { ...@@ -649,7 +652,6 @@ public class Checker {
parameterMap.compute(proto.getCheckId(), (k, v) -> { parameterMap.compute(proto.getCheckId(), (k, v) -> {
if (v == null) { if (v == null) {
v = new HashMap<>(); v = new HashMap<>();
v.put(GlobalParameters.NUMBER_OF_ROUNDING_PLACES, config.getNumberOfRoundingPlacesAsString());
v.put(GlobalParameters.MIN_VERTEX_DISTANCE, config.getMinVertexDistanceAsString()); v.put(GlobalParameters.MIN_VERTEX_DISTANCE, config.getMinVertexDistanceAsString());
} }
v.putAll(e.getValue().getParameters()); v.putAll(e.getValue().getParameters());
...@@ -692,7 +694,8 @@ public class Checker { ...@@ -692,7 +694,8 @@ public class Checker {
AtomicInteger checkedCount = new AtomicInteger(0); AtomicInteger checkedCount = new AtomicInteger(0);
// clear global errors // clear global errors
model.getGlobalErrors().clear(); model.getGlobalErrors().removeIf(e -> !e.isParsingError());
CityObjectCache cache = model.getCache(); CityObjectCache cache = model.getCache();
logger.trace("Setting up ThreadPool"); logger.trace("Setting up ThreadPool");
int threadCount = Runtime.getRuntime().availableProcessors() * 5; int threadCount = Runtime.getRuntime().availableProcessors() * 5;
...@@ -700,12 +703,11 @@ public class Checker { ...@@ -700,12 +703,11 @@ public class Checker {
(threadCount, threadCount, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); (threadCount, threadCount, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
try{ try{
long startTime = System.nanoTime(); long startTime = System.nanoTime();
ImplicitGeometryHolder.preparePrototypeGeometriesForChecking();
List<Future<GmlId>> futures = runChecksOnFeatures(exec, cache, features, checkedCount, l); List<Future<GmlId>> futures = runChecksOnFeatures(exec, cache, features, checkedCount, l);
List<GmlId> missedFeatures = getMissedFeatures(futures); List<GmlId> missedFeatures = getMissedFeatures(futures);
ImplicitGeometryHolder.clearMetaDataOfPrototypeGeometries();
if (!missedFeatures.isEmpty()){ if (!missedFeatures.isEmpty()){
logger.error(Localization.getText("Checker.dbUnresponsive")); logger.error(Localization.getText("Checker.dbUnresponsive"));
if (logger.isDebugEnabled()){ if (logger.isDebugEnabled()){
...@@ -739,8 +741,22 @@ public class Checker { ...@@ -739,8 +741,22 @@ public class Checker {
} }
} }
/**
* Runs the validation on a list of Features. The checks are run asynchronously and multithreaded. Returns a list
* of {@link Future Futures} for evaluation if Features failed to be checked due to exceptions or failing to load from
* the cache. If failed, the respective Future will return the affected Feature's GmlID.
* <p>Checks finding an error in a Feature, or not having their dependencies met, are not a check-failure in this
* context.<p/>
* @param exec the threadpool for the tasks
* @param cache the cache of the model
* @param ids the list of GmlIds to check
* @param checkedCount the number of already checked Features for tracking the total progress of validation across retries
* @param l a listener for the progress of the validation, can be null
* @return a list of Futures containing the GmlId of Features that failed to execute all checks, and contain null otherwise
* @throws InterruptedException if interrupted while invoking the tasks for checking
*/
private List<Future<GmlId>> runChecksOnFeatures(ExecutorService exec, CityObjectCache cache, List<GmlId> ids, AtomicInteger checkedCount, private List<Future<GmlId>> runChecksOnFeatures(ExecutorService exec, CityObjectCache cache, List<GmlId> ids, AtomicInteger checkedCount,
ProgressListener l) @Nullable ProgressListener l)
throws InterruptedException { throws InterruptedException {
float featureSum = ids.size() + (float) checkedCount.get(); float featureSum = ids.size() + (float) checkedCount.get();
...@@ -754,11 +770,9 @@ public class Checker { ...@@ -754,11 +770,9 @@ public class Checker {
} }
if(Thread.interrupted()){ if(Thread.interrupted()){
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
return null; return id;
} }
co.prepareForChecking();
executeChecksForCityObject(co); executeChecksForCityObject(co);
co.clearMetaInformation();
cache.put(co); cache.put(co);
checkedCount.incrementAndGet(); checkedCount.incrementAndGet();
if (l!=null){ if (l!=null){
...@@ -773,7 +787,7 @@ public class Checker { ...@@ -773,7 +787,7 @@ public class Checker {
private List<GmlId> getMissedFeatures(List<Future<GmlId>> futures) throws InterruptedException{ private List<GmlId> getMissedFeatures(List<Future<GmlId>> futures) throws InterruptedException{
List<GmlId> missedList = new ArrayList<>(); List<GmlId> missedList = new ArrayList<>();
Set<ExecutionException> errors = new HashSet<>(); Set<String> errors = new HashSet<>();
for (Future<GmlId> future : futures) { for (Future<GmlId> future : futures) {
try{ try{
GmlId gmlId = future.get(); GmlId gmlId = future.get();
...@@ -783,7 +797,7 @@ public class Checker { ...@@ -783,7 +797,7 @@ public class Checker {
} catch (ExecutionException e){ } catch (ExecutionException e){
logger.debug("A Task failed due to an unexpected exception", e); logger.debug("A Task failed due to an unexpected exception", e);
logger.debug(e.getCause()); logger.debug(e.getCause());
errors.add(e); errors.add(e.getCause().toString());
} }
} }
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
...@@ -831,7 +845,9 @@ public class Checker { ...@@ -831,7 +845,9 @@ public class Checker {
if (!filterObject(co)) { if (!filterObject(co)) {
return; return;
} }
co.prepareForChecking();
executeChecksForCheckable(co); executeChecksForCheckable(co);
co.clearMetaInformation();
} }
/** /**
...@@ -912,11 +928,25 @@ public class Checker { ...@@ -912,11 +928,25 @@ public class Checker {
XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName); XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName);
PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName); PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName);
// XML validation
if (config.isXmlValidation() && (xmlReporter != null || pdfReporter != null)) {
List<String> validationIssues = CityGmlParser.validateFile(null, inputFile.toPath());
for (String xmlError : validationIssues) {
XMLValidationError validationError = new XMLValidationError(xmlError);
if (xmlReporter != null) {
xmlReporter.reportGlobalError(validationError);
}
if (pdfReporter != null) {
pdfReporter.reportGlobalError(validationError);
}
}
}
// execute schematron first // execute schematron first
SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, inputFile); SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, inputFile);
CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, l); CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, l);
// parse and validate // parse and validate
CityGmlParser.streamCityGml(inputFile.getAbsolutePath(), config.getParserConfiguration(), con, outputFile); CityGmlParser.streamCityGml(inputFile, config.getParserConfiguration(), con, outputFile);
// write reports if available // write reports if available
writeReport(xmlReporter); writeReport(xmlReporter);
......
...@@ -53,7 +53,6 @@ public class ValidationConfiguration implements Serializable { ...@@ -53,7 +53,6 @@ public class ValidationConfiguration implements Serializable {
private static final long serialVersionUID = -8020055032177740646L; private static final long serialVersionUID = -8020055032177740646L;
private static final Logger logger = LogManager.getLogger(ValidationConfiguration.class); private static final Logger logger = LogManager.getLogger(ValidationConfiguration.class);
private Map<String, String> globalParameters = new HashMap<>(); private Map<String, String> globalParameters = new HashMap<>();
private boolean xmlValidation = false; private boolean xmlValidation = false;
private boolean useStreaming = false; private boolean useStreaming = false;
...@@ -62,10 +61,17 @@ public class ValidationConfiguration implements Serializable { ...@@ -62,10 +61,17 @@ public class ValidationConfiguration implements Serializable {
private ParserConfiguration parserConfig; private ParserConfiguration parserConfig;
public static ValidationConfiguration loadValidationConfig(String validationFile) throws FileNotFoundException { public static ValidationConfiguration loadValidationConfigFile(String validationFile) throws FileNotFoundException {
return loadValidationConfig(new FileInputStream(validationFile)); return loadValidationConfig(new FileInputStream(validationFile));
} }
public static ValidationConfiguration loadValidationConfigFromString(String validationString) throws FileNotFoundException {
Yaml yaml = new Yaml(new Constructor(ValidationConfiguration.class, new LoaderOptions()));
ValidationConfiguration config = yaml.load(validationString);
config.validateConfiguration();
return config;
}
public static ValidationConfiguration loadValidationConfig(InputStream stream) { public static ValidationConfiguration loadValidationConfig(InputStream stream) {
Yaml yaml = new Yaml(new Constructor(ValidationConfiguration.class, new LoaderOptions())); Yaml yaml = new Yaml(new Constructor(ValidationConfiguration.class, new LoaderOptions()));
ValidationConfiguration config = yaml.load(stream); ValidationConfiguration config = yaml.load(stream);
...@@ -96,15 +102,25 @@ public class ValidationConfiguration implements Serializable { ...@@ -96,15 +102,25 @@ public class ValidationConfiguration implements Serializable {
} }
public void saveAs(File f) throws IOException { public void saveAs(File f) throws IOException {
Yaml yaml = prepareDumpingOfConfig();
try (BufferedWriter bw = new BufferedWriter(new FileWriter(f))) {
yaml.dump(this, bw);
}
}
public String saveAsString() throws IOException {
Yaml yaml = prepareDumpingOfConfig();
return yaml.dump(this);
}
private Yaml prepareDumpingOfConfig() {
DumperOptions options = new DumperOptions(); DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setPrettyFlow(true); options.setPrettyFlow(true);
Representer rep = new ValidationConfigurationRepresenter(options); Representer rep = new ValidationConfigurationRepresenter(options);
rep.addClassTag(ValidationConfiguration.class, Tag.MAP); rep.addClassTag(ValidationConfiguration.class, Tag.MAP);
Yaml yaml = new Yaml(rep); Yaml yaml = new Yaml(rep);
try (BufferedWriter bw = new BufferedWriter(new FileWriter(f))) { return yaml;
yaml.dump(this, bw);
}
} }
public boolean isXmlValidation() { public boolean isXmlValidation() {
...@@ -174,8 +190,7 @@ public class ValidationConfiguration implements Serializable { ...@@ -174,8 +190,7 @@ public class ValidationConfiguration implements Serializable {
}); });
} }
globalParameters.putIfAbsent(GlobalParameters.MIN_VERTEX_DISTANCE, MIN_VERTEX_DISTANCE_DEFAULT); globalParameters.putIfAbsent(GlobalParameters.MIN_VERTEX_DISTANCE, MIN_VERTEX_DISTANCE_DEFAULT);
globalParameters.putIfAbsent(GlobalParameters.NUMBER_OF_ROUNDING_PLACES, globalParameters.putIfAbsent(GlobalParameters.NUMBER_OF_ROUNDING_PLACES, NUMBER_OF_ROUNDING_PLACES_DEFAULT);
NUMBER_OF_ROUNDING_PLACES_DEFAULT);
} }
public int getNumberOfRoundingPlaces() { public int getNumberOfRoundingPlaces() {
......
...@@ -62,7 +62,7 @@ public class CheckContainer extends Check { ...@@ -62,7 +62,7 @@ public class CheckContainer extends Check {
} }
@Override @Override
public void init(Map<String, String> parameters, ParserConfiguration config) { public void init(Map<CheckId, Map<String, String>> parameters, ParserConfiguration config) {
check.init(parameters, config); check.init(parameters, config);
} }
......
...@@ -30,10 +30,12 @@ import de.hft.stuttgart.citydoctor2.check.CheckId; ...@@ -30,10 +30,12 @@ import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.RequirementType; import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Checkable; import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.GlobalParameters;
import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError; import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError;
import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError; import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError;
import de.hft.stuttgart.citydoctor2.checks.Checks;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
...@@ -52,8 +54,6 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; ...@@ -52,8 +54,6 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
*/ */
public class DuplicatePointsCheck extends Check { public class DuplicatePointsCheck extends Check {
private static final String EPSILON_NAME = "minVertexDistance";
private static final List<CheckId> dependencies; private static final List<CheckId> dependencies;
static { static {
...@@ -66,11 +66,16 @@ public class DuplicatePointsCheck extends Check { ...@@ -66,11 +66,16 @@ public class DuplicatePointsCheck extends Check {
classes.add(LinearRing.class); classes.add(LinearRing.class);
} }
private double epsilon = 0.0001; private double epsilon = Checks.MIN_VERTEX_DISTANCE_DEFAULT;
@Override @Override
public void init(Map<String, String> params, ParserConfiguration config) { public void init(Map<CheckId, Map<String, String>> params, ParserConfiguration config) {
String epsilonString = params.get(EPSILON_NAME); Map<String, String> localParameters = params.get(getCheckId());
if (localParameters == null) {
// no parameters
return;
}
String epsilonString = localParameters.get(GlobalParameters.MIN_VERTEX_DISTANCE);
if (epsilonString != null) { if (epsilonString != null) {
epsilon = Double.parseDouble(epsilonString); epsilon = Double.parseDouble(epsilonString);
} }
......
...@@ -29,10 +29,12 @@ import de.hft.stuttgart.citydoctor2.check.Check; ...@@ -29,10 +29,12 @@ import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.GlobalParameters;
import de.hft.stuttgart.citydoctor2.check.RequirementType; import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError; import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError;
import de.hft.stuttgart.citydoctor2.checks.Checks;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
...@@ -49,8 +51,6 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; ...@@ -49,8 +51,6 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
*/ */
public class InteriorDisconnectedCheck extends Check { public class InteriorDisconnectedCheck extends Check {
private static final String EPSILON_NAME = "minVertexDistance";
private static final List<CheckId> dependencies; private static final List<CheckId> dependencies;
static { static {
...@@ -63,11 +63,16 @@ public class InteriorDisconnectedCheck extends Check { ...@@ -63,11 +63,16 @@ public class InteriorDisconnectedCheck extends Check {
dependencies = Collections.unmodifiableList(deps); dependencies = Collections.unmodifiableList(deps);
} }
private double epsilon = 0.0001; private double epsilon = Checks.MIN_VERTEX_DISTANCE_DEFAULT;
@Override @Override
public void init(Map<String, String> params, ParserConfiguration config) { public void init(Map<CheckId, Map<String, String>> params, ParserConfiguration config) {
String epsilonString = params.get(EPSILON_NAME); Map<String, String> localParameters = params.get(getCheckId());
if (localParameters == null) {
// no parameters
return;
}
String epsilonString = localParameters.get(GlobalParameters.MIN_VERTEX_DISTANCE);
if (epsilonString != null) { if (epsilonString != null) {
epsilon = Double.parseDouble(epsilonString); epsilon = Double.parseDouble(epsilonString);
} }
......
...@@ -44,9 +44,10 @@ import de.hft.stuttgart.citydoctor2.tesselation.TesselatedRing; ...@@ -44,9 +44,10 @@ import de.hft.stuttgart.citydoctor2.tesselation.TesselatedRing;
public class NullAreaCheck extends Check { public class NullAreaCheck extends Check {
private static final String DELTA_NAME = "delta"; private static final String DELTA_NAME = "delta";
private static final List<CheckId> dependencies; private static final List<CheckId> dependencies;
private double delta = 0.0001;
static { static {
ArrayList<CheckId> deps = new ArrayList<>(); ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS); deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
...@@ -55,12 +56,16 @@ public class NullAreaCheck extends Check { ...@@ -55,12 +56,16 @@ public class NullAreaCheck extends Check {
dependencies = Collections.unmodifiableList(deps); dependencies = Collections.unmodifiableList(deps);
} }
private double delta = 0.0001;
@Override @Override
public void init(Map<String, String> parameters, ParserConfiguration config) { public void init(Map<CheckId, Map<String, String>> params, ParserConfiguration config) {
if (parameters.containsKey(DELTA_NAME)) { Map<String, String> localParameters = params.get(getCheckId());
delta = Double.parseDouble(parameters.get(DELTA_NAME)); if (localParameters == null) {
// no parameters
return;
}
String epsilonString = localParameters.get(DELTA_NAME);
if (epsilonString != null) {
delta = Double.parseDouble(epsilonString);
} }
} }
......
...@@ -81,17 +81,22 @@ public class PlanarCheck extends Check { ...@@ -81,17 +81,22 @@ public class PlanarCheck extends Check {
private double delta = 0.01; private double delta = 0.01;
@Override @Override
public void init(Map<String, String> parameters, ParserConfiguration config) { public void init(Map<CheckId, Map<String, String>> parameters, ParserConfiguration config) {
if (parameters.containsKey(TYPE)) { Map<String, String> localParameters = parameters.get(getCheckId());
planarCheckType = parameters.get(TYPE).toLowerCase(); if (localParameters == null) {
// no parameters
return;
}
if (localParameters.containsKey(TYPE)) {
planarCheckType = localParameters.get(TYPE).toLowerCase();
} else { } else {
throw new IllegalStateException("Parameter " + TYPE + " is missing from parameters"); throw new IllegalStateException("Parameter " + TYPE + " is missing from parameters");
} }
if (parameters.containsKey(ANGLE_TOLERANCE)) { if (localParameters.containsKey(ANGLE_TOLERANCE)) {
rad = Math.toRadians(Double.parseDouble(parameters.get(ANGLE_TOLERANCE))); rad = Math.toRadians(Double.parseDouble(localParameters.get(ANGLE_TOLERANCE)));
} }
if (parameters.containsKey(DISTANCE_TOLERANCE)) { if (localParameters.containsKey(DISTANCE_TOLERANCE)) {
delta = Double.parseDouble(parameters.get(DISTANCE_TOLERANCE)); delta = Double.parseDouble(localParameters.get(DISTANCE_TOLERANCE));
} }
} }
......
...@@ -29,6 +29,7 @@ import de.hft.stuttgart.citydoctor2.check.Check; ...@@ -29,6 +29,7 @@ import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.GlobalParameters;
import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.RequirementType; import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ResultStatus;
...@@ -59,15 +60,12 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; ...@@ -59,15 +60,12 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
*/ */
public class RingSelfIntCheck extends Check { public class RingSelfIntCheck extends Check {
private static final String EPSILON_NAME = "minVertexDistance"; private static final List<CheckId> dependencies;
// check requirement class for default parameters // check requirement class for default parameters
private double degeneratedRingTolerance = 0.01; private double degeneratedRingTolerance = 0.01;
private double epsilon = Checks.MIN_VERTEX_DISTANCE_DEFAULT; private double epsilon = Checks.MIN_VERTEX_DISTANCE_DEFAULT;
private static final List<CheckId> dependencies;
static { static {
ArrayList<CheckId> deps = new ArrayList<>(); ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS); deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
...@@ -78,13 +76,18 @@ public class RingSelfIntCheck extends Check { ...@@ -78,13 +76,18 @@ public class RingSelfIntCheck extends Check {
@Override @Override
public void init(Map<String, String> parameters, ParserConfiguration config) { public void init(Map<CheckId, Map<String, String>> parameters, ParserConfiguration config) {
String epsilonString = parameters.get(EPSILON_NAME); Map<String, String> localParameters = parameters.get(getCheckId());
if (localParameters == null) {
// no parameters
return;
}
String epsilonString = localParameters.get(GlobalParameters.MIN_VERTEX_DISTANCE);
if (epsilonString != null) { if (epsilonString != null) {
epsilon = Double.parseDouble(epsilonString); epsilon = Double.parseDouble(epsilonString);
} }
if (parameters.containsKey(Requirement.DEGENERATED_RING_TOLERANCE)) { if (localParameters.containsKey(Requirement.DEGENERATED_RING_TOLERANCE)) {
degeneratedRingTolerance = Double.parseDouble(parameters.get(Requirement.DEGENERATED_RING_TOLERANCE)); degeneratedRingTolerance = Double.parseDouble(localParameters.get(Requirement.DEGENERATED_RING_TOLERANCE));
} }
} }
......
...@@ -73,10 +73,16 @@ public class SolidSelfIntCheck extends Check { ...@@ -73,10 +73,16 @@ public class SolidSelfIntCheck extends Check {
} }
@Override @Override
public void init(Map<String, String> parameters, ParserConfiguration config) { public void init(Map<CheckId, Map<String, String>> parameters, ParserConfiguration config) {
if (parameters.containsKey(PlanarCheck.DISTANCE_TOLERANCE)) { Map<String, String> planarParameters = parameters.get(CheckId.C_GE_P_NON_PLANAR);
delta = Double.parseDouble(parameters.get(PlanarCheck.DISTANCE_TOLERANCE)); if (planarParameters == null) {
// no parameters
return;
} }
planarParameters.computeIfPresent(PlanarCheck.DISTANCE_TOLERANCE, (k, v) -> {
delta = Double.parseDouble(v);
return v;
});
} }
@Override @Override
......
...@@ -75,13 +75,18 @@ public class IsWallCheck extends Check { ...@@ -75,13 +75,18 @@ public class IsWallCheck extends Check {
private double upperAngleCos = Math.cos(135 * Math.PI / 180); private double upperAngleCos = Math.cos(135 * Math.PI / 180);
@Override @Override
public void init(Map<String, String> params, ParserConfiguration config) { public void init(Map<CheckId, Map<String, String>> params, ParserConfiguration config) {
String lowerAngleString = params.get(LOWER_ANGLE_NAME); Map<String, String> localParameters = params.get(getCheckId());
if (localParameters == null) {
// no parameters
return;
}
String lowerAngleString = localParameters.get(LOWER_ANGLE_NAME);
if (lowerAngleString != null) { if (lowerAngleString != null) {
lowerAngleCos = Double.parseDouble(lowerAngleString); lowerAngleCos = Double.parseDouble(lowerAngleString);
lowerAngleCos = Math.cos(lowerAngleCos * Math.PI / 180); lowerAngleCos = Math.cos(lowerAngleCos * Math.PI / 180);
} }
String upperAngleString = params.get(UPPER_ANGLE_NAME); String upperAngleString = localParameters.get(UPPER_ANGLE_NAME);
if (upperAngleString != null) { if (upperAngleString != null) {
upperAngleCos = Double.parseDouble(upperAngleString); upperAngleCos = Double.parseDouble(upperAngleString);
upperAngleCos = Math.cos(upperAngleCos * Math.PI / 180); upperAngleCos = Math.cos(upperAngleCos * Math.PI / 180);
......
...@@ -60,8 +60,13 @@ public class RoofSurfaceUnfragmentedCheck extends Check { ...@@ -60,8 +60,13 @@ public class RoofSurfaceUnfragmentedCheck extends Check {
} }
@Override @Override
public void init(Map<String, String> params, ParserConfiguration config) { public void init(Map<CheckId, Map<String, String>> params, ParserConfiguration config) {
String maxAngleString = params.get(MAX_ANGLE_DEVIATION); Map<String, String> localParameters = params.get(getCheckId());
if (localParameters == null) {
// no parameters
return;
}
String maxAngleString = localParameters.get(MAX_ANGLE_DEVIATION);
if (maxAngleString != null) { if (maxAngleString != null) {
maxAngleDeviation = Math.toRadians(Double.parseDouble(maxAngleString)); maxAngleDeviation = Math.toRadians(Double.parseDouble(maxAngleString));
} }
......
...@@ -20,7 +20,9 @@ package de.hft.stuttgart.citydoctor2.checks.util; ...@@ -20,7 +20,9 @@ package de.hft.stuttgart.citydoctor2.checks.util;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.IntStream;
public class CollectionUtils { public class CollectionUtils {
...@@ -40,4 +42,11 @@ public class CollectionUtils { ...@@ -40,4 +42,11 @@ public class CollectionUtils {
return set; return set;
} }
public static <T> List<List<T>> partition(List<T> list, int chunkSize){
return IntStream.iterate(0, i-> i < list.size(), i-> i+ chunkSize)
.mapToObj(i -> list.subList(i, Math.min(i+chunkSize, list.size())))
.toList();
}
} }
...@@ -40,8 +40,6 @@ import org.locationtech.jts.geom.impl.CoordinateArraySequence; ...@@ -40,8 +40,6 @@ import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.locationtech.jts.operation.overlay.OverlayOp; import org.locationtech.jts.operation.overlay.OverlayOp;
import org.locationtech.jts.operation.overlay.snap.SnapIfNeededOverlayOp; import org.locationtech.jts.operation.overlay.snap.SnapIfNeededOverlayOp;
import Jama.EigenvalueDecomposition;
import Jama.Matrix;
import de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection; import de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
...@@ -53,10 +51,8 @@ import de.hft.stuttgart.citydoctor2.edge.EdgePolygon; ...@@ -53,10 +51,8 @@ import de.hft.stuttgart.citydoctor2.edge.EdgePolygon;
import de.hft.stuttgart.citydoctor2.edge.IntersectPlanarPolygons; import de.hft.stuttgart.citydoctor2.edge.IntersectPlanarPolygons;
import de.hft.stuttgart.citydoctor2.edge.MeshSurface; import de.hft.stuttgart.citydoctor2.edge.MeshSurface;
import de.hft.stuttgart.citydoctor2.edge.PolygonPolygonIntersection; import de.hft.stuttgart.citydoctor2.edge.PolygonPolygonIntersection;
import de.hft.stuttgart.citydoctor2.math.CovarianceMatrix;
import de.hft.stuttgart.citydoctor2.math.MovedPolygon; import de.hft.stuttgart.citydoctor2.math.MovedPolygon;
import de.hft.stuttgart.citydoctor2.math.MovedRing; import de.hft.stuttgart.citydoctor2.math.MovedRing;
import de.hft.stuttgart.citydoctor2.math.OrthogonalRegressionPlane;
import de.hft.stuttgart.citydoctor2.math.Plane; import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection; import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection;
import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection.Type; import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection.Type;
...@@ -95,18 +91,7 @@ public class SelfIntersectionUtil { ...@@ -95,18 +91,7 @@ public class SelfIntersectionUtil {
TesselatedPolygon tessPolygon = EarcutTesselator.tesselatePolygon(p); TesselatedPolygon tessPolygon = EarcutTesselator.tesselatePolygon(p);
for (Iterator<Triangle3d> iterator = tessPolygon.getTriangles().iterator(); iterator.hasNext();) { for (Iterator<Triangle3d> iterator = tessPolygon.getTriangles().iterator(); iterator.hasNext();) {
Triangle3d t = iterator.next(); Triangle3d t = iterator.next();
List<Vector3d> vertices = new ArrayList<>(3); if (!t.hasMinExtent(delta)) {
vertices.add(t.getP1());
vertices.add(t.getP2());
vertices.add(t.getP3());
Vector3d centroid = CovarianceMatrix.getCentroid(vertices);
EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid);
Matrix eigenValues = ed.getD();
double[] eigenValuesArray = new double[3];
eigenValuesArray[0] = eigenValues.get(0, 0);
eigenValuesArray[1] = eigenValues.get(1, 1);
eigenValuesArray[2] = eigenValues.get(2, 2);
if (eigenValuesArray[1] < delta) {
iterator.remove(); iterator.remove();
} }
} }
...@@ -448,8 +433,8 @@ public class SelfIntersectionUtil { ...@@ -448,8 +433,8 @@ public class SelfIntersectionUtil {
private static GeometrySelfIntersection doPolygonsIntersect(TesselatedPolygon p1, TesselatedPolygon p2, double epsilon) { private static GeometrySelfIntersection doPolygonsIntersect(TesselatedPolygon p1, TesselatedPolygon p2, double epsilon) {
for (int p1Index = 0; p1Index < p1.getTriangles().size(); p1Index++) { for (int p1Index = 0; p1Index < p1.getTriangles().size(); p1Index++) {
for (int p2Index = 0; p2Index < p2.getTriangles().size(); p2Index++) {
Triangle3d t1 = p1.getTriangles().get(p1Index); Triangle3d t1 = p1.getTriangles().get(p1Index);
for (int p2Index = 0; p2Index < p2.getTriangles().size(); p2Index++) {
Triangle3d t2 = p2.getTriangles().get(p2Index); Triangle3d t2 = p2.getTriangles().get(p2Index);
if (t1.doesIntersect(t2, epsilon)) { if (t1.doesIntersect(t2, epsilon)) {
logger.trace("{} intersects {}", t1, t2); logger.trace("{} intersects {}", t1, t2);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment