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

Merge branch 'dev' into 'master'

CityDoctor Version 3.18.2

See merge request !40
parents 947020ff 4b27a7e4
Pipeline #12385 passed with stage
in 2 minutes and 4 seconds
...@@ -32,6 +32,5 @@ public interface CityGmlConsumer { ...@@ -32,6 +32,5 @@ public interface CityGmlConsumer {
} }
// public void endOfFile();
} }
...@@ -28,6 +28,7 @@ import java.nio.file.Path; ...@@ -28,6 +28,7 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
...@@ -43,8 +44,6 @@ import javax.xml.validation.Schema; ...@@ -43,8 +44,6 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator; 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.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;
...@@ -84,8 +83,11 @@ import org.xmlobjects.schema.SchemaHandlerException; ...@@ -84,8 +83,11 @@ 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.check.error.XMLValidationError;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper; import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.Vector3d;
...@@ -100,615 +102,623 @@ import de.hft.stuttgart.quality.QualityADEModule; ...@@ -100,615 +102,623 @@ import de.hft.stuttgart.quality.QualityADEModule;
*/ */
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 Proj4FileReader csReader = new Proj4FileReader(); private static Proj4FileReader csReader = new Proj4FileReader();
private static final Pattern P_VUNITS = Pattern.compile("^\\+vunits=.+"); private static final Pattern P_VUNITS = Pattern.compile("^\\+vunits=.+");
private static final Pattern P_GEOIDGRID = Pattern.compile("^\\+geoidgrid=.+"); private static final Pattern P_GEOIDGRID = Pattern.compile("^\\+geoidgrid=.+");
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",
System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); "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 setupContext() {
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(Localization.getText("CityGmlParser.creating4jContextFailed"), e); logger.fatal(Localization.getText("CityGmlParser.creating4jContextFailed"), 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(Localization.getText("CityGmlParser.creatingAdePluginsFailed"), e); logger.fatal(Localization.getText("CityGmlParser.creatingAdePluginsFailed"), 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;
} }
/** /**
* 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
* <p/> * a {@link CityDoctorModel}.
* The nested parse uses an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache}, which * <p/>
* is not connected to the database. * The nested parse uses an
* <p/> * {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache
* Nested parsing should only be used for accessing Features in auxiliary, small CityGML files, e.g. parsing of * UnconnectedCache}, which is not connected to the database.
* {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject LibraryObject} files. * <p/>
* * Nested parsing should only be used for accessing Features in auxiliary, small
* @param filePath String path to the file * CityGML files, e.g. parsing of
* @param config Configuration of the parser * {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject
* @return A {@link CityDoctorModel} object with the parsed Features * LibraryObject} files.
* @throws CityGmlParseException if parsing of the CityGML-file failed *
* @throws InvalidGmlFileException if the GMLValidationHandler found an error * @param filePath String path to the file
*/ * @param config Configuration of the parser
public static CityDoctorModel nestedCityGmlFileParse(String filePath, ParserConfiguration config) * @return A {@link CityDoctorModel} object with the parsed Features
throws CityGmlParseException, InvalidGmlFileException { * @throws CityGmlParseException if parsing of the CityGML-file failed
return parseCityGmlFile(filePath, config, null, null, false, true); * @throws InvalidGmlFileException if the GMLValidationHandler found an error
} */
public static CityDoctorModel nestedCityGmlFileParse(String filePath, ParserConfiguration config)
/** throws CityGmlParseException, InvalidGmlFileException {
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. return parseCityGmlFile(filePath, config, null, null, false, true);
* <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. /**
* * Parses a given CityGML file and collects the contained CityObject Features in
* @param filePath String path to the file * a {@link CityDoctorModel}.
* @param config Configuration of the parser * <p/>
* @return A {@link CityDoctorModel} object with the parsed Features * Features are held in a non-persistent, embedded Database with an in-memory
* @throws CityGmlParseException if parsing of the CityGML-file failed * upstream cache. Thus, strong references to {@link CityObject} are to be
* @throws InvalidGmlFileException if the GMLValidationHandler found an error * avoided, to allow for the memory management of the cache.
*/ *
public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config) * @param filePath String path to the file
throws CityGmlParseException, InvalidGmlFileException { * @param config Configuration of the parser
return parseCityGmlFile(filePath, config, null, null); * @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
*/
/** public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config)
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. throws CityGmlParseException, InvalidGmlFileException {
* <p/> return parseCityGmlFile(filePath, config, null, null);
* 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 * Parses a given CityGML file and collects the contained CityObject Features in
* @param config Configuration of the parser * a {@link CityDoctorModel}.
* @param l Listener hook for tracking of parsing progress * <p/>
* @return A {@link CityDoctorModel} object with the parsed Features * Features are held in a non-persistent, embedded Database with an in-memory
* @throws CityGmlParseException if parsing of the CityGML-file failed * upstream cache. Thus, strong references to {@link CityObject} are to be
* @throws InvalidGmlFileException if the GMLValidationHandler found an error * avoided, to allow for the memory management of the cache.
*/ *
public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l) * @param filePath String path to the file
throws CityGmlParseException, InvalidGmlFileException { * @param config Configuration of the parser
return parseCityGmlFile(filePath, config, l, null); * @param l Listener hook for tracking of parsing progress
} * @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
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. */
* <p/> public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l)
* Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references throws CityGmlParseException, InvalidGmlFileException {
* to {@link CityObject} are to be avoided, to allow for the memory management of the cache. return parseCityGmlFile(filePath, config, l, null);
* }
* @param filePath String path to the file
* @param config Configuration of the parser /**
* @param l Listener hook for tracking of parsing progress * Parses a given CityGML file and collects the contained CityObject Features in
* @param handler Validation handler for GML-file validation * a {@link CityDoctorModel}.
* @return A {@link CityDoctorModel} object with the parsed Features * <p/>
* @throws CityGmlParseException if parsing of the CityGML-file failed * Features are held in a non-persistent, embedded Database with an in-memory
* @throws InvalidGmlFileException if the GMLValidationHandler found an error * upstream cache. Thus, strong references to {@link CityObject} are to be
*/ * avoided, to allow for the memory management of the cache.
public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l, *
GMLValidationHandler handler) throws CityGmlParseException, InvalidGmlFileException { * @param filePath String path to the file
return parseCityGmlFile(filePath, config, l, handler, true, false); * @param config Configuration of the parser
} * @param l Listener hook for tracking of parsing progress
* @param handler Validation handler for GML-file validation
/** * @return A {@link CityDoctorModel} object with the parsed Features
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. * @throws CityGmlParseException if parsing of the CityGML-file failed
* <p/> * @throws InvalidGmlFileException if the GMLValidationHandler found an error
* 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. public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l,
* GMLValidationHandler handler) throws CityGmlParseException, InvalidGmlFileException {
* @param filePath String path to the file return parseCityGmlFile(filePath, config, l, handler, true, false);
* @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 * Parses a given CityGML file and collects the contained CityObject Features in
* @param unconnected If true the parser will use an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache} * a {@link CityDoctorModel}.
* to collect the parsed CityObjects * <p/>
* @return A {@link CityDoctorModel} object with the parsed Features * Features are held in a non-persistent, embedded Database with an in-memory
* @throws CityGmlParseException if parsing of the CityGML-file failed * upstream cache. Thus, strong references to {@link CityObject} are to be
* @throws InvalidGmlFileException if the GMLValidationHandler found an error * avoided, to allow for the memory management of the cache.
*/ *
private static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l, * @param filePath String path to the file
GMLValidationHandler handler, boolean verbose, boolean unconnected) * @param config Configuration of the parser
throws CityGmlParseException, InvalidGmlFileException { * @param l Listener hook for tracking of parsing progress
CityGMLContext context = getContext(); * @param handler Validation handler for GML-file validation
Path file = Paths.get(filePath); * @param verbose If verbosity is enabled the parser will write info logs to
if (config.getValidate()) { * the logger
List<String> messages = validateFile(context, handler, file); * @param unconnected If true the parser will use an
if (!messages.isEmpty()) { * {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache
throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0)); * 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
try { */
parseEpsgCodeFromFile(file, config, verbose); private static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l,
CityGMLInputFactory in = context.createCityGMLInputFactory() GMLValidationHandler handler, boolean verbose, boolean unconnected)
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); throws CityGmlParseException, InvalidGmlFileException {
try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { CityGMLContext context = setupContext();
if (l != null) { Path file = Paths.get(filePath);
ois.addListener(l::updateProgress); List<String> validationIssues = Collections.emptyList();
} if (config.getValidate()) {
return readAndKeepFeatures(config, file, in, ois, verbose, unconnected); validationIssues = validateFile(handler, file);
} }
} catch (CityGMLReadException | IOException e) { try {
throw new CityGmlParseException("Failed to read CityGML file", e); parseEpsgCodeFromFile(file, config, verbose);
} CityGMLInputFactory in = context.createCityGMLInputFactory()
} .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) {
if (l != null) {
ois.addListener(l::updateProgress);
public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, }
String outputFile) throws CityGmlParseException { CityDoctorModel model = readAndKeepFeatures(config, file, in, ois, verbose, unconnected);
Path f = Paths.get(file); for (String xmlError : validationIssues) {
streamCityGml(f, config, null, cityObjectConsumer, outputFile); model.addGlobalError(new XMLValidationError(xmlError));
} }
return model;
public static void streamCityGml(File file, ParserConfiguration parserConfig, CityGmlConsumer cityObjectConsumer, }
String outputFile) throws CityGmlParseException { } catch (CityGMLReadException | IOException e) {
streamCityGml(file.toPath(), parserConfig, null, cityObjectConsumer, outputFile); throw new CityGmlParseException("Failed to read CityGML file", e);
} }
}
public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException { public static void streamCityGml(File file, ParserConfiguration parserConfig, CityGmlConsumer cityObjectConsumer,
parseEpsgCodeFromFile(file, config, true); String outputFile) throws CityGmlParseException {
startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile); streamCityGml(file.toPath(), parserConfig, null, cityObjectConsumer, outputFile);
} }
public static CityModel parseOnlyCityModel(File inputFile) throws CityGmlParseException { public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l,
try { CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException {
CityGMLInputFactory inputFactory = context.createCityGMLInputFactory() parseEpsgCodeFromFile(file, config, true);
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile);
try (CityGMLReader reader = inputFactory.createCityGMLReader(inputFile)) { }
while (reader.hasNext()) {
AbstractFeature chunk = reader.next(); public static CityModel parseOnlyCityModel(File inputFile) throws CityGmlParseException {
if (chunk instanceof CityModel cModel) { try {
cModel.setCityObjectMembers(null); CityGMLInputFactory inputFactory = context.createCityGMLInputFactory()
return cModel; .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
} try (CityGMLReader reader = inputFactory.createCityGMLReader(inputFile)) {
} while (reader.hasNext()) {
} AbstractFeature chunk = reader.next();
} catch (CityGMLReadException e) { if (chunk instanceof CityModel cModel) {
throw new CityGmlParseException(e); cModel.setCityObjectMembers(null);
} return cModel;
throw new CityGmlParseException("Did not find any CityModel in CityGML file"); }
} }
}
private static void startReadingCityGmlFile(Path file, ParserConfiguration config, ProgressListener l, } catch (CityGMLReadException e) {
CityGmlConsumer cityObjectConsumer, String outputFile) { throw new CityGmlParseException(e);
try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { }
if (l != null) { throw new CityGmlParseException("Did not find any CityModel in CityGML file");
ois.addListener(l::updateProgress); }
}
readAndDiscardFeatures(file, config, ois, cityObjectConsumer, outputFile); private static void startReadingCityGmlFile(Path file, ParserConfiguration config, ProgressListener l,
} catch (IOException | CityGMLReadException e) { CityGmlConsumer cityObjectConsumer, String outputFile) {
logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage()); try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) {
logger.catching(Level.ERROR, e); if (l != null) {
} ois.addListener(l::updateProgress);
} }
readAndDiscardFeatures(file, config, ois, cityObjectConsumer, outputFile);
private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois, } catch (IOException | CityGMLReadException e) {
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage());
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file, false); logger.catching(Level.ERROR, e);
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); }
}
}
private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois,
private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois, CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file, false);
getContext(); readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile, config, file);
CityGMLInputFactory inputFactory = context.createCityGMLInputFactory()
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); }
CityGMLChunkWriter writer = null;
try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois,
CityGmlConsumer cityObjectConsumer, String outputFile, ParserConfiguration config, Path file)
CityDoctorModel model = mapper.getModel(); throws CityGMLReadException {
boolean isInitialized = false; setupContext();
while (reader.hasNext()) { CityGMLInputFactory inputFactory = context.createCityGMLInputFactory()
AbstractFeature chunk = reader.next(); .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
if (writer == null) { CityGMLChunkWriter writer = null;
writer = createCityModelWriter(outputFile, reader); try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) {
} CityDoctorModel model = mapper.getModel();
if (!isInitialized && writer != null && reader.getParentInfo() != null boolean isInitialized = false;
&& reader.getParentInfo().getTypeName().getLocalPart().equals("CityModel")) { while (reader.hasNext()) {
FeatureInfo parentInfo = reader.getParentInfo(); AbstractFeature chunk = reader.next();
writer.withCityModelInfo(parentInfo); if (writer == null) {
isInitialized = true; writer = createCityModelWriter(outputFile, reader);
} }
if (chunk instanceof AbstractCityObject ag) { if (!isInitialized && writer != null && reader.getParentInfo() != null
ag.accept(mapper); && reader.getParentInfo().getTypeName().getLocalPart().equals("CityModel")) {
drainCityModel(model, cityObjectConsumer); FeatureInfo parentInfo = reader.getParentInfo();
writeAbstractCityObject(writer, ag); writer.withCityModelInfo(parentInfo);
} else if (chunk instanceof CityModel cModel) { isInitialized = true;
cModel.setCityObjectMembers(null); }
mapper.setCityModel(cModel); if (chunk instanceof AbstractCityObject ag) {
cityObjectConsumer.accept(cModel); ag.accept(mapper);
writeCityModel(writer, cModel); drainCityModel(model, cityObjectConsumer);
} else if (writer != null) { writeAbstractCityObject(writer, ag);
writer.writeMember(chunk); } else if (chunk instanceof CityModel cModel) {
} cModel.setCityObjectMembers(null);
} mapper.setCityModel(cModel);
cityObjectConsumer.accept(cModel);
// end of stream writeCityModel(writer, cModel);
logger.debug("End of gml file stream"); } else if (writer != null) {
} catch (CityGMLReadException e) { writer.writeMember(chunk);
logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage(), e); }
} catch (CityGMLWriteException e) { }
logger.error(Localization.getText("CityGmlParser.errorWritingGmlFile"), e.getMessage(), e);
} finally { // end of stream
if (writer != null) { logger.debug("End of gml file stream");
try { } catch (CityGMLReadException e) {
writer.close(); logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage(), e);
} catch (CityGMLWriteException e) { } catch (CityGMLWriteException e) {
// ignore logger.error(Localization.getText("CityGmlParser.errorWritingGmlFile"), e.getMessage(), e);
} } finally {
} if (writer != null) {
} try {
} writer.close();
} catch (CityGMLWriteException e) {
private static void writeCityModel(CityGMLChunkWriter writer, CityModel cModel) throws CityGMLWriteException { // ignore
if (writer != null) { }
for (AbstractFeatureProperty featureProp : cModel.getFeatureMembers()) { }
writer.writeMember(featureProp.getObject()); }
} }
}
} private static void writeCityModel(CityGMLChunkWriter writer, CityModel cModel) throws CityGMLWriteException {
if (writer != null) {
private static void writeAbstractCityObject(CityGMLChunkWriter writer, AbstractCityObject ag) for (AbstractFeatureProperty featureProp : cModel.getFeatureMembers()) {
throws CityGMLWriteException { writer.writeMember(featureProp.getObject());
if (writer != null) { }
writer.writeMember(ag); }
} }
}
private static void writeAbstractCityObject(CityGMLChunkWriter writer, AbstractCityObject ag)
private static CityGMLChunkWriter createCityModelWriter(String outputFile, CityGMLReader reader) throws CityGMLWriteException {
throws CityGMLWriteException { if (writer != null) {
if (outputFile == null) { writer.writeMember(ag);
return null; }
} }
CityGMLContext gmlContext = CityGmlParser.getContext();
CityGMLVersion version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); private static CityGMLChunkWriter createCityModelWriter(String outputFile, CityGMLReader reader)
CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(version); throws CityGMLWriteException {
CityGMLChunkWriter writer = factory.createCityGMLChunkWriter(new File(outputFile), if (outputFile == null) {
StandardCharsets.UTF_8.name()); return null;
writer.withPrefix("qual", QualityADEModule.NAMESPACE_URI); }
writer.withSchemaLocation(QualityADEModule.NAMESPACE_URI, QualityADEModule.NAMESPACE_URI + "/qualityAde.xsd"); CityGMLContext gmlContext = CityGmlParser.setupContext();
writer.withIndent(" "); CityGMLVersion version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI());
writer.withDefaultPrefixes(); CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(version);
writer.withDefaultSchemaLocations(); CityGMLChunkWriter writer = factory.createCityGMLChunkWriter(new File(outputFile),
return writer; StandardCharsets.UTF_8.name());
} writer.withPrefix("qual", QualityADEModule.NAMESPACE_URI);
writer.withSchemaLocation(QualityADEModule.NAMESPACE_URI, QualityADEModule.NAMESPACE_URI + "/qualityAde.xsd");
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file, writer.withIndent(" ");
CityGMLInputFactory inputFactory, ObservedInputStream ois, writer.withDefaultPrefixes();
boolean verbose, boolean unconnected) throws CityGMLReadException { writer.withDefaultSchemaLocations();
return readAndKeepModel(new Citygml3FeatureMapper(config, file, unconnected), inputFactory, ois, verbose); return writer;
} }
private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory, private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file,
ObservedInputStream ois, boolean verbose) throws CityGMLReadException { CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose, boolean unconnected)
try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { throws CityGMLReadException {
CityGMLVersion version = null; return readAndKeepModel(new Citygml3FeatureMapper(config, file, unconnected), inputFactory, ois, verbose);
// model is read in chunked mode }
// object members are replaced by href in model
// need to remove the refs and re-add unparsed objects private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory,
List<AbstractCityObject> acos = new ArrayList<>(); ObservedInputStream ois, boolean verbose) throws CityGMLReadException {
CityModel cgmlModel = null; try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) {
CityDoctorModel cdModel = mapper.getModel(); CityGMLVersion version = null;
AutoCloseable parsingContext = cdModel.getCache().startParsingMode(); // model is read in chunked mode
try (parsingContext){ // object members are replaced by href in model
List<AbstractCityObject> queuedObjects = new ArrayList<>(); // need to remove the refs and re-add unparsed objects
while (reader.hasNext()) { List<AbstractCityObject> acos = new ArrayList<>();
AbstractFeature chunk = reader.next(); CityModel cgmlModel = null;
version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); CityDoctorModel cdModel = mapper.getModel();
if (chunk instanceof CityModel cModel) { AutoCloseable parsingContext = cdModel.getCache().startParsingMode();
cModel.setCityObjectMembers(null); try (parsingContext) {
mapper.setCityModel(cModel); List<AbstractCityObject> queuedObjects = new ArrayList<>();
mapper.setCityGMLVersion(version); while (reader.hasNext()) {
cgmlModel = cModel; AbstractFeature chunk = reader.next();
version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI());
if (!queuedObjects.isEmpty()) { if (chunk instanceof CityModel cModel) {
for (AbstractCityObject aco : queuedObjects) { cModel.setCityObjectMembers(null);
if (!cdModel.containsGmlId(aco.getId())) { mapper.setCityModel(cModel);
cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); mapper.setCityGMLVersion(version);
} cgmlModel = cModel;
}
} if (!queuedObjects.isEmpty()) {
} else if (chunk instanceof AbstractCityObject aco) { for (AbstractCityObject aco : queuedObjects) {
acos.add(aco); // getid if (!cdModel.containsGmlId(aco.getId())) {
String id = aco.getId(); cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
aco.accept(mapper); }
if (cgmlModel == null) { }
// Add object to queue till the CityModel has been parsed }
queuedObjects.add(aco); } else if (chunk instanceof AbstractCityObject aco) {
}else if (!cdModel.containsGmlId(id)){ acos.add(aco); // getid
cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); String id = aco.getId();
} aco.accept(mapper);
} if (cgmlModel == null) {
} // Add object to queue till the CityModel has been parsed
queuedObjects.add(aco);
if (mapper.getModel().getCityModel() == null) { } else if (!cdModel.containsGmlId(id)) {
// file does not contain a city model? cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
// create it for now }
logger.warn(Localization.getText("CityGmlParser.missingCityModel")); }
mapper.setCityModel(new CityModel()); }
for (AbstractCityObject aco : queuedObjects) {
if (!cdModel.containsGmlId(aco.getId())) { if (mapper.getModel().getCityModel() == null) {
mapper.getModel().getCityModel().getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); // file does not contain a city model?
} // create it for now
} logger.warn(Localization.getText("CityGmlParser.missingCityModel"));
} mapper.setCityModel(new CityModel());
for (AbstractCityObject aco : queuedObjects) {
ImplicitGeometryHolder.resolveDeferredObjects(mapper.getModel().getCache()); if (!cdModel.containsGmlId(aco.getId())) {
if (logger.isInfoEnabled() && verbose) { mapper.getModel().getCityModel().getCityObjectMembers()
logger.info(Localization.getText("CityGmlParser.parsedObjects"), .add(new AbstractCityObjectProperty(aco));
mapper.getModel().getNumberOfFeatures()); }
} }
mapper.setCityGMLVersion(version); }
return mapper.getModel();
} catch (Exception e) { ImplicitGeometryHolder.resolveDeferredObjects(mapper.getModel().getCache());
throw new CityGMLReadException(e); if (logger.isInfoEnabled() && verbose) {
} logger.info(Localization.getText("CityGmlParser.parsedObjects"),
mapper.getModel().getNumberOfFeatures());
} }
} mapper.checkUnresolvedReferences();
mapper.setCityGMLVersion(version);
private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config, boolean verbose) throws CityGmlParseException { return mapper.getModel();
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) { } catch (Exception e) {
parseEpsgCodeFromStream(bis, config, verbose); throw new CityGMLReadException(e);
} catch (ParserConfigurationException | SAXException | IOException e) { }
throw new CityGmlParseException("Failed to read CityGML file", e);
} }
} }
private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config, boolean verbose) private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config, boolean verbose)
throws ParserConfigurationException, SAXException { throws CityGmlParseException {
SAXParser parser = FACTORY.newSAXParser(); try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) {
CityGmlHandler handler = new CityGmlHandler(); parseEpsgCodeFromStream(bis, config, verbose);
try { } catch (ParserConfigurationException | SAXException | IOException e) {
parser.parse(new InputSource(is), handler); throw new CityGmlParseException("Failed to read CityGML file", e);
} catch (EnvelopeFoundException e) { }
try { }
parseCoordinateSystem(config, handler, verbose);
} catch (Exception e2) { private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config, boolean verbose)
logEpsgParseError(e2); throws ParserConfigurationException, SAXException {
} SAXParser parser = FACTORY.newSAXParser();
} catch (Exception e) { CityGmlHandler handler = new CityGmlHandler();
logEpsgParseError(e); try {
} parser.parse(new InputSource(is), handler);
if (handler.getEpsg() == null && logger.isInfoEnabled() && verbose) { } catch (EnvelopeFoundException e) {
logger.info(Localization.getText("CityGmlParser.missingEPSGCode")); try {
} parseCoordinateSystem(config, handler, verbose);
} catch (Exception e2) {
} logEpsgParseError(e2);
}
private static void logEpsgParseError(Exception e) { } catch (Exception e) {
logger.debug("Exception while parsing for EPSG code", e); logEpsgParseError(e);
if (logger.isWarnEnabled()) { }
logger.warn(Localization.getText("CityGmlParser.failedEPSGParse")); if (handler.getEpsg() == null && logger.isInfoEnabled() && verbose) {
} logger.info(Localization.getText("CityGmlParser.missingEPSGCode"));
} }
private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler, boolean verbose) { }
if (handler.getEpsg() == null) {
return; private static void logEpsgParseError(Exception e) {
} logger.debug("Exception while parsing for EPSG code", e);
CoordinateReferenceSystem crs = crsFromSrsName(handler.getEpsg()); if (logger.isWarnEnabled()) {
if (crs == null) { logger.warn(Localization.getText("CityGmlParser.failedEPSGParse"));
// could not find a coordinate system for srsName }
// assuming metric system }
if (logger.isInfoEnabled() && verbose) {
logger.info(Localization.getText("CityGmlParser.missingEPSGCode")); private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler, boolean verbose) {
} if (handler.getEpsg() == null) {
return; return;
} }
if (crs.getProjection().getUnits() == Units.METRES) { CoordinateReferenceSystem crs = crsFromSrsName(handler.getEpsg());
// coordinate system is in meters, do not convert if (crs == null) {
if (logger.isInfoEnabled() && verbose) { // could not find a coordinate system for srsName
logger.info(Localization.getText("CityGmlParser.noConversionNeeded")); // assuming metric system
} if (logger.isInfoEnabled() && verbose) {
return; logger.info(Localization.getText("CityGmlParser.missingEPSGCode"));
} }
parseMeterConversion(config, crs); return;
Vector3d low = handler.getLowerCorner(); }
Vector3d up = handler.getUpperCorner(); if (crs.getProjection().getUnits() == Units.METRES) {
double centerLong = low.getX() + ((up.getX() - low.getX()) / 2); // coordinate system is in meters, do not convert
double centerLat = low.getY() + ((up.getY() - low.getY()) / 2); if (logger.isInfoEnabled() && verbose) {
if (!crs.getName().equals(WGS_84)) { logger.info(Localization.getText("CityGmlParser.noConversionNeeded"));
// need to convert coordinates first to WGS84, then find UTM Zone }
CoordinateReferenceSystem wgs84 = crsFromSrsName(WGS_84); return;
ProjCoordinate p1 = new ProjCoordinate(); }
p1.setValue(centerLong, centerLat); parseMeterConversion(config, crs);
ProjCoordinate p2 = new ProjCoordinate(); Vector3d low = handler.getLowerCorner();
BasicCoordinateTransform bct = new BasicCoordinateTransform(crs, wgs84); Vector3d up = handler.getUpperCorner();
bct.transform(p1, p2); double centerLong = low.getX() + ((up.getX() - low.getX()) / 2);
centerLong = p2.x; double centerLat = low.getY() + ((up.getY() - low.getY()) / 2);
centerLat = p2.y; if (!crs.getName().equals(WGS_84)) {
} // need to convert coordinates first to WGS84, then find UTM Zone
int zone = (int) (31 + Math.round(centerLong / 6)); CoordinateReferenceSystem wgs84 = crsFromSrsName(WGS_84);
CoordinateReferenceSystem utm; ProjCoordinate p1 = new ProjCoordinate();
if (centerLat < 0) { p1.setValue(centerLong, centerLat);
// south ProjCoordinate p2 = new ProjCoordinate();
if (logger.isInfoEnabled() && verbose) { BasicCoordinateTransform bct = new BasicCoordinateTransform(crs, wgs84);
logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneS"), zone); bct.transform(p1, p2);
} centerLong = p2.x;
utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south"); centerLat = p2.y;
} else { }
// north int zone = (int) (31 + Math.round(centerLong / 6));
if (logger.isInfoEnabled() && verbose) { CoordinateReferenceSystem utm;
logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneN"), zone); if (centerLat < 0) {
} // south
utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone); if (logger.isInfoEnabled() && verbose) {
} logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneS"), zone);
config.setCoordinateSystem(crs, utm); }
} utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south");
} else {
private static void parseMeterConversion(ParserConfiguration config, CoordinateReferenceSystem crs) { // north
Projection projection = crs.getProjection(); if (logger.isInfoEnabled() && verbose) {
double fromMetres = projection.getFromMetres(); logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneN"), zone);
if (fromMetres > 0) { }
// also transform height information utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone);
config.setFromMeters(fromMetres); }
} else { config.setCoordinateSystem(crs, utm);
config.setFromMeters(1.0); }
}
} private static void parseMeterConversion(ParserConfiguration config, CoordinateReferenceSystem crs) {
Projection projection = crs.getProjection();
/** double fromMetres = projection.getFromMetres();
* The srsName (The name by which this reference system is identified) inside if (fromMetres > 0) {
* the CityGML file can have multiple formats. This method tries to parse the // also transform height information
* string and detect the corresponding reference system. If it is found, it config.setFromMeters(fromMetres);
* returns a proj4j.CoordinateReferenceSystem. It throws an } else {
* IllegalArgumentException otherwise. config.setFromMeters(1.0);
* <p> }
* 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 srsName (The name by which this reference system is identified) inside
* <p> * the CityGML file can have multiple formats. This method tries to parse the
* The EPSG ids and parameters are defined in resources ('nad/epsg') inside * string and detect the corresponding reference system. If it is found, it
* proj4j-0.1.0.jar. Some EPSG ids are missing though, e.g. 7415 * returns a proj4j.CoordinateReferenceSystem. It throws an
* * IllegalArgumentException otherwise.
* @param srsName * <p>
* @return CoordinateReferenceSystem * 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
private static CoordinateReferenceSystem crsFromSrsName(String srsName) { * those are also included. It isn't guaranteed that those formats are correctly
srsName = srsName.trim(); * parsed, though.
Matcher mEPSG = P_EPSG.matcher(srsName); * <p>
if (mEPSG.find()) { * The EPSG ids and parameters are defined in resources ('nad/epsg') inside
if ("EPSG:4979".contentEquals(srsName)) { * proj4j-0.1.0.jar. Some EPSG ids are missing though, e.g. 7415
srsName = "EPSG:4236"; *
} else if ("EPSG:7415".contentEquals(srsName)) { * @param srsName
return CRS_FACTORY.createFromParameters("EPSG:7415", * @return CoordinateReferenceSystem
"+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"); */
} private static CoordinateReferenceSystem crsFromSrsName(String srsName) {
return CRS_FACTORY.createFromName(srsName); srsName = srsName.trim();
} Matcher mEPSG = P_EPSG.matcher(srsName);
if (mEPSG.find()) {
Matcher mOGC = P_OGC.matcher(srsName); if ("EPSG:4979".contentEquals(srsName)) {
if (mOGC.find()) { srsName = "EPSG:4236";
return getCrsFromOGCSrsCode(mOGC.group(1)); } else if ("EPSG:7415".contentEquals(srsName)) {
} return CRS_FACTORY.createFromParameters("EPSG:7415",
Matcher mOGC2 = P_OGC2.matcher(srsName); "+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 (mOGC2.find()) { }
return getCrsFromOGCSrsCode(mOGC2.group(1)); return CRS_FACTORY.createFromName(srsName);
}
}
Matcher mURN = P_URN.matcher(srsName); Matcher mOGC = P_OGC.matcher(srsName);
// NOTE: Could use a HashMap if the switch/case becomes too long. if (mOGC.find()) {
if (mURN.find()) { return getCrsFromOGCSrsCode(mOGC.group(1));
return switch (mURN.group(1)) { }
case "DE_DHDN_3GK2" -> CRS_FACTORY.createFromName("EPSG:31466"); Matcher mOGC2 = P_OGC2.matcher(srsName);
case "DE_DHDN_3GK3" -> CRS_FACTORY.createFromName("EPSG:31467"); if (mOGC2.find()) {
case "DE_DHDN_3GK4" -> CRS_FACTORY.createFromName("EPSG:31468"); return getCrsFromOGCSrsCode(mOGC2.group(1));
case "DE_DHDN_3GK5" -> CRS_FACTORY.createFromName("EPSG:31469");
case "ETRS89_UTM32" -> CRS_FACTORY.createFromName("EPSG:25832"); }
default -> null; Matcher mURN = P_URN.matcher(srsName);
}; // NOTE: Could use a HashMap if the switch/case becomes too long.
} if (mURN.find()) {
if (srsName.equals("http://www.opengis.net/def/crs/EPSG/0/6697")) { return switch (mURN.group(1)) {
return CRS_FACTORY.createFromParameters("EPSG:6697", "+proj=longlat +ellps=GRS80 +no_defs +axis=neu"); case "DE_DHDN_3GK2" -> CRS_FACTORY.createFromName("EPSG:31466");
} case "DE_DHDN_3GK3" -> CRS_FACTORY.createFromName("EPSG:31467");
return null; case "DE_DHDN_3GK4" -> CRS_FACTORY.createFromName("EPSG:31468");
} case "DE_DHDN_3GK5" -> CRS_FACTORY.createFromName("EPSG:31469");
case "ETRS89_UTM32" -> CRS_FACTORY.createFromName("EPSG:25832");
private static CoordinateReferenceSystem getCrsFromOGCSrsCode(String srsCode) { default -> null;
String crsName = "EPSG:" + srsCode; };
List<String> params = new ArrayList<>(Arrays.stream(csReader.getParameters(crsName)).toList()); }
params.removeIf(P_VUNITS.asPredicate()); if (srsName.equals("http://www.opengis.net/def/crs/EPSG/0/6697")) {
params.removeIf(P_GEOIDGRID.asPredicate()); return CRS_FACTORY.createFromParameters("EPSG:6697", "+proj=longlat +ellps=GRS80 +no_defs +axis=neu");
return CRS_FACTORY.createFromParameters(crsName, params.toArray(new String[0])); }
} return null;
}
private static List<String> validateFile(CityGMLContext context, GMLValidationHandler handler, Path file)
throws CityGmlParseException { private static CoordinateReferenceSystem getCrsFromOGCSrsCode(String srsCode) {
if (handler == null) { String crsName = "EPSG:" + srsCode;
handler = new GMLValidationHandler(); List<String> params = new ArrayList<>(Arrays.stream(csReader.getParameters(crsName)).toList());
} params.removeIf(P_VUNITS.asPredicate());
try { params.removeIf(P_GEOIDGRID.asPredicate());
SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler()); return CRS_FACTORY.createFromParameters(crsName, params.toArray(new String[0]));
readAdditionalSchemaDefinitions(context, file, schemaHandler); }
Source[] schemas = schemaHandler.getSchemas();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); public static List<String> validateFile(GMLValidationHandler handler, Path file)
schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); throws CityGmlParseException {
Schema schema = schemaFactory.newSchema(schemas); if (handler == null) {
Validator validator = schema.newValidator(); handler = new GMLValidationHandler();
validator.setErrorHandler(handler); }
validator.validate(new StreamSource(file.toFile())); setupContext();
return handler.getMessages(); try {
} catch (SchemaHandlerException | SAXException | IOException e) { SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler());
throw new CityGmlParseException("Failed to validate CityGML file", e); readAdditionalSchemaDefinitions(context, file, 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);
private static void readAdditionalSchemaDefinitions(CityGMLContext context, Path file, SchemaHandler schemaHandler) Schema schema = schemaFactory.newSchema(schemas);
throws CityGmlParseException { Validator validator = schema.newValidator();
try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()) validator.setErrorHandler(handler);
.withSchemaHandler(schemaHandler) validator.validate(new StreamSource(file.toFile()));
.createReader(file)) { return handler.getMessages();
reader.nextTag(); } catch (SchemaHandlerException | SAXException | IOException e) {
} catch (Exception e) { throw new CityGmlParseException("Failed to validate CityGML file", e);
throw new CityGmlParseException("Failed to read file " + file.toAbsolutePath() + ".", e); }
} }
}
private static void readAdditionalSchemaDefinitions(CityGMLContext context, Path file, SchemaHandler schemaHandler)
private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) { throws CityGmlParseException {
List<GmlId> id = new ArrayList<>(); try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()).withSchemaHandler(schemaHandler)
model.createFeatureStream().forEach(co -> { .createReader(file)) {
cityObjectConsumer.accept(co); reader.nextTag();
id.add(co.getGmlId()); } catch (Exception e) {
}); throw new CityGmlParseException("Failed to read file " + file.toAbsolutePath() + ".", e);
id.forEach(model::removeFeature); }
}
}
private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) {
List<GmlId> id = new ArrayList<>();
model.createFeatureStream().forEach(co -> {
cityObjectConsumer.accept(co);
id.add(co.getGmlId());
});
id.forEach(model::removeFeature);
}
} }
...@@ -41,175 +41,174 @@ import java.util.Set; ...@@ -41,175 +41,174 @@ import java.util.Set;
*/ */
public final class CityGmlUtils { public final class CityGmlUtils {
private CityGmlUtils() {
private CityGmlUtils() { // util class
// util class }
}
public static org.xmlobjects.gml.model.geometry.primitives.Polygon createGmlPolygon(GeometryFactory factory,
public static org.xmlobjects.gml.model.geometry.primitives.Polygon createGmlPolygon(GeometryFactory factory, Polygon cdPoly, ParserConfiguration config) {
Polygon cdPoly, ParserConfiguration config) { if (cdPoly.getExteriorRing() == null) {
if (cdPoly.getExteriorRing() == null) { return null;
return null; }
} org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = new org.xmlobjects.gml.model.geometry.primitives.Polygon();
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = new org.xmlobjects.gml.model.geometry.primitives.Polygon(); // exterior ring
// exterior ring LinearRing extLr = cdPoly.getExteriorRing();
LinearRing extLr = cdPoly.getExteriorRing(); if (extLr.getVertices().size() < 3) {
if (extLr.getVertices().size() < 3) { // this ring does not have enough points in it
// this ring does not have enough points in it // this leads to errors when exporting therefore ignore it
// this leads to errors when exporting therefore ignore it return null;
return null; }
} org.xmlobjects.gml.model.geometry.primitives.LinearRing gmlLr = createGmlRing(factory, config, extLr);
org.xmlobjects.gml.model.geometry.primitives.LinearRing gmlLr = createGmlRing(factory, config, extLr); gmlPoly.setExterior(new AbstractRingProperty(gmlLr));
gmlPoly.setExterior(new AbstractRingProperty(gmlLr));
// interior rings
// interior rings for (LinearRing lr : cdPoly.getInnerRings()) {
for (LinearRing lr : cdPoly.getInnerRings()) { gmlLr = createGmlRing(factory, config, lr);
gmlLr = createGmlRing(factory, config, lr); if (lr.getVertices().size() < 3) {
if (lr.getVertices().size() < 3) { // this ring does not have enough points in it
// this ring does not have enough points in it // this leads to errors when exporting therefore ignore it
// this leads to errors when exporting therefore ignore it return null;
return null; }
} gmlPoly.getInterior().add(new AbstractRingProperty(gmlLr));
gmlPoly.getInterior().add(new AbstractRingProperty(gmlLr)); }
} gmlPoly.setId(cdPoly.getGmlId().getGmlString());
gmlPoly.setId(cdPoly.getGmlId().getGmlString()); return gmlPoly;
return gmlPoly; }
}
public static CompositeSurface createGmlComposite(GeometryFactory factory, CompositeCollection comp,
public static CompositeSurface createGmlComposite(GeometryFactory factory, CompositeCollection comp, ParserConfiguration config) {
ParserConfiguration config) { List<SurfaceProperty> surfaces = new ArrayList<>();
List<SurfaceProperty> surfaces = new ArrayList<>(); for (ConcretePolygon cd : comp.getNonRecursiveCompositeMembers()) {
for (ConcretePolygon cd : comp.getNonRecursiveCompositeMembers()) { surfaces.add(new SurfaceProperty(createGmlPolygon(factory, cd, config)));
surfaces.add(new SurfaceProperty(createGmlPolygon(factory, cd, config))); }
} for (CompositeCollection cc : comp.getChildComposites()) {
for (CompositeCollection cc : comp.getChildComposites()) { surfaces.add(new SurfaceProperty(createGmlComposite(factory, cc, config)));
surfaces.add(new SurfaceProperty(createGmlComposite(factory, cc, config))); }
} if (surfaces.isEmpty()) {
if (surfaces.isEmpty()) { return null;
return null; }
} return new CompositeSurface(surfaces);
return new CompositeSurface(surfaces); }
}
public static org.xmlobjects.gml.model.geometry.primitives.LinearRing createGmlRing(GeometryFactory factory,
ParserConfiguration config, LinearRing lr) {
public static org.xmlobjects.gml.model.geometry.primitives.LinearRing createGmlRing(GeometryFactory factory,
ParserConfiguration config, LinearRing lr) { ProjCoordinate p1 = new ProjCoordinate();
ProjCoordinate p2 = new ProjCoordinate();
ProjCoordinate p1 = new ProjCoordinate(); List<Double> ringValues = new ArrayList<>();
ProjCoordinate p2 = new ProjCoordinate(); BasicCoordinateTransform trans = config.getOriginalTransform();
List<Double> ringValues = new ArrayList<>(); for (Vertex v : lr.getVertices()) {
BasicCoordinateTransform trans = config.getOriginalTransform(); double x = v.getX();
for (Vertex v : lr.getVertices()) { double y = v.getY();
double x = v.getX(); double z = v.getZ();
double y = v.getY(); if (trans != null) {
double z = v.getZ(); p1.x = x;
if (trans != null) { p1.y = y;
p1.x = x; trans.transform(p1, p2);
p1.y = y; x = p2.x;
trans.transform(p1, p2); y = p2.y;
x = p2.x; z = z * config.getFromMeters();
y = p2.y; }
z = z * config.getFromMeters(); ringValues.add(x);
} ringValues.add(y);
ringValues.add(x); ringValues.add(z);
ringValues.add(y); }
ringValues.add(z);
} org.xmlobjects.gml.model.geometry.primitives.LinearRing gmlLr = factory.createLinearRing(ringValues, 3);
gmlLr.setId(lr.getGmlId().getGmlString());
org.xmlobjects.gml.model.geometry.primitives.LinearRing gmlLr = factory.createLinearRing(ringValues, 3); return gmlLr;
gmlLr.setId(lr.getGmlId().getGmlString()); }
return gmlLr;
} public static Solid createSolid(Geometry geom, GeometryFactory factory, ParserConfiguration config) {
if (geom.getType() != GeometryType.SOLID) {
public static Solid createSolid(Geometry geom, GeometryFactory factory, ParserConfiguration config) { throw new IllegalArgumentException("Only solids are allowed");
if (geom.getType() != GeometryType.SOLID) { }
throw new IllegalArgumentException("Only solids are allowed");
} CompositeSurface comp = new CompositeSurface();
List<SurfaceProperty> surfaceMember = comp.getSurfaceMembers();
CompositeSurface comp = new CompositeSurface(); for (Polygon cdPoly : geom.getPolygons()) {
List<SurfaceProperty> surfaceMember = comp.getSurfaceMembers(); if (!cdPoly.isLink()) {
for (Polygon cdPoly : geom.getPolygons()) { org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly,
if (!cdPoly.isLink()) { config);
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config); if (gmlPoly != null) {
if (gmlPoly != null) { surfaceMember.add(new SurfaceProperty(gmlPoly));
surfaceMember.add(new SurfaceProperty(gmlPoly)); }
} } else {
} else { // add reference to polygon
// add reference to polygon surfaceMember.add(new SurfaceProperty("#" + cdPoly.getGmlId().getGmlString()));
surfaceMember.add(new SurfaceProperty("#" + cdPoly.getGmlId().getGmlString())); }
} }
}
if (surfaceMember.isEmpty()) {
if (surfaceMember.isEmpty()) { return null;
return null; }
} Solid solid = new Solid();
Solid solid = new Solid(); Shell shell = new Shell(surfaceMember);
Shell shell = new Shell(surfaceMember); solid.setExterior(new ShellProperty(shell));
solid.setExterior(new ShellProperty(shell)); solid.setId(geom.getGmlId().getGmlString());
solid.setId(geom.getGmlId().getGmlString()); return solid;
return solid; }
}
public static MultiSurface createMultiSurface(Geometry geom, GeometryFactory factory, ParserConfiguration config) {
public static MultiSurface createMultiSurface(Geometry geom, GeometryFactory factory, if (geom.getType() != GeometryType.MULTI_SURFACE && geom.getType() != GeometryType.COMPOSITE_SURFACE) {
ParserConfiguration config) { throw new IllegalArgumentException("This can only handle MultiSurfaces");
if (geom.getType() != GeometryType.MULTI_SURFACE && geom.getType() != GeometryType.COMPOSITE_SURFACE) { }
throw new IllegalArgumentException("This can only handle MultiSurfaces"); List<SurfaceProperty> surfaces = new ArrayList<>();
} Set<CompositeCollection> compositeCollections = new HashSet<>();
List<SurfaceProperty> surfaces = new ArrayList<>(); for (Polygon cdPoly : geom.getPolygons()) {
Set<CompositeCollection> compositeCollections = new HashSet<>(); if (cdPoly instanceof ConcretePolygon conc && conc.isCompositeMember()) {
for (Polygon cdPoly : geom.getPolygons()) { compositeCollections.add(conc.getPartOfComposite());
if (cdPoly instanceof ConcretePolygon conc && conc.isCompositeMember()) { } else if (!cdPoly.isLink()) {
compositeCollections.add(conc.getPartOfComposite()); // is not part of a boundary surface
} else if (!cdPoly.isLink()) { org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly,
// is not part of a boundary surface config);
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config); if (gmlPoly != null) {
if (gmlPoly != null) { surfaces.add(new SurfaceProperty(gmlPoly));
surfaces.add(new SurfaceProperty(gmlPoly)); }
} } else {
} else { // add reference to polygon
// add reference to polygon surfaces.add(new SurfaceProperty("#" + cdPoly.getGmlId().getGmlString()));
surfaces.add(new SurfaceProperty("#" + cdPoly.getGmlId().getGmlString())); }
} }
} for (CompositeCollection cdPoly : compositeCollections) {
for (CompositeCollection cdPoly : compositeCollections) { surfaces.add(new SurfaceProperty(createGmlComposite(factory, cdPoly, config)));
surfaces.add(new SurfaceProperty(createGmlComposite(factory, cdPoly, config))); }
} if (surfaces.isEmpty()) {
if (surfaces.isEmpty()) { return null;
return null; }
} MultiSurface ms = new MultiSurface(surfaces);
MultiSurface ms = new MultiSurface(surfaces); ms.setId(geom.getGmlId().getGmlString());
ms.setId(geom.getGmlId().getGmlString()); return ms;
return ms; }
}
public static MultiSurface createMultiSurface(List<Polygon> polygons, GeometryFactory factory,
public static MultiSurface createMultiSurface(List<Polygon> polygons, GeometryFactory factory, ParserConfiguration config) {
ParserConfiguration config) { List<SurfaceProperty> surfaces = new ArrayList<>();
List<SurfaceProperty> surfaces = new ArrayList<>(); for (Polygon cdPoly : polygons) {
for (Polygon cdPoly : polygons) { 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) {
if (gmlPoly != null) { surfaces.add(new SurfaceProperty(gmlPoly));
surfaces.add(new SurfaceProperty(gmlPoly)); }
} }
} if (surfaces.isEmpty()) {
if (surfaces.isEmpty()) { return null;
return null; }
} return new MultiSurface(surfaces);
return new MultiSurface(surfaces); }
}
public static CompositeSurface createCompositeSurface(Geometry geom, GeometryFactory factory,
public static CompositeSurface createCompositeSurface(Geometry geom, GeometryFactory factory, ParserConfiguration config) {
ParserConfiguration config) { List<SurfaceProperty> surfaces = new ArrayList<>();
List<SurfaceProperty> surfaces = new ArrayList<>(); for (Polygon cdPoly : geom.getPolygons()) {
for (Polygon cdPoly : geom.getPolygons()) { 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) {
if (gmlPoly != null) { surfaces.add(new SurfaceProperty(gmlPoly));
surfaces.add(new SurfaceProperty(gmlPoly)); }
} }
} if (surfaces.isEmpty()) {
if (surfaces.isEmpty()) { return null;
return null; }
} return new CompositeSurface(surfaces);
return new CompositeSurface(surfaces); }
}
} }
...@@ -6,7 +6,7 @@ import de.hft.stuttgart.citydoctor2.check.Checkable; ...@@ -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. * 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 @Override
public void check(Checkable checkable) { public void check(Checkable checkable) {
......
...@@ -65,7 +65,7 @@ public class CityGMLWriterUtils { ...@@ -65,7 +65,7 @@ public class CityGMLWriterUtils {
} }
public static void writeCityModel(String file, CityDoctorModel model) throws CityDoctorWriteException { public static void writeCityModel(String file, CityDoctorModel model) throws CityDoctorWriteException {
CityGMLContext gmlContext = CityGmlParser.getContext(); CityGMLContext gmlContext = CityGmlParser.setupContext();
CityModel cModel = model.getCityModel(); CityModel cModel = model.getCityModel();
CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(model.getCityGMLVersion()); CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(model.getCityGMLVersion());
try (CityGMLWriter writer = factory.createCityGMLWriter(new File(file))) { try (CityGMLWriter writer = factory.createCityGMLWriter(new File(file))) {
......
package org.xmlobjects.xml;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
public class Attributes implements Serializable {
private static final long serialVersionUID = 8942583366234714632L;
private final Map<String, Map<String, TextContent>> attributes = new HashMap<>();
public boolean isEmpty() {
return attributes.isEmpty();
}
public void add(String namespaceURI, String localName, TextContent value) {
attributes.computeIfAbsent(namespaceURI, v -> new HashMap<>()).put(localName, value);
}
public void add(String namespaceURI, String localName, String value) {
add(namespaceURI, localName, TextContent.of(value));
}
public void add(String localName, TextContent value) {
add(XMLConstants.NULL_NS_URI, localName, value);
}
public void add(String localName, String value) {
add(localName, TextContent.of(value));
}
public void add(QName name, TextContent value) {
add(name.getNamespaceURI(), name.getLocalPart(), value);
}
public void add(QName name, String value) {
add(name, TextContent.of(value));
}
public void addAll(String namespaceURI, Map<String, TextContent> attributes) {
this.attributes.computeIfAbsent(namespaceURI, v -> new HashMap<>()).putAll(attributes);
}
public Map<String, Map<String, TextContent>> get() {
return attributes;
}
public Map<String, TextContent> get(String namespaceURI) {
return attributes.getOrDefault(namespaceURI, Collections.emptyMap());
}
public TextContent getValue(String localName) {
return getValue(XMLConstants.NULL_NS_URI, localName);
}
public TextContent getValue(String namespaceURI, String localName) {
return get(namespaceURI).getOrDefault(localName, TextContent.empty());
}
public TextContent getValue(QName name) {
return getValue(name.getNamespaceURI(), name.getLocalPart());
}
public Attributes copy() {
Attributes copy = new Attributes();
copy.attributes.putAll(attributes);
return copy;
}
}
package org.xmlobjects.xml;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
public class TextContent implements Serializable {
private static final long serialVersionUID = -3862132079185243841L;
private static final DatatypeFactory XML_TYPE_FACTORY;
private static final TextContent EMPTY = new TextContent("");
private static Function<LocalDateTime, ZoneOffset> ZONE_OFFSET_PROVIDER = dateTime -> ZoneOffset.systemDefault().getRules().getOffset(dateTime);
private static boolean WITH_TIME_OFFSET = true;
private static boolean WITH_DATE_OFFSET = false;
private enum Fields { YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, NANO, TIMEZONE }
private static final EnumSet<Fields> DATE_TIME_FIELDS = EnumSet.allOf(Fields.class);
private static final EnumSet<Fields> TIME_FIELDS = EnumSet.of(Fields.HOUR, Fields.MINUTE, Fields.SECOND, Fields.NANO, Fields.TIMEZONE);
private static final EnumSet<Fields> DATE_FIELDS = EnumSet.of(Fields.YEAR, Fields.MONTH, Fields.DAY, Fields.TIMEZONE);
private static final EnumSet<Fields> GYEAR_MONTH_FIELDS = EnumSet.of(Fields.YEAR, Fields.MONTH, Fields.TIMEZONE);
private static final EnumSet<Fields> GMONTH_DAY_FIELDS = EnumSet.of(Fields.MONTH, Fields.DAY, Fields.TIMEZONE);
private static final EnumSet<Fields> GDAY_FIELDS = EnumSet.of(Fields.DAY, Fields.TIMEZONE);
private static final EnumSet<Fields> GMONTH_FIELDS = EnumSet.of(Fields.MONTH, Fields.TIMEZONE);
private static final EnumSet<Fields> GYEAR_FIELDS = EnumSet.of(Fields.YEAR, Fields.TIMEZONE);
static {
try {
XML_TYPE_FACTORY = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) {
throw new RuntimeException("Failed to initialize datatype factory.", e);
}
}
private String content;
private String trimmedContent;
private String[] tokenizedContent;
private Object value;
private transient Function<LocalDateTime, ZoneOffset> zoneOffsetProvider = ZONE_OFFSET_PROVIDER;
private TextContent(String content) {
this.content = Objects.requireNonNull(content, "Content must not be null.");
}
public static TextContent empty() {
return EMPTY;
}
public static TextContent of(String content) {
return ofObject(content);
}
public static TextContent ofList(List<String> content) {
return ofObjectList(content);
}
public static TextContent ofBoolean(Boolean content) {
return ofObject(content);
}
public static TextContent ofBooleanList(List<Boolean> content) {
return ofObjectList(content);
}
public static TextContent ofDouble(Double content) {
return ofObject(content);
}
public static TextContent ofDoubleList(List<Double> content) {
return ofObjectList(content);
}
public static TextContent ofInteger(Integer content) {
return ofObject(content);
}
public static TextContent ofIntegerList(List<Integer> content) {
return ofObjectList(content);
}
public static TextContent ofDuration(Duration content) {
return ofObject(content);
}
public static TextContent ofDurationList(List<Duration> content) {
return ofObjectList(content);
}
public static TextContent ofDateTime(OffsetDateTime content, boolean withOffset) {
return ofOffsetDateTime(content, DATE_TIME_FIELDS, withOffset);
}
public static TextContent ofDateTime(OffsetDateTime content) {
return ofOffsetDateTime(content, DATE_TIME_FIELDS, WITH_TIME_OFFSET);
}
public static TextContent ofDateTimeList(List<OffsetDateTime> content, boolean withOffset) {
return ofOffsetDateTimeList(content, DATE_TIME_FIELDS, withOffset);
}
public static TextContent ofDateTimeList(List<OffsetDateTime> content) {
return ofOffsetDateTimeList(content, DATE_TIME_FIELDS, WITH_TIME_OFFSET);
}
public static TextContent ofTime(OffsetDateTime content, boolean withOffset) {
return ofOffsetDateTime(content, TIME_FIELDS, withOffset);
}
public static TextContent ofTime(OffsetDateTime content) {
return ofOffsetDateTime(content, TIME_FIELDS, WITH_TIME_OFFSET);
}
public static TextContent ofTimeList(List<OffsetDateTime> content, boolean withOffset) {
return ofOffsetDateTimeList(content, TIME_FIELDS, withOffset);
}
public static TextContent ofTimeList(List<OffsetDateTime> content) {
return ofOffsetDateTimeList(content, TIME_FIELDS, WITH_TIME_OFFSET);
}
public static TextContent ofDate(OffsetDateTime content, boolean withOffset) {
return ofOffsetDateTime(content, DATE_FIELDS, withOffset);
}
public static TextContent ofDate(OffsetDateTime content) {
return ofOffsetDateTime(content, DATE_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofDateList(List<OffsetDateTime> content, boolean withOffset) {
return ofOffsetDateTimeList(content, DATE_FIELDS, withOffset);
}
public static TextContent ofDateList(List<OffsetDateTime> content) {
return ofOffsetDateTimeList(content, DATE_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGYearMonth(OffsetDateTime content, boolean withOffset) {
return ofOffsetDateTime(content, GYEAR_MONTH_FIELDS, withOffset);
}
public static TextContent ofGYearMonth(OffsetDateTime content) {
return ofOffsetDateTime(content, GYEAR_MONTH_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGYearMonthList(List<OffsetDateTime> content, boolean withOffset) {
return ofOffsetDateTimeList(content, GYEAR_MONTH_FIELDS, withOffset);
}
public static TextContent ofGYearMonthList(List<OffsetDateTime> content) {
return ofOffsetDateTimeList(content, GYEAR_MONTH_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGMonthDay(OffsetDateTime content, boolean withOffset) {
return ofOffsetDateTime(content, GMONTH_DAY_FIELDS, withOffset);
}
public static TextContent ofGMonthDay(OffsetDateTime content) {
return ofOffsetDateTime(content, GMONTH_DAY_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGMonthDayList(List<OffsetDateTime> content, boolean withOffset) {
return ofOffsetDateTimeList(content, GMONTH_DAY_FIELDS, withOffset);
}
public static TextContent ofGMonthDayList(List<OffsetDateTime> content) {
return ofOffsetDateTimeList(content, GMONTH_DAY_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGDay(OffsetDateTime content, boolean withOffset) {
return ofOffsetDateTime(content, GDAY_FIELDS, withOffset);
}
public static TextContent ofGDay(OffsetDateTime content) {
return ofOffsetDateTime(content, GDAY_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGDayList(List<OffsetDateTime> content, boolean withOffset) {
return ofOffsetDateTimeList(content, GDAY_FIELDS, withOffset);
}
public static TextContent ofGDayList(List<OffsetDateTime> content) {
return ofOffsetDateTimeList(content, GDAY_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGMonth(OffsetDateTime content, boolean withOffset) {
return ofOffsetDateTime(content, GMONTH_FIELDS, withOffset);
}
public static TextContent ofGMonth(OffsetDateTime content) {
return ofOffsetDateTime(content, GMONTH_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGMonthList(List<OffsetDateTime> content, boolean withOffset) {
return ofOffsetDateTimeList(content, GMONTH_FIELDS, withOffset);
}
public static TextContent ofGMonthList(List<OffsetDateTime> content) {
return ofOffsetDateTimeList(content, GMONTH_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGYear(OffsetDateTime content, boolean withOffset) {
return ofOffsetDateTime(content, GYEAR_FIELDS, withOffset);
}
public static TextContent ofGYear(OffsetDateTime content) {
return ofOffsetDateTime(content, GYEAR_FIELDS, WITH_DATE_OFFSET);
}
public static TextContent ofGYearList(List<OffsetDateTime> content, boolean withOffset) {
return ofOffsetDateTimeList(content, GYEAR_FIELDS, withOffset);
}
public static TextContent ofGYearList(List<OffsetDateTime> content) {
return ofOffsetDateTimeList(content, GYEAR_FIELDS, WITH_DATE_OFFSET);
}
public boolean isEmpty() {
return this == EMPTY;
}
public TextContent trim() {
content = trimmedContent();
return this;
}
public TextContent normalize() {
int length = content.length();
if (length != 0) {
StringBuilder normalized = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char ch = content.charAt(i);
normalized.append(isWhiteSpace(ch) ? ' ' : ch);
}
content = normalized.toString();
}
return this;
}
public TextContent collapse() {
int length = content.length();
if (length != 0) {
char ch = 0;
int i = 0;
for (; i < length; i++) {
ch = content.charAt(i);
if (!isWhiteSpace(ch))
break;
}
if (i != length) {
StringBuilder collapsed = new StringBuilder(length - i).append(ch);
boolean isWhiteSpace = false;
for (i += 1; i < length; i++) {
ch = content.charAt(i);
if (isWhiteSpace(ch)) {
isWhiteSpace = true;
} else {
if (isWhiteSpace) {
collapsed.append(' ');
isWhiteSpace = false;
}
collapsed.append(ch);
}
}
content = trimmedContent = collapsed.toString();
} else
content = trimmedContent = "";
}
return this;
}
public TextContent filter(Predicate<TextContent> predicate) {
return predicate.test(this) ? this : EMPTY;
}
public String get() {
return isPresent() ? content : null;
}
public boolean isPresent() {
return this != EMPTY;
}
public void ifPresent(Consumer<String> action) {
if (isPresent())
action.accept(content);
}
@SuppressWarnings("unchecked")
public List<String> getAsList() {
if (isListOfType(value, String.class)) {
return (List<String>) value;
} else if (tokenizeContent() != 0) {
List<String> strings = new ArrayList<>(tokenizedContent.length);
Collections.addAll(strings, tokenizedContent);
return setValue(strings);
} else {
return setValue(null);
}
}
public boolean isList() {
return getAsList() != null;
}
public void ifList(Consumer<List<String>> action) {
List<String> value = getAsList();
if (value != null) {
action.accept(value);
}
}
public Boolean getAsBoolean() {
return value instanceof Boolean bool ? bool : setValue(toBoolean(trimmedContent()));
}
public boolean isBoolean() {
return getAsBoolean() != null;
}
public void ifBoolean(Consumer<Boolean> action) {
Boolean value = getAsBoolean();
if (value != null) {
action.accept(value);
}
}
@SuppressWarnings("unchecked")
public List<Boolean> getAsBooleanList() {
if (isListOfType(value, Boolean.class)) {
return (List<Boolean>) value;
} else if (tokenizeContent() != 0) {
List<Boolean> booleans = new ArrayList<>(tokenizedContent.length);
for (String token : tokenizedContent) {
Boolean value = toBoolean(token);
if (value != null) {
booleans.add(value);
} else {
return setValue(null);
}
}
return setValue(booleans);
} else {
return setValue(null);
}
}
public boolean isBooleanList() {
return getAsBooleanList() != null;
}
public void ifBooleanList(Consumer<List<Boolean>> action) {
List<Boolean> value = getAsBooleanList();
if (value != null) {
action.accept(value);
}
}
public Double getAsDouble() {
if (value instanceof Double doubleValue) {
return doubleValue;
} else if (!isEmpty() && !trimmedContent().isEmpty()) {
try {
return setValue(Double.parseDouble(trimmedContent));
} catch (NumberFormatException e) {
return setValue(null);
}
} else {
return setValue(null);
}
}
public boolean isDouble() {
return getAsDouble() != null;
}
public void ifDouble(Consumer<Double> action) {
Double value = getAsDouble();
if (value != null) {
action.accept(value);
}
}
@SuppressWarnings("unchecked")
public List<Double> getAsDoubleList() {
if (isListOfType(value, Double.class)) {
return (List<Double>) value;
} else if (tokenizeContent() != 0) {
List<Double> doubles = new ArrayList<>(tokenizedContent.length);
for (String token : tokenizedContent) {
try {
doubles.add(Double.parseDouble(token));
} catch (NumberFormatException e) {
return setValue(null);
}
}
return setValue(doubles);
} else {
return setValue(null);
}
}
public boolean isDoubleList() {
return getAsDoubleList() != null;
}
public void ifDoubleList(Consumer<List<Double>> action) {
List<Double> value = getAsDoubleList();
if (value != null) {
action.accept(value);
}
}
public Integer getAsInteger() {
if (value instanceof Integer intValue) {
return intValue;
} else if (!isEmpty() && !trimmedContent().isEmpty()) {
try {
return setValue(Integer.parseInt(trimmedContent));
} catch (NumberFormatException e) {
return setValue(null);
}
} else {
return setValue(null);
}
}
public boolean isInteger() {
return getAsInteger() != null;
}
public void ifInteger(Consumer<Integer> action) {
Integer value = getAsInteger();
if (value != null) {
action.accept(value);
}
}
@SuppressWarnings("unchecked")
public List<Integer> getAsIntegerList() {
if (isListOfType(value, Integer.class)) {
return (List<Integer>) value;
} else if (tokenizeContent() != 0) {
List<Integer> integers = new ArrayList<>(tokenizedContent.length);
for (String token : tokenizedContent) {
try {
integers.add(Integer.parseInt(token));
} catch (NumberFormatException e) {
return setValue(null);
}
}
return setValue(integers);
} else {
return setValue(null);
}
}
public boolean isIntegerList() {
return getAsIntegerList() != null;
}
public void ifIntegerList(Consumer<List<Integer>> action) {
List<Integer> value = getAsIntegerList();
if (value != null) {
action.accept(value);
}
}
public Duration getAsDuration() {
if (value instanceof Duration duration) {
return duration;
} else if (!isEmpty() && !trimmedContent().isEmpty()) {
try {
return setValue(XML_TYPE_FACTORY.newDuration(trimmedContent));
} catch (Throwable e) {
return setValue(null);
}
} else {
return setValue(null);
}
}
public boolean isDuration() {
return getAsDuration() != null;
}
public void ifDuration(Consumer<Duration> action) {
Duration value = getAsDuration();
if (value != null) {
action.accept(value);
}
}
@SuppressWarnings("unchecked")
public List<Duration> getAsDurationList() {
if (isListOfType(value, Duration.class)) {
return (List<Duration>) value;
} else if (tokenizeContent() != 0) {
List<Duration> durations = new ArrayList<>(tokenizedContent.length);
for (String token : tokenizedContent) {
try {
durations.add(XML_TYPE_FACTORY.newDuration(token));
} catch (Throwable e) {
return setValue(null);
}
}
return setValue(durations);
} else {
return setValue(null);
}
}
public boolean isDurationList() {
return getAsDurationList() != null;
}
public void ifDurationList(Consumer<List<Duration>> action) {
List<Duration> value = getAsDurationList();
if (value != null) {
action.accept(value);
}
}
public OffsetDateTime getAsDateTime() {
return getAsOffsetDateTime("dateTime");
}
public boolean isDateTime() {
return getAsDateTime() != null;
}
public void ifDateTime(Consumer<OffsetDateTime> action) {
OffsetDateTime value = getAsDateTime();
if (value != null) {
action.accept(value);
}
}
public List<OffsetDateTime> getAsDateTimeList() {
return getAsOffsetDateTimeList("dateTime");
}
public boolean isDateTimeList() {
return getAsDateTimeList() != null;
}
public void ifDateTimeList(Consumer<List<OffsetDateTime>> action) {
List<OffsetDateTime> value = getAsDateTimeList();
if (value != null) {
action.accept(value);
}
}
public OffsetDateTime getAsTime() {
return getAsOffsetDateTime("time");
}
public boolean isTime() {
return getAsTime() != null;
}
public void ifTime(Consumer<OffsetDateTime> action) {
OffsetDateTime value = getAsTime();
if (value != null) {
action.accept(value);
}
}
public List<OffsetDateTime> getAsTimeList() {
return getAsOffsetDateTimeList("time");
}
public boolean isTimeList() {
return getAsTimeList() != null;
}
public void ifTimeList(Consumer<List<OffsetDateTime>> action) {
List<OffsetDateTime> value = getAsTimeList();
if (value != null) {
action.accept(value);
}
}
public OffsetDateTime getAsDate() {
return getAsOffsetDateTime("date");
}
public boolean isDate() {
return getAsDate() != null;
}
public void ifDate(Consumer<OffsetDateTime> action) {
OffsetDateTime value = getAsDate();
if (value != null) {
action.accept(value);
}
}
public List<OffsetDateTime> getAsDateList() {
return getAsOffsetDateTimeList("date");
}
public boolean isDateList() {
return getAsDateList() != null;
}
public void ifDateList(Consumer<List<OffsetDateTime>> action) {
List<OffsetDateTime> value = getAsDateList();
if (value != null) {
action.accept(value);
}
}
public OffsetDateTime getAsGYearMonth() {
return getAsOffsetDateTime("gYearMonth");
}
public boolean isGYearMonth() {
return getAsGYearMonth() != null;
}
public void ifGYearMonth(Consumer<OffsetDateTime> action) {
OffsetDateTime value = getAsGYearMonth();
if (value != null) {
action.accept(value);
}
}
public List<OffsetDateTime> getAsGYearMonthList() {
return getAsOffsetDateTimeList("gYearMonth");
}
public boolean isGYearMonthList() {
return getAsGYearMonthList() != null;
}
public void ifGYearMonthList(Consumer<List<OffsetDateTime>> action) {
List<OffsetDateTime> value = getAsGYearMonthList();
if (value != null) {
action.accept(value);
}
}
public OffsetDateTime getAsGMonthDay() {
return getAsOffsetDateTime("gMonthDay");
}
public boolean isGMonthDay() {
return getAsGMonthDay() != null;
}
public void ifGMonthDay(Consumer<OffsetDateTime> action) {
OffsetDateTime value = getAsGMonthDay();
if (value != null) {
action.accept(value);
}
}
public List<OffsetDateTime> getAsGMonthDayList() {
return getAsOffsetDateTimeList("gMonthDay");
}
public boolean isGMonthDayList() {
return getAsGMonthDayList() != null;
}
public void ifGMonthDayList(Consumer<List<OffsetDateTime>> action) {
List<OffsetDateTime> value = getAsGMonthDayList();
if (value != null) {
action.accept(value);
}
}
public OffsetDateTime getAsGDay() {
return getAsOffsetDateTime("gDay");
}
public boolean isGDay() {
return getAsGDay() != null;
}
public void ifGDay(Consumer<OffsetDateTime> action) {
OffsetDateTime value = getAsGDay();
if (value != null) {
action.accept(value);
}
}
public List<OffsetDateTime> getAsGDayList() {
return getAsOffsetDateTimeList("gDay");
}
public boolean isGDayList() {
return getAsGDayList() != null;
}
public void ifGDayList(Consumer<List<OffsetDateTime>> action) {
List<OffsetDateTime> value = getAsGDayList();
if (value != null) {
action.accept(value);
}
}
public OffsetDateTime getAsGMonth() {
return getAsOffsetDateTime("gMonth");
}
public boolean isGMonth() {
return getAsGMonth() != null;
}
public void ifGMonth(Consumer<OffsetDateTime> action) {
OffsetDateTime value = getAsGMonth();
if (value != null) {
action.accept(value);
}
}
public List<OffsetDateTime> getAsGMonthList() {
return getAsOffsetDateTimeList("gMonth");
}
public boolean isGMonthList() {
return getAsGMonthList() != null;
}
public void ifGMonthList(Consumer<List<OffsetDateTime>> action) {
List<OffsetDateTime> value = getAsGMonthList();
if (value != null) {
action.accept(value);
}
}
public OffsetDateTime getAsGYear() {
return getAsOffsetDateTime("gYear");
}
public boolean isGYear() {
return getAsGYear() != null;
}
public void ifGYear(Consumer<OffsetDateTime> action) {
OffsetDateTime value = getAsGYear();
if (value != null) {
action.accept(value);
}
}
public List<OffsetDateTime> getAsGYearList() {
return getAsOffsetDateTimeList("gYear");
}
public boolean isGYearList() {
return getAsGYearList() != null;
}
public void ifGYearList(Consumer<List<OffsetDateTime>> action) {
List<OffsetDateTime> value = getAsGYearList();
if (value != null) {
action.accept(value);
}
}
public TextContent withZoneOffsetProvider(Function<LocalDateTime, ZoneOffset> zoneOffsetProvider) {
if (zoneOffsetProvider != null) {
this.zoneOffsetProvider = zoneOffsetProvider;
}
return this;
}
private OffsetDateTime getAsOffsetDateTime(String localName) {
if (value instanceof XMLGregorianCalendar calendar
&& calendar.getXMLSchemaType().getLocalPart().equals(localName)) {
return toOffsetDateTime((XMLGregorianCalendar) value);
} else if (!isEmpty() && !trimmedContent().isEmpty()) {
XMLGregorianCalendar calendar = setValue(toCalendar(trimmedContent(), localName));
return calendar != null ? toOffsetDateTime(calendar) : null;
} else {
return setValue(null);
}
}
@SuppressWarnings("unchecked")
private List<OffsetDateTime> getAsOffsetDateTimeList(String localName) {
List<XMLGregorianCalendar> calendars;
if (value instanceof List<?> list
&& list.stream().allMatch(v -> v instanceof XMLGregorianCalendar calendar
&& calendar.getXMLSchemaType().getLocalPart().equals(localName))) {
calendars = (List<XMLGregorianCalendar>) value;
} else if (tokenizeContent() != 0) {
calendars = new ArrayList<>(tokenizedContent.length);
for (String token : tokenizedContent) {
XMLGregorianCalendar value = toCalendar(token, localName);
if (value != null) {
calendars.add(value);
} else {
return setValue(null);
}
}
setValue(calendars);
} else {
return setValue(null);
}
List<OffsetDateTime> result = new ArrayList<>(calendars.size());
for (XMLGregorianCalendar calendar : calendars) {
result.add(toOffsetDateTime(calendar));
}
return result;
}
private <T> T setValue(T value) {
this.value = value;
return value;
}
private boolean isListOfType(Object value, Class<?> type) {
return value instanceof List<?> list && list.stream().allMatch(type::isInstance);
}
private Boolean toBoolean(String value) {
if (value.isEmpty()) {
return null;
} else if ("true".equals(value)
|| "1".equals(value)) {
return Boolean.TRUE;
} else if ("false".equals(value)
|| "0".equals(value)) {
return Boolean.FALSE;
} else {
return null;
}
}
private XMLGregorianCalendar toCalendar(String value, String localName) {
try {
XMLGregorianCalendar calendar = XML_TYPE_FACTORY.newXMLGregorianCalendar(value);
if (calendar.getXMLSchemaType().getLocalPart().equals(localName)) {
return calendar;
}
} catch (Throwable e) {
//
}
return null;
}
private OffsetDateTime toOffsetDateTime(XMLGregorianCalendar calendar) {
int day = calendar.getDay();
int month = calendar.getMonth();
int year = calendar.getYear();
int hour = calendar.getHour();
int minute = calendar.getMinute();
int second = calendar.getSecond();
int offset = calendar.getTimezone();
BigDecimal fractional = calendar.getFractionalSecond();
int nano = fractional != null ? (int) (fractional.doubleValue() * 1e+9) : DatatypeConstants.FIELD_UNDEFINED;
LocalDateTime dateTime = LocalDateTime.of(
year != DatatypeConstants.FIELD_UNDEFINED ? year : 0,
month != DatatypeConstants.FIELD_UNDEFINED ? month : 1,
day != DatatypeConstants.FIELD_UNDEFINED ? day : 1,
hour != DatatypeConstants.FIELD_UNDEFINED ? hour : 0,
minute != DatatypeConstants.FIELD_UNDEFINED ? minute : 0,
second != DatatypeConstants.FIELD_UNDEFINED ? second : 0,
nano != DatatypeConstants.FIELD_UNDEFINED ? nano : 0);
return OffsetDateTime.of(dateTime, offset != DatatypeConstants.FIELD_UNDEFINED ?
ZoneOffset.ofTotalSeconds(offset * 60) :
zoneOffsetProvider.apply(dateTime));
}
private static TextContent ofObject(Object content) {
return content != null ? new TextContent(content.toString()) : EMPTY;
}
private static TextContent ofObjectList(List<?> content) {
if (content != null && !content.isEmpty()) {
StringBuilder builder = new StringBuilder(content.size());
boolean first = true;
for (Object object : content) {
if (object != null) {
if (!first) {
builder.append(" ");
} else {
first = false;
}
builder.append(object);
}
}
return new TextContent(builder.toString());
} else {
return EMPTY;
}
}
private static TextContent ofOffsetDateTime(OffsetDateTime content, EnumSet<Fields> fields, boolean withOffset) {
XMLGregorianCalendar calendar = toCalendar(content, fields, withOffset);
return calendar != null ? new TextContent(calendar.toXMLFormat()) : EMPTY;
}
private static TextContent ofOffsetDateTimeList(List<OffsetDateTime> content, EnumSet<Fields> fields, boolean withOffset) {
if (content != null && !content.isEmpty()) {
StringBuilder builder = new StringBuilder(content.size());
boolean first = true;
for (OffsetDateTime dateTime : content) {
XMLGregorianCalendar calendar = toCalendar(dateTime, fields, withOffset);
if (calendar != null) {
if (!first) {
builder.append(" ");
} else {
first = false;
}
builder.append(calendar.toXMLFormat());
}
}
return new TextContent(builder.toString());
} else {
return EMPTY;
}
}
private static XMLGregorianCalendar toCalendar(OffsetDateTime dateTime, EnumSet<Fields> fields, boolean withOffset) {
XMLGregorianCalendar calendar = null;
if (dateTime != null) {
calendar = XML_TYPE_FACTORY.newXMLGregorianCalendar(
fields.contains(Fields.YEAR) ? dateTime.getYear() : DatatypeConstants.FIELD_UNDEFINED,
fields.contains(Fields.MONTH) ? dateTime.getMonthValue() : DatatypeConstants.FIELD_UNDEFINED,
fields.contains(Fields.DAY) ? dateTime.getDayOfMonth() : DatatypeConstants.FIELD_UNDEFINED,
fields.contains(Fields.HOUR) ? dateTime.getHour() : DatatypeConstants.FIELD_UNDEFINED,
fields.contains(Fields.MINUTE) ? dateTime.getMinute() : DatatypeConstants.FIELD_UNDEFINED,
fields.contains(Fields.SECOND) ? dateTime.getSecond() : DatatypeConstants.FIELD_UNDEFINED,
DatatypeConstants.FIELD_UNDEFINED,
withOffset && fields.contains(Fields.TIMEZONE) ?
dateTime.getOffset().getTotalSeconds() / 60 :
DatatypeConstants.FIELD_UNDEFINED);
if (fields.contains(Fields.NANO) && dateTime.getNano() != 0) {
calendar.setFractionalSecond(BigDecimal.valueOf(dateTime.getNano(), 9).stripTrailingZeros());
}
}
return calendar;
}
public static void setZoneOffsetProvider(Function<LocalDateTime, ZoneOffset> zoneOffsetProvider) {
if (zoneOffsetProvider != null) {
ZONE_OFFSET_PROVIDER = zoneOffsetProvider;
}
}
public static void serializeTimeWithOffset(boolean useTimeOffset) {
WITH_TIME_OFFSET = useTimeOffset;
}
public static void serializeDateWithOffset(boolean useDateOffset) {
WITH_DATE_OFFSET = useDateOffset;
}
private String trimmedContent() {
if (trimmedContent == null) {
trimmedContent = content.trim();
}
return trimmedContent;
}
private int tokenizeContent() {
if (tokenizedContent == null) {
int length = content.length();
String[] tokens = new String[(length / 2) + 1];
int noOfTokens = 0;
int current = -1;
int next;
do {
next = nextWhiteSpace(content, current + 1, length);
if (next != current + 1) {
tokens[noOfTokens++] = content.substring(current + 1, next);
}
current = next;
} while (next != length);
tokenizedContent = new String[noOfTokens];
System.arraycopy(tokens, 0, tokenizedContent, 0, noOfTokens);
}
return tokenizedContent.length;
}
private int nextWhiteSpace(String value, int pos, int length) {
for (int i = pos; i < length; i++) {
if (isWhiteSpace(value.charAt(i))) {
return i;
}
}
return length;
}
private boolean isWhiteSpace(char ch) {
return ch == ' ' || ch == '\n' || Character.isWhitespace(ch);
}
@Override
public String toString() {
return content;
}
}
...@@ -6,15 +6,18 @@ CheckContainer.error=Unexpected exception while executing check: {} ...@@ -6,15 +6,18 @@ CheckContainer.error=Unexpected exception while executing check: {}
CheckDialog.availableChecksLabel=Available Checks CheckDialog.availableChecksLabel=Available Checks
CheckDialog.cancelBtn=Cancel CheckDialog.cancelBtn=Cancel
CheckDialog.checkBtn=Check CheckDialog.checkBtn=Check
CheckDialog.checkDescriptionsLabel=Explanations for the requirements are available on CityDoctor's homepage
CheckDialog.checkEnabled=Enabled CheckDialog.checkEnabled=Enabled
CheckDialog.checkName=Name CheckDialog.checkName=Name
CheckDialog.checksDone=Checks done CheckDialog.checksDone=Checks done
CheckDialog.checksReenabledAlert=Some checks have been reenabled so that other wanted checks can be executed\nSee the log for more information. CheckDialog.checksReenabledAlert=Some checks have been reenabled so that other wanted checks can be executed\nSee the log for more information.
CheckDialog.checksTab=Checks CheckDialog.checksTab=Checks
CheckDialog.clickHereToOpen=[Click here to open]
CheckDialog.failedChecks=Failed to execute checks CheckDialog.failedChecks=Failed to execute checks
CheckDialog.filterTab=Filter CheckDialog.filterTab=Filter
CheckDialog.geometricChecksLabel=Geometric Checks CheckDialog.geometricChecksLabel=Geometric Checks
CheckDialog.globalParametersLabel=Global Parameters CheckDialog.globalParametersLabel=Global Parameters
CheckDialog.homepageHyperlink=https://transfer.hft-stuttgart.de/pages/citydoctor/citydoctorhomepage/en/requirements/
CheckDialog.noPathError=Output path is missing! CheckDialog.noPathError=Output path is missing!
CheckDialog.outputPathBtn=Select CheckDialog.outputPathBtn=Select
CheckDialog.outputPathLabel=Output directory (Required): CheckDialog.outputPathLabel=Output directory (Required):
...@@ -25,12 +28,14 @@ CheckDialog.schematronChooserTitle=Choose schematron file ... ...@@ -25,12 +28,14 @@ CheckDialog.schematronChooserTitle=Choose schematron file ...
CheckDialog.schematronFileLabel=Schematron File: CheckDialog.schematronFileLabel=Schematron File:
CheckDialog.schematronFileNotExisting=Schematron file does not exist, Schematron will be skipped CheckDialog.schematronFileNotExisting=Schematron file does not exist, Schematron will be skipped
CheckDialog.schematronFiles=Schematron Files CheckDialog.schematronFiles=Schematron Files
CheckDialog.selectAllChecksBtn=Select All
CheckDialog.selectBtn=Select CheckDialog.selectBtn=Select
CheckDialog.semanticChecksLabel=Semantic Checks CheckDialog.semanticChecksLabel=Semantic Checks
CheckDialog.startingChecks=Starting checks CheckDialog.startingChecks=Starting checks
CheckDialog.streamCheckLabel=Citymodels are not loaded and kept in memory. Check results are saved to the QualityADE in copies of the input files. CheckDialog.streamCheckLabel=Citymodels are not loaded and kept in memory. Check results are saved to the QualityADE in copies of the input files.
CheckDialog.streamCheckOption=Use low memory mode CheckDialog.streamCheckOption=Use low memory mode
CheckDialog.title=Check Configuration CheckDialog.title=Check Configuration
CheckDialog.unselectAllChecksBtn=Unselect All
CheckDialog.validationConfiguration=Validation Configuration CheckDialog.validationConfiguration=Validation Configuration
CheckDialog.xmlReportsOption=Create XML reports CheckDialog.xmlReportsOption=Create XML reports
Checker.checkFeature=Checking feature: {} Checker.checkFeature=Checking feature: {}
...@@ -70,6 +75,9 @@ CityDoctorValidation.notExactlyOneOutputFile=Specify exactly one file as output. ...@@ -70,6 +75,9 @@ CityDoctorValidation.notExactlyOneOutputFile=Specify exactly one file as output.
CityDoctorValidation.notExactlyOnePDFOutput=Specify exactly one file as pdfReport CityDoctorValidation.notExactlyOnePDFOutput=Specify exactly one file as pdfReport
CityDoctorValidation.notExactlyOneXMLOutput=Specify exactly one file as xmlReport CityDoctorValidation.notExactlyOneXMLOutput=Specify exactly one file as xmlReport
CityDoctorValidation.wrongInputFileFormat=Input file '{}' does not end with .gml or .xml CityDoctorValidation.wrongInputFileFormat=Input file '{}' does not end with .gml or .xml
CityGmlFeatureMapper.crossFeatureReferencesDetected=Model has Features containing cross-feature references without their own copies of the referenced polygons
CityGmlFeatureMapper.emptyCrossFeatureReference=Polygon {} of Feature {} is being referenced in other Features: {}
CityGmlFeatureMapper.unresolvedReferencesDetected=Model contains polygon references that could not be found
CityGmlParser.chunkReadFailed=Failed to read GML file in chunks, falling back to reading the complete file CityGmlParser.chunkReadFailed=Failed to read GML file in chunks, falling back to reading the complete file
CityGmlParser.convertCrsToUtmZoneN=Converting coordinate system to UTM zone {}N CityGmlParser.convertCrsToUtmZoneN=Converting coordinate system to UTM zone {}N
CityGmlParser.convertCrsToUtmZoneS=Converting coordinate system to UTM zone {}S CityGmlParser.convertCrsToUtmZoneS=Converting coordinate system to UTM zone {}S
...@@ -99,6 +107,9 @@ DatabaseHandler.serializationFailure=Serialization of CityObject {} failed! ...@@ -99,6 +107,9 @@ DatabaseHandler.serializationFailure=Serialization of CityObject {} failed!
DatabaseHandler.setupFailure=FATAL ERROR: Creation of embedded database failed! DatabaseHandler.setupFailure=FATAL ERROR: Creation of embedded database failed!
DatabaseHandler.tableFailure=FATAL ERROR: Creation of features table failed! DatabaseHandler.tableFailure=FATAL ERROR: Creation of features table failed!
DatabaseHandler.unmarshallingFailure=Loading of CityObject {} from embedded database failed! DatabaseHandler.unmarshallingFailure=Loading of CityObject {} from embedded database failed!
DatabaseSettings.invalidPath=Properties for database directory and name do not combine to a valid path. Using default value instead
DatabaseSettings.poolSizeLessThanOne=ConnectionPoolSize property was set to be less than 1. Using default value instead
DatabaseSettings.poolSizeNaN=ConnectionPoolSize property is not an integer. Using default value instead
DistanceError.distanceFromPlane=distance from plane DistanceError.distanceFromPlane=distance from plane
ErrorItemVisitor.childId=Child id ErrorItemVisitor.childId=Child id
ErrorItemVisitor.component=Component ErrorItemVisitor.component=Component
......
...@@ -6,15 +6,18 @@ CheckContainer.error=Unerwarteter Fehler bei der Ausf\u00fchrung von Pr\u00fcfun ...@@ -6,15 +6,18 @@ CheckContainer.error=Unerwarteter Fehler bei der Ausf\u00fchrung von Pr\u00fcfun
CheckDialog.availableChecksLabel=Verf\u00fcgbare Pr\u00fcfungen CheckDialog.availableChecksLabel=Verf\u00fcgbare Pr\u00fcfungen
CheckDialog.cancelBtn=Abbrechen CheckDialog.cancelBtn=Abbrechen
CheckDialog.checkBtn=Pr\u00fcfen CheckDialog.checkBtn=Pr\u00fcfen
CheckDialog.checkDescriptionsLabel=Beschreibungen der Pr\u00fcfungen sind auf der CityDoctor Homepage verf\u00fcgbar
CheckDialog.checkEnabled=Aktiv CheckDialog.checkEnabled=Aktiv
CheckDialog.checkName=Name CheckDialog.checkName=Name
CheckDialog.checksDone=Pr\u00fcfung abgeschlossen CheckDialog.checksDone=Pr\u00fcfung abgeschlossen
CheckDialog.checksReenabledAlert=Manche Pr\u00fcfungen wurden reaktiviert damit andere gewollte Pr\u00fcfungen durchgef\u00fchrt werden k\u00f6nnen\nMehr Details sind im Log geschrieben CheckDialog.checksReenabledAlert=Manche Pr\u00fcfungen wurden reaktiviert damit andere gewollte Pr\u00fcfungen durchgef\u00fchrt werden k\u00f6nnen\nMehr Details sind im Log geschrieben
CheckDialog.checksTab=Pr\u00fcfungen CheckDialog.checksTab=Pr\u00fcfungen
CheckDialog.clickHereToOpen=[Hier Klicken zum \u00d6ffnen]
CheckDialog.failedChecks=Konnte Pr\u00fcfungen nicht ausf\u00fchren CheckDialog.failedChecks=Konnte Pr\u00fcfungen nicht ausf\u00fchren
CheckDialog.filterTab=Filter CheckDialog.filterTab=Filter
CheckDialog.geometricChecksLabel=Geometrische Pr\u00fcfungen CheckDialog.geometricChecksLabel=Geometrische Pr\u00fcfungen
CheckDialog.globalParametersLabel=Globale Parameter CheckDialog.globalParametersLabel=Globale Parameter
CheckDialog.homepageHyperlink=https://transfer.hft-stuttgart.de/pages/citydoctor/citydoctorhomepage/de/requirements/
CheckDialog.noPathError=Ausgabepfad wurde nicht gesetzt! CheckDialog.noPathError=Ausgabepfad wurde nicht gesetzt!
CheckDialog.outputPathBtn=Ausw\u00e4hlen CheckDialog.outputPathBtn=Ausw\u00e4hlen
CheckDialog.outputPathLabel=Ausgabeordner (Pflichtfeld): CheckDialog.outputPathLabel=Ausgabeordner (Pflichtfeld):
...@@ -25,12 +28,14 @@ CheckDialog.schematronChooserTitle=W\u00e4hle Schematron Datei ... ...@@ -25,12 +28,14 @@ CheckDialog.schematronChooserTitle=W\u00e4hle Schematron Datei ...
CheckDialog.schematronFileLabel=Schematron Datei: CheckDialog.schematronFileLabel=Schematron Datei:
CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, Schematron-Pr\u00fcfung wird \u00fcbersprungen CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, Schematron-Pr\u00fcfung wird \u00fcbersprungen
CheckDialog.schematronFiles=Schematron Dateien CheckDialog.schematronFiles=Schematron Dateien
CheckDialog.selectAllChecksBtn=Alle aktivieren
CheckDialog.selectBtn=Ausw\u00e4hlen CheckDialog.selectBtn=Ausw\u00e4hlen
CheckDialog.semanticChecksLabel=Semantische Pr\u00fcfungen CheckDialog.semanticChecksLabel=Semantische Pr\u00fcfungen
CheckDialog.startingChecks=Starte Pr\u00fcfung CheckDialog.startingChecks=Starte Pr\u00fcfung
CheckDialog.streamCheckLabel=Stadtmodelle werden nicht in den Arbeitsspeicher geladen. Ergebnisse werden in der QualityADE in Kopien der Eingangsdateien gespeichert. CheckDialog.streamCheckLabel=Stadtmodelle werden nicht in den Arbeitsspeicher geladen. Ergebnisse werden in der QualityADE in Kopien der Eingangsdateien gespeichert.
CheckDialog.streamCheckOption=Niedrigen Arbeitsspeicherverbrauchsmodus aktivieren CheckDialog.streamCheckOption=Niedrigen Arbeitsspeicherverbrauchsmodus aktivieren
CheckDialog.title=Pr\u00fcfungskonfiguration CheckDialog.title=Pr\u00fcfungskonfiguration
CheckDialog.unselectAllChecksBtn=Alle deaktivieren
CheckDialog.validationConfiguration=Validierungskonfiguration CheckDialog.validationConfiguration=Validierungskonfiguration
CheckDialog.xmlReportsOption=XML-Berichte erstellen CheckDialog.xmlReportsOption=XML-Berichte erstellen
Checker.checkFeature=Pr\u00fcfe Feature: {} Checker.checkFeature=Pr\u00fcfe Feature: {}
...@@ -70,6 +75,9 @@ CityDoctorValidation.notExactlyOneOutputFile=Es muss exakt ein Ausgabedateipfad ...@@ -70,6 +75,9 @@ CityDoctorValidation.notExactlyOneOutputFile=Es muss exakt ein Ausgabedateipfad
CityDoctorValidation.notExactlyOnePDFOutput=Es muss exakt ein Ausgabedateipfad f\u00fcr den PDF-Bericht angegeben werden. CityDoctorValidation.notExactlyOnePDFOutput=Es muss exakt ein Ausgabedateipfad f\u00fcr den PDF-Bericht angegeben werden.
CityDoctorValidation.notExactlyOneXMLOutput=Es muss exakt ein Ausgabedateipfad f\u00fcr den XML-Bericht angegeben werden. CityDoctorValidation.notExactlyOneXMLOutput=Es muss exakt ein Ausgabedateipfad f\u00fcr den XML-Bericht angegeben werden.
CityDoctorValidation.wrongInputFileFormat=Eingabedatei '{}' ist keine .gml oder .xml Datei. CityDoctorValidation.wrongInputFileFormat=Eingabedatei '{}' ist keine .gml oder .xml Datei.
CityGmlFeatureMapper.crossFeatureReferencesDetected=Modell enth\u00e4lt Features mit Feature\u00fcbergreifenden Polygonreferenzen ohne eigene Duplikate
CityGmlFeatureMapper.emptyCrossFeatureReference=Polygon {} in Feature {} wird Feature\u00fcbergreifend referenziert durch Features: {}
CityGmlFeatureMapper.unresolvedReferencesDetected=Modell enth\u00e4lt Features mit Polygonreferenzen welche nicht aufgel\u00f6st werden konnten
CityGmlParser.chunkReadFailed=Konnte Datei nicht in St\u00fccken lesen, versuche komplett zu lesen CityGmlParser.chunkReadFailed=Konnte Datei nicht in St\u00fccken lesen, versuche komplett zu lesen
CityGmlParser.convertCrsToUtmZoneN=Konvertiere Koordinatensystem zu UTM Zone {}N CityGmlParser.convertCrsToUtmZoneN=Konvertiere Koordinatensystem zu UTM Zone {}N
CityGmlParser.convertCrsToUtmZoneS=Konvertiere Koordinatensystem zu UTM Zone {}S CityGmlParser.convertCrsToUtmZoneS=Konvertiere Koordinatensystem zu UTM Zone {}S
...@@ -99,6 +107,9 @@ DatabaseHandler.serializationFailure=Serialisierung des CityObject {} fehlgeschl ...@@ -99,6 +107,9 @@ DatabaseHandler.serializationFailure=Serialisierung des CityObject {} fehlgeschl
DatabaseHandler.setupFailure=KRITISCHER FEHLER: Erstellen der eingebetteten Datenbank fehlgeschlagen! DatabaseHandler.setupFailure=KRITISCHER FEHLER: Erstellen der eingebetteten Datenbank fehlgeschlagen!
DatabaseHandler.tableFailure=KRITISCHER FEHLER: Erstellen der "features" Tabelle fehlgeschlagen! DatabaseHandler.tableFailure=KRITISCHER FEHLER: Erstellen der "features" Tabelle fehlgeschlagen!
DatabaseHandler.unmarshallingFailure=Laden des CityObject {} aus der Datenbank fehlgeschlagen! DatabaseHandler.unmarshallingFailure=Laden des CityObject {} aus der Datenbank fehlgeschlagen!
DatabaseSettings.invalidPath=Einstellungen f\u00fcr Directory und Name der Datenbank bilden keinen validen Pfad. Standardwerte werden stattdessen genutzt
DatabaseSettings.poolSizeLessThanOne=ConnectionPoolSize Einstellung wurde auf unter 1 gesetzt. Standardwert wird stattdessen genutzt
DatabaseSettings.poolSizeNaN=ConnectionPoolSize Einstellung ist kein Integer. Standardwert wird stattdessen genutzt
DistanceError.distanceFromPlane=Abstand von der Ebene DistanceError.distanceFromPlane=Abstand von der Ebene
ErrorItemVisitor.childId=Kind ID ErrorItemVisitor.childId=Kind ID
ErrorItemVisitor.component=Komponente ErrorItemVisitor.component=Komponente
......
package de.hft.stuttgart.citydoctor2.database;
import org.junit.jupiter.api.Test;
public class DatabaseSettingsTest {
public void testSetLocation(){
String tempdirectory = System.getProperty("java.io.tmpdir");
DatabaseSettings.setDBLocation(tempdirectory + "/citydoctor");
}
}
...@@ -16,7 +16,6 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; ...@@ -16,7 +16,6 @@ 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.parser.ParserConfiguration;
import org.citygml4j.core.model.building.AbstractBuilding; import org.citygml4j.core.model.building.AbstractBuilding;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
...@@ -25,25 +24,44 @@ import java.util.List; ...@@ -25,25 +24,44 @@ import java.util.List;
import static de.hft.stuttgart.citydoctor2.database.SerializationTest.assertCityObjectEquality; import static de.hft.stuttgart.citydoctor2.database.SerializationTest.assertCityObjectEquality;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class EmbeddedDataBaseHandlerTest { public class EmbeddedDataBaseHandlerTest {
private EmbeddedDatabaseHandler handler; private final EmbeddedDatabaseHandler handler = new EmbeddedDatabaseHandler(new EmbeddedDatabaseConfiguration());
@Test
public void testTempFileConfiguration(){
EmbeddedDatabaseConfiguration tempFileconfig = new EmbeddedDatabaseConfiguration("database",
"inTempConfig",1, false,false,
false,true);
EmbeddedDatabaseHandler tempHandler = new EmbeddedDatabaseHandler(tempFileconfig);
assertTrue(tempHandler.getTempFileDBLocation().exists());
@Before
public void resetDatabase() {
try {
handler = new EmbeddedDatabaseHandler();
handler.setupFeaturesTable();
} catch (Exception e) {
fail(e.getMessage());
}
} }
@Test
public void testInMemoryConfiguration() throws CityGmlParseException, InvalidGmlFileException {
EmbeddedDatabaseConfiguration inMemoryConfig = new EmbeddedDatabaseConfiguration("database",
"irrelevant",1, true,false,
false,false);
EmbeddedDatabaseHandler memHandler = new EmbeddedDatabaseHandler(inMemoryConfig);
assertNull(memHandler.getTempFileDBLocation());
ParserConfiguration config = new ParserConfiguration(8, false);
CityDoctorModel model = CityGmlParser.nestedCityGmlFileParse("src/test/resources/SimpleSolid_SrefBS.gml", config);
Building b = model.getBuildings().toList().get(0);
assertEquals(0, memHandler.getFeatureCount());
memHandler.marshallCityObject(b);
assertEquals(1, memHandler.getFeatureCount());
CityObject co = memHandler.unmarshallCityObject(b.getGmlId());
assertNotNull(co);
assertTrue(co instanceof Building);
}
@Test @Test
public void testGetBboxIntersectingObjects() { public void testGetBboxIntersectingObjects() {
handler.setupFeaturesTable();
Building b = new Building(); Building b = new Building();
Building b2 = new Building(); Building b2 = new Building();
b2.setGmlObject(Mockito.mock(AbstractBuilding.class)); b2.setGmlObject(Mockito.mock(AbstractBuilding.class));
...@@ -124,6 +142,7 @@ public class EmbeddedDataBaseHandlerTest { ...@@ -124,6 +142,7 @@ public class EmbeddedDataBaseHandlerTest {
@Test @Test
public void testGetBboxBufferIntersectingObjects() { public void testGetBboxBufferIntersectingObjects() {
handler.setupFeaturesTable();
Building b = new Building(); Building b = new Building();
Building b2 = new Building(); Building b2 = new Building();
b2.setGmlObject(Mockito.mock(AbstractBuilding.class)); b2.setGmlObject(Mockito.mock(AbstractBuilding.class));
...@@ -210,6 +229,7 @@ public class EmbeddedDataBaseHandlerTest { ...@@ -210,6 +229,7 @@ public class EmbeddedDataBaseHandlerTest {
Building b = model.getBuildings().toList().get(0); Building b = model.getBuildings().toList().get(0);
assertNotNull(b); assertNotNull(b);
assertEquals(1, handler.getFeatureCount());
handler.marshallCityObject(b); handler.marshallCityObject(b);
assertEquals(1, handler.getFeatureCount()); assertEquals(1, handler.getFeatureCount());
......
...@@ -30,7 +30,7 @@ import java.util.ArrayList; ...@@ -30,7 +30,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import de.hft.stuttgart.citydoctor2.utils.visitors.ClearVisitorResultsVisitor; import de.hft.stuttgart.citydoctor2.utils.visitors.ClearResultsVisitor;
import de.hft.stuttgart.citydoctor2.utils.visitors.GmlGeometryRebuilder; import de.hft.stuttgart.citydoctor2.utils.visitors.GmlGeometryRebuilder;
import de.hft.stuttgart.citydoctor2.utils.visitors.UnsetGeometriesVisitor; import de.hft.stuttgart.citydoctor2.utils.visitors.UnsetGeometriesVisitor;
import org.citygml4j.core.model.construction.AbstractConstructionSurface; import org.citygml4j.core.model.construction.AbstractConstructionSurface;
...@@ -141,7 +141,7 @@ public class BoundarySurfaceTest { ...@@ -141,7 +141,7 @@ public class BoundarySurfaceTest {
assertFalse(bs.getAllCheckResults().isEmpty()); assertFalse(bs.getAllCheckResults().isEmpty());
bs.addOpening(oMock); bs.addOpening(oMock);
bs.clearAllContainedCheckResults(); bs.clearAllContainedCheckResults();
verify(oMock).accept(any(ClearVisitorResultsVisitor.class)); verify(oMock).accept(any(ClearResultsVisitor.class));
assertTrue(bs.getAllCheckResults().isEmpty()); assertTrue(bs.getAllCheckResults().isEmpty());
} }
......
package de.hft.stuttgart.citydoctor2.datastructure;
import static org.junit.jupiter.api.Assertions.*;
import org.citygml4j.core.model.construction.WallSurface;
import org.citygml4j.core.util.geometry.GeometryFactory;
import org.junit.jupiter.api.Test;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
class BridgeConstructiveElementTest {
@Test
void testBoundarySurfaceGeometryRecreation() {
WallSurface wallSurface = new WallSurface();
BoundarySurface surface = new BoundarySurface(wallSurface);
surface.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD2));
var gmlBce = new org.citygml4j.core.model.bridge.BridgeConstructiveElement();
BridgeConstructiveElement bce = new BridgeConstructiveElement(gmlBce);
bce.addBoundarySurface(surface);
assertNull(wallSurface.getLod2MultiSurface());
GeometryFactory factory = GeometryFactory.newInstance();
ParserConfiguration config = new ParserConfiguration(8, false);
bce.reCreateGeometries(factory, config);
assertNotNull(wallSurface.getLod2MultiSurface());
}
}
...@@ -52,12 +52,12 @@ public class BuildingTest { ...@@ -52,12 +52,12 @@ public class BuildingTest {
Vertex v2 = new Vertex(2, 0, 0); Vertex v2 = new Vertex(2, 0, 0);
LinearRing lr = new LinearRing(LinearRingType.EXTERIOR); LinearRing lr = new LinearRing(LinearRingType.EXTERIOR);
ConcretePolygon p = new ConcretePolygon(); ConcretePolygon p = new ConcretePolygon();
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD0, Orientation.OUTWARD);
geom.addPolygon(p);
p.setExteriorRing(lr); p.setExteriorRing(lr);
lr.addVertex(v1); lr.addVertex(v1);
lr.addVertex(v2); lr.addVertex(v2);
lr.addVertex(v1); lr.addVertex(v1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD0, Orientation.OUTWARD);
geom.addPolygon(p);
geom.updateEdgesAndVertices(); geom.updateEdgesAndVertices();
b.addGeometry(geom); b.addGeometry(geom);
......
...@@ -33,7 +33,7 @@ import java.util.ArrayList; ...@@ -33,7 +33,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor;
import de.hft.stuttgart.citydoctor2.utils.visitors.ClearVisitorResultsVisitor; import de.hft.stuttgart.citydoctor2.utils.visitors.ClearResultsVisitor;
import org.citygml4j.core.model.construction.AbstractConstructionSurface; import org.citygml4j.core.model.construction.AbstractConstructionSurface;
import org.citygml4j.core.model.construction.WallSurface; import org.citygml4j.core.model.construction.WallSurface;
import org.citygml4j.core.util.geometry.GeometryFactory; import org.citygml4j.core.util.geometry.GeometryFactory;
...@@ -105,9 +105,9 @@ public class GeometryTest { ...@@ -105,9 +105,9 @@ public class GeometryTest {
ConcretePolygon p = new ConcretePolygon(); ConcretePolygon p = new ConcretePolygon();
LinearRing lr = new LinearRing(LinearRingType.EXTERIOR); LinearRing lr = new LinearRing(LinearRingType.EXTERIOR);
p.setExteriorRing(lr); p.setExteriorRing(lr);
geom.addPolygon(p);
lr.addVertex(new Vertex(0, 0, 0)); lr.addVertex(new Vertex(0, 0, 0));
geom.addPolygon(p);
p.setPartOfSurface(bs); p.setPartOfSurface(bs);
p.setPartOfInstallation(bi); p.setPartOfInstallation(bi);
...@@ -139,9 +139,9 @@ public class GeometryTest { ...@@ -139,9 +139,9 @@ public class GeometryTest {
ConcretePolygon p = new ConcretePolygon(); ConcretePolygon p = new ConcretePolygon();
LinearRing lr = new LinearRing(LinearRingType.EXTERIOR); LinearRing lr = new LinearRing(LinearRingType.EXTERIOR);
p.setExteriorRing(lr); p.setExteriorRing(lr);
geom.addPolygon(p);
lr.addVertex(new Vertex(0, 0, 0)); lr.addVertex(new Vertex(0, 0, 0));
geom.addPolygon(p);
p.setPartOfSurface(bs); p.setPartOfSurface(bs);
bs.addGeometry(geom); bs.addGeometry(geom);
...@@ -165,10 +165,10 @@ public class GeometryTest { ...@@ -165,10 +165,10 @@ public class GeometryTest {
ConcretePolygon p = new ConcretePolygon(); ConcretePolygon p = new ConcretePolygon();
LinearRing lr = new LinearRing(LinearRingType.EXTERIOR); LinearRing lr = new LinearRing(LinearRingType.EXTERIOR);
p.setExteriorRing(lr); p.setExteriorRing(lr);
geom.addPolygon(p);
lr.addVertex(new Vertex(0, 0, 0)); lr.addVertex(new Vertex(0, 0, 0));
bs.addGeometry(geom); bs.addGeometry(geom);
geom.addPolygon(p);
p.setPartOfSurface(bs); p.setPartOfSurface(bs);
p.setPartOfInstallation(bi); p.setPartOfInstallation(bi);
...@@ -301,7 +301,7 @@ public class GeometryTest { ...@@ -301,7 +301,7 @@ public class GeometryTest {
Polygon p = mock(Polygon.class); Polygon p = mock(Polygon.class);
geom.addPolygon(p); geom.addPolygon(p);
geom.clearAllContainedCheckResults(); geom.clearAllContainedCheckResults();
verify(p).accept(any(ClearVisitorResultsVisitor.class)); verify(p).accept(any(ClearResultsVisitor.class));
assertFalse(geom.containsError(CheckId.C_GE_P_HOLE_OUTSIDE)); assertFalse(geom.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
} }
......
...@@ -90,8 +90,8 @@ public class ImplicitGeometryTest { ...@@ -90,8 +90,8 @@ public class ImplicitGeometryTest {
assertFalse(lib.containsError(CheckId.C_GE_P_NON_PLANAR)); assertFalse(lib.containsError(CheckId.C_GE_P_NON_PLANAR));
igh.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class))); igh.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class)));
assertFalse("ImplicitGeometryHolder's errors should be ignored and delegate the call to the prototype geometry", assertFalse(lib.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
lib.containsError(CheckId.C_GE_P_HOLE_OUTSIDE)); assertTrue(igh.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
lib.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class))); lib.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class)));
assertTrue(lib.containsError(CheckId.C_GE_P_HOLE_OUTSIDE)); assertTrue(lib.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
...@@ -105,27 +105,39 @@ public class ImplicitGeometryTest { ...@@ -105,27 +105,39 @@ public class ImplicitGeometryTest {
@Test @Test
public void testClearAllContainedCheckResults() { public void testClearAllContainedCheckResults() {
igh.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.OK, null));
lib.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.OK, null)); lib.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.OK, null));
assertEquals(1, lib.getAllCheckResults().size()); assertEquals(1, lib.getAllCheckResults().size());
assertEquals(1, igh.getAllCheckResults().size());
lib.clearAllContainedCheckResults(); lib.clearAllContainedCheckResults();
assertTrue(lib.getAllCheckResults().isEmpty()); assertTrue(lib.getAllCheckResults().isEmpty());
assertEquals(1, igh.getAllCheckResults().size());
igh.clearAllContainedCheckResults();
assertTrue(igh.getAllCheckResults().isEmpty());
} }
@Test @Test
public void testContainsAnyError() { public void testContainsAnyError() {
assertFalse(lib.containsAnyError()); assertFalse(lib.containsAnyError());
assertFalse(igh.containsAnyError());
igh.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.OK, null)); igh.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.OK, null));
assertFalse(lib.containsAnyError()); assertFalse(lib.containsAnyError());
assertFalse(igh.containsAnyError());
lib.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.OK, null)); lib.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.OK, null));
assertFalse(lib.containsAnyError()); assertFalse(lib.containsAnyError());
assertFalse(igh.containsAnyError());
igh.addCheckResult( igh.addCheckResult(
new CheckResult(CheckId.C_GE_P_INNER_RINGS_NESTED, ResultStatus.ERROR, mock(CheckError.class))); new CheckResult(CheckId.C_GE_P_INNER_RINGS_NESTED, ResultStatus.ERROR, mock(CheckError.class)));
assertFalse(lib.containsAnyError()); assertFalse(lib.containsAnyError());
assertTrue(igh.containsAnyError());
lib.addCheckResult( lib.addCheckResult(
new CheckResult(CheckId.C_GE_P_INNER_RINGS_NESTED, ResultStatus.ERROR, mock(CheckError.class))); new CheckResult(CheckId.C_GE_P_INNER_RINGS_NESTED, ResultStatus.ERROR, mock(CheckError.class)));
assertTrue(lib.containsAnyError()); assertTrue(lib.containsAnyError());
assertTrue(igh.containsAnyError());
} }
@Test @Test
...@@ -134,19 +146,18 @@ public class ImplicitGeometryTest { ...@@ -134,19 +146,18 @@ public class ImplicitGeometryTest {
igh.addCheckResult(new CheckResult(CheckId.C_GE_P_NON_PLANAR, ResultStatus.ERROR, mock(CheckError.class))); igh.addCheckResult(new CheckResult(CheckId.C_GE_P_NON_PLANAR, ResultStatus.ERROR, mock(CheckError.class)));
igh.collectContainedErrors(errors); igh.collectContainedErrors(errors);
assertEquals("ImplicitGeometryHolder's errors should be ignored and delegate the call to the prototype geometry", assertEquals(1, errors.size());
0, errors.size()); errors.clear();
lib.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class))); lib.addCheckResult(new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, mock(CheckError.class)));
igh.collectContainedErrors(errors); igh.collectContainedErrors(errors);
assertEquals("ImplicitGeometryHolder's errors should be ignored and delegate the call to the prototype geometry", assertEquals(1, errors.size());
1, errors.size());
} }
@Test @Test
public void testAccept() { public void testAccept() {
int polyCount = lib.getPolygons().size(); int polyCount = igh.getPolygons().size();
AtomicInteger loCounter = new AtomicInteger(0); AtomicInteger loCounter = new AtomicInteger(0);
...@@ -157,7 +168,7 @@ public class ImplicitGeometryTest { ...@@ -157,7 +168,7 @@ public class ImplicitGeometryTest {
} }
}; };
igh.accept(c); igh.accept(c);
assertEquals("ImplicitGeometryHolder should only check polygons of prototype geometry", assertEquals("Visitors should only walk over ImplicitGeometryHolder",
polyCount, loCounter.intValue()); polyCount, loCounter.intValue());
} }
......
...@@ -22,6 +22,7 @@ import static org.junit.Assert.*; ...@@ -22,6 +22,7 @@ import static org.junit.Assert.*;
import org.junit.Test; import org.junit.Test;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry.Orientation;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType;
import de.hft.stuttgart.citydoctor2.math.UnitVector3d; import de.hft.stuttgart.citydoctor2.math.UnitVector3d;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.Vector3d;
...@@ -35,7 +36,9 @@ public class LinearRingTest { ...@@ -35,7 +36,9 @@ public class LinearRingTest {
@Test @Test
public void testIsPointInside1() { public void testIsPointInside1() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD2, Orientation.OUTWARD);
Polygon p = new ConcretePolygon(); Polygon p = new ConcretePolygon();
geom.addPolygon(p);
LinearRing lr = new LinearRing(LinearRingType.EXTERIOR); LinearRing lr = new LinearRing(LinearRingType.EXTERIOR);
p.setExteriorRing(lr); p.setExteriorRing(lr);
......
...@@ -6,7 +6,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckId; ...@@ -6,7 +6,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.visitors.ClearVisitorResultsVisitor; import de.hft.stuttgart.citydoctor2.utils.visitors.ClearResultsVisitor;
import org.citygml4j.core.model.core.AbstractSpaceBoundary; import org.citygml4j.core.model.core.AbstractSpaceBoundary;
import org.citygml4j.core.model.core.AbstractUnoccupiedSpace; import org.citygml4j.core.model.core.AbstractUnoccupiedSpace;
import org.citygml4j.core.model.deprecated.core.DeprecatedPropertiesOfAbstractCityObject; import org.citygml4j.core.model.deprecated.core.DeprecatedPropertiesOfAbstractCityObject;
...@@ -91,7 +91,7 @@ public class TrafficSpaceTest { ...@@ -91,7 +91,7 @@ public class TrafficSpaceTest {
tso.clearAllContainedCheckResults(); tso.clearAllContainedCheckResults();
assertFalse(tso.containsError(CheckId.C_GE_P_HOLE_OUTSIDE)); assertFalse(tso.containsError(CheckId.C_GE_P_HOLE_OUTSIDE));
verify(taMock).accept(any(ClearVisitorResultsVisitor.class)); verify(taMock).accept(any(ClearResultsVisitor.class));
} }
@Test @Test
......
...@@ -62,20 +62,4 @@ public class VertexTest { ...@@ -62,20 +62,4 @@ public class VertexTest {
assertEquals(1, adjacentRings.size()); assertEquals(1, adjacentRings.size());
assertTrue(adjacentRings.contains(lr)); assertTrue(adjacentRings.contains(lr));
} }
@Test(expected = IllegalStateException.class)
public void testGetAdjacentRingsGeometryNotContainingVertex() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p = new ConcretePolygon();
LinearRing lr = new LinearRing(LinearRingType.EXTERIOR);
p.setExteriorRing(lr);
Vertex v0 = new Vertex(0, 0, 0);
lr.addVertex(v0);
v0.getAdjacentRings(geom);
}
} }
...@@ -37,7 +37,7 @@ public class CityGml3ParserTest { ...@@ -37,7 +37,7 @@ public class CityGml3ParserTest {
@Test @Test
public void testGetContext() { public void testGetContext() {
CityGMLContext context = CityGmlParser.getContext(); CityGMLContext context = CityGmlParser.setupContext();
List<ADE> ades = ADERegistry.getInstance().getADEs(); List<ADE> ades = ADERegistry.getInstance().getADEs();
ADE ade = ades.get(0); ADE ade = ades.get(0);
assertEquals(QualityADEContext.class, ade.getClass()); assertEquals(QualityADEContext.class, ade.getClass());
......
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