Commit 671c277b authored by Riegel's avatar Riegel
Browse files

Style: Reformat code

parent 491b8247
Showing with 1564 additions and 1561 deletions
+1564 -1561
/*- /*-
* Copyright 2022 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2022 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * CityDoctor2 is free software: you can redistribute it and/or modify
...@@ -18,28 +18,16 @@ ...@@ -18,28 +18,16 @@
*/ */
package de.hft.stuttgart.citydoctor2.parser; package de.hft.stuttgart.citydoctor2.parser;
import java.io.*; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import java.nio.charset.StandardCharsets; import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import java.nio.file.Path; import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper;
import java.nio.file.Paths; import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler;
import java.util.ArrayList; import de.hft.stuttgart.citydoctor2.math.Vector3d;
import java.util.List; import de.hft.stuttgart.citydoctor2.utils.Localization;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile; import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile;
import de.hft.stuttgart.quality.QualityADEContext;
import de.hft.stuttgart.quality.QualityADEModule;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
...@@ -69,605 +57,623 @@ import org.locationtech.proj4j.CoordinateReferenceSystem; ...@@ -69,605 +57,623 @@ import org.locationtech.proj4j.CoordinateReferenceSystem;
import org.locationtech.proj4j.ProjCoordinate; import org.locationtech.proj4j.ProjCoordinate;
import org.locationtech.proj4j.proj.Projection; import org.locationtech.proj4j.proj.Projection;
import org.locationtech.proj4j.units.Units; import org.locationtech.proj4j.units.Units;
import org.xml.sax.*; import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xmlobjects.schema.SchemaHandler; import org.xmlobjects.schema.SchemaHandler;
import org.xmlobjects.schema.SchemaHandlerException; import org.xmlobjects.schema.SchemaHandlerException;
import org.xmlobjects.stream.XMLReader; import org.xmlobjects.stream.XMLReader;
import org.xmlobjects.stream.XMLReaderFactory; import org.xmlobjects.stream.XMLReaderFactory;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import javax.xml.XMLConstants;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import javax.xml.namespace.QName;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper; import javax.xml.parsers.ParserConfigurationException;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; import javax.xml.parsers.SAXParser;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import javax.xml.parsers.SAXParserFactory;
import de.hft.stuttgart.citydoctor2.utils.Localization; import javax.xml.transform.Source;
import de.hft.stuttgart.quality.QualityADEContext; import javax.xml.transform.stream.StreamSource;
import de.hft.stuttgart.quality.QualityADEModule; import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Utility class to parse CityGML files. * Utility class to parse CityGML files.
*
* @author Matthias Betz
* *
* @author Matthias Betz
*/ */
public class CityGmlParser { public class CityGmlParser {
private static final String CITY_OBJECT_MEMBER = "cityObjectMember"; private static final String CITY_OBJECT_MEMBER = "cityObjectMember";
private static final String WGS_84 = "EPSG:4326"; private static final String WGS_84 = "EPSG:4326";
private static final Logger logger = LogManager.getLogger(CityGmlParser.class); private static final Logger logger = LogManager.getLogger(CityGmlParser.class);
private static final CRSFactory CRS_FACTORY = new CRSFactory(); private static final CRSFactory CRS_FACTORY = new CRSFactory();
// EPSG:31467 // EPSG:31467
private static final Pattern P_EPSG = Pattern.compile("^(EPSG:\\d+)$"); private static final Pattern P_EPSG = Pattern.compile("^(EPSG:\\d+)$");
// urn:ogc:def:crs,crs:EPSG:6.12:31467,crs:EPSG:6.12:5783 // urn:ogc:def:crs,crs:EPSG:6.12:31467,crs:EPSG:6.12:5783
// or // or
// urn:ogc:def:crs,crs:EPSG::28992 // urn:ogc:def:crs,crs:EPSG::28992
private static final Pattern P_OGC = Pattern.compile("urn:ogc:def:crs,crs:EPSG:[\\d\\.]*:([\\d]+)\\D*"); private static final Pattern P_OGC = Pattern.compile("urn:ogc:def:crs,crs:EPSG:[\\d\\.]*:([\\d]+)\\D*");
private static final Pattern P_OGC2 = Pattern.compile("urn:ogc:def:crs:EPSG:[\\d\\.]*:([\\d]+)\\D*"); private static final Pattern P_OGC2 = Pattern.compile("urn:ogc:def:crs:EPSG:[\\d\\.]*:([\\d]+)\\D*");
// urn:adv:crs:DE_DHDN_3GK3*DE_DHHN92_NH // urn:adv:crs:DE_DHDN_3GK3*DE_DHHN92_NH
// urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH // urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH
private static final Pattern P_URN = Pattern.compile("urn:adv:crs:([^\\*]+)"); private static final Pattern P_URN = Pattern.compile("urn:adv:crs:([^\\*]+)");
private static final SAXParserFactory FACTORY; private static final SAXParserFactory FACTORY;
private static CityGMLContext context; private static CityGMLContext context;
private static List<QName> chunkProperties = new ArrayList<>(); private static List<QName> chunkProperties = new ArrayList<>();
static { static {
System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
FACTORY = SAXParserFactory.newInstance(); FACTORY = SAXParserFactory.newInstance();
try { try {
FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false); FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
} catch (SAXNotRecognizedException | SAXNotSupportedException | ParserConfigurationException e) { } catch (SAXNotRecognizedException | SAXNotSupportedException | ParserConfigurationException e) {
logger.catching(e); logger.catching(e);
} }
chunkProperties.add(new QName(CityGMLConstants.CITYGML_1_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER)); chunkProperties.add(new QName(CityGMLConstants.CITYGML_1_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER));
chunkProperties.add(new QName(CityGMLConstants.CITYGML_2_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER)); chunkProperties.add(new QName(CityGMLConstants.CITYGML_2_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER));
chunkProperties.add(new QName(CityGMLConstants.CITYGML_3_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER)); chunkProperties.add(new QName(CityGMLConstants.CITYGML_3_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER));
} }
private CityGmlParser() { private CityGmlParser() {
} }
public static synchronized CityGMLContext getContext() { public static synchronized CityGMLContext getContext() {
if (context == null) { if (context == null) {
try { try {
context = CityGMLContext.newInstance(CityGmlParser.class.getClassLoader()); context = CityGMLContext.newInstance(CityGmlParser.class.getClassLoader());
// also setup ades // also setup ades
ADERegistry adeRegistry = ADERegistry.getInstance(); ADERegistry adeRegistry = ADERegistry.getInstance();
adeRegistry.loadADE(new QualityADEContext()); adeRegistry.loadADE(new QualityADEContext());
} catch (CityGMLContextException e) { } catch (CityGMLContextException e) {
logger.fatal("Unable to create citygml4j context", e); logger.fatal("Unable to create citygml4j context", e);
throw new IllegalStateException("Unable to create citygml4j context"); throw new IllegalStateException("Unable to create citygml4j context");
} catch (ADEException e) { } catch (ADEException e) {
logger.fatal("Unable to add ADE plugins to citygml4j", e); logger.fatal("Unable to add ADE plugins to citygml4j", e);
throw new IllegalStateException("Unable to add ADE plugins to citygml4j"); throw new IllegalStateException("Unable to add ADE plugins to citygml4j");
} }
} }
return context; return context;
} }
public static CityDoctorModel parseCityGmlFileSilently(String file, ParserConfiguration config) public static CityDoctorModel parseCityGmlFileSilently(String file, ParserConfiguration config)
throws CityGmlParseException, InvalidGmlFileException { throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(file, config, null, null, false); return parseCityGmlFile(file, config, null, null, false);
} }
public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config) public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config)
throws CityGmlParseException, InvalidGmlFileException { throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(file, config, null, null, true); return parseCityGmlFile(file, config, null, null, true);
} }
public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config, ProgressListener l) public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config, ProgressListener l)
throws CityGmlParseException, InvalidGmlFileException { throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(file, config, l, null, true); return parseCityGmlFile(file, config, l, null, true);
} }
public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l, public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l,
GMLValidationHandler handler, boolean verbose) throws CityGmlParseException, InvalidGmlFileException { GMLValidationHandler handler, boolean verbose) throws CityGmlParseException, InvalidGmlFileException {
CityGMLContext context = getContext(); CityGMLContext context = getContext();
Path file = Paths.get(filePath); Path file = Paths.get(filePath);
if (config.getValidate()) { if (config.getValidate()) {
List<String> messages = validateFile(context, handler, file); List<String> messages = validateFile(context, handler, file);
if (!messages.isEmpty()) { if (!messages.isEmpty()) {
throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0)); throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0));
} }
} }
try { try {
parseEpsgCodeFromFile(file, config); parseEpsgCodeFromFile(file, config);
CityGMLInputFactory in = context.createCityGMLInputFactory() CityGMLInputFactory in = context.createCityGMLInputFactory()
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) {
if (l != null) { if (l != null) {
ois.addListener(l::updateProgress); ois.addListener(l::updateProgress);
} }
return readAndKeepFeatures(config, file, in, ois, verbose); return readAndKeepFeatures(config, file, in, ois, verbose);
} }
} catch (CityGMLReadException | IOException e) { } catch (CityGMLReadException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e); throw new CityGmlParseException("Failed to read CityGML file", e);
} }
} }
public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config) public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config)
throws CityGmlParseException, InvalidGmlFileException, IOException { throws CityGmlParseException, InvalidGmlFileException, IOException {
CityGMLContext context = getContext(); CityGMLContext context = getContext();
if (config.getValidate()) { if (config.getValidate()) {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)){ try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
List<String> messages = validateStream(entryFile.getInputStream(),context); List<String> messages = validateStream(entryFile.getInputStream(), context);
if (!messages.isEmpty()) { if (!messages.isEmpty()) {
throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0)); throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0));
} }
} catch (Exception e) { } catch (Exception e) {
throw new CityGmlParseException(e); throw new CityGmlParseException(e);
} }
} }
return decompressAndParseCityGmlEntry(entry, config, context); return decompressAndParseCityGmlEntry(entry, config, context);
} }
public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, CityGMLContext context) public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, CityGMLContext context)
throws CityGmlParseException { throws CityGmlParseException {
return decompressAndParseCityGmlEntry(entry, config, null, context); return decompressAndParseCityGmlEntry(entry, config, null, context);
} }
public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, CityGMLContext context) public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, CityGMLContext context)
throws CityGmlParseException { throws CityGmlParseException {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)){ try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
BufferedInputStream bis = new BufferedInputStream(entryFile.getInputStream()); BufferedInputStream bis = new BufferedInputStream(entryFile.getInputStream());
readEpsgCodeFromInputStream(bis,config); readEpsgCodeFromInputStream(bis, config);
CityGMLInputFactory in = context.createCityGMLInputFactory() CityGMLInputFactory in = context.createCityGMLInputFactory()
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
try(ObservedInputStream ois = new ObservedInputStream(bis, bis.available())){ try (ObservedInputStream ois = new ObservedInputStream(bis, bis.available())) {
if (l != null){ if (l != null) {
ois.addListener(l::updateProgress); ois.addListener(l::updateProgress);
} }
return readAndKeepFeatures(config, entry, in, ois, false); return readAndKeepFeatures(config, entry, in, ois, false);
} }
} catch (CityGMLReadException | IOException e) { } catch (CityGMLReadException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e); throw new CityGmlParseException("Failed to read CityGML file", e);
} catch (Exception e) { } catch (Exception e) {
throw new CityGmlParseException(e);
}
}
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, CityGmlZipEntry entry,
CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException {
return readAndKeepModel(new Citygml3FeatureMapper(config, entry), inputFactory, ois, verbose);
}
private static List<String> validateStream(InputStream vis, CityGMLContext context) throws CityGmlParseException {
GMLValidationHandler handler = new GMLValidationHandler();
try {
BufferedInputStream bis = new BufferedInputStream(vis);
SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler());
readAdditionalSchemaDefinitions(context, bis, schemaHandler);
Source[] schemas = schemaHandler.getSchemas();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Schema schema = schemaFactory.newSchema(schemas);
Validator validator = schema.newValidator();
validator.setErrorHandler(handler);
validator.validate(new StreamSource(bis));
return handler.getMessages();
} catch (SchemaHandlerException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to validate CityGML file", e);
}
}
private static void readAdditionalSchemaDefinitions(CityGMLContext context, BufferedInputStream bis, SchemaHandler schemaHandler)
throws CityGmlParseException {
bis.mark(Integer.MAX_VALUE);
try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects())
.withSchemaHandler(schemaHandler)
.createReader(bis)) {
reader.nextTag();
bis.reset();
} catch (Exception e) {
throw new CityGmlParseException("Failed to read Schema from stream.", e);
}
}
private static void readEpsgCodeFromInputStream(BufferedInputStream bis, ParserConfiguration config) throws CityGmlParseException {
try {
// Mark start position of GML-"file"
bis.mark(Integer.MAX_VALUE);
// Buffer the first 10000 chars of the Stream, EPSG/envelope info should be found within that range
int peekingWidth = Math.min(10000, bis.available());
byte[] buf = new byte[peekingWidth];
bis.read(buf, 0, peekingWidth);
bis.reset();
parseEpsgCodeFromBuffer(buf, config);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e);
}
}
public static void streamCityGml(CityGmlZipEntry entry, ParserConfiguration config, CityGmlConsumer cityObjectConsumer,
String outputFile) throws CityGmlParseException, IOException {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
parseEpsgCodeFromStream(entryFile.getInputStream(), config);
startReadingCityGmlZipEntry(entry, config, null, cityObjectConsumer, outputFile);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML stream", e);
}
}
public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer,
String outputFile) throws CityGmlParseException {
Path f = Paths.get(file);
streamCityGml(f, config, null, cityObjectConsumer, outputFile);
}
public static void streamCityGml(File file, ParserConfiguration parserConfig, CityGmlConsumer cityObjectConsumer,
String outputFile) throws CityGmlParseException {
streamCityGml(file.toPath(), parserConfig, null, cityObjectConsumer, outputFile);
}
public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException {
parseEpsgCodeFromFile(file, config);
startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile);
}
public static CityModel parseOnlyCityModel(File inputFile) throws CityGmlParseException {
try {
CityGMLInputFactory inputFactory = context.createCityGMLInputFactory()
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
try (CityGMLReader reader = inputFactory.createCityGMLReader(inputFile)) {
while (reader.hasNext()) {
AbstractFeature chunk = reader.next();
if (chunk instanceof CityModel cModel) {
cModel.setCityObjectMembers(null);
return cModel;
}
}
}
} catch (CityGMLReadException e) {
throw new CityGmlParseException(e); throw new CityGmlParseException(e);
} }
throw new CityGmlParseException("Did not find any CityModel in CityGML file");
} }
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, CityGmlZipEntry entry, private static void startReadingCityGmlFile(Path file, ParserConfiguration config, ProgressListener l,
CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException { CityGmlConsumer cityObjectConsumer, String outputFile) {
return readAndKeepModel(new Citygml3FeatureMapper(config, entry), inputFactory, ois, verbose); try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) {
} if (l != null) {
ois.addListener(l::updateProgress);
private static List<String> validateStream(InputStream vis, CityGMLContext context) throws CityGmlParseException { }
GMLValidationHandler handler = new GMLValidationHandler(); readAndDiscardFeatures(file, config, ois, cityObjectConsumer, outputFile);
} catch (IOException | CityGMLReadException e) {
try { logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage());
BufferedInputStream bis = new BufferedInputStream(vis); logger.catching(Level.ERROR, e);
SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler()); }
readAdditionalSchemaDefinitions(context, bis, schemaHandler); }
Source[] schemas = schemaHandler.getSchemas();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois,
schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
Schema schema = schemaFactory.newSchema(schemas); Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file);
Validator validator = schema.newValidator(); readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile);
validator.setErrorHandler(handler);
validator.validate(new StreamSource(bis)); }
return handler.getMessages();
} catch (SchemaHandlerException | SAXException | IOException e) { private static void startReadingCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l,
throw new CityGmlParseException("Failed to validate CityGML file", e); CityGmlConsumer cityObjectConsumer, String outputFile) {
} try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry);
} ObservedInputStream ois = new ObservedInputStream(entryFile.getInputStream(), entry.getFileSize())) {
if (l != null) {
private static void readAdditionalSchemaDefinitions(CityGMLContext context, BufferedInputStream bis, SchemaHandler schemaHandler) ois.addListener(l::updateProgress);
throws CityGmlParseException { }
bis.mark(Integer.MAX_VALUE); streamAndDiscardFeatures(entry, config, ois, cityObjectConsumer, outputFile);
try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()) } catch (IOException | CityGMLReadException e) {
.withSchemaHandler(schemaHandler) logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage());
.createReader(bis)) { logger.catching(Level.ERROR, e);
reader.nextTag(); }
bis.reset(); }
} catch (Exception e) {
throw new CityGmlParseException("Failed to read Schema from stream.", e); private static void streamAndDiscardFeatures(CityGmlZipEntry entry, ParserConfiguration config, ObservedInputStream ois,
} CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
} Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, entry);
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile);
private static void readEpsgCodeFromInputStream(BufferedInputStream bis, ParserConfiguration config) throws CityGmlParseException { }
try{
// Mark start position of GML-"file" private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois,
bis.mark(Integer.MAX_VALUE); CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
// Buffer the first 10000 chars of the Stream, EPSG/envelope info should be found within that range getContext();
int peekingWidth = Math.min(10000, bis.available()); CityGMLInputFactory inputFactory = context.createCityGMLInputFactory()
byte[] buf = new byte[peekingWidth]; .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
bis.read(buf, 0, peekingWidth); CityGMLChunkWriter writer = null;
bis.reset(); try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) {
parseEpsgCodeFromBuffer(buf, config);
} catch (ParserConfigurationException | SAXException | IOException e) { CityDoctorModel model = mapper.getModel();
throw new CityGmlParseException("Failed to read CityGML file", e); boolean isInitialized = false;
} while (reader.hasNext()) {
AbstractFeature chunk = reader.next();
} if (writer == null) {
writer = createCityModelWriter(outputFile, reader);
public static void streamCityGml(CityGmlZipEntry entry, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, }
String outputFile) throws CityGmlParseException, IOException { if (!isInitialized && writer != null && reader.getParentInfo() != null
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { && reader.getParentInfo().getTypeName().getLocalPart().equals("CityModel")) {
parseEpsgCodeFromStream(entryFile.getInputStream(), config); FeatureInfo parentInfo = reader.getParentInfo();
startReadingCityGmlZipEntry(entry, config, null,cityObjectConsumer, outputFile); writer.withCityModelInfo(parentInfo);
} catch (ParserConfigurationException | SAXException | IOException e) { isInitialized = true;
throw new CityGmlParseException("Failed to read CityGML stream", e); }
} if (chunk instanceof AbstractCityObject ag) {
} ag.accept(mapper);
drainCityModel(model, cityObjectConsumer);
public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, writeAbstractCityObject(writer, ag);
String outputFile) throws CityGmlParseException { } else if (chunk instanceof CityModel cModel) {
Path f = Paths.get(file); cModel.setCityObjectMembers(null);
streamCityGml(f, config, null, cityObjectConsumer, outputFile); mapper.setCityModel(cModel);
} cityObjectConsumer.accept(cModel);
writeCityModel(writer, cModel);
public static void streamCityGml(File file, ParserConfiguration parserConfig, CityGmlConsumer cityObjectConsumer, } else if (writer != null) {
String outputFile) throws CityGmlParseException { writer.writeMember(chunk);
streamCityGml(file.toPath(), parserConfig, null, cityObjectConsumer, outputFile); }
} }
// end of stream
public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l, logger.debug("End of gml file stream");
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException { } catch (CityGMLReadException e) {
parseEpsgCodeFromFile(file, config); logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage(), e);
startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile); } catch (CityGMLWriteException e) {
} logger.error(Localization.getText("CityGmlParser.errorWritingGmlFile"), e.getMessage(), e);
} finally {
public static CityModel parseOnlyCityModel(File inputFile) throws CityGmlParseException { if (writer != null) {
try { try {
CityGMLInputFactory inputFactory = context.createCityGMLInputFactory() writer.close();
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); } catch (CityGMLWriteException e) {
try (CityGMLReader reader = inputFactory.createCityGMLReader(inputFile)) { // ignore
while (reader.hasNext()) { }
AbstractFeature chunk = reader.next(); }
if (chunk instanceof CityModel cModel) { }
cModel.setCityObjectMembers(null); }
return cModel;
} private static void writeCityModel(CityGMLChunkWriter writer, CityModel cModel) {
} if (writer != null) {
} for (ADEProperty genEle : cModel.getADEProperties()) {
} catch (CityGMLReadException e) { writer.getCityModelInfo().addADEProperty(genEle);
throw new CityGmlParseException(e); }
} }
throw new CityGmlParseException("Did not find any CityModel in CityGML file"); }
}
private static void writeAbstractCityObject(CityGMLChunkWriter writer, AbstractCityObject ag)
private static void startReadingCityGmlFile(Path file, ParserConfiguration config, ProgressListener l, throws CityGMLWriteException {
CityGmlConsumer cityObjectConsumer, String outputFile) { if (writer != null) {
try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { writer.writeMember(ag);
if (l != null) { }
ois.addListener(l::updateProgress); }
}
readAndDiscardFeatures(file, config, ois, cityObjectConsumer, outputFile); private static CityGMLChunkWriter createCityModelWriter(String outputFile, CityGMLReader reader)
} catch (IOException | CityGMLReadException e) { throws CityGMLWriteException {
logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage()); if (outputFile == null) {
logger.catching(Level.ERROR, e); return null;
} }
} CityGMLContext gmlContext = CityGmlParser.getContext();
CityGMLVersion version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI());
private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois, CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(version);
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { CityGMLChunkWriter writer = factory.createCityGMLChunkWriter(new File(outputFile),
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file); StandardCharsets.UTF_8.name());
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); writer.withPrefix("qual", QualityADEModule.NAMESPACE_URI);
writer.withSchemaLocation(QualityADEModule.NAMESPACE_URI, QualityADEModule.NAMESPACE_URI + "/qualityAde.xsd");
} writer.withIndent(" ");
writer.withDefaultPrefixes();
private static void startReadingCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, writer.withDefaultSchemaLocations();
CityGmlConsumer cityObjectConsumer, String outputFile) { return writer;
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry); }
ObservedInputStream ois = new ObservedInputStream(entryFile.getInputStream(), entry.getFileSize())){
if (l != null) { private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file,
ois.addListener(l::updateProgress); CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException {
} return readAndKeepModel(new Citygml3FeatureMapper(config, file), inputFactory, ois, verbose);
streamAndDiscardFeatures(entry, config, ois, cityObjectConsumer, outputFile); }
} catch (IOException | CityGMLReadException e) {
logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage()); private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory,
logger.catching(Level.ERROR, e); ObservedInputStream ois, boolean verbose) throws CityGMLReadException {
} try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) {
} CityGMLVersion version = null;
// model is read in chunked mode
private static void streamAndDiscardFeatures(CityGmlZipEntry entry, ParserConfiguration config, ObservedInputStream ois, // object members are replaced by href in model
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { // need to remove the refs and re-add unparsed objects
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, entry); List<AbstractCityObject> acos = new ArrayList<>();
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); while (reader.hasNext()) {
} AbstractFeature chunk = reader.next();
version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI());
private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois, if (chunk instanceof CityModel cModel) {
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { cModel.setCityObjectMembers(null);
getContext(); mapper.setCityModel(cModel);
CityGMLInputFactory inputFactory = context.createCityGMLInputFactory() mapper.setCityGMLVersion(version);
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); } else if (chunk instanceof AbstractCityObject aco) {
CityGMLChunkWriter writer = null; acos.add(aco);
try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { aco.accept(mapper);
}
CityDoctorModel model = mapper.getModel(); }
boolean isInitialized = false;
while (reader.hasNext()) { if (mapper.getModel().getCityModel() == null) {
AbstractFeature chunk = reader.next(); // file does not contain a city model?
if (writer == null) { // create it for now
writer = createCityModelWriter(outputFile, reader); mapper.setCityModel(new CityModel());
} }
if (!isInitialized && writer != null && reader.getParentInfo() != null CityModel cModel = mapper.getModel().getCityModel();
&& reader.getParentInfo().getTypeName().getLocalPart().equals("CityModel")) {
FeatureInfo parentInfo = reader.getParentInfo(); // remove those that should have been parsed
writer.withCityModelInfo(parentInfo); List<AbstractCityObject> parsedCityObjects = mapper.getModel().createFeatureStream()
isInitialized = true; .map(CityObject::getGmlObject).toList();
} acos.removeAll(parsedCityObjects);
if (chunk instanceof AbstractCityObject ag) { // re-add all not parsed objects
ag.accept(mapper); for (AbstractCityObject aco : acos) {
drainCityModel(model, cityObjectConsumer); cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
writeAbstractCityObject(writer, ag); }
} else if (chunk instanceof CityModel cModel) { if (logger.isInfoEnabled() && verbose) {
cModel.setCityObjectMembers(null); logger.info(Localization.getText("CityGmlParser.parsedObjects"),
mapper.setCityModel(cModel); mapper.getModel().getNumberOfFeatures());
cityObjectConsumer.accept(cModel); }
writeCityModel(writer, cModel); mapper.setCityGMLVersion(version);
} else if (writer != null) { return mapper.getModel();
writer.writeMember(chunk); }
} }
}
// end of stream private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config) throws CityGmlParseException {
logger.debug("End of gml file stream"); try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) {
} catch (CityGMLReadException e) { parseEpsgCodeFromStream(bis, config);
logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage(), e); } catch (ParserConfigurationException | SAXException | IOException e) {
} catch (CityGMLWriteException e) { throw new CityGmlParseException("Failed to read CityGML file", e);
logger.error(Localization.getText("CityGmlParser.errorWritingGmlFile"), e.getMessage(), e); }
} finally { }
if (writer != null) {
try { private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config)
writer.close(); throws ParserConfigurationException, SAXException {
} catch (CityGMLWriteException e) { SAXParser parser = FACTORY.newSAXParser();
// ignore CityGmlHandler handler = new CityGmlHandler();
} try {
} parser.parse(new InputSource(is), handler);
} } catch (EnvelopeFoundException e) {
} try {
parseCoordinateSystem(config, handler);
private static void writeCityModel(CityGMLChunkWriter writer, CityModel cModel) { } catch (Exception e2) {
if (writer != null) { logEpsgParseError(e2);
for (ADEProperty genEle : cModel.getADEProperties()) { }
writer.getCityModelInfo().addADEProperty(genEle); } catch (Exception e) {
} logEpsgParseError(e);
} }
} }
private static void writeAbstractCityObject(CityGMLChunkWriter writer, AbstractCityObject ag) private static void parseEpsgCodeFromBuffer(byte[] buffer, ParserConfiguration config)
throws CityGMLWriteException { throws ParserConfigurationException, SAXException {
if (writer != null) { InputStream is = new ByteArrayInputStream(buffer);
writer.writeMember(ag); SAXParser parser = FACTORY.newSAXParser();
} CityGmlHandler handler = new CityGmlHandler();
} try {
parser.parse(new InputSource(is), handler);
private static CityGMLChunkWriter createCityModelWriter(String outputFile, CityGMLReader reader) } catch (EnvelopeFoundException e) {
throws CityGMLWriteException { try {
if (outputFile == null) { parseCoordinateSystem(config, handler);
return null;
} } catch (Exception e2) {
CityGMLContext gmlContext = CityGmlParser.getContext(); logEpsgParseError(e2);
CityGMLVersion version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); }
CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(version); } catch (SAXParseException spe) {
CityGMLChunkWriter writer = factory.createCityGMLChunkWriter(new File(outputFile), // suppress XML document structure warning
StandardCharsets.UTF_8.name()); if (!spe.getMessage().matches("XML document structures must start and end within the same entity.")) {
writer.withPrefix("qual", QualityADEModule.NAMESPACE_URI); logEpsgParseError(spe);
writer.withSchemaLocation(QualityADEModule.NAMESPACE_URI, QualityADEModule.NAMESPACE_URI + "/qualityAde.xsd"); }
writer.withIndent(" "); } catch (Exception e) {
writer.withDefaultPrefixes(); logEpsgParseError(e);
writer.withDefaultSchemaLocations(); }
return writer; }
}
private static void logEpsgParseError(Exception e) {
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file, logger.debug("Exception while parsing for EPSG code", e);
CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException { if (logger.isWarnEnabled()) {
return readAndKeepModel(new Citygml3FeatureMapper(config, file), inputFactory, ois, verbose); logger.warn(Localization.getText("CityGmlParser.noEPSG"));
} }
}
private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory,
ObservedInputStream ois, boolean verbose) throws CityGMLReadException{ private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler) {
try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { if (handler.getEpsg() == null) {
CityGMLVersion version = null; return;
// model is read in chunked mode }
// object members are replaced by href in model CoordinateReferenceSystem crs = crsFromSrsName(handler.getEpsg());
// need to remove the refs and re-add unparsed objects if (crs == null) {
List<AbstractCityObject> acos = new ArrayList<>(); // could not find a coordinate system for srsName
while (reader.hasNext()) { // assuming metric system
AbstractFeature chunk = reader.next(); return;
version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); }
if (chunk instanceof CityModel cModel) { if (crs.getProjection().getUnits() == Units.METRES) {
cModel.setCityObjectMembers(null); // coordinate system is in meters, do not convert
mapper.setCityModel(cModel); if (logger.isInfoEnabled()) {
mapper.setCityGMLVersion(version); logger.info(Localization.getText("CityGmlParser.noConversionNeeded"));
} else if (chunk instanceof AbstractCityObject aco) { }
acos.add(aco); return;
aco.accept(mapper); }
} parseMeterConversion(config, crs);
} Vector3d low = handler.getLowerCorner();
Vector3d up = handler.getUpperCorner();
if (mapper.getModel().getCityModel() == null) { double centerLong = low.getX() + ((up.getX() - low.getX()) / 2);
// file does not contain a city model? double centerLat = low.getY() + ((up.getY() - low.getY()) / 2);
// create it for now if (!crs.getName().equals(WGS_84)) {
mapper.setCityModel(new CityModel()); // need to convert coordinates first to WGS84, then find UTM Zone
} CoordinateReferenceSystem wgs84 = crsFromSrsName(WGS_84);
CityModel cModel = mapper.getModel().getCityModel(); ProjCoordinate p1 = new ProjCoordinate();
p1.setValue(centerLong, centerLat);
// remove those that should have been parsed ProjCoordinate p2 = new ProjCoordinate();
List<AbstractCityObject> parsedCityObjects = mapper.getModel().createFeatureStream() BasicCoordinateTransform bct = new BasicCoordinateTransform(crs, wgs84);
.map(CityObject::getGmlObject).toList(); bct.transform(p1, p2);
acos.removeAll(parsedCityObjects); centerLong = p2.x;
// re-add all not parsed objects centerLat = p2.y;
for (AbstractCityObject aco : acos) { }
cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); int zone = (int) (31 + Math.round(centerLong / 6));
} CoordinateReferenceSystem utm;
if (logger.isInfoEnabled() && verbose) { if (centerLat < 0) {
logger.info(Localization.getText("CityGmlParser.parsedObjects"), // south
mapper.getModel().getNumberOfFeatures()); logger.info("Converting coordiante system to UTM zone {}S", zone);
} utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south");
mapper.setCityGMLVersion(version); } else {
return mapper.getModel(); // north
} logger.info("Converting coordiante system to UTM zone {}N", zone);
} utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone);
}
private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config) throws CityGmlParseException { config.setCoordinateSystem(crs, utm);
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) { }
parseEpsgCodeFromStream(bis, config);
} catch (ParserConfigurationException | SAXException | IOException e) { private static void parseMeterConversion(ParserConfiguration config, CoordinateReferenceSystem crs) {
throw new CityGmlParseException("Failed to read CityGML file", e); Projection projection = crs.getProjection();
} double fromMetres = projection.getFromMetres();
} if (fromMetres > 0) {
// also transform height information
private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config) config.setFromMeters(fromMetres);
throws ParserConfigurationException, SAXException { } else {
SAXParser parser = FACTORY.newSAXParser(); config.setFromMeters(1.0);
CityGmlHandler handler = new CityGmlHandler(); }
try { }
parser.parse(new InputSource(is), handler);
} catch (EnvelopeFoundException e) { /**
try { * The srsName (The name by which this reference system is identified) inside
parseCoordinateSystem(config, handler); * the CityGML file can have multiple formats. This method tries to parse the
} catch (Exception e2) { * string and detect the corresponding reference system. If it is found, it
logEpsgParseError(e2); * returns a proj4j.CoordinateReferenceSystem. It throws an
} * IllegalArgumentException otherwise.
} catch (Exception e) { * <p>
logEpsgParseError(e); * This method should be able to parse any EPSG id : e.g. "EPSG:1234". German
} * Citygmls might also have "DE_DHDN_3GK3" or "ETRS89_UTM32" as srsName, so
} * those are also included. It isn't guaranteed that those formats are correctly
* parsed, though.
private static void parseEpsgCodeFromBuffer(byte[] buffer, ParserConfiguration config) * <p>
throws ParserConfigurationException, SAXException { * The EPSG ids and parameters are defined in resources ('nad/epsg') inside
InputStream is = new ByteArrayInputStream(buffer); * proj4j-0.1.0.jar. Some EPSG ids are missing though, e.g. 7415
SAXParser parser = FACTORY.newSAXParser(); *
CityGmlHandler handler = new CityGmlHandler(); * @param srsName
try { * @return CoordinateReferenceSystem
parser.parse(new InputSource(is), handler); */
} catch (EnvelopeFoundException e) { private static CoordinateReferenceSystem crsFromSrsName(String srsName) {
try { srsName = srsName.trim();
parseCoordinateSystem(config, handler); Matcher mEPSG = P_EPSG.matcher(srsName);
if (mEPSG.find()) {
} catch (Exception e2) { if ("EPSG:4979".contentEquals(srsName)) {
logEpsgParseError(e2); srsName = "EPSG:4236";
} } else if ("EPSG:7415".contentEquals(srsName)) {
}catch (SAXParseException spe){ return CRS_FACTORY.createFromParameters("EPSG:7415",
// suppress XML document structure warning "+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.417,50.3319,465.552,-0.398957,0.343988,-1.8774,4.0725 +units=m +no_defs");
if (!spe.getMessage().matches("XML document structures must start and end within the same entity.")){ }
logEpsgParseError(spe); return CRS_FACTORY.createFromName(srsName);
} }
}catch (Exception e) {
logEpsgParseError(e); Matcher mOGC = P_OGC.matcher(srsName);
} if (mOGC.find()) {
} return CRS_FACTORY.createFromName("EPSG:" + mOGC.group(1));
}
private static void logEpsgParseError(Exception e){ Matcher mOGC2 = P_OGC2.matcher(srsName);
logger.debug("Exception while parsing for EPSG code", e); if (mOGC2.find()) {
if (logger.isWarnEnabled()) { return CRS_FACTORY.createFromName("EPSG:" + mOGC2.group(1));
logger.warn(Localization.getText("CityGmlParser.noEPSG")); }
} Matcher mURN = P_URN.matcher(srsName);
} // NOTE: Could use a HashMap if the switch/case becomes too long.
if (mURN.find()) {
private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler) {
if (handler.getEpsg() == null) {
return;
}
CoordinateReferenceSystem crs = crsFromSrsName(handler.getEpsg());
if (crs == null) {
// could not find a coordinate system for srsName
// assuming metric system
return;
}
if (crs.getProjection().getUnits() == Units.METRES) {
// coordinate system is in meters, do not convert
if (logger.isInfoEnabled()) {
logger.info(Localization.getText("CityGmlParser.noConversionNeeded"));
}
return;
}
parseMeterConversion(config, crs);
Vector3d low = handler.getLowerCorner();
Vector3d up = handler.getUpperCorner();
double centerLong = low.getX() + ((up.getX() - low.getX()) / 2);
double centerLat = low.getY() + ((up.getY() - low.getY()) / 2);
if (!crs.getName().equals(WGS_84)) {
// need to convert coordinates first to WGS84, then find UTM Zone
CoordinateReferenceSystem wgs84 = crsFromSrsName(WGS_84);
ProjCoordinate p1 = new ProjCoordinate();
p1.setValue(centerLong, centerLat);
ProjCoordinate p2 = new ProjCoordinate();
BasicCoordinateTransform bct = new BasicCoordinateTransform(crs, wgs84);
bct.transform(p1, p2);
centerLong = p2.x;
centerLat = p2.y;
}
int zone = (int) (31 + Math.round(centerLong / 6));
CoordinateReferenceSystem utm;
if (centerLat < 0) {
// south
logger.info("Converting coordiante system to UTM zone {}S", zone);
utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south");
} else {
// north
logger.info("Converting coordiante system to UTM zone {}N", zone);
utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone);
}
config.setCoordinateSystem(crs, utm);
}
private static void parseMeterConversion(ParserConfiguration config, CoordinateReferenceSystem crs) {
Projection projection = crs.getProjection();
double fromMetres = projection.getFromMetres();
if (fromMetres > 0) {
// also transform height information
config.setFromMeters(fromMetres);
} else {
config.setFromMeters(1.0);
}
}
/**
* The srsName (The name by which this reference system is identified) inside
* the CityGML file can have multiple formats. This method tries to parse the
* string and detect the corresponding reference system. If it is found, it
* returns a proj4j.CoordinateReferenceSystem. It throws an
* IllegalArgumentException otherwise.
*
* This method should be able to parse any EPSG id : e.g. "EPSG:1234". German
* Citygmls might also have "DE_DHDN_3GK3" or "ETRS89_UTM32" as srsName, so
* those are also included. It isn't guaranteed that those formats are correctly
* parsed, though.
*
* The EPSG ids and parameters are defined in resources ('nad/epsg') inside
* proj4j-0.1.0.jar. Some EPSG ids are missing though, e.g. 7415
*
* @param srsName
* @return CoordinateReferenceSystem
*/
private static CoordinateReferenceSystem crsFromSrsName(String srsName) {
srsName = srsName.trim();
Matcher mEPSG = P_EPSG.matcher(srsName);
if (mEPSG.find()) {
if ("EPSG:4979".contentEquals(srsName)) {
srsName = "EPSG:4236";
} else if ("EPSG:7415".contentEquals(srsName)) {
return CRS_FACTORY.createFromParameters("EPSG:7415",
"+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.417,50.3319,465.552,-0.398957,0.343988,-1.8774,4.0725 +units=m +no_defs");
}
return CRS_FACTORY.createFromName(srsName);
}
Matcher mOGC = P_OGC.matcher(srsName);
if (mOGC.find()) {
return CRS_FACTORY.createFromName("EPSG:" + mOGC.group(1));
}
Matcher mOGC2 = P_OGC2.matcher(srsName);
if (mOGC2.find()) {
return CRS_FACTORY.createFromName("EPSG:" + mOGC2.group(1));
}
Matcher mURN = P_URN.matcher(srsName);
// NOTE: Could use a HashMap if the switch/case becomes too long.
if (mURN.find()) {
return switch (mURN.group(1)) { return switch (mURN.group(1)) {
case "DE_DHDN_3GK2" -> CRS_FACTORY.createFromName("EPSG:31466"); case "DE_DHDN_3GK2" -> CRS_FACTORY.createFromName("EPSG:31466");
case "DE_DHDN_3GK3" -> CRS_FACTORY.createFromName("EPSG:31467"); case "DE_DHDN_3GK3" -> CRS_FACTORY.createFromName("EPSG:31467");
...@@ -676,59 +682,59 @@ public class CityGmlParser { ...@@ -676,59 +682,59 @@ public class CityGmlParser {
case "ETRS89_UTM32" -> CRS_FACTORY.createFromName("EPSG:25832"); case "ETRS89_UTM32" -> CRS_FACTORY.createFromName("EPSG:25832");
default -> null; default -> null;
}; };
} }
if (srsName.equals("http://www.opengis.net/def/crs/EPSG/0/6697")) { if (srsName.equals("http://www.opengis.net/def/crs/EPSG/0/6697")) {
return CRS_FACTORY.createFromParameters("EPSG:6697", "+proj=longlat +ellps=GRS80 +no_defs +axis=neu"); return CRS_FACTORY.createFromParameters("EPSG:6697", "+proj=longlat +ellps=GRS80 +no_defs +axis=neu");
} }
return null; return null;
} }
private static List<String> validateFile(CityGMLContext context, GMLValidationHandler handler, Path file) private static List<String> validateFile(CityGMLContext context, GMLValidationHandler handler, Path file)
throws CityGmlParseException { throws CityGmlParseException {
if (handler == null) { if (handler == null) {
handler = new GMLValidationHandler(); handler = new GMLValidationHandler();
} }
try { try {
SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler()); SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler());
readAdditionalSchemaDefinitions(context, file, schemaHandler); readAdditionalSchemaDefinitions(context, file, schemaHandler);
Source[] schemas = schemaHandler.getSchemas(); Source[] schemas = schemaHandler.getSchemas();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Schema schema = schemaFactory.newSchema(schemas); Schema schema = schemaFactory.newSchema(schemas);
Validator validator = schema.newValidator(); Validator validator = schema.newValidator();
validator.setErrorHandler(handler); validator.setErrorHandler(handler);
validator.validate(new StreamSource(file.toFile())); validator.validate(new StreamSource(file.toFile()));
return handler.getMessages(); return handler.getMessages();
} catch (SchemaHandlerException | SAXException | IOException e) { } catch (SchemaHandlerException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to validate CityGML file", e); throw new CityGmlParseException("Failed to validate CityGML file", e);
} }
} }
private static void readAdditionalSchemaDefinitions(CityGMLContext context, Path file, SchemaHandler schemaHandler) private static void readAdditionalSchemaDefinitions(CityGMLContext context, Path file, SchemaHandler schemaHandler)
throws CityGmlParseException { throws CityGmlParseException {
try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()) try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects())
.withSchemaHandler(schemaHandler) .withSchemaHandler(schemaHandler)
.createReader(file)) { .createReader(file)) {
reader.nextTag(); reader.nextTag();
} catch (Exception e) { } catch (Exception e) {
throw new CityGmlParseException("Failed to read file " + file.toAbsolutePath() + ".", e); throw new CityGmlParseException("Failed to read file " + file.toAbsolutePath() + ".", e);
} }
} }
private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) { private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) {
drainCityObjectList(model.getBuildings(), cityObjectConsumer); drainCityObjectList(model.getBuildings(), cityObjectConsumer);
drainCityObjectList(model.getBridges(), cityObjectConsumer); drainCityObjectList(model.getBridges(), cityObjectConsumer);
drainCityObjectList(model.getVegetation(), cityObjectConsumer); drainCityObjectList(model.getVegetation(), cityObjectConsumer);
drainCityObjectList(model.getLand(), cityObjectConsumer); drainCityObjectList(model.getLand(), cityObjectConsumer);
drainCityObjectList(model.getTransportation(), cityObjectConsumer); drainCityObjectList(model.getTransportation(), cityObjectConsumer);
drainCityObjectList(model.getWater(), cityObjectConsumer); drainCityObjectList(model.getWater(), cityObjectConsumer);
} }
private static void drainCityObjectList(List<? extends CityObject> objects, CityGmlConsumer cityObjectConsumer) { private static void drainCityObjectList(List<? extends CityObject> objects, CityGmlConsumer cityObjectConsumer) {
for (CityObject co : objects) { for (CityObject co : objects) {
cityObjectConsumer.accept(co); cityObjectConsumer.accept(co);
} }
objects.clear(); objects.clear();
} }
} }
...@@ -5,12 +5,14 @@ import de.hft.stuttgart.citydoctor2.utils.ArchivePacker; ...@@ -5,12 +5,14 @@ import de.hft.stuttgart.citydoctor2.utils.ArchivePacker;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.*; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
...@@ -32,8 +34,7 @@ public class CityGmlZipArchive implements Serializable { ...@@ -32,8 +34,7 @@ public class CityGmlZipArchive implements Serializable {
CityGmlZipArchive cgmlArchive = new CityGmlZipArchive(Path.of(zipFile)); CityGmlZipArchive cgmlArchive = new CityGmlZipArchive(Path.of(zipFile));
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) { try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
ZipEntry ze; ZipEntry ze;
while ((ze = zis.getNextEntry()) != null ) while ((ze = zis.getNextEntry()) != null) {
{
if (ze.isDirectory()) { if (ze.isDirectory()) {
continue; continue;
} }
...@@ -49,9 +50,9 @@ public class CityGmlZipArchive implements Serializable { ...@@ -49,9 +50,9 @@ public class CityGmlZipArchive implements Serializable {
} }
} }
public void mountArchive(ParserConfiguration config){ public void mountArchive(ParserConfiguration config) {
try (ZipFile zip = new ZipFile(archivePath.toFile())) { try (ZipFile zip = new ZipFile(archivePath.toFile())) {
for (CityGmlZipEntry entry : entries){ for (CityGmlZipEntry entry : entries) {
entry.loadEntry(config); entry.loadEntry(config);
} }
} catch (IOException e) { } catch (IOException e) {
...@@ -59,9 +60,9 @@ public class CityGmlZipArchive implements Serializable { ...@@ -59,9 +60,9 @@ public class CityGmlZipArchive implements Serializable {
} }
} }
private CityGmlZipArchive(Path archivePath){ private CityGmlZipArchive(Path archivePath) {
this.archivePath = archivePath; this.archivePath = archivePath;
this.archiveNameRE = archivePath.getFileName().toString().replace(".zip","") + File.separator; this.archiveNameRE = archivePath.getFileName().toString().replace(".zip", "") + File.separator;
} }
private void setEntries(List<CityGmlZipEntry> entries) { private void setEntries(List<CityGmlZipEntry> entries) {
...@@ -69,16 +70,16 @@ public class CityGmlZipArchive implements Serializable { ...@@ -69,16 +70,16 @@ public class CityGmlZipArchive implements Serializable {
entries.forEach(e -> e.setArchive(this)); entries.forEach(e -> e.setArchive(this));
} }
public void exportToZipFile(String path) { public void exportToZipFile(String path) {
ArchivePacker.packArchive(path, this); ArchivePacker.packArchive(path, this);
} }
public CityGmlZipEntry getEntry(String fileName) { public CityGmlZipEntry getEntry(String fileName) {
fileName = stripArchivePath(fileName); fileName = stripArchivePath(fileName);
for(CityGmlZipEntry entry : entries){ for (CityGmlZipEntry entry : entries) {
String entryName = stripArchivePath(entry.getFileName()); String entryName = stripArchivePath(entry.getFileName());
if(entryName.equals(fileName)){ if (entryName.equals(fileName)) {
return entry; return entry;
} }
} }
......
...@@ -27,39 +27,39 @@ public class CityGmlZipEntry implements Serializable { ...@@ -27,39 +27,39 @@ public class CityGmlZipEntry implements Serializable {
private static final long MB = 1024 * 1024L; private static final long MB = 1024 * 1024L;
private ZipEntryErrorType errorType = null; private ZipEntryErrorType errorType = null;
public static CityGmlZipEntry of(ZipEntry entry,CityGmlZipArchive parentArchive, ParserConfiguration config){ public static CityGmlZipEntry of(ZipEntry entry, CityGmlZipArchive parentArchive, ParserConfiguration config) {
CityGmlZipEntry ze = CityGmlZipEntry.register(entry, parentArchive); CityGmlZipEntry ze = CityGmlZipEntry.register(entry, parentArchive);
ze.loadEntry(config); ze.loadEntry(config);
return ze; return ze;
} }
public void loadEntry(ParserConfiguration config){ public void loadEntry(ParserConfiguration config) {
if (decompressed){ if (decompressed) {
return; return;
} }
if (errorType != null){ if (errorType != null) {
logger.warn("Tried loading erroneous CityGmlZipEntry"); logger.warn("Tried loading erroneous CityGmlZipEntry");
return; return;
} }
try{ try {
this.model = CityGmlParser.parseCityGmlZipEntry(this, config); this.model = CityGmlParser.parseCityGmlZipEntry(this, config);
this.decompressed = true; this.decompressed = true;
} catch (CityGmlParseException | InvalidGmlFileException e) { } catch (CityGmlParseException | InvalidGmlFileException e) {
logger.error(e); logger.error(e);
this.errorType = ZipEntryErrorType.INVALID_CITY_GML_FILE; this.errorType = ZipEntryErrorType.INVALID_CITY_GML_FILE;
} catch (IOException e){ } catch (IOException e) {
logger.error(e); logger.error(e);
this.errorType = ZipEntryErrorType.IO_ERROR; this.errorType = ZipEntryErrorType.IO_ERROR;
} }
} }
public static CityGmlZipEntry register(ZipEntry entry, CityGmlZipArchive parentArchive){ public static CityGmlZipEntry register(ZipEntry entry, CityGmlZipArchive parentArchive) {
CityGmlZipEntry cgzEntry = new CityGmlZipEntry(entry, parentArchive,false); CityGmlZipEntry cgzEntry = new CityGmlZipEntry(entry, parentArchive, false);
try{ try {
if (!cgzEntry.entrySizeWithinMemoryLimits()) { if (!cgzEntry.entrySizeWithinMemoryLimits()) {
cgzEntry.errorType = ZipEntryErrorType.EXCESSIVE_FILESIZE; cgzEntry.errorType = ZipEntryErrorType.EXCESSIVE_FILESIZE;
} }
} catch (IOException e){ } catch (IOException e) {
logger.error(e); logger.error(e);
cgzEntry.errorType = ZipEntryErrorType.IO_ERROR; cgzEntry.errorType = ZipEntryErrorType.IO_ERROR;
} }
...@@ -67,11 +67,11 @@ public class CityGmlZipEntry implements Serializable { ...@@ -67,11 +67,11 @@ public class CityGmlZipEntry implements Serializable {
} }
private boolean entrySizeWithinMemoryLimits() throws IOException { private boolean entrySizeWithinMemoryLimits() throws IOException {
long memoryLimit = (long) Math.ceil(((double) Runtime.getRuntime().maxMemory() / MB)*0.9); long memoryLimit = (long) Math.ceil(((double) Runtime.getRuntime().maxMemory() / MB) * 0.9);
if (fileSize == -1L) { if (fileSize == -1L) {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(this)){ try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(this)) {
long filesize = entryFile.getFileSize(); long filesize = entryFile.getFileSize();
if (filesize != -1){ if (filesize != -1) {
this.fileSize = filesize; this.fileSize = filesize;
} else { } else {
return false; return false;
...@@ -83,9 +83,9 @@ public class CityGmlZipEntry implements Serializable { ...@@ -83,9 +83,9 @@ public class CityGmlZipEntry implements Serializable {
return memoryLimit > fileSize; return memoryLimit > fileSize;
} }
protected CityGmlZipEntry(ZipEntry entry,CityGmlZipArchive parentArchive, boolean decompressed) { protected CityGmlZipEntry(ZipEntry entry, CityGmlZipArchive parentArchive, boolean decompressed) {
this.fileName = entry.getName(); this.fileName = entry.getName();
if (entry.getSize() != -1){ if (entry.getSize() != -1) {
this.fileSize = entry.getSize(); this.fileSize = entry.getSize();
} }
this.model = null; this.model = null;
...@@ -93,15 +93,14 @@ public class CityGmlZipEntry implements Serializable { ...@@ -93,15 +93,14 @@ public class CityGmlZipEntry implements Serializable {
this.parentArchive = parentArchive; this.parentArchive = parentArchive;
} }
public void setArchive(CityGmlZipArchive archive){ public void setArchive(CityGmlZipArchive archive) {
parentArchive = archive; parentArchive = archive;
} }
public CityGmlZipArchive getArchive(){ public CityGmlZipArchive getArchive() {
return parentArchive; return parentArchive;
} }
public String getFileName() { public String getFileName() {
return fileName; return fileName;
} }
...@@ -118,7 +117,7 @@ public class CityGmlZipEntry implements Serializable { ...@@ -118,7 +117,7 @@ public class CityGmlZipEntry implements Serializable {
fileSize = size; fileSize = size;
} }
public long getFileSize(){ public long getFileSize() {
return fileSize; return fileSize;
} }
} }
...@@ -9,11 +9,14 @@ import java.io.IOException; ...@@ -9,11 +9,14 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class ZipTest { public class ZipTest {
ParserConfiguration config = new ParserConfiguration(8,false); ParserConfiguration config = new ParserConfiguration(8, false);
@Test @Test
...@@ -25,8 +28,8 @@ public class ZipTest { ...@@ -25,8 +28,8 @@ public class ZipTest {
checkMockArchive(cgmlArch); checkMockArchive(cgmlArch);
} }
private void checkMockArchive(CityGmlZipArchive cgmlArch){ private void checkMockArchive(CityGmlZipArchive cgmlArch) {
assertEquals(5,cgmlArch.getEntries().size()); assertEquals(5, cgmlArch.getEntries().size());
for (CityGmlZipEntry entry : cgmlArch.getEntries()) { for (CityGmlZipEntry entry : cgmlArch.getEntries()) {
assertNotNull(entry); assertNotNull(entry);
assertTrue(entry.getFileName().matches("^mock[1-5].gml$")); assertTrue(entry.getFileName().matches("^mock[1-5].gml$"));
...@@ -79,8 +82,8 @@ public class ZipTest { ...@@ -79,8 +82,8 @@ public class ZipTest {
} }
@Test @Test
public void testXMLValidation(){ public void testXMLValidation() {
ParserConfiguration valConfig = new ParserConfiguration(8,true); ParserConfiguration valConfig = new ParserConfiguration(8, true);
CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/validate.zip"); CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/validate.zip");
assertNotNull(cgmlArch); assertNotNull(cgmlArch);
cgmlArch.mountArchive(valConfig); cgmlArch.mountArchive(valConfig);
...@@ -91,7 +94,7 @@ public class ZipTest { ...@@ -91,7 +94,7 @@ public class ZipTest {
@Test @Test
public void testImplicitParsing(){ public void testImplicitParsing() {
CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/implicit.zip"); CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/implicit.zip");
assertNotNull(cgmlArch); assertNotNull(cgmlArch);
cgmlArch.mountArchive(config); cgmlArch.mountArchive(config);
......
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * CityDoctor2 is free software: you can redistribute it and/or modify
...@@ -18,41 +18,6 @@ ...@@ -18,41 +18,6 @@
*/ */
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.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.stream.Stream;
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 de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile;
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;
...@@ -78,6 +43,8 @@ import de.hft.stuttgart.citydoctor2.reporting.XmlValidationReporter; ...@@ -78,6 +43,8 @@ import de.hft.stuttgart.citydoctor2.reporting.XmlValidationReporter;
import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfReporter; import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfReporter;
import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfStreamReporter; import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfStreamReporter;
import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile;
import de.hft.stuttgart.quality.model.enums.RequirementId; import de.hft.stuttgart.quality.model.enums.RequirementId;
import de.hft.stuttgart.quality.model.enums.TopLevelFeatureType; import de.hft.stuttgart.quality.model.enums.TopLevelFeatureType;
import de.hft.stuttgart.quality.model.properties.CheckingProperty; import de.hft.stuttgart.quality.model.properties.CheckingProperty;
...@@ -88,309 +55,339 @@ import de.hft.stuttgart.quality.model.properties.RequirementProperty; ...@@ -88,309 +55,339 @@ 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.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.stream.Stream;
/** /**
* The main container class for checking. It contains the logic for validation, * The main container class for checking. It contains the logic for validation,
* as well as contains the state of the checks performed. * as well as contains the state of the checks performed.
*
* @author Matthias Betz
* *
* @author Matthias Betz
*/ */
public class Checker { public class Checker {
private static final Logger logger = LogManager.getLogger(Checker.class); private static final Logger logger = LogManager.getLogger(Checker.class);
private ValidationConfiguration config; private ValidationConfiguration config;
private List<List<Check>> execLayers; private List<List<Check>> execLayers;
private List<Filter> includeFilters; private List<Filter> includeFilters;
private List<Filter> excludeFilters; private List<Filter> excludeFilters;
private final Checks checkConfig; private final Checks checkConfig;
private final CityDoctorModel model; private final CityDoctorModel model;
public Checker(CityDoctorModel model) { public Checker(CityDoctorModel model) {
this(ValidationConfiguration.loadStandardValidationConfig(), model); this(ValidationConfiguration.loadStandardValidationConfig(), model);
} }
public Checker(ValidationConfiguration config, CityDoctorModel model) { public Checker(ValidationConfiguration config, CityDoctorModel model) {
this.model = model; this.model = model;
checkConfig = new Checks(); checkConfig = new Checks();
setValidationConfig(config); setValidationConfig(config);
} }
public Checks getChecks() { public Checks getChecks() {
return checkConfig; return checkConfig;
} }
public CityDoctorModel getModel() { public CityDoctorModel getModel() {
return model; return model;
} }
/** /**
* Write the xml report for the given CityDoctorModel. If no report location is * Write the xml report for the given CityDoctorModel. If no report location is
* given or this checker has not validated anything, nothing is done. * given or this checker has not validated anything, nothing is done.
* *
* @param xmlOutput the output file location for the XML report. Can be null. * @param xmlOutput the output file location for the XML report. Can be null.
*/ */
public void writeXmlReport(String xmlOutput) { public void writeXmlReport(String xmlOutput) {
if (!model.isValidated() || xmlOutput == null) { if (!model.isValidated() || xmlOutput == null) {
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();
} }
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()))) {
reporter.writeReport(checkConfig, bos, model, config); reporter.writeReport(checkConfig, bos, model, config);
} catch (CheckReportWriteException | IOException e) { } catch (CheckReportWriteException | IOException e) {
logger.error(Localization.getText("Checker.failXml"), e); logger.error(Localization.getText("Checker.failXml"), e);
} }
} }
public void writePdfReport(String pdfOutput) { public void writePdfReport(String pdfOutput) {
if (!model.isValidated() || pdfOutput == null) { if (!model.isValidated() || pdfOutput == null) {
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();
} }
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);
} catch (IOException | CheckReportWriteException e) { } catch (IOException | CheckReportWriteException e) {
logger.error(Localization.getText("Checker.failPdf"), e); logger.error(Localization.getText("Checker.failPdf"), e);
} }
} }
public void runChecks() { public void runChecks() {
runChecks((ProgressListener) null); runChecks((ProgressListener) null);
} }
public void runChecks(String xmlOutput) { public void runChecks(String xmlOutput) {
runChecks(); runChecks();
writeXmlReport(xmlOutput); writeXmlReport(xmlOutput);
} }
public void runChecks(String xmlOutput, String pdfOutput) { public void runChecks(String xmlOutput, String pdfOutput) {
runChecks(); runChecks();
writeXmlReport(xmlOutput); writeXmlReport(xmlOutput);
writePdfReport(pdfOutput); writePdfReport(pdfOutput);
} }
public void runChecks(String xmlOutput, String pdfOutput, ProgressListener l) { public void runChecks(String xmlOutput, String pdfOutput, ProgressListener l) {
runChecks(l); runChecks(l);
writeXmlReport(xmlOutput); writeXmlReport(xmlOutput);
writePdfReport(pdfOutput); writePdfReport(pdfOutput);
} }
public void runChecks(ProgressListener l) { public void runChecks(ProgressListener l) {
if (config == null) { if (config == null) {
config = ValidationConfiguration.loadStandardValidationConfig(); config = ValidationConfiguration.loadStandardValidationConfig();
} }
checkCityModel(model, l); checkCityModel(model, l);
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info(Localization.getText("Checker.checksFinished")); logger.info(Localization.getText("Checker.checksFinished"));
} }
SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile()); SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile());
if (handler != null) { if (handler != null) {
handleSchematronResults(handler); handleSchematronResults(handler);
} }
model.setValidated(createValidationPlan()); model.setValidated(createValidationPlan());
} }
private void handleSchematronResults(SvrlContentHandler handler) { private void handleSchematronResults(SvrlContentHandler handler) {
model.addGlobalErrors(handler.getGeneralErrors()); model.addGlobalErrors(handler.getGeneralErrors());
Map<String, CityObject> featureMap = new HashMap<>(); Map<String, CityObject> featureMap = new HashMap<>();
model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f)); model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f));
handler.getFeatureErrors().forEach((k, v) -> { handler.getFeatureErrors().forEach((k, v) -> {
if (k.trim().isEmpty()) { if (k.trim().isEmpty()) {
// missing gml id, ignore? // missing gml id, ignore?
return; return;
} }
CityObject co = featureMap.get(k); CityObject co = featureMap.get(k);
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
for (SchematronError se : v) { for (SchematronError se : v) {
model.addGlobalError(se); model.addGlobalError(se);
} }
} else { } else {
handleSchematronErrorsForCityObject(v, co); handleSchematronErrorsForCityObject(v, co);
} }
}); });
} }
public static void handleSchematronErrorsForCityObject(List<SchematronError> v, CityObject co) { public static void handleSchematronErrorsForCityObject(List<SchematronError> v, CityObject co) {
int count = 0; int count = 0;
for (SchematronError se : v) { for (SchematronError se : v) {
CheckError err; CheckError err;
if (AttributeMissingError.ID.getIdString().equals(se.getErrorIdString())) { if (AttributeMissingError.ID.getIdString().equals(se.getErrorIdString())) {
err = new AttributeMissingError(co, se.getChildId(), se.getNameOfAttribute()); err = new AttributeMissingError(co, se.getChildId(), se.getNameOfAttribute());
} else if (AttributeValueWrongError.ID.getIdString().equals(se.getErrorIdString())) { } else if (AttributeValueWrongError.ID.getIdString().equals(se.getErrorIdString())) {
err = new AttributeValueWrongError(co, se.getChildId(), se.getNameOfAttribute()); err = new AttributeValueWrongError(co, se.getChildId(), se.getNameOfAttribute());
} else if (AttributeInvalidError.ID.getIdString().equals(se.getErrorIdString())) { } else if (AttributeInvalidError.ID.getIdString().equals(se.getErrorIdString())) {
err = new AttributeInvalidError(co, se.getChildId(), se.getNameOfAttribute()); err = new AttributeInvalidError(co, se.getChildId(), se.getNameOfAttribute());
} else { } else {
throw new IllegalStateException( throw new IllegalStateException(
"Unknown error ID was given in schematron file: " + se.getErrorIdString()); "Unknown error ID was given in schematron file: " + se.getErrorIdString());
} }
co.addCheckResult(new CheckResult(new CheckId("SchematronCheck " + count), ResultStatus.ERROR, err)); co.addCheckResult(new CheckResult(new CheckId("SchematronCheck " + count), ResultStatus.ERROR, err));
count++; count++;
} }
} }
ValidationPlan createValidationPlan() { ValidationPlan createValidationPlan() {
ValidationPlan plan = new ValidationPlan(); ValidationPlan plan = new ValidationPlan();
de.hft.stuttgart.quality.model.types.Filter filter = createFilter(); de.hft.stuttgart.quality.model.types.Filter filter = createFilter();
plan.setFilter(new FilterProperty(filter)); plan.setFilter(new FilterProperty(filter));
Map<String, de.hft.stuttgart.citydoctor2.check.Requirement> reqs = Checks.getAvailableRequirements(); Map<String, de.hft.stuttgart.citydoctor2.check.Requirement> reqs = Checks.getAvailableRequirements();
for (Entry<String, RequirementConfiguration> e : config.getRequirements().entrySet()) { for (Entry<String, RequirementConfiguration> e : config.getRequirements().entrySet()) {
RequirementId reqId = mapToRequirement(e.getKey()); RequirementId reqId = mapToRequirement(e.getKey());
if (reqId == null) { if (reqId == null) {
continue; continue;
} }
de.hft.stuttgart.quality.model.types.Requirement req = new de.hft.stuttgart.quality.model.types.Requirement(); de.hft.stuttgart.quality.model.types.Requirement req = new de.hft.stuttgart.quality.model.types.Requirement();
req.setRequirementType(reqId); req.setRequirementType(reqId);
req.setEnabled(e.getValue().isEnabled()); req.setEnabled(e.getValue().isEnabled());
plan.getRequirements().add(new RequirementProperty(req)); plan.getRequirements().add(new RequirementProperty(req));
Map<String, String> parameters = e.getValue().getParameters(); Map<String, String> parameters = e.getValue().getParameters();
if (parameters != null) { if (parameters != null) {
for (Entry<String, String> param : parameters.entrySet()) { for (Entry<String, String> param : parameters.entrySet()) {
Parameter p = new Parameter(); Parameter p = new Parameter();
DefaultParameter defaultP = getDefaultParameter(e.getKey(), reqs, param.getKey()); DefaultParameter defaultP = getDefaultParameter(e.getKey(), reqs, param.getKey());
if (defaultP != null) { if (defaultP != null) {
p.setUom(defaultP.getUnitType().getGmlRepresentation()); p.setUom(defaultP.getUnitType().getGmlRepresentation());
} }
p.setName(param.getKey()); p.setName(param.getKey());
p.setValue(param.getValue()); p.setValue(param.getValue());
req.getParameters().add(new ParameterProperty(p)); req.getParameters().add(new ParameterProperty(p));
} }
} }
} }
de.hft.stuttgart.quality.model.types.Requirement missing = new de.hft.stuttgart.quality.model.types.Requirement(); de.hft.stuttgart.quality.model.types.Requirement missing = new de.hft.stuttgart.quality.model.types.Requirement();
missing.setRequirementType(RequirementId.R_SE_ATTRIBUTES_EXISTING); missing.setRequirementType(RequirementId.R_SE_ATTRIBUTES_EXISTING);
de.hft.stuttgart.quality.model.types.Requirement correct = new de.hft.stuttgart.quality.model.types.Requirement(); de.hft.stuttgart.quality.model.types.Requirement correct = new de.hft.stuttgart.quality.model.types.Requirement();
correct.setRequirementType(RequirementId.R_SE_ATTRIBUTES_CORRECT); correct.setRequirementType(RequirementId.R_SE_ATTRIBUTES_CORRECT);
missing.setEnabled(config.getSchematronFilePath() != null); missing.setEnabled(config.getSchematronFilePath() != null);
correct.setEnabled(config.getSchematronFilePath() != null); correct.setEnabled(config.getSchematronFilePath() != null);
plan.getRequirements().add(new RequirementProperty(missing)); plan.getRequirements().add(new RequirementProperty(missing));
plan.getRequirements().add(new RequirementProperty(correct)); plan.getRequirements().add(new RequirementProperty(correct));
Parameter numRounding = new Parameter(); Parameter numRounding = new Parameter();
numRounding.setName("numberOfRoundingPlaces"); numRounding.setName("numberOfRoundingPlaces");
numRounding.setValue("" + config.getNumberOfRoundingPlaces()); numRounding.setValue("" + config.getNumberOfRoundingPlaces());
Parameter minVertexDistance = new Parameter(); Parameter minVertexDistance = new Parameter();
minVertexDistance.setName("minVertexDistance"); minVertexDistance.setName("minVertexDistance");
minVertexDistance.setUom("m"); minVertexDistance.setUom("m");
minVertexDistance.setValue("" + config.getMinVertexDistance()); minVertexDistance.setValue("" + config.getMinVertexDistance());
Parameter schematronFile = new Parameter(); Parameter schematronFile = new Parameter();
schematronFile.setName("schematronFile"); schematronFile.setName("schematronFile");
schematronFile.setValue(config.getSchematronFilePath()); schematronFile.setValue(config.getSchematronFilePath());
de.hft.stuttgart.quality.model.types.GlobalParameters globParams = new de.hft.stuttgart.quality.model.types.GlobalParameters(); de.hft.stuttgart.quality.model.types.GlobalParameters globParams = new de.hft.stuttgart.quality.model.types.GlobalParameters();
plan.setGlobalParameters(new GlobalParametersProperty(globParams)); plan.setGlobalParameters(new GlobalParametersProperty(globParams));
globParams.getParameters().add(new ParameterProperty(numRounding)); globParams.getParameters().add(new ParameterProperty(numRounding));
globParams.getParameters().add(new ParameterProperty(minVertexDistance)); globParams.getParameters().add(new ParameterProperty(minVertexDistance));
globParams.getParameters().add(new ParameterProperty(schematronFile)); globParams.getParameters().add(new ParameterProperty(schematronFile));
return plan; return plan;
} }
private DefaultParameter getDefaultParameter(String reqKey, private DefaultParameter getDefaultParameter(String reqKey,
Map<String, de.hft.stuttgart.citydoctor2.check.Requirement> reqs, String paramName) { Map<String, de.hft.stuttgart.citydoctor2.check.Requirement> reqs, String paramName) {
de.hft.stuttgart.citydoctor2.check.Requirement requirement = reqs.get(reqKey); de.hft.stuttgart.citydoctor2.check.Requirement requirement = reqs.get(reqKey);
if (requirement != null) { if (requirement != null) {
for (DefaultParameter param : requirement.getDefaultParameter()) { for (DefaultParameter param : requirement.getDefaultParameter()) {
if (param.getName().equals(paramName)) { if (param.getName().equals(paramName)) {
return param; return param;
} }
} }
} }
return null; return null;
} }
private de.hft.stuttgart.quality.model.types.Filter createFilter() { private de.hft.stuttgart.quality.model.types.Filter createFilter() {
var filter = new de.hft.stuttgart.quality.model.types.Filter(); var filter = new de.hft.stuttgart.quality.model.types.Filter();
handleInputFilter(filter); handleInputFilter(filter);
if (excludeFilters != null) { if (excludeFilters != null) {
for (Filter f : excludeFilters) { for (Filter f : excludeFilters) {
if (f instanceof TypeFilter tf) { if (f instanceof TypeFilter tf) {
FeatureType type = tf.getType(); FeatureType type = tf.getType();
TopLevelFeatureType tlft = mapToTopLevelFeatureType(type); TopLevelFeatureType tlft = mapToTopLevelFeatureType(type);
if (tlft == null) { if (tlft == null) {
continue; continue;
} }
removeFilter(tlft, filter); removeFilter(tlft, filter);
} }
} }
} }
return filter; return filter;
} }
private void handleInputFilter(de.hft.stuttgart.quality.model.types.Filter filter) { private void handleInputFilter(de.hft.stuttgart.quality.model.types.Filter filter) {
if (includeFilters == null || includeFilters.isEmpty()) { if (includeFilters == null || includeFilters.isEmpty()) {
// no filter means, use all // no filter means, use all
addAllFilters(filter); addAllFilters(filter);
} else { } else {
for (Filter f : includeFilters) { for (Filter f : includeFilters) {
if (f instanceof TypeFilter tf) { if (f instanceof TypeFilter tf) {
FeatureType type = tf.getType(); FeatureType type = tf.getType();
TopLevelFeatureType tlft = mapToTopLevelFeatureType(type); TopLevelFeatureType tlft = mapToTopLevelFeatureType(type);
if (tlft == null) { if (tlft == null) {
continue; continue;
} }
Checking c = new Checking(); Checking c = new Checking();
c.setFeatureType(tlft); c.setFeatureType(tlft);
filter.getChecking().add(new CheckingProperty(c)); filter.getChecking().add(new CheckingProperty(c));
} }
} }
if (filter.getChecking().isEmpty()) { if (filter.getChecking().isEmpty()) {
// this happens if no type include filter was used // this happens if no type include filter was used
// it is possible only single objects were tested then // it is possible only single objects were tested then
// so include everything // so include everything
addAllFilters(filter); addAllFilters(filter);
} }
} }
} }
private void addAllFilters(de.hft.stuttgart.quality.model.types.Filter filter) { private void addAllFilters(de.hft.stuttgart.quality.model.types.Filter filter) {
Checking buildingChecking = new Checking(); Checking buildingChecking = new Checking();
buildingChecking.setFeatureType(TopLevelFeatureType.BUILDING); buildingChecking.setFeatureType(TopLevelFeatureType.BUILDING);
filter.getChecking().add(new CheckingProperty(buildingChecking)); filter.getChecking().add(new CheckingProperty(buildingChecking));
Checking bridgeChecking = new Checking(); Checking bridgeChecking = new Checking();
bridgeChecking.setFeatureType(TopLevelFeatureType.BRIDGE); bridgeChecking.setFeatureType(TopLevelFeatureType.BRIDGE);
filter.getChecking().add(new CheckingProperty(bridgeChecking)); filter.getChecking().add(new CheckingProperty(bridgeChecking));
Checking landChecking = new Checking(); Checking landChecking = new Checking();
landChecking.setFeatureType(TopLevelFeatureType.LAND); landChecking.setFeatureType(TopLevelFeatureType.LAND);
filter.getChecking().add(new CheckingProperty(landChecking)); filter.getChecking().add(new CheckingProperty(landChecking));
Checking transportationChecking = new Checking(); Checking transportationChecking = new Checking();
transportationChecking.setFeatureType(TopLevelFeatureType.TRANSPORTATION); transportationChecking.setFeatureType(TopLevelFeatureType.TRANSPORTATION);
filter.getChecking().add(new CheckingProperty(transportationChecking)); filter.getChecking().add(new CheckingProperty(transportationChecking));
Checking vegetationChecking = new Checking(); Checking vegetationChecking = new Checking();
vegetationChecking.setFeatureType(TopLevelFeatureType.VEGETATION); vegetationChecking.setFeatureType(TopLevelFeatureType.VEGETATION);
filter.getChecking().add(new CheckingProperty(vegetationChecking)); filter.getChecking().add(new CheckingProperty(vegetationChecking));
Checking waterChecking = new Checking(); Checking waterChecking = new Checking();
waterChecking.setFeatureType(TopLevelFeatureType.WATER); waterChecking.setFeatureType(TopLevelFeatureType.WATER);
filter.getChecking().add(new CheckingProperty(waterChecking)); filter.getChecking().add(new CheckingProperty(waterChecking));
} }
private void removeFilter(TopLevelFeatureType tlft, de.hft.stuttgart.quality.model.types.Filter filter) { private void removeFilter(TopLevelFeatureType tlft, de.hft.stuttgart.quality.model.types.Filter filter) {
for (CheckingProperty c : filter.getChecking()) { for (CheckingProperty c : filter.getChecking()) {
if (c.getObject().getFeatureType().equals(tlft)) { if (c.getObject().getFeatureType().equals(tlft)) {
filter.getChecking().remove(c); filter.getChecking().remove(c);
return; return;
} }
} }
} }
private TopLevelFeatureType mapToTopLevelFeatureType(FeatureType type) { private TopLevelFeatureType mapToTopLevelFeatureType(FeatureType type) {
return switch (type) { return switch (type) {
case BRIDGE -> TopLevelFeatureType.BRIDGE; case BRIDGE -> TopLevelFeatureType.BRIDGE;
case BUILDING -> TopLevelFeatureType.BUILDING; case BUILDING -> TopLevelFeatureType.BUILDING;
...@@ -400,298 +397,298 @@ public class Checker { ...@@ -400,298 +397,298 @@ public class Checker {
case WATER -> TopLevelFeatureType.WATER; case WATER -> TopLevelFeatureType.WATER;
default -> null; default -> null;
}; };
} }
private RequirementId mapToRequirement(String requirementName) { private RequirementId mapToRequirement(String requirementName) {
try { try {
return RequirementId.valueOf(requirementName); return RequirementId.valueOf(requirementName);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return null; return null;
} }
} }
public ValidationConfiguration getConfig() { public ValidationConfiguration getConfig() {
return config; return config;
} }
public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, File file) { public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, File file) {
if (file == null || !file.exists()) { if (file == null || !file.exists()) {
return null; return null;
} }
try { try {
return executeSchematronValidationIfAvailable(config, new FileInputStream(file)); return executeSchematronValidationIfAvailable(config, new FileInputStream(file));
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }
} }
public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config,
InputStream in) { InputStream in) {
if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) { if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info(Localization.getText("Checker.schematronValidation")); logger.info(Localization.getText("Checker.schematronValidation"));
} }
try { try {
TransformerFactory transformerFactory = TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl", Checker.class.getClassLoader()); TransformerFactory transformerFactory = TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl", Checker.class.getClassLoader());
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
transformerFactory.setURIResolver((href, base) -> new StreamSource(Checker.class.getResourceAsStream(href))); transformerFactory.setURIResolver((href, base) -> new StreamSource(Checker.class.getResourceAsStream(href)));
Source dsdlXslSource = new StreamSource(Checker.class.getResourceAsStream("iso_dsdl_include.xsl")); Source dsdlXslSource = new StreamSource(Checker.class.getResourceAsStream("iso_dsdl_include.xsl"));
Transformer dsdlXslTransformer = transformerFactory.newTransformer(dsdlXslSource); Transformer dsdlXslTransformer = transformerFactory.newTransformer(dsdlXslSource);
DOMResult dsdlXslResult = new DOMResult(); DOMResult dsdlXslResult = new DOMResult();
dsdlXslTransformer.transform(new StreamSource(new File(config.getSchematronFilePath())), dsdlXslResult); dsdlXslTransformer.transform(new StreamSource(new File(config.getSchematronFilePath())), dsdlXslResult);
Source abstractExpandXsl = new StreamSource(Checker.class.getResourceAsStream("iso_abstract_expand.xsl")); Source abstractExpandXsl = new StreamSource(Checker.class.getResourceAsStream("iso_abstract_expand.xsl"));
Transformer abstractExpandTransformer = transformerFactory.newTransformer(abstractExpandXsl); Transformer abstractExpandTransformer = transformerFactory.newTransformer(abstractExpandXsl);
DOMResult abstractExpandResult = new DOMResult(); DOMResult abstractExpandResult = new DOMResult();
abstractExpandTransformer.transform(new DOMSource(dsdlXslResult.getNode()), abstractExpandResult); abstractExpandTransformer.transform(new DOMSource(dsdlXslResult.getNode()), abstractExpandResult);
Source svrlXslSource = new StreamSource(Checker.class.getResourceAsStream("iso_svrl_for_xslt2.xsl")); Source svrlXslSource = new StreamSource(Checker.class.getResourceAsStream("iso_svrl_for_xslt2.xsl"));
Transformer svrlTransformer = transformerFactory.newTransformer(svrlXslSource); Transformer svrlTransformer = transformerFactory.newTransformer(svrlXslSource);
DOMResult schematronXsltResult = new DOMResult(); DOMResult schematronXsltResult = new DOMResult();
svrlTransformer.transform(new DOMSource(abstractExpandResult.getNode()), schematronXsltResult); svrlTransformer.transform(new DOMSource(abstractExpandResult.getNode()), schematronXsltResult);
Transformer schematronTransformer = transformerFactory.newTransformer(new DOMSource(schematronXsltResult.getNode())); Transformer schematronTransformer = transformerFactory.newTransformer(new DOMSource(schematronXsltResult.getNode()));
Source cityGmlSource = new StreamSource(in); Source cityGmlSource = new StreamSource(in);
SvrlContentHandler handler = new SvrlContentHandler(); SvrlContentHandler handler = new SvrlContentHandler();
Result finalResult = new SAXResult(handler); Result finalResult = new SAXResult(handler);
schematronTransformer.transform(cityGmlSource, finalResult); schematronTransformer.transform(cityGmlSource, finalResult);
return handler; return handler;
} catch (TransformerException e) { } catch (TransformerException e) {
logger.catching(e); logger.catching(e);
} }
} }
return null; return null;
} }
private void buildFilters() { private void buildFilters() {
FilterConfiguration filterConfig = config.getFilter(); FilterConfiguration filterConfig = config.getFilter();
if (filterConfig == null) { if (filterConfig == null) {
includeFilters = Collections.emptyList(); includeFilters = Collections.emptyList();
excludeFilters = Collections.emptyList(); excludeFilters = Collections.emptyList();
return; return;
} }
excludeFilters = buildExcludeFilters(filterConfig); excludeFilters = buildExcludeFilters(filterConfig);
includeFilters = buildIncludeFilters(filterConfig); includeFilters = buildIncludeFilters(filterConfig);
} }
private List<Filter> buildExcludeFilters(FilterConfiguration filterConfig) { private List<Filter> buildExcludeFilters(FilterConfiguration filterConfig) {
if (filterConfig == null) { if (filterConfig == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
ExcludeFilterConfiguration excludeConfig = filterConfig.getExclude(); ExcludeFilterConfiguration excludeConfig = filterConfig.getExclude();
if (excludeConfig == null) { if (excludeConfig == null) {
return Collections.emptyList(); return Collections.emptyList();
} else { } else {
List<Filter> filters = new ArrayList<>(); List<Filter> filters = new ArrayList<>();
if (excludeConfig.getTypes() != null) { if (excludeConfig.getTypes() != null) {
for (FeatureType excludeType : excludeConfig.getTypes()) { for (FeatureType excludeType : excludeConfig.getTypes()) {
filters.add(new TypeFilter(excludeType)); filters.add(new TypeFilter(excludeType));
} }
} }
if (excludeConfig.getIds() != null) { if (excludeConfig.getIds() != null) {
for (String excludePattern : excludeConfig.getIds()) { for (String excludePattern : excludeConfig.getIds()) {
Filter f = new EqualsIgnoreCaseFilter(excludePattern); Filter f = new EqualsIgnoreCaseFilter(excludePattern);
filters.add(f); filters.add(f);
} }
} }
return filters; return filters;
} }
} }
private List<Filter> buildIncludeFilters(FilterConfiguration filterConfig) { private List<Filter> buildIncludeFilters(FilterConfiguration filterConfig) {
if (filterConfig == null) { if (filterConfig == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
IncludeFilterConfiguration includeConfig = filterConfig.getInclude(); IncludeFilterConfiguration includeConfig = filterConfig.getInclude();
if (includeConfig == null) { if (includeConfig == null) {
return Collections.emptyList(); return Collections.emptyList();
} else { } else {
List<Filter> filters = new ArrayList<>(); List<Filter> filters = new ArrayList<>();
if (includeConfig.getTypes() != null) { if (includeConfig.getTypes() != null) {
for (FeatureType includeType : includeConfig.getTypes()) { for (FeatureType includeType : includeConfig.getTypes()) {
filters.add(new TypeFilter(includeType)); filters.add(new TypeFilter(includeType));
} }
} }
if (includeConfig.getIds() != null) { if (includeConfig.getIds() != null) {
for (String includePattern : includeConfig.getIds()) { for (String includePattern : includeConfig.getIds()) {
Filter f = new EqualsIgnoreCaseFilter(includePattern); Filter f = new EqualsIgnoreCaseFilter(includePattern);
filters.add(f); filters.add(f);
} }
} }
return filters; return filters;
} }
} }
private void setValidationConfig(ValidationConfiguration config) { private void setValidationConfig(ValidationConfiguration config) {
if (config == null) { if (config == null) {
throw new IllegalArgumentException("Validation configuration may not be null"); throw new IllegalArgumentException("Validation configuration may not be null");
} }
this.config = config; this.config = config;
buildFilters(); buildFilters();
ParserConfiguration parserConfig = config.getParserConfiguration(); ParserConfiguration parserConfig = config.getParserConfiguration();
List<Check> checks = collectEnabledChecksAndInit(parserConfig, config); List<Check> checks = collectEnabledChecksAndInit(parserConfig, config);
execLayers = buildExecutionLayers(checks); execLayers = buildExecutionLayers(checks);
} }
private List<Check> collectEnabledChecksAndInit(ParserConfiguration parserConfig, ValidationConfiguration config) { private List<Check> collectEnabledChecksAndInit(ParserConfiguration parserConfig, ValidationConfiguration config) {
Set<CheckId> enabledCheck = new HashSet<>(); Set<CheckId> enabledCheck = new HashSet<>();
Map<CheckId, Map<String, String>> parameterMap = new HashMap<>(); Map<CheckId, Map<String, String>> parameterMap = new HashMap<>();
for (Entry<String, RequirementConfiguration> e : config.getRequirements().entrySet()) { for (Entry<String, RequirementConfiguration> e : config.getRequirements().entrySet()) {
de.hft.stuttgart.citydoctor2.check.Requirement req = Checks.getAvailableRequirements().get(e.getKey()); de.hft.stuttgart.citydoctor2.check.Requirement req = Checks.getAvailableRequirements().get(e.getKey());
if (req == null) { if (req == null) {
logger.warn("Could not find any check that satisfies requirement {}, it will not be checked", logger.warn("Could not find any check that satisfies requirement {}, it will not be checked",
e.getKey()); e.getKey());
} else { } else {
if (e.getValue().isEnabled()) { if (e.getValue().isEnabled()) {
// this requirement is enabled // this requirement is enabled
insertGlobalParameters(config, enabledCheck, parameterMap, e, req); insertGlobalParameters(config, enabledCheck, parameterMap, e, req);
} }
} }
} }
fillParameterMapsWithDefaultParameter(enabledCheck, parameterMap); fillParameterMapsWithDefaultParameter(enabledCheck, parameterMap);
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.get(id), parserConfig);
checkList.add(c); checkList.add(c);
} }
return checkList; return checkList;
} }
private void insertGlobalParameters(ValidationConfiguration config, Set<CheckId> enabledCheck, private void insertGlobalParameters(ValidationConfiguration config, Set<CheckId> enabledCheck,
Map<CheckId, Map<String, String>> parameterMap, Entry<String, RequirementConfiguration> e, Map<CheckId, Map<String, String>> parameterMap, Entry<String, RequirementConfiguration> e,
de.hft.stuttgart.citydoctor2.check.Requirement req) { de.hft.stuttgart.citydoctor2.check.Requirement req) {
for (CheckPrototype proto : Checks.getCheckPrototypes()) { for (CheckPrototype proto : Checks.getCheckPrototypes()) {
if (proto.checksRequirements().contains(req)) { if (proto.checksRequirements().contains(req)) {
// this requirement is checked by this check // this requirement is checked by this check
// put all requirement parameter in the map // put all requirement parameter in the map
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.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());
return v; return v;
}); });
enabledCheck.add(proto.getCheckId()); enabledCheck.add(proto.getCheckId());
collectDependencyChecks(proto, enabledCheck); collectDependencyChecks(proto, enabledCheck);
} }
} }
} }
private void fillParameterMapsWithDefaultParameter(Set<CheckId> enabledCheck, private void fillParameterMapsWithDefaultParameter(Set<CheckId> enabledCheck,
Map<CheckId, Map<String, String>> parameterMap) { Map<CheckId, Map<String, String>> parameterMap) {
for (CheckId id : enabledCheck) { for (CheckId id : enabledCheck) {
CheckPrototype proto = Checks.getCheckPrototypeForId(id); CheckPrototype proto = Checks.getCheckPrototypeForId(id);
Map<String, String> map = parameterMap.computeIfAbsent(id, k -> new HashMap<>()); Map<String, String> map = parameterMap.computeIfAbsent(id, k -> new HashMap<>());
for (de.hft.stuttgart.citydoctor2.check.Requirement req : proto.checksRequirements()) { for (de.hft.stuttgart.citydoctor2.check.Requirement req : proto.checksRequirements()) {
if (proto.checksRequirements().contains(req)) { if (proto.checksRequirements().contains(req)) {
for (DefaultParameter param : req.getDefaultParameter()) { for (DefaultParameter param : req.getDefaultParameter()) {
map.computeIfAbsent(param.getName(), k -> param.getValue()); map.computeIfAbsent(param.getName(), k -> param.getValue());
} }
} }
} }
} }
} }
private void collectDependencyChecks(CheckPrototype proto, Set<CheckId> enabledChecks) { private void collectDependencyChecks(CheckPrototype proto, Set<CheckId> enabledChecks) {
enabledChecks.addAll(proto.getDependencies()); enabledChecks.addAll(proto.getDependencies());
for (CheckId id : proto.getDependencies()) { for (CheckId id : proto.getDependencies()) {
if (enabledChecks.contains(id)) { if (enabledChecks.contains(id)) {
continue; continue;
} }
CheckPrototype depProto = Checks.getCheckPrototypeForId(id); CheckPrototype depProto = Checks.getCheckPrototypeForId(id);
collectDependencyChecks(depProto, enabledChecks); collectDependencyChecks(depProto, enabledChecks);
} }
} }
private void checkCityModel(CityDoctorModel model, ProgressListener l) { private void checkCityModel(CityDoctorModel model, ProgressListener l) {
Stream<CityObject> features = model.createFeatureStream(); Stream<CityObject> features = model.createFeatureStream();
float featureSum = model.getNumberOfFeatures(); float featureSum = model.getNumberOfFeatures();
// stupid lamda with final variable restrictions // stupid lamda with final variable restrictions
int[] currentFeature = new int[1]; int[] currentFeature = new int[1];
features.forEach(co -> { features.forEach(co -> {
if (config.getParserConfiguration().useLowMemoryConsumption()) { if (config.getParserConfiguration().useLowMemoryConsumption()) {
// no edges have been created yet, create them // no edges have been created yet, create them
co.prepareForChecking(); co.prepareForChecking();
} }
// check every feature // check every feature
executeChecksForCityObject(co); executeChecksForCityObject(co);
if (config.getParserConfiguration().useLowMemoryConsumption()) { if (config.getParserConfiguration().useLowMemoryConsumption()) {
// low memory consumption, remove edges again // low memory consumption, remove edges again
co.clearMetaInformation(); co.clearMetaInformation();
} }
if (l != null) { if (l != null) {
currentFeature[0]++; currentFeature[0]++;
l.updateProgress(currentFeature[0] / featureSum); l.updateProgress(currentFeature[0] / featureSum);
} }
}); });
} }
private boolean filterObject(CityObject co) { private boolean filterObject(CityObject co) {
return isObjectIncluded(co, includeFilters, excludeFilters); return isObjectIncluded(co, includeFilters, excludeFilters);
} }
private boolean isObjectIncluded(CityObject co, List<Filter> includeFilters, List<Filter> excludeFilters) { private boolean isObjectIncluded(CityObject co, List<Filter> includeFilters, List<Filter> excludeFilters) {
if (!includeFilters.isEmpty()) { if (!includeFilters.isEmpty()) {
boolean include = false; boolean include = false;
for (Filter f : includeFilters) { for (Filter f : includeFilters) {
if (f.matches(co)) { if (f.matches(co)) {
include = true; include = true;
break; break;
} }
} }
if (!include) { if (!include) {
// not included, ignore // not included, ignore
return false; return false;
} }
} }
// check if object is excluded // check if object is excluded
for (Filter f : excludeFilters) { for (Filter f : excludeFilters) {
if (f.matches(co)) { if (f.matches(co)) {
// exclude object // exclude object
return false; return false;
} }
} }
return true; return true;
} }
/** /**
* Checks the city object if it has not been removed by the filters. The check * Checks the city object if it has not been removed by the filters. The check
* result are stored into the city object itself. * result are stored into the city object itself.
* *
* @param co the city object that is going to be checked * @param co the city object that is going to be checked
*/ */
private void executeChecksForCityObject(CityObject co) { private void executeChecksForCityObject(CityObject co) {
if (!filterObject(co)) { if (!filterObject(co)) {
return; return;
} }
executeChecksForCheckable(co); executeChecksForCheckable(co);
} }
/** /**
* Executes all checks for the checkable. This will bypass the filters. This * Executes all checks for the checkable. This will bypass the filters. This
* will clear the old check results * will clear the old check results
* *
* @param co the checkable. * @param co the checkable.
*/ */
public void executeChecksForCheckable(Checkable co) { public void executeChecksForCheckable(Checkable co) {
// throw away old results // throw away old results
co.clearAllContainedCheckResults(); co.clearAllContainedCheckResults();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(Localization.getText("Checker.checkFeature"), co); logger.debug(Localization.getText("Checker.checkFeature"), co);
} }
for (List<Check> execLayer : execLayers) { for (List<Check> execLayer : execLayers) {
for (Check check : execLayer) { for (Check check : execLayer) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
...@@ -700,162 +697,162 @@ public class Checker { ...@@ -700,162 +697,162 @@ public class Checker {
co.accept(check); co.accept(check);
} }
} }
} }
public static List<List<Check>> buildExecutionLayers(List<Check> checks) { public static List<List<Check>> buildExecutionLayers(List<Check> checks) {
List<List<Check>> result = new ArrayList<>(); List<List<Check>> result = new ArrayList<>();
Set<Check> availableChecks = new HashSet<>(checks); Set<Check> availableChecks = new HashSet<>(checks);
Set<CheckId> usedChecks = new HashSet<>(); Set<CheckId> usedChecks = new HashSet<>();
while (!availableChecks.isEmpty()) { while (!availableChecks.isEmpty()) {
List<Check> layer = new ArrayList<>(); List<Check> layer = new ArrayList<>();
Iterator<Check> iterator = availableChecks.iterator(); Iterator<Check> iterator = availableChecks.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Check c = iterator.next(); Check c = iterator.next();
boolean hasUnusedDependency = searchForUnusedDependency(usedChecks, c); boolean hasUnusedDependency = searchForUnusedDependency(usedChecks, c);
if (!hasUnusedDependency) { if (!hasUnusedDependency) {
iterator.remove(); iterator.remove();
layer.add(new CheckContainer(c)); layer.add(new CheckContainer(c));
} }
} }
if (layer.isEmpty()) { if (layer.isEmpty()) {
throw new IllegalStateException( throw new IllegalStateException(
"There are checks that have dependencies that are not executed or are unknown, aborting"); "There are checks that have dependencies that are not executed or are unknown, aborting");
} }
result.add(layer); result.add(layer);
for (Check c : layer) { for (Check c : layer) {
usedChecks.add(c.getCheckId()); usedChecks.add(c.getCheckId());
} }
} }
return result; return result;
} }
private static boolean searchForUnusedDependency(Set<CheckId> usedChecks, Check c) { private static boolean searchForUnusedDependency(Set<CheckId> usedChecks, Check c) {
boolean hasUnusedDependency = false; boolean hasUnusedDependency = false;
for (CheckId id : c.getDependencies()) { for (CheckId id : c.getDependencies()) {
if (!usedChecks.contains(id)) { if (!usedChecks.contains(id)) {
hasUnusedDependency = true; hasUnusedDependency = true;
break; break;
} }
} }
return hasUnusedDependency; return hasUnusedDependency;
} }
public static void streamCheck (CityGmlZipEntry entry, String xmlOutput, String pdfOutput, ValidationConfiguration config, public static void streamCheck(CityGmlZipEntry entry, String xmlOutput, String pdfOutput, ValidationConfiguration config,
String outputFile) throws IOException, CityGmlParseException{ String outputFile) throws IOException, CityGmlParseException {
try (BufferedOutputStream xmlBos = getXmlOutputMaybe(xmlOutput); try (BufferedOutputStream xmlBos = getXmlOutputMaybe(xmlOutput);
BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) { BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) {
Checker c = new Checker(config, null); Checker c = new Checker(config, null);
String fileName = entry.getFileName(); String fileName = entry.getFileName();
// create reporter if available // create reporter if available
XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName); XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName);
PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName); PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName);
// execute schematron first // execute schematron first
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, entryFile.getInputStream()); SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, entryFile.getInputStream());
CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, null); CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, null);
// parse and validate // parse and validate
CityGmlParser.streamCityGml(entry, config.getParserConfiguration(), con, outputFile); CityGmlParser.streamCityGml(entry, config.getParserConfiguration(), con, outputFile);
// write reports if available // write reports if available
writeReport(xmlReporter); writeReport(xmlReporter);
writeReport(pdfReporter); writeReport(pdfReporter);
} }
} catch (CheckReportWriteException e) { } catch (CheckReportWriteException e) {
logger.error(Localization.getText("Checker.failReports"), e); logger.error(Localization.getText("Checker.failReports"), e);
} }
} }
public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config, public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config,
String outputFile) throws IOException, CityGmlParseException { String outputFile) throws IOException, CityGmlParseException {
streamCheck(inputFile, xmlOutput, pdfOutput, config, null, outputFile); streamCheck(inputFile, xmlOutput, pdfOutput, config, null, outputFile);
} }
public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config, public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config,
FeatureCheckedListener l, String outputFile) throws IOException, CityGmlParseException { FeatureCheckedListener l, String outputFile) throws IOException, CityGmlParseException {
try (BufferedOutputStream xmlBos = getXmlOutputMaybe(xmlOutput); try (BufferedOutputStream xmlBos = getXmlOutputMaybe(xmlOutput);
BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) { BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) {
Checker c = new Checker(config, null); Checker c = new Checker(config, null);
String fileName = inputFile.getName(); String fileName = inputFile.getName();
// create reporter if available // create reporter if available
XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName); XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName);
PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName); PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName);
// 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.getAbsolutePath(), config.getParserConfiguration(), con, outputFile);
// write reports if available // write reports if available
writeReport(xmlReporter); writeReport(xmlReporter);
writeReport(pdfReporter); writeReport(pdfReporter);
} catch (CheckReportWriteException e) { } catch (CheckReportWriteException e) {
logger.error(Localization.getText("Checker.failReports"), e); logger.error(Localization.getText("Checker.failReports"), e);
} }
} }
private static XmlStreamReporter getXmlReporter(ValidationConfiguration config, BufferedOutputStream xmlBos, private static XmlStreamReporter getXmlReporter(ValidationConfiguration config, BufferedOutputStream xmlBos,
String fileName) { String fileName) {
XmlStreamReporter xmlReporter; XmlStreamReporter xmlReporter;
if (xmlBos != null) { if (xmlBos != null) {
xmlReporter = new XmlStreamReporter(xmlBos, fileName, config); xmlReporter = new XmlStreamReporter(xmlBos, fileName, config);
} else { } else {
xmlReporter = null; xmlReporter = null;
} }
return xmlReporter; return xmlReporter;
} }
private static PdfStreamReporter getPdfReporter(ValidationConfiguration config, BufferedOutputStream pdfBos, private static PdfStreamReporter getPdfReporter(ValidationConfiguration config, BufferedOutputStream pdfBos,
String fileName) { String fileName) {
PdfStreamReporter pdfReporter; PdfStreamReporter pdfReporter;
if (pdfBos != null) { if (pdfBos != null) {
pdfReporter = new PdfStreamReporter(pdfBos, fileName, config); pdfReporter = new PdfStreamReporter(pdfBos, fileName, config);
} else { } else {
pdfReporter = null; pdfReporter = null;
} }
return pdfReporter; return pdfReporter;
} }
public static void writeReport(StreamReporter reporter) throws CheckReportWriteException { public static void writeReport(StreamReporter reporter) throws CheckReportWriteException {
if (reporter != null) { if (reporter != null) {
reporter.finishReport(); reporter.finishReport();
} }
} }
public static BufferedOutputStream getPdfOutputMaybe(String pdfOutput) throws FileNotFoundException { public static BufferedOutputStream getPdfOutputMaybe(String pdfOutput) throws FileNotFoundException {
return pdfOutput != null ? new BufferedOutputStream(new FileOutputStream(pdfOutput)) : null; return pdfOutput != null ? new BufferedOutputStream(new FileOutputStream(pdfOutput)) : null;
} }
public static BufferedOutputStream getXmlOutputMaybe(String xmlOutput) throws FileNotFoundException { public static BufferedOutputStream getXmlOutputMaybe(String xmlOutput) throws FileNotFoundException {
return xmlOutput != null ? new BufferedOutputStream(new FileOutputStream(xmlOutput)) : null; return xmlOutput != null ? new BufferedOutputStream(new FileOutputStream(xmlOutput)) : null;
} }
/** /**
* Checks the city object and writes report information into reporters. Clears * Checks the city object and writes report information into reporters. Clears
* old check results. If the city object would be filtered by the configured * old check results. If the city object would be filtered by the configured
* filters it is ignored and old check results are not cleared. * filters it is ignored and old check results are not cleared.
* *
* @param xmlReporter a xml reporter * @param xmlReporter a xml reporter
* @param pdfReporter a pdf reporter * @param pdfReporter a pdf reporter
* @param co the city object to be checked * @param co the city object to be checked
*/ */
public void checkFeature(XmlStreamReporter xmlReporter, PdfStreamReporter pdfReporter, CityObject co) { public void checkFeature(XmlStreamReporter xmlReporter, PdfStreamReporter pdfReporter, CityObject co) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(Localization.getText("Checker.checkFeature"), co); logger.debug(Localization.getText("Checker.checkFeature"), co);
} }
executeChecksForCityObject(co); executeChecksForCityObject(co);
if (xmlReporter != null) { if (xmlReporter != null) {
xmlReporter.report(co); xmlReporter.report(co);
} }
if (pdfReporter != null) { if (pdfReporter != null) {
pdfReporter.report(co); pdfReporter.report(co);
} }
} }
} }
/*- /*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
* *
* This file is part of CityDoctor2. * This file is part of CityDoctor2.
* *
* CityDoctor2 is free software: you can redistribute it and/or modify * CityDoctor2 is free software: you can redistribute it and/or modify
...@@ -18,16 +18,6 @@ ...@@ -18,16 +18,6 @@
*/ */
package de.hft.stuttgart.citydoctor2.check; package de.hft.stuttgart.citydoctor2.check;
import java.io.File;
import java.io.IOException;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import de.hft.stuttgart.citydoctor2.CityDoctorValidation; import de.hft.stuttgart.citydoctor2.CityDoctorValidation;
import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
...@@ -35,99 +25,106 @@ import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; ...@@ -35,99 +25,106 @@ 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.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
*
* @author Matthias Betz * @author Matthias Betz
*
*/ */
public class CheckerTest { public class CheckerTest {
@Rule @Rule
public TemporaryFolder folder = new TemporaryFolder(); public TemporaryFolder folder = new TemporaryFolder();
@Test @Test
public void testSchematron() throws CityGmlParseException, InvalidGmlFileException { public void testSchematron() throws CityGmlParseException, InvalidGmlFileException {
ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig();
config.getRequirements().get(Requirement.R_SE_BS_ROOF_UNFRAGMENTED.toString()).setEnabled(false); config.getRequirements().get(Requirement.R_SE_BS_ROOF_UNFRAGMENTED.toString()).setEnabled(false);
config.setSchematronFilePathInGlobalParameters("src/test/resources/schematronTest.xml"); config.setSchematronFilePathInGlobalParameters("src/test/resources/schematronTest.xml");
CityDoctorModel model = CityGmlParser.parseCityGmlFile( CityDoctorModel model = CityGmlParser.parseCityGmlFile(
"src/test/resources/SimpleSolid_SrefBS_SchematronTest.gml", config.getParserConfiguration()); "src/test/resources/SimpleSolid_SrefBS_SchematronTest.gml", config.getParserConfiguration());
Checker checker = new Checker(config, model); Checker checker = new Checker(config, model);
checker.runChecks(); checker.runChecks();
for (Building b : model.getBuildings()) { for (Building b : model.getBuildings()) {
if (b.getGmlId().getGmlString().equals("_Simple_BD.1")) { if (b.getGmlId().getGmlString().equals("_Simple_BD.1")) {
assertTrue(b.containsAnyError()); assertTrue(b.containsAnyError());
} else { } else {
assertFalse(b.containsAnyError()); assertFalse(b.containsAnyError());
} }
} }
assertFalse(model.getGlobalErrors().isEmpty()); assertFalse(model.getGlobalErrors().isEmpty());
} }
@Test @Test
public void testChecker() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException { public void testChecker() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException {
File f = folder.newFile(); File f = folder.newFile();
File f2 = folder.newFile(); File f2 = folder.newFile();
try { try {
String[] args = new String[6]; String[] args = new String[6];
args[0] = "-in"; args[0] = "-in";
args[1] = "src/test/resources/QA-CS-CONCOMP.gml"; args[1] = "src/test/resources/QA-CS-CONCOMP.gml";
args[2] = "-xmlReport"; args[2] = "-xmlReport";
args[3] = f.getAbsolutePath(); args[3] = f.getAbsolutePath();
args[4] = "-pdfReport"; args[4] = "-pdfReport";
args[5] = f2.getAbsolutePath(); args[5] = f2.getAbsolutePath();
CityDoctorValidation.main(args); CityDoctorValidation.main(args);
assertTrue(f.exists()); assertTrue(f.exists());
assertTrue(f2.exists()); assertTrue(f2.exists());
} finally { } finally {
f.delete(); f.delete();
f2.delete(); f2.delete();
} }
} }
@Test @Test
public void testStreaming() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException { public void testStreaming() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException {
File f = folder.newFile(); File f = folder.newFile();
File f2 = folder.newFile(); File f2 = folder.newFile();
File f3 = folder.newFile(); File f3 = folder.newFile();
try { try {
String[] args = new String[10]; String[] args = new String[10];
args[0] = "-in"; args[0] = "-in";
args[1] = "src/test/resources/testarea.gml"; args[1] = "src/test/resources/testarea.gml";
args[2] = "-config"; args[2] = "-config";
args[3] = "src/test/resources/testConfigWithStreaming.yml"; args[3] = "src/test/resources/testConfigWithStreaming.yml";
args[4] = "-pdfReport"; args[4] = "-pdfReport";
args[5] = f.getAbsolutePath(); args[5] = f.getAbsolutePath();
args[6] = "-xmlReport"; args[6] = "-xmlReport";
args[7] = f2.getAbsolutePath(); args[7] = f2.getAbsolutePath();
args[8] = "-out"; args[8] = "-out";
args[9] = f3.getAbsolutePath(); args[9] = f3.getAbsolutePath();
CityDoctorValidation.main(args); CityDoctorValidation.main(args);
assertTrue(f.exists()); assertTrue(f.exists());
assertTrue(f2.exists()); assertTrue(f2.exists());
assertTrue(f3.exists()); assertTrue(f3.exists());
} finally { } finally {
f.delete(); f.delete();
f2.delete(); f2.delete();
f3.delete(); f3.delete();
} }
} }
@Test @Test
public void testZipEntryChecking() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException { public void testZipEntryChecking() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException {
CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zipArchive.zip"); CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zipArchive.zip");
assertNotNull(cgmlArch); assertNotNull(cgmlArch);
cgmlArch.mountArchive(new ParserConfiguration(8,false)); cgmlArch.mountArchive(new ParserConfiguration(8, false));
ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig();
for (CityGmlZipEntry entry : cgmlArch.getEntries()){ for (CityGmlZipEntry entry : cgmlArch.getEntries()) {
Checker.streamCheck(entry, null, null, config, null ); Checker.streamCheck(entry, null, null, config, null);
} }
} }
} }
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