Commit 219d36ea authored by Luna Riegel's avatar Luna Riegel
Browse files

Merge branch 'refs/heads/dev' into dev_advanced_db_management

# Conflicts:
#	CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java
parents 1c665219 5df59c7a
......@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.18.1] (2025-12-10)
### Hotfix
This hotfix removes the AUTO_SERVER flag from the embedded database, which causes a Firewall popup when the database is
started, due to opening local ports for accessing the database while CityDoctor is running.
The Firewall request can be safely denied, as CityDoctor uses the database-file for connections to the database.
Accepting the request does not open up remote connections to the database, as H2 by default only allows connections from
the local machine.
## [3.18.0] (2025-12-05)
**Semi-breaking change:** This MR introduces breaking changes to the CityDoctorModel and CityObject classes. Usage of the CityDoctorValidation API and CLI remain backwards compatible.
......
......@@ -6,7 +6,7 @@
<parent>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId>
<version>3.18.0</version>
<version>3.18.1</version>
</parent>
<artifactId>CityDoctorCheckResult</artifactId>
<dependencies>
......
......@@ -22,6 +22,7 @@ import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -65,7 +66,11 @@ public class CheckReport {
private Map<String, FeatureReport> featureReports;
public static CheckReport load(String file) throws CheckReportParseException {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
return load(Path.of(file));
}
public static CheckReport load(Path path) throws CheckReportParseException {
try (BufferedReader reader = new BufferedReader(new FileReader(path.toFile()))) {
CheckReport report = (CheckReport) getContext().createUnmarshaller().unmarshal(new InputSource(reader));
enrichReportStructure(report);
return report;
......
......@@ -6,7 +6,7 @@
<parent>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId>
<version>3.18.0</version>
<version>3.18.1</version>
</parent>
<artifactId>CityDoctorEdge</artifactId>
<dependencies>
......
......@@ -6,7 +6,7 @@
<parent>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId>
<version>3.18.0</version>
<version>3.18.1</version>
</parent>
<properties>
<versionString>${project.version}-${git.commit.id.abbrev}</versionString>
......
......@@ -40,6 +40,7 @@ import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.check.error.XMLValidationError;
public abstract class AbstractErrorVisitor implements ErrorVisitor {
......@@ -207,4 +208,8 @@ public abstract class AbstractErrorVisitor implements ErrorVisitor {
public void visit(SolidError err) {
}
@Override
public void visit(XMLValidationError err) {
}
}
......@@ -83,4 +83,8 @@ public interface CheckError extends Serializable {
*/
public void report(ErrorReport report);
public default boolean isParsingError() {
return false;
}
}
......@@ -26,7 +26,7 @@ import java.util.Map;
import de.hft.stuttgart.citydoctor2.utils.CheckErrorFound;
import de.hft.stuttgart.citydoctor2.utils.visitors.CheckableErrorCollector;
import de.hft.stuttgart.citydoctor2.utils.visitors.ClearVisitorResultsVisitor;
import de.hft.stuttgart.citydoctor2.utils.visitors.ClearResultsVisitor;
import de.hft.stuttgart.citydoctor2.utils.visitors.ClearMetaInformationVisitor;
import de.hft.stuttgart.citydoctor2.utils.visitors.ContainsAnyErrorVisitor;
import de.hft.stuttgart.citydoctor2.utils.visitors.ContainsErrorVisitor;
......@@ -221,18 +221,23 @@ public abstract class Checkable implements Serializable {
}
/**
* Clears the checkResults list of this checkable.
* Clears the checkResults of this checkable unless the error occured during parsing.
*/
public void clearCheckResults() {
setValidated(false);
checkResults.clear();
checkResults.entrySet().removeIf(e -> {
if (e.getValue().getError() == null) {
return true;
}
return !e.getValue().getError().isParsingError();
});
setValidated(!checkResults.isEmpty());
}
/**
* Clears the checkResults list of this checkable and all child objects in its datastructure.
*/
public final void clearAllContainedCheckResults() {
this.accept(new ClearVisitorResultsVisitor());
this.accept(new ClearResultsVisitor());
}
/**
......
......@@ -66,6 +66,7 @@ public record ErrorId(String name) implements Serializable {
public static final ErrorId SE_BS_UNFRAGMENTED = new ErrorId("SE_BS_UNFRAGMENTED");
public static final ErrorId GE_P_DEGENERATED_RING = new ErrorId("GE_P_DEGENERATED_POLYGON");
public static final ErrorId SE_POLYGON_WITHOUT_SURFACE = new ErrorId("SE_POLYGON_WITHOUT_SURFACE");
public static final ErrorId SC_SCHEMA_VALIDATION = new ErrorId("SC_SCHEMA_VALIDATION");
......
......@@ -58,6 +58,7 @@ import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.check.error.XMLValidationError;
/**
* Visitor pattern interface to access errors. This is to determine the type of
......@@ -150,4 +151,6 @@ public interface ErrorVisitor {
public void visit(SolidError err);
public void visit(XMLValidationError err);
}
......@@ -51,6 +51,7 @@ import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.check.error.XMLValidationError;
/**
* Visitor pattern interface used in the healing procedures. The modification
......@@ -200,6 +201,10 @@ public interface HealingMethod {
return false;
}
default boolean visit(XMLValidationError err, ModificationListener l) {
return false;
}
public HealingMethod createNew();
}
......@@ -58,6 +58,7 @@ import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.check.error.XMLValidationError;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
......@@ -404,4 +405,9 @@ public class QualityAdeErrorVisitor extends AbstractErrorVisitor {
// not translated
}
@Override
public void visit(XMLValidationError err) {
// not translated
}
}
package de.hft.stuttgart.citydoctor2.check.error;
import java.io.Serial;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.ErrorId;
import de.hft.stuttgart.citydoctor2.check.ErrorReport;
import de.hft.stuttgart.citydoctor2.check.ErrorType;
import de.hft.stuttgart.citydoctor2.check.ErrorVisitor;
import de.hft.stuttgart.citydoctor2.check.HealingMethod;
import de.hft.stuttgart.citydoctor2.check.ModificationListener;
import de.hft.stuttgart.citydoctor2.datastructure.GmlElement;
public class XMLValidationError implements CheckError {
@Serial
private static final long serialVersionUID = 4002356100680460598L;
private String message;
public XMLValidationError(String message) {
this.message = message;
}
@Override
public ErrorType getType() {
return ErrorType.ERROR;
}
@Override
public ErrorId getErrorId() {
return ErrorId.SC_SCHEMA_VALIDATION;
}
@Override
public GmlElement getFeature() {
return null;
}
@Override
public void accept(ErrorVisitor errorVisitor) {
errorVisitor.visit(this);
}
@Override
public boolean accept(HealingMethod method, ModificationListener l) {
return method.visit(this, l);
}
public String getMessage() {
return message;
}
@Override
public void report(ErrorReport report) {
report.add("message", message);
}
@Override
public boolean isParsingError() {
return true;
}
}
......@@ -18,13 +18,10 @@
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.check.CheckableVisitor;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
import javafx.scene.paint.Color;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty;
import java.io.Serial;
import java.util.ArrayList;
import java.util.List;
import org.citygml4j.core.model.deprecated.bridge.DeprecatedPropertiesOfBridgeConstructiveElement;
import org.citygml4j.core.util.geometry.GeometryFactory;
import org.xmlobjects.gml.model.geometry.GeometryProperty;
......@@ -34,14 +31,13 @@ import org.xmlobjects.gml.model.geometry.complexes.CompositeSurface;
import org.xmlobjects.gml.model.geometry.primitives.Solid;
import org.xmlobjects.gml.model.geometry.primitives.SolidProperty;
import java.io.Serial;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.check.CheckableVisitor;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
import javafx.scene.paint.Color;
public class BridgeConstructiveElement extends CityObject {
private static final Logger logger = LogManager.getLogger(BridgeConstructiveElement.class);
private static final String CANNOT_ADD = "Cannot add ";
@Serial
......@@ -105,20 +101,6 @@ public class BridgeConstructiveElement extends CityObject {
}
}
private void reCreateBoundarySurface(GeometryFactory factory, ParserConfiguration config, BoundarySurface bs) {
if (bs.getGeometries().isEmpty()) {
for (AbstractSpaceBoundaryProperty bsp : gmlBridgeElement.getBoundaries()) {
if (bsp.getObject() != null && bsp.getObject() == bs.getGmlObject()) {
logger.warn("Found empty boundary surface: {}, removing from BridgeConstructiveElement", bs.getGmlId());
gmlBridgeElement.getBoundaries().remove(bsp);
break;
}
}
return;
}
bs.reCreateGeometries(factory, config);
}
private void setCompositeSurfaceAccordingToLod(Geometry geom, CompositeSurface cs) {
switch (geom.getLod()) {
case LOD1:
......
......@@ -23,6 +23,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.function.Function;
import de.hft.stuttgart.citydoctor2.database.FeatureCache;
import de.hft.stuttgart.citydoctor2.database.UnconnectedCache;
......@@ -172,10 +174,13 @@ public class Citygml3FeatureMapper extends ObjectWalker {
private final Path directory;
private final double neighborDistance;
private Map<String, ConcretePolygon> polygonMap = new HashMap<>();
private Map<String, CompositeCollection> compositeMap = new HashMap<>();
private Map<String, CompositeCollection> compositeMap = new HashMap<>();;
private List<ResolvableReference> references = new ArrayList<>();
private Map<Vertex, Vertex> vertexMap = new HashMap<>();
private Map<String, List<GmlId>> unresolvedPolyRefIDMap = new HashMap<>();
private Map<String, GmlId> parsedPolygonsIdMap = new HashMap<>();
public Citygml3FeatureMapper(ParserConfiguration config, Path path, boolean unconnected) {
this.config = config;
this.directory = path.getParent();
......@@ -197,6 +202,37 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
}
public void checkUnresolvedReferences() {
if (unresolvedPolyRefIDMap.isEmpty()) {
return;
}
boolean xLinkGlobalError = false;
List<String> crossRefs = new ArrayList<>();
for (Map.Entry<String, List<GmlId>> entry : unresolvedPolyRefIDMap.entrySet()) {
String id = entry.getKey();
boolean emptyCrossFeatureReference = parsedPolygonsIdMap.containsKey(id);
xLinkGlobalError = xLinkGlobalError || emptyCrossFeatureReference;
if (emptyCrossFeatureReference) {
List<GmlId> gmlIds = entry.getValue();
StringJoiner joiner = new StringJoiner(", ");
gmlIds.forEach(gmlId -> joiner.add(gmlId.toString()));
GmlId crossFeatureId = parsedPolygonsIdMap.get(id);
//TODO: Localize String
logger.warn("Polygon {} of Feature {} is being referenced in other Features: {}", id, crossFeatureId, joiner);
crossRefs.add(id);
}
}
if (xLinkGlobalError) {
//TODO: Localize String
logger.warn("Model has Features containing cross-feature references without their own copies of the referenced polygons");
}
crossRefs.forEach(unresolvedPolyRefIDMap::remove);
if(!unresolvedPolyRefIDMap.isEmpty()) {
logger.warn("Model contains polygon references that could not be found");
}
}
@Override
public void visit(AbstractSpace space) {
// if we are here, an AbstractSpace thing was read that is not handled in the
......@@ -903,7 +939,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
private void finishCityObjectConstruction(CityObject co) {
parseId(co.getGmlObject(), co);
co.clearGmlGeometries();
resolveAndClearReferences();
resolveAndClearReferences(co);
updateEdgesAndVertices(co);
}
......@@ -1092,7 +1128,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
return igh;
}
private void resolveAndClearReferences() {
private void resolveAndClearReferences(CityObject co) {
for (ResolvableReference ref : references) {
String href = ref.href();
if (href.startsWith("#")) {
......@@ -1107,6 +1143,9 @@ public class Citygml3FeatureMapper extends ObjectWalker {
handlePolygonReference(href, geom);
}
}
for(String polygonId: polygonMap.keySet()){
parsedPolygonsIdMap.put(polygonId, co.getGmlId());
}
// clear storage for polygons and vertices
references = new ArrayList<>();
......@@ -1117,9 +1156,12 @@ public class Citygml3FeatureMapper extends ObjectWalker {
private void handlePolygonReference(String href, Geometry geom) {
ConcretePolygon concPoly = polygonMap.get(href);
if (concPoly == null) {
if (logger.isWarnEnabled()) {
logger.warn(Localization.getText("FeatureMapper.polygonUnreferenced"), href);
}
List<GmlId> featureList = unresolvedPolyRefIDMap.computeIfAbsent(href, k -> new ArrayList<>());
GmlId featureId = geom.getParent().getTopLevelCityObject().getGmlId();
featureList.add(featureId);
// if (logger.isWarnEnabled()) {
// logger.warn(Localization.getText("FeatureMapper.polygonUnreferenced"), href);
// }
} else {
LinkedPolygon lPoly = new LinkedPolygon(concPoly, geom);
if (geom.getParent() instanceof BoundarySurface bs) {
......
......@@ -28,29 +28,38 @@ import org.xml.sax.SAXParseException;
public class GMLValidationHandler implements ErrorHandler {
private static final int MAX_LINE_SIZE = 200;
private static final Logger logger = LogManager.getLogger(GMLValidationHandler.class);
private final List<String> messages = new ArrayList<>();
@Override
public void error(SAXParseException exception) {
String message = "[" + exception.getLineNumber() + ", " + exception.getColumnNumber() + "] "
+ exception.getMessage();
String message = constructMessage(exception);
logger.error(message);
messages.add(message);
}
private String constructMessage(SAXParseException exception) {
String exceptionMessage = exception.getMessage();
String message = "[line: " + exception.getLineNumber() + "] "
+ exceptionMessage.substring(0, Math.min(exceptionMessage.length(), MAX_LINE_SIZE));
if (exceptionMessage.length() > MAX_LINE_SIZE) {
message += "...";
}
return message;
}
@Override
public void warning(SAXParseException exception) {
String message = "[" + exception.getLineNumber() + ", " + exception.getColumnNumber() + "] "
+ exception.getMessage();
String message = constructMessage(exception);
logger.warn(message);
messages.add(message);
}
@Override
public void fatalError(SAXParseException exception) {
String message = "[" + exception.getLineNumber() + ", " + exception.getColumnNumber() + "] "
+ exception.getMessage();
String message = constructMessage(exception);
logger.fatal(message);
messages.add(message);
}
......
......@@ -32,6 +32,5 @@ public interface CityGmlConsumer {
}
// public void endOfFile();
}
......@@ -28,6 +28,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
......@@ -43,8 +44,6 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -84,8 +83,11 @@ import org.xmlobjects.schema.SchemaHandlerException;
import org.xmlobjects.stream.XMLReader;
import org.xmlobjects.stream.XMLReaderFactory;
import de.hft.stuttgart.citydoctor2.check.error.XMLValidationError;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
......@@ -129,9 +131,9 @@ public class CityGmlParser {
private static CityGMLContext context;
private static List<QName> chunkProperties = new ArrayList<>();
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();
try {
FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
......@@ -147,7 +149,7 @@ public class CityGmlParser {
private CityGmlParser() {
}
public static synchronized CityGMLContext getContext() {
public static synchronized CityGMLContext setupContext() {
if (context == null) {
try {
context = CityGMLContext.newInstance(CityGmlParser.class.getClassLoader());
......@@ -166,13 +168,17 @@ public class CityGmlParser {
}
/**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* Parses a given CityGML file and collects the contained CityObject Features in
* a {@link CityDoctorModel}.
* <p/>
* The nested parse uses an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache}, which
* is not connected to the database.
* The nested parse uses an
* {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache
* UnconnectedCache}, which is not connected to the database.
* <p/>
* Nested parsing should only be used for accessing Features in auxiliary, small CityGML files, e.g. parsing of
* {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject LibraryObject} files.
* Nested parsing should only be used for accessing Features in auxiliary, small
* CityGML files, e.g. parsing of
* {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject
* LibraryObject} files.
*
* @param filePath String path to the file
* @param config Configuration of the parser
......@@ -186,10 +192,12 @@ public class CityGmlParser {
}
/**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* Parses a given CityGML file and collects the contained CityObject Features in
* a {@link CityDoctorModel}.
* <p/>
* Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references
* to {@link CityObject} are to be avoided, to allow for the memory management of the cache.
* Features are held in a non-persistent, embedded Database with an in-memory
* upstream cache. Thus, strong references to {@link CityObject} are to be
* avoided, to allow for the memory management of the cache.
*
* @param filePath String path to the file
* @param config Configuration of the parser
......@@ -202,12 +210,13 @@ public class CityGmlParser {
return parseCityGmlFile(filePath, config, null, null);
}
/**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* Parses a given CityGML file and collects the contained CityObject Features in
* a {@link CityDoctorModel}.
* <p/>
* Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references
* to {@link CityObject} are to be avoided, to allow for the memory management of the cache.
* Features are held in a non-persistent, embedded Database with an in-memory
* upstream cache. Thus, strong references to {@link CityObject} are to be
* avoided, to allow for the memory management of the cache.
*
* @param filePath String path to the file
* @param config Configuration of the parser
......@@ -222,10 +231,12 @@ public class CityGmlParser {
}
/**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* Parses a given CityGML file and collects the contained CityObject Features in
* a {@link CityDoctorModel}.
* <p/>
* Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references
* to {@link CityObject} are to be avoided, to allow for the memory management of the cache.
* Features are held in a non-persistent, embedded Database with an in-memory
* upstream cache. Thus, strong references to {@link CityObject} are to be
* avoided, to allow for the memory management of the cache.
*
* @param filePath String path to the file
* @param config Configuration of the parser
......@@ -241,18 +252,22 @@ public class CityGmlParser {
}
/**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* Parses a given CityGML file and collects the contained CityObject Features in
* a {@link CityDoctorModel}.
* <p/>
* Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references
* to {@link CityObject} are to be avoided, to allow for the memory management of the cache.
* Features are held in a non-persistent, embedded Database with an in-memory
* upstream cache. Thus, strong references to {@link CityObject} are to be
* avoided, to allow for the memory management of the cache.
*
* @param filePath String path to the file
* @param config Configuration of the parser
* @param l Listener hook for tracking of parsing progress
* @param handler Validation handler for GML-file validation
* @param verbose If verbosity is enabled the parser will write info logs to the logger
* @param unconnected If true the parser will use an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache}
* to collect the parsed CityObjects
* @param verbose If verbosity is enabled the parser will write info logs to
* the logger
* @param unconnected If true the parser will use an
* {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache
* UnconnectedCache} to collect the parsed CityObjects
* @return A {@link CityDoctorModel} object with the parsed Features
* @throws CityGmlParseException if parsing of the CityGML-file failed
* @throws InvalidGmlFileException if the GMLValidationHandler found an error
......@@ -260,15 +275,12 @@ public class CityGmlParser {
private static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l,
GMLValidationHandler handler, boolean verbose, boolean unconnected)
throws CityGmlParseException, InvalidGmlFileException {
CityGMLContext context = getContext();
CityGMLContext context = setupContext();
Path file = Paths.get(filePath);
List<String> validationIssues = Collections.emptyList();
if (config.getValidate()) {
List<String> messages = validateFile(context, handler, file);
if (!messages.isEmpty()) {
throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0));
validationIssues = validateFile(handler, file);
}
}
try {
parseEpsgCodeFromFile(file, config, verbose);
CityGMLInputFactory in = context.createCityGMLInputFactory()
......@@ -277,15 +289,17 @@ public class CityGmlParser {
if (l != null) {
ois.addListener(l::updateProgress);
}
return readAndKeepFeatures(config, file, in, ois, verbose, unconnected);
CityDoctorModel model = readAndKeepFeatures(config, file, in, ois, verbose, unconnected);
for (String xmlError : validationIssues) {
model.addGlobalError(new XMLValidationError(xmlError));
}
return model;
}
} catch (CityGMLReadException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e);
}
}
public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer,
String outputFile) throws CityGmlParseException {
Path f = Paths.get(file);
......@@ -338,18 +352,18 @@ public class CityGmlParser {
private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file, false);
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile);
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile, config, file);
}
private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
getContext();
CityGmlConsumer cityObjectConsumer, String outputFile, ParserConfiguration config, Path file)
throws CityGMLReadException {
setupContext();
CityGMLInputFactory inputFactory = context.createCityGMLInputFactory()
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
CityGMLChunkWriter writer = null;
try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) {
CityDoctorModel model = mapper.getModel();
boolean isInitialized = false;
while (reader.hasNext()) {
......@@ -414,7 +428,7 @@ public class CityGmlParser {
if (outputFile == null) {
return null;
}
CityGMLContext gmlContext = CityGmlParser.getContext();
CityGMLContext gmlContext = CityGmlParser.setupContext();
CityGMLVersion version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI());
CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(version);
CityGMLChunkWriter writer = factory.createCityGMLChunkWriter(new File(outputFile),
......@@ -428,8 +442,8 @@ public class CityGmlParser {
}
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file,
CityGMLInputFactory inputFactory, ObservedInputStream ois,
boolean verbose, boolean unconnected) throws CityGMLReadException {
CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose, boolean unconnected)
throws CityGMLReadException {
return readAndKeepModel(new Citygml3FeatureMapper(config, file, unconnected), inputFactory, ois, verbose);
}
......@@ -444,7 +458,7 @@ public class CityGmlParser {
CityModel cgmlModel = null;
CityDoctorModel cdModel = mapper.getModel();
AutoCloseable parsingContext = cdModel.getCache().startParsingMode();
try (parsingContext){
try (parsingContext) {
List<AbstractCityObject> queuedObjects = new ArrayList<>();
while (reader.hasNext()) {
AbstractFeature chunk = reader.next();
......@@ -469,7 +483,7 @@ public class CityGmlParser {
if (cgmlModel == null) {
// Add object to queue till the CityModel has been parsed
queuedObjects.add(aco);
}else if (!cdModel.containsGmlId(id)){
} else if (!cdModel.containsGmlId(id)) {
cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
}
}
......@@ -482,7 +496,8 @@ public class CityGmlParser {
mapper.setCityModel(new CityModel());
for (AbstractCityObject aco : queuedObjects) {
if (!cdModel.containsGmlId(aco.getId())) {
mapper.getModel().getCityModel().getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
mapper.getModel().getCityModel().getCityObjectMembers()
.add(new AbstractCityObjectProperty(aco));
}
}
}
......@@ -492,6 +507,7 @@ public class CityGmlParser {
logger.info(Localization.getText("CityGmlParser.parsedObjects"),
mapper.getModel().getNumberOfFeatures());
}
mapper.checkUnresolvedReferences();
mapper.setCityGMLVersion(version);
return mapper.getModel();
} catch (Exception e) {
......@@ -501,7 +517,8 @@ public class CityGmlParser {
}
}
private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config, boolean verbose) throws CityGmlParseException {
private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config, boolean verbose)
throws CityGmlParseException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) {
parseEpsgCodeFromStream(bis, config, verbose);
} catch (ParserConfigurationException | SAXException | IOException e) {
......@@ -668,11 +685,12 @@ public class CityGmlParser {
return CRS_FACTORY.createFromParameters(crsName, params.toArray(new String[0]));
}
private static List<String> validateFile(CityGMLContext context, GMLValidationHandler handler, Path file)
public static List<String> validateFile(GMLValidationHandler handler, Path file)
throws CityGmlParseException {
if (handler == null) {
handler = new GMLValidationHandler();
}
setupContext();
try {
SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler());
readAdditionalSchemaDefinitions(context, file, schemaHandler);
......@@ -691,8 +709,7 @@ public class CityGmlParser {
private static void readAdditionalSchemaDefinitions(CityGMLContext context, Path file, SchemaHandler schemaHandler)
throws CityGmlParseException {
try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects())
.withSchemaHandler(schemaHandler)
try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()).withSchemaHandler(schemaHandler)
.createReader(file)) {
reader.nextTag();
} catch (Exception e) {
......@@ -710,5 +727,4 @@ public class CityGmlParser {
}
}
......@@ -41,7 +41,6 @@ import java.util.Set;
*/
public final class CityGmlUtils {
private CityGmlUtils() {
// util class
}
......@@ -91,7 +90,6 @@ public final class CityGmlUtils {
return new CompositeSurface(surfaces);
}
public static org.xmlobjects.gml.model.geometry.primitives.LinearRing createGmlRing(GeometryFactory factory,
ParserConfiguration config, LinearRing lr) {
......@@ -130,7 +128,8 @@ public final class CityGmlUtils {
List<SurfaceProperty> surfaceMember = comp.getSurfaceMembers();
for (Polygon cdPoly : geom.getPolygons()) {
if (!cdPoly.isLink()) {
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly,
config);
if (gmlPoly != null) {
surfaceMember.add(new SurfaceProperty(gmlPoly));
}
......@@ -150,8 +149,7 @@ public final class CityGmlUtils {
return solid;
}
public static MultiSurface createMultiSurface(Geometry geom, GeometryFactory factory,
ParserConfiguration config) {
public static MultiSurface createMultiSurface(Geometry geom, GeometryFactory factory, ParserConfiguration config) {
if (geom.getType() != GeometryType.MULTI_SURFACE && geom.getType() != GeometryType.COMPOSITE_SURFACE) {
throw new IllegalArgumentException("This can only handle MultiSurfaces");
}
......@@ -162,7 +160,8 @@ public final class CityGmlUtils {
compositeCollections.add(conc.getPartOfComposite());
} else if (!cdPoly.isLink()) {
// is not part of a boundary surface
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly,
config);
if (gmlPoly != null) {
surfaces.add(new SurfaceProperty(gmlPoly));
}
......
......@@ -6,7 +6,7 @@ import de.hft.stuttgart.citydoctor2.check.Checkable;
/**
* This Visitor removes the check results from a CityObject and the objects in its datastructure.
*/
public class ClearVisitorResultsVisitor extends CheckableUtilsVisitor {
public class ClearResultsVisitor extends CheckableUtilsVisitor {
@Override
public void check(Checkable checkable) {
......
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