From ef413298dcef3214342b549a86091b31d63f9e71 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 4 Jun 2025 16:23:50 +0200 Subject: [PATCH 001/118] Feature: Add 2D WKT representation for bbox --- .../datastructure/BoundingBox.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java index f2d535fe..e2596c80 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java @@ -161,4 +161,29 @@ public class BoundingBox { public double getDiagonalLength() { return bbox[1].getDistance(bbox[0]); } + + /** + * Returns a WKT polygon representation in 2D (the "shadow") of this bbox + * + * @return WKT String of this bbox's shadow + */ + public String toWktShadow() { + double x1 = bbox[0].getX(); + double y1 = bbox[0].getY(); + double x2 = x1 + bbox[1].getX(); + double y2 = y1 + bbox[1].getY(); + + double minX = Math.min(x1, x2); + double minY = Math.min(y1, y2); + double maxX = Math.max(x1, x2); + double maxY = Math.max(y1, y2); + + return String.format("POLYGON((%.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f))", + minX, minY, + maxX, minY, + maxX, maxY, + minX, maxY, + minX, minY + ); + } } -- GitLab From e1dfa87051519cdf3b92bd864e92a9d39ce366d0 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 4 Jun 2025 16:24:39 +0200 Subject: [PATCH 002/118] Feat: Add cache for CityObjects --- .../citydoctor2/database/CityObjectCache.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java new file mode 100644 index 00000000..9911632d --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -0,0 +1,55 @@ +package de.hft.stuttgart.citydoctor2.database; + +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; + +import java.util.List; +import java.util.Map; +import java.lang.ref.SoftReference; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * Soft-reference cache for + */ +public class CityObjectCache { + private final Map> cache = new ConcurrentHashMap<>(); + + public void put(GmlId id, CityObject cityObject) { + cache.put(id, new SoftReference<>(cityObject)); + } + + public void cache(List objects) { + + } + + public CityObject get(GmlId id) { + SoftReference ref = cache.get(id); + if (ref == null) { + return null; + } + CityObject cityObject = ref.get(); + if (cityObject == null) { + cityObject = DatabaseHandler.unmarshallCityObject(id); + if (cityObject == null) { + cache.remove(id); + } else { + this.put(id, cityObject); + } + } + return cityObject; + } + + public void remove(GmlId id) { + cache.remove(id); + } + + public void clear() { + cache.clear(); + } + + public int size() { + return cache.size(); + } +} + -- GitLab From bd7cb1230ec9e8cdbb99e0e176dcc9f7a5f2a78a Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 4 Jun 2025 16:25:19 +0200 Subject: [PATCH 003/118] Feat: Add Embedded database implementation --- .../citydoctor2/database/DatabaseHandler.java | 169 ++++++++++++++++++ CityDoctorParent/pom.xml | 5 + 2 files changed, 174 insertions(+) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java new file mode 100644 index 00000000..7c68e117 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -0,0 +1,169 @@ +package de.hft.stuttgart.citydoctor2.database; + +import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.h2gis.functions.factory.H2GISDBFactory; +import org.h2gis.utilities.wrapper.ConnectionWrapper; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class DatabaseHandler { + + private static final Logger logger = LogManager.getLogger(DatabaseHandler.class); + private static final String DB_NAME = "cd_db"; + + + /** + * Sets up a new, empty embedded database. Will override an existing embedded database. + */ + public static void setupDatabase() { + try { + H2GISDBFactory.createSpatialDataBase(DB_NAME, + true, ";FILE_LOCK=SOCKET"); + setupFeaturesTable(); + } catch (Exception e) { + logger.fatal("FATAL ERROR: Creation of embedded database failed!"); + logger.fatal(e.getMessage()); + } + + + } + + public static void setupFeaturesTable() { + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (PreparedStatement dropPs = con.prepareStatement("DROP TABLE IF EXISTS features")) { + dropPs.executeUpdate(); + if (logger.isDebugEnabled()) { + logger.debug("Dropped existing features table"); + } + } + try (PreparedStatement createPs = con.prepareStatement("CREATE TABLE features (gmlid VARCHAR(255) PRIMARY KEY," + + "bbox GEOMETRY, data BLOB, type VARCHAR(255) NOT NULL)")) { + createPs.executeUpdate(); + if (logger.isDebugEnabled()) { + logger.debug("Created features table"); + } + } + + } catch (SQLException e) { + logger.fatal("FATAL ERROR: Creation of CityDoctorModel database table failed!"); + logger.fatal(e.getMessage()); + } + } + + public static void marshallCityObject(CityObject co) { + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?, ?)")) { + ps.setString(1, co.getGmlId().toString()); + String bbox = BoundingBox.of(co.getHighestLodGeometry().getPolygons()).toWktShadow(); + ps.setString(2, bbox); + if (logger.isDebugEnabled()) { + logger.debug("Attempting to serialize city object {} ", co); + } + ps.setString(4, co.getClass().getSimpleName()); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try (ObjectOutputStream out = new ObjectOutputStream(bos)) { + out.writeObject(co); + } + byte[] bytes = bos.toByteArray(); + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ps.setBinaryStream(3, bis, bytes.length); + ps.executeUpdate(); + logger.debug("Successfully serialized CityObject to database"); + } + } catch (SQLException e) { + logger.error("Saving of city object to embedded database failed!"); + logger.debug(e.getMessage()); + } catch (IOException e) { + logger.error("Encountered unknown writing error for embedded database!"); + logger.debug(e.getMessage()); + } + } + + public static CityObject unmarshallCityObject(GmlId id) { + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (PreparedStatement ps = con.prepareStatement("SELECT * FROM features WHERE gmlid = ?")) { + ps.setString(1, id.toString()); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + try (InputStream is = rs.getBinaryStream("data")) { + return deserializeCityObject(new ObjectInputStream(is), rs.getString("type")); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } catch (SQLException e) { + logger.error("Loading of city object from embedded database failed!"); + logger.debug(e.getMessage()); + } + + return null; + } + + public static List getBufferIntersectingObjects(CityObject co, Double buffersize) { + ResultSet rs = null; + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + + } catch (SQLException e) { + logger.error("Connection to database failed while trying to get buffer intersecting objects!"); + logger.debug(e.getMessage()); + } + return Collections.emptyList(); + } + + public static List getBBoxIntersectingObjects(CityObject co) { + List intersectingObjects = new ArrayList<>(); + + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (PreparedStatement ps = con.prepareStatement("SELECT * FROM features AS tab1" + + " WHERE ST_INTERSECTS(tab1.geom, (SELECT GEOM FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { + ps.setString(1, co.getGmlId().toString()); + ps.setString(2, co.getGmlId().toString()); + ResultSet rs = ps.executeQuery(); + intersectingObjects.addAll(retrieveCityObjects(rs)); + } + } catch (SQLException e) { + logger.error("Connection to database failed while trying to get bbox intersecting objects!"); + logger.debug(e.getMessage()); + } + return intersectingObjects; + } + + private static List retrieveCityObjects(ResultSet rs) throws SQLException { + List cos = new ArrayList<>(); + while (rs.next()) { + try (InputStream is = rs.getBinaryStream("data")) { + cos.add(deserializeCityObject(new ObjectInputStream(is), rs.getString("type"))); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + return cos; + } + + private static CityObject deserializeCityObject(ObjectInputStream ois, String className) throws IOException, ClassNotFoundException { + Class type = Class.forName(className); + return (CityObject) type.cast(ois.readObject()); + } +} diff --git a/CityDoctorParent/pom.xml b/CityDoctorParent/pom.xml index 7c218dcc..64ab2ea3 100644 --- a/CityDoctorParent/pom.xml +++ b/CityDoctorParent/pom.xml @@ -251,6 +251,11 @@ 4.6.1 test + + org.orbisgis + h2gis + 2.2.3 + org.openjfx javafx-controls -- GitLab From 47cc6a9f63a6416ceac57d035b36edd725ac2759 Mon Sep 17 00:00:00 2001 From: Riegel Date: Mon, 23 Jun 2025 17:01:34 +0200 Subject: [PATCH 004/118] Feat: Implement database entry counting --- .../citydoctor2/database/DatabaseHandler.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 7c68e117..bfda3ce0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -148,6 +148,23 @@ public class DatabaseHandler { return intersectingObjects; } + public static int getFeatureCount() { + int count = -1; + + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(gmlid) AS nr FROM features")) { + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + count = rs.getInt("nr"); + } + } + } catch (SQLException e) { + logger.error("Connection to database failed while trying to get bbox intersecting objects!"); + logger.debug(e.getMessage()); + } + return count; + } + private static List retrieveCityObjects(ResultSet rs) throws SQLException { List cos = new ArrayList<>(); while (rs.next()) { -- GitLab From 6ebc29496120dbe4e2d71b3ddc0984c6fd0dbc10 Mon Sep 17 00:00:00 2001 From: Riegel Date: Mon, 23 Jun 2025 17:03:22 +0200 Subject: [PATCH 005/118] Feat: Implement selection via buffer in database --- .../citydoctor2/database/DatabaseHandler.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index bfda3ce0..2a3ac126 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -121,13 +121,22 @@ public class DatabaseHandler { public static List getBufferIntersectingObjects(CityObject co, Double buffersize) { ResultSet rs = null; + public static List getBufferIntersectingObjects(CityObject co, Double buffersize) { + List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features AS tab1" + + " WHERE ST_INTERSECTS(ST_BUFFER(tab1.geom,?), (SELECT GEOM FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { + ps.setDouble(1, buffersize); + ps.setString(2, co.getGmlId().toString()); + ps.setString(3, co.getGmlId().toString()); + ResultSet rs = ps.executeQuery(); + intersectingObjects.addAll(retrieveCityObjects(rs)); + } } catch (SQLException e) { logger.error("Connection to database failed while trying to get buffer intersecting objects!"); logger.debug(e.getMessage()); } - return Collections.emptyList(); + return intersectingObjects; } public static List getBBoxIntersectingObjects(CityObject co) { -- GitLab From 4e6dafd4ee98cbf487156da37fc24e8f5b30fbaf Mon Sep 17 00:00:00 2001 From: Riegel Date: Mon, 23 Jun 2025 17:04:06 +0200 Subject: [PATCH 006/118] Feat: Implement getting all Features from database --- .../citydoctor2/database/DatabaseHandler.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 2a3ac126..34a475e3 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -20,7 +20,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class DatabaseHandler { @@ -98,7 +97,7 @@ public class DatabaseHandler { public static CityObject unmarshallCityObject(GmlId id) { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT * FROM features WHERE gmlid = ?")) { + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features WHERE gmlid = ?")) { ps.setString(1, id.toString()); ResultSet rs = ps.executeQuery(); if (rs.next()) { @@ -119,8 +118,20 @@ public class DatabaseHandler { return null; } - public static List getBufferIntersectingObjects(CityObject co, Double buffersize) { - ResultSet rs = null; + public static List getAllCityObjects() { + List cos = new ArrayList<>(); + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features")) { + ResultSet rs = ps.executeQuery(); + cos.addAll(retrieveCityObjects(rs)); + } + } catch (SQLException e) { + logger.error("Connection to database failed while trying to get all CityObjects!"); + logger.debug(e.getMessage()); + } + return cos; + } + public static List getBufferIntersectingObjects(CityObject co, Double buffersize) { List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { @@ -143,7 +154,7 @@ public class DatabaseHandler { List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT * FROM features AS tab1" + + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features AS tab1" + " WHERE ST_INTERSECTS(tab1.geom, (SELECT GEOM FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { ps.setString(1, co.getGmlId().toString()); ps.setString(2, co.getGmlId().toString()); -- GitLab From 9755404f92c34531edb63f7bbafa7e8d325e3619 Mon Sep 17 00:00:00 2001 From: Riegel Date: Mon, 23 Jun 2025 17:05:24 +0200 Subject: [PATCH 007/118] Test: Setup database test-case --- .../database/DataBaseHandlerTest.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java new file mode 100644 index 00000000..d2253bcc --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -0,0 +1,62 @@ +package de.hft.stuttgart.citydoctor2.database; + +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; +import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; +import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import org.h2gis.functions.factory.H2GISDBFactory; +import org.h2gis.utilities.wrapper.ConnectionWrapper; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +public class DataBaseHandlerTest { + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + DatabaseHandler.setupDatabase(); + try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { + assertNotNull(con); + } + } + + @Before + public void prepareDatabase() { + try { + DatabaseHandler.setupFeaturesTable(); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void cityObjectMarshallingTest() throws InvalidGmlFileException, CityGmlParseException { + try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { + assertNotNull(con); + ParserConfiguration config = new ParserConfiguration(8, false); + CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS.gml", config); + Building b = model.getBuildings().get(0); + assertNotNull(b); + assertEquals(0, DatabaseHandler.getFeatureCount()); + DatabaseHandler.marshallCityObject(b); + assertEquals(1, DatabaseHandler.getFeatureCount()); + +// Building b2 = (Building) DatabaseHandler.unmarshallCityObject(b.getGmlId()); +// assertNotNull(b2); +// assertEquals(1, DatabaseHandler.getFeatureCount()); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} -- GitLab From adb25c458f556f0fc77902d172e5dc1e44d6625b Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 26 Jun 2025 11:04:30 +0200 Subject: [PATCH 008/118] Style: Rename function --- .../hft/stuttgart/citydoctor2/datastructure/BoundingBox.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java index e2596c80..7a5c4c4f 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java @@ -163,11 +163,11 @@ public class BoundingBox { } /** - * Returns a WKT polygon representation in 2D (the "shadow") of this bbox + * Returns a WKT polygon representation in 2D of this bbox * * @return WKT String of this bbox's shadow */ - public String toWktShadow() { + public String to2DWkt() { double x1 = bbox[0].getX(); double y1 = bbox[0].getY(); double x2 = x1 + bbox[1].getX(); -- GitLab From d99ede5d7f995a55dcf759feed9d9f0c043092b6 Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 26 Jun 2025 11:09:27 +0200 Subject: [PATCH 009/118] Fix: Determining target class for deserialization --- .../citydoctor2/database/DatabaseHandler.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 34a475e3..8f52a370 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -28,6 +28,10 @@ public class DatabaseHandler { private static final String DB_NAME = "cd_db"; + private DatabaseHandler() { + } + + /** * Sets up a new, empty embedded database. Will override an existing embedded database. */ @@ -39,6 +43,7 @@ public class DatabaseHandler { } catch (Exception e) { logger.fatal("FATAL ERROR: Creation of embedded database failed!"); logger.fatal(e.getMessage()); + // TODO: implement popup and shutdown } @@ -70,12 +75,12 @@ public class DatabaseHandler { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?, ?)")) { ps.setString(1, co.getGmlId().toString()); - String bbox = BoundingBox.of(co.getHighestLodGeometry().getPolygons()).toWktShadow(); + String bbox = BoundingBox.of(co.getHighestLodGeometry().getPolygons()).to2DWkt(); ps.setString(2, bbox); if (logger.isDebugEnabled()) { logger.debug("Attempting to serialize city object {} ", co); } - ps.setString(4, co.getClass().getSimpleName()); + ps.setString(4, co.getClass().getName()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); try (ObjectOutputStream out = new ObjectOutputStream(bos)) { out.writeObject(co); @@ -201,6 +206,7 @@ public class DatabaseHandler { private static CityObject deserializeCityObject(ObjectInputStream ois, String className) throws IOException, ClassNotFoundException { Class type = Class.forName(className); - return (CityObject) type.cast(ois.readObject()); + CityObject castCo = (CityObject) type.cast(ois.readObject()); + return castCo.getTopLevelCityObject(); } } -- GitLab From d30f033079130bfed6c471503d00327874322650 Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 26 Jun 2025 11:09:57 +0200 Subject: [PATCH 010/118] Feat: Add function stub for method --- .../stuttgart/citydoctor2/database/DatabaseHandler.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 8f52a370..f802e45b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -3,6 +3,7 @@ package de.hft.stuttgart.citydoctor2.database; import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import de.hft.stuttgart.citydoctor2.utils.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.h2gis.functions.factory.H2GISDBFactory; @@ -173,6 +174,12 @@ public class DatabaseHandler { return intersectingObjects; } + public static List>> getCollidingObjects() { + List>> collidingObjects = new ArrayList<>(); + // TODO: Implement query to return all Objects that have a collision and the list of whhich other objects collide + return collidingObjects; + } + public static int getFeatureCount() { int count = -1; -- GitLab From 25b56fba600bc55cc8c9037bee1908d05b4de947 Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 26 Jun 2025 11:46:40 +0200 Subject: [PATCH 011/118] Fix: Use double primitive --- .../hft/stuttgart/citydoctor2/database/DatabaseHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index f802e45b..5255eea5 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -138,12 +138,12 @@ public class DatabaseHandler { return cos; } - public static List getBufferIntersectingObjects(CityObject co, Double buffersize) { + public static List getBufferIntersectingObjects(CityObject co, double bufferSize) { List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features AS tab1" + " WHERE ST_INTERSECTS(ST_BUFFER(tab1.geom,?), (SELECT GEOM FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { - ps.setDouble(1, buffersize); + ps.setDouble(1, bufferSize); ps.setString(2, co.getGmlId().toString()); ps.setString(3, co.getGmlId().toString()); ResultSet rs = ps.executeQuery(); -- GitLab From a9a5780b79af97fbe1411461754f5a3363dad0f5 Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 26 Jun 2025 11:47:23 +0200 Subject: [PATCH 012/118] Test: Extend database marshalling testcase --- .../database/DataBaseHandlerTest.java | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index d2253bcc..962d95a8 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -1,21 +1,23 @@ package de.hft.stuttgart.citydoctor2.database; +import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.Copy; +import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector; import org.h2gis.functions.factory.H2GISDBFactory; -import org.h2gis.utilities.wrapper.ConnectionWrapper; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import java.io.File; import java.sql.Connection; -import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -45,16 +47,28 @@ public class DataBaseHandlerTest { try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { assertNotNull(con); ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS.gml", config); + CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/FZK_haus.gml", config); Building b = model.getBuildings().get(0); + Copy.copy(b); assertNotNull(b); assertEquals(0, DatabaseHandler.getFeatureCount()); DatabaseHandler.marshallCityObject(b); assertEquals(1, DatabaseHandler.getFeatureCount()); -// Building b2 = (Building) DatabaseHandler.unmarshallCityObject(b.getGmlId()); -// assertNotNull(b2); -// assertEquals(1, DatabaseHandler.getFeatureCount()); + CityObject b2 = DatabaseHandler.unmarshallCityObject(b.getGmlId()); + assertNotNull(b2); + assertEquals(1, DatabaseHandler.getFeatureCount()); + assertEquals(b.getGmlId(), b2.getGmlId()); + assertEquals(b.getFeatureType(), b2.getFeatureType()); + + CityObjectCollector collector = new CityObjectCollector(); + b.accept(collector); + Set coB = collector.getCityObjects(); + collector = new CityObjectCollector(); + b2.accept(collector); + Set coB2 = collector.getCityObjects(); + assertEquals(coB.size(), coB2.size()); + } catch (SQLException e) { throw new RuntimeException(e); } -- GitLab From cfe0a6ca0c0bf0927eb5816427487b23b030223f Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 26 Jun 2025 11:49:43 +0200 Subject: [PATCH 013/118] Build: Adjust dependency management --- CityDoctorParent/CityDoctorModel/pom.xml | 10 +++++----- CityDoctorParent/pom.xml | 21 ++++++++++++++++----- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/pom.xml b/CityDoctorParent/CityDoctorModel/pom.xml index a5a024e3..5406750b 100644 --- a/CityDoctorParent/CityDoctorModel/pom.xml +++ b/CityDoctorParent/CityDoctorModel/pom.xml @@ -38,11 +38,6 @@ org.jogamp.jogl jogl-all-main - - junit - junit - test - org.apache.logging.log4j log4j-api @@ -77,6 +72,11 @@ io.github.earcut4j earcut4j + + org.orbisgis + h2gis + + diff --git a/CityDoctorParent/pom.xml b/CityDoctorParent/pom.xml index 64ab2ea3..8d1a487a 100644 --- a/CityDoctorParent/pom.xml +++ b/CityDoctorParent/pom.xml @@ -242,6 +242,11 @@ earcut4j 2.2.2 + + org.orbisgis + h2gis + 2.2.3 + @@ -251,11 +256,6 @@ 4.6.1 test - - org.orbisgis - h2gis - 2.2.3 - org.openjfx javafx-controls @@ -272,6 +272,17 @@ info.picocli picocli + + org.apache.logging.log4j + log4j-slf4j18-impl + 2.18.0 + test + + + junit + junit + test + -- GitLab From 6e94b4c82d7e7ca49cf0185b29393c884d8bde0d Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 15 Jul 2025 14:47:21 +0200 Subject: [PATCH 014/118] Fix: BoundingBoxCalculator not including BoundarySurfaces --- .../utils/BoundingBoxCalculator.java | 72 ++++++++----------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java index 94a65d45..78acc1d0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java @@ -26,12 +26,9 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Polygon; -import de.hft.stuttgart.citydoctor2.datastructure.TopLevelTransportFeature; -import de.hft.stuttgart.citydoctor2.datastructure.TrafficSpaceObject; -import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject; -import de.hft.stuttgart.citydoctor2.datastructure.TransportationSpace; import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.math.Vector3d; +import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector; /** * Utility class for calculating axis aligned bounding boxes for different @@ -99,14 +96,13 @@ public class BoundingBoxCalculator { * @return the bounding box of the model */ public static BoundingBox calculateBoundingBox(CityDoctorModel model) { - Vector3d low = new Vector3d(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); + Vector3d low = new Vector3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); Vector3d high = new Vector3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); - //TODO: Rework this using visitors findMinMax(low, high, model.getBuildings()); findMinMax(low, high, model.getBridges()); findMinMax(low, high, model.getLand()); - findMinMaxTransport(low, high, model.getTransportation()); + findMinMax(low, high, model.getTransportation()); findMinMax(low, high, model.getWater()); findMinMax(low, high, model.getVegetation()); findMinMax(low, high, model.getTunnels()); @@ -159,47 +155,39 @@ public class BoundingBoxCalculator { } } - //TODO: Implement this properly with visitor. Quick and dirty fix for the renderer - private static void findMinMaxTransport(Vector3d low, Vector3d high, List features) { - for (TransportationObject to : features) { - findMinMax(low, high, to); - if (to instanceof TransportationSpace ts) { - findMinMaxTransport(low, high, ts.getTrafficSpaces()); - findMinMaxTransport(low, high, ts.getAuxTrafficSpaces()); - if (to instanceof TopLevelTransportFeature top) { - findMinMaxTransport(low, high, top.getSections()); - findMinMaxTransport(low, high, top.getIntersections()); + + private static void findMinMax(Vector3d low, Vector3d high, CityObject co) { + CityObjectCollector collector = new CityObjectCollector(); + co.accept(collector); + for (CityObject object : collector.getCityObjects()) { + for (Geometry geom : object.getGeometries()) { + if (geom.getVertices() == null) { + geom.updateVertices(); } - } else if (to instanceof TrafficSpaceObject tso) { - findMinMaxTransport(low, high, tso.getTrafficAreas()); + findMinMax(low, high, geom); } } } - private static void findMinMax(Vector3d low, Vector3d high, CityObject co) { - for (Geometry geom : co.getGeometries()) { - if (geom.getVertices() == null) { - geom.updateVertices(); + private static void findMinMax(Vector3d low, Vector3d high, Geometry geom) { + for (Vertex v : geom.getVertices()) { + if (v.getX() < low.getX()) { + low.setX(v.getX()); } - for (Vertex v : geom.getVertices()) { - if (v.getX() < low.getX()) { - low.setX(v.getX()); - } - if (v.getX() > high.getX()) { - high.setX(v.getX()); - } - if (v.getY() < low.getY()) { - low.setY(v.getY()); - } - if (v.getY() > high.getY()) { - high.setY(v.getY()); - } - if (v.getZ() < low.getZ()) { - low.setZ(v.getZ()); - } - if (v.getZ() > high.getZ()) { - high.setZ(v.getZ()); - } + if (v.getX() > high.getX()) { + high.setX(v.getX()); + } + if (v.getY() < low.getY()) { + low.setY(v.getY()); + } + if (v.getY() > high.getY()) { + high.setY(v.getY()); + } + if (v.getZ() < low.getZ()) { + low.setZ(v.getZ()); + } + if (v.getZ() > high.getZ()) { + high.setZ(v.getZ()); } } } -- GitLab From 6aa2677f844410ecc253720a243ecf2673714cdb Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 15 Jul 2025 16:12:14 +0200 Subject: [PATCH 015/118] Fix: Parsing of RelativeGeometry --- .../datastructure/ImplicitGeometryHolder.java | 42 ++++++++++++++++++- .../datastructure/RelativeGeometry.java | 14 ++++--- .../citygml3/Citygml3FeatureMapper.java | 38 +++++++++++------ .../citydoctor2/parser/CityGmlParser.java | 3 ++ 4 files changed, 79 insertions(+), 18 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java index 872725ee..6a01fa84 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java @@ -1,8 +1,13 @@ package de.hft.stuttgart.citydoctor2.datastructure; import java.io.Serial; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.core.ImplicitGeometry; import de.hft.stuttgart.citydoctor2.math.TransformationMatrix; @@ -16,9 +21,11 @@ public class ImplicitGeometryHolder extends Geometry { @Serial private static final long serialVersionUID = -8938931081577196349L; + private static final Logger logger = LogManager.getLogger(ImplicitGeometryHolder.class); - private ImplicitGeometry cgmlImplicitGeometry = null; + private ImplicitGeometry cgmlImplicitGeometry = null; + private static ConcurrentHashMap deferredRelGeomObjects = new ConcurrentHashMap<>(); private final PrototypeGeometryType type; private final Geometry prototypeGeometry; @@ -38,7 +45,40 @@ public class ImplicitGeometryHolder extends Geometry { applyTransformation(); } + public static ImplicitGeometryHolder deferRelativeGeomParsing(ImplicitGeometry ig, Lod lod) { + ImplicitGeometryHolder igh = new ImplicitGeometryHolder(lod); + deferredRelGeomObjects.put(igh, ig); + return igh; + } + + public static void resolveDeferredObjects() { + Set missingGeoms = new HashSet<>(); + for (ImplicitGeometryHolder key : deferredRelGeomObjects.keySet()) { + ImplicitGeometry ig = deferredRelGeomObjects.get(key); + String hrefId = ig.getRelativeGeometry().getHref(); + hrefId = hrefId.startsWith("#") ? hrefId.substring(1) : hrefId; + GmlId gmlId = new GmlId(hrefId); + RelativeGeometry relGeom = RelativeGeometry.of(gmlId); + if (relGeom != null) { + ImplicitGeometryHolder igh = new ImplicitGeometryHolder(ig, relGeom); + CityObject parent = key.getParent(); + parent.removeGeometry(igh.getLod(), igh.getType()); + parent.addGeometry(igh); + } else { + missingGeoms.add(gmlId); + } + } + if (!missingGeoms.isEmpty()) { + logger.warn("RelativeGeometries of the following gmlIDs could not be resolved: {}", missingGeoms); + } + deferredRelGeomObjects.clear(); + } + private ImplicitGeometryHolder(Lod lod) { + super(null, lod, null); + type = null; + prototypeGeometry = new Geometry(null, lod, null); + } /** * Applies the transformation matrix of the implicit geometry to a copy of the reference geometry and copies the resulting * transformed geometry into this object. diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java index bbf61550..48f8ccd4 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java @@ -14,19 +14,23 @@ public class RelativeGeometry extends Geometry { @Serial private static final long serialVersionUID = -686112245455298977L; - private static Map relativeGeometries = new ConcurrentHashMap<>(); + private static final Map relativeGeometries = new ConcurrentHashMap<>(); - public static RelativeGeometry of(Geometry geom) { - if (relativeGeometries.containsKey(geom)) { - return relativeGeometries.get(geom); + public static RelativeGeometry of(GmlId gmlId, Geometry geom) { + if (relativeGeometries.containsKey(gmlId)) { + return relativeGeometries.get(gmlId); } RelativeGeometry relGeo = new RelativeGeometry(geom.getType(), geom.getLod(), geom.getOrientation()); geom.getPolygons().forEach(relGeo::addPolygon); relGeo.updateEdgesAndVertices(); - relativeGeometries.put(geom, relGeo); + relativeGeometries.put(gmlId, relGeo); return relGeo; } + public static RelativeGeometry of(GmlId gmlId) { + return relativeGeometries.getOrDefault(gmlId, null); + } + private RelativeGeometry(GeometryType type, Lod lod, Orientation orientation) { super(type, lod, orientation); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index e5f5c543..ade1f2bb 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -1035,19 +1035,33 @@ public class Citygml3FeatureMapper extends ObjectWalker { } } else if (ig.getRelativeGeometry() != null) { AbstractGeometry aGeom = ig.getRelativeGeometry().getObject(); - Geometry geom = null; Lod lod = Lod.values()[lodInt]; - RelativeGeometry relGeo = null; - if (aGeom instanceof MultiSurface ms) { - geom = parseMultiSurface(ms, lod); - } else if (aGeom instanceof Solid s) { - geom = parseSolid(s, lod, orientation); - } else if (aGeom instanceof CompositeSurface cs) { - geom = parseCompositeSurface(cs, lod); - } - if (geom != null) { - relGeo = RelativeGeometry.of(geom); - igh = new ImplicitGeometryHolder(ig, relGeo); + if (aGeom != null) { + Geometry geom = null; + if (aGeom instanceof MultiSurface ms) { + geom = parseMultiSurface(ms, lod); + } else if (aGeom instanceof Solid s) { + geom = parseSolid(s, lod, orientation); + } else if (aGeom instanceof CompositeSurface cs) { + geom = parseCompositeSurface(cs, lod); + } + if (geom != null) { + geom.setGmlId(new GmlId(aGeom.getId())); + RelativeGeometry relGeo = RelativeGeometry.of(geom.getGmlId(), geom); + igh = new ImplicitGeometryHolder(ig, relGeo); + } + } else if (ig.getRelativeGeometry().getHref() != null) { + String hrefId = ig.getRelativeGeometry().getHref(); + hrefId = hrefId.startsWith("#") ? hrefId.substring(1) : hrefId; + GmlId gmlId = new GmlId(hrefId); + RelativeGeometry relGeo = RelativeGeometry.of(gmlId); + if (relGeo == null) { + igh = ImplicitGeometryHolder.deferRelativeGeomParsing(ig, lod); + } else { + igh = new ImplicitGeometryHolder(ig, relGeo); + } + } else { + logger.warn("Relative geometry of GML-ID {} has neither geometry nor an href", ig.getId()); } } else { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 06d42a00..e8ec04fe 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -44,6 +44,7 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -528,6 +529,8 @@ public class CityGmlParser { logger.info(Localization.getText("CityGmlParser.parsedObjects"), mapper.getModel().getNumberOfFeatures()); } + // After parsing is complete, try to resolve deferred RelativeGeometries + ImplicitGeometryHolder.resolveDeferredObjects(); mapper.setCityGMLVersion(version); return mapper.getModel(); } -- GitLab From 69907f4906664ae7d944d33c894c657e4810576b Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 16 Jul 2025 10:10:26 +0200 Subject: [PATCH 016/118] Refactor: Rework BBox calculation using Visitors --- .../datastructure/CityDoctorModel.java | 13 +++++ .../datastructure/GenericAttribute.java | 18 ++++++ .../utils/BoundingBoxCalculator.java | 21 ++----- .../utils/visitors/MinMaxExtentVisitor.java | 57 +++++++++++++++++++ 4 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/visitors/MinMaxExtentVisitor.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 41b2b003..0f475c44 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -20,6 +20,7 @@ package de.hft.stuttgart.citydoctor2.datastructure; import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.writer.CityGMLWriterUtils; @@ -121,6 +122,18 @@ public class CityDoctorModel { tunnels.stream(), water.stream(), cityfurniture.stream(), genericObjects.stream()).flatMap(co -> co); } + public void accept(CheckableVisitor visitor) { + buildings.forEach(co -> co.accept(visitor)); + vegetation.forEach(co -> co.accept(visitor)); + bridges.forEach(co -> co.accept(visitor)); + land.forEach(co -> co.accept(visitor)); + roads.forEach(co -> co.accept(visitor)); + tunnels.forEach(co -> co.accept(visitor)); + water.forEach(co -> co.accept(visitor)); + cityfurniture.forEach(co -> co.accept(visitor)); + genericObjects.forEach(co -> co.accept(visitor)); + } + public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException { if (file.endsWith(".off")) { exportAsOff(file); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java index 041c688b..cd8cec53 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java @@ -5,6 +5,8 @@ import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.core.AbstractGenericAttributeProperty; import org.citygml4j.core.model.generics.*; +import java.util.List; + public class GenericAttribute { @@ -45,6 +47,22 @@ public class GenericAttribute { } else if (attributeProperty.getObject() instanceof CodeAttribute ca) { type = String.format("CodeAttribute (%s)", ca.getValue().getLanguage()); value = String.format("'''%s''' %n CodeSpace: %s", ca.getValue().getValue(), ca.getValue().getCodeSpace()); + } else if (attributeProperty.getObject() instanceof GenericAttributeSet gas) { + type = "GenericAttributeSet"; + List attributes = gas.getValue(); + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (AbstractGenericAttributeProperty attribute : attributes) { + if (attribute.getObject() != null) { + sb.append(attribute.getObject().getName()); + sb.append(" = "); + sb.append(attribute.getObject().getValue()); + sb.append("; "); + } + } + sb.replace(sb.length() - 1, sb.length(), "}"); + value = String.format("'%s'", sb); + } else { logger.warn("GenericAttribute {} is of unknown type {}", attributeProperty, attributeProperty.getObject()); value = attributeProperty.getObject().getValue().toString(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java index 78acc1d0..80bace6d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java @@ -29,6 +29,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector; +import de.hft.stuttgart.citydoctor2.utils.visitors.MinMaxExtentVisitor; /** * Utility class for calculating axis aligned bounding boxes for different @@ -96,23 +97,11 @@ public class BoundingBoxCalculator { * @return the bounding box of the model */ public static BoundingBox calculateBoundingBox(CityDoctorModel model) { - Vector3d low = new Vector3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); - Vector3d high = new Vector3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); + // TODO: Rework to Visitor + MinMaxExtentVisitor mima = new MinMaxExtentVisitor(); + model.accept(mima); - findMinMax(low, high, model.getBuildings()); - findMinMax(low, high, model.getBridges()); - findMinMax(low, high, model.getLand()); - findMinMax(low, high, model.getTransportation()); - findMinMax(low, high, model.getWater()); - findMinMax(low, high, model.getVegetation()); - findMinMax(low, high, model.getTunnels()); - findMinMax(low, high, model.getCityFurniture()); - findMinMax(low, high, model.getGenericCityObjects()); - - Vector3d[] result = new Vector3d[2]; - result[0] = low; - result[1] = high; - return BoundingBox.of(result); + return BoundingBox.of(mima.getMinMaxExtent()); } public static BoundingBox calculateBoundingBoxFromPoints(List points) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/visitors/MinMaxExtentVisitor.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/visitors/MinMaxExtentVisitor.java new file mode 100644 index 00000000..8ba58ac0 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/visitors/MinMaxExtentVisitor.java @@ -0,0 +1,57 @@ +package de.hft.stuttgart.citydoctor2.utils.visitors; + +import de.hft.stuttgart.citydoctor2.check.Checkable; +import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.Geometry; +import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.math.Vector3d; + +public class MinMaxExtentVisitor extends CheckableUtilsVisitor { + + Vector3d low; + Vector3d high; + + public MinMaxExtentVisitor() { + low = new Vector3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + high = new Vector3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); + } + + public Vector3d[] getMinMaxExtent() { + Vector3d[] result = new Vector3d[2]; + result[0] = low; + result[1] = high; + return result; + } + + @Override + public void check(CityObject co) { + for (Geometry geom : co.getGeometries()) { + if (geom.getVertices() == null) { + geom.updateVertices(); + } + for (Vertex v : geom.getVertices()) { + if (v.getX() < low.getX()) { + low.setX(v.getX()); + } + if (v.getX() > high.getX()) { + high.setX(v.getX()); + } + if (v.getY() < low.getY()) { + low.setY(v.getY()); + } + if (v.getY() > high.getY()) { + high.setY(v.getY()); + } + if (v.getZ() < low.getZ()) { + low.setZ(v.getZ()); + } + if (v.getZ() > high.getZ()) { + high.setZ(v.getZ()); + } + } + } + } + +} -- GitLab From aa17c02e22ca7e3a1a6ccca5bfe71c8c70eb1509 Mon Sep 17 00:00:00 2001 From: Riegel Date: Fri, 18 Jul 2025 12:32:26 +0200 Subject: [PATCH 017/118] Refactor: Make GenericAttribute serializable --- .../citydoctor2/datastructure/GenericAttribute.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java index 041c688b..6fabd2b0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericAttribute.java @@ -5,8 +5,14 @@ import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.core.AbstractGenericAttributeProperty; import org.citygml4j.core.model.generics.*; +import java.io.Serial; +import java.io.Serializable; -public class GenericAttribute { + +public class GenericAttribute implements Serializable { + + @Serial + private static final long serialVersionUID = -4101408947632132328L; private static final Logger logger = LogManager.getLogger(GenericAttribute.class); -- GitLab From fecb165581483944c9022757113fe8556991196a Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Jul 2025 13:40:24 +0200 Subject: [PATCH 018/118] Refactor: Make fields transient --- .../hft/stuttgart/citydoctor2/datastructure/Geometry.java | 6 +++--- .../de/hft/stuttgart/citydoctor2/datastructure/Vertex.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java index d7c17240..205bed8d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java @@ -53,9 +53,9 @@ public class Geometry extends GmlElement { private CityObject parent; private final List polygons = new ArrayList<>(2); - private List edges; - private Map, Edge> edgeMap; - private List vertices; + private transient List edges; + private transient Map, Edge> edgeMap; + private transient List vertices; private Orientation orientation; public Geometry(GeometryType type, Lod lod, Orientation orientation) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Vertex.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Vertex.java index a49aec95..d8347233 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Vertex.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Vertex.java @@ -37,7 +37,7 @@ public class Vertex extends Vector3d { @Serial private static final long serialVersionUID = -5525361920397934892L; - private List>> adjacentRings = new ArrayList<>(2); + private transient List>> adjacentRings = new ArrayList<>(2); public Vertex(double x, double y, double z) { super(x, y, z); -- GitLab From 400435943e81e2febaed991ba6cad1fa77925210 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Jul 2025 13:41:23 +0200 Subject: [PATCH 019/118] Refactor: Add special deserializer for Vertex --- .../hft/stuttgart/citydoctor2/datastructure/Vertex.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Vertex.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Vertex.java index d8347233..e4244007 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Vertex.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Vertex.java @@ -21,6 +21,8 @@ package de.hft.stuttgart.citydoctor2.datastructure; import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.utils.SerializablePair; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serial; import java.util.ArrayList; import java.util.HashSet; @@ -91,6 +93,12 @@ public class Vertex extends Vector3d { throw new IllegalStateException("Requested adjacent polygons with Geometry not containing this vertex"); } + @Serial + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + clearAdjacentRings(); + } + public Set getAdjacentPolygons(Geometry geom) { return getAdjacentPolygonsWithoutNeighbor(geom); } -- GitLab From 1970e72edf57845c966c1b6ae342adbcf17567d1 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Jul 2025 14:02:25 +0200 Subject: [PATCH 020/118] Refactor: Add special deserializer for Geometry --- .../hft/stuttgart/citydoctor2/datastructure/Geometry.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java index 205bed8d..14c8d045 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java @@ -26,6 +26,8 @@ import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator; import de.hft.stuttgart.citydoctor2.utils.SerializablePair; import javafx.scene.paint.Color; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serial; import java.util.*; @@ -344,6 +346,11 @@ public class Geometry extends GmlElement { return false; } + @Serial + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + updateVertices(); + } public void clearMetaData() { if (vertices != null) { -- GitLab From 99385663f2fed69265282480ea928b47c8c91907 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Jul 2025 14:16:59 +0200 Subject: [PATCH 021/118] Refactor: Add BBox calculation for single CityObjects --- .../stuttgart/citydoctor2/database/DatabaseHandler.java | 2 +- .../stuttgart/citydoctor2/datastructure/BoundingBox.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 5255eea5..82336ae8 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -76,7 +76,7 @@ public class DatabaseHandler { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?, ?)")) { ps.setString(1, co.getGmlId().toString()); - String bbox = BoundingBox.of(co.getHighestLodGeometry().getPolygons()).to2DWkt(); + String bbox = BoundingBox.of(co).to2DWkt(); ps.setString(2, bbox); if (logger.isDebugEnabled()) { logger.debug("Attempting to serialize city object {} ", co); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java index 7a5c4c4f..a14f9797 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java @@ -20,6 +20,7 @@ package de.hft.stuttgart.citydoctor2.datastructure; import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator; +import de.hft.stuttgart.citydoctor2.utils.visitors.MinMaxExtentVisitor; import java.util.Collection; import java.util.List; @@ -47,6 +48,12 @@ public class BoundingBox { return BoundingBoxCalculator.calculateBoundingBoxFromPoints(points); } + public static BoundingBox of(CityObject co) { + MinMaxExtentVisitor mima = new MinMaxExtentVisitor(); + co.accept(mima); + return BoundingBox.of(mima.getMinMaxExtent()); + } + /** * Creates an axis aligned bounding box of the whole model. * -- GitLab From 2d3ef9cb6ce582f884cb3fe1c97d77c697e70ae2 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Jul 2025 14:23:13 +0200 Subject: [PATCH 022/118] Refactor: Use SerializationUtils for DatabaseHandler --- .../citydoctor2/database/DatabaseHandler.java | 28 ++++--------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 82336ae8..f49f9c98 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -4,17 +4,15 @@ import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.utils.Pair; +import org.apache.commons.lang3.SerializationUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.h2gis.functions.factory.H2GISDBFactory; import org.h2gis.utilities.wrapper.ConnectionWrapper; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -82,11 +80,7 @@ public class DatabaseHandler { logger.debug("Attempting to serialize city object {} ", co); } ps.setString(4, co.getClass().getName()); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - try (ObjectOutputStream out = new ObjectOutputStream(bos)) { - out.writeObject(co); - } - byte[] bytes = bos.toByteArray(); + byte[] bytes = SerializationUtils.serialize(co); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ps.setBinaryStream(3, bis, bytes.length); ps.executeUpdate(); @@ -95,9 +89,6 @@ public class DatabaseHandler { } catch (SQLException e) { logger.error("Saving of city object to embedded database failed!"); logger.debug(e.getMessage()); - } catch (IOException e) { - logger.error("Encountered unknown writing error for embedded database!"); - logger.debug(e.getMessage()); } } @@ -108,13 +99,11 @@ public class DatabaseHandler { ResultSet rs = ps.executeQuery(); if (rs.next()) { try (InputStream is = rs.getBinaryStream("data")) { - return deserializeCityObject(new ObjectInputStream(is), rs.getString("type")); + return SerializationUtils.deserialize(is); } } } catch (IOException e) { throw new RuntimeException(e); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); } } catch (SQLException e) { logger.error("Loading of city object from embedded database failed!"); @@ -197,23 +186,16 @@ public class DatabaseHandler { return count; } - private static List retrieveCityObjects(ResultSet rs) throws SQLException { + private static List retrieveCityObjects(ResultSet rs) throws SQLException { List cos = new ArrayList<>(); while (rs.next()) { try (InputStream is = rs.getBinaryStream("data")) { - cos.add(deserializeCityObject(new ObjectInputStream(is), rs.getString("type"))); + cos.add(SerializationUtils.deserialize(is)); } catch (IOException e) { throw new RuntimeException(e); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); } } return cos; } - private static CityObject deserializeCityObject(ObjectInputStream ois, String className) throws IOException, ClassNotFoundException { - Class type = Class.forName(className); - CityObject castCo = (CityObject) type.cast(ois.readObject()); - return castCo.getTopLevelCityObject(); - } } -- GitLab From df3605061031a457d6cac34116159ccc014fcce3 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Jul 2025 14:25:07 +0200 Subject: [PATCH 023/118] Test: Add Serialization testcases --- .../database/DataBaseHandlerTest.java | 57 +- .../database/SerializationTest.java | 59 + ...D2-Tex-Storey-CG30-V01-InteriorSurface.gml | 1655 +++++++++++++++++ 3 files changed, 1752 insertions(+), 19 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/SerializationTest.java create mode 100644 CityDoctorParent/CityDoctorModel/src/test/resources/04-FZK-Haus-LoD2-Tex-Storey-CG30-V01-InteriorSurface.gml diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index 962d95a8..7494161c 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -1,6 +1,5 @@ package de.hft.stuttgart.citydoctor2.database; -import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; @@ -8,8 +7,6 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; -import de.hft.stuttgart.citydoctor2.utils.Copy; -import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector; import org.h2gis.functions.factory.H2GISDBFactory; import org.junit.Before; import org.junit.BeforeClass; @@ -17,8 +14,8 @@ import org.junit.Test; import java.sql.Connection; import java.sql.SQLException; -import java.util.Set; +import static de.hft.stuttgart.citydoctor2.database.SerializationTest.assertCityObjectEquality; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -26,11 +23,8 @@ import static org.junit.Assert.fail; public class DataBaseHandlerTest { @BeforeClass - public static void setUpBeforeClass() throws Exception { + public static void setUpBeforeClass() { DatabaseHandler.setupDatabase(); - try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { - assertNotNull(con); - } } @Before @@ -43,13 +37,45 @@ public class DataBaseHandlerTest { } @Test - public void cityObjectMarshallingTest() throws InvalidGmlFileException, CityGmlParseException { + public void testSetup() throws SQLException { + try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { + assertNotNull(con); + } + } + + @Test + public void testMarshallingRoundTripSimpleSolid() throws InvalidGmlFileException, CityGmlParseException { + try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { + assertNotNull(con); + ParserConfiguration config = new ParserConfiguration(8, false); + CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS.gml", config); + + Building b = model.getBuildings().get(0); + assertNotNull(b); + assertEquals(0, DatabaseHandler.getFeatureCount()); + DatabaseHandler.marshallCityObject(b); + assertEquals(1, DatabaseHandler.getFeatureCount()); + + CityObject b2 = DatabaseHandler.unmarshallCityObject(b.getGmlId()); + assertNotNull(b2); + assertEquals(1, DatabaseHandler.getFeatureCount()); + + assertCityObjectEquality(b, b2); + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + + @Test + public void testMarshallingRoundTripFzk() throws InvalidGmlFileException, CityGmlParseException { try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { assertNotNull(con); ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/FZK_haus.gml", config); + Building b = model.getBuildings().get(0); - Copy.copy(b); assertNotNull(b); assertEquals(0, DatabaseHandler.getFeatureCount()); DatabaseHandler.marshallCityObject(b); @@ -58,19 +84,12 @@ public class DataBaseHandlerTest { CityObject b2 = DatabaseHandler.unmarshallCityObject(b.getGmlId()); assertNotNull(b2); assertEquals(1, DatabaseHandler.getFeatureCount()); - assertEquals(b.getGmlId(), b2.getGmlId()); - assertEquals(b.getFeatureType(), b2.getFeatureType()); - CityObjectCollector collector = new CityObjectCollector(); - b.accept(collector); - Set coB = collector.getCityObjects(); - collector = new CityObjectCollector(); - b2.accept(collector); - Set coB2 = collector.getCityObjects(); - assertEquals(coB.size(), coB2.size()); + assertCityObjectEquality(b, b2); } catch (SQLException e) { throw new RuntimeException(e); } } + } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/SerializationTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/SerializationTest.java new file mode 100644 index 00000000..d6f18376 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/SerializationTest.java @@ -0,0 +1,59 @@ +package de.hft.stuttgart.citydoctor2.database; + +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; +import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; +import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector; +import org.apache.commons.lang3.SerializationUtils; +import org.junit.Test; + +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class SerializationTest { + + static void assertCityObjectEquality(CityObject c1, CityObject c2) { + assertEquals(c1.getGmlId(), c2.getGmlId()); + assertEquals(c1.getFeatureType(), c2.getFeatureType()); + assertEquals(c1.getGmlObject().getId(), c2.getGmlObject().getId()); + + CityObjectCollector collector = new CityObjectCollector(); + c1.accept(collector); + Set coB = collector.getCityObjects(); + collector = new CityObjectCollector(); + c2.accept(collector); + Set coB2 = collector.getCityObjects(); + assertEquals(coB.size(), coB2.size()); + } + + @Test + public void testRoundtripSimpleSolid() throws CityGmlParseException, InvalidGmlFileException { + ParserConfiguration config = new ParserConfiguration(8, false); + CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS.gml", config); + Building b = model.getBuildings().get(0); + assertNotNull(b); + Building b2 = SerializationUtils.roundtrip(b); + assertNotNull(b2); + + assertCityObjectEquality(b, b2); + } + + + @Test + public void testRoundtripFzk() throws CityGmlParseException, InvalidGmlFileException { + ParserConfiguration config = new ParserConfiguration(8, false); + CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/FZK_haus.gml", config); + Building b = model.getBuildings().get(0); + assertNotNull(b); + Building b2 = SerializationUtils.roundtrip(b); + + assertCityObjectEquality(b, b2); + } + +} diff --git a/CityDoctorParent/CityDoctorModel/src/test/resources/04-FZK-Haus-LoD2-Tex-Storey-CG30-V01-InteriorSurface.gml b/CityDoctorParent/CityDoctorModel/src/test/resources/04-FZK-Haus-LoD2-Tex-Storey-CG30-V01-InteriorSurface.gml new file mode 100644 index 00000000..da8303f7 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/test/resources/04-FZK-Haus-LoD2-Tex-Storey-CG30-V01-InteriorSurface.gml @@ -0,0 +1,1655 @@ + + + + + + + + + FZK-Haus + + + 458900.0 5438785.0 113.5 + 458912.0 5438795.0 120.017691 + + + + + KIT Test Building FZK-Haus adapted for CityGML 3.0 + KIT Test Building FZK-Haus for CityGML 3.0 + + + @cityeditor:layer + LoD2-Ausgeblendet + + + + + + + + + + + + + + + + + + + + + + projected + + + highestRoofEdge + lowestGroundPoint + measured + 6.52 + + + + + 2 + + + + + Ground Floor + + + Ground + + + + + + + 458912.0 5438785.0 + 113.5 458900.0 5438785.0 113.5 458900.0 5438795.0 + 113.5 458912.0 5438795.0 113.5 458912.0 5438785.0 + 113.5 + + + + + + + + + + + Ground Floor / Wall East + + + + + + + 458912.0 5438795.0 + 113.5 458912.0 5438795.0 116.4 458912.0 5438785.0 + 116.4 458912.0 5438785.0 113.5 458912.0 5438795.0 + 113.5 + + + + + + + + + + + Ground Floor / Wall South + + + + + + + 458912.0 5438785.0 + 113.5 458912.0 5438785.0 116.4 458900.0 5438785.0 + 116.4 458900.0 5438785.0 113.5 458912.0 5438785.0 + 113.5 + + + + + + + + + + + Ground Floor / Wall West + + + + + + + 458900.0 5438785.0 + 113.5 458900.0 5438785.0 116.4 458900.0 5438795.0 + 116.4 458900.0 5438795.0 113.5 458900.0 5438785.0 + 113.5 + + + + + + + + + + + Ground Floor / Wall North + + + + + + + 458900.0 5438795.0 + 113.5 458900.0 5438795.0 116.4 458912.0 5438795.0 + 116.4 458912.0 5438795.0 113.5 458900.0 5438795.0 + 113.5 + + + + + + + + + + + 02 Ground Floor - Kitchen + + + Ground Floor / Kitchen / Closure Surface North + + + + + + + 458904.695 + 5438789.01 116.2 458903.8 5438789.01 116.2 + 458903.8 5438789.01 113.7 458904.695 5438789.01 + 113.7 458904.695 5438789.01 116.2 + + + + + + + + + + + Ground Floor / Kitchen / Closure Surface East + + + + + + + 458904.695 + 5438789.01 116.2 458904.695 5438789.01 113.7 + 458904.695 5438785.3 113.7 458904.695 5438785.3 + 116.2 458904.695 5438789.01 116.2 + + + + + + + + + + + Ground Floor / Kitchen / Ceiling + + + + + + + 458900.3 5438785.3 + 116.2 458900.3 5438789.01 116.2 458903.8 + 5438789.01 116.2 458904.695 5438789.01 116.2 + 458904.695 5438785.3 116.2 458900.3 5438785.3 + 116.2 + + + + + + + + + + + Ground Floor / Kitchen / Interior Wall West + + + + + + + 458900.3 5438785.3 + 116.2 458900.3 5438785.3 113.7 458900.3 5438789.01 + 113.7 458900.3 5438789.01 116.2 458900.3 5438785.3 + 116.2 + + + + + + + + + + + Ground Floor / Kitchen / Interior Wall South + + + + + + + 458904.695 5438785.3 + 116.2 458904.695 5438785.3 113.7 458900.3 + 5438785.3 113.7 458900.3 5438785.3 116.2 + 458904.695 5438785.3 116.2 + + + + + + + + + + + Ground Floor / Kitchen / Interior Wall North + + + + + + + 458900.3 5438789.01 + 113.7 458903.8 5438789.01 113.7 458903.8 + 5438789.01 116.2 458900.3 5438789.01 116.2 + 458900.3 5438789.01 113.7 + + + + + + + + + + + Ground Floor / Kitchen / Floor + + + + + + + 458903.8 5438789.01 + 113.7 458900.3 5438789.01 113.7 458900.3 5438785.3 + 113.7 458904.695 5438785.3 113.7 458904.695 + 5438789.01 113.7 458903.8 5438789.01 + 113.7 + + + + + + + + + + + + + 03 Ground Floor - Corridor + + + Ground Floor / Corridor / Closure Surface South + + + + + + + 458907.41 5438789.01 + 113.7 458904.695 5438789.01 113.7 458904.695 + 5438789.01 116.2 458907.41 5438789.01 116.2 + 458907.41 5438789.01 113.7 + + + + + + + + + + + Ground Floor / Corridor / Closure Surface South + + + + + + + 458903.8 5438789.01 + 116.2 458904.695 5438789.01 116.2 458904.695 + 5438789.01 113.7 458903.8 5438789.01 113.7 + 458903.8 5438789.01 116.2 + + + + + + + + + + + Ground Floor / Corridor / Ceiling + + + + + + + 458903.8 5438789.25 + 116.2 458900.3 5438789.25 116.2 458900.3 + 5438790.75 116.2 458907.41 5438790.75 116.2 + 458907.41 5438789.01 116.2 458904.695 5438789.01 + 116.2 458903.8 5438789.01 116.2 458903.8 + 5438789.25 116.2 + + + + + + + + + + + Ground Floor / Corridor / Wall East + + + + + + + 458907.41 5438790.75 + 116.2 458907.41 5438790.75 113.7 458907.41 + 5438789.01 113.7 458907.41 5438789.01 116.2 + 458907.41 5438790.75 116.2 + + + + + + + + + + + Ground Floor / Corridor / Wall North + + + + + + + 458900.3 5438790.75 + 113.7 458907.41 5438790.75 113.7 458907.41 + 5438790.75 116.2 458900.3 5438790.75 116.2 + 458900.3 5438790.75 113.7 + + + + + + + + + + + Ground Floor / Corridor / Wall West I + + + + + + + 458900.3 5438789.25 + 113.7 458900.3 5438790.75 113.7 458900.3 + 5438790.75 116.2 458900.3 5438789.25 116.2 + 458900.3 5438789.25 113.7 + + + + + + + + + + + Ground Floor / Corridor / Wall South + + + + + + + 458903.8 5438789.25 + 113.7 458900.3 5438789.25 113.7 458900.3 + 5438789.25 116.2 458903.8 5438789.25 116.2 + 458903.8 5438789.25 113.7 + + + + + + + + + + + Ground Floor / Corridor / Wall West II + + + + + + + 458903.8 5438789.01 + 113.7 458903.8 5438789.25 113.7 458903.8 + 5438789.25 116.2 458903.8 5438789.01 116.2 + 458903.8 5438789.01 113.7 + + + + + + + + + + + Ground Floor / Corridor / Floor + + + + + + + 458903.8 5438789.01 + 113.7 458904.695 5438789.01 113.7 458907.41 + 5438789.01 113.7 458907.41 5438790.75 113.7 + 458900.3 5438790.75 113.7 458900.3 5438789.25 + 113.7 458903.8 5438789.25 113.7 458903.8 + 5438789.01 113.7 + + + + + + + + + + + + + 04 Ground Floor - Living Room + + + Ground Floor / Living Room / Closure Surface West + + + + + + + 458904.695 5438785.3 + 116.2 458904.695 5438785.3 113.7 458904.695 + 5438789.01 113.7 458904.695 5438789.01 116.2 + 458904.695 5438785.3 116.2 + + + + + + + + + + + Ground Floor / Living Room / Closure Surface North + + + + + + + 458904.695 + 5438789.01 113.7 458907.41 5438789.01 113.7 + 458907.41 5438789.01 116.2 458904.695 5438789.01 + 116.2 458904.695 5438789.01 113.7 + + + + + + + + + + + Ground Floor / Living Room / Ceiling + + + + + + + 458904.695 + 5438789.01 116.2 458907.41 5438789.01 116.2 + 458911.7 5438789.01 116.2 458911.7 5438785.3 116.2 + 458904.695 5438785.3 116.2 458904.695 5438789.01 + 116.2 + + + + + + + + + + + Ground Floor / Living Room / Interior Wall North + + + + + + + 458911.7 5438789.01 + 116.2 458907.41 5438789.01 116.2 458907.41 + 5438789.01 113.7 458911.7 5438789.01 113.7 + 458911.7 5438789.01 116.2 + + + + + + + + + + + Ground Floor / Living Room / Interior Wall South + + + + + + + 458911.7 5438785.3 + 116.2 458911.7 5438785.3 113.7 458904.695 + 5438785.3 113.7 458904.695 5438785.3 116.2 + 458911.7 5438785.3 116.2 + + + + + + + + + + + Ground Floor / Living Room / Interior Wall East + + + + + + + 458911.7 5438789.01 + 116.2 458911.7 5438789.01 113.7 458911.7 5438785.3 + 113.7 458911.7 5438785.3 116.2 458911.7 5438789.01 + 116.2 + + + + + + + + + + + Ground Floor / Living Room / Floor + + + + + + + 458904.695 5438785.3 + 113.7 458911.7 5438785.3 113.7 458911.7 5438789.01 + 113.7 458907.41 5438789.01 113.7 458904.695 + 5438789.01 113.7 458904.695 5438785.3 + 113.7 + + + + + + + + + + + + + 05 Ground Floor - Bedroom + + + Ground Floor / Bedroom / Ceiling + + + + + + + 458911.7 5438789.25 + 116.2 458907.65 5438789.25 116.2 458907.65 + 5438794.7 116.2 458911.7 5438794.7 116.2 458911.7 + 5438789.25 116.2 + + + + + + + + + + + Ground Floor / Bedroom / Interior Wall West + + + + + + + 458907.65 5438789.25 + 116.2 458907.65 5438789.25 113.7 458907.65 + 5438794.7 113.7 458907.65 5438794.7 116.2 + 458907.65 5438789.25 116.2 + + + + + + + + + + + Ground Floor / Bedroom / Interior Wall South + + + + + + + 458911.7 5438789.25 + 116.2 458911.7 5438789.25 113.7 458907.65 + 5438789.25 113.7 458907.65 5438789.25 116.2 + 458911.7 5438789.25 116.2 + + + + + + + + + + + Ground Floor / Bedroom / Interior Wall North + + + + + + + 458907.65 5438794.7 + 116.2 458907.65 5438794.7 113.7 458911.7 5438794.7 + 113.7 458911.7 5438794.7 116.2 458907.65 5438794.7 + 116.2 + + + + + + + + + + + Ground Floor / Bedroom / Interior Wall East + + + + + + + 458911.7 5438794.7 + 116.2 458911.7 5438794.7 113.7 458911.7 5438789.25 + 113.7 458911.7 5438789.25 116.2 458911.7 5438794.7 + 116.2 + + + + + + + + + + + Ground Floor / Bedroom / Floor + + + + + + + 458911.7 5438789.25 + 113.7 458911.7 5438794.7 113.7 458907.65 5438794.7 + 113.7 458907.65 5438789.25 113.7 458911.7 + 5438789.25 113.7 + + + + + + + + + + + + + 06 Ground Floor - Bathroom + + + Ground Floor / Bathroom / Ceiling + + + + + + + 458907.41 5438790.99 + 116.2 458904.04 5438790.99 116.2 458904.04 + 5438794.7 116.2 458907.41 5438794.7 116.2 + 458907.41 5438790.99 116.2 + + + + + + + + + + + Ground Floor / Bathroom / Interior Wall West + + + + + + + 458904.04 5438790.99 + 116.2 458904.04 5438790.99 113.7 458904.04 + 5438794.7 113.7 458904.04 5438794.7 116.2 + 458904.04 5438790.99 116.2 + + + + + + + + + + + Ground Floor / Bathroom / Interior Wall South + + + + + + + 458907.41 5438790.99 + 116.2 458907.41 5438790.99 113.7 458904.04 + 5438790.99 113.7 458904.04 5438790.99 116.2 + 458907.41 5438790.99 116.2 + + + + + + + + + + + Ground Floor / Bathroom / Interior Wall North + + + + + + + 458904.04 5438794.7 + 116.2 458904.04 5438794.7 113.7 458907.41 + 5438794.7 113.7 458907.41 5438794.7 116.2 + 458904.04 5438794.7 116.2 + + + + + + + + + + + Ground Floor / Bathroom / Interior Wall East + + + + + + + 458907.41 5438794.7 + 116.2 458907.41 5438794.7 113.7 458907.41 + 5438790.99 113.7 458907.41 5438790.99 116.2 + 458907.41 5438794.7 116.2 + + + + + + + + + + + Ground Floor / Bathroom / Floor + + + + + + + 458907.41 5438790.99 + 113.7 458907.41 5438794.7 113.7 458904.04 + 5438794.7 113.7 458904.04 5438790.99 113.7 + 458907.41 5438790.99 113.7 + + + + + + + + + + + + + 07 Ground Floor - Office + + + Ground Floor / Office / Ceiling + + + + + + + 458903.8 5438790.99 + 116.2 458900.3 5438790.99 116.2 458900.3 5438794.7 + 116.2 458903.8 5438794.7 116.2 458903.8 5438790.99 + 116.2 + + + + + + + + + + + Ground Floor / Office / Interior Wall West + + + + + + + 458900.3 5438790.99 + 116.2 458900.3 5438790.99 113.7 458900.3 5438794.7 + 113.7 458900.3 5438794.7 116.2 458900.3 5438790.99 + 116.2 + + + + + + + + + + + Ground Floor / Office / Interior Wall South + + + + + + + 458903.8 5438790.99 + 116.2 458903.8 5438790.99 113.7 458900.3 + 5438790.99 113.7 458900.3 5438790.99 116.2 + 458903.8 5438790.99 116.2 + + + + + + + + + + + Ground Floor / Office / Interior Wall North + + + + + + + 458900.3 5438794.7 + 116.2 458900.3 5438794.7 113.7 458903.8 5438794.7 + 113.7 458903.8 5438794.7 116.2 458900.3 5438794.7 + 116.2 + + + + + + + + + + + Ground Floor / Office / Interior Wall East + + + + + + + 458903.8 5438794.7 + 116.2 458903.8 5438794.7 113.7 458903.8 5438790.99 + 113.7 458903.8 5438790.99 116.2 458903.8 5438794.7 + 116.2 + + + + + + + + + + + Ground Floor / Office / Floor + + + + + + + 458903.8 5438790.99 + 113.7 458903.8 5438794.7 113.7 458900.3 5438794.7 + 113.7 458900.3 5438790.99 113.7 458903.8 + 5438790.99 113.7 + + + + + + + + + + + + + + + Second Floor + + + Second Floor / Roof South + + + + + + + 458912.0 5438790.0 + 120.017691 458900.0 5438790.0 120.017691 458900.0 + 5438785.0 117.13094 458912.0 5438785.0 117.13094 + 458912.0 5438790.0 120.017691 + + + + + + + + + + + Second Floor / Roof North + + + + + + + 458900.0 5438790.0 + 120.017691 458912.0 5438790.0 120.017691 458912.0 + 5438795.0 117.13094 458900.0 5438795.0 117.13094 + 458900.0 5438790.0 120.017691 + + + + + + + + + + + Second Floor / Wall South + + + + + + + 458900.0 5438785.0 + 117.13094 458900.0 5438785.0 116.4 458912.0 + 5438785.0 116.4 458912.0 5438785.0 117.13094 + 458900.0 5438785.0 117.13094 + + + + + + + + + + + Second Floor / Wall West + + + + + + + 458900.0 5438795.0 + 117.13094 458900.0 5438795.0 116.4 458900.0 + 5438785.0 116.4 458900.0 5438785.0 117.13094 + 458900.0 5438790.0 120.017691 458900.0 5438795.0 + 117.13094 + + + + + + + + + + + Second Floor / Wall East + + + + + + + 458912.0 5438785.0 + 117.13094 458912.0 5438785.0 116.4 458912.0 + 5438795.0 116.4 458912.0 5438795.0 117.13094 + 458912.0 5438790.0 120.017691 458912.0 5438785.0 + 117.13094 + + + + + + + + + + + Second Floor / Wall North + + + + + + + 458912.0 5438795.0 + 117.13094 458912.0 5438795.0 116.4 458900.0 + 5438795.0 116.4 458900.0 5438795.0 117.13094 + 458912.0 5438795.0 117.13094 + + + + + + + + + + + 01 Second Floor - Galery + + + Second Floor / Galery / Ceiling North + + + + + + + 458900.3 5438794.7 + 117.073205 458911.7 5438794.7 117.073205 458911.7 + 5438790.0 119.786751 458900.3 5438790.0 119.786751 + 458900.3 5438794.7 117.073205 + + + + + + + + + + + Second Floor / Galery / Ceiling South + + + + + + + 458900.3 5438785.3 + 117.073205 458900.3 5438790.0 119.786751 458911.7 + 5438790.0 119.786751 458911.7 5438785.3 117.073205 + 458900.3 5438785.3 117.073205 + + + + + + + + + + + Second Floor / Galery / Interior Wall North + + + + + + + 458911.7 5438794.7 + 116.4 458911.7 5438794.7 117.073205 458900.3 + 5438794.7 117.073205 458900.3 5438794.7 116.4 + 458911.7 5438794.7 116.4 + + + + + + + + + + + Second Floor / Galery / Interior Wall East + + + + + + + 458911.7 5438794.7 + 117.073205 458911.7 5438794.7 116.4 458911.7 + 5438785.3 116.4 458911.7 5438785.3 117.073205 + 458911.7 5438790.0 119.786751 458911.7 5438794.7 + 117.073205 + + + + + + + + + + + Second Floor / Galery / Interior Wall West + + + + + + + 458900.3 5438790.0 + 119.786751 458900.3 5438785.3 117.073205 458900.3 + 5438785.3 116.4 458900.3 5438794.7 116.4 458900.3 + 5438794.7 117.073205 458900.3 5438790.0 + 119.786751 + + + + + + + + + + + Second Floor / Galery / Interior Wall South + + + + + + + 458900.3 5438785.3 + 116.4 458900.3 5438785.3 117.073205 458911.7 + 5438785.3 117.073205 458911.7 5438785.3 116.4 + 458900.3 5438785.3 116.4 + + + + + + + + + + + Second Floor / Galery / Floor + + + + + + + 458911.7 5438785.3 + 116.4 458911.7 5438794.7 116.4 458900.3 5438794.7 + 116.4 458900.3 5438785.3 116.4 458911.7 5438785.3 + 116.4 + + + + + + + + + + + + + + + + + + + textures/FZK-Haus-Front.png + wrap + + + #KIT-Test-Building-01_PG.nfWbzHM5j2MqtisYAjgV + + + -6.01592849610218e-17 1.000011046207786 + -3.0079642480510886e-17 0.7987000468203478 + 1.0000000000000002 0.7987000468203478 1.0000000000000002 + 1.000011046207786 -6.01592849610218e-17 + 1.000011046207786 + #KIT-Test-Building-01_LR.l5vPQsQlvAxe0PfGrItK + + + + + + + #KIT-Test-Building-01_PG.8Oi6zRMGuUdvjDA3942O + + + 1.0000000000000002 + -4.454490602082857e-31 1.0000000000000002 0.7987000468203478 + -3.007964248051091e-17 0.7987000468203478 0.0 + -5.359579389737923e-47 1.0000000000000002 + -4.454490602082857e-31 + #KIT-Test-Building-01_LR.dQRaAbGZocTDsiXUS3Wo + + + + + + + + + textures/FZK-Haus-Right.png + wrap + + + #KIT-Test-Building-01_PG.ioJ6HX4PnhWvLSYhPA7h + + + 7.560328766851198e-17 + 0.5570891737385654 5.087501393878691e-16 0.444942234223729 + 1.0000000000000004 0.44494223422372897 1.0000000000000004 + 0.5570891737385655 0.49999999999999994 0.9999986887435715 + 7.560328766851198e-17 0.5570891737385654 + #KIT-Test-Building-01_LR.VpCnRrYqLGD4bFFZLmWC + + + + + + + #KIT-Test-Building-01_PG.WH3ZPoThoidUosBGLd28 + + + 1.0000000000000004 + -2.907494171674344e-16 1.0000000000000004 + 0.44494223422372897 5.087501393878691e-16 0.444942234223729 + 7.560328766851198e-17 -2.907494171674344e-16 + 1.0000000000000004 + -2.907494171674344e-16 + #KIT-Test-Building-01_LR.kb6UBRDjXdQhuZgyrrXF + + + + + + + + + textures/FZK-Haus-Left.png + wrap + + + #KIT-Test-Building-01_PG.VdnPNlkOAJHnTVZL5R37 + + + -1.0 0.5570891737385658 + -0.9999999999999996 0.44494223422372936 + 4.331468517193571e-16 0.44494223422372936 + 4.331468517193571e-16 0.5570891737385657 + -0.49999999999999944 0.9999986887435719 -1.0 + 0.5570891737385658 + #KIT-Test-Building-01_LR.CWPcEAowucw9pxGFPH3S + + + + + + + #KIT-Test-Building-01_PG.87LG6xx5gYYjprCeHCDY + + + 4.331468517193571e-16 + -1.1157709843049066e-47 4.331468517193571e-16 + 0.44494223422372936 -0.9999999999999996 0.44494223422372936 + -1.0 -8.368282382286802e-48 4.331468517193571e-16 + -1.1157709843049066e-47 + #KIT-Test-Building-01_LR.z42J72DCA0DG7fsjO1iJ + + + + + + + + + textures/Roofing_Shingles_GAF_Estates.jpg + wrap + + + #KIT-Test-Building-01_PG.as933cYedpVOQ8Wqm9j7 + + + 21.47458840372227 18.00558453022584 + 1.8506199769767277e-15 18.00558453022584 + -1.2918958832001822e-15 4.124469685250767 21.47458840372227 + 4.124469685250767 21.47458840372227 18.00558453022584 + #KIT-Test-Building-01_LR.FJ2G4jJFUVmWcdGG3gyw + + + + + + + #KIT-Test-Building-01_PG.CUXCVrNXAnCgCUjdXnY1 + + + -2.0055287249601352e-15 + -2.816087737236785 -21.47458840372227 -2.816087737236785 + -21.47458840372227 -16.697202582211872 + 1.8024740244805496e-15 -16.697202582211872 + -2.0055287249601352e-15 -2.816087737236785 + #KIT-Test-Building-01_LR.cYvvi9l6K4nl9jeHjt03 + + + + + + + + + textures/FZK-Haus-Back.png + wrap + + + #KIT-Test-Building-01_PG.cdHLpEJXgkf3Hq9x6PF7 + + + -1.0000000000000002 1.000011046207795 + -1.0000000000000002 0.7987000468203566 + 2.7071678232459823e-16 0.7987000468203567 + 3.007964248051091e-16 1.000011046207795 -1.0000000000000002 + 1.000011046207795 + #KIT-Test-Building-01_LR.ODQPptDGkQ9xxB6LArAA + + + + + + + #KIT-Test-Building-01_PG.Y4DuK1eWWjbSOQxjL75Z + + + 3.0079642480510913e-16 + -1.4416909618544142e-15 2.707167823245983e-16 + 0.7987000468203463 -1.0000000000000002 0.7987000468203463 + -1.0000000000000002 -1.4416909618544138e-15 + 3.0079642480510913e-16 -1.4416909618544142e-15 + #KIT-Test-Building-01_LR.CvfxHmYhr7DvEJeZnfKH + + + + + + + + + 0.2 + 0.4666666666666667 0.34509803921568627 + 0.21176470588235294 + 0.0 0.0 0.0 + 1.0 1.0 1.0 + 0.2 + 0.0 + false + #KIT-Test-Building-01_PG.B9OH2yr4U3AkPsKWbFy2 + + + + + -- GitLab From ec6dae8f4fa69452b8c8d102ea18fd60433b51f3 Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 24 Jul 2025 13:56:18 +0200 Subject: [PATCH 024/118] Fix: Fix error in BBox 2D-WKT projection --- .../hft/stuttgart/citydoctor2/datastructure/BoundingBox.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java index a14f9797..c8b87d8a 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java @@ -177,8 +177,8 @@ public class BoundingBox { public String to2DWkt() { double x1 = bbox[0].getX(); double y1 = bbox[0].getY(); - double x2 = x1 + bbox[1].getX(); - double y2 = y1 + bbox[1].getY(); + double x2 = bbox[1].getX(); + double y2 = bbox[1].getY(); double minX = Math.min(x1, x2); double minY = Math.min(y1, y2); -- GitLab From 9496d3ae86181a0e739942d034d25f272f20d52f Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 24 Jul 2025 13:57:15 +0200 Subject: [PATCH 025/118] Fix: Fix columname in prepared statements --- .../hft/stuttgart/citydoctor2/database/DatabaseHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index f49f9c98..bcbb1f7b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -131,7 +131,7 @@ public class DatabaseHandler { List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features AS tab1" + - " WHERE ST_INTERSECTS(ST_BUFFER(tab1.geom,?), (SELECT GEOM FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { + " WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { ps.setDouble(1, bufferSize); ps.setString(2, co.getGmlId().toString()); ps.setString(3, co.getGmlId().toString()); @@ -150,7 +150,7 @@ public class DatabaseHandler { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features AS tab1" + - " WHERE ST_INTERSECTS(tab1.geom, (SELECT GEOM FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { + " WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { ps.setString(1, co.getGmlId().toString()); ps.setString(2, co.getGmlId().toString()); ResultSet rs = ps.executeQuery(); -- GitLab From 31b9b2cb174e98d76206cd68378ccd61905b07ff Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 24 Jul 2025 13:58:24 +0200 Subject: [PATCH 026/118] Fix: Initialize transient fields after deserializing --- .../de/hft/stuttgart/citydoctor2/datastructure/Geometry.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java index 14c8d045..4f9d8685 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java @@ -349,6 +349,8 @@ public class Geometry extends GmlElement { @Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + edges = new ArrayList<>(); + edgeMap = new HashMap<>(); updateVertices(); } -- GitLab From e66869037444db61e53d7f5d4167a3ce6b3961c6 Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 24 Jul 2025 13:59:55 +0200 Subject: [PATCH 027/118] Test: Add cases for DatabaseHandler --- .../database/DataBaseHandlerTest.java | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index 7494161c..18e96368 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -3,17 +3,28 @@ package de.hft.stuttgart.citydoctor2.database; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; +import de.hft.stuttgart.citydoctor2.datastructure.Geometry; +import de.hft.stuttgart.citydoctor2.datastructure.GeometryTestUtils; +import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; +import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; +import de.hft.stuttgart.citydoctor2.datastructure.Lod; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import org.citygml4j.core.model.building.AbstractBuilding; +import org.citygml4j.core.visitor.ObjectVisitor; import org.h2gis.functions.factory.H2GISDBFactory; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.Mockito; import java.sql.Connection; import java.sql.SQLException; +import java.util.List; import static de.hft.stuttgart.citydoctor2.database.SerializationTest.assertCityObjectEquality; import static org.junit.Assert.assertEquals; @@ -43,6 +54,152 @@ public class DataBaseHandlerTest { } } + @Test + public void testGetAllFeatures() { + try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { + Building b = new Building(); + b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); + Building b2 = new Building(); + b2.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD2)); + Building b3 = new Building(); + b3.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD3)); + + DatabaseHandler.marshallCityObject(b); + DatabaseHandler.marshallCityObject(b2); + DatabaseHandler.marshallCityObject(b3); + assertEquals(3, DatabaseHandler.getFeatureCount()); + + List objects = DatabaseHandler.getAllCityObjects(); + assertEquals(3, objects.size()); + assertEquals(3, DatabaseHandler.getFeatureCount()); + + } catch (SQLException e) { + throw new RuntimeException(e); + } + + } + + @Test + public void testGetBboxIntersectingObjects() { + try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { + Building b = new Building(); + Building b2 = new Building(); + b2.setGmlObject(Mockito.mock(AbstractBuilding.class)); + Building b3 = new Building(); + + + Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon = new ConcretePolygon(); + geom.getPolygons().add(polygon); + polygon.setParent(geom); + LinearRing lr = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon.setExteriorRing(lr); + Vertex v1 = new Vertex(10, 10, 8); + lr.getVertices().add(v1); + Vertex v2 = new Vertex(14, 15, 7); + lr.getVertices().add(v2); + Vertex v3 = new Vertex(14, 15, 3); + lr.getVertices().add(v3); + Vertex v4 = new Vertex(10, 10, 3); + lr.getVertices().add(v4); + lr.getVertices().add(v1); + geom.updateEdgesAndVertices(); + b.addGeometry(geom); + + + Geometry geom2 = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon2 = new ConcretePolygon(); + geom2.getPolygons().add(polygon2); + polygon2.setParent(geom2); + LinearRing lr2 = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon2.setExteriorRing(lr2); + Vertex v1b2 = new Vertex(6, 6, 8); + lr2.getVertices().add(v1b2); + Vertex v2b2 = new Vertex(11, 13, 7); + lr2.getVertices().add(v2b2); + Vertex v3b2 = new Vertex(11, 13, 3); + lr2.getVertices().add(v3b2); + Vertex v4b2 = new Vertex(6, 6, 3); + lr2.getVertices().add(v4b2); + lr2.getVertices().add(v1b2); + geom2.updateEdgesAndVertices(); + b2.addGeometry(geom2); + + Geometry geom3 = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon3 = new ConcretePolygon(); + geom3.getPolygons().add(polygon3); + polygon3.setParent(geom3); + LinearRing lr3 = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon3.setExteriorRing(lr3); + Vertex v1b3 = new Vertex(6, 6, 8); + lr3.getVertices().add(v1b3); + Vertex v2b3 = new Vertex(8, 9, 7); + lr3.getVertices().add(v2b3); + Vertex v3b3 = new Vertex(8, 9, 3); + lr3.getVertices().add(v3b3); + Vertex v4b3 = new Vertex(6, 6, 3); + lr3.getVertices().add(v4b3); + lr3.getVertices().add(v1b3); + geom3.updateEdgesAndVertices(); + b3.addGeometry(geom3); + + DatabaseHandler.marshallCityObject(b); + DatabaseHandler.marshallCityObject(b2); + DatabaseHandler.marshallCityObject(b3); + assertEquals(3, DatabaseHandler.getFeatureCount()); + + List objects = DatabaseHandler.getBBoxIntersectingObjects(b); + assertEquals(1, objects.size()); + assertEquals(3, DatabaseHandler.getFeatureCount()); + + assertCityObjectEquality(b2, objects.get(0)); + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testGetBboxBufferIntersectingObjects() { + try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { + Building b = new Building(); + b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); + Building b2 = new Building(); + b2.setGmlObject(Mockito.mock(AbstractBuilding.class)); + + Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon = new ConcretePolygon(); + geom.getPolygons().add(polygon); + polygon.setParent(geom); + LinearRing lr = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon.setExteriorRing(lr); + Vertex v1 = new Vertex(427583.300, 6003501.572, 9.911); + lr.getVertices().add(v1); + Vertex v2 = new Vertex(427583.296, 6003502.570, 9.913); + lr.getVertices().add(v2); + Vertex v3 = new Vertex(427583.296, 6003502.570, 4.297); + lr.getVertices().add(v3); + Vertex v4 = new Vertex(427583.300, 6003502.572, 4.297); + lr.getVertices().add(v4); + lr.getVertices().add(v1); + geom.updateEdgesAndVertices(); + b2.addGeometry(geom); + + DatabaseHandler.marshallCityObject(b); + DatabaseHandler.marshallCityObject(b2); + assertEquals(2, DatabaseHandler.getFeatureCount()); + + List objects = DatabaseHandler.getBufferIntersectingObjects(b, 0.005); + assertEquals(1, objects.size()); + assertEquals(2, DatabaseHandler.getFeatureCount()); + + assertCityObjectEquality(b2, objects.get(0)); + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + @Test public void testMarshallingRoundTripSimpleSolid() throws InvalidGmlFileException, CityGmlParseException { try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { -- GitLab From b1b1c84f26dd0a6d53cf9e8da6a998c14417c898 Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 24 Jul 2025 14:46:34 +0200 Subject: [PATCH 028/118] Style: Format code --- .../database/DataBaseHandlerTest.java | 291 ++++++++++-------- 1 file changed, 161 insertions(+), 130 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index 18e96368..c12e9995 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -15,7 +15,6 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import org.citygml4j.core.model.building.AbstractBuilding; -import org.citygml4j.core.visitor.ObjectVisitor; import org.h2gis.functions.factory.H2GISDBFactory; import org.junit.Before; import org.junit.BeforeClass; @@ -39,7 +38,7 @@ public class DataBaseHandlerTest { } @Before - public void prepareDatabase() { + public void resetDatabase() { try { DatabaseHandler.setupFeaturesTable(); } catch (Exception e) { @@ -56,148 +55,180 @@ public class DataBaseHandlerTest { @Test public void testGetAllFeatures() { - try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { - Building b = new Building(); - b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); - Building b2 = new Building(); - b2.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD2)); - Building b3 = new Building(); - b3.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD3)); + Building b = new Building(); + b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); + Building b2 = new Building(); + b2.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD2)); + Building b3 = new Building(); + b3.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD3)); - DatabaseHandler.marshallCityObject(b); - DatabaseHandler.marshallCityObject(b2); - DatabaseHandler.marshallCityObject(b3); - assertEquals(3, DatabaseHandler.getFeatureCount()); + DatabaseHandler.marshallCityObject(b); + DatabaseHandler.marshallCityObject(b2); + DatabaseHandler.marshallCityObject(b3); + assertEquals(3, DatabaseHandler.getFeatureCount()); - List objects = DatabaseHandler.getAllCityObjects(); - assertEquals(3, objects.size()); - assertEquals(3, DatabaseHandler.getFeatureCount()); + List objects = DatabaseHandler.getAllCityObjects(); + assertEquals(3, objects.size()); + assertEquals(3, DatabaseHandler.getFeatureCount()); - } catch (SQLException e) { - throw new RuntimeException(e); - } } @Test public void testGetBboxIntersectingObjects() { - try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { - Building b = new Building(); - Building b2 = new Building(); - b2.setGmlObject(Mockito.mock(AbstractBuilding.class)); - Building b3 = new Building(); - - - Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); - ConcretePolygon polygon = new ConcretePolygon(); - geom.getPolygons().add(polygon); - polygon.setParent(geom); - LinearRing lr = new LinearRing(LinearRing.LinearRingType.EXTERIOR); - polygon.setExteriorRing(lr); - Vertex v1 = new Vertex(10, 10, 8); - lr.getVertices().add(v1); - Vertex v2 = new Vertex(14, 15, 7); - lr.getVertices().add(v2); - Vertex v3 = new Vertex(14, 15, 3); - lr.getVertices().add(v3); - Vertex v4 = new Vertex(10, 10, 3); - lr.getVertices().add(v4); - lr.getVertices().add(v1); - geom.updateEdgesAndVertices(); - b.addGeometry(geom); - - - Geometry geom2 = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); - ConcretePolygon polygon2 = new ConcretePolygon(); - geom2.getPolygons().add(polygon2); - polygon2.setParent(geom2); - LinearRing lr2 = new LinearRing(LinearRing.LinearRingType.EXTERIOR); - polygon2.setExteriorRing(lr2); - Vertex v1b2 = new Vertex(6, 6, 8); - lr2.getVertices().add(v1b2); - Vertex v2b2 = new Vertex(11, 13, 7); - lr2.getVertices().add(v2b2); - Vertex v3b2 = new Vertex(11, 13, 3); - lr2.getVertices().add(v3b2); - Vertex v4b2 = new Vertex(6, 6, 3); - lr2.getVertices().add(v4b2); - lr2.getVertices().add(v1b2); - geom2.updateEdgesAndVertices(); - b2.addGeometry(geom2); - - Geometry geom3 = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); - ConcretePolygon polygon3 = new ConcretePolygon(); - geom3.getPolygons().add(polygon3); - polygon3.setParent(geom3); - LinearRing lr3 = new LinearRing(LinearRing.LinearRingType.EXTERIOR); - polygon3.setExteriorRing(lr3); - Vertex v1b3 = new Vertex(6, 6, 8); - lr3.getVertices().add(v1b3); - Vertex v2b3 = new Vertex(8, 9, 7); - lr3.getVertices().add(v2b3); - Vertex v3b3 = new Vertex(8, 9, 3); - lr3.getVertices().add(v3b3); - Vertex v4b3 = new Vertex(6, 6, 3); - lr3.getVertices().add(v4b3); - lr3.getVertices().add(v1b3); - geom3.updateEdgesAndVertices(); - b3.addGeometry(geom3); + Building b = new Building(); + Building b2 = new Building(); + b2.setGmlObject(Mockito.mock(AbstractBuilding.class)); + Building b3 = new Building(); + Building b4 = new Building(); + b4.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); + + Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon = new ConcretePolygon(); + geom.getPolygons().add(polygon); + polygon.setParent(geom); + LinearRing lr = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon.setExteriorRing(lr); + Vertex v1 = new Vertex(10, 10, 8); + lr.getVertices().add(v1); + Vertex v2 = new Vertex(14, 15, 7); + lr.getVertices().add(v2); + Vertex v3 = new Vertex(14, 15, 3); + lr.getVertices().add(v3); + Vertex v4 = new Vertex(10, 10, 3); + lr.getVertices().add(v4); + lr.getVertices().add(v1); + geom.updateEdgesAndVertices(); + b.addGeometry(geom); + + + Geometry geom2 = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon2 = new ConcretePolygon(); + geom2.getPolygons().add(polygon2); + polygon2.setParent(geom2); + LinearRing lr2 = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon2.setExteriorRing(lr2); + Vertex v1b2 = new Vertex(6, 6, 8); + lr2.getVertices().add(v1b2); + Vertex v2b2 = new Vertex(11, 13, 7); + lr2.getVertices().add(v2b2); + Vertex v3b2 = new Vertex(11, 13, 3); + lr2.getVertices().add(v3b2); + Vertex v4b2 = new Vertex(6, 6, 3); + lr2.getVertices().add(v4b2); + lr2.getVertices().add(v1b2); + geom2.updateEdgesAndVertices(); + b2.addGeometry(geom2); + + Geometry geom3 = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon3 = new ConcretePolygon(); + geom3.getPolygons().add(polygon3); + polygon3.setParent(geom3); + LinearRing lr3 = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon3.setExteriorRing(lr3); + Vertex v1b3 = new Vertex(6, 6, 8); + lr3.getVertices().add(v1b3); + Vertex v2b3 = new Vertex(10, 9.999, 7); + lr3.getVertices().add(v2b3); + Vertex v3b3 = new Vertex(10, 9.999, 3); + lr3.getVertices().add(v3b3); + Vertex v4b3 = new Vertex(6, 6, 3); + lr3.getVertices().add(v4b3); + lr3.getVertices().add(v1b3); + geom3.updateEdgesAndVertices(); + b3.addGeometry(geom3); + + DatabaseHandler.marshallCityObject(b); + DatabaseHandler.marshallCityObject(b2); + DatabaseHandler.marshallCityObject(b3); + DatabaseHandler.marshallCityObject(b4); + assertEquals(4, DatabaseHandler.getFeatureCount()); + + List objects = DatabaseHandler.getBBoxIntersectingObjects(b); + assertEquals(1, objects.size()); + assertEquals(4, DatabaseHandler.getFeatureCount()); + + assertCityObjectEquality(b2, objects.get(0)); - DatabaseHandler.marshallCityObject(b); - DatabaseHandler.marshallCityObject(b2); - DatabaseHandler.marshallCityObject(b3); - assertEquals(3, DatabaseHandler.getFeatureCount()); - - List objects = DatabaseHandler.getBBoxIntersectingObjects(b); - assertEquals(1, objects.size()); - assertEquals(3, DatabaseHandler.getFeatureCount()); - - assertCityObjectEquality(b2, objects.get(0)); - - } catch (SQLException e) { - throw new RuntimeException(e); - } } @Test public void testGetBboxBufferIntersectingObjects() { - try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { - Building b = new Building(); - b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); - Building b2 = new Building(); - b2.setGmlObject(Mockito.mock(AbstractBuilding.class)); - - Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); - ConcretePolygon polygon = new ConcretePolygon(); - geom.getPolygons().add(polygon); - polygon.setParent(geom); - LinearRing lr = new LinearRing(LinearRing.LinearRingType.EXTERIOR); - polygon.setExteriorRing(lr); - Vertex v1 = new Vertex(427583.300, 6003501.572, 9.911); - lr.getVertices().add(v1); - Vertex v2 = new Vertex(427583.296, 6003502.570, 9.913); - lr.getVertices().add(v2); - Vertex v3 = new Vertex(427583.296, 6003502.570, 4.297); - lr.getVertices().add(v3); - Vertex v4 = new Vertex(427583.300, 6003502.572, 4.297); - lr.getVertices().add(v4); - lr.getVertices().add(v1); - geom.updateEdgesAndVertices(); - b2.addGeometry(geom); - - DatabaseHandler.marshallCityObject(b); - DatabaseHandler.marshallCityObject(b2); - assertEquals(2, DatabaseHandler.getFeatureCount()); + Building b = new Building(); + Building b2 = new Building(); + b2.setGmlObject(Mockito.mock(AbstractBuilding.class)); + Building b3 = new Building(); + Building b4 = new Building(); + b4.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); + + + Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon = new ConcretePolygon(); + geom.getPolygons().add(polygon); + polygon.setParent(geom); + LinearRing lr = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon.setExteriorRing(lr); + Vertex v1 = new Vertex(10, 10, 8); + lr.getVertices().add(v1); + Vertex v2 = new Vertex(14, 15, 7); + lr.getVertices().add(v2); + Vertex v3 = new Vertex(14, 15, 3); + lr.getVertices().add(v3); + Vertex v4 = new Vertex(10, 10, 3); + lr.getVertices().add(v4); + lr.getVertices().add(v1); + geom.updateEdgesAndVertices(); + b.addGeometry(geom); + + + Geometry geom2 = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon2 = new ConcretePolygon(); + geom2.getPolygons().add(polygon2); + polygon2.setParent(geom2); + LinearRing lr2 = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon2.setExteriorRing(lr2); + Vertex v1b2 = new Vertex(6, 6, 8); + lr2.getVertices().add(v1b2); + Vertex v2b2 = new Vertex(9, 9, 7); + lr2.getVertices().add(v2b2); + Vertex v3b2 = new Vertex(9, 9, 3); + lr2.getVertices().add(v3b2); + Vertex v4b2 = new Vertex(6, 6, 3); + lr2.getVertices().add(v4b2); + lr2.getVertices().add(v1b2); + geom2.updateEdgesAndVertices(); + b2.addGeometry(geom2); + + Geometry geom3 = new Geometry(GeometryType.SOLID, Lod.LOD0, Geometry.Orientation.OUTWARD); + ConcretePolygon polygon3 = new ConcretePolygon(); + geom3.getPolygons().add(polygon3); + polygon3.setParent(geom3); + LinearRing lr3 = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + polygon3.setExteriorRing(lr3); + Vertex v1b3 = new Vertex(4, 4, 8); + lr3.getVertices().add(v1b3); + Vertex v2b3 = new Vertex(8.999, 8.999, 7); + lr3.getVertices().add(v2b3); + Vertex v3b3 = new Vertex(8.999, 8.999, 3); + lr3.getVertices().add(v3b3); + Vertex v4b3 = new Vertex(4, 4, 3); + lr3.getVertices().add(v4b3); + lr3.getVertices().add(v1b3); + geom3.updateEdgesAndVertices(); + b3.addGeometry(geom3); + + DatabaseHandler.marshallCityObject(b); + DatabaseHandler.marshallCityObject(b2); + DatabaseHandler.marshallCityObject(b3); + DatabaseHandler.marshallCityObject(b4); + assertEquals(4, DatabaseHandler.getFeatureCount()); + + List objects = DatabaseHandler.getBufferIntersectingObjects(b, Math.sqrt(2)); + assertEquals(1, objects.size()); + assertEquals(4, DatabaseHandler.getFeatureCount()); + + assertCityObjectEquality(b2, objects.get(0)); - List objects = DatabaseHandler.getBufferIntersectingObjects(b, 0.005); - assertEquals(1, objects.size()); - assertEquals(2, DatabaseHandler.getFeatureCount()); - - assertCityObjectEquality(b2, objects.get(0)); - - } catch (SQLException e) { - throw new RuntimeException(e); - } } @Test -- GitLab From 786171a6972775062bd30c994cc4a6e447bf9f0b Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 24 Jul 2025 14:47:40 +0200 Subject: [PATCH 029/118] Style: Change logging of CityObject marshalling --- .../hft/stuttgart/citydoctor2/database/DatabaseHandler.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index bcbb1f7b..1d039d3d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -17,7 +17,6 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; - import java.util.ArrayList; import java.util.List; @@ -77,14 +76,13 @@ public class DatabaseHandler { String bbox = BoundingBox.of(co).to2DWkt(); ps.setString(2, bbox); if (logger.isDebugEnabled()) { - logger.debug("Attempting to serialize city object {} ", co); + logger.debug("Attempting to serialize CityObject: {} ", co); } ps.setString(4, co.getClass().getName()); byte[] bytes = SerializationUtils.serialize(co); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ps.setBinaryStream(3, bis, bytes.length); ps.executeUpdate(); - logger.debug("Successfully serialized CityObject to database"); } } catch (SQLException e) { logger.error("Saving of city object to embedded database failed!"); -- GitLab From fcc98badab2c73288454b68ce2ed6cb8d7c394d8 Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 24 Jul 2025 15:34:20 +0200 Subject: [PATCH 030/118] Refactor: Move method to Cache class --- .../citydoctor2/database/CityObjectCache.java | 13 +++++++ .../citydoctor2/database/DatabaseHandler.java | 39 +++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java index 9911632d..b73127d4 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -2,7 +2,9 @@ package de.hft.stuttgart.citydoctor2.database; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import de.hft.stuttgart.citydoctor2.utils.Pair; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.lang.ref.SoftReference; @@ -40,6 +42,17 @@ public class CityObjectCache { return cityObject; } + public static List>> getCollidingObjectsList() { + List>> collidingObjects = new ArrayList<>(); + for (GmlId id : DatabaseHandler.getAllGmlIds()) { + List collidingIds = DatabaseHandler.getBBoxIntersectingObjects(id); + if (collidingIds != null && !collidingIds.isEmpty()) { + collidingObjects.add(new Pair<>(id, collidingIds)); + } + } + return collidingObjects; + } + public void remove(GmlId id) { cache.remove(id); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 1d039d3d..a0ae29c7 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -125,6 +125,22 @@ public class DatabaseHandler { return cos; } + public static List getAllGmlIds() { + List cos = new ArrayList<>(); + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features")) { + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + cos.add(new GmlId(rs.getString("gmlid"))); + } + } + } catch (SQLException e) { + logger.error("Connection to database failed while trying to get all CityObjects!"); + logger.debug(e.getMessage()); + } + return cos; + } + public static List getBufferIntersectingObjects(CityObject co, double bufferSize) { List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { @@ -161,12 +177,27 @@ public class DatabaseHandler { return intersectingObjects; } - public static List>> getCollidingObjects() { - List>> collidingObjects = new ArrayList<>(); - // TODO: Implement query to return all Objects that have a collision and the list of whhich other objects collide - return collidingObjects; + public static List getBBoxIntersectingObjects(GmlId id) { + List intersectingObjects = new ArrayList<>(); + + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + + " WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { + ps.setString(1, id.toString()); + ps.setString(2, id.toString()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + intersectingObjects.add(new GmlId(rs.getString("gmlid"))); + } + } + } catch (SQLException e) { + logger.error("Connection to database failed while trying to get bbox intersecting objects!"); + logger.debug(e.getMessage()); + } + return intersectingObjects; } + public static int getFeatureCount() { int count = -1; -- GitLab From 4b335e3e50d7e25df3084a2e6153d71cc4d76e49 Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 29 Jul 2025 10:52:20 +0200 Subject: [PATCH 031/118] Build: Add caffeine caching library --- CityDoctorParent/CityDoctorModel/pom.xml | 5 ++++- CityDoctorParent/pom.xml | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/pom.xml b/CityDoctorParent/CityDoctorModel/pom.xml index 49334400..945fd739 100644 --- a/CityDoctorParent/CityDoctorModel/pom.xml +++ b/CityDoctorParent/CityDoctorModel/pom.xml @@ -76,7 +76,10 @@ org.orbisgis h2gis - + + com.github.ben-manes.caffeine + caffeine + diff --git a/CityDoctorParent/pom.xml b/CityDoctorParent/pom.xml index 5dacd999..7067d3e8 100644 --- a/CityDoctorParent/pom.xml +++ b/CityDoctorParent/pom.xml @@ -247,6 +247,11 @@ h2gis 2.2.3 + + com.github.ben-manes.caffeine + caffeine + 3.2.2 + -- GitLab From ee22bc29a8d3007d2e979dbb4a6951e46b04d454 Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 29 Jul 2025 11:10:08 +0200 Subject: [PATCH 032/118] Test: Fix Copy.Copy failure for LinkedPolygons --- .../citydoctor2/database/DatabaseHandler.java | 13 +++++++++++-- .../citydoctor2/datastructure/Geometry.java | 1 - .../citydoctor2/datastructure/BuildingTest.java | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index a0ae29c7..17b602e8 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -1,9 +1,10 @@ package de.hft.stuttgart.citydoctor2.database; +import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; -import de.hft.stuttgart.citydoctor2.utils.Pair; import org.apache.commons.lang3.SerializationUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -97,7 +98,15 @@ public class DatabaseHandler { ResultSet rs = ps.executeQuery(); if (rs.next()) { try (InputStream is = rs.getBinaryStream("data")) { - return SerializationUtils.deserialize(is); + CityObject co = SerializationUtils.deserialize(is); + // Rebuild the adjacency maps + co.accept(new CheckableUtilsVisitor() { + @Override + public void check(Geometry geom) { + geom.updateVertices(); + } + }); + return co; } } } catch (IOException e) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java index 4f9d8685..f2c0d419 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java @@ -351,7 +351,6 @@ public class Geometry extends GmlElement { in.defaultReadObject(); edges = new ArrayList<>(); edgeMap = new HashMap<>(); - updateVertices(); } public void clearMetaData() { diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingTest.java index a27eee43..72698175 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingTest.java @@ -75,7 +75,8 @@ public class BuildingTest { assertNotSame(b, copy); assertEquals(b.getGmlId(), copy.getGmlId()); - + + copy.prepareForChecking(); Geometry copyGeom = copy.getGeometries().get(0); assertNotSame(geom, copyGeom); -- GitLab From 3a45b8d9ed76efb55d1bea8fd5d7bba624749b07 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 29 Jul 2025 13:20:17 +0200 Subject: [PATCH 033/118] Refactor: Rework handling of DatabaseHandler returns --- .../citydoctor2/database/DatabaseHandler.java | 45 ++++++++++--------- .../database/DataBaseHandlerTest.java | 13 ++++-- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 17b602e8..924e94e2 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -70,6 +70,10 @@ public class DatabaseHandler { } } + /** + * Saves a CityObject to the embedded database, updating an existing entry if it exists. + * @param co the CityObject + */ public static void marshallCityObject(CityObject co) { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?, ?)")) { @@ -91,6 +95,11 @@ public class DatabaseHandler { } } + /** + * Retrieves a CityObject from the embedded database. + * @param id the gmlId of the requested Feature + * @return the Feature, or null if no entry was found + */ public static CityObject unmarshallCityObject(GmlId id) { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features WHERE gmlid = ?")) { @@ -150,16 +159,22 @@ public class DatabaseHandler { return cos; } - public static List getBufferIntersectingObjects(CityObject co, double bufferSize) { - List intersectingObjects = new ArrayList<>(); + public static List getBufferIntersectingObjects(CityObject co, double bufferSize) { + return getBufferIntersectingObjects(co.getGmlId(), bufferSize); + } + + public static List getBufferIntersectingObjects(GmlId gmlID, double bufferSize) { + List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features AS tab1" + + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + " WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { ps.setDouble(1, bufferSize); - ps.setString(2, co.getGmlId().toString()); - ps.setString(3, co.getGmlId().toString()); + ps.setString(2, gmlID.toString()); + ps.setString(3, gmlID.toString()); ResultSet rs = ps.executeQuery(); - intersectingObjects.addAll(retrieveCityObjects(rs)); + while (rs.next()) { + intersectingObjects.add(new GmlId(rs.getString("gmlid"))); + } } } catch (SQLException e) { logger.error("Connection to database failed while trying to get buffer intersecting objects!"); @@ -168,22 +183,8 @@ public class DatabaseHandler { return intersectingObjects; } - public static List getBBoxIntersectingObjects(CityObject co) { - List intersectingObjects = new ArrayList<>(); - - try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features AS tab1" + - " WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { - ps.setString(1, co.getGmlId().toString()); - ps.setString(2, co.getGmlId().toString()); - ResultSet rs = ps.executeQuery(); - intersectingObjects.addAll(retrieveCityObjects(rs)); - } - } catch (SQLException e) { - logger.error("Connection to database failed while trying to get bbox intersecting objects!"); - logger.debug(e.getMessage()); - } - return intersectingObjects; + public static List getBBoxIntersectingObjects(CityObject co) { + return getBBoxIntersectingObjects(co.getGmlId()); } public static List getBBoxIntersectingObjects(GmlId id) { diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index c12e9995..6b28f175 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -7,6 +7,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.GeometryTestUtils; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.Lod; import de.hft.stuttgart.citydoctor2.datastructure.Vertex; @@ -144,11 +145,13 @@ public class DataBaseHandlerTest { DatabaseHandler.marshallCityObject(b4); assertEquals(4, DatabaseHandler.getFeatureCount()); - List objects = DatabaseHandler.getBBoxIntersectingObjects(b); + List objects = DatabaseHandler.getBBoxIntersectingObjects(b); assertEquals(1, objects.size()); assertEquals(4, DatabaseHandler.getFeatureCount()); - assertCityObjectEquality(b2, objects.get(0)); + CityObject co = DatabaseHandler.unmarshallCityObject(objects.get(0)); + assertNotNull(co); + assertCityObjectEquality(b2, co); } @@ -223,11 +226,13 @@ public class DataBaseHandlerTest { DatabaseHandler.marshallCityObject(b4); assertEquals(4, DatabaseHandler.getFeatureCount()); - List objects = DatabaseHandler.getBufferIntersectingObjects(b, Math.sqrt(2)); + List objects = DatabaseHandler.getBufferIntersectingObjects(b, Math.sqrt(2)); assertEquals(1, objects.size()); assertEquals(4, DatabaseHandler.getFeatureCount()); - assertCityObjectEquality(b2, objects.get(0)); + CityObject co = DatabaseHandler.unmarshallCityObject(objects.get(0)); + assertNotNull(co); + assertCityObjectEquality(b2, co); } -- GitLab From 7da614ff1cc451b9b9651272e683fb87866a73f0 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 29 Jul 2025 13:21:57 +0200 Subject: [PATCH 034/118] Refactor: Implement CityObjectCache with Caffeine --- .../citydoctor2/database/CityObjectCache.java | 54 ++++++------------- .../database/CityObjectCacheTest.java | 45 ++++++++++++++++ 2 files changed, 62 insertions(+), 37 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/CityObjectCacheTest.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java index b73127d4..32e0f7b0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -1,5 +1,7 @@ package de.hft.stuttgart.citydoctor2.database; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.utils.Pair; @@ -12,57 +14,35 @@ import java.util.concurrent.ConcurrentHashMap; /** - * Soft-reference cache for + * Cache for CityObjects */ public class CityObjectCache { - private final Map> cache = new ConcurrentHashMap<>(); + private final LoadingCache cache; - public void put(GmlId id, CityObject cityObject) { - cache.put(id, new SoftReference<>(cityObject)); - } - public void cache(List objects) { + public CityObjectCache() { + cache = Caffeine.newBuilder().maximumSize(1000).build(DatabaseHandler::unmarshallCityObject); + } + public void put(CityObject cityObject) { + DatabaseHandler.marshallCityObject(cityObject); + cache.put(cityObject.getGmlId(), cityObject); } public CityObject get(GmlId id) { - SoftReference ref = cache.get(id); - if (ref == null) { - return null; - } - CityObject cityObject = ref.get(); - if (cityObject == null) { - cityObject = DatabaseHandler.unmarshallCityObject(id); - if (cityObject == null) { - cache.remove(id); - } else { - this.put(id, cityObject); - } - } - return cityObject; + return cache.get(id); } - public static List>> getCollidingObjectsList() { - List>> collidingObjects = new ArrayList<>(); - for (GmlId id : DatabaseHandler.getAllGmlIds()) { - List collidingIds = DatabaseHandler.getBBoxIntersectingObjects(id); - if (collidingIds != null && !collidingIds.isEmpty()) { - collidingObjects.add(new Pair<>(id, collidingIds)); - } + public List get(List ids) { + List features = new ArrayList<>(); + for (GmlId id : ids) { + features.add(cache.get(id)); } - return collidingObjects; + return features; } - public void remove(GmlId id) { - cache.remove(id); - } - public void clear() { - cache.clear(); - } - public int size() { - return cache.size(); - } + } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/CityObjectCacheTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/CityObjectCacheTest.java new file mode 100644 index 00000000..6fee8496 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/CityObjectCacheTest.java @@ -0,0 +1,45 @@ +package de.hft.stuttgart.citydoctor2.database; + +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.datastructure.GeometryTestUtils; +import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; +import de.hft.stuttgart.citydoctor2.datastructure.Lod; +import org.citygml4j.core.model.building.AbstractBuilding; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +public class CityObjectCacheTest { + + @BeforeClass + public static void setUpBeforeClass() { + DatabaseHandler.setupDatabase(); + } + + @Before + public void resetDatabase() { + try { + DatabaseHandler.setupFeaturesTable(); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testPut(){ + Building b = new Building(); + b.setGmlObject(mock(AbstractBuilding.class)); + b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); + + assertEquals(0, DatabaseHandler.getFeatureCount()); + CityObjectCache cache = new CityObjectCache(); + cache.put(b); + assertEquals(1, DatabaseHandler.getFeatureCount()); + } + + +} -- GitLab From 286d394ca6126e81a265d3d80249ebf3e956b2a2 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 29 Jul 2025 14:25:04 +0200 Subject: [PATCH 035/118] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 23bdc026..51eea88b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ /CityDoctorParent/.idea/jarRepositories.xml /CityDoctorParent/.idea/misc.xml /CityDoctorParent/.idea/vcs.xml +/CityDoctorParent/.idea .idea /GUISettings.properties -- GitLab From ad52781c4a6d31adfe75a2d517188052ab2935b0 Mon Sep 17 00:00:00 2001 From: Matthias Betz Date: Fri, 19 Sep 2025 11:45:20 +0200 Subject: [PATCH 036/118] fix linux start script for GUI --- .../Extensions/CityDoctorGUI/src/assembly/lin/start.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/assembly/lin/start.sh b/CityDoctorParent/Extensions/CityDoctorGUI/src/assembly/lin/start.sh index 7066c344..90ce0220 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/assembly/lin/start.sh +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/assembly/lin/start.sh @@ -1,2 +1,2 @@ -#!/bin/sh -./runtime/bin/java -classpath app/*:plugin/* de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter \ No newline at end of file +#! /usr/bin/env sh +java -classpath "app/*:plugin/*" de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter -- GitLab From 28c42d2de1773d5fe5502186dc47e9b87397f01a Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 2 Oct 2025 16:53:49 +0200 Subject: [PATCH 037/118] !Feature: Implement GeoDB and FeatureCache BREAKING CHANGE: Implements the saving and loading of CityObjects to a GeoDB with in-memory caching. This allows reading of bigger CityGml files and serves as basis for the implementation of Topological Checks. CityDoctorModel will now return Streams instead of Lists for the CityObject-getters (like getBuildings()). This requires a rework of all methods calling them. --- .../citydoctor2/check/Checkable.java | 2 +- .../citydoctor2/database/CityObjectCache.java | 48 -- .../citydoctor2/database/FeatureCache.java | 97 +++ .../citydoctor2/database/SubPartAdapter.java | 33 + .../database/TopLevelCityObjectAdapter.java | 19 + .../datastructure/CityDoctorModel.java | 250 ++++---- .../citydoctor2/parser/CityGmlParser.java | 17 +- .../writer/CityGMLWriterUtils.java | 26 +- ...ctCacheTest.java => FeatureCacheTest.java} | 4 +- .../reporting/XmlValidationReporter.java | 29 +- .../citydoctor2/gui/CityDoctorController.java | 592 +++++++++++++----- .../citydoctor2/gui/TriangulatedGeometry.java | 5 +- .../gui/tree/AddMoreButtonRunnable.java | 68 ++ .../gui/tree/node/CityObjectNode.java | 4 +- .../healer/gui/HealerController.java | 4 +- .../healer/genetic/gui/GeneticsGUI.java | 2 +- CityDoctorParent/pom.xml | 2 +- 17 files changed, 818 insertions(+), 384 deletions(-) delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/SubPartAdapter.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/TopLevelCityObjectAdapter.java rename CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/{CityObjectCacheTest.java => FeatureCacheTest.java} (93%) create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AddMoreButtonRunnable.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java index c9b94124..3994bdd1 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java @@ -52,7 +52,7 @@ public abstract class Checkable implements Serializable { private final Map checkResults = new HashMap<>(); private boolean isValidated = false; - protected void setValidated(boolean validated) { + public void setValidated(boolean validated) { isValidated = validated; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java deleted file mode 100644 index 32e0f7b0..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ /dev/null @@ -1,48 +0,0 @@ -package de.hft.stuttgart.citydoctor2.database; - -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; -import de.hft.stuttgart.citydoctor2.datastructure.CityObject; -import de.hft.stuttgart.citydoctor2.datastructure.GmlId; -import de.hft.stuttgart.citydoctor2.utils.Pair; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.lang.ref.SoftReference; -import java.util.concurrent.ConcurrentHashMap; - - -/** - * Cache for CityObjects - */ -public class CityObjectCache { - private final LoadingCache cache; - - - public CityObjectCache() { - cache = Caffeine.newBuilder().maximumSize(1000).build(DatabaseHandler::unmarshallCityObject); - } - - public void put(CityObject cityObject) { - DatabaseHandler.marshallCityObject(cityObject); - cache.put(cityObject.getGmlId(), cityObject); - } - - public CityObject get(GmlId id) { - return cache.get(id); - } - - public List get(List ids) { - List features = new ArrayList<>(); - for (GmlId id : ids) { - features.add(cache.get(id)); - } - return features; - } - - - - -} - diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java new file mode 100644 index 00000000..b73b2859 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -0,0 +1,97 @@ +package de.hft.stuttgart.citydoctor2.database; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.github.benmanes.caffeine.cache.RemovalCause; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + + +/** + * Cache for CityObjects + */ +public class FeatureCache{ + private final LoadingCache cache; + + private final Cache subCache; + public FeatureCache() { + cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> { + if (value != null && cause.wasEvicted()) { + DatabaseHandler.marshallCityObject(value); + } + }).build(DatabaseHandler::unmarshallCityObject); + subCache = Caffeine.newBuilder().maximumSize(250).build(); + DatabaseHandler.setupDatabase(); + } + + public void put(CityObject cityObject) { + DatabaseHandler.marshallCityObject(cityObject); + cache.put(cityObject.getGmlId(), cityObject); + } + + public CityObject get(GmlId id) { + return cache.get(id); + } + + public void replace(GmlId id, CityObject cityObject) { + cache.invalidate(id); + put(cityObject); + } + + public Stream getStream(List gmlIds) { + return gmlIds.stream().map(this::get); + } + + /** + * Gets a non-top-level CityObject directly. Only use this in situations where directly accessing the object is required + * and access via the top-level CityObject is impossible or highly inefficient, e.g. rendering the associated object + * of a CityObjectNode. + * @param coa the adapter object of the subpart + * @return the associated CityObject + */ + public CityObject getSubPart(SubPartAdapter coa) { + CityObject co = subCache.getIfPresent(coa.getFeatureId()); + if (co == null) { + return buildAndGetSubPart(coa); + } + else return co; + } + + /** + * + * @param coa + * @return + */ + private CityObject buildAndGetSubPart(SubPartAdapter coa) { + CityObject parent = cache.get(coa.getTopLevelId()); + if (parent == null) { + throw new IllegalStateException("CityObjectAdapter has non-existent parent - GmlId: " + coa.getTopLevelId()); + } + CityObjectCollector collector = new CityObjectCollector(); + parent.accept(collector); + collector.getCityObjects().forEach(cityObject -> subCache.put(cityObject.getGmlId(), cityObject)); + CityObject co = subCache.getIfPresent(coa.getFeatureId()); + if (co == null) { + throw new IllegalStateException("CityObjectAdapter has non-existent feature - GmlId: " + coa.getFeatureId() + + ", parent GmlId: " + coa.getTopLevelId()); + } + return co; + } + + public List get(List ids) { + List features = new ArrayList<>(); + for (GmlId id : ids) { + features.add(cache.get(id)); + } + return features; + } + + +} + diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/SubPartAdapter.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/SubPartAdapter.java new file mode 100644 index 00000000..b788521d --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/SubPartAdapter.java @@ -0,0 +1,33 @@ +package de.hft.stuttgart.citydoctor2.database; + +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; + +/** + * Adapter class for Lists. All function calls on objects of this + * class get passed to their respective CityObjects. + */ +public class SubPartAdapter { +// TODO: Remove CO extension and change model to stream and map objects to their respective classes + private final GmlId featureId; + private final GmlId topLevelId; + private FeatureCache cache; + + public SubPartAdapter(CityObject co, FeatureCache cache) { + this.featureId = co.getGmlId(); + this.topLevelId = co.getTopLevelCityObject().getGmlId(); + this.cache = cache; + } + + public boolean isValidated(){ + return cache.get(featureId).isValidated(); + } + + public GmlId getFeatureId() { + return featureId; + } + + public GmlId getTopLevelId() { + return topLevelId; + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/TopLevelCityObjectAdapter.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/TopLevelCityObjectAdapter.java new file mode 100644 index 00000000..fb1b7856 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/TopLevelCityObjectAdapter.java @@ -0,0 +1,19 @@ +package de.hft.stuttgart.citydoctor2.database; + +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; + +public class TopLevelCityObjectAdapter { + + private final GmlId featureId; + private FeatureCache cache; + + public TopLevelCityObjectAdapter(CityObject co, FeatureCache cache) { + this.featureId = co.getGmlId(); + this.cache = cache; + } + + public boolean isValidated(){ + return cache.get(featureId).isValidated(); + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 0f475c44..2f852155 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -21,6 +21,7 @@ package de.hft.stuttgart.citydoctor2.datastructure; import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; +import de.hft.stuttgart.citydoctor2.database.FeatureCache; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.writer.CityGMLWriterUtils; @@ -46,15 +47,15 @@ import java.util.stream.Stream; public class CityDoctorModel { private static final String COULD_NOT_FIND_FEATURE = "Could not find feature: "; - private final List buildings; - private final List vegetation; - private final List bridges; - private final List cityfurniture; - private final List genericObjects; - private final List land; - private final List roads; - private final List tunnels; - private final List water; + private final List buildings; + private final List vegetation; + private final List bridges; + private final List cityfurniture; + private final List genericObjects; + private final List land; + private final List roads; + private final List tunnels; + private final List water; private CityModel cModel; private final ParserConfiguration config; private final String fileName; @@ -63,6 +64,7 @@ public class CityDoctorModel { private boolean isValidated = false; private ValidationPlan plan; private CityGMLVersion cityGMLVersion; + private final FeatureCache cache; public CityDoctorModel(ParserConfiguration config, File file) { if (config == null) { @@ -81,6 +83,7 @@ public class CityDoctorModel { cityfurniture = new ArrayList<>(); genericObjects = new ArrayList<>(); globalErrors = new ArrayList<>(); + cache = new FeatureCache(); } public boolean isValidated() { @@ -118,20 +121,12 @@ public class CityDoctorModel { } public Stream createFeatureStream() { - return Stream.of(buildings.stream(), vegetation.stream(), bridges.stream(), land.stream(), roads.stream(), - tunnels.stream(), water.stream(), cityfurniture.stream(), genericObjects.stream()).flatMap(co -> co); + return Stream.of(getBuildings(), getVegetation(), getBridges(), getLand(), getTransportation(), + getTunnels(), getWater(), getCityFurniture(), getGenericCityObjects()).flatMap(co -> co); } public void accept(CheckableVisitor visitor) { - buildings.forEach(co -> co.accept(visitor)); - vegetation.forEach(co -> co.accept(visitor)); - bridges.forEach(co -> co.accept(visitor)); - land.forEach(co -> co.accept(visitor)); - roads.forEach(co -> co.accept(visitor)); - tunnels.forEach(co -> co.accept(visitor)); - water.forEach(co -> co.accept(visitor)); - cityfurniture.forEach(co -> co.accept(visitor)); - genericObjects.forEach(co -> co.accept(visitor)); + createFeatureStream().forEach(cityObject -> cityObject.accept(visitor)); } public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException { @@ -228,106 +223,153 @@ public class CityDoctorModel { public Set collectErrors() { List errors = new ArrayList<>(); - collectErrorsFromList(errors, buildings); - collectErrorsFromList(errors, vegetation); - collectErrorsFromList(errors, bridges); - collectErrorsFromList(errors, land); - collectErrorsFromList(errors, roads); - collectErrorsFromList(errors, tunnels); - collectErrorsFromList(errors, water); - collectErrorsFromList(errors, cityfurniture); - collectErrorsFromList(errors, genericObjects); + createFeatureStream().forEach(cityObject -> cityObject.collectErrors(errors)); return new HashSet<>(errors); } - private void collectErrorsFromList(List errors, List cos) { - for (CityObject co : cos) { - co.collectContainedErrors(errors); - } - } public String getFileName() { return fileName; } - public void addBridge(BridgeObject coBridge) { - bridges.add(coBridge); - } public void addBuilding(Building coBuilding) { - buildings.add(coBuilding); + buildings.add(coBuilding.getGmlId()); + cache.put(coBuilding); } - public List getBuildings() { - return buildings; + public Stream getBuildings() { + return buildings.stream().map(co -> (Building) cache.get(co)); } - public List getBridges() { - return bridges; + public int getBuildingCount(){ + return buildings.size(); + } + + public void addBridge(BridgeObject coBridge) { + bridges.add(coBridge.getGmlId()); + cache.put(coBridge); } - public List getCityFurniture() { - return cityfurniture; + public Stream getBridges() { + return bridges.stream().map(co -> (BridgeObject) cache.get(co)); + } + + public int getBridgeCount(){ + return bridges.size(); } public void addCityFurniture(CityFurniture coFurniture) { - cityfurniture.add(coFurniture); + cityfurniture.add(coFurniture.getGmlId()); + cache.put(coFurniture); + } + + public Stream getCityFurniture() { + return cityfurniture.stream().map(co -> (CityFurniture) cache.get(co)); } - public List getGenericCityObjects() { - return genericObjects; + public int getCityFurnitureCount(){ + return cityfurniture.size(); } public void addGenericCityObject(GenericCityObject coGenericCityObject) { - genericObjects.add(coGenericCityObject); + genericObjects.add(coGenericCityObject.getGmlId()); + cache.put(coGenericCityObject); } - public void setCityModel(CityModel cModel) { - this.cModel = cModel; + public Stream getGenericCityObjects() { + return genericObjects.stream().map(co -> (GenericCityObject) cache.get(co)); } - public CityModel getCityModel() { - return cModel; + public int getGenericCityObjectCount(){ + return genericObjects.size(); + } + + public void addTransportation(TransportationObject to) { + roads.add(to.getGmlId()); + cache.put(to); } - public List getTransportation() { - return roads; + public Stream getTransportation() { + return roads.stream().map(co -> (TransportationObject) cache.get(co)); } - public List getTunnels() { - return tunnels; + public int getTransportationCount(){ + return roads.size(); } - public List getWater() { - return water; + public void addTunnel(Tunnel tunnel) { + tunnels.add(tunnel.getGmlId()); + cache.put(tunnel); } - public List getLand() { - return land; + public Stream getTunnels() { + return tunnels.stream().map(co -> (Tunnel) cache.get(co)); } - public List getVegetation() { - return vegetation; + public int getTunnelCount(){ + return tunnels.size(); } public void addWater(WaterObject wo) { - water.add(wo); + water.add(wo.getGmlId()); + cache.put(wo); + } + + public Stream getWater() { + return water.stream().map(co -> (WaterObject) cache.get(co)); + } + + public int getWaterCount(){ + return water.size(); } public void addLand(LandObject lo) { - land.add(lo); + land.add(lo.getGmlId()); + cache.put(lo); + } + + public void addRelief(ReliefObject relief) { + land.add(relief.getGmlId()); + cache.put(relief); + } + + public void addTin(TinObject tin) { + land.add(tin.getGmlId()); + cache.put(tin); + } + + public Stream getLand() { + return land.stream().map(cache::get); + } + + public int getLandCount(){ + return land.size(); } public void addVegetation(Vegetation veg) { - vegetation.add(veg); + vegetation.add(veg.getGmlId()); + cache.put(veg); } - public void addTransportation(TransportationObject to) { - roads.add(to); + public Stream getVegetation() { + return vegetation.stream().map(co -> (Vegetation) cache.get(co)); } - public void addTunnel(Tunnel tunnel) { - tunnels.add(tunnel); + public int getVegetationCount(){ + return vegetation.size(); + } + + public void setCityModel(CityModel cModel) { + this.cModel = cModel; + } + + public CityModel getCityModel() { + return cModel; + } + + public FeatureCache getCache(){ + return cache; } public int getNumberOfFeatures() { @@ -362,100 +404,105 @@ public class CityDoctorModel { } private void replaceWaterObject(CityObject currentFeature, CityObject nextFeature) { - int index = water.indexOf(currentFeature); + int index = water.indexOf(currentFeature.getGmlId()); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in water objects"); } - water.set(index, (WaterObject) nextFeature); + cache.replace(currentFeature.getGmlId(), nextFeature); + water.set(index, nextFeature.getGmlId()); } private void replaceLandObject(CityObject currentFeature, CityObject nextFeature) { - int index = land.indexOf(currentFeature); + int index = land.indexOf(currentFeature.getGmlId()); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in land objects"); } - land.set(index, nextFeature); + cache.replace(currentFeature.getGmlId(), nextFeature); + land.set(index, nextFeature.getGmlId()); } private void replaceVegetation(CityObject currentFeature, CityObject nextFeature) { - int index = vegetation.indexOf(currentFeature); + int index = vegetation.indexOf(currentFeature.getGmlId()); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in vegetation"); } - vegetation.set(index, (Vegetation) nextFeature); + cache.replace(currentFeature.getGmlId(), nextFeature); + vegetation.set(index, nextFeature.getGmlId()); } private void replaceCityFurniture(CityObject currentFeature, CityObject nextFeature) { - int index = cityfurniture.indexOf(currentFeature); + int index = cityfurniture.indexOf(currentFeature.getGmlId()); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in vegetation"); } - cityfurniture.set(index, (CityFurniture) nextFeature); + cache.replace(currentFeature.getGmlId(), nextFeature); + cityfurniture.set(index, nextFeature.getGmlId()); } private void replaceGenericCityObject(CityObject currentFeature, CityObject nextFeature) { - int index = genericObjects.indexOf(currentFeature); + int index = genericObjects.indexOf(currentFeature.getGmlId()); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in generic city objects"); } - genericObjects.set(index, (GenericCityObject) nextFeature); + cache.replace(currentFeature.getGmlId(), nextFeature); + genericObjects.set(index, nextFeature.getGmlId()); } private void replaceTransportationObject(CityObject currentFeature, CityObject nextFeature) { - int index = roads.indexOf(currentFeature); + int index = roads.indexOf(currentFeature.getGmlId()); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in transportation objects"); } - roads.set(index, (TransportationObject) nextFeature); + roads.set(index, nextFeature.getGmlId()); } private void replaceTunnel(CityObject currentFeature, CityObject nextFeature) { - int index = tunnels.indexOf(currentFeature); + int index = tunnels.indexOf(currentFeature.getGmlId()); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in tunnels"); } - tunnels.set(index, (Tunnel) nextFeature); + tunnels.set(index, nextFeature.getGmlId()); } private void replaceBridge(CityObject currentFeature, CityObject nextFeature) { - int index = bridges.indexOf(currentFeature); + int index = bridges.indexOf(currentFeature.getGmlId()); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in bridges"); } - bridges.set(index, (BridgeObject) nextFeature); + bridges.set(index, nextFeature.getGmlId()); } private void replaceBuilding(CityObject currentFeature, CityObject nextFeature) { - int index = buildings.indexOf(currentFeature); + int index = buildings.indexOf(currentFeature.getGmlId()); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in buildings"); } - buildings.set(index, (Building) nextFeature); + buildings.set(index, nextFeature.getGmlId()); } public void addCityObject(CityObject co) { if (co instanceof Building b) { - buildings.add(b); + buildings.add(b.getGmlId()); } else if (co instanceof BridgeObject bo) { - bridges.add(bo); + bridges.add(bo.getGmlId()); } else if (co instanceof TransportationObject to) { - roads.add(to); + roads.add(to.getGmlId()); } else if (co instanceof Tunnel tu) { - tunnels.add(tu); + tunnels.add(tu.getGmlId()); } else if (co instanceof Vegetation veg) { - vegetation.add(veg); + vegetation.add(veg.getGmlId()); } else if (co instanceof LandObject lo) { - land.add(lo); + land.add(lo.getGmlId()); } else if (co instanceof WaterObject wo) { - water.add(wo); + water.add(wo.getGmlId()); } else if (co instanceof ReliefObject) { - land.add(co); + land.add(co.getGmlId()); } else if (co instanceof TinObject) { - land.add(co); + land.add(co.getGmlId()); } else if (co instanceof CityFurniture cf) { - cityfurniture.add(cf); + cityfurniture.add(cf.getGmlId()); } else if (co instanceof GenericCityObject gco) { - genericObjects.add(gco); + genericObjects.add(gco.getGmlId()); } } @@ -467,11 +514,4 @@ public class CityDoctorModel { return cityGMLVersion; } - public void addRelief(ReliefObject relief) { - addCityObject(relief); - } - - public void addTin(TinObject tin) { - addCityObject(tin); - } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index e8ec04fe..ec7d839f 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -759,22 +759,9 @@ public class CityGmlParser { } private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) { - drainCityObjectList(model.getBuildings(), cityObjectConsumer); - drainCityObjectList(model.getBridges(), cityObjectConsumer); - drainCityObjectList(model.getVegetation(), cityObjectConsumer); - drainCityObjectList(model.getLand(), cityObjectConsumer); - drainCityObjectList(model.getTransportation(), cityObjectConsumer); - drainCityObjectList(model.getWater(), cityObjectConsumer); - drainCityObjectList(model.getTunnels(), cityObjectConsumer); - drainCityObjectList(model.getCityFurniture(), cityObjectConsumer); - drainCityObjectList(model.getGenericCityObjects(), cityObjectConsumer); - } - private static void drainCityObjectList(List objects, CityGmlConsumer cityObjectConsumer) { - for (CityObject co : objects) { - cityObjectConsumer.accept(co); - } - objects.clear(); + model.createFeatureStream().forEach(cityObjectConsumer::accept); } + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java index 4069621e..d58b4b67 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java @@ -22,12 +22,12 @@ import java.io.File; import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -141,6 +141,7 @@ public class CityGMLWriterUtils { stats.setName(adeId); statistics.getErrors().add(new ErrorProperty(stats)); } + // TODO: Quality ADE update to CityGML 3.0 statistics.setNumErrorBuildings(new FeatureStatisticsProperty(countValidatedCityObjects(model.getBuildings()))); statistics.setNumErrorBridgeObjects(new FeatureStatisticsProperty(countValidatedCityObjects(model.getBridges()))); statistics.setNumErrorLandObjects(new FeatureStatisticsProperty(countValidatedCityObjects(model.getLand()))); @@ -152,29 +153,30 @@ public class CityGMLWriterUtils { return val; } - private static FeatureStatistics countValidatedCityObjects(List cos) { - int numChecked = 0; - int numError = 0; - for (CityObject co : cos) { + private static FeatureStatistics countValidatedCityObjects(Stream cos) { + AtomicInteger numChecked = new AtomicInteger(); + AtomicInteger numError = new AtomicInteger(); + cos.forEach(co -> { if (co.isValidated()) { - numChecked++; + numChecked.getAndIncrement(); if (co.containsAnyError()) { - numError++; + numError.getAndIncrement(); } } - } - return new FeatureStatistics(numChecked, numError); + }); + return new FeatureStatistics(numChecked.get(), numError.get()); } - private static void storeCityObjects(List cos, GeometryFactory gmlFactory, CityDoctorModel model, CityModel cModel, Validation val) { - for (CityObject co : cos) { + private static void storeCityObjects(Stream cos, GeometryFactory gmlFactory, CityDoctorModel model, CityModel cModel, Validation val) { + cos.forEach(co -> { if (model.isValidated() && val != null) { QualityADEUtils.writeQualityAde(co, val); } co.reCreateGeometries(gmlFactory, model.getParserConfig()); cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(co.getGmlObject())); - } + }); + } } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/CityObjectCacheTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java similarity index 93% rename from CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/CityObjectCacheTest.java rename to CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java index 6fee8496..773adcbb 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/CityObjectCacheTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java @@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; -public class CityObjectCacheTest { +public class FeatureCacheTest { @BeforeClass public static void setUpBeforeClass() { @@ -36,7 +36,7 @@ public class CityObjectCacheTest { b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); assertEquals(0, DatabaseHandler.getFeatureCount()); - CityObjectCache cache = new CityObjectCache(); + FeatureCache cache = new FeatureCache(); cache.put(b); assertEquals(1, DatabaseHandler.getFeatureCount()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/XmlValidationReporter.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/XmlValidationReporter.java index 5bf19ad3..3c550383 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/XmlValidationReporter.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/XmlValidationReporter.java @@ -45,33 +45,8 @@ public class XmlValidationReporter implements Reporter { @Override public void writeReport(Checks checks, OutputStream filePath, CityDoctorModel model, ValidationConfiguration config) throws CheckReportWriteException { StreamReporter streamReporter = new XmlStreamReporter(filePath, model.getFileName(), config); - for (BridgeObject bo : model.getBridges()) { - streamReporter.report(bo); - } - for (Building b : model.getBuildings()) { - streamReporter.report(b); - } - for (TransportationObject to : model.getTransportation()) { - streamReporter.report(to); - } - for (CityObject lo : model.getLand()) { - streamReporter.report(lo); - } - for (Vegetation v : model.getVegetation()) { - streamReporter.report(v); - } - for (WaterObject wo : model.getWater()) { - streamReporter.report(wo); - } - for (Tunnel tunnel : model.getTunnels()) { - streamReporter.report(tunnel); - } - for (CityFurniture cf : model.getCityFurniture()) { - streamReporter.report(cf); - } - for (GenericCityObject gco : model.getGenericCityObjects()) { - streamReporter.report(gco); - } + model.createFeatureStream().forEach(streamReporter::report); + for (CheckError err : model.getGlobalErrors()) { streamReporter.reportGlobalError(err); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index fd4320d1..b42cd4bc 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -1,6 +1,7 @@ package de.hft.stuttgart.citydoctor2.gui; import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.Checkable; import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.ErrorId; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; @@ -48,6 +49,8 @@ import java.util.*; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; public class CityDoctorController { @@ -79,6 +82,8 @@ public class CityDoctorController { private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0); private AtomicInteger genericCityObjectsChunkNr = new AtomicInteger(0); + + public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) { this.mainWindow = mainWindow; this.highlightController = highlightController; @@ -205,6 +210,10 @@ public class CityDoctorController { return zipArchive; } + public static int getMaxFeaturesPerChunk(){ + return MAX_FEATURES_PER_CHUNK; + } + public void loadZipEntry(CityGmlZipEntry entry) { if (!entry.isLoaded()) { entry.loadEntry(currentConfig); @@ -260,15 +269,15 @@ public class CityDoctorController { } private void setupFeatureTabs() { - mainWindow.setDisableOfFeatureTab(FeatureType.BUILDING, model.getBuildings().isEmpty()); - mainWindow.setDisableOfFeatureTab(FeatureType.VEGETATION, model.getVegetation().isEmpty()); - mainWindow.setDisableOfFeatureTab(FeatureType.TRANSPORTATION, model.getTransportation().isEmpty()); - mainWindow.setDisableOfFeatureTab(FeatureType.TUNNEL, model.getTunnels().isEmpty()); - mainWindow.setDisableOfFeatureTab(FeatureType.BRIDGE, model.getBridges().isEmpty()); - mainWindow.setDisableOfFeatureTab(FeatureType.WATER, model.getWater().isEmpty()); - mainWindow.setDisableOfFeatureTab(FeatureType.LAND, model.getLand().isEmpty()); - mainWindow.setDisableOfFeatureTab(FeatureType.CITY_FURNITURE, model.getCityFurniture().isEmpty()); - mainWindow.setDisableOfFeatureTab(FeatureType.GENERIC_CITY_OBJECT, model.getGenericCityObjects().isEmpty()); + mainWindow.setDisableOfFeatureTab(FeatureType.BUILDING, model.getBuildingCount() == 0); + mainWindow.setDisableOfFeatureTab(FeatureType.VEGETATION, model.getVegetationCount() == 0); + mainWindow.setDisableOfFeatureTab(FeatureType.TRANSPORTATION, model.getTransportationCount() == 0); + mainWindow.setDisableOfFeatureTab(FeatureType.TUNNEL, model.getTunnelCount() == 0); + mainWindow.setDisableOfFeatureTab(FeatureType.BRIDGE, model.getBridgeCount() == 0); + mainWindow.setDisableOfFeatureTab(FeatureType.WATER, model.getWaterCount() == 0); + mainWindow.setDisableOfFeatureTab(FeatureType.LAND, model.getLandCount() == 0); + mainWindow.setDisableOfFeatureTab(FeatureType.CITY_FURNITURE, model.getCityFurnitureCount() == 0); + mainWindow.setDisableOfFeatureTab(FeatureType.GENERIC_CITY_OBJECT, model.getGenericCityObjectCount() == 0); } public void buildTrees() { @@ -281,7 +290,7 @@ public class CityDoctorController { buildWater(model); buildLand(model); buildCityFurniture(model); - buildOtherCityObjects(model); + buildGenericCityObjects(model); buildTunnel(model); } @@ -298,122 +307,130 @@ public class CityDoctorController { } private void buildLand(CityDoctorModel model) { - if (model.getLand().isEmpty()) { + if (model.getLandCount() == 0) { return; } TreeView landView = mainWindow.getTerrainView(); - TreeItem landRoot = new TreeItem<>(new CityObjectGroupNode(model.getLand(), "Land", + TreeItem landRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Land", Color.BROWN)); landRoot.setExpanded(true); landView.setRoot(landRoot); - buildLandFromList(model.getLand(), landView.getRoot()); - addMoreButtonIfNecessary(model.getLand(), landView, landRoot, landChunkNr); + Supplier> supply = model::getLand; + buildLandTreeFromList(supply, landView.getRoot()); + addMoreButtonForLand(landView, landRoot, landChunkNr, supply, model.getLandCount()); } - private void buildLandFromList(List list, TreeItem root) { + private void buildLandTreeFromList(Supplier> supply, TreeItem root) { int landChunk = landChunkNr.get(); - for (int i = landChunk * MAX_FEATURES_PER_CHUNK; i < (landChunk + 1) * MAX_FEATURES_PER_CHUNK - && i < list.size(); i++) { - CityObject land = list.get(i); + supply.get().skip((long) landChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(land ->{ CityObjectNode node = new CityObjectNode(land); TreeItem item = new TreeItem<>(node); item.setExpanded(true); root.getChildren().add(item); createGeometryNodes(land, item); - - } + }); } private void buildCityFurniture(CityDoctorModel model) { - if (model.getCityFurniture().isEmpty()) { + if (model.getCityFurnitureCount() == 0) { return; } TreeView cityFurnitureView = mainWindow.getCityFurnitureView(); - TreeItem cityFurnitureRoot = new TreeItem<>(new CityObjectGroupNode(model.getCityFurniture(), + TreeItem cityFurnitureRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "City Furniture", Color.BLUEVIOLET)); cityFurnitureRoot.setExpanded(true); cityFurnitureView.setRoot(cityFurnitureRoot); - buildCityFurnitureTreeFromList(model.getCityFurniture(), cityFurnitureView.getRoot()); - addMoreButtonIfNecessary(model.getCityFurniture(), cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr); + Supplier> supply = model::getCityFurniture; + buildCityFurnitureTreeFromList(supply, cityFurnitureView.getRoot()); + addMoreButtonForCityFurniture(cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr, supply, + model.getCityFurnitureCount()); } - private void buildOtherCityObjects(CityDoctorModel model) { - if (model.getGenericCityObjects().isEmpty()) { + private void buildGenericCityObjects(CityDoctorModel model) { + if (model.getGenericCityObjectCount() == 0) { return; } TreeView otherObjectsView = mainWindow.getGenericCityObjectView(); - TreeItem otherObjectsRoot = new TreeItem<>(new CityObjectGroupNode(model.getGenericCityObjects(), + TreeItem otherObjectsRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Generic CityObjects", Color.DARKGRAY)); otherObjectsRoot.setExpanded(true); otherObjectsView.setRoot(otherObjectsRoot); - buildGenericCityObjectTreeFromList(model.getGenericCityObjects(), otherObjectsView.getRoot()); - addMoreButtonIfNecessary(model.getGenericCityObjects(), otherObjectsView, otherObjectsRoot, genericCityObjectsChunkNr); + Supplier> supply = model::getGenericCityObjects; + buildGenericCityObjectTreeFromList(supply, otherObjectsView.getRoot()); + addMoreButtonForGenericCityObjects( otherObjectsView, otherObjectsRoot, genericCityObjectsChunkNr, supply, + model.getGenericCityObjectCount()); } private void buildWater(CityDoctorModel model) { - if (model.getWater().isEmpty()) { + if (model.getWaterCount() == 0) { return; } TreeView waterView = mainWindow.getWaterView(); - TreeItem waterRoot = new TreeItem<>(new CityObjectGroupNode(model.getWater(), "Water", + TreeItem waterRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Water", Color.LIGHTSKYBLUE)); waterRoot.setExpanded(true); waterView.setRoot(waterRoot); - buildWaterTreeFromList(model.getWater(), waterRoot); - addMoreButtonIfNecessary(model.getWater(), waterView, waterRoot, waterChunkNr); + Supplier> supply = model::getWater; + buildWaterTreeFromList(supply, waterRoot); + addMoreButtonForWater(waterView, waterRoot, waterChunkNr, supply, model.getWaterCount()); } private void buildBridges(CityDoctorModel model) { - if (model.getBridges().isEmpty()) { + if (model.getBridgeCount() == 0) { return; } TreeView bridgeView = mainWindow.getBridgeView(); - TreeItem bridgeRoot = new TreeItem<>(new CityObjectGroupNode(model.getBridges(), "Bridges", + TreeItem bridgeRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Bridges", Color.CORAL)); bridgeRoot.setExpanded(true); bridgeView.setRoot(bridgeRoot); - buildBridgeTreeFromList(model.getBridges(), bridgeView.getRoot()); - addMoreButtonIfNecessary(model.getBridges(), bridgeView, bridgeRoot, bridgeChunkNr); + Supplier> supply = model::getBridges; + buildBridgeTreeFromList(supply, bridgeView.getRoot()); + addMoreButtonForBridges(bridgeView, bridgeRoot, bridgeChunkNr, supply, model.getBridgeCount()); } private void buildTransportation(CityDoctorModel model) { - if (model.getTransportation().isEmpty()) { + if (model.getTransportationCount() == 0) { return; } TreeView transView = mainWindow.getTransportationView(); - TreeItem transRoot = new TreeItem<>(new CityObjectGroupNode(model.getTransportation(), + TreeItem transRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Transportation", Color.YELLOW)); transRoot.setExpanded(true); transView.setRoot(transRoot); - buildTransportationTreeFromList(model.getTransportation(), transView.getRoot()); - addMoreButtonIfNecessary(model.getTransportation(), transView, transRoot, transportationChunkNr); + Supplier> supply = model::getTransportation; + buildTransportationTreeFromList(supply, transView.getRoot()); + addMoreButtonForTransportation(transView, transRoot, transportationChunkNr, supply, + model.getTransportationCount()); } private void buildTunnel(CityDoctorModel model) { - if (model.getTunnels().isEmpty()) { + if (model.getTunnelCount() == 0) { return; } TreeView tunnelView = mainWindow.getTunnelView(); - TreeItem tunnelRoot = new TreeItem<>(new CityObjectGroupNode(model.getTunnels(), "Tunnel", + TreeItem tunnelRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Tunnel", Color.SLATEGRAY)); tunnelRoot.setExpanded(true); tunnelView.setRoot(tunnelRoot); - buildTunnelTreeFromList(model.getTunnels(), tunnelRoot); - addMoreButtonIfNecessary(model.getTunnels(), tunnelView, tunnelRoot, tunnelChunkNr); + Supplier> supply = model::getTunnels; + buildTunnelTreeFromList(supply, tunnelRoot); + addMoreButtonForTunnel(tunnelView, tunnelRoot, tunnelChunkNr, supply, model.getTunnelCount()); } private void buildVegetation(CityDoctorModel model) { - if (model.getVegetation().isEmpty()) { + if (model.getVegetationCount() == 0) { return; } TreeView vegetationsView = mainWindow.getVegetationView(); - TreeItem vegRoot = new TreeItem<>(new CityObjectGroupNode(model.getVegetation(), + TreeItem vegRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Vegetation", Color.LIGHTGREEN)); vegRoot.setExpanded(true); vegetationsView.setRoot(vegRoot); - buildVegetationTreeFromList(model.getVegetation(), vegRoot); - addMoreButtonIfNecessary(model.getVegetation(), vegetationsView, vegRoot, vegetationChunkNr); + Supplier> supply = model::getVegetation; + buildVegetationTreeFromList(supply, vegRoot); + addMoreButtonForVegetation(vegetationsView, vegRoot, vegetationChunkNr, supply, model.getVegetationCount()); } private void addMoreButtonIfNecessary(List list, TreeView view, @@ -429,7 +446,7 @@ public class CityDoctorController { chunkCounter.incrementAndGet(); int visibleFeatures = countVisibleNodes(view); - buildTreeFromList(list, root, chunkCounter); +// buildTreeFromList(list, root, chunkCounter); updateTree(root); if (selectedIndex >= 0) { view.getSelectionModel().select(selectedIndex); @@ -445,17 +462,35 @@ public class CityDoctorController { } } + private void moreButton(CityDoctorModel model, TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> streamSupplier){ + int featureCount = model.getBuildingCount(); + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildTreeFromList(streamSupplier, root, chunkCounter); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + private void buildBuildings(CityDoctorModel model) { - if (model.getBuildings().isEmpty()) { + if (model.getBuildingCount() == 0) { return; } + //TODO: Continue Supplier implementation TreeView buildingsView = mainWindow.getBuildingsView(); - TreeItem root = new TreeItem<>(new CityObjectGroupNode(model.getBuildings(), "Buildings", + TreeItem root = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Buildings", Color.WHITE)); root.setExpanded(true); buildingsView.setRoot(root); - buildBuildingTreeFromList(model.getBuildings(), root); - addMoreButtonToBuildingsIfNecessary(model.getBuildings(), buildingsView, root, buildingChunkNr); + Supplier> supply = model::getBuildings; + buildBuildingTreeFromList(supply, root); + addMoreButtonForBuildings(buildingsView, root, buildingChunkNr, supply, model.getBuildingCount()); } @@ -471,7 +506,7 @@ public class CityDoctorController { root.getChildren().remove(root.getChildren().size() - 1); chunkCounter.getAndIncrement(); int visibleFeatures = countVisibleNodes(view); - buildBuildingTreeFromList(list, root); +// buildBuildingTreeFromList(list, root); updateTree(root); if (selectedIndex >= 0) { view.getSelectionModel().select(selectedIndex); @@ -487,6 +522,141 @@ public class CityDoctorController { } } + private void addMoreButtonForBuildings(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount) { + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildBuildingTreeFromList(supply, root); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + private void addMoreButtonForBridges(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount){ + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildBridgeTreeFromList(supply, root); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + private void addMoreButtonForVegetation(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount){ + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildVegetationTreeFromList(supply, root); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + private void addMoreButtonForTunnel(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount){ + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildTunnelTreeFromList(supply, root); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + private void addMoreButtonForTransportation(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount){ + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildTransportationTreeFromList(supply, root); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + private void addMoreButtonForLand(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount){ + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildLandTreeFromList(supply, root); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + private void addMoreButtonForWater(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount){ + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildWaterTreeFromList(supply, root); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + private void addMoreButtonForCityFurniture(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount){ + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildCityFurnitureTreeFromList(supply, root); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + private void addMoreButtonForGenericCityObjects(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount){ + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { + @Override + public void buildNodeTreeCall() { + buildGenericCityObjectTreeFromList(supply, root); + updateTree(root); + } + }; + //add button + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + private static int countVisibleNodes(TreeView view) { return countVisibleNodes(view.getRoot()) + 1; } @@ -501,17 +671,16 @@ public class CityDoctorController { return visibleFeatures; } - private void buildTreeFromList(List cos, TreeItem root, + private void buildTreeFromList(Supplier> supply, TreeItem root, AtomicInteger chunkCounter) { - int chunk = chunkCounter.get(); - for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) { - CityObject co = cos.get(i); + int buildingChunk = buildingChunkNr.get(); + supply.get().skip((long) buildingChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(co ->{ CityObjectNode node = new CityObjectNode(co); TreeItem nodeItem = new TreeItem<>(node); nodeItem.setExpanded(true); root.getChildren().add(nodeItem); createGeometryNodes(co, nodeItem); - } + }); } private void buildTreeFromListWithProducer(List cos, TreeItem root, @@ -582,11 +751,9 @@ public class CityDoctorController { return nodeItem; } - private void buildBuildingTreeFromList(List list, TreeItem root) { + private void buildBuildingTreeFromList(Supplier> supply, TreeItem root) { int buildingChunk = buildingChunkNr.get(); - for (int i = buildingChunk * MAX_FEATURES_PER_CHUNK; i < (buildingChunk + 1) * MAX_FEATURES_PER_CHUNK - && i < list.size(); i++) { - Building b = list.get(i); + supply.get().skip((long) buildingChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(b ->{ TreeItem item = buildTopLevelCityObjectNode(b, root, b.getBoundarySurfaces()); createBuildingInstallationNodes(b, item); @@ -596,14 +763,12 @@ public class CityDoctorController { createBuildingStoreyNodes(b, item); createBuildingUnitNodes(b, item); createBuildingPartNodes(b, item); - } + }); } - private void buildBridgeTreeFromList(List list, TreeItem root) { + private void buildBridgeTreeFromList(Supplier> supply, TreeItem root) { int bridgeChunk = bridgeChunkNr.get(); - for (int i = bridgeChunk * MAX_FEATURES_PER_CHUNK; i < (bridgeChunk + 1) * MAX_FEATURES_PER_CHUNK - && i < list.size(); i++) { - BridgeObject bridge = list.get(i); + supply.get().skip((long) bridgeChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(bridge ->{ TreeItem item = buildTopLevelCityObjectNode(bridge, root, bridge.getBoundarySurfaces()); createBridgeRoomNodes(bridge, item); @@ -611,55 +776,46 @@ public class CityDoctorController { createBridgeFurnitureNodes(bridge, item); createBridgeConstructiveElementsNodes(bridge, item); createBridgePartNodes(bridge, item); - } + }); } - private void buildTransportationTreeFromList(List list, TreeItem root) { + private void buildTransportationTreeFromList(Supplier> supply, TreeItem root) { int transportChunk = transportationChunkNr.get(); - for (int i = transportChunk * MAX_FEATURES_PER_CHUNK; i < (transportChunk + 1) * MAX_FEATURES_PER_CHUNK - && i < list.size(); i++) { - TransportationObject t = list.get(i); - TreeItem item = buildTopLevelCityObjectNode(t, root); - createTransportSectionNodes(t, item); - createTrafficSpacesNodes(t, item); - } + supply.get().skip((long) transportChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(trans ->{ + TreeItem item = buildTopLevelCityObjectNode(trans, root); + createTransportSectionNodes(trans, item); + createTrafficSpacesNodes(trans, item); + }); } - private void buildVegetationTreeFromList(List list, TreeItem root) { + private void buildVegetationTreeFromList(Supplier> supply, TreeItem root) { int vegetationChunk = vegetationChunkNr.get(); - for (int i = vegetationChunk * MAX_FEATURES_PER_CHUNK; i < (vegetationChunk + 1) * MAX_FEATURES_PER_CHUNK - && i < list.size(); i++) { - Vegetation v = list.get(i); - buildTopLevelCityObjectNode(v, root); - } + supply.get().skip((long) vegetationChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(veg ->{ + buildTopLevelCityObjectNode(veg, root); + }); + } - private void buildWaterTreeFromList(List list, TreeItem root) { + private void buildWaterTreeFromList(Supplier> supply, TreeItem root) { int waterChunk = waterChunkNr.get(); - for (int i = waterChunk * MAX_FEATURES_PER_CHUNK; i < (waterChunk + 1) * MAX_FEATURES_PER_CHUNK - && i < list.size(); i++) { - WaterObject w = list.get(i); - buildTopLevelCityObjectNode(w, root); - } + supply.get().skip((long) waterChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(wat ->{ + buildTopLevelCityObjectNode(wat, root); + }); } - private void buildCityFurnitureTreeFromList(List list, TreeItem root) { + private void buildCityFurnitureTreeFromList(Supplier> supply, TreeItem root) { int cityFurnChunk = cityFurnitureChunkNr.get(); - for (int i = cityFurnChunk * MAX_FEATURES_PER_CHUNK; i < (cityFurnChunk + 1) * MAX_FEATURES_PER_CHUNK - && i < list.size(); i++) { - CityFurniture cf = list.get(i); + supply.get().skip((long) cityFurnChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(cf ->{ buildTopLevelCityObjectNode(cf, root); - } + }); } - private void buildGenericCityObjectTreeFromList(List list, TreeItem root) { + private void buildGenericCityObjectTreeFromList(Supplier> supply, TreeItem root) { int genericObjectChunk = genericCityObjectsChunkNr.get(); - for (int i = genericObjectChunk * MAX_FEATURES_PER_CHUNK; i < (genericObjectChunk + 1) * MAX_FEATURES_PER_CHUNK - && i < list.size(); i++) { - GenericCityObject goc = list.get(i); - buildTopLevelCityObjectNode(goc, root); - } + supply.get().skip((long) genericObjectChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(gen ->{ + buildTopLevelCityObjectNode(gen, root); + }); } private void createTransportSectionNodes(TransportationObject t, TreeItem root) { @@ -733,10 +889,9 @@ public class CityDoctorController { } - private void buildTunnelTreeFromList(List list, TreeItem root) { + private void buildTunnelTreeFromList(Supplier> supply, TreeItem root) { int tunnelChunk = tunnelChunkNr.get(); - for (int i = tunnelChunk * MAX_FEATURES_PER_CHUNK; i < (tunnelChunk + 1) * MAX_FEATURES_PER_CHUNK && i < list.size(); i++) { - Tunnel tunnel = list.get(i); + supply.get().skip((long) tunnelChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(tunnel ->{ TreeItem item = buildTopLevelCityObjectNode(tunnel, root); createTunnelHollowNodes(tunnel, item); @@ -744,7 +899,8 @@ public class CityDoctorController { createTunnelFurnitureNodes(tunnel, item); createTunnelConstructiveElementsNodes(tunnel, item); createTunnelPartNodes(tunnel, item); - } + }); + } private void createTunnelInstallationNodes(Tunnel tunnel, TreeItem root) { @@ -1236,15 +1392,13 @@ public class CityDoctorController { private Map getErrorStats() { Map stats = new HashMap<>(); - addErrorStats(stats, model.getBridges()); - addErrorStats(stats, model.getBuildings()); - addErrorStats(stats, model.getLand()); - addErrorStats(stats, model.getTunnels()); - addErrorStats(stats, model.getTransportation()); - addErrorStats(stats, model.getVegetation()); - addErrorStats(stats, model.getWater()); - addErrorStats(stats, model.getCityFurniture()); - addErrorStats(stats, model.getGenericCityObjects()); + List errors = new ArrayList<>(); + model.createFeatureStream().forEach(co -> co.collectContainedErrors(errors)); + Set filteredErrors = new HashSet<>(errors); + for (CheckError error : filteredErrors) { + ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new); + stat.incrementCount(); + } return stats; } @@ -1258,6 +1412,8 @@ public class CityDoctorController { ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new); stat.incrementCount(); } + + } public void export(CityObject co) { @@ -1326,89 +1482,106 @@ public class CityDoctorController { } private void filterBuildingTree(String searchString) { - List foundBuildings = filterFeatures(searchString, model.getBuildings()); + Supplier> foundBuildings = () -> model.getBuildings(). + filter(co -> featureFilter(co, searchString)); + int featureCount = (int) foundBuildings.get().count(); TreeView buildingsView = mainWindow.getBuildingsView(); TreeItem root = buildingsView.getRoot(); root.getChildren().clear(); buildBuildingTreeFromList(foundBuildings, root); updateTree(root); - addMoreButtonToBuildingsIfNecessary(foundBuildings, buildingsView, root, buildingChunkNr); + addMoreButtonForBuildings(buildingsView, root, buildingChunkNr, foundBuildings, featureCount); } private void filterBridgeTree(String searchString) { - List foundBridges = filterFeatures(searchString, model.getBridges()); + Supplier> foundBridges = () -> model.getBridges(). + filter(co -> featureFilter(co, searchString)); + int featureCount = (int) foundBridges.get().count(); TreeView bridgeView = mainWindow.getBridgeView(); TreeItem root = bridgeView.getRoot(); root.getChildren().clear(); buildBridgeTreeFromList(foundBridges, root); updateTree(root); - addMoreButtonIfNecessary(foundBridges, bridgeView, root, bridgeChunkNr); + addMoreButtonForBridges(bridgeView, root, bridgeChunkNr, foundBridges, featureCount); } private void filterTunnelTree(String searchString) { - List foundTunnel = filterFeatures(searchString, model.getTunnels()); + Supplier> foundTunnel = () -> model.getTunnels().filter(co -> featureFilter(co, searchString)); + int featureCount = (int) foundTunnel.get().count(); TreeView tunnelView = mainWindow.getTunnelView(); TreeItem root = tunnelView.getRoot(); root.getChildren().clear(); buildTunnelTreeFromList(foundTunnel, root); updateTree(root); - addMoreButtonIfNecessary(foundTunnel, tunnelView, root, tunnelChunkNr); + addMoreButtonForTunnel(tunnelView, root, tunnelChunkNr, foundTunnel, featureCount); } private void filterVegetationTree(String searchString) { - List foundVegetation = filterFeatures(searchString, model.getVegetation()); + Supplier> foundVegetation = () -> model.getVegetation(). + filter(co -> featureFilter(co, searchString)); + int featureCount = (int) foundVegetation.get().count(); TreeView vegetationView = mainWindow.getVegetationView(); TreeItem root = vegetationView.getRoot(); root.getChildren().clear(); buildVegetationTreeFromList(foundVegetation, root); updateTree(root); - addMoreButtonIfNecessary(foundVegetation, vegetationView, root, vegetationChunkNr); + addMoreButtonForVegetation(vegetationView, root, vegetationChunkNr, foundVegetation, featureCount); } private void filterTransportationTree(String searchString) { - List foundTransportation = filterFeatures(searchString, model.getTransportation()); + Supplier> foundTransportation = () -> model.getTransportation(). + filter(co -> featureFilter(co, searchString)); + int featureCount = (int) foundTransportation.get().count(); TreeView transportationView = mainWindow.getTransportationView(); TreeItem root = transportationView.getRoot(); root.getChildren().clear(); buildTransportationTreeFromList(foundTransportation, root); updateTree(root); - addMoreButtonIfNecessary(foundTransportation, transportationView, root, transportationChunkNr); + addMoreButtonForTransportation(transportationView, root, transportationChunkNr, + foundTransportation, featureCount); } private void filterWaterTree(String searchString) { - List foundWater = filterFeatures(searchString, model.getWater()); + Supplier> foundWater = () -> model.getWater().filter(co -> featureFilter(co, searchString)); + int featureCount = (int) foundWater.get().count(); TreeView waterView = mainWindow.getWaterView(); TreeItem root = waterView.getRoot(); root.getChildren().clear(); buildWaterTreeFromList(foundWater, root); - addMoreButtonIfNecessary(foundWater, waterView, root, waterChunkNr); + addMoreButtonForWater(waterView, root, waterChunkNr, foundWater, featureCount); } private void filterLandTree(String searchString) { - List foundLand = filterFeatures(searchString, model.getLand()); + Supplier> foundLand = () -> model.getLand().filter(co -> featureFilter(co, searchString)); + int featureCount = (int) foundLand.get().count(); TreeView landView = mainWindow.getTerrainView(); TreeItem root = landView.getRoot(); root.getChildren().clear(); - buildLandFromList(foundLand, root); - addMoreButtonIfNecessary(foundLand, landView, root, landChunkNr); + buildLandTreeFromList(foundLand, root); + addMoreButtonForLand(landView, root, landChunkNr, foundLand, featureCount); } private void filterCityFurnitureTree(String searchString) { - List foundCityFurniture = filterFeatures(searchString, model.getCityFurniture()); + Supplier> foundCityFurniture = () -> model.getCityFurniture(). + filter(co -> featureFilter(co, searchString)); + int featureCount = (int) foundCityFurniture.get().count(); TreeView cityFurnitureView = mainWindow.getCityFurnitureView(); TreeItem root = cityFurnitureView.getRoot(); root.getChildren().clear(); buildCityFurnitureTreeFromList(foundCityFurniture, root); - addMoreButtonIfNecessary(foundCityFurniture, cityFurnitureView, root, cityFurnitureChunkNr); + addMoreButtonForCityFurniture(cityFurnitureView, root, cityFurnitureChunkNr, foundCityFurniture, featureCount); } private void filterGenericCityObjectTree(String searchString) { - List foundGenericCityObject = filterFeatures(searchString, model.getGenericCityObjects()); + Supplier> foundGenericCityObject = () -> model.getGenericCityObjects(). + filter(co ->featureFilter(co, searchString)); + int featureCount = (int) foundGenericCityObject.get().count(); TreeView genericCityObjectView = mainWindow.getGenericCityObjectView(); TreeItem root = genericCityObjectView.getRoot(); root.getChildren().clear(); buildGenericCityObjectTreeFromList(foundGenericCityObject, root); - addMoreButtonIfNecessary(foundGenericCityObject, genericCityObjectView, root, genericCityObjectsChunkNr); + addMoreButtonForGenericCityObjects(genericCityObjectView, root, genericCityObjectsChunkNr, + foundGenericCityObject, featureCount); } private List filterFeatures(String searchString, List features) { @@ -1421,6 +1594,10 @@ public class CityDoctorController { return foundFeatures; } + private boolean featureFilter(CityObject co, String searchString) { + return co.getGmlId().getGmlString().contains(searchString); + } + public void resetSearch(FeatureType selectedTab) { if (model == null) { return; @@ -1455,94 +1632,174 @@ public class CityDoctorController { } mainWindow.resetSearchBar(); root.getChildren().clear(); - List errorBuildings = new ArrayList<>(); - for (Building b : model.getBuildings()) { - if (b.containsAnyError()) { - errorBuildings.add(b); - } - } resetFeatureChunks(); - buildBuildingTreeFromList(errorBuildings, root); + List foundBuildings = new ArrayList<>(); + model.getBuildings().filter(Checkable::containsAnyError).forEach(co -> foundBuildings.add(co.getGmlId())); + Supplier> supply = () -> model.getCache().getStream(foundBuildings).map(co -> (Building) co); + buildBuildingTreeFromList(supply, root); updateTree(root); - addMoreButtonToBuildingsIfNecessary(errorBuildings, buildingsView, root, buildingChunkNr); + addMoreButtonForBuildings(buildingsView, root, buildingChunkNr, supply, foundBuildings.size()); } public void fillTreeViewWithErrorVegetation() { + TreeView vegetationView = mainWindow.getVegetationView(); + TreeItem root = vegetationView.getRoot(); if (model == null) { return; } - fillTreeViewWithErrorCityObjects(mainWindow.getVegetationView(), model.getVegetation(), vegetationChunkNr); + if (root == null) { + return; + } + mainWindow.resetSearchBar(); + root.getChildren().clear(); + resetFeatureChunks(); + List foundVegetation = new ArrayList<>(); + model.getVegetation().filter(Checkable::containsAnyError).forEach(co -> foundVegetation.add(co.getGmlId())); + Supplier> supply = () -> model.getCache().getStream(foundVegetation).map(co -> (Vegetation) co); + buildVegetationTreeFromList(supply, root); + updateTree(root); + addMoreButtonForVegetation(vegetationView, root, vegetationChunkNr, supply, foundVegetation.size()); } public void fillTreeViewWithErrorBridges() { + TreeView bridgeView = mainWindow.getBridgeView(); + TreeItem root = bridgeView.getRoot(); if (model == null) { return; } - fillTreeViewWithErrorCityObjects(mainWindow.getBridgeView(), model.getBridges(), bridgeChunkNr); + if (root == null) { + return; + } + mainWindow.resetSearchBar(); + root.getChildren().clear(); + resetFeatureChunks(); + List foundBridge = new ArrayList<>(); + model.getBridges().filter(Checkable::containsAnyError).forEach(co -> foundBridge.add(co.getGmlId())); + Supplier> supply = () -> model.getCache().getStream(foundBridge).map(co -> (BridgeObject) co); + buildBridgeTreeFromList(supply, root); + updateTree(root); + addMoreButtonForBridges(bridgeView, root, bridgeChunkNr, supply, foundBridge.size()); } public void fillTreeViewWithErrorLand() { + TreeView landView = mainWindow.getTerrainView(); + TreeItem root = landView.getRoot(); if (model == null) { return; } - fillTreeViewWithErrorCityObjects(mainWindow.getTerrainView(), model.getLand(), landChunkNr); + if (root == null) { + return; + } + mainWindow.resetSearchBar(); + root.getChildren().clear(); + resetFeatureChunks(); + List foundLand = new ArrayList<>(); + model.getLand().filter(Checkable::containsAnyError).forEach(co -> foundLand.add(co.getGmlId())); + Supplier> supply = () -> model.getCache().getStream(foundLand).map(co -> (CityObject) co); + buildLandTreeFromList(supply, root); + updateTree(root); + addMoreButtonForLand(landView, root, landChunkNr, supply, foundLand.size()); } public void fillTreeViewWithErrorCityFurniture() { + TreeView cityFurnitureView = mainWindow.getCityFurnitureView(); + TreeItem root = cityFurnitureView.getRoot(); if (model == null) { return; } - fillTreeViewWithErrorCityObjects(mainWindow.getCityFurnitureView(), - model.getCityFurniture(), cityFurnitureChunkNr); + if (root == null) { + return; + } + mainWindow.resetSearchBar(); + root.getChildren().clear(); + resetFeatureChunks(); + List foundCityFurniture = new ArrayList<>(); + model.getCityFurniture().filter(Checkable::containsAnyError).forEach(co -> foundCityFurniture.add(co.getGmlId())); + Supplier> supply = () -> model.getCache().getStream(foundCityFurniture).map(co -> (CityFurniture) co); + buildCityFurnitureTreeFromList(supply, root); + updateTree(root); + addMoreButtonForCityFurniture(cityFurnitureView, root, cityFurnitureChunkNr, supply, foundCityFurniture.size()); } public void fillTreeViewWithErrorGenericCityObjects() { + TreeView genericCityObjectsView = mainWindow.getGenericCityObjectView(); + TreeItem root = genericCityObjectsView.getRoot(); if (model == null) { return; } - fillTreeViewWithErrorCityObjects(mainWindow.getGenericCityObjectView(), - model.getGenericCityObjects(), genericCityObjectsChunkNr); + if (root == null) { + return; + } + mainWindow.resetSearchBar(); + root.getChildren().clear(); + resetFeatureChunks(); + List foundGenericCityObjects = new ArrayList<>(); + model.getGenericCityObjects().filter(Checkable::containsAnyError).forEach(co -> foundGenericCityObjects.add(co.getGmlId())); + Supplier> supply = () -> model.getCache().getStream(foundGenericCityObjects).map(co -> (GenericCityObject) co); + buildGenericCityObjectTreeFromList(supply, root); + updateTree(root); + addMoreButtonForGenericCityObjects(genericCityObjectsView, root, genericCityObjectsChunkNr, supply, foundGenericCityObjects.size()); } public void fillTreeViewWithErrorTransportation() { + TreeView transportationView = mainWindow.getTransportationView(); + TreeItem root = transportationView.getRoot(); if (model == null) { return; } - fillTreeViewWithErrorCityObjects(mainWindow.getTransportationView(), model.getTransportation(), - transportationChunkNr); + if (root == null) { + return; + } + mainWindow.resetSearchBar(); + root.getChildren().clear(); + resetFeatureChunks(); + List foundTransportation = new ArrayList<>(); + model.getTransportation().filter(Checkable::containsAnyError).forEach(co -> foundTransportation.add(co.getGmlId())); + Supplier> supply = () -> model.getCache().getStream(foundTransportation).map(co -> (TransportationObject) co); + buildTransportationTreeFromList(supply, root); + updateTree(root); + addMoreButtonForTransportation(transportationView, root, transportationChunkNr, supply, foundTransportation.size()); } public void fillTreeViewWithErrorTunnel() { + TreeView tunnelView = mainWindow.getTunnelView(); + TreeItem root = tunnelView.getRoot(); if (model == null) { return; } - fillTreeViewWithErrorCityObjects(mainWindow.getTunnelView(), model.getTunnels(), tunnelChunkNr); + if (root == null) { + return; + } + mainWindow.resetSearchBar(); + root.getChildren().clear(); + resetFeatureChunks(); + List foundTunnel = new ArrayList<>(); + model.getTunnels().filter(Checkable::containsAnyError).forEach(co -> foundTunnel.add(co.getGmlId())); + Supplier> supply = () -> model.getCache().getStream(foundTunnel).map(co -> (Tunnel) co); + buildTunnelTreeFromList(supply, root); + updateTree(root); + addMoreButtonForTunnel(tunnelView, root, tunnelChunkNr, supply, foundTunnel.size()); + } public void fillTreeViewWithErrorWater() { + TreeView waterView = mainWindow.getWaterView(); + TreeItem root = waterView.getRoot(); if (model == null) { return; } - fillTreeViewWithErrorCityObjects(mainWindow.getWaterView(), model.getWater(), waterChunkNr); - } - - private void fillTreeViewWithErrorCityObjects(TreeView treeView, List objects, - AtomicInteger chunkCounter) { - TreeItem root = treeView.getRoot(); if (root == null) { return; } mainWindow.resetSearchBar(); root.getChildren().clear(); - List errorObjects = new ArrayList<>(); - for (CityObject co : objects) { - if (co.containsAnyError()) { - errorObjects.add(co); - } - } - buildTreeFromList(errorObjects, root, chunkCounter); + resetFeatureChunks(); + List foundWater = new ArrayList<>(); + model.getWater().filter(Checkable::containsAnyError).forEach(co -> foundWater.add(co.getGmlId())); + Supplier> supply = () -> model.getCache().getStream(foundWater).map(co -> (WaterObject) co); + buildWaterTreeFromList(supply, root); updateTree(root); - addMoreButtonIfNecessary(errorObjects, treeView, root, chunkCounter); + addMoreButtonForWater(waterView, root, tunnelChunkNr, supply, foundWater.size()); } public void fillTreeViewFromFeatures(FeatureType selectedTab) { @@ -1584,7 +1841,7 @@ public class CityDoctorController { updateTree(mainWindow.getCityFurnitureView().getRoot()); break; case GENERIC_CITY_OBJECT: - buildOtherCityObjects(model); + buildGenericCityObjects(model); updateTree(mainWindow.getGenericCityObjectView().getRoot()); break; default: @@ -1673,6 +1930,9 @@ public class CityDoctorController { case TRANSPORTATION: fillTreeViewWithErrorTransportation(); break; + case TUNNEL: + fillTreeViewWithErrorTunnel(); + break; case WATER: fillTreeViewWithErrorWater(); break; diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java index 9709b353..705a2c28 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java @@ -13,6 +13,7 @@ import javafx.scene.paint.PhongMaterial; import javafx.scene.shape.*; import java.util.*; +import java.util.stream.Stream; public class TriangulatedGeometry { @@ -98,8 +99,8 @@ public class TriangulatedGeometry { - private static void addPolygonDataFromCityObjects(List cos, - TriangulatedGeometry triGeom, Color color, List filters) { + private static void addPolygonDataFromCityObjects(Stream cos, + TriangulatedGeometry triGeom, Color color, List filters) { CityObjectCollector collector = new CityObjectCollector(); cos.forEach(cityObject -> cityObject.accept(collector)); collector.getCityObjects().forEach(cityObject -> addPolygonData(cityObject, triGeom, color, filters)); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AddMoreButtonRunnable.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AddMoreButtonRunnable.java new file mode 100644 index 00000000..262b3090 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AddMoreButtonRunnable.java @@ -0,0 +1,68 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import de.hft.stuttgart.citydoctor2.gui.CityDoctorController; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; + +import java.util.concurrent.atomic.AtomicInteger; + + +public abstract class AddMoreButtonRunnable implements Runnable{ + + private int featureCount; + private final TreeView view; + private final TreeItem root; + private final AtomicInteger chunkCounter; + private static final int MAX_FEATURES_PER_CHUNK = CityDoctorController.getMaxFeaturesPerChunk(); + + + protected AddMoreButtonRunnable(int featureCount, TreeView view, + TreeItem root, AtomicInteger chunkCounter){ + this.featureCount = featureCount; + this.view = view; + this.root = root; + this.chunkCounter = chunkCounter; + } + + @Override + public void run() { + // 29 is the height of one item + int numberOfDisplayedItems = (int) (view.getHeight() / 29); + int selectedIndex = view.getSelectionModel().getSelectedIndex(); + // remove button node + root.getChildren().remove(root.getChildren().size() - 1); + chunkCounter.getAndIncrement(); + int visibleFeatures = countVisibleNodes(view); + buildNodeTreeCall(); + if (selectedIndex >= 0) { + view.getSelectionModel().select(selectedIndex); + } + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { + root.getChildren().add(new TreeItem<>(new ButtonRenderable(this))); + } + view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1); + + } + + /** + * Function hook for the buildTree call of the view + */ + public abstract void buildNodeTreeCall(); + + + private static int countVisibleNodes(TreeView view) { + return countVisibleNodes(view.getRoot()) + 1; + } + + private static int countVisibleNodes(TreeItem root) { + int visibleFeatures = root.getChildren().size(); + for (TreeItem child : root.getChildren()) { + if (!child.getChildren().isEmpty() && child.isExpanded()) { + visibleFeatures += countVisibleNodes(child); + } + } + return visibleFeatures; + } + +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/node/CityObjectNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/node/CityObjectNode.java index 170891fe..7d3fae37 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/node/CityObjectNode.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/node/CityObjectNode.java @@ -12,9 +12,11 @@ public class CityObjectNode extends Renderable { private static final Logger logger = LogManager.getLogger(CityObjectNode.class); private final CityObject co; + private String text; public CityObjectNode(CityObject co) { this.co = co; + this.text = co.getDisplayText(); } @Override @@ -30,7 +32,7 @@ public class CityObjectNode extends Renderable { @Override public String getText() { - return co.getDisplayText(); + return text; } @Override diff --git a/CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerController.java b/CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerController.java index 4cb6311c..61dd2710 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerController.java +++ b/CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerController.java @@ -689,9 +689,7 @@ public class HealerController { // triangle area is too small, ignore return; } - for (Building b : model.getBuildings()) { - MeshGenerator.replaceGeometryWithMeshedGeometry(b, area); - } + model.getBuildings().forEach(building -> MeshGenerator.replaceGeometryWithMeshedGeometry(building, area)); } catch (NumberFormatException e) { // no real number was inserted, do nothing } diff --git a/CityDoctorParent/Extensions/CityDoctorHealerGenetic/src/main/java/de/hft/stuttgart/citydoctor2/healer/genetic/gui/GeneticsGUI.java b/CityDoctorParent/Extensions/CityDoctorHealerGenetic/src/main/java/de/hft/stuttgart/citydoctor2/healer/genetic/gui/GeneticsGUI.java index 894e4c7b..c9a65976 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealerGenetic/src/main/java/de/hft/stuttgart/citydoctor2/healer/genetic/gui/GeneticsGUI.java +++ b/CityDoctorParent/Extensions/CityDoctorHealerGenetic/src/main/java/de/hft/stuttgart/citydoctor2/healer/genetic/gui/GeneticsGUI.java @@ -329,7 +329,7 @@ public class GeneticsGUI extends View { parserConfig); currentChecker = new Checker(model1); currentChecker.runChecks(); - Building building = model1.getBuildings().get(0); + Building building = model1.getBuildings().toList().getFirst(); currentGeometry = building.getGeometries().get(0); currentFeature = building; showCurrentGeometry(); diff --git a/CityDoctorParent/pom.xml b/CityDoctorParent/pom.xml index 7067d3e8..e5308cae 100644 --- a/CityDoctorParent/pom.xml +++ b/CityDoctorParent/pom.xml @@ -298,7 +298,7 @@ Extensions/CityDoctorGUI - Extensions/CityDoctorAutoPro + Extensions/CityDoctorHealer Extensions/CityDoctorHealerGenetic Extensions/CityDoctorHealerGUI -- GitLab From e178e81eae8ae654d3871257d912e19327007738 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 14 Oct 2025 16:27:49 +0200 Subject: [PATCH 038/118] Refactor: Connect GeoDB to internal model --- .../citydoctor2/database/CacheManager.java | 21 +++ .../citydoctor2/database/CityObjectCache.java | 22 +++ .../citydoctor2/database/DatabaseHandler.java | 100 ++++++++----- .../citydoctor2/database/FeatureCache.java | 79 ++++------ .../database/UnconnectedCache.java | 47 ++++++ .../datastructure/BoundingBox.java | 22 ++- .../datastructure/CityDoctorModel.java | 14 +- .../datastructure/ImplicitGeometryHolder.java | 6 +- .../datastructure/LibraryObject.java | 6 +- .../citygml3/Citygml3FeatureMapper.java | 17 ++- .../citydoctor2/parser/CityGmlParser.java | 135 ++++++++++++++---- .../citydoctor2/zip/CityGmlZipEntry.java | 14 +- .../database/DataBaseHandlerTest.java | 77 +++++----- .../database/FeatureCacheTest.java | 21 +-- .../database/SerializationTest.java | 4 +- .../datastructure/CityDoctorModelTest.java | 76 +++++----- .../datastructure/CompositePolygonTest.java | 2 +- .../citydoctor2/mapper/FeatureMapperTest.java | 32 ++--- .../parser/CityGml3ParserTest.java | 2 +- .../stuttgart/citydoctor2/zip/ZipTest.java | 2 +- .../citydoctor2/check/CheckerTest.java | 4 +- .../citydoctor2/gui/CityDoctorController.java | 3 +- 22 files changed, 449 insertions(+), 257 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java new file mode 100644 index 00000000..ecde3ea4 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -0,0 +1,21 @@ +package de.hft.stuttgart.citydoctor2.database; + +public class CacheManager { + + private static FeatureCache cache = null; + + public static CityObjectCache getFeatureCache(String filename) { + if (cache == null){ + cache = new FeatureCache(filename); + } else { + cache.changeFileName(filename); + } + return cache; + } + + public static CityObjectCache getUnconnectedCache(){ + return new UnconnectedCache(); + } + + +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java new file mode 100644 index 00000000..bc949ec3 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -0,0 +1,22 @@ +package de.hft.stuttgart.citydoctor2.database; + +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; + +import java.util.List; +import java.util.stream.Stream; + +public interface CityObjectCache { + + + void put(CityObject cityObject); + + CityObject get(GmlId id); + + void replace(GmlId id, CityObject cityObject); + + Stream getStream(List ids); + + int getFeatureCount(); + +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 924e94e2..dbdccd97 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -5,6 +5,8 @@ import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import javafx.scene.control.Alert; +import org.apache.commons.lang3.SerializationException; import org.apache.commons.lang3.SerializationUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -25,39 +27,49 @@ public class DatabaseHandler { private static final Logger logger = LogManager.getLogger(DatabaseHandler.class); private static final String DB_NAME = "cd_db"; + private final String fileName; + static{ + // Creates the Database at initialization + try { + H2GISDBFactory.createSpatialDataBase(DB_NAME, + true, ";FILE_LOCK=SOCKET"); - private DatabaseHandler() { + } catch (Exception e) { + logger.fatal("FATAL ERROR: Creation of embedded database failed!"); + logger.fatal(e.getMessage()); + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("FATAL ERROR"); + alert.setHeaderText("FATAL ERROR"); + alert.setContentText("Creation of embedded database failed!"); + alert.showAndWait(); + } } + public DatabaseHandler(String fileName){ + this.fileName = fileName; + setupFeaturesTable(); + } /** * Sets up a new, empty embedded database. Will override an existing embedded database. */ public static void setupDatabase() { - try { - H2GISDBFactory.createSpatialDataBase(DB_NAME, - true, ";FILE_LOCK=SOCKET"); - setupFeaturesTable(); - } catch (Exception e) { - logger.fatal("FATAL ERROR: Creation of embedded database failed!"); - logger.fatal(e.getMessage()); - // TODO: implement popup and shutdown - } + } - public static void setupFeaturesTable() { + public void setupFeaturesTable() { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement dropPs = con.prepareStatement("DROP TABLE IF EXISTS features")) { + try (PreparedStatement dropPs = con.prepareStatement(String.format("DROP TABLE IF EXISTS %s", fileName))) { dropPs.executeUpdate(); if (logger.isDebugEnabled()) { logger.debug("Dropped existing features table"); } } - try (PreparedStatement createPs = con.prepareStatement("CREATE TABLE features (gmlid VARCHAR(255) PRIMARY KEY," + - "bbox GEOMETRY, data BLOB, type VARCHAR(255) NOT NULL)")) { + try (PreparedStatement createPs = con.prepareStatement(String.format("CREATE TABLE %s (gmlid VARCHAR(255)" + + " PRIMARY KEY, bbox GEOMETRY, data BLOB, type VARCHAR(255) NOT NULL)", fileName))) { createPs.executeUpdate(); if (logger.isDebugEnabled()) { logger.debug("Created features table"); @@ -74,9 +86,9 @@ public class DatabaseHandler { * Saves a CityObject to the embedded database, updating an existing entry if it exists. * @param co the CityObject */ - public static void marshallCityObject(CityObject co) { + public void marshallCityObject(CityObject co) { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?, ?)")) { + try (PreparedStatement ps = con.prepareStatement(String.format("MERGE INTO %s VALUES (?, ?, ?, ?)", fileName))) { ps.setString(1, co.getGmlId().toString()); String bbox = BoundingBox.of(co).to2DWkt(); ps.setString(2, bbox); @@ -84,14 +96,20 @@ public class DatabaseHandler { logger.debug("Attempting to serialize CityObject: {} ", co); } ps.setString(4, co.getClass().getName()); - byte[] bytes = SerializationUtils.serialize(co); - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - ps.setBinaryStream(3, bis, bytes.length); - ps.executeUpdate(); + try { + byte[] bytes = SerializationUtils.serialize(co); + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ps.setBinaryStream(3, bis, bytes.length); + ps.executeUpdate(); + } catch (SerializationException e) { + logger.error(e.getMessage()); + logger.error(co.getGmlId()); + + } } } catch (SQLException e) { - logger.error("Saving of city object to embedded database failed!"); - logger.debug(e.getMessage()); + logger.error("Saving of city object {} to embedded database failed!", co.getGmlId()); + logger.error(e.getMessage()); } } @@ -100,9 +118,10 @@ public class DatabaseHandler { * @param id the gmlId of the requested Feature * @return the Feature, or null if no entry was found */ - public static CityObject unmarshallCityObject(GmlId id) { + public CityObject unmarshallCityObject(GmlId id) { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features WHERE gmlid = ?")) { + try (PreparedStatement ps = con.prepareStatement(String.format( + "SELECT gmlid, bbox, data, type FROM %s WHERE gmlid = ?", fileName))) { ps.setString(1, id.toString()); ResultSet rs = ps.executeQuery(); if (rs.next()) { @@ -129,10 +148,10 @@ public class DatabaseHandler { return null; } - public static List getAllCityObjects() { + public List getAllCityObjects() { List cos = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data, type FROM features")) { + try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid, bbox, data, type FROM %s", fileName))) { ResultSet rs = ps.executeQuery(); cos.addAll(retrieveCityObjects(rs)); } @@ -143,10 +162,11 @@ public class DatabaseHandler { return cos; } - public static List getAllGmlIds() { + public List getAllGmlIds() { List cos = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features")) { + try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid FROM %s", fileName))) { + ps.setString(1, fileName); ResultSet rs = ps.executeQuery(); while (rs.next()) { cos.add(new GmlId(rs.getString("gmlid"))); @@ -159,15 +179,16 @@ public class DatabaseHandler { return cos; } - public static List getBufferIntersectingObjects(CityObject co, double bufferSize) { + public List getBufferIntersectingObjects(CityObject co, double bufferSize) { return getBufferIntersectingObjects(co.getGmlId(), bufferSize); } - public static List getBufferIntersectingObjects(GmlId gmlID, double bufferSize) { + public List getBufferIntersectingObjects(GmlId gmlID, double bufferSize) { List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + - " WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { + try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid FROM %s AS tab1" + + " WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM %s WHERE gmlid = ?)) " + + "AND tab1.gmlid <> ?", fileName, fileName))) { ps.setDouble(1, bufferSize); ps.setString(2, gmlID.toString()); ps.setString(3, gmlID.toString()); @@ -183,16 +204,17 @@ public class DatabaseHandler { return intersectingObjects; } - public static List getBBoxIntersectingObjects(CityObject co) { + public List getBBoxIntersectingObjects(CityObject co) { return getBBoxIntersectingObjects(co.getGmlId()); } - public static List getBBoxIntersectingObjects(GmlId id) { + public List getBBoxIntersectingObjects(GmlId id) { List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + - " WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { + try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid FROM %s AS tab1" + + " WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM %s WHERE gmlid = ?)) AND tab1.gmlid <> ?", + fileName, fileName))) { ps.setString(1, id.toString()); ps.setString(2, id.toString()); ResultSet rs = ps.executeQuery(); @@ -208,11 +230,11 @@ public class DatabaseHandler { } - public static int getFeatureCount() { + public int getFeatureCount() { int count = -1; try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(gmlid) AS nr FROM features")) { + try (PreparedStatement ps = con.prepareStatement(String.format("SELECT COUNT(gmlid) AS nr FROM %s", fileName))) { ResultSet rs = ps.executeQuery(); if (rs.next()) { count = rs.getInt("nr"); @@ -225,7 +247,7 @@ public class DatabaseHandler { return count; } - private static List retrieveCityObjects(ResultSet rs) throws SQLException { + private List retrieveCityObjects(ResultSet rs) throws SQLException { List cos = new ArrayList<>(); while (rs.next()) { try (InputStream is = rs.getBinaryStream("data")) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index b73b2859..c269e482 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -1,95 +1,68 @@ package de.hft.stuttgart.citydoctor2.database; -import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.RemovalCause; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; -import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Stream; /** * Cache for CityObjects */ -public class FeatureCache{ +public class FeatureCache implements CityObjectCache{ private final LoadingCache cache; + private DatabaseHandler handler; + private final Map handlerList = new HashMap<>(); - private final Cache subCache; - public FeatureCache() { + FeatureCache(String fileName) { + handler = new DatabaseHandler(fileName); + handler.setupFeaturesTable(); cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> { if (value != null && cause.wasEvicted()) { - DatabaseHandler.marshallCityObject(value); + handler.marshallCityObject(value); } - }).build(DatabaseHandler::unmarshallCityObject); - subCache = Caffeine.newBuilder().maximumSize(250).build(); - DatabaseHandler.setupDatabase(); + }).build(handler::unmarshallCityObject); } + @Override public void put(CityObject cityObject) { - DatabaseHandler.marshallCityObject(cityObject); + handler.marshallCityObject(cityObject); cache.put(cityObject.getGmlId(), cityObject); } + @Override public CityObject get(GmlId id) { return cache.get(id); } + public void changeFileName(String fileName){ + handler = handlerList.computeIfAbsent(fileName, k -> { + DatabaseHandler handler1 = new DatabaseHandler(fileName); + handler1.setupFeaturesTable(); + return handler1; + }); + } + + @Override public void replace(GmlId id, CityObject cityObject) { cache.invalidate(id); put(cityObject); } + @Override public Stream getStream(List gmlIds) { return gmlIds.stream().map(this::get); } - /** - * Gets a non-top-level CityObject directly. Only use this in situations where directly accessing the object is required - * and access via the top-level CityObject is impossible or highly inefficient, e.g. rendering the associated object - * of a CityObjectNode. - * @param coa the adapter object of the subpart - * @return the associated CityObject - */ - public CityObject getSubPart(SubPartAdapter coa) { - CityObject co = subCache.getIfPresent(coa.getFeatureId()); - if (co == null) { - return buildAndGetSubPart(coa); - } - else return co; - } - - /** - * - * @param coa - * @return - */ - private CityObject buildAndGetSubPart(SubPartAdapter coa) { - CityObject parent = cache.get(coa.getTopLevelId()); - if (parent == null) { - throw new IllegalStateException("CityObjectAdapter has non-existent parent - GmlId: " + coa.getTopLevelId()); - } - CityObjectCollector collector = new CityObjectCollector(); - parent.accept(collector); - collector.getCityObjects().forEach(cityObject -> subCache.put(cityObject.getGmlId(), cityObject)); - CityObject co = subCache.getIfPresent(coa.getFeatureId()); - if (co == null) { - throw new IllegalStateException("CityObjectAdapter has non-existent feature - GmlId: " + coa.getFeatureId() + - ", parent GmlId: " + coa.getTopLevelId()); - } - return co; - } - - public List get(List ids) { - List features = new ArrayList<>(); - for (GmlId id : ids) { - features.add(cache.get(id)); - } - return features; + @Override + public int getFeatureCount() { + return handler.getFeatureCount(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java new file mode 100644 index 00000000..f346ec8c --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java @@ -0,0 +1,47 @@ +package de.hft.stuttgart.citydoctor2.database; + +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +/** + * A lightweight, unmanaged CityObjectCache that is not connected with the embedded Database. + */ +public class UnconnectedCache implements CityObjectCache{ + + private final Map cache; + + UnconnectedCache() { + cache = new HashMap<>(); + + } + + @Override + public void put(CityObject cityObject) { + cache.put(cityObject.getGmlId(), cityObject); + } + + @Override + public CityObject get(GmlId id) { + return cache.get(id); + } + + @Override + public void replace(GmlId id, CityObject cityObject) { + // Not needed + } + + @Override + public Stream getStream(List ids) { + return cache.values().stream().filter(c -> ids.contains(c.getGmlId())); + } + + @Override + public int getFeatureCount() { + return cache.size(); + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java index c8b87d8a..2d37f4af 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java @@ -170,11 +170,27 @@ public class BoundingBox { } /** - * Returns a WKT polygon representation in 2D of this bbox - * - * @return WKT String of this bbox's shadow + * Returns whether this BoundinBox is undefined, meaning that one or all corners are infinite. + * @return True if the BoundingBox is undefined, false otherwise + */ + public boolean isUndefined(){ + double x1 = bbox[0].getX(); + double y1 = bbox[0].getY(); + double x2 = bbox[1].getX(); + double y2 = bbox[1].getY(); + return (Double.isInfinite(x1) || Double.isInfinite(y1) || Double.isInfinite(x2) || Double.isInfinite(y2)); + } + + /** + * Returns an axis-aligned 2D polygon projection of this bbox as a WKT String. + *

+ * An undefined bbox will return an empty polygon. + * @return The 2D polygon WKT for this bbox */ public String to2DWkt() { + if (this.isUndefined()){ + return "POLYGON EMPTY"; + } double x1 = bbox[0].getX(); double y1 = bbox[0].getY(); double x2 = bbox[1].getX(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 2f852155..829b1352 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -21,11 +21,13 @@ package de.hft.stuttgart.citydoctor2.datastructure; import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; -import de.hft.stuttgart.citydoctor2.database.FeatureCache; +import de.hft.stuttgart.citydoctor2.database.CacheManager; +import de.hft.stuttgart.citydoctor2.database.CityObjectCache; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.writer.CityGMLWriterUtils; import de.hft.stuttgart.quality.model.types.ValidationPlan; +import org.apache.commons.io.FilenameUtils; import org.citygml4j.core.model.CityGMLVersion; import org.citygml4j.core.model.core.CityModel; @@ -64,9 +66,13 @@ public class CityDoctorModel { private boolean isValidated = false; private ValidationPlan plan; private CityGMLVersion cityGMLVersion; - private final FeatureCache cache; + private final CityObjectCache cache; public CityDoctorModel(ParserConfiguration config, File file) { + this(config, file, CacheManager.getFeatureCache(FilenameUtils.removeExtension(file.getName()))); + } + + public CityDoctorModel(ParserConfiguration config, File file, CityObjectCache cache) { if (config == null) { throw new IllegalArgumentException("Parser configuration may not be null"); } @@ -83,7 +89,7 @@ public class CityDoctorModel { cityfurniture = new ArrayList<>(); genericObjects = new ArrayList<>(); globalErrors = new ArrayList<>(); - cache = new FeatureCache(); + this.cache = cache; } public boolean isValidated() { @@ -368,7 +374,7 @@ public class CityDoctorModel { return cModel; } - public FeatureCache getCache(){ + public CityObjectCache getCache(){ return cache; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java index 6a01fa84..22c43541 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java @@ -6,6 +6,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; +import de.hft.stuttgart.citydoctor2.database.CityObjectCache; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.core.ImplicitGeometry; @@ -51,8 +52,11 @@ public class ImplicitGeometryHolder extends Geometry { return igh; } - public static void resolveDeferredObjects() { + public static void resolveDeferredObjects(CityObjectCache cache) { Set missingGeoms = new HashSet<>(); + if(!deferredRelGeomObjects.isEmpty()) { + logger.info("Resolving deferred relative geometries"); + } for (ImplicitGeometryHolder key : deferredRelGeomObjects.keySet()) { ImplicitGeometry ig = deferredRelGeomObjects.get(key); String hrefId = ig.getRelativeGeometry().getHref(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java index f757da76..62fe94ed 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java @@ -27,7 +27,7 @@ public class LibraryObject extends Geometry { @Serial private static final long serialVersionUID = -50293435187454911L; - private static Map libraryObjects = new ConcurrentHashMap<>(); + private static final Map libraryObjects = new ConcurrentHashMap<>(); public static LibraryObject of(Path path, ParserConfiguration config) { if (libraryObjects.containsKey(path.toString())) { @@ -69,7 +69,7 @@ public class LibraryObject extends Geometry { Geometry geo = null; if (Files.exists(path)) { try { - geo = getProtoGeometry(CityGmlParser.parseCityGmlFileSilently(path.toString(), config)); + geo = getProtoGeometry(CityGmlParser.nestedCityGmlFileParse(path.toString(), config)); } catch (CityGmlParseException e) { logger.error(String.format( "Encountered an error while parsing library object %s", path)); @@ -86,7 +86,7 @@ public class LibraryObject extends Geometry { private static Geometry parseZipEntry(CityGmlZipEntry entry, ParserConfiguration config) { Geometry geo = null; try { - entry.loadEntry(config); + entry.loadEntry(config, true); geo = getProtoGeometry(entry.getModel()); } catch (InvalidGmlFileException e) { logger.error(e.getStackTrace()); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index ade1f2bb..1978e4fb 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import de.hft.stuttgart.citydoctor2.database.CacheManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.CityGMLVersion; @@ -173,19 +174,27 @@ public class Citygml3FeatureMapper extends ObjectWalker { private List references = new ArrayList<>(); private Map vertexMap = new HashMap<>(); - public Citygml3FeatureMapper(ParserConfiguration config, Path path) { + public Citygml3FeatureMapper(ParserConfiguration config, Path path, boolean nested) { this.config = config; this.directory = path.getParent(); - model = new CityDoctorModel(config, path.toFile()); + if (nested){ + model = new CityDoctorModel(config, path.toFile(), CacheManager.getUnconnectedCache()); + } else { + model = new CityDoctorModel(config, path.toFile()); + } neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces()); zipEntry = null; } - public Citygml3FeatureMapper(ParserConfiguration config, CityGmlZipEntry entry) { + public Citygml3FeatureMapper(ParserConfiguration config, CityGmlZipEntry entry, boolean nested) { this.config = config; this.directory = null; this.zipEntry = entry; - model = new CityDoctorModel(config, Path.of(entry.getEntrySubPath()).toFile()); + if (nested){ + model = new CityDoctorModel(config, Path.of(entry.getEntrySubPath()).toFile(), CacheManager.getUnconnectedCache()); + } else { + model = new CityDoctorModel(config, Path.of(entry.getEntrySubPath()).toFile()); + } neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces()); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index ec7d839f..bbf87402 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -30,6 +30,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -167,24 +168,102 @@ public class CityGmlParser { } return context; } + //TODO: Implement resolving of library objects with database - public static CityDoctorModel parseCityGmlFileSilently(String file, ParserConfiguration config) + /** + * Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. + *

+ * The nested parse uses an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache}, which + * is not connected to the database. + *

+ * Nested parsing should only be used for accessing Features in auxiliary, small CityGML files, e.g. parsing of + * {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject LibraryObject} files. + * + * @param filePath String path to the file + * @param config Configuration of the parser + * @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 nestedCityGmlFileParse(String filePath, ParserConfiguration config) throws CityGmlParseException, InvalidGmlFileException { - return parseCityGmlFile(file, config, null, null, false); + return parseCityGmlFile(filePath, config, null, null, false, true); } - public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config) + /** + * Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. + *

+ * Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references + * to {@link CityObject} are to be avoided, to allow for the memory management of the cache. + * + * @param filePath String path to the file + * @param config Configuration of the parser + * @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) throws CityGmlParseException, InvalidGmlFileException { - return parseCityGmlFile(file, config, null, null, true); + return parseCityGmlFile(filePath, config, null, null); } - public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config, ProgressListener l) + + /** + * Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. + *

+ * Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references + * to {@link CityObject} are to be avoided, to allow for the memory management of the cache. + * + * @param filePath String path to the file + * @param config Configuration of the parser + * @param l Listener hook for tracking of parsing progress + * @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, ProgressListener l) throws CityGmlParseException, InvalidGmlFileException { - return parseCityGmlFile(file, config, l, null, true); + return parseCityGmlFile(filePath, config, l, null); } + /** + * Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. + *

+ * Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references + * to {@link CityObject} are to be avoided, to allow for the memory management of the cache. + * + * @param filePath String path to the file + * @param config Configuration of the parser + * @param l Listener hook for tracking of parsing progress + * @param handler Validation handler for GML-file validation + * @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, ProgressListener l, - GMLValidationHandler handler, boolean verbose) throws CityGmlParseException, InvalidGmlFileException { + GMLValidationHandler handler) throws CityGmlParseException, InvalidGmlFileException { + return parseCityGmlFile(filePath, config, l, handler, true, false); + } + + /** + * Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. + *

+ * Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references + * to {@link CityObject} are to be avoided, to allow for the memory management of the cache. + * + * @param filePath String path to the file + * @param config Configuration of the parser + * @param l Listener hook for tracking of parsing progress + * @param handler Validation handler for GML-file validation + * @param verbose If verbosity is enabled the parser will write info logs to the logger + * @param nested If nested is enabled the parser will use an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache} + * @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 + */ + private static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l, + GMLValidationHandler handler, boolean verbose, boolean nested) + throws CityGmlParseException, InvalidGmlFileException { CityGMLContext context = getContext(); Path file = Paths.get(filePath); if (config.getValidate()) { @@ -202,7 +281,7 @@ public class CityGmlParser { if (l != null) { ois.addListener(l::updateProgress); } - return readAndKeepFeatures(config, file, in, ois, verbose); + return readAndKeepFeatures(config, file, in, ois, verbose, nested); } } catch (CityGMLReadException | IOException e) { throw new CityGmlParseException("Failed to read CityGML file", e); @@ -210,12 +289,13 @@ public class CityGmlParser { } - public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config) + public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l) throws CityGmlParseException, InvalidGmlFileException, IOException { - return parseCityGmlZipEntry(entry, config, null); + return parseCityGmlZipEntry(entry, config, l, false); } - public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l) + public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, + ProgressListener l, boolean nested) throws CityGmlParseException, InvalidGmlFileException, IOException { CityGMLContext context = getContext(); @@ -230,15 +310,12 @@ public class CityGmlParser { } } - return decompressAndParseCityGmlEntry(entry, config, l, context); + return decompressAndParseCityGmlEntry(entry, config, l, context, nested); } - public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, CityGMLContext context) - throws CityGmlParseException { - return decompressAndParseCityGmlEntry(entry, config, null, context); - } - public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, CityGMLContext context) + public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, + ProgressListener l, CityGMLContext context, boolean nested) throws CityGmlParseException { try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { @@ -250,7 +327,7 @@ public class CityGmlParser { if (l != null) { ois.addListener(l::updateProgress); } - return readAndKeepFeatures(config, entry, in, ois, false); + return readAndKeepFeatures(config, entry, in, ois, false, nested); } } catch (CityGMLReadException | IOException e) { throw new CityGmlParseException("Failed to read CityGML file", e); @@ -260,8 +337,10 @@ public class CityGmlParser { } private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, CityGmlZipEntry entry, - CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException { - return readAndKeepModel(new Citygml3FeatureMapper(config, entry), inputFactory, ois, verbose); + CityGMLInputFactory inputFactory, ObservedInputStream ois, + boolean verbose, boolean nested) + throws CityGMLReadException { + return readAndKeepModel(new Citygml3FeatureMapper(config, entry, nested), inputFactory, ois, verbose); } private static List validateStream(InputStream vis, CityGMLContext context) throws CityGmlParseException { @@ -374,7 +453,7 @@ public class CityGmlParser { private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois, CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { - Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file); + Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file, false); readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); } @@ -395,7 +474,7 @@ public class CityGmlParser { private static void streamAndDiscardFeatures(CityGmlZipEntry entry, ParserConfiguration config, ObservedInputStream ois, CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { - Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, entry); + Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, entry, false); readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); } @@ -485,8 +564,9 @@ public class CityGmlParser { } private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file, - CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException { - return readAndKeepModel(new Citygml3FeatureMapper(config, file), inputFactory, ois, verbose); + CityGMLInputFactory inputFactory, ObservedInputStream ois, + boolean verbose, boolean nested) throws CityGMLReadException { + return readAndKeepModel(new Citygml3FeatureMapper(config, file, nested), inputFactory, ois, verbose); } private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory, @@ -518,10 +598,13 @@ public class CityGmlParser { CityModel cModel = mapper.getModel().getCityModel(); // remove those that should have been parsed - List parsedCityObjects = mapper.getModel().createFeatureStream() + logger.info("Getting parsed object from CityDoctorModel"); + List parsedCityObjects = mapper.getModel().createFeatureStream().filter(Objects::nonNull) .map(CityObject::getGmlObject).toList(); + logger.info("Removing all parsed city objects from acos"); acos.removeAll(parsedCityObjects); // re-add all not parsed objects + logger.info("Resolving HRefs"); for (AbstractCityObject aco : acos) { cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); } @@ -530,7 +613,7 @@ public class CityGmlParser { mapper.getModel().getNumberOfFeatures()); } // After parsing is complete, try to resolve deferred RelativeGeometries - ImplicitGeometryHolder.resolveDeferredObjects(); + ImplicitGeometryHolder.resolveDeferredObjects(mapper.getModel().getCache()); mapper.setCityGMLVersion(version); return mapper.getModel(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java index d9a86770..81f8f122 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java @@ -46,7 +46,15 @@ public class CityGmlZipEntry implements Serializable { * @param config Parser configuration for this model */ public void loadEntry(ParserConfiguration config) { - loadEntry(config, null); + loadEntry(config, null, false); + } + + public void loadEntry(ParserConfiguration config, boolean nested) { + loadEntry(config, null, nested); + } + + public void loadEntry(ParserConfiguration config, ProgressListener progressListener) { + loadEntry(config, progressListener, false); } /** @@ -58,7 +66,7 @@ public class CityGmlZipEntry implements Serializable { * @param config Parser configuration for this model * @param l ProgressListener for tracking parsing progress */ - public void loadEntry(ParserConfiguration config, ProgressListener l) { + public void loadEntry(ParserConfiguration config, ProgressListener l, boolean nested) { if (inMemory) { return; } @@ -67,7 +75,7 @@ public class CityGmlZipEntry implements Serializable { return; } try { - this.model = CityGmlParser.parseCityGmlZipEntry(this, config, l); + this.model = CityGmlParser.parseCityGmlZipEntry(this, config, l, nested); this.inMemory = true; } catch (CityGmlParseException | InvalidGmlFileException e) { logger.error(e); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index 6b28f175..1d6aa10e 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -18,7 +18,6 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import org.citygml4j.core.model.building.AbstractBuilding; import org.h2gis.functions.factory.H2GISDBFactory; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; @@ -33,15 +32,13 @@ import static org.junit.Assert.fail; public class DataBaseHandlerTest { - @BeforeClass - public static void setUpBeforeClass() { - DatabaseHandler.setupDatabase(); - } + private DatabaseHandler handler; @Before public void resetDatabase() { try { - DatabaseHandler.setupFeaturesTable(); + handler = new DatabaseHandler("test"); + handler.setupFeaturesTable(); } catch (Exception e) { fail(e.getMessage()); } @@ -63,14 +60,14 @@ public class DataBaseHandlerTest { Building b3 = new Building(); b3.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD3)); - DatabaseHandler.marshallCityObject(b); - DatabaseHandler.marshallCityObject(b2); - DatabaseHandler.marshallCityObject(b3); - assertEquals(3, DatabaseHandler.getFeatureCount()); + handler.marshallCityObject(b); + handler.marshallCityObject(b2); + handler.marshallCityObject(b3); + assertEquals(3, handler.getFeatureCount()); - List objects = DatabaseHandler.getAllCityObjects(); + List objects = handler.getAllCityObjects(); assertEquals(3, objects.size()); - assertEquals(3, DatabaseHandler.getFeatureCount()); + assertEquals(3, handler.getFeatureCount()); } @@ -139,17 +136,17 @@ public class DataBaseHandlerTest { geom3.updateEdgesAndVertices(); b3.addGeometry(geom3); - DatabaseHandler.marshallCityObject(b); - DatabaseHandler.marshallCityObject(b2); - DatabaseHandler.marshallCityObject(b3); - DatabaseHandler.marshallCityObject(b4); - assertEquals(4, DatabaseHandler.getFeatureCount()); + handler.marshallCityObject(b); + handler.marshallCityObject(b2); + handler.marshallCityObject(b3); + handler.marshallCityObject(b4); + assertEquals(4, handler.getFeatureCount()); - List objects = DatabaseHandler.getBBoxIntersectingObjects(b); + List objects = handler.getBBoxIntersectingObjects(b); assertEquals(1, objects.size()); - assertEquals(4, DatabaseHandler.getFeatureCount()); + assertEquals(4, handler.getFeatureCount()); - CityObject co = DatabaseHandler.unmarshallCityObject(objects.get(0)); + CityObject co = handler.unmarshallCityObject(objects.get(0)); assertNotNull(co); assertCityObjectEquality(b2, co); @@ -220,17 +217,17 @@ public class DataBaseHandlerTest { geom3.updateEdgesAndVertices(); b3.addGeometry(geom3); - DatabaseHandler.marshallCityObject(b); - DatabaseHandler.marshallCityObject(b2); - DatabaseHandler.marshallCityObject(b3); - DatabaseHandler.marshallCityObject(b4); - assertEquals(4, DatabaseHandler.getFeatureCount()); + handler.marshallCityObject(b); + handler.marshallCityObject(b2); + handler.marshallCityObject(b3); + handler.marshallCityObject(b4); + assertEquals(4, handler.getFeatureCount()); - List objects = DatabaseHandler.getBufferIntersectingObjects(b, Math.sqrt(2)); + List objects = handler.getBufferIntersectingObjects(b, Math.sqrt(2)); assertEquals(1, objects.size()); - assertEquals(4, DatabaseHandler.getFeatureCount()); + assertEquals(4, handler.getFeatureCount()); - CityObject co = DatabaseHandler.unmarshallCityObject(objects.get(0)); + CityObject co = handler.unmarshallCityObject(objects.get(0)); assertNotNull(co); assertCityObjectEquality(b2, co); @@ -243,15 +240,15 @@ public class DataBaseHandlerTest { ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS.gml", config); - Building b = model.getBuildings().get(0); + Building b = model.getBuildings().toList().get(0); assertNotNull(b); - assertEquals(0, DatabaseHandler.getFeatureCount()); - DatabaseHandler.marshallCityObject(b); - assertEquals(1, DatabaseHandler.getFeatureCount()); + assertEquals(0, handler.getFeatureCount()); + handler.marshallCityObject(b); + assertEquals(1, handler.getFeatureCount()); - CityObject b2 = DatabaseHandler.unmarshallCityObject(b.getGmlId()); + CityObject b2 = handler.unmarshallCityObject(b.getGmlId()); assertNotNull(b2); - assertEquals(1, DatabaseHandler.getFeatureCount()); + assertEquals(1, handler.getFeatureCount()); assertCityObjectEquality(b, b2); @@ -268,15 +265,15 @@ public class DataBaseHandlerTest { ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/FZK_haus.gml", config); - Building b = model.getBuildings().get(0); + Building b = model.getBuildings().toList().get(0); assertNotNull(b); - assertEquals(0, DatabaseHandler.getFeatureCount()); - DatabaseHandler.marshallCityObject(b); - assertEquals(1, DatabaseHandler.getFeatureCount()); + assertEquals(0, handler.getFeatureCount()); + handler.marshallCityObject(b); + assertEquals(1, handler.getFeatureCount()); - CityObject b2 = DatabaseHandler.unmarshallCityObject(b.getGmlId()); + CityObject b2 = handler.unmarshallCityObject(b.getGmlId()); assertNotNull(b2); - assertEquals(1, DatabaseHandler.getFeatureCount()); + assertEquals(1, handler.getFeatureCount()); assertCityObjectEquality(b, b2); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java index 773adcbb..ea2008c4 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java @@ -5,40 +5,25 @@ import de.hft.stuttgart.citydoctor2.datastructure.GeometryTestUtils; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; import de.hft.stuttgart.citydoctor2.datastructure.Lod; import org.citygml4j.core.model.building.AbstractBuilding; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; public class FeatureCacheTest { - @BeforeClass - public static void setUpBeforeClass() { - DatabaseHandler.setupDatabase(); - } - @Before - public void resetDatabase() { - try { - DatabaseHandler.setupFeaturesTable(); - } catch (Exception e) { - fail(e.getMessage()); - } - } @Test public void testPut(){ + FeatureCache cache = new FeatureCache("test"); Building b = new Building(); b.setGmlObject(mock(AbstractBuilding.class)); b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); - assertEquals(0, DatabaseHandler.getFeatureCount()); - FeatureCache cache = new FeatureCache(); + assertEquals(0, cache.getFeatureCount()); cache.put(b); - assertEquals(1, DatabaseHandler.getFeatureCount()); + assertEquals(1, cache.getFeatureCount()); } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/SerializationTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/SerializationTest.java index d6f18376..d664d660 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/SerializationTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/SerializationTest.java @@ -36,7 +36,7 @@ public class SerializationTest { public void testRoundtripSimpleSolid() throws CityGmlParseException, InvalidGmlFileException { ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS.gml", config); - Building b = model.getBuildings().get(0); + Building b = model.getBuildings().toList().get(0); assertNotNull(b); Building b2 = SerializationUtils.roundtrip(b); assertNotNull(b2); @@ -49,7 +49,7 @@ public class SerializationTest { public void testRoundtripFzk() throws CityGmlParseException, InvalidGmlFileException { ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/FZK_haus.gml", config); - Building b = model.getBuildings().get(0); + Building b = model.getBuildings().toList().get(0); assertNotNull(b); Building b2 = SerializationUtils.roundtrip(b); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java index 7eeee289..0cad81c9 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java @@ -147,7 +147,7 @@ public class CityDoctorModelTest { assertTrue(saveFile.length() > 0); CityDoctorModel parsedModel = CityGmlParser.parseCityGmlFile(saveFile.getAbsolutePath(), config); - Building parsedBuilding = parsedModel.getBuildings().get(0); + Building parsedBuilding = parsedModel.getBuildings().toList().get(0); assertEquals("testId", parsedBuilding.getGmlId().getGmlString()); assertEquals(1, parsedBuilding.getGeometries().size()); } @@ -214,7 +214,7 @@ public class CityDoctorModelTest { assertTrue(saveFile.length() > 0); CityDoctorModel parsedModel = CityGmlParser.parseCityGmlFile(saveFile.getAbsolutePath(), config); - Building parsedBuilding = parsedModel.getBuildings().get(0); + Building parsedBuilding = parsedModel.getBuildings().toList().get(0); assertEquals("testId", parsedBuilding.getGmlId().getGmlString()); assertEquals(1, parsedBuilding.getGeometries().size()); List props = parsedBuilding.getGmlObject().getADEProperties(CityObjectProperties.class); @@ -271,7 +271,7 @@ public class CityDoctorModelTest { CityDoctorModel model = new CityDoctorModel(config, f); Building b = Mockito.mock(Building.class); model.addBuilding(b); - assertEquals(b, model.getBuildings().get(0)); + assertEquals(b, model.getBuildings().toList().get(0)); } @Test @@ -281,7 +281,7 @@ public class CityDoctorModelTest { CityDoctorModel model = new CityDoctorModel(config, f); BridgeObject b = Mockito.mock(BridgeObject.class); model.addBridge(b); - assertEquals(b, model.getBridges().get(0)); + assertEquals(b, model.getBridges().toList().get(0)); } @Test @@ -291,7 +291,7 @@ public class CityDoctorModelTest { CityDoctorModel model = new CityDoctorModel(config, f); Vegetation veg = Mockito.mock(Vegetation.class); model.addVegetation(veg); - assertEquals(veg, model.getVegetation().get(0)); + assertEquals(veg, model.getVegetation().toList().get(0)); } @Test @@ -301,7 +301,7 @@ public class CityDoctorModelTest { CityDoctorModel model = new CityDoctorModel(config, f); Tunnel tunnel = Mockito.mock(Tunnel.class); model.addTunnel(tunnel); - assertEquals(tunnel, model.getTunnels().get(0)); + assertEquals(tunnel, model.getTunnels().toList().get(0)); } @@ -312,7 +312,7 @@ public class CityDoctorModelTest { CityDoctorModel model = new CityDoctorModel(config, f); TransportationObject trans = Mockito.mock(TransportationObject.class); model.addTransportation(trans); - assertEquals(trans, model.getTransportation().get(0)); + assertEquals(trans, model.getTransportation().toList().get(0)); } @@ -323,7 +323,7 @@ public class CityDoctorModelTest { CityDoctorModel model = new CityDoctorModel(config, f); LandObject land = Mockito.mock(LandObject.class); model.addLand(land); - assertEquals(land, model.getLand().get(0)); + assertEquals(land, model.getLand().toList().get(0)); } @Test @@ -333,7 +333,7 @@ public class CityDoctorModelTest { CityDoctorModel model = new CityDoctorModel(config, f); WaterObject water = Mockito.mock(WaterObject.class); model.addWater(water); - assertEquals(water, model.getWater().get(0)); + assertEquals(water, model.getWater().toList().get(0)); } @Test @@ -343,7 +343,7 @@ public class CityDoctorModelTest { CityDoctorModel model = new CityDoctorModel(config, f); CityFurniture cf = Mockito.mock(CityFurniture.class); model.addCityFurniture(cf); - assertEquals(cf, model.getCityFurniture().get(0)); + assertEquals(cf, model.getCityFurniture().toList().get(0)); } @Test @@ -353,7 +353,7 @@ public class CityDoctorModelTest { CityDoctorModel model = new CityDoctorModel(config, f); GenericCityObject gen = Mockito.mock(GenericCityObject.class); model.addGenericCityObject(gen); - assertEquals(gen, model.getGenericCityObjects().get(0)); + assertEquals(gen, model.getGenericCityObjects().toList().get(0)); } @Test @@ -402,10 +402,10 @@ public class CityDoctorModelTest { Building b1 = Mockito.mock(Building.class); Building b2 = Mockito.mock(Building.class); model.addBuilding(b1); - assertEquals(b1, model.getBuildings().get(0)); + assertEquals(b1, model.getBuildings().toList().get(0)); model.replaceFeature(b1, b2); - assertEquals(b2, model.getBuildings().get(0)); - assertEquals(1, model.getBuildings().size()); + assertEquals(b2, model.getBuildings().toList().get(0)); + assertEquals(1, model.getBuildings().toList().size()); } @Test(expected = IllegalStateException.class) @@ -426,10 +426,10 @@ public class CityDoctorModelTest { BridgeObject b1 = Mockito.mock(BridgeObject.class); BridgeObject b2 = Mockito.mock(BridgeObject.class); model.addBridge(b1); - assertEquals(b1, model.getBridges().get(0)); + assertEquals(b1, model.getBridges().toList().get(0)); model.replaceFeature(b1, b2); - assertEquals(b2, model.getBridges().get(0)); - assertEquals(1, model.getBridges().size()); + assertEquals(b2, model.getBridges().toList().get(0)); + assertEquals(1, model.getBridges().toList().size()); } @Test(expected = IllegalStateException.class) @@ -450,10 +450,10 @@ public class CityDoctorModelTest { Vegetation v1 = Mockito.mock(Vegetation.class); Vegetation v2 = Mockito.mock(Vegetation.class); model.addVegetation(v1); - assertEquals(v1, model.getVegetation().get(0)); + assertEquals(v1, model.getVegetation().toList().get(0)); model.replaceFeature(v1, v2); - assertEquals(v2, model.getVegetation().get(0)); - assertEquals(1, model.getVegetation().size()); + assertEquals(v2, model.getVegetation().toList().get(0)); + assertEquals(1, model.getVegetation().toList().size()); } @Test(expected = IllegalStateException.class) @@ -474,10 +474,10 @@ public class CityDoctorModelTest { Tunnel t1 = Mockito.mock(Tunnel.class); Tunnel t2 = Mockito.mock(Tunnel.class); model.addTunnel(t1); - assertEquals(t1, model.getTunnels().get(0)); + assertEquals(t1, model.getTunnels().toList().get(0)); model.replaceFeature(t1, t2); - assertEquals(t2, model.getTunnels().get(0)); - assertEquals(1, model.getTunnels().size()); + assertEquals(t2, model.getTunnels().toList().get(0)); + assertEquals(1, model.getTunnels().toList().size()); } @Test(expected = IllegalStateException.class) @@ -498,10 +498,10 @@ public class CityDoctorModelTest { TransportationObject t1 = Mockito.mock(TransportationObject.class); TransportationObject t2 = Mockito.mock(TransportationObject.class); model.addTransportation(t1); - assertEquals(t1, model.getTransportation().get(0)); + assertEquals(t1, model.getTransportation().toList().get(0)); model.replaceFeature(t1, t2); - assertEquals(t2, model.getTransportation().get(0)); - assertEquals(1, model.getTransportation().size()); + assertEquals(t2, model.getTransportation().toList().get(0)); + assertEquals(1, model.getTransportation().toList().size()); } @Test(expected = IllegalStateException.class) @@ -522,10 +522,10 @@ public class CityDoctorModelTest { LandObject lo1 = Mockito.mock(LandObject.class); LandObject lo2 = Mockito.mock(LandObject.class); model.addLand(lo1); - assertEquals(lo1, model.getLand().get(0)); + assertEquals(lo1, model.getLand().toList().get(0)); model.replaceFeature(lo1, lo2); - assertEquals(lo2, model.getLand().get(0)); - assertEquals(1, model.getLand().size()); + assertEquals(lo2, model.getLand().toList().get(0)); + assertEquals(1, model.getLand().toList().size()); } @Test(expected = IllegalStateException.class) @@ -546,10 +546,10 @@ public class CityDoctorModelTest { WaterObject t1 = Mockito.mock(WaterObject.class); WaterObject t2 = Mockito.mock(WaterObject.class); model.addWater(t1); - assertEquals(t1, model.getWater().get(0)); + assertEquals(t1, model.getWater().toList().get(0)); model.replaceFeature(t1, t2); - assertEquals(t2, model.getWater().get(0)); - assertEquals(1, model.getWater().size()); + assertEquals(t2, model.getWater().toList().get(0)); + assertEquals(1, model.getWater().toList().size()); } @Test(expected = IllegalStateException.class) @@ -570,10 +570,10 @@ public class CityDoctorModelTest { CityFurniture cf1 = Mockito.mock(CityFurniture.class); CityFurniture cf2 = Mockito.mock(CityFurniture.class); model.addCityFurniture(cf1); - assertEquals(cf1, model.getCityFurniture().get(0)); + assertEquals(cf1, model.getCityFurniture().toList().get(0)); model.replaceFeature(cf1, cf2); - assertEquals(cf2, model.getCityFurniture().get(0)); - assertEquals(1, model.getCityFurniture().size()); + assertEquals(cf2, model.getCityFurniture().toList().get(0)); + assertEquals(1, model.getCityFurniture().toList().size()); } @Test(expected = IllegalStateException.class) @@ -594,10 +594,10 @@ public class CityDoctorModelTest { GenericCityObject gco1 = Mockito.mock(GenericCityObject.class); GenericCityObject gco2 = Mockito.mock(GenericCityObject.class); model.addGenericCityObject(gco1); - assertEquals(gco1, model.getGenericCityObjects().get(0)); + assertEquals(gco1, model.getGenericCityObjects().toList().get(0)); model.replaceFeature(gco1, gco2); - assertEquals(gco2, model.getGenericCityObjects().get(0)); - assertEquals(1, model.getGenericCityObjects().size()); + assertEquals(gco2, model.getGenericCityObjects().toList().get(0)); + assertEquals(1, model.getGenericCityObjects().toList().size()); } @Test(expected = IllegalStateException.class) diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CompositePolygonTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CompositePolygonTest.java index 7aa9ad77..9c306ab3 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CompositePolygonTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CompositePolygonTest.java @@ -16,7 +16,7 @@ public class CompositePolygonTest { ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel m = CityGmlParser.parseCityGmlFile("src/test/resources/FZK_haus.gml", config); - Building b = m.getBuildings().get(0); + Building b = m.getBuildings().toList().get(0); assertNotNull(b); assertFalse(b.getBoundarySurfaces().isEmpty()); for (BoundarySurface bs : b.getBoundarySurfaces()) { diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/mapper/FeatureMapperTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/mapper/FeatureMapperTest.java index a4d8f261..456d4012 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/mapper/FeatureMapperTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/mapper/FeatureMapperTest.java @@ -63,20 +63,20 @@ public class FeatureMapperTest { public void testVisitWaterBody() { WaterBody body = new WaterBody(); Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(mock(ParserConfiguration.class), - new File("").toPath()); + new File("").toPath(), false); mapper.visit(body); - assertEquals(1, mapper.getModel().getWater().size()); + assertEquals(1, mapper.getModel().getWater().toList().size()); body.setId("test1"); mapper.visit(body); - assertEquals(2, mapper.getModel().getWater().size()); - WaterObject waterObject = mapper.getModel().getWater().get(1); + assertEquals(2, mapper.getModel().getWater().toList().size()); + WaterObject waterObject = mapper.getModel().getWater().toList().get(1); assertEquals("test1", waterObject.getGmlId().getGmlString()); body.getDeprecatedProperties().setLod1MultiSurface(createDummyMsp()); mapper.visit(body); - assertEquals(3, mapper.getModel().getWater().size()); + assertEquals(3, mapper.getModel().getWater().toList().size()); assertNull(body.getDeprecatedProperties().getLod1MultiSurface()); - Geometry geometry = mapper.getModel().getWater().get(2).getGeometries().get(0); + Geometry geometry = mapper.getModel().getWater().toList().get(2).getGeometries().get(0); assertEquals(1, geometry.getPolygons().size()); assertEquals(Lod.LOD1, geometry.getLod()); assertEquals(GeometryType.MULTI_SURFACE, geometry.getType()); @@ -86,7 +86,7 @@ public class FeatureMapperTest { public void testVisitPlantCover() { PlantCover cover = new PlantCover(); Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(mock(ParserConfiguration.class), - new File("").toPath()); + new File("").toPath(), false); mapper.visit(cover); } @@ -124,12 +124,12 @@ public class FeatureMapperTest { body.getDeprecatedProperties().setLod1MultiSurface(new MultiSurfaceProperty(ms)); ParserConfiguration config = new ParserConfiguration(8, false); - Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, folder.newFile().toPath()); + Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, folder.newFile().toPath(), false); mapper.visit(body); CityDoctorModel model = mapper.getModel(); - assertEquals(1, model.getWater().size()); - WaterObject waterObject = model.getWater().get(0); + assertEquals(1, model.getWater().toList().size()); + WaterObject waterObject = model.getWater().toList().get(0); Geometry geometry = waterObject.getGeometries().get(0); assertEquals(1, geometry.getVertices().size()); Vertex vertex = geometry.getVertices().get(0); @@ -152,12 +152,12 @@ public class FeatureMapperTest { body.getDeprecatedProperties().setLod1MultiSurface(new MultiSurfaceProperty(ms)); ParserConfiguration config = new ParserConfiguration(8, false); - Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, folder.newFile().toPath()); + Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, folder.newFile().toPath(), false); mapper.visit(body); CityDoctorModel model = mapper.getModel(); - assertEquals(1, model.getWater().size()); - WaterObject waterObject = model.getWater().get(0); + assertEquals(1, model.getWater().toList().size()); + WaterObject waterObject = model.getWater().toList().get(0); Geometry geometry = waterObject.getGeometries().get(0); assertEquals(2, geometry.getVertices().size()); Vertex vertex = geometry.getVertices().get(0); @@ -199,12 +199,12 @@ public class FeatureMapperTest { body.getDeprecatedProperties().setLod1MultiSurface(new MultiSurfaceProperty(ms)); ParserConfiguration config = new ParserConfiguration(8, false); - Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, folder.newFile().toPath()); + Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, folder.newFile().toPath(), false); mapper.visit(body); CityDoctorModel model = mapper.getModel(); - assertEquals(1, model.getWater().size()); - WaterObject waterObject = model.getWater().get(0); + assertEquals(1, model.getWater().toList().size()); + WaterObject waterObject = model.getWater().toList().get(0); Geometry geometry = waterObject.getGeometries().get(0); assertEquals(7, geometry.getVertices().size()); assertEquals(8, geometry.getEdges().size()); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/parser/CityGml3ParserTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/parser/CityGml3ParserTest.java index cbc926f1..88b958f4 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/parser/CityGml3ParserTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/parser/CityGml3ParserTest.java @@ -49,7 +49,7 @@ public class CityGml3ParserTest { ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS.gml", config); assertNotNull(model); - List buildings = model.getBuildings(); + List buildings = model.getBuildings().toList(); assertEquals(1, buildings.size()); Building building = buildings.get(0); assertEquals("_Simple_BD.1", building.getGmlId().getGmlString()); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java index cb9b8e2b..42afda9c 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java @@ -100,7 +100,7 @@ public class ZipTest { cgmlArch.mountArchive(config); CityDoctorModel mainModel = cgmlArch.getEntry("Main_model.gml").getModel(); - assertEquals(18, mainModel.getGenericCityObjects().size()); + assertEquals(18, mainModel.getGenericCityObjects().toList().size()); } } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java index 799b2730..311f19b6 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java @@ -68,7 +68,7 @@ public class CheckerTest { "src/test/resources/SimpleSolid_SrefBS_SchematronTest.gml", config.getParserConfiguration()); Checker checker = new Checker(config, model); checker.runChecks(); - for (Building b : model.getBuildings()) { + for (Building b : model.getBuildings().toList()) { if (b.getGmlId().getGmlString().equals("_Simple_BD.1")) { assertTrue(b.containsAnyError()); } else { @@ -207,7 +207,7 @@ public class CheckerTest { Path xmlPath = tmpDir.resolve("report.xml"); Path pdfPath = tmpDir.resolve("report.pdf"); File features = new File("src/test/resources/feature_types.gml"); - CityDoctorModel model = CityGmlParser.parseCityGmlFileSilently( + CityDoctorModel model = CityGmlParser.nestedCityGmlFileParse( features.toString(), new ParserConfiguration(8, false)); assertNotNull(model); Checker checker = new Checker(model); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index b42cd4bc..7f42d2ca 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -140,7 +140,7 @@ public class CityDoctorController { } }; } - model = CityGmlParser.parseCityGmlFile(path, currentConfig, l, handler, true); + model = CityGmlParser.parseCityGmlFile(path, currentConfig, l, handler); if (!validationIssues.isEmpty()) { StringJoiner sj = new StringJoiner("\n"); validationIssues.stream().forEach(sj::add); @@ -482,7 +482,6 @@ public class CityDoctorController { if (model.getBuildingCount() == 0) { return; } - //TODO: Continue Supplier implementation TreeView buildingsView = mainWindow.getBuildingsView(); TreeItem root = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Buildings", Color.WHITE)); -- GitLab From d5af975e3627f4139c744abe4ee0042fe3c5f7d0 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 16 Oct 2025 11:36:29 +0200 Subject: [PATCH 039/118] Fix: Fix test failures --- .../edge/MeshSurfaceUtilsTest.java | 2 +- .../citydoctor2/database/CacheManager.java | 14 +- .../datastructure/CityDoctorModel.java | 18 +- .../citydoctor2/parser/CityGmlParser.java | 8 +- .../datastructure/CityDoctorModelTest.java | 224 ++++++++---------- .../citydoctor2/check/FilterTest.java | 8 +- ...dSelfIntCheckFalsePositiveBigMeshTest.java | 2 +- .../geometry/SolidSelfIntCheckTest.java | 4 +- .../AllPolygonWrongOrientedTest.java | 2 +- .../systemtest/ConCompCheckTest.java | 4 +- .../systemtest/DupPointsCheckTest.java | 2 +- .../systemtest/HoleOutsideSystemTest.java | 2 +- .../InnerRingsNestedSystemTest.java | 2 +- .../InteriorDisconnectedSystemTest.java | 2 +- .../systemtest/MultipleComponentTest.java | 4 +- .../systemtest/NonManifoldEdgeSystemTest.java | 4 +- .../NonManifoldVertexSystemTest.java | 4 +- .../systemtest/NumPointsCheckTest.java | 2 +- .../systemtest/OrientationSameSystemTest.java | 2 +- .../systemtest/OuterEdgeCheckTest.java | 2 +- .../citydoctor2/systemtest/PlanarTest.java | 2 +- .../PolygonIntersectingRingsTest.java | 4 +- .../systemtest/RingClosedCheckTest.java | 4 +- .../systemtest/RingSelfIntersectionTest.java | 4 +- .../systemtest/ShellNotClosedSystemTest.java | 4 +- .../citydoctor2/systemtest/TestUtil.java | 33 ++- .../systemtest/TooFewPolygonsSystemTest.java | 4 +- ...lAllPolygonsWrongOrientationErrorTest.java | 4 +- .../HealConsecutivePointsSameErrorTest.java | 4 +- .../healing/HealHoleOutsideErrorTest.java | 4 +- .../healing/HealMissingSolidTest.java | 4 +- .../healing/HealNonManifoldEdgeErrorTest.java | 4 +- .../healing/HealPlanarPolygonErrorTest.java | 4 +- .../HealPolygonWithoutSurfaceTest.java | 6 +- .../HealPolygonWrongOrientationErrorTest.java | 4 +- .../healing/HealRingNotClosedErrorTest.java | 4 +- .../healing/HealRingSelfIntErrorTest.java | 12 +- .../healing/HealSameOrientationErrorTest.java | 4 +- .../healing/HealSolidNotClosedErrorTest.java | 4 +- .../healing/HealTooFewPointsTest.java | 4 +- .../citydoctor2/healing/TestHealer.java | 32 +-- .../citydoctor2/healing/TestUtil.java | 12 +- 42 files changed, 243 insertions(+), 230 deletions(-) diff --git a/CityDoctorParent/CityDoctorEdge/src/test/java/de/hft/stuttgart/citydoctor2/edge/MeshSurfaceUtilsTest.java b/CityDoctorParent/CityDoctorEdge/src/test/java/de/hft/stuttgart/citydoctor2/edge/MeshSurfaceUtilsTest.java index 89fa9def..557e619d 100644 --- a/CityDoctorParent/CityDoctorEdge/src/test/java/de/hft/stuttgart/citydoctor2/edge/MeshSurfaceUtilsTest.java +++ b/CityDoctorParent/CityDoctorEdge/src/test/java/de/hft/stuttgart/citydoctor2/edge/MeshSurfaceUtilsTest.java @@ -1259,7 +1259,7 @@ public class MeshSurfaceUtilsTest { public void testKaiserwallComplexBuilding() throws CityGmlParseException, InvalidGmlFileException { ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/KaiserwallComplexBuilding.gml", config); - Building building = model.getBuildings().get(0); + Building building = model.getBuildings().toList().get(0); Geometry geom = building.getBuildingInstallations().get(0).getGeometries().get(0); assertNotNull(geom); MeshSurface surface = MeshSurface.of(geom); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java index ecde3ea4..fb36bba9 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -1,10 +1,20 @@ package de.hft.stuttgart.citydoctor2.database; +import org.apache.commons.io.FilenameUtils; + +import java.io.File; + public class CacheManager { private static FeatureCache cache = null; - public static CityObjectCache getFeatureCache(String filename) { + private CacheManager() {} + + public static FeatureCache getFeatureCache(File file) { + if (file == null){ + throw new IllegalArgumentException("File is null"); + } + String filename = FilenameUtils.removeExtension(file.getName()); if (cache == null){ cache = new FeatureCache(filename); } else { @@ -13,7 +23,7 @@ public class CacheManager { return cache; } - public static CityObjectCache getUnconnectedCache(){ + public static UnconnectedCache getUnconnectedCache(){ return new UnconnectedCache(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 829b1352..61a99a8a 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -69,7 +69,7 @@ public class CityDoctorModel { private final CityObjectCache cache; public CityDoctorModel(ParserConfiguration config, File file) { - this(config, file, CacheManager.getFeatureCache(FilenameUtils.removeExtension(file.getName()))); + this(config, file, CacheManager.getFeatureCache(file)); } public CityDoctorModel(ParserConfiguration config, File file, CityObjectCache cache) { @@ -244,6 +244,18 @@ public class CityDoctorModel { cache.put(coBuilding); } + public void removeFeature(GmlId id){ + buildings.remove(id); + vegetation.remove(id); + bridges.remove(id); + land.remove(id); + roads.remove(id); + tunnels.remove(id); + water.remove(id); + cityfurniture.remove(id); + genericObjects.remove(id); + } + public Stream getBuildings() { return buildings.stream().map(co -> (Building) cache.get(co)); } @@ -459,6 +471,7 @@ public class CityDoctorModel { if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in transportation objects"); } + cache.replace(currentFeature.getGmlId(), nextFeature); roads.set(index, nextFeature.getGmlId()); } @@ -467,6 +480,7 @@ public class CityDoctorModel { if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in tunnels"); } + cache.replace(currentFeature.getGmlId(), nextFeature); tunnels.set(index, nextFeature.getGmlId()); } @@ -475,6 +489,7 @@ public class CityDoctorModel { if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in bridges"); } + cache.replace(currentFeature.getGmlId(), nextFeature); bridges.set(index, nextFeature.getGmlId()); } @@ -483,6 +498,7 @@ public class CityDoctorModel { if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in buildings"); } + cache.replace(currentFeature.getGmlId(), nextFeature); buildings.set(index, nextFeature.getGmlId()); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index bbf87402..99344413 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -45,6 +45,7 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -842,8 +843,13 @@ public class CityGmlParser { } private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) { + List id = new ArrayList<>(); + model.createFeatureStream().forEach(co -> { + cityObjectConsumer.accept(co); + id.add(co.getGmlId()); + }); + id.forEach(model::removeFeature); - model.createFeatureStream().forEach(cityObjectConsumer::accept); } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java index 0cad81c9..b32e4d39 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java @@ -32,6 +32,7 @@ import org.citygml4j.core.model.CityGMLVersion; import org.citygml4j.core.model.core.AbstractFeatureProperty; import org.citygml4j.core.model.core.CityModel; import org.citygml4j.xml.writer.CityGMLWriteException; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -63,28 +64,39 @@ import de.hft.stuttgart.quality.model.types.ValidationPlan; public class CityDoctorModelTest { + private File mockFile; + private ParserConfiguration config; + + @Before + public void setUp(){ + mockFile = Mockito.mock(File.class); + Mockito.when(mockFile.getName()).thenReturn("test"); + config = new ParserConfiguration(8, false); + } + @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test(expected = IllegalArgumentException.class) public void testCityDoctorModelNoConfig() { - new CityDoctorModel(null, null); + new CityDoctorModel(null, mockFile); + } + + @Test(expected = IllegalArgumentException.class) + public void testCityDoctorModelNoFile() { + new CityDoctorModel(config, null); } @Test public void testSetValidated() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); model.setValidated(Mockito.mock(ValidationPlan.class)); assertTrue(model.isValidated()); } @Test public void testAddGlobalError() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); CheckError err = Mockito.mock(CheckError.class); model.addGlobalError(err); assertEquals(err, model.getGlobalErrors().get(0)); @@ -92,9 +104,7 @@ public class CityDoctorModelTest { @Test public void testAddGlobalErrors() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); CheckError err1 = Mockito.mock(CheckError.class); CheckError err2 = Mockito.mock(CheckError.class); List errors = new ArrayList<>(); @@ -107,19 +117,17 @@ public class CityDoctorModelTest { @Test public void testGetFile() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); - assertEquals(f, model.getFile()); + CityDoctorModel model = new CityDoctorModel(config, mockFile); + assertEquals(mockFile, model.getFile()); } @Test public void testCreateFeatureStream() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b = Mockito.mock(Building.class); + Mockito.when(b.getGmlId()).thenReturn(new GmlId("b")); Vegetation v = Mockito.mock(Vegetation.class); + Mockito.when(v.getGmlId()).thenReturn(new GmlId("v")); model.addBuilding(b); model.addVegetation(v); assertTrue(model.createFeatureStream().anyMatch(c -> c == b)); @@ -129,9 +137,7 @@ public class CityDoctorModelTest { @Test public void testSaveAsWithoutValidation() throws CityGMLWriteException, ADEException, IOException, CityGmlParseException, InvalidGmlFileException, CityDoctorWriteException { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); model.setParsedCityGMLVersion(CityGMLVersion.v2_0); CityModel cModel = new CityModel(); model.setCityModel(cModel); @@ -155,9 +161,7 @@ public class CityDoctorModelTest { @Test public void testSaveAsWithValidation() throws CityGMLWriteException, ADEException, IOException, CityGmlParseException, InvalidGmlFileException, CityDoctorWriteException { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); model.setParsedCityGMLVersion(CityGMLVersion.v2_0); CityModel cModel = new CityModel(); ValidationPlan plan = new ValidationPlan(); @@ -234,9 +238,7 @@ public class CityDoctorModelTest { @Test public void testCollectErrors() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b = new Building(); model.addBuilding(b); CheckError error = Mockito.mock(CheckError.class); @@ -247,18 +249,14 @@ public class CityDoctorModelTest { @Test public void testGetFileName() { - File f = new File("test"); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); assertEquals("test", model.getFileName()); - assertEquals(f, model.getFile()); + assertEquals(mockFile, model.getFile()); } @Test public void testSetCityModel() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); CityModel cModel = Mockito.mock(CityModel.class); model.setCityModel(cModel); assertEquals(cModel, model.getCityModel()); @@ -266,40 +264,36 @@ public class CityDoctorModelTest { @Test public void testAddBuilding() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b = Mockito.mock(Building.class); + Mockito.when(b.getGmlId()).thenReturn(new GmlId("b")); model.addBuilding(b); assertEquals(b, model.getBuildings().toList().get(0)); } @Test public void testAddBridge() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); BridgeObject b = Mockito.mock(BridgeObject.class); + Mockito.when(b.getGmlId()).thenReturn(new GmlId("b")); model.addBridge(b); assertEquals(b, model.getBridges().toList().get(0)); } @Test public void testAddVegetation() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Vegetation veg = Mockito.mock(Vegetation.class); + Mockito.when(veg.getGmlId()).thenReturn(new GmlId("v")); model.addVegetation(veg); assertEquals(veg, model.getVegetation().toList().get(0)); } @Test public void testAddTunnel() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Tunnel tunnel = Mockito.mock(Tunnel.class); + Mockito.when(tunnel.getGmlId()).thenReturn(new GmlId("t")); model.addTunnel(tunnel); assertEquals(tunnel, model.getTunnels().toList().get(0)); } @@ -307,10 +301,9 @@ public class CityDoctorModelTest { @Test public void testAddTransportation() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); TransportationObject trans = Mockito.mock(TransportationObject.class); + Mockito.when(trans.getGmlId()).thenReturn(new GmlId("t")); model.addTransportation(trans); assertEquals(trans, model.getTransportation().toList().get(0)); } @@ -318,59 +311,62 @@ public class CityDoctorModelTest { @Test public void testAddLand() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); LandObject land = Mockito.mock(LandObject.class); + Mockito.when(land.getGmlId()).thenReturn(new GmlId("l")); model.addLand(land); assertEquals(land, model.getLand().toList().get(0)); } @Test public void testAddWater() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); WaterObject water = Mockito.mock(WaterObject.class); + Mockito.when(water.getGmlId()).thenReturn(new GmlId("w")); model.addWater(water); assertEquals(water, model.getWater().toList().get(0)); } @Test public void testAddCityFurniture() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); CityFurniture cf = Mockito.mock(CityFurniture.class); + Mockito.when(cf.getGmlId()).thenReturn(new GmlId("cf")); model.addCityFurniture(cf); assertEquals(cf, model.getCityFurniture().toList().get(0)); } @Test public void testAddGenericCityObject() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); GenericCityObject gen = Mockito.mock(GenericCityObject.class); + Mockito.when(gen.getGmlId()).thenReturn(new GmlId("g")); model.addGenericCityObject(gen); assertEquals(gen, model.getGenericCityObjects().toList().get(0)); } @Test public void testGetNumberOfFeatures() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b = Mockito.mock(Building.class); + Mockito.when(b.getGmlId()).thenReturn(new GmlId("b")); BridgeObject bo = Mockito.mock(BridgeObject.class); + Mockito.when(bo.getGmlId()).thenReturn(new GmlId("bo")); Vegetation veg = Mockito.mock(Vegetation.class); + Mockito.when(veg.getGmlId()).thenReturn(new GmlId("ve")); Tunnel tunnel = Mockito.mock(Tunnel.class); + Mockito.when(tunnel.getGmlId()).thenReturn(new GmlId("t")); TransportationObject trans = Mockito.mock(TransportationObject.class); + Mockito.when(trans.getGmlId()).thenReturn(new GmlId("tr")); LandObject land = Mockito.mock(LandObject.class); + Mockito.when(land.getGmlId()).thenReturn(new GmlId("l")); WaterObject water = Mockito.mock(WaterObject.class); + Mockito.when(water.getGmlId()).thenReturn(new GmlId("w")); CityFurniture cf = Mockito.mock(CityFurniture.class); + Mockito.when(cf.getGmlId()).thenReturn(new GmlId("cf")); GenericCityObject generic = Mockito.mock(GenericCityObject.class); + Mockito.when(generic.getGmlId()).thenReturn(new GmlId("g")); model.addBuilding(b); model.addBridge(bo); @@ -388,19 +384,18 @@ public class CityDoctorModelTest { @Test public void testGetParserConfig() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); assertEquals(config, model.getParserConfig()); } @Test public void testReplaceBuilding() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b1 = Mockito.mock(Building.class); + Mockito.when(b1.getGmlId()).thenReturn(new GmlId("b1")); Building b2 = Mockito.mock(Building.class); + Mockito.when(b2.getGmlId()).thenReturn(new GmlId("b2")); + model.addBuilding(b1); assertEquals(b1, model.getBuildings().toList().get(0)); model.replaceFeature(b1, b2); @@ -410,9 +405,7 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceBuildingMissing() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b1 = Mockito.mock(Building.class); Building b2 = Mockito.mock(Building.class); model.replaceFeature(b1, b2); @@ -420,11 +413,11 @@ public class CityDoctorModelTest { @Test public void testReplaceBridge() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); BridgeObject b1 = Mockito.mock(BridgeObject.class); + Mockito.when(b1.getGmlId()).thenReturn(new GmlId("b1")); BridgeObject b2 = Mockito.mock(BridgeObject.class); + Mockito.when(b2.getGmlId()).thenReturn(new GmlId("b2")); model.addBridge(b1); assertEquals(b1, model.getBridges().toList().get(0)); model.replaceFeature(b1, b2); @@ -434,9 +427,7 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceBridgeMissing() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); BridgeObject b1 = Mockito.mock(BridgeObject.class); BridgeObject b2 = Mockito.mock(BridgeObject.class); model.replaceFeature(b1, b2); @@ -444,11 +435,12 @@ public class CityDoctorModelTest { @Test public void testReplaceVegetation() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Vegetation v1 = Mockito.mock(Vegetation.class); + Mockito.when(v1.getGmlId()).thenReturn(new GmlId("v1")); Vegetation v2 = Mockito.mock(Vegetation.class); + Mockito.when(v2.getGmlId()).thenReturn(new GmlId("v2")); + model.addVegetation(v1); assertEquals(v1, model.getVegetation().toList().get(0)); model.replaceFeature(v1, v2); @@ -458,9 +450,7 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceVegetationMissing() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Vegetation v1 = Mockito.mock(Vegetation.class); Vegetation v2 = Mockito.mock(Vegetation.class); model.replaceFeature(v1, v2); @@ -468,11 +458,12 @@ public class CityDoctorModelTest { @Test public void testReplaceTunnel() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Tunnel t1 = Mockito.mock(Tunnel.class); + Mockito.when(t1.getGmlId()).thenReturn(new GmlId("t1")); Tunnel t2 = Mockito.mock(Tunnel.class); + Mockito.when(t2.getGmlId()).thenReturn(new GmlId("t2")); + model.addTunnel(t1); assertEquals(t1, model.getTunnels().toList().get(0)); model.replaceFeature(t1, t2); @@ -482,9 +473,7 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceTunnelMissing() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); Tunnel t1 = Mockito.mock(Tunnel.class); Tunnel t2 = Mockito.mock(Tunnel.class); model.replaceFeature(t1, t2); @@ -492,11 +481,12 @@ public class CityDoctorModelTest { @Test public void testReplaceTransportation() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); TransportationObject t1 = Mockito.mock(TransportationObject.class); + Mockito.when(t1.getGmlId()).thenReturn(new GmlId("t1")); TransportationObject t2 = Mockito.mock(TransportationObject.class); + Mockito.when(t2.getGmlId()).thenReturn(new GmlId("t2")); + model.addTransportation(t1); assertEquals(t1, model.getTransportation().toList().get(0)); model.replaceFeature(t1, t2); @@ -506,9 +496,7 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceTransportationMissing() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); TransportationObject t1 = Mockito.mock(TransportationObject.class); TransportationObject t2 = Mockito.mock(TransportationObject.class); model.replaceFeature(t1, t2); @@ -516,11 +504,12 @@ public class CityDoctorModelTest { @Test public void testReplaceLand() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); LandObject lo1 = Mockito.mock(LandObject.class); + Mockito.when(lo1.getGmlId()).thenReturn(new GmlId("lo1")); LandObject lo2 = Mockito.mock(LandObject.class); + Mockito.when(lo2.getGmlId()).thenReturn(new GmlId("lo2")); + model.addLand(lo1); assertEquals(lo1, model.getLand().toList().get(0)); model.replaceFeature(lo1, lo2); @@ -530,9 +519,7 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceLandMissing() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); LandObject lo1 = Mockito.mock(LandObject.class); LandObject lo2 = Mockito.mock(LandObject.class); model.replaceFeature(lo1, lo2); @@ -540,11 +527,12 @@ public class CityDoctorModelTest { @Test public void testReplaceWater() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); WaterObject t1 = Mockito.mock(WaterObject.class); + Mockito.when(t1.getGmlId()).thenReturn(new GmlId("t1")); WaterObject t2 = Mockito.mock(WaterObject.class); + Mockito.when(t2.getGmlId()).thenReturn(new GmlId("t2")); + model.addWater(t1); assertEquals(t1, model.getWater().toList().get(0)); model.replaceFeature(t1, t2); @@ -554,9 +542,7 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceWaterMissing() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); WaterObject t1 = Mockito.mock(WaterObject.class); WaterObject t2 = Mockito.mock(WaterObject.class); model.replaceFeature(t1, t2); @@ -564,11 +550,12 @@ public class CityDoctorModelTest { @Test public void testReplaceCityFurniture() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); CityFurniture cf1 = Mockito.mock(CityFurniture.class); + Mockito.when(cf1.getGmlId()).thenReturn(new GmlId("cf1")); CityFurniture cf2 = Mockito.mock(CityFurniture.class); + Mockito.when(cf2.getGmlId()).thenReturn(new GmlId("cf2")); + model.addCityFurniture(cf1); assertEquals(cf1, model.getCityFurniture().toList().get(0)); model.replaceFeature(cf1, cf2); @@ -578,9 +565,7 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceCityFurnitureMissing() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); CityFurniture cf1 = Mockito.mock(CityFurniture.class); CityFurniture cf2 = Mockito.mock(CityFurniture.class); model.replaceFeature(cf1, cf2); @@ -588,11 +573,12 @@ public class CityDoctorModelTest { @Test public void testReplaceGenericCityObject() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); GenericCityObject gco1 = Mockito.mock(GenericCityObject.class); + Mockito.when(gco1.getGmlId()).thenReturn(new GmlId("gco1")); GenericCityObject gco2 = Mockito.mock(GenericCityObject.class); + Mockito.when(gco2.getGmlId()).thenReturn(new GmlId("gco2")); + model.addGenericCityObject(gco1); assertEquals(gco1, model.getGenericCityObjects().toList().get(0)); model.replaceFeature(gco1, gco2); @@ -602,9 +588,7 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceGenericCityObjectMissing() { - File f = Mockito.mock(File.class); - ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, f); + CityDoctorModel model = new CityDoctorModel(config, mockFile); GenericCityObject gco1 = Mockito.mock(GenericCityObject.class); GenericCityObject gco2 = Mockito.mock(GenericCityObject.class); model.replaceFeature(gco1, gco2); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/FilterTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/FilterTest.java index f3c096f0..b8c2d367 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/FilterTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/FilterTest.java @@ -45,12 +45,12 @@ public class FilterTest { Checker c = new Checker(config, model); c.runChecks(); - Building b = model.getBuildings().get(0); + Building b = model.getBuildings().toList().get(0); Geometry geom = b.getGeometries().get(0); assertFalse(geom.hasCheckResults()); - TransportationObject to = model.getTransportation().get(0); + TransportationObject to = model.getTransportation().toList().get(0); Geometry geom2 = to.getGeometries().get(0); assertTrue(geom2.getPolygons().get(0).hasCheckResults()); @@ -65,12 +65,12 @@ public class FilterTest { Checker c = new Checker(config, model); c.runChecks(); - Building b = model.getBuildings().get(0); + Building b = model.getBuildings().toList().get(0); Geometry geom = b.getGeometries().get(0); assertFalse(geom.hasCheckResults()); - TransportationObject to = model.getTransportation().get(0); + TransportationObject to = model.getTransportation().toList().get(0); Geometry geom2 = to.getGeometries().get(0); assertTrue(geom2.getPolygons().get(0).hasCheckResults()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckFalsePositiveBigMeshTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckFalsePositiveBigMeshTest.java index d79fed45..20ad29a1 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckFalsePositiveBigMeshTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckFalsePositiveBigMeshTest.java @@ -35,7 +35,7 @@ public class SolidSelfIntCheckFalsePositiveBigMeshTest { CityDoctorModel m = CityGmlParser.parseCityGmlFile(gml_filepath, config.getParserConfiguration()); Checker c = new Checker(config, m); c.runChecks(); - Building building = m.getBuildings().get(0); + Building building = m.getBuildings().toList().get(0); /* * The examples have no actual self-intersections, but can contain other actual model defects. * If an error is detected, it is thus required to check if the diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java index 847de23f..78c11861 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java @@ -75,7 +75,7 @@ public class SolidSelfIntCheckTest { CityDoctorModel m = CityGmlParser.parseCityGmlFile("src/test/resources/SolidSelfIntTest1.gml", config.getParserConfiguration()); Checker c = new Checker(config, m); c.runChecks(); - assertFalse(m.getBuildings().get(0).containsAnyError()); + assertFalse(m.getBuildings().toList().get(0).containsAnyError()); } @@ -85,7 +85,7 @@ public class SolidSelfIntCheckTest { CityDoctorModel m = CityGmlParser.parseCityGmlFile(gml_filepath, config.getParserConfiguration()); Checker c = new Checker(config, m); c.runChecks(); - Building building = m.getBuildings().get(0); + Building building = m.getBuildings().toList().get(0); /* * The examples have no actual self-intersections, but can contain other actual model defects. * If an error is detected, it is thus required to check if the diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/AllPolygonWrongOrientedTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/AllPolygonWrongOrientedTest.java index e941df19..e96b4e45 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/AllPolygonWrongOrientedTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/AllPolygonWrongOrientedTest.java @@ -48,7 +48,7 @@ public class AllPolygonWrongOrientedTest { Checker c = new Checker(config, model); c.runChecks(); - Building b = model.getBuildings().get(0); + Building b = model.getBuildings().toList().get(0); assertFalse(b.containsError(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION)); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/ConCompCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/ConCompCheckTest.java index 2a8c1317..b9a50809 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/ConCompCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/ConCompCheckTest.java @@ -51,7 +51,7 @@ public class ConCompCheckTest extends TestCleanUp { Checker c = new Checker(m); c.runChecks(); - Geometry geom = m.getBuildings().get(0).getGeometries().get(0); + Geometry geom = m.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = geom.getCheckResult(CheckId.C_GE_S_MULTIPLE_CONNECTED_COMPONENTS); assertNotNull(cr); assertEquals(ResultStatus.ERROR, cr.getResultStatus()); @@ -65,7 +65,7 @@ public class ConCompCheckTest extends TestCleanUp { Checker c = new Checker(m); c.runChecks(); - Geometry geom = m.getBuildings().get(0).getGeometries().get(0); + Geometry geom = m.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = geom.getCheckResult(CheckId.C_GE_S_MULTIPLE_CONNECTED_COMPONENTS); assertNotNull(cr); assertEquals(ResultStatus.ERROR, cr.getResultStatus()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/DupPointsCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/DupPointsCheckTest.java index 924c651e..2720b011 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/DupPointsCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/DupPointsCheckTest.java @@ -79,7 +79,7 @@ public class DupPointsCheckTest { @Test public void testDupPoints3() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel m = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - for (Polygon p : m.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : m.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { LinearRing r = p.getExteriorRing(); CheckResult cr = r.getCheckResult(CheckId.C_GE_R_DUPLICATE_POINT); assertEquals(ResultStatus.OK, cr.getResultStatus()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/HoleOutsideSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/HoleOutsideSystemTest.java index 89072edc..dd603053 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/HoleOutsideSystemTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/HoleOutsideSystemTest.java @@ -43,7 +43,7 @@ public class HoleOutsideSystemTest { @Test public void testHoleOutside1() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { CheckResult cr = p.getCheckResult(CheckId.C_GE_P_HOLE_OUTSIDE); assertEquals(ResultStatus.OK, cr.getResultStatus()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/InnerRingsNestedSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/InnerRingsNestedSystemTest.java index 6ae1d478..ca2982e9 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/InnerRingsNestedSystemTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/InnerRingsNestedSystemTest.java @@ -43,7 +43,7 @@ public class InnerRingsNestedSystemTest { @Test public void innerRingsNested1() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { CheckResult cr = p.getCheckResult(CheckId.C_GE_P_INNER_RINGS_NESTED); assertEquals(ResultStatus.OK, cr.getResultStatus()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/InteriorDisconnectedSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/InteriorDisconnectedSystemTest.java index 4ddd49c4..9d7ac446 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/InteriorDisconnectedSystemTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/InteriorDisconnectedSystemTest.java @@ -43,7 +43,7 @@ public class InteriorDisconnectedSystemTest { @Test public void testInteriorDisconnected1() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { CheckResult cr = p.getCheckResult(CheckId.C_GE_P_INTERIOR_DISCONNECTED); assertEquals(ResultStatus.OK, cr.getResultStatus()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/MultipleComponentTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/MultipleComponentTest.java index 33aaafbd..f958a9e0 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/MultipleComponentTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/MultipleComponentTest.java @@ -43,7 +43,7 @@ public class MultipleComponentTest { @Test public void testNoMultipleComponents() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_MULTIPLE_CONNECTED_COMPONENTS); assertEquals(ResultStatus.OK, cr.getResultStatus()); } @@ -51,7 +51,7 @@ public class MultipleComponentTest { @Test public void testMultipleComponents() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0005-T0001.gml"); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_MULTIPLE_CONNECTED_COMPONENTS); assertEquals(ResultStatus.ERROR, cr.getResultStatus()); assertEquals(ErrorId.GE_S_MULTIPLE_CONNECTED_COMPONENTS, cr.getError().getErrorId()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldEdgeSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldEdgeSystemTest.java index 66e121b2..597bd462 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldEdgeSystemTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldEdgeSystemTest.java @@ -46,7 +46,7 @@ public class NonManifoldEdgeSystemTest { @Test public void testNonManifoldEdge1() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NON_MANIFOLD_EDGE); assertEquals(ResultStatus.OK, cr.getResultStatus()); } @@ -59,7 +59,7 @@ public class NonManifoldEdgeSystemTest { paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0004-T0001.gml", paramMap); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NON_MANIFOLD_EDGE); assertEquals(ResultStatus.ERROR, cr.getResultStatus()); assertEquals(ErrorId.GE_S_NON_MANIFOLD_EDGE, cr.getError().getErrorId()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldVertexSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldVertexSystemTest.java index 12774541..9ab2e229 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldVertexSystemTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldVertexSystemTest.java @@ -43,7 +43,7 @@ public class NonManifoldVertexSystemTest { @Test public void testNonManifoldVertex1() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NON_MANIFOLD_VERTEX); assertEquals(ResultStatus.OK, cr.getResultStatus()); } @@ -51,7 +51,7 @@ public class NonManifoldVertexSystemTest { @Test public void testNonManifoldVertex2() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0003-T0001.gml"); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NON_MANIFOLD_VERTEX); assertEquals(ResultStatus.ERROR, cr.getResultStatus()); assertEquals(ErrorId.GE_S_NON_MANIFOLD_VERTEX, cr.getError().getErrorId()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NumPointsCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NumPointsCheckTest.java index 2a1a6b37..22c2c426 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NumPointsCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NumPointsCheckTest.java @@ -80,7 +80,7 @@ public class NumPointsCheckTest { public void testNumPoints3() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { LinearRing r = p.getExteriorRing(); assertEquals(ResultStatus.OK, r.getCheckResult(CheckId.C_GE_R_TOO_FEW_POINTS).getResultStatus()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/OrientationSameSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/OrientationSameSystemTest.java index 68787131..1e3b3a56 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/OrientationSameSystemTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/OrientationSameSystemTest.java @@ -43,7 +43,7 @@ public class OrientationSameSystemTest { @Test public void testOrientationSame1() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { CheckResult cr = p.getCheckResult(CheckId.C_GE_P_ORIENTATION_RINGS_SAME); assertEquals(ResultStatus.OK, cr.getResultStatus()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/OuterEdgeCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/OuterEdgeCheckTest.java index 2b6af2fb..08e88d65 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/OuterEdgeCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/OuterEdgeCheckTest.java @@ -71,7 +71,7 @@ public class OuterEdgeCheckTest { expectedErrorEdges.add(new Vertex(1.0, 0.0, 3.5)); expectedErrorEdges.add(new Vertex(2.0, 0.0, 3.0)); - Geometry geom = m.getBuildings().get(0).getGeometries().get(0); + Geometry geom = m.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = geom.getCheckResult(CheckId.C_GE_S_NOT_CLOSED); Assert.assertEquals(ResultStatus.ERROR, cr.getResultStatus()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java index f15f1289..a53fd74e 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java @@ -106,7 +106,7 @@ public class PlanarTest { @Test public void testPlanarPolygon3() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { CheckResult cr = p.getCheckResult(CheckId.C_GE_P_NON_PLANAR); assertEquals(ResultStatus.OK, cr.getResultStatus()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PolygonIntersectingRingsTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PolygonIntersectingRingsTest.java index a4dadfb3..a18ed09e 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PolygonIntersectingRingsTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PolygonIntersectingRingsTest.java @@ -43,7 +43,7 @@ public class PolygonIntersectingRingsTest { @Test public void testSelfIntPolygon1() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { CheckResult cr = p.getCheckResult(CheckId.C_GE_P_INTERSECTING_RINGS); assertEquals(ResultStatus.OK, cr.getResultStatus()); } @@ -52,7 +52,7 @@ public class PolygonIntersectingRingsTest { @Test public void testSelfIntPolygon2() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { CheckResult cr = p.getCheckResult(CheckId.C_GE_P_INTERSECTING_RINGS); assertEquals(ResultStatus.OK, cr.getResultStatus()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/RingClosedCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/RingClosedCheckTest.java index 7cdf629c..9be6709e 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/RingClosedCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/RingClosedCheckTest.java @@ -91,7 +91,7 @@ public class RingClosedCheckTest extends TestCleanUp { @Test public void testClosedRing3() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { LinearRing r = p.getExteriorRing(); CheckResult cr = r.getCheckResult(CheckId.C_GE_R_NOT_CLOSED); assertEquals(ResultStatus.OK, cr.getResultStatus()); @@ -111,7 +111,7 @@ public class RingClosedCheckTest extends TestCleanUp { @Test public void testClosedRing5() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-LR-0003-T0002.gml", 1); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { LinearRing r = p.getExteriorRing(); CheckResult cr = r.getCheckResult(CheckId.C_GE_R_NOT_CLOSED); assertEquals(ResultStatus.OK, cr.getResultStatus()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/RingSelfIntersectionTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/RingSelfIntersectionTest.java index 9b9da948..76e04e96 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/RingSelfIntersectionTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/RingSelfIntersectionTest.java @@ -101,7 +101,7 @@ public class RingSelfIntersectionTest { @Test public void testSelfIntPoints4() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { LinearRing r = p.getExteriorRing(); CheckResult cr = r.getCheckResult(CheckId.C_GE_R_SELF_INTERSECTION); assertEquals(ResultStatus.OK, cr.getResultStatus()); @@ -131,7 +131,7 @@ public class RingSelfIntersectionTest { @Test public void testSelfIntPoints7() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-LR-0004-T0005.gml", 2); - for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { + for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) { LinearRing r = p.getExteriorRing(); CheckResult cr = r.getCheckResult(CheckId.C_GE_R_SELF_INTERSECTION); assertEquals(ResultStatus.OK, cr.getResultStatus()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/ShellNotClosedSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/ShellNotClosedSystemTest.java index e26695bc..f5100249 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/ShellNotClosedSystemTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/ShellNotClosedSystemTest.java @@ -43,7 +43,7 @@ public class ShellNotClosedSystemTest { @Test public void testShellNotClosed1() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NOT_CLOSED); assertEquals(ResultStatus.OK, cr.getResultStatus()); } @@ -51,7 +51,7 @@ public class ShellNotClosedSystemTest { @Test public void testShellNotClosed2() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0002-T0001.gml"); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NOT_CLOSED); assertEquals(ResultStatus.ERROR, cr.getResultStatus()); assertEquals(ErrorId.GE_S_NOT_CLOSED, cr.getError().getErrorId()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TestUtil.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TestUtil.java index f1dd8d20..48e82e8d 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TestUtil.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TestUtil.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; @@ -89,15 +90,16 @@ public class TestUtil { if (gmlId == null) { return null; } - Polygon result = getPolygonById(gmlId, model.getBuildings()); + Polygon result = getPolygonById(gmlId, model.getBuildings().toList()); + if (result != null) { return result; } - result = getPolygonById(gmlId, model.getVegetation()); + result = getPolygonById(gmlId, model.getVegetation().toList()); if (result != null) { return result; } - return getPolygonById(gmlId, model.getTransportation()); + return getPolygonById(gmlId, model.getTransportation().toList()); } private static Polygon getPolygonById(String gmlId, List features) { @@ -136,22 +138,17 @@ public class TestUtil { if (featureId == null) { return null; } - for (CityObject co : model.getBuildings()) { - if (featureId.equals(co.getGmlId().getGmlString())) { - return co; - } + Optional result = null; + result = model.getBuildings().filter(b -> featureId.equals(b.getGmlId().toString())).findFirst(); + if (result.isPresent()){ + return result.get(); } - for (CityObject co : model.getTransportation()) { - if (featureId.equals(co.getGmlId().getGmlString())) { - return co; - } + result = model.getTransportation().filter(t-> featureId.equals(t.getGmlId().toString())).findFirst(); + if (result.isPresent()){ + return result.get(); } - for (CityObject co : model.getVegetation()) { - if (featureId.equals(co.getGmlId().getGmlString())) { - return co; - } - } - return null; - } + result = model.getVegetation().filter(t-> featureId.equals(t.getGmlId().toString())).findFirst(); + return result.orElse(null); + } } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TooFewPolygonsSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TooFewPolygonsSystemTest.java index 4cf0caa6..0bf43727 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TooFewPolygonsSystemTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/TooFewPolygonsSystemTest.java @@ -43,7 +43,7 @@ public class TooFewPolygonsSystemTest { @Test public void testTooFewPolygons1() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_TOO_FEW_POLYGONS); assertEquals(ResultStatus.OK, cr.getResultStatus()); } @@ -51,7 +51,7 @@ public class TooFewPolygonsSystemTest { @Test public void testTooFewPolygons2() throws CityGmlParseException, IOException, InvalidGmlFileException { CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0001-T0001.gml"); - Geometry g = c.getBuildings().get(0).getGeometries().get(0); + Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_TOO_FEW_POLYGONS); assertEquals(ResultStatus.ERROR, cr.getResultStatus()); assertEquals(ErrorId.GE_S_TOO_FEW_POLYGONS, cr.getError().getErrorId()); diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealAllPolygonsWrongOrientationErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealAllPolygonsWrongOrientationErrorTest.java index 58b52a5f..18146a77 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealAllPolygonsWrongOrientationErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealAllPolygonsWrongOrientationErrorTest.java @@ -24,11 +24,11 @@ public class HealAllPolygonsWrongOrientationErrorTest { CityDoctorModel model = TestUtil.loadCityModel(path, config); Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealConsecutivePointsSameErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealConsecutivePointsSameErrorTest.java index 0e530923..efd83d48 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealConsecutivePointsSameErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealConsecutivePointsSameErrorTest.java @@ -26,11 +26,11 @@ public class HealConsecutivePointsSameErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_R_DUPLICATE_POINT)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_R_DUPLICATE_POINT)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealHoleOutsideErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealHoleOutsideErrorTest.java index 50ca0b47..909bd46c 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealHoleOutsideErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealHoleOutsideErrorTest.java @@ -25,11 +25,11 @@ public class HealHoleOutsideErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Building building = model.getBuildings().get(0); + Building building = model.getBuildings().toList().get(0); Assert.assertTrue(building.containsError(CheckId.C_GE_P_HOLE_OUTSIDE)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(building.containsError(CheckId.C_GE_P_HOLE_OUTSIDE)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealMissingSolidTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealMissingSolidTest.java index 0b064845..07adf8c1 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealMissingSolidTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealMissingSolidTest.java @@ -138,7 +138,7 @@ public class HealMissingSolidTest { model.saveAs(createSolidFile.toString(), false); CityDoctorModel parsedModel = CityGmlParser.parseCityGmlFile(createSolidFile.toString(), config.getParserConfiguration()); - Building building = parsedModel.getBuildings().get(0); + Building building = parsedModel.getBuildings().toList().get(0); assertEquals(1, building.getGeometries().size()); Geometry geometry2 = building.getGeometries().get(0); assertEquals(GeometryType.SOLID, geometry2.getType()); @@ -162,7 +162,7 @@ public class HealMissingSolidTest { new RequirementConfiguration(false)); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS_withoutSolid.gml", config.getParserConfiguration()); - Building b = model.getBuildings().get(0); + Building b = model.getBuildings().toList().get(0); assertTrue(b.getGeometries().isEmpty()); Checker c = new Checker(config, model); c.runChecks(); diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealNonManifoldEdgeErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealNonManifoldEdgeErrorTest.java index 11025411..2bfca2e7 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealNonManifoldEdgeErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealNonManifoldEdgeErrorTest.java @@ -25,11 +25,11 @@ public class HealNonManifoldEdgeErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_S_NON_MANIFOLD_EDGE)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_S_NON_MANIFOLD_EDGE)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPlanarPolygonErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPlanarPolygonErrorTest.java index 7b5576bd..5359f2d6 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPlanarPolygonErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPlanarPolygonErrorTest.java @@ -43,12 +43,12 @@ public class HealPlanarPolygonErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_P_NON_PLANAR)); Healer healer = new Healer(c); healer.setNumberOfIterations(2); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_P_NON_PLANAR)); diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPolygonWithoutSurfaceTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPolygonWithoutSurfaceTest.java index 28e6832c..0ccc24f0 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPolygonWithoutSurfaceTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPolygonWithoutSurfaceTest.java @@ -35,11 +35,11 @@ public class HealPolygonWithoutSurfaceTest { Checker c = new Checker(config, model); c.runChecks(); - Building building = model.getBuildings().get(0); + Building building = model.getBuildings().toList().get(0); Assert.assertTrue(building.containsError(CheckId.C_SE_POLYGON_WITHOUT_SURFACE)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(building.containsError(CheckId.C_SE_POLYGON_WITHOUT_SURFACE)); @@ -49,7 +49,7 @@ public class HealPolygonWithoutSurfaceTest { CityDoctorModel loadedModel = TestUtil.loadCityModel(outputFile.toString(), config); c = new Checker(config, loadedModel); c.runChecks(); - building = loadedModel.getBuildings().get(0); + building = loadedModel.getBuildings().toList().get(0); Assert.assertFalse(building.containsError(CheckId.C_SE_POLYGON_WITHOUT_SURFACE)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPolygonWrongOrientationErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPolygonWrongOrientationErrorTest.java index 7f640b96..9d8e1b8d 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPolygonWrongOrientationErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealPolygonWrongOrientationErrorTest.java @@ -41,11 +41,11 @@ public class HealPolygonWrongOrientationErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_S_POLYGON_WRONG_ORIENTATION)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_S_POLYGON_WRONG_ORIENTATION)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealRingNotClosedErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealRingNotClosedErrorTest.java index 6622e355..e8f03e12 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealRingNotClosedErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealRingNotClosedErrorTest.java @@ -25,11 +25,11 @@ public class HealRingNotClosedErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_R_NOT_CLOSED)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_R_NOT_CLOSED)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealRingSelfIntErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealRingSelfIntErrorTest.java index 8a33573b..4408e122 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealRingSelfIntErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealRingSelfIntErrorTest.java @@ -190,11 +190,11 @@ public class HealRingSelfIntErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_R_SELF_INTERSECTION)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_R_SELF_INTERSECTION)); } @@ -208,11 +208,11 @@ public class HealRingSelfIntErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_R_SELF_INTERSECTION)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_R_SELF_INTERSECTION)); } @@ -228,11 +228,11 @@ public class HealRingSelfIntErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_R_SELF_INTERSECTION)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_R_SELF_INTERSECTION)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealSameOrientationErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealSameOrientationErrorTest.java index e5b64193..f0802787 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealSameOrientationErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealSameOrientationErrorTest.java @@ -26,11 +26,11 @@ public class HealSameOrientationErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Assert.assertTrue(geom.containsError(CheckId.C_GE_P_ORIENTATION_RINGS_SAME)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_P_ORIENTATION_RINGS_SAME)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealSolidNotClosedErrorTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealSolidNotClosedErrorTest.java index 5a8713a8..06e4d9e1 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealSolidNotClosedErrorTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealSolidNotClosedErrorTest.java @@ -439,7 +439,7 @@ public class HealSolidNotClosedErrorTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); geom.updateVertices(); for (Vertex v : geom.getVertices()) { v.setX(v.getX() - 10); @@ -449,7 +449,7 @@ public class HealSolidNotClosedErrorTest { Healer healer = new Healer(c); //healer.setNumberOfIterations(2); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); Assert.assertFalse(geom.containsError(CheckId.C_GE_S_NOT_CLOSED)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealTooFewPointsTest.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealTooFewPointsTest.java index eac3472b..8d22d6e6 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealTooFewPointsTest.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/HealTooFewPointsTest.java @@ -27,11 +27,11 @@ public class HealTooFewPointsTest { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); assertTrue(geom.containsError(CheckId.C_GE_R_TOO_FEW_POINTS)); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); assertFalse(geom.containsError(CheckId.C_GE_R_TOO_FEW_POINTS)); } diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/TestHealer.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/TestHealer.java index a36ce00d..63ccbd05 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/TestHealer.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/TestHealer.java @@ -45,7 +45,7 @@ public class TestHealer { c.runChecks(); Building b = null; - for (Building building : model.getBuildings()) { + for (Building building : model.getBuildings().toList()) { if (building.getGmlId().getGmlString().equals("DEBE08YYF0004KZs")) { b = building; break; @@ -125,8 +125,8 @@ public class TestHealer { c.runChecks(); Healer healer = new Healer(c); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); - healer.healCityObject(model.getBuildings().get(0)); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); + healer.healCityObject(model.getBuildings().toList().get(0)); c.runChecks(); assertFalse(geom.containsAnyError()); @@ -151,11 +151,11 @@ public class TestHealer { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Healer healer = new Healer(c); healer.setNumberOfIterations(33); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); c.runChecks(); assertFalse(geom.containsAnyError()); @@ -171,10 +171,10 @@ public class TestHealer { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Healer healer = new Healer(c); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); c.runChecks(); assertFalse(geom.containsAnyError()); @@ -190,11 +190,11 @@ public class TestHealer { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Healer healer = new Healer(c); healer.setNumberOfIterations(83); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); c.runChecks(); assertFalse(geom.containsAnyError()); @@ -210,11 +210,11 @@ public class TestHealer { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Healer healer = new Healer(c); healer.setNumberOfIterations(165); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); c.runChecks(); assertFalse(geom.containsAnyError()); @@ -241,7 +241,7 @@ public class TestHealer { Checker c = new Checker(config, model); c.runChecks(); - Building b = model.getBuildings().get(0); + Building b = model.getBuildings().toList().get(0); Healer healer = new Healer(c); healer.healCityObject(b); c.runChecks(); @@ -261,11 +261,11 @@ public class TestHealer { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Healer healer = new Healer(c); healer.setNumberOfIterations(45); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); c.runChecks(); assertFalse(geom.containsAnyError()); @@ -297,10 +297,10 @@ public class TestHealer { Checker c = new Checker(config, model); c.runChecks(); - Geometry geom = model.getBuildings().get(0).getGeometries().get(0); + Geometry geom = model.getBuildings().toList().get(0).getGeometries().get(0); Healer healer = new Healer(c); healer.setNumberOfIterations(100); - healer.healCityObject(model.getBuildings().get(0)); + healer.healCityObject(model.getBuildings().toList().get(0)); c.runChecks(); assertFalse(geom.containsAnyError()); diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/TestUtil.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/TestUtil.java index ca21944c..72a2450f 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/TestUtil.java +++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/TestUtil.java @@ -38,15 +38,15 @@ public class TestUtil { if (gmlId == null) { return null; } - Polygon result = getPolygonById(gmlId, model.getBuildings()); + Polygon result = getPolygonById(gmlId, model.getBuildings().toList()); if (result != null) { return result; } - result = getPolygonById(gmlId, model.getVegetation()); + result = getPolygonById(gmlId, model.getVegetation().toList()); if (result != null) { return result; } - return getPolygonById(gmlId, model.getTransportation()); + return getPolygonById(gmlId, model.getTransportation().toList()); } private static Polygon getPolygonById(String gmlId, List features) { @@ -85,17 +85,17 @@ public class TestUtil { if (featureId == null) { return null; } - for (CityObject co : model.getBuildings()) { + for (CityObject co : model.getBuildings().toList()) { if (featureId.equals(co.getGmlId().getGmlString())) { return co; } } - for (CityObject co : model.getTransportation()) { + for (CityObject co : model.getTransportation().toList()) { if (featureId.equals(co.getGmlId().getGmlString())) { return co; } } - for (CityObject co : model.getVegetation()) { + for (CityObject co : model.getVegetation().toList()) { if (featureId.equals(co.getGmlId().getGmlString())) { return co; } -- GitLab From 3cb4b163005ebc8ccb07c97947adf3f6a34e7e2b Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 16 Oct 2025 12:07:54 +0200 Subject: [PATCH 040/118] Build: Remove broken modules from build --- CityDoctorParent/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CityDoctorParent/pom.xml b/CityDoctorParent/pom.xml index e5308cae..079e21e8 100644 --- a/CityDoctorParent/pom.xml +++ b/CityDoctorParent/pom.xml @@ -299,9 +299,9 @@ Extensions/CityDoctorGUI - Extensions/CityDoctorHealer - Extensions/CityDoctorHealerGenetic - Extensions/CityDoctorHealerGUI + + + \ No newline at end of file -- GitLab From 269e45c7f0bfc192381c6f51083c7d5af7f4abda Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 16 Oct 2025 15:25:30 +0200 Subject: [PATCH 041/118] Refactor: Make debugging logs debug log-level --- .../citydoctor2/datastructure/ImplicitGeometryHolder.java | 3 +++ .../hft/stuttgart/citydoctor2/parser/CityGmlParser.java | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java index 22c43541..75fe723c 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java @@ -76,6 +76,9 @@ public class ImplicitGeometryHolder extends Geometry { logger.warn("RelativeGeometries of the following gmlIDs could not be resolved: {}", missingGeoms); } deferredRelGeomObjects.clear(); + if (logger.isDebugEnabled()) { + logger.debug("Resolving HRef geometries finished"); + } } private ImplicitGeometryHolder(Lod lod) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 99344413..8d6ee36a 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -599,13 +599,15 @@ public class CityGmlParser { CityModel cModel = mapper.getModel().getCityModel(); // remove those that should have been parsed - logger.info("Getting parsed object from CityDoctorModel"); + logger.debug("Getting parsed object from CityDoctorModel"); List parsedCityObjects = mapper.getModel().createFeatureStream().filter(Objects::nonNull) .map(CityObject::getGmlObject).toList(); - logger.info("Removing all parsed city objects from acos"); + logger.debug("Removing all parsed city objects from acos"); acos.removeAll(parsedCityObjects); // re-add all not parsed objects - logger.info("Resolving HRefs"); + if (logger.isInfoEnabled() && verbose){ + logger.info("Resolving HRefs"); + } for (AbstractCityObject aco : acos) { cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); } -- GitLab From 616fee879652cbd224b7645a6b1b150522c3a3de Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 16 Oct 2025 15:27:45 +0200 Subject: [PATCH 042/118] Fix: Fix database failure due to special chars --- .../de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index dbdccd97..01f67faa 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -47,7 +47,7 @@ public class DatabaseHandler { } public DatabaseHandler(String fileName){ - this.fileName = fileName; + this.fileName = "\"" + fileName + "\""; setupFeaturesTable(); } -- GitLab From a9ebb77b15b43d63b5be76a06fc85e36dfbc1a51 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 16 Oct 2025 15:32:26 +0200 Subject: [PATCH 043/118] Fix: Cached CityObjects leaking to other files Invalidates all Cache entries before starting parsing of a new file to prevent cached CityObjects being unintentionally transferred to wrong database table. --- .../de/hft/stuttgart/citydoctor2/database/CacheManager.java | 1 + .../hft/stuttgart/citydoctor2/database/CityObjectCache.java | 1 + .../de/hft/stuttgart/citydoctor2/database/FeatureCache.java | 4 ++++ .../hft/stuttgart/citydoctor2/database/UnconnectedCache.java | 4 ++++ 4 files changed, 10 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java index fb36bba9..c9d0c4b3 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -18,6 +18,7 @@ public class CacheManager { if (cache == null){ cache = new FeatureCache(filename); } else { + cache.resetCache(); cache.changeFileName(filename); } return cache; diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java index bc949ec3..98a06b03 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -19,4 +19,5 @@ public interface CityObjectCache { int getFeatureCount(); + void resetCache(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index c269e482..d51ae8b4 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -65,6 +65,10 @@ public class FeatureCache implements CityObjectCache{ return handler.getFeatureCount(); } + @Override + public void resetCache(){ + cache.invalidateAll(); + } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java index f346ec8c..72d5637a 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java @@ -44,4 +44,8 @@ public class UnconnectedCache implements CityObjectCache{ public int getFeatureCount() { return cache.size(); } + + public void resetCache(){ + cache.clear(); + } } -- GitLab From b9f844bd50c99c424033adca44f3eb2bd9f2c38d Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 17 Oct 2025 16:00:30 +0200 Subject: [PATCH 044/118] Fix: Fix resolving of LibraryObjects in ZIP-files --- .../citydoctor2/database/CacheManager.java | 8 ++++++++ .../citydoctor2/database/CityObjectCache.java | 3 +++ .../citydoctor2/database/DatabaseHandler.java | 7 ++++++- .../citydoctor2/database/FeatureCache.java | 18 ++++++++++++++++++ .../citydoctor2/database/UnconnectedCache.java | 9 +++++++-- .../datastructure/LibraryObject.java | 16 +++++++++++++++- .../mapper/citygml3/Citygml3FeatureMapper.java | 1 + .../citydoctor2/parser/CityGmlParser.java | 6 ------ 8 files changed, 58 insertions(+), 10 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java index c9d0c4b3..efae1a1e 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -1,8 +1,12 @@ package de.hft.stuttgart.citydoctor2.database; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; import org.apache.commons.io.FilenameUtils; import java.io.File; +import java.util.List; public class CacheManager { @@ -24,6 +28,10 @@ public class CacheManager { return cache; } + public static List retrieveLibraryObject(CityGmlZipEntry entry) throws InvalidGmlFileException { + return entry.getModel().getCache().retrieveLibraryObject(entry.getDisplayName()); + } + public static UnconnectedCache getUnconnectedCache(){ return new UnconnectedCache(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java index 98a06b03..fbfb7d62 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -2,6 +2,7 @@ package de.hft.stuttgart.citydoctor2.database; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import java.util.List; import java.util.stream.Stream; @@ -20,4 +21,6 @@ public interface CityObjectCache { int getFeatureCount(); void resetCache(); + + List retrieveLibraryObject(String fileName) throws InvalidGmlFileException; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 01f67faa..d0147daa 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -6,6 +6,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import javafx.scene.control.Alert; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.SerializationException; import org.apache.commons.lang3.SerializationUtils; import org.apache.logging.log4j.LogManager; @@ -47,7 +48,7 @@ public class DatabaseHandler { } public DatabaseHandler(String fileName){ - this.fileName = "\"" + fileName + "\""; + this.fileName = FilenameUtils.removeExtension(fileName).replaceAll(" [^a-zA-Z0-9]",""); setupFeaturesTable(); } @@ -58,6 +59,10 @@ public class DatabaseHandler { + } + + public String getFileName() { + return fileName; } public void setupFeaturesTable() { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index d51ae8b4..8c016f61 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -5,6 +5,7 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.RemovalCause; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import java.util.HashMap; import java.util.List; @@ -70,5 +71,22 @@ public class FeatureCache implements CityObjectCache{ cache.invalidateAll(); } + @Override + public List retrieveLibraryObject(String fileName) throws InvalidGmlFileException { + String prevHandler = handler.getFileName(); + this.changeFileName(fileName); + int featureCount = getFeatureCount(); + if (featureCount != 1){ + if (featureCount == 0){ + throw new InvalidGmlFileException(String.format("LibraryObject ZIP-entry %s contains no Feature", fileName)); + } else { + throw new InvalidGmlFileException(String.format("LibraryObject ZIP-entry %s contains more than one Feature", fileName)); + } + } + List objects = handler.getAllCityObjects(); + this.changeFileName(prevHandler); + return objects; + } + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java index 72d5637a..94c042b6 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java @@ -2,6 +2,7 @@ package de.hft.stuttgart.citydoctor2.database; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import java.util.HashMap; import java.util.List; @@ -17,7 +18,6 @@ public class UnconnectedCache implements CityObjectCache{ UnconnectedCache() { cache = new HashMap<>(); - } @Override @@ -46,6 +46,11 @@ public class UnconnectedCache implements CityObjectCache{ } public void resetCache(){ - cache.clear(); + // + } + + @Override + public List retrieveLibraryObject(String fileName) throws InvalidGmlFileException { + return List.copyOf(cache.values()); } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java index 62fe94ed..8cdd2554 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java @@ -1,5 +1,6 @@ package de.hft.stuttgart.citydoctor2.datastructure; +import de.hft.stuttgart.citydoctor2.database.CacheManager; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; @@ -87,7 +88,7 @@ public class LibraryObject extends Geometry { Geometry geo = null; try { entry.loadEntry(config, true); - geo = getProtoGeometry(entry.getModel()); + geo = getProtoGeometry(entry); } catch (InvalidGmlFileException e) { logger.error(e.getStackTrace()); } catch (CityGmlParseException e) { @@ -111,4 +112,17 @@ public class LibraryObject extends Geometry { return objects.get(0).getHighestLodGeometry(); } + private static Geometry getProtoGeometry(CityGmlZipEntry entry) throws InvalidGmlFileException, CityGmlParseException { + if (entry.getModel() == null) { + throw new CityGmlParseException("CityDoctorModel of referenced LibraryObject is null"); + } + List objects = CacheManager.retrieveLibraryObject(entry); + if (objects.isEmpty()) { + throw new InvalidGmlFileException("Referenced library-object gml file does not contain any CityGML objects!"); + } else if (objects.size() > 1) { + throw new InvalidGmlFileException("Referenced library-object gml file contains more than one CityGML object!"); + } + return objects.get(0).getHighestLodGeometry(); + } + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index 1978e4fb..2eaa0de8 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -1038,6 +1038,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { Path loPath = zipEntry.resolveLibraryObjectPath(libraryObjectPath); CityGmlZipEntry libEntry = zipEntry.getArchive().getEntry(loPath.toString()); libObj = LibraryObject.of(libEntry, config); + } if (libObj != null) { igh = new ImplicitGeometryHolder(ig, libObj); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 8d6ee36a..bf3bdb1e 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -289,12 +289,6 @@ public class CityGmlParser { } } - - public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l) - throws CityGmlParseException, InvalidGmlFileException, IOException { - return parseCityGmlZipEntry(entry, config, l, false); - } - public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, boolean nested) throws CityGmlParseException, InvalidGmlFileException, IOException { -- GitLab From 97349c7608d7cd4b7e05b3553222eed589cb08bb Mon Sep 17 00:00:00 2001 From: riegel Date: Thu, 23 Oct 2025 14:48:01 +0200 Subject: [PATCH 045/118] Refactor: Batch sql commits during parsing --- .../citydoctor2/database/CacheManager.java | 6 ++ .../citydoctor2/database/CityObjectCache.java | 2 +- .../citydoctor2/database/DatabaseHandler.java | 64 +++++++++++--- .../citydoctor2/database/FeatureCache.java | 78 ++++++++++++++++- .../database/UnconnectedCache.java | 2 +- .../datastructure/CityDoctorModel.java | 84 ++++++++++++++++++- .../citydoctor2/parser/CityGmlParser.java | 67 ++++++++++----- .../database/FeatureCacheTest.java | 24 ++++-- .../geometry/DegeneratedPolygonCheckTest.java | 6 +- 9 files changed, 287 insertions(+), 46 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java index efae1a1e..038ebd08 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -32,6 +32,12 @@ public class CacheManager { return entry.getModel().getCache().retrieveLibraryObject(entry.getDisplayName()); } + public static void finishParsing(){ + if (cache != null){ + cache.finishParsing(); + } + } + public static UnconnectedCache getUnconnectedCache(){ return new UnconnectedCache(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java index fbfb7d62..ad696f04 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -18,7 +18,7 @@ public interface CityObjectCache { Stream getStream(List ids); - int getFeatureCount(); + int getTotalFeatureCount(); void resetCache(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index d0147daa..0d140b21 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -23,27 +23,24 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class DatabaseHandler { private static final Logger logger = LogManager.getLogger(DatabaseHandler.class); private static final String DB_NAME = "cd_db"; private final String fileName; - + // TODO: Change handler back to using only a single featuretable static{ // Creates the Database at initialization try { +// Cleanup hook Runtime.getRuntime().addShutdownHook(new Thread(() -> {})); H2GISDBFactory.createSpatialDataBase(DB_NAME, true, ";FILE_LOCK=SOCKET"); } catch (Exception e) { logger.fatal("FATAL ERROR: Creation of embedded database failed!"); logger.fatal(e.getMessage()); - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle("FATAL ERROR"); - alert.setHeaderText("FATAL ERROR"); - alert.setContentText("Creation of embedded database failed!"); - alert.showAndWait(); } } @@ -60,7 +57,7 @@ public class DatabaseHandler { } - + //TODO: Refactor this to remove filename public String getFileName() { return fileName; } @@ -73,12 +70,15 @@ public class DatabaseHandler { logger.debug("Dropped existing features table"); } } + // TODO: Remove type and add a row with an atomic integer model id + // TODO: Refactor this to only use a single feature table try (PreparedStatement createPs = con.prepareStatement(String.format("CREATE TABLE %s (gmlid VARCHAR(255)" + - " PRIMARY KEY, bbox GEOMETRY, data BLOB, type VARCHAR(255) NOT NULL)", fileName))) { + " PRIMARY KEY, bbox GEOMETRY, data BLOB)", fileName))) { createPs.executeUpdate(); if (logger.isDebugEnabled()) { logger.debug("Created features table"); } + //CREATE SPATIAL INDEX GEO_TABLE_SPATIAL_INDEX ON GEO_TABLE(THE_GEOM); <- Create spatial id } } catch (SQLException e) { @@ -93,14 +93,13 @@ public class DatabaseHandler { */ public void marshallCityObject(CityObject co) { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format("MERGE INTO %s VALUES (?, ?, ?, ?)", fileName))) { + try (PreparedStatement ps = con.prepareStatement(String.format("MERGE INTO %s VALUES (?, ?, ?)", fileName))) { ps.setString(1, co.getGmlId().toString()); String bbox = BoundingBox.of(co).to2DWkt(); ps.setString(2, bbox); if (logger.isDebugEnabled()) { logger.debug("Attempting to serialize CityObject: {} ", co); } - ps.setString(4, co.getClass().getName()); try { byte[] bytes = SerializationUtils.serialize(co); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); @@ -118,6 +117,47 @@ public class DatabaseHandler { } } + public boolean tryMarshallCityObjectMap(Map map) { + // Id String for error logging + String id = ""; + try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (PreparedStatement ps = con.prepareStatement(String.format("MERGE INTO %s VALUES (?, ?, ?)", fileName))) { + con.setAutoCommit(false); + for (Map.Entry entry : map.entrySet()) { + CityObject co = entry.getValue(); + id = entry.getKey().toString(); + ps.setString(1, id); + String bbox = BoundingBox.of(co).to2DWkt(); + ps.setString(2, bbox); + if (logger.isTraceEnabled()) { + logger.trace("Attempting to serialize CityObject: {} ", co); + } + try { + byte[] bytes = SerializationUtils.serialize(co); + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ps.setBinaryStream(3, bis, bytes.length); + ps.addBatch(); + } catch (SerializationException e) { + logger.error("Serialization of CityObject {} failed",co.getGmlId()); + logger.error(e.getMessage()); + } + } + ps.executeBatch(); + con.commit(); + } catch (SQLException e) { + con.rollback(); + logger.error("Batch-commit aborted: Saving of CityObject {} to embedded database critically failed!", id); + logger.error(e.getMessage()); + return false; + } + } catch (SQLException e){ + logger.error("Connection to embedded database could not be opened!"); + logger.error(e.getMessage()); + return false; + } + return true; + } + /** * Retrieves a CityObject from the embedded database. * @param id the gmlId of the requested Feature @@ -126,7 +166,7 @@ public class DatabaseHandler { public CityObject unmarshallCityObject(GmlId id) { try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { try (PreparedStatement ps = con.prepareStatement(String.format( - "SELECT gmlid, bbox, data, type FROM %s WHERE gmlid = ?", fileName))) { + "SELECT gmlid, bbox, data FROM %s WHERE gmlid = ?", fileName))) { ps.setString(1, id.toString()); ResultSet rs = ps.executeQuery(); if (rs.next()) { @@ -156,7 +196,7 @@ public class DatabaseHandler { public List getAllCityObjects() { List cos = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid, bbox, data, type FROM %s", fileName))) { + try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid, bbox, data FROM %s", fileName))) { ResultSet rs = ps.executeQuery(); cos.addAll(retrieveCityObjects(rs)); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index 8c016f61..3b819d6a 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -6,10 +6,13 @@ import com.github.benmanes.caffeine.cache.RemovalCause; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; @@ -17,9 +20,15 @@ import java.util.stream.Stream; * Cache for CityObjects */ public class FeatureCache implements CityObjectCache{ + + + private static final Logger logger = LogManager.getLogger(FeatureCache.class); + private final LoadingCache cache; private DatabaseHandler handler; private final Map handlerList = new HashMap<>(); + private AtomicInteger putsCounter = new AtomicInteger(0); + private boolean parsingFinished = false; FeatureCache(String fileName) { handler = new DatabaseHandler(fileName); @@ -33,13 +42,56 @@ public class FeatureCache implements CityObjectCache{ @Override public void put(CityObject cityObject) { - handler.marshallCityObject(cityObject); cache.put(cityObject.getGmlId(), cityObject); + /* Batch marshall during parsing to improve performance + */ + if (!parsingFinished){ + int count = putsCounter.incrementAndGet(); + if (count >= 2500) { + batchMarshall(); + } + } + } + + + + + private void batchMarshall(){ + logger.trace("Setting up batch Marshaller"); + Map cacheContent= new HashMap<>(cache.asMap()); + cache.invalidateAll(); + putsCounter.set(0); + + Thread batcher = new Thread(() -> { + logger.trace("Entered batch Marshaller"); + boolean success = handler.tryMarshallCityObjectMap(cacheContent); + if (!success) { + logger.trace("Batch marshalling failed, falling back to one-by-one marshalling"); + // Batch marshalling failed, marshall them one-by-one + for (Map.Entry entry : cacheContent.entrySet()) { + handler.marshallCityObject(entry.getValue()); + } + } + logger.trace("Batch marshalling done"); + }); + batcher.start(); + logger.trace("Batch Marshaller started"); } @Override public CityObject get(GmlId id) { - return cache.get(id); + CityObject result = cache.get(id); + if (result == null){ + logger.trace("Cache-value missing for {}", id); + //Key has null value, try to restore from Database + result = handler.unmarshallCityObject(id); + if (result == null){ + logger.error("CityObject with id {} is null in cache and database", id); + } else { + cache.put(id, result); + } + } + return result; } public void changeFileName(String fileName){ @@ -62,20 +114,23 @@ public class FeatureCache implements CityObjectCache{ } @Override - public int getFeatureCount() { + public int getTotalFeatureCount() { return handler.getFeatureCount(); } @Override public void resetCache(){ + logger.trace("Resetting cache"); cache.invalidateAll(); + putsCounter.set(0); + parsingFinished = false; } @Override public List retrieveLibraryObject(String fileName) throws InvalidGmlFileException { String prevHandler = handler.getFileName(); this.changeFileName(fileName); - int featureCount = getFeatureCount(); + int featureCount = getTotalFeatureCount(); if (featureCount != 1){ if (featureCount == 0){ throw new InvalidGmlFileException(String.format("LibraryObject ZIP-entry %s contains no Feature", fileName)); @@ -88,5 +143,20 @@ public class FeatureCache implements CityObjectCache{ return objects; } + public void finishParsing() { + if (!parsingFinished) { + HashMap cacheContent= new HashMap<>(cache.asMap()); + boolean success = handler.tryMarshallCityObjectMap(cacheContent); + if (!success) { + logger.trace("Batch marshalling failed, falling back to one-by-one marshalling"); + // Batch marshalling failed, marshall them one-by-one + for (Map.Entry entry : cacheContent.entrySet()) { + handler.marshallCityObject(entry.getValue()); + } + } + parsingFinished = true; + } + } + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java index 94c042b6..8478fcd9 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java @@ -41,7 +41,7 @@ public class UnconnectedCache implements CityObjectCache{ } @Override - public int getFeatureCount() { + public int getTotalFeatureCount() { return cache.size(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 61a99a8a..84b8019b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -27,7 +27,6 @@ import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.writer.CityGMLWriterUtils; import de.hft.stuttgart.quality.model.types.ValidationPlan; -import org.apache.commons.io.FilenameUtils; import org.citygml4j.core.model.CityGMLVersion; import org.citygml4j.core.model.core.CityModel; @@ -131,6 +130,53 @@ public class CityDoctorModel { getTunnels(), getWater(), getCityFurniture(), getGenericCityObjects()).flatMap(co -> co); } + public List getFeatureIds() { + List featureIds = new ArrayList<>(); + featureIds.addAll(buildings); + featureIds.addAll(vegetation); + featureIds.addAll(bridges); + featureIds.addAll(land); + featureIds.addAll(roads); + featureIds.addAll(tunnels); + featureIds.addAll(water); + featureIds.addAll(cityfurniture); + featureIds.addAll(genericObjects); + return featureIds; + + } + + public boolean containsGmlId(String id) { + if (id == null) { + return false; + } + GmlId gmlId = new GmlId(id); + if (buildings.contains(gmlId)) { + return true; + } + if (vegetation.contains(gmlId)) { + return true; + } + if (bridges.contains(gmlId)) { + return true; + } + if (land.contains(gmlId)) { + return true; + } + if (roads.contains(gmlId)) { + return true; + } + if (tunnels.contains(gmlId)) { + return true; + } + if (water.contains(gmlId)) { + return true; + } + if (cityfurniture.contains(gmlId)) { + return true; + } + return genericObjects.contains(gmlId); + } + public void accept(CheckableVisitor visitor) { createFeatureStream().forEach(cityObject -> cityObject.accept(visitor)); } @@ -260,6 +306,10 @@ public class CityDoctorModel { return buildings.stream().map(co -> (Building) cache.get(co)); } + public List getBuildingIds() { + return buildings; + } + public int getBuildingCount(){ return buildings.size(); } @@ -273,6 +323,10 @@ public class CityDoctorModel { return bridges.stream().map(co -> (BridgeObject) cache.get(co)); } + public List getBridgeIds() { + return bridges; + } + public int getBridgeCount(){ return bridges.size(); } @@ -286,6 +340,10 @@ public class CityDoctorModel { return cityfurniture.stream().map(co -> (CityFurniture) cache.get(co)); } + public List getCityFurnitureIds() { + return cityfurniture; + } + public int getCityFurnitureCount(){ return cityfurniture.size(); } @@ -299,6 +357,10 @@ public class CityDoctorModel { return genericObjects.stream().map(co -> (GenericCityObject) cache.get(co)); } + public List getGenericCityObjectIds() { + return genericObjects; + } + public int getGenericCityObjectCount(){ return genericObjects.size(); } @@ -312,6 +374,10 @@ public class CityDoctorModel { return roads.stream().map(co -> (TransportationObject) cache.get(co)); } + public List getTransportationIds() { + return roads; + } + public int getTransportationCount(){ return roads.size(); } @@ -325,6 +391,10 @@ public class CityDoctorModel { return tunnels.stream().map(co -> (Tunnel) cache.get(co)); } + public List getTunnelIds() { + return tunnels; + } + public int getTunnelCount(){ return tunnels.size(); } @@ -338,6 +408,10 @@ public class CityDoctorModel { return water.stream().map(co -> (WaterObject) cache.get(co)); } + public List getWaterIds() { + return water; + } + public int getWaterCount(){ return water.size(); } @@ -361,6 +435,10 @@ public class CityDoctorModel { return land.stream().map(cache::get); } + public List getLandIds() { + return land; + } + public int getLandCount(){ return land.size(); } @@ -374,6 +452,10 @@ public class CityDoctorModel { return vegetation.stream().map(co -> (Vegetation) cache.get(co)); } + public List getVegetationIds() { + return vegetation; + } + public int getVegetationCount(){ return vegetation.size(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index bf3bdb1e..8e5bf74c 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -30,7 +30,6 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -45,6 +44,7 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +import de.hft.stuttgart.citydoctor2.database.CacheManager; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; import org.apache.logging.log4j.Level; @@ -572,6 +572,9 @@ public class CityGmlParser { // object members are replaced by href in model // need to remove the refs and re-add unparsed objects List acos = new ArrayList<>(); + CityModel cgmlModel = null; + CityDoctorModel cdModel = mapper.getModel(); + List queuedObjects = new ArrayList<>(); while (reader.hasNext()) { AbstractFeature chunk = reader.next(); version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); @@ -579,39 +582,65 @@ public class CityGmlParser { cModel.setCityObjectMembers(null); mapper.setCityModel(cModel); mapper.setCityGMLVersion(version); + cgmlModel = cModel; + + if (!queuedObjects.isEmpty()) { + for (AbstractCityObject aco : queuedObjects) { + if (!cdModel.containsGmlId(aco.getId())) { + cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + } + } + } } else if (chunk instanceof AbstractCityObject aco) { - acos.add(aco); + acos.add(aco); // getid + String id = aco.getId(); aco.accept(mapper); + if (cgmlModel == null) { + // Add object to queue till the CityModel has been parsed + queuedObjects.add(aco); + }else if (!cdModel.containsGmlId(id)){ + cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + } + // TODO: add check if aco was parsed successfully, add aco to cmodel if it wasnt } } if (mapper.getModel().getCityModel() == null) { // file does not contain a city model? // create it for now + logger.warn("File does not contain a CityModel definition! Temporary CityModel has been created."); mapper.setCityModel(new CityModel()); + for (AbstractCityObject aco : queuedObjects) { + if (!cdModel.containsGmlId(aco.getId())) { + mapper.getModel().getCityModel().getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + } + } } - CityModel cModel = mapper.getModel().getCityModel(); - - // remove those that should have been parsed - logger.debug("Getting parsed object from CityDoctorModel"); - List parsedCityObjects = mapper.getModel().createFeatureStream().filter(Objects::nonNull) - .map(CityObject::getGmlObject).toList(); - logger.debug("Removing all parsed city objects from acos"); - acos.removeAll(parsedCityObjects); - // re-add all not parsed objects - if (logger.isInfoEnabled() && verbose){ - logger.info("Resolving HRefs"); - } - for (AbstractCityObject aco : acos) { - cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); - } +// cgmlModel = mapper.getModel().getCityModel(); +// +// // remove those that should have been parsed +// if (logger.isInfoEnabled() && verbose){ +// logger.info("Resolving HRefs"); +// } +//// List parsedCityObjects = mapper.getModel().createFeatureStream().filter(Objects::nonNull) +//// .map(CityObject::getGmlObject).toList(); +//// mapper.getModel().createFeatureStream().filter(Objects::nonNull) +//// .map(CityObject::getGmlObject).forEach(acos::remove); +// mapper.getModel().getFeatureIds().forEach(id -> {acos.removeIf(aco -> aco.getId().equals(id.getGmlString()));}); +//// acos.removeAll(parsedCityObjects); +// // re-add all not parsed objects +// for (AbstractCityObject aco : acos) { +// cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); +// } + // After parsing is complete, try to resolve deferred RelativeGeometries + + ImplicitGeometryHolder.resolveDeferredObjects(mapper.getModel().getCache()); if (logger.isInfoEnabled() && verbose) { logger.info(Localization.getText("CityGmlParser.parsedObjects"), mapper.getModel().getNumberOfFeatures()); } - // After parsing is complete, try to resolve deferred RelativeGeometries - ImplicitGeometryHolder.resolveDeferredObjects(mapper.getModel().getCache()); mapper.setCityGMLVersion(version); + CacheManager.finishParsing(); return mapper.getModel(); } } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java index ea2008c4..b3c3cf2f 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java @@ -1,9 +1,6 @@ package de.hft.stuttgart.citydoctor2.database; -import de.hft.stuttgart.citydoctor2.datastructure.Building; -import de.hft.stuttgart.citydoctor2.datastructure.GeometryTestUtils; -import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; -import de.hft.stuttgart.citydoctor2.datastructure.Lod; +import de.hft.stuttgart.citydoctor2.datastructure.*; import org.citygml4j.core.model.building.AbstractBuilding; import org.junit.Test; @@ -19,11 +16,26 @@ public class FeatureCacheTest { FeatureCache cache = new FeatureCache("test"); Building b = new Building(); b.setGmlObject(mock(AbstractBuilding.class)); + b.setGmlId(new GmlId("test")); b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); - assertEquals(0, cache.getFeatureCount()); + assertEquals(0, cache.getTotalFeatureCount()); cache.put(b); - assertEquals(1, cache.getFeatureCount()); + cache.finishParsing(); + assertEquals(1, cache.getTotalFeatureCount()); + } + + @Test + public void testGet(){ + FeatureCache cache = new FeatureCache("test"); + DatabaseHandler handler = new DatabaseHandler("test"); + Building b = new Building(); + b.setGmlId(new GmlId("building")); + b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); + handler.marshallCityObject(b); + + CityObject b2 = cache.get(new GmlId("building")); + assertEquals(b.getGmlId(), b2.getGmlId()); } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java index 98669c40..152790e5 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java @@ -44,6 +44,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.Geometry.Orientation; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.quality.model.enums.RequirementId; +import org.mockito.Mockito; public class DegeneratedPolygonCheckTest { @@ -68,9 +69,10 @@ public class DegeneratedPolygonCheckTest { Building b = new Building(); b.addGeometry(geom); - + File file = Mockito.mock(File.class); + Mockito.when(file.getName()).thenReturn("building"); ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, new File("")); + CityDoctorModel model = new CityDoctorModel(config, file); model.addBuilding(b); ValidationConfiguration valConfig = ValidationConfiguration.loadStandardValidationConfig(); -- GitLab From 177f16acca6ad9a076e39e2e6366c84341d9256e Mon Sep 17 00:00:00 2001 From: riegel Date: Mon, 27 Oct 2025 11:42:40 +0100 Subject: [PATCH 046/118] Refactor: Implement Checker multithreading --- .../citydoctor2/database/CacheManager.java | 4 + .../citydoctor2/database/CityObjectCache.java | 2 + .../citydoctor2/database/FeatureCache.java | 5 +- .../datastructure/CityDoctorModel.java | 14 ++++ .../stuttgart/citydoctor2/check/Checker.java | 79 ++++++++++++++----- .../check/CheckerThreadsSynchronizer.java | 66 ++++++++++++++++ 6 files changed, 150 insertions(+), 20 deletions(-) create mode 100644 CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckerThreadsSynchronizer.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java index 038ebd08..2966d24b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -1,6 +1,7 @@ package de.hft.stuttgart.citydoctor2.database; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; import org.apache.commons.io.FilenameUtils; @@ -43,4 +44,7 @@ public class CacheManager { } + public static void preCacheFeatures(List features) { + cache.preCacheFeatures(features); + } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java index ad696f04..8d5fac36 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -23,4 +23,6 @@ public interface CityObjectCache { void resetCache(); List retrieveLibraryObject(String fileName) throws InvalidGmlFileException; + + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index 3b819d6a..9f1011c9 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -55,7 +55,6 @@ public class FeatureCache implements CityObjectCache{ - private void batchMarshall(){ logger.trace("Setting up batch Marshaller"); Map cacheContent= new HashMap<>(cache.asMap()); @@ -102,6 +101,10 @@ public class FeatureCache implements CityObjectCache{ }); } + public void preCacheFeatures(List ids) { + cache.getAll(ids); + } + @Override public void replace(GmlId id, CityObject cityObject) { cache.invalidate(id); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 84b8019b..26c5d88b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -196,6 +196,20 @@ public class CityDoctorModel { } } + public void preCacheShownFeatures(){ + List features = new ArrayList<>(); + features.addAll(buildings.subList(0,Math.min(buildings.size(),200))); + features.addAll(vegetation.subList(0,Math.min(vegetation.size(),200))); + features.addAll(bridges.subList(0,Math.min(bridges.size(),200))); + features.addAll(land.subList(0,Math.min(land.size(),200))); + features.addAll(roads.subList(0,Math.min(roads.size(),200))); + features.addAll(tunnels.subList(0,Math.min(tunnels.size(),200))); + features.addAll(water.subList(0,Math.min(water.size(),200))); + features.addAll(cityfurniture.subList(0,Math.min(cityfurniture.size(),200))); + features.addAll(genericObjects.subList(0,Math.min(genericObjects.size(),200))); + CacheManager.preCacheFeatures(features); + } + private void exportAsGML(String file) throws CityDoctorWriteException { CityGMLWriterUtils.writeCityModel(file, this); } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 061e4184..44c9c254 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -89,6 +89,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.*; import java.util.stream.Stream; /** @@ -195,6 +196,9 @@ public class Checker { if (logger.isInfoEnabled()) { logger.info(Localization.getText("Checker.checksFinished")); } + if (l != null) { + l.updateProgress(-1); + } SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile()); if (handler != null) { handleSchematronResults(handler); @@ -629,28 +633,65 @@ public class Checker { private void checkCityModel(CityDoctorModel model, ProgressListener l) { Stream features = model.createFeatureStream(); float featureSum = model.getNumberOfFeatures(); + boolean lowMemoryMode = config.getParserConfiguration().useLowMemoryConsumption(); // clear global errors model.getGlobalErrors().clear(); - - // stupid lamda with final variable restrictions - int[] currentFeature = new int[1]; - features.forEach(co -> { - if (config.getParserConfiguration().useLowMemoryConsumption()) { - // no edges have been created yet, create them - co.prepareForChecking(); - } - // check every feature - executeChecksForCityObject(co); - - if (config.getParserConfiguration().useLowMemoryConsumption()) { - // low memory consumption, remove edges again - co.clearMetaInformation(); - } - if (l != null) { - currentFeature[0]++; - l.updateProgress(currentFeature[0] / featureSum); + CheckerThreadsSynchronizer sync = new CheckerThreadsSynchronizer(model, l); + + ExecutorService exec = new ThreadPoolExecutor + (10, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); + + Runnable run = new Runnable() { + @Override + public void run() { + while (sync.hasNext()) { + CityObject co = sync.getNext(); +// if (lowMemoryMode) { + co.prepareForChecking(); +// } + executeChecksForCityObject(co); +// if (lowMemoryMode) { + co.clearMetaInformation(); +// } + sync.updateFeature(co); + sync.updateCheckedCount(); + } } - }); + }; + long startTime = System.nanoTime(); + for (int i = 0; i < 10; i++) { + exec.execute(run); + } +// // stupid lamda with final variable restrictions +// int[] currentFeature = new int[1]; +// features.forEach(co -> { +// if (lowMemoryMode) { +// // no edges have been created yet, create them +// co.prepareForChecking(); +// } +// // check every feature +// executeChecksForCityObject(co); +// +// if (lowMemoryMode) { +// // low memory consumption, remove edges again +// co.clearMetaInformation(); +// } +// if (l != null) { +// currentFeature[0]++; +// l.updateProgress(currentFeature[0] / featureSum); +// } +// }); + + + exec.shutdown(); + try { + exec.awaitTermination(30, TimeUnit.DAYS); + long endTime = System.nanoTime(); + logger.info("Time: " + (endTime -startTime) / 1_000_000 + " ms"); + model.preCacheShownFeatures(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } private boolean filterObject(CityObject co) { diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckerThreadsSynchronizer.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckerThreadsSynchronizer.java new file mode 100644 index 00000000..e7aea60d --- /dev/null +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckerThreadsSynchronizer.java @@ -0,0 +1,66 @@ +package de.hft.stuttgart.citydoctor2.check; + +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import de.hft.stuttgart.citydoctor2.parser.ProgressListener; + +import java.util.EmptyStackException; +import java.util.Stack; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; + +public class CheckerThreadsSynchronizer { + + + private AtomicInteger checkedCount = new AtomicInteger(0); + private final float featureCount; + private CityDoctorModel model; + private ProgressListener listener; + private ReentrantLock lock = new ReentrantLock(); + private Stack ids; + + public CheckerThreadsSynchronizer(CityDoctorModel model, ProgressListener listener) { + this.model = model; + this.listener = listener; + featureCount = model.getNumberOfFeatures(); + ids = new Stack<>(); + ids.addAll(model.getFeatureIds()); + } + + public CityObject getNext() { +// lock.lock(); +// try { + if (!ids.isEmpty()){ + return model.getCache().get(ids.pop()); + } + return null; +// } finally { +// lock.unlock(); +// } + }; + + public void updateFeature(CityObject feature) { + model.getCache().put(feature); + } + + public boolean hasNext(){ +// lock.lock(); + try { + ids.peek(); + return true; + } catch (EmptyStackException e) { + return false; + } finally { +// lock.unlock(); + } + } + + public void updateCheckedCount(){ + checkedCount.getAndIncrement(); + if (listener!=null){ + listener.updateProgress(checkedCount.get() / featureCount); + } + } + +} -- GitLab From f6e2907d8ddbacf784e685e308d0a36255814456 Mon Sep 17 00:00:00 2001 From: riegel Date: Wed, 29 Oct 2025 11:11:42 +0100 Subject: [PATCH 047/118] Refactor: Rework Database table structure All CityObjects are now saved to a single "features" table instead of per file tables. --- .../citydoctor2/database/CacheManager.java | 12 +-- .../citydoctor2/database/DatabaseHandler.java | 79 ++++++++----------- .../citydoctor2/database/FeatureCache.java | 24 ++---- .../datastructure/CityDoctorModel.java | 2 +- .../database/DataBaseHandlerTest.java | 3 +- .../database/FeatureCacheTest.java | 6 +- 6 files changed, 46 insertions(+), 80 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java index 2966d24b..264b72e2 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -15,16 +15,10 @@ public class CacheManager { private CacheManager() {} - public static FeatureCache getFeatureCache(File file) { - if (file == null){ - throw new IllegalArgumentException("File is null"); - } - String filename = FilenameUtils.removeExtension(file.getName()); + public static FeatureCache getFeatureCache() { + if (cache == null){ - cache = new FeatureCache(filename); - } else { - cache.resetCache(); - cache.changeFileName(filename); + cache = new FeatureCache(); } return cache; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 0d140b21..9624302b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -13,7 +13,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.h2gis.functions.factory.H2GISDBFactory; import org.h2gis.utilities.wrapper.ConnectionWrapper; +import org.h2gis.utilities.wrapper.DataSourceWrapper; +import javax.sql.DataSource; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -28,52 +30,36 @@ import java.util.Map; public class DatabaseHandler { private static final Logger logger = LogManager.getLogger(DatabaseHandler.class); - private static final String DB_NAME = "cd_db"; - private final String fileName; - // TODO: Change handler back to using only a single featuretable - static{ - // Creates the Database at initialization + private static final String DB_NAME = "/database/cd_db"; + private DataSourceWrapper dataSource; + + //TODO: Connection pooling + public DatabaseHandler(){ try { // Cleanup hook Runtime.getRuntime().addShutdownHook(new Thread(() -> {})); - H2GISDBFactory.createSpatialDataBase(DB_NAME, - true, ";FILE_LOCK=SOCKET"); + dataSource = new DataSourceWrapper(H2GISDBFactory.createDataSource(DB_NAME, + true, ";AUTO_SERVER=TRUE;MV_STORE=TRUE;CACHE_SIZE=65536;LOCK_TIMEOUT=10000")); + setupFeaturesTable(); } catch (Exception e) { logger.fatal("FATAL ERROR: Creation of embedded database failed!"); logger.fatal(e.getMessage()); } - } - - public DatabaseHandler(String fileName){ - this.fileName = FilenameUtils.removeExtension(fileName).replaceAll(" [^a-zA-Z0-9]",""); - setupFeaturesTable(); - } - - /** - * Sets up a new, empty embedded database. Will override an existing embedded database. - */ - public static void setupDatabase() { - } - //TODO: Refactor this to remove filename - public String getFileName() { - return fileName; - } + public void setupFeaturesTable() { - try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement dropPs = con.prepareStatement(String.format("DROP TABLE IF EXISTS %s", fileName))) { + try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) { + try (PreparedStatement dropPs = con.prepareStatement("DROP TABLE IF EXISTS features")){ dropPs.executeUpdate(); if (logger.isDebugEnabled()) { logger.debug("Dropped existing features table"); } } - // TODO: Remove type and add a row with an atomic integer model id - // TODO: Refactor this to only use a single feature table - try (PreparedStatement createPs = con.prepareStatement(String.format("CREATE TABLE %s (gmlid VARCHAR(255)" + - " PRIMARY KEY, bbox GEOMETRY, data BLOB)", fileName))) { + try (PreparedStatement createPs = con.prepareStatement("CREATE TABLE features (gmlid VARCHAR(255)" + + " PRIMARY KEY, bbox GEOMETRY, data BLOB)")) { createPs.executeUpdate(); if (logger.isDebugEnabled()) { logger.debug("Created features table"); @@ -92,8 +78,8 @@ public class DatabaseHandler { * @param co the CityObject */ public void marshallCityObject(CityObject co) { - try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format("MERGE INTO %s VALUES (?, ?, ?)", fileName))) { + try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) { + try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?)")) { ps.setString(1, co.getGmlId().toString()); String bbox = BoundingBox.of(co).to2DWkt(); ps.setString(2, bbox); @@ -120,8 +106,8 @@ public class DatabaseHandler { public boolean tryMarshallCityObjectMap(Map map) { // Id String for error logging String id = ""; - try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format("MERGE INTO %s VALUES (?, ?, ?)", fileName))) { + try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) { + try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?)")) { con.setAutoCommit(false); for (Map.Entry entry : map.entrySet()) { CityObject co = entry.getValue(); @@ -164,9 +150,9 @@ public class DatabaseHandler { * @return the Feature, or null if no entry was found */ public CityObject unmarshallCityObject(GmlId id) { - try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format( - "SELECT gmlid, bbox, data FROM %s WHERE gmlid = ?", fileName))) { + try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) { + try (PreparedStatement ps = con.prepareStatement( + "SELECT gmlid, bbox, data FROM features WHERE gmlid = ?")) { ps.setString(1, id.toString()); ResultSet rs = ps.executeQuery(); if (rs.next()) { @@ -187,7 +173,7 @@ public class DatabaseHandler { } } catch (SQLException e) { logger.error("Loading of city object from embedded database failed!"); - logger.debug(e.getMessage()); + logger.error(e.getMessage()); } return null; @@ -196,7 +182,7 @@ public class DatabaseHandler { public List getAllCityObjects() { List cos = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid, bbox, data FROM %s", fileName))) { + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data FROM features")) { ResultSet rs = ps.executeQuery(); cos.addAll(retrieveCityObjects(rs)); } @@ -210,8 +196,7 @@ public class DatabaseHandler { public List getAllGmlIds() { List cos = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid FROM %s", fileName))) { - ps.setString(1, fileName); + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features")) { ResultSet rs = ps.executeQuery(); while (rs.next()) { cos.add(new GmlId(rs.getString("gmlid"))); @@ -231,9 +216,9 @@ public class DatabaseHandler { public List getBufferIntersectingObjects(GmlId gmlID, double bufferSize) { List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid FROM %s AS tab1" + - " WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM %s WHERE gmlid = ?)) " + - "AND tab1.gmlid <> ?", fileName, fileName))) { + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + + " WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM features WHERE gmlid = ?)) " + + "AND tab1.gmlid <> ?")) { ps.setDouble(1, bufferSize); ps.setString(2, gmlID.toString()); ps.setString(3, gmlID.toString()); @@ -257,9 +242,9 @@ public class DatabaseHandler { List intersectingObjects = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid FROM %s AS tab1" + - " WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM %s WHERE gmlid = ?)) AND tab1.gmlid <> ?", - fileName, fileName))) { + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + + " WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) + { ps.setString(1, id.toString()); ps.setString(2, id.toString()); ResultSet rs = ps.executeQuery(); @@ -279,7 +264,7 @@ public class DatabaseHandler { int count = -1; try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { - try (PreparedStatement ps = con.prepareStatement(String.format("SELECT COUNT(gmlid) AS nr FROM %s", fileName))) { + try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(gmlid) AS nr FROM features")) { ResultSet rs = ps.executeQuery(); if (rs.next()) { count = rs.getInt("nr"); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index 9f1011c9..03a75f2b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -30,8 +30,8 @@ public class FeatureCache implements CityObjectCache{ private AtomicInteger putsCounter = new AtomicInteger(0); private boolean parsingFinished = false; - FeatureCache(String fileName) { - handler = new DatabaseHandler(fileName); + FeatureCache() { + handler = new DatabaseHandler(); handler.setupFeaturesTable(); cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> { if (value != null && cause.wasEvicted()) { @@ -79,23 +79,14 @@ public class FeatureCache implements CityObjectCache{ @Override public CityObject get(GmlId id) { - CityObject result = cache.get(id); - if (result == null){ - logger.trace("Cache-value missing for {}", id); - //Key has null value, try to restore from Database - result = handler.unmarshallCityObject(id); - if (result == null){ - logger.error("CityObject with id {} is null in cache and database", id); - } else { - cache.put(id, result); - } - } - return result; + return cache.get(id); } + + public void changeFileName(String fileName){ handler = handlerList.computeIfAbsent(fileName, k -> { - DatabaseHandler handler1 = new DatabaseHandler(fileName); + DatabaseHandler handler1 = new DatabaseHandler(); handler1.setupFeaturesTable(); return handler1; }); @@ -131,8 +122,6 @@ public class FeatureCache implements CityObjectCache{ @Override public List retrieveLibraryObject(String fileName) throws InvalidGmlFileException { - String prevHandler = handler.getFileName(); - this.changeFileName(fileName); int featureCount = getTotalFeatureCount(); if (featureCount != 1){ if (featureCount == 0){ @@ -142,7 +131,6 @@ public class FeatureCache implements CityObjectCache{ } } List objects = handler.getAllCityObjects(); - this.changeFileName(prevHandler); return objects; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 26c5d88b..c2c72afa 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -68,7 +68,7 @@ public class CityDoctorModel { private final CityObjectCache cache; public CityDoctorModel(ParserConfiguration config, File file) { - this(config, file, CacheManager.getFeatureCache(file)); + this(config, file, CacheManager.getFeatureCache()); } public CityDoctorModel(ParserConfiguration config, File file, CityObjectCache cache) { diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index 1d6aa10e..8381e96c 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -37,7 +37,7 @@ public class DataBaseHandlerTest { @Before public void resetDatabase() { try { - handler = new DatabaseHandler("test"); + handler = new DatabaseHandler(); handler.setupFeaturesTable(); } catch (Exception e) { fail(e.getMessage()); @@ -242,7 +242,6 @@ public class DataBaseHandlerTest { Building b = model.getBuildings().toList().get(0); assertNotNull(b); - assertEquals(0, handler.getFeatureCount()); handler.marshallCityObject(b); assertEquals(1, handler.getFeatureCount()); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java index b3c3cf2f..902ca8f8 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java @@ -13,7 +13,7 @@ public class FeatureCacheTest { @Test public void testPut(){ - FeatureCache cache = new FeatureCache("test"); + FeatureCache cache = new FeatureCache(); Building b = new Building(); b.setGmlObject(mock(AbstractBuilding.class)); b.setGmlId(new GmlId("test")); @@ -27,8 +27,8 @@ public class FeatureCacheTest { @Test public void testGet(){ - FeatureCache cache = new FeatureCache("test"); - DatabaseHandler handler = new DatabaseHandler("test"); + FeatureCache cache = new FeatureCache(); + DatabaseHandler handler = new DatabaseHandler(); Building b = new Building(); b.setGmlId(new GmlId("building")); b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); -- GitLab From b3753a5f9f732998eafd1d895debfb7d30b148dd Mon Sep 17 00:00:00 2001 From: riegel Date: Wed, 29 Oct 2025 11:12:25 +0100 Subject: [PATCH 048/118] Refactor: Implement multithreaded checking --- .../stuttgart/citydoctor2/check/Checker.java | 130 +++++++++++------- .../citydoctor2/gui/CityDoctorController.java | 2 + 2 files changed, 81 insertions(+), 51 deletions(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 44c9c254..c5288bac 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -28,9 +28,11 @@ import de.hft.stuttgart.citydoctor2.checks.CheckPrototype; import de.hft.stuttgart.citydoctor2.checks.Checks; import de.hft.stuttgart.citydoctor2.checks.SvrlContentHandler; import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener; +import de.hft.stuttgart.citydoctor2.database.CityObjectCache; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; +import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.parser.CityGmlConsumer; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; @@ -79,6 +81,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -90,6 +93,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; /** @@ -196,9 +200,6 @@ public class Checker { if (logger.isInfoEnabled()) { logger.info(Localization.getText("Checker.checksFinished")); } - if (l != null) { - l.updateProgress(-1); - } SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile()); if (handler != null) { handleSchematronResults(handler); @@ -446,6 +447,10 @@ public class Checker { if (logger.isInfoEnabled()) { logger.info(Localization.getText("Checker.schematronValidation")); } + if (!Files.exists(Path.of(config.getSchematronFilePath()))) { + logger.warn("Schematron file {} could not be found", config.getSchematronFilePath()); + return null; + } try { TransformerFactory transformerFactory = TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl", Checker.class.getClassLoader()); @@ -630,70 +635,93 @@ public class Checker { } } - private void checkCityModel(CityDoctorModel model, ProgressListener l) { - Stream features = model.createFeatureStream(); + private void checkCityModel(CityDoctorModel model, ProgressListener l){ + List features = model.getFeatureIds(); + CityObjectCache cache = model.getCache(); float featureSum = model.getNumberOfFeatures(); - boolean lowMemoryMode = config.getParserConfiguration().useLowMemoryConsumption(); + AtomicInteger checkedCount = new AtomicInteger(0); + AtomicInteger missingCount = new AtomicInteger(0); // clear global errors model.getGlobalErrors().clear(); CheckerThreadsSynchronizer sync = new CheckerThreadsSynchronizer(model, l); + int threadCount = Runtime.getRuntime().availableProcessors() * 5; ExecutorService exec = new ThreadPoolExecutor - (10, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); - - Runnable run = new Runnable() { - @Override - public void run() { - while (sync.hasNext()) { - CityObject co = sync.getNext(); -// if (lowMemoryMode) { - co.prepareForChecking(); -// } - executeChecksForCityObject(co); -// if (lowMemoryMode) { - co.clearMetaInformation(); -// } - sync.updateFeature(co); - sync.updateCheckedCount(); - } - } - }; - long startTime = System.nanoTime(); - for (int i = 0; i < 10; i++) { - exec.execute(run); - } -// // stupid lamda with final variable restrictions -// int[] currentFeature = new int[1]; -// features.forEach(co -> { -// if (lowMemoryMode) { -// // no edges have been created yet, create them -// co.prepareForChecking(); -// } -// // check every feature -// executeChecksForCityObject(co); -// -// if (lowMemoryMode) { -// // low memory consumption, remove edges again -// co.clearMetaInformation(); -// } -// if (l != null) { -// currentFeature[0]++; -// l.updateProgress(currentFeature[0] / featureSum); -// } -// }); - + (threadCount, threadCount, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); + long startTime = System.nanoTime(); + for (GmlId id : features) { + exec.execute(()->{ + CityObject co = cache.get(id); + if (co == null) { + missingCount.incrementAndGet(); + if (l!=null){ + l.updateProgress((checkedCount.get() + missingCount.get()) / featureSum); + } + return; + } + co.prepareForChecking(); + executeChecksForCityObject(co); + co.clearMetaInformation(); + cache.put(co); + checkedCount.incrementAndGet(); + if (l!=null){ + l.updateProgress((checkedCount.get() + missingCount.get()) / featureSum); + } + }); + } exec.shutdown(); try { exec.awaitTermination(30, TimeUnit.DAYS); long endTime = System.nanoTime(); - logger.info("Time: " + (endTime -startTime) / 1_000_000 + " ms"); - model.preCacheShownFeatures(); + logger.info("Checks finished in " + (endTime -startTime) / 1_000_000 + " ms"); + if (missingCount.get() > 0) { + int missing = missingCount.get(); + String msg = String.format("Checker could not load %d/%.0f features (%.2f%%)", missing, featureSum, (missing/featureSum) * 100); + logger.warn(msg); + } +// model.preCacheShownFeatures(); } catch (InterruptedException e) { throw new RuntimeException(e); } } + private void checkCityModelOLD(CityDoctorModel model, ProgressListener l) { + Stream features = model.createFeatureStream(); + float featureSum = model.getNumberOfFeatures(); + boolean lowMemoryMode = config.getParserConfiguration().useLowMemoryConsumption(); + + // clear global errors + model.getGlobalErrors().clear(); + + long startTime = System.nanoTime(); + + // stupid lamda with final variable restrictions + int[] currentFeature = new int[1]; + features.forEach(co -> { + if (lowMemoryMode) { + // no edges have been created yet, create them + co.prepareForChecking(); + } + // check every feature + executeChecksForCityObject(co); + + if (lowMemoryMode) { + // low memory consumption, remove edges again + co.clearMetaInformation(); + } + if (l != null) { + currentFeature[0]++; + l.updateProgress(currentFeature[0] / featureSum); + } + }); + + long endTime = System.nanoTime(); + model.preCacheShownFeatures(); + logger.info("Checks finished in " + (endTime -startTime) / 1_000_000 + " ms"); + + } + private boolean filterObject(CityObject co) { return isObjectIncluded(co, includeFilters, excludeFilters); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index 7f42d2ca..b3464d3e 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -1197,6 +1197,7 @@ public class CityDoctorController { Platform.runLater(() -> { // apply check results to tree views + buildTrees(); updateFeatureTrees(); updateTree(mainWindow.getPolygonsView().getRoot()); for (CheckError e : model.getGlobalErrors()) { @@ -1250,6 +1251,7 @@ public class CityDoctorController { } Platform.runLater(() -> { // apply check results to tree views + buildTrees(); updateFeatureTrees(); updateTree(mainWindow.getPolygonsView().getRoot()); for (CheckError e : model.getGlobalErrors()) { -- GitLab From 142e750d0a4b47f5e933ea02e1cb856551629631 Mon Sep 17 00:00:00 2001 From: riegel Date: Wed, 29 Oct 2025 11:13:00 +0100 Subject: [PATCH 049/118] Build: Add HikariCP dependency --- CityDoctorParent/CityDoctorModel/pom.xml | 4 ++++ CityDoctorParent/pom.xml | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/pom.xml b/CityDoctorParent/CityDoctorModel/pom.xml index 945fd739..231741b7 100644 --- a/CityDoctorParent/CityDoctorModel/pom.xml +++ b/CityDoctorParent/CityDoctorModel/pom.xml @@ -80,6 +80,10 @@ com.github.ben-manes.caffeine caffeine + + com.zaxxer + HikariCP + diff --git a/CityDoctorParent/pom.xml b/CityDoctorParent/pom.xml index 079e21e8..05537176 100644 --- a/CityDoctorParent/pom.xml +++ b/CityDoctorParent/pom.xml @@ -252,6 +252,11 @@ caffeine 3.2.2 + + com.zaxxer + HikariCP + 7.0.2 + -- GitLab From 58a625c9dd3d01baa56bcc7febade42e4acd6e30 Mon Sep 17 00:00:00 2001 From: riegel Date: Wed, 29 Oct 2025 15:11:49 +0100 Subject: [PATCH 050/118] Fix: Add non-null check for file --- .../stuttgart/citydoctor2/datastructure/CityDoctorModel.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index c2c72afa..f06e2672 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -75,6 +75,9 @@ public class CityDoctorModel { if (config == null) { throw new IllegalArgumentException("Parser configuration may not be null"); } + if( file == null ) { + throw new IllegalArgumentException("File may not be null"); + } this.fileName = file.getName(); this.config = config; this.file = file; -- GitLab From 6b95778b5649d73cb433f22deb16479a8c38c993 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 30 Oct 2025 16:31:46 +0100 Subject: [PATCH 051/118] Feat: Add connection pooling to DatabaseHandler --- .../citydoctor2/database/DatabaseHandler.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 9624302b..381ff125 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -1,12 +1,13 @@ package de.hft.stuttgart.citydoctor2.database; +import com.zaxxer.hikari.HikariDataSource; + import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; -import javafx.scene.control.Alert; -import org.apache.commons.io.FilenameUtils; + import org.apache.commons.lang3.SerializationException; import org.apache.commons.lang3.SerializationUtils; import org.apache.logging.log4j.LogManager; @@ -15,7 +16,6 @@ import org.h2gis.functions.factory.H2GISDBFactory; import org.h2gis.utilities.wrapper.ConnectionWrapper; import org.h2gis.utilities.wrapper.DataSourceWrapper; -import javax.sql.DataSource; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -27,20 +27,29 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; + public class DatabaseHandler { private static final Logger logger = LogManager.getLogger(DatabaseHandler.class); private static final String DB_NAME = "/database/cd_db"; private DataSourceWrapper dataSource; - //TODO: Connection pooling public DatabaseHandler(){ try { // Cleanup hook Runtime.getRuntime().addShutdownHook(new Thread(() -> {})); - dataSource = new DataSourceWrapper(H2GISDBFactory.createDataSource(DB_NAME, - true, ";AUTO_SERVER=TRUE;MV_STORE=TRUE;CACHE_SIZE=65536;LOCK_TIMEOUT=10000")); - + String jdbcurl = H2GISDBFactory.createDataSource(DB_NAME, + true, ";AUTO_SERVER=TRUE").getConnection().getMetaData().getURL(); + HikariDataSource ds = new HikariDataSource(); setupFeaturesTable(); + ds.setJdbcUrl(jdbcurl); + ds.setUsername("sa"); + ds.setPassword("sa"); + int coreCount = Runtime.getRuntime().availableProcessors(); + ds.setMaximumPoolSize(coreCount*5); + ds.setMinimumIdle(coreCount*5); + + dataSource = new DataSourceWrapper(ds); + } catch (Exception e) { logger.fatal("FATAL ERROR: Creation of embedded database failed!"); logger.fatal(e.getMessage()); @@ -78,7 +87,7 @@ public class DatabaseHandler { * @param co the CityObject */ public void marshallCityObject(CityObject co) { - try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) { + try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?)")) { ps.setString(1, co.getGmlId().toString()); String bbox = BoundingBox.of(co).to2DWkt(); @@ -106,7 +115,7 @@ public class DatabaseHandler { public boolean tryMarshallCityObjectMap(Map map) { // Id String for error logging String id = ""; - try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) { + try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?)")) { con.setAutoCommit(false); for (Map.Entry entry : map.entrySet()) { @@ -150,7 +159,7 @@ public class DatabaseHandler { * @return the Feature, or null if no entry was found */ public CityObject unmarshallCityObject(GmlId id) { - try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) { + try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement( "SELECT gmlid, bbox, data FROM features WHERE gmlid = ?")) { ps.setString(1, id.toString()); -- GitLab From b248c718cc72aa063c3ec5faaabdd04db73fab5d Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 30 Oct 2025 16:32:11 +0100 Subject: [PATCH 052/118] Build: Disable Hikari debug logs --- .../src/main/resources/log4j2.xml | 3 +++ .../src/main/resources/log4j2.xml | 18 ++++++++++++++++++ .../src/main/resources/log4j2.xml | 3 +++ 3 files changed, 24 insertions(+) create mode 100644 CityDoctorParent/CityDoctorValidation/src/main/resources/log4j2.xml diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/log4j2.xml b/CityDoctorParent/CityDoctorModel/src/main/resources/log4j2.xml index 4e951d6b..9f04f6cb 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/log4j2.xml +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/log4j2.xml @@ -14,5 +14,8 @@ + + + \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorValidation/src/main/resources/log4j2.xml b/CityDoctorParent/CityDoctorValidation/src/main/resources/log4j2.xml new file mode 100644 index 00000000..b1b870e0 --- /dev/null +++ b/CityDoctorParent/CityDoctorValidation/src/main/resources/log4j2.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/log4j2.xml b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/log4j2.xml index 52b82fc2..88b67835 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/log4j2.xml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/log4j2.xml @@ -19,5 +19,8 @@ + + + \ No newline at end of file -- GitLab From 5e19ae6048d8040535ef1006c18b0aa313b2cade Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 3 Nov 2025 10:50:27 +0100 Subject: [PATCH 053/118] Refactor: Delete useless classes --- .../citydoctor2/database/SubPartAdapter.java | 33 ------------------- .../database/TopLevelCityObjectAdapter.java | 19 ----------- .../check/StreamCityGmlConsumer.java | 2 +- 3 files changed, 1 insertion(+), 53 deletions(-) delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/SubPartAdapter.java delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/TopLevelCityObjectAdapter.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/SubPartAdapter.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/SubPartAdapter.java deleted file mode 100644 index b788521d..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/SubPartAdapter.java +++ /dev/null @@ -1,33 +0,0 @@ -package de.hft.stuttgart.citydoctor2.database; - -import de.hft.stuttgart.citydoctor2.datastructure.CityObject; -import de.hft.stuttgart.citydoctor2.datastructure.GmlId; - -/** - * Adapter class for Lists. All function calls on objects of this - * class get passed to their respective CityObjects. - */ -public class SubPartAdapter { -// TODO: Remove CO extension and change model to stream and map objects to their respective classes - private final GmlId featureId; - private final GmlId topLevelId; - private FeatureCache cache; - - public SubPartAdapter(CityObject co, FeatureCache cache) { - this.featureId = co.getGmlId(); - this.topLevelId = co.getTopLevelCityObject().getGmlId(); - this.cache = cache; - } - - public boolean isValidated(){ - return cache.get(featureId).isValidated(); - } - - public GmlId getFeatureId() { - return featureId; - } - - public GmlId getTopLevelId() { - return topLevelId; - } -} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/TopLevelCityObjectAdapter.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/TopLevelCityObjectAdapter.java deleted file mode 100644 index fb1b7856..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/TopLevelCityObjectAdapter.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.hft.stuttgart.citydoctor2.database; - -import de.hft.stuttgart.citydoctor2.datastructure.CityObject; -import de.hft.stuttgart.citydoctor2.datastructure.GmlId; - -public class TopLevelCityObjectAdapter { - - private final GmlId featureId; - private FeatureCache cache; - - public TopLevelCityObjectAdapter(CityObject co, FeatureCache cache) { - this.featureId = co.getGmlId(); - this.cache = cache; - } - - public boolean isValidated(){ - return cache.get(featureId).isValidated(); - } -} diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/StreamCityGmlConsumer.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/StreamCityGmlConsumer.java index c3ff365c..1c542e65 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/StreamCityGmlConsumer.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/StreamCityGmlConsumer.java @@ -134,7 +134,7 @@ public class StreamCityGmlConsumer implements CityGmlConsumer { pdfReporter.report(co); } - // TODO: Update QualityADE to be compatible with CityGML 3.0 or deprecate it, unhandled top-level feature types are just skipped for now + // TODO: Update QualityADE to be compatible with CityGML 3.0, unhandled top-level feature types are just skipped for now if (!(co instanceof AbstractTunnel || co instanceof CityFurniture || co instanceof GenericCityObject)) { // store quality ade datastructures in cityobject QualityADEUtils.writeQualityAde(co, val); -- GitLab From 1310568f460ffd80fb74e568496b3e680dc9f350 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 3 Nov 2025 11:11:48 +0100 Subject: [PATCH 054/118] Style: Remove completed TODOs --- .../de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java | 2 -- .../hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java | 1 - .../citydoctor2/datastructure/BoundarySurfaceTest.java | 2 -- .../stuttgart/citydoctor2/datastructure/BridgeObjectTest.java | 1 - .../stuttgart/citydoctor2/datastructure/VegetationTest.java | 1 - .../de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java | 2 +- .../main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java | 3 +++ 7 files changed, 4 insertions(+), 8 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 8e5bf74c..3b6e8141 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -169,7 +169,6 @@ public class CityGmlParser { } return context; } - //TODO: Implement resolving of library objects with database /** * Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}. @@ -601,7 +600,6 @@ public class CityGmlParser { }else if (!cdModel.containsGmlId(id)){ cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); } - // TODO: add check if aco was parsed successfully, add aco to cmodel if it wasnt } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java index 80bace6d..1285b0c7 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/BoundingBoxCalculator.java @@ -97,7 +97,6 @@ public class BoundingBoxCalculator { * @return the bounding box of the model */ public static BoundingBox calculateBoundingBox(CityDoctorModel model) { - // TODO: Rework to Visitor MinMaxExtentVisitor mima = new MinMaxExtentVisitor(); model.accept(mima); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurfaceTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurfaceTest.java index 1275cc85..9953196f 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurfaceTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurfaceTest.java @@ -51,8 +51,6 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; public class BoundarySurfaceTest { - //Todo: Check this class for rework potential with the new Visitors - @Test public void testVisitorPropagation() { BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObjectTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObjectTest.java index d185d272..931ce45c 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObjectTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObjectTest.java @@ -51,7 +51,6 @@ import de.hft.stuttgart.citydoctor2.utils.visitors.UnsetGeometriesVisitor; public class BridgeObjectTest { - //TODO: Rework this class with the new Visitors @Test public void testAccept() { diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/VegetationTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/VegetationTest.java index 5f9c97a3..4df10469 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/VegetationTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/VegetationTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.mock; public class VegetationTest { - //TODO: Recreate geometries tests @Test public void testContainsError() { diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index b3464d3e..f2ece4ff 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -1899,7 +1899,7 @@ public class CityDoctorController { public void delete(TreeItem selectedItem) { if (model == null) { return; - }/* TODO: Rework this properly via Renderable implementation + }/* Old debug function, would first need a refactor due to the RenderNode and GeoDB reworks Renderable render = selectedItem.getValue(); if (render instanceof BuildingNode node) { model.getBuildings().remove(node.getBuilding()); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index 58810fd5..d1fca153 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -801,10 +801,13 @@ public class MainWindow extends Application { }); menu.getItems().add(copyToClipboardOption); + /* Was used for debugging the repair module, functionless since the RenderNode rework. + * Commented out for now, might be needed later on again MenuItem deleteOption = new MenuItem(Localization.getText("MainWindow.delete")); deleteOption.setOnAction(ae -> controller.delete(featureView.getSelectionModel().getSelectedItem())); menu.getItems().add(deleteOption); featureView.setContextMenu(menu); + */ } public void fetchCopyAction(Renderable node) { -- GitLab From b9d7b793b3afbb7d16c456dd86a3c77f75ab8e01 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 3 Nov 2025 15:00:43 +0100 Subject: [PATCH 055/118] Refactor: Remove Zip file support --- .../citydoctor2/database/CacheManager.java | 10 +- .../citydoctor2/database/CityObjectCache.java | 4 - .../citydoctor2/database/FeatureCache.java | 13 - .../database/UnconnectedCache.java | 5 - .../datastructure/LibraryObject.java | 50 +- .../citygml3/Citygml3FeatureMapper.java | 21 - .../citydoctor2/parser/CityGmlParser.java | 129 +---- .../citydoctor2/utils/ArchivePacker.java | 98 ---- .../citydoctor2/zip/CityGmlZipArchive.java | 151 ------ .../citydoctor2/zip/CityGmlZipEntry.java | 219 --------- .../citydoctor2/zip/CityGmlZipEntryFile.java | 79 ---- .../zip/MalformedZipFileException.java | 17 - .../citydoctor2/zip/ZipEntryErrorType.java | 8 - .../stuttgart/citydoctor2/zip/ZipParser.java | 12 - .../CityDoctorLocalization.properties | 21 - .../CityDoctorLocalization_de.properties | 21 - .../citydoctor2/CityDoctorValidation.java | 27 -- .../stuttgart/citydoctor2/check/Checker.java | 72 --- .../citydoctor2/check/CheckerTest.java | 70 --- .../citydoctor2/gui/CheckDialog.java | 64 +-- .../citydoctor2/gui/CityDoctorController.java | 252 +--------- .../citydoctor2/gui/MainToolBar.java | 23 - .../stuttgart/citydoctor2/gui/MainWindow.java | 7 - .../citydoctor2/gui/OpenFileDialog.java | 5 +- .../citydoctor2/gui/ZipEntryManager.java | 439 ------------------ .../gui/tree/ZipEntryListCell.java | 22 - .../gui/tree/node/ZipEntryNode.java | 43 -- .../citydoctor2/gui/CheckDialog.fxml | 73 --- .../citydoctor2/gui/MainToolBar.fxml | 12 +- .../citydoctor2/gui/ZipEntryManager.fxml | 213 --------- 30 files changed, 14 insertions(+), 2166 deletions(-) delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/ArchivePacker.java delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipArchive.java delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntryFile.java delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/MalformedZipFileException.java delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/ZipEntryErrorType.java delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/ZipParser.java delete mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ZipEntryManager.java delete mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ZipEntryListCell.java delete mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/node/ZipEntryNode.java delete mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/ZipEntryManager.fxml diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java index 264b72e2..5429d09f 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -1,12 +1,8 @@ package de.hft.stuttgart.citydoctor2.database; -import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; -import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; -import org.apache.commons.io.FilenameUtils; -import java.io.File; + import java.util.List; public class CacheManager { @@ -23,10 +19,6 @@ public class CacheManager { return cache; } - public static List retrieveLibraryObject(CityGmlZipEntry entry) throws InvalidGmlFileException { - return entry.getModel().getCache().retrieveLibraryObject(entry.getDisplayName()); - } - public static void finishParsing(){ if (cache != null){ cache.finishParsing(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java index 8d5fac36..1b4a0344 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -2,7 +2,6 @@ package de.hft.stuttgart.citydoctor2.database; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; -import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import java.util.List; import java.util.stream.Stream; @@ -22,7 +21,4 @@ public interface CityObjectCache { void resetCache(); - List retrieveLibraryObject(String fileName) throws InvalidGmlFileException; - - } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index 03a75f2b..f99dfd27 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -120,19 +120,6 @@ public class FeatureCache implements CityObjectCache{ parsingFinished = false; } - @Override - public List retrieveLibraryObject(String fileName) throws InvalidGmlFileException { - int featureCount = getTotalFeatureCount(); - if (featureCount != 1){ - if (featureCount == 0){ - throw new InvalidGmlFileException(String.format("LibraryObject ZIP-entry %s contains no Feature", fileName)); - } else { - throw new InvalidGmlFileException(String.format("LibraryObject ZIP-entry %s contains more than one Feature", fileName)); - } - } - List objects = handler.getAllCityObjects(); - return objects; - } public void finishParsing() { if (!parsingFinished) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java index 8478fcd9..a668099d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java @@ -48,9 +48,4 @@ public class UnconnectedCache implements CityObjectCache{ public void resetCache(){ // } - - @Override - public List retrieveLibraryObject(String fileName) throws InvalidGmlFileException { - return List.copyOf(cache.values()); - } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java index 8cdd2554..4051504c 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java @@ -1,11 +1,9 @@ package de.hft.stuttgart.citydoctor2.datastructure; -import de.hft.stuttgart.citydoctor2.database.CacheManager; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -45,22 +43,6 @@ public class LibraryObject extends Geometry { return libOb; } - public static LibraryObject of(CityGmlZipEntry entry, ParserConfiguration config) { - String fileName = entry.getEntrySubPath(); - if (libraryObjects.containsKey(fileName)){ - return libraryObjects.get(fileName); - } - Geometry protoGeom = parseZipEntry(entry, config); - if (protoGeom == null) { - return null; - } - LibraryObject libOb = new LibraryObject(protoGeom.getType(), protoGeom.getLod(), protoGeom.getOrientation()); - protoGeom.getPolygons().forEach(libOb::addPolygon); - libOb.updateEdgesAndVertices(); - libraryObjects.put(fileName, libOb); - return libOb; - } - private LibraryObject(GeometryType type, Lod lod, Orientation orientation) { super(type, lod, orientation); } @@ -72,6 +54,7 @@ public class LibraryObject extends Geometry { try { geo = getProtoGeometry(CityGmlParser.nestedCityGmlFileParse(path.toString(), config)); } catch (CityGmlParseException e) { + //TODO: Localize Strings logger.error(String.format( "Encountered an error while parsing library object %s", path)); logger.error(e.getStackTrace()); @@ -79,50 +62,27 @@ public class LibraryObject extends Geometry { logger.error(e.getStackTrace()); } } else { + //TODO: Localize Strings logger.error(String.format("Implicit geometry references non-existing library object file %s.", path)); } return geo; } - private static Geometry parseZipEntry(CityGmlZipEntry entry, ParserConfiguration config) { - Geometry geo = null; - try { - entry.loadEntry(config, true); - geo = getProtoGeometry(entry); - } catch (InvalidGmlFileException e) { - logger.error(e.getStackTrace()); - } catch (CityGmlParseException e) { - logger.error(String.format( - "Encountered an error while parsing library object %s", entry.getEntrySubPath())); - logger.error(e.getStackTrace()); - } - return geo; - } - private static Geometry getProtoGeometry(CityDoctorModel model) throws InvalidGmlFileException, CityGmlParseException { if (model == null) { + //TODO: Localize Strings throw new CityGmlParseException("CityDoctorModel of referenced LibraryObject is null"); } List objects = model.createFeatureStream().toList(); if (objects.isEmpty()) { + //TODO: Localize Strings throw new InvalidGmlFileException("Referenced library-object gml file does not contain any CityGML objects!"); } else if (objects.size() > 1) { + //TODO: Localize Strings throw new InvalidGmlFileException("Referenced library-object gml file contains more than one CityGML object!"); } return objects.get(0).getHighestLodGeometry(); } - private static Geometry getProtoGeometry(CityGmlZipEntry entry) throws InvalidGmlFileException, CityGmlParseException { - if (entry.getModel() == null) { - throw new CityGmlParseException("CityDoctorModel of referenced LibraryObject is null"); - } - List objects = CacheManager.retrieveLibraryObject(entry); - if (objects.isEmpty()) { - throw new InvalidGmlFileException("Referenced library-object gml file does not contain any CityGML objects!"); - } else if (objects.size() > 1) { - throw new InvalidGmlFileException("Referenced library-object gml file contains more than one CityGML object!"); - } - return objects.get(0).getHighestLodGeometry(); - } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index 2eaa0de8..0216f946 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -158,7 +158,6 @@ import de.hft.stuttgart.citydoctor2.datastructure.WaterObject; import de.hft.stuttgart.citydoctor2.math.graph.KDTree; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.Localization; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; public class Citygml3FeatureMapper extends ObjectWalker { @@ -167,7 +166,6 @@ public class Citygml3FeatureMapper extends ObjectWalker { private final CityDoctorModel model; private final ParserConfiguration config; private final Path directory; - private final CityGmlZipEntry zipEntry; private final double neighborDistance; private Map polygonMap = new HashMap<>(); private Map compositeMap = new HashMap<>(); @@ -183,19 +181,6 @@ public class Citygml3FeatureMapper extends ObjectWalker { model = new CityDoctorModel(config, path.toFile()); } neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces()); - zipEntry = null; - } - - public Citygml3FeatureMapper(ParserConfiguration config, CityGmlZipEntry entry, boolean nested) { - this.config = config; - this.directory = null; - this.zipEntry = entry; - if (nested){ - model = new CityDoctorModel(config, Path.of(entry.getEntrySubPath()).toFile(), CacheManager.getUnconnectedCache()); - } else { - model = new CityDoctorModel(config, Path.of(entry.getEntrySubPath()).toFile()); - } - neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces()); } public static void parseId(AbstractGML gml, GmlElement gmlElement) { @@ -1033,12 +1018,6 @@ public class Citygml3FeatureMapper extends ObjectWalker { if (directory != null){ Path libraryObjectPath = directory.resolve(ig.getLibraryObject()); libObj = LibraryObject.of(libraryObjectPath, config); - } else { - String libraryObjectPath = ig.getLibraryObject(); - Path loPath = zipEntry.resolveLibraryObjectPath(libraryObjectPath); - CityGmlZipEntry libEntry = zipEntry.getArchive().getEntry(loPath.toString()); - libObj = LibraryObject.of(libEntry, config); - } if (libObj != null) { igh = new ImplicitGeometryHolder(ig, libObj); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 3b6e8141..963ac642 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -93,8 +93,6 @@ import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper; import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.utils.Localization; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile; import de.hft.stuttgart.quality.QualityADEContext; import de.hft.stuttgart.quality.QualityADEModule; @@ -284,117 +282,12 @@ public class CityGmlParser { return readAndKeepFeatures(config, file, in, ois, verbose, nested); } } catch (CityGMLReadException | IOException e) { + //TODO: Localize String throw new CityGmlParseException("Failed to read CityGML file", e); } } - public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, - ProgressListener l, boolean nested) - throws CityGmlParseException, InvalidGmlFileException, IOException { - CityGMLContext context = getContext(); - - if (config.getValidate()) { - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { - List messages = validateStream(entryFile.getInputStream(), context); - if (!messages.isEmpty()) { - throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0)); - } - } catch (Exception e) { - throw new CityGmlParseException(e); - } - - } - return decompressAndParseCityGmlEntry(entry, config, l, context, nested); - } - - - public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, - ProgressListener l, CityGMLContext context, boolean nested) - throws CityGmlParseException { - - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { - BufferedInputStream bis = new BufferedInputStream(entryFile.getInputStream()); - readEpsgCodeFromInputStream(bis, config); - CityGMLInputFactory in = context.createCityGMLInputFactory() - .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); - try (ObservedInputStream ois = new ObservedInputStream(bis, bis.available())) { - if (l != null) { - ois.addListener(l::updateProgress); - } - return readAndKeepFeatures(config, entry, in, ois, false, nested); - } - } catch (CityGMLReadException | IOException e) { - throw new CityGmlParseException("Failed to read CityGML file", e); - } catch (Exception e) { - throw new CityGmlParseException(e); - } - } - - private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, CityGmlZipEntry entry, - CityGMLInputFactory inputFactory, ObservedInputStream ois, - boolean verbose, boolean nested) - throws CityGMLReadException { - return readAndKeepModel(new Citygml3FeatureMapper(config, entry, nested), inputFactory, ois, verbose); - } - - private static List validateStream(InputStream vis, CityGMLContext context) throws CityGmlParseException { - GMLValidationHandler handler = new GMLValidationHandler(); - - try { - BufferedInputStream bis = new BufferedInputStream(vis); - SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler()); - readAdditionalSchemaDefinitions(context, bis, schemaHandler); - Source[] schemas = schemaHandler.getSchemas(); - SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - Schema schema = schemaFactory.newSchema(schemas); - Validator validator = schema.newValidator(); - validator.setErrorHandler(handler); - validator.validate(new StreamSource(bis)); - return handler.getMessages(); - } catch (SchemaHandlerException | SAXException | IOException e) { - throw new CityGmlParseException("Failed to validate CityGML file", e); - } - } - - private static void readAdditionalSchemaDefinitions(CityGMLContext context, BufferedInputStream bis, SchemaHandler schemaHandler) - throws CityGmlParseException { - bis.mark(Integer.MAX_VALUE); - try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()) - .withSchemaHandler(schemaHandler) - .createReader(bis)) { - reader.nextTag(); - bis.reset(); - } catch (Exception e) { - throw new CityGmlParseException("Failed to read Schema from stream.", e); - } - } - - private static void readEpsgCodeFromInputStream(BufferedInputStream bis, ParserConfiguration config) throws CityGmlParseException { - try { - // Mark start position of GML-"file" - bis.mark(Integer.MAX_VALUE); - // Buffer the first 10000 chars of the Stream, EPSG/envelope info should be found within that range - int peekingWidth = Math.min(10000, bis.available()); - byte[] buf = new byte[peekingWidth]; - bis.read(buf, 0, peekingWidth); - bis.reset(); - parseEpsgCodeFromBuffer(buf, config); - } catch (ParserConfigurationException | SAXException | IOException e) { - throw new CityGmlParseException("Failed to read CityGML file", e); - } - } - - public static void streamCityGml(CityGmlZipEntry entry, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, - String outputFile) throws CityGmlParseException, IOException { - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { - parseEpsgCodeFromStream(entryFile.getInputStream(), config); - startReadingCityGmlZipEntry(entry, config, null, cityObjectConsumer, outputFile); - } catch (ParserConfigurationException | SAXException | IOException e) { - throw new CityGmlParseException("Failed to read CityGML stream", e); - } - } public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException { @@ -452,26 +345,6 @@ public class CityGmlParser { } - private static void startReadingCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, - CityGmlConsumer cityObjectConsumer, String outputFile) { - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry); - ObservedInputStream ois = new ObservedInputStream(entryFile.getInputStream(), entry.getFileSize())) { - if (l != null) { - ois.addListener(l::updateProgress); - } - streamAndDiscardFeatures(entry, config, ois, cityObjectConsumer, outputFile); - } catch (IOException | CityGMLReadException e) { - logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage()); - logger.catching(Level.ERROR, e); - } - } - - private static void streamAndDiscardFeatures(CityGmlZipEntry entry, ParserConfiguration config, ObservedInputStream ois, - CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { - Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, entry, false); - readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); - } - private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois, CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { getContext(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/ArchivePacker.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/ArchivePacker.java deleted file mode 100644 index feadacb3..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/ArchivePacker.java +++ /dev/null @@ -1,98 +0,0 @@ -package de.hft.stuttgart.citydoctor2.utils; - -import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -public class ArchivePacker { - - private static final Logger logger = LogManager.getLogger(ArchivePacker.class); - - private ArchivePacker(){} - - public static void packArchive(String targetPath, CityGmlZipArchive archive){ - Path outputPath = Path.of(targetPath); - outputPath = outputPath.getParent().resolve( - FilenameUtils.removeExtension(outputPath.getFileName().toString()) + ".zip"); - Path tmpDir = null; - try{ - tmpDir = Files.createTempDirectory("zipTmp"); - tmpDir.toFile().deleteOnExit(); - for (CityGmlZipEntry entry : archive.getEntries()) { - if (entry.getErrorType() != null || !entry.isLoaded()) { - continue; - } - CityDoctorModel model = entry.getModel(); - Path filePath = tmpDir.resolve(entry.getEntrySubPath()); - Files.createDirectories(filePath.getParent()); - model.saveAs(filePath.toString(), model.isValidated()); - } - zipFolder(tmpDir.toFile(), outputPath.toFile()); - - }catch (Exception e){ - logger.error(e); - } finally { - if (tmpDir != null) { - try { - FileUtils.deleteDirectory(tmpDir.toFile()); - } catch (IOException e) { - logger.error(e); - } - } - } - } - - - public static void zipFolder(File folder, File zipFile) throws IOException { - zipFolder(folder, new FileOutputStream(zipFile)); - } - - public static void zipFolder(File folder, OutputStream outputStream) throws IOException { - try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) { - processFolder(folder, zipOutputStream, folder.getPath().length() + 1); - } - } - - private static void processFolder(File folder, ZipOutputStream zipOutputStream, int prefixLength) - throws IOException { - for (File file : folder.listFiles()) { - if (file.isFile()) { - ZipEntry zipEntry = new ZipEntry(file.getPath().substring(prefixLength)); - zipOutputStream.putNextEntry(zipEntry); - try (FileInputStream inputStream = new FileInputStream(file)) { - IOUtils.copy(inputStream, zipOutputStream); - } - zipOutputStream.closeEntry(); - } else if (file.isDirectory()) { - processFolder(file, zipOutputStream, prefixLength); - } - } - } - - public static void packAndDeleteDirectory(String directoryPath) throws IOException { - Path sourcePath = Path.of(directoryPath); - if (!Files.exists(sourcePath)) { - throw new IllegalStateException("Directory " + directoryPath + " does not exist"); - } - - if (!Files.isDirectory(sourcePath)) { - throw new IllegalStateException("Path " + directoryPath + " is not a directory"); - } - Path outputPath = sourcePath.getParent().resolve(sourcePath.getFileName() + ".zip"); - zipFolder(sourcePath.toFile(), outputPath.toFile()); - - FileUtils.deleteDirectory(sourcePath.toFile()); - } - -} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipArchive.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipArchive.java deleted file mode 100644 index be6e4cda..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipArchive.java +++ /dev/null @@ -1,151 +0,0 @@ -package de.hft.stuttgart.citydoctor2.zip; - -import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; -import de.hft.stuttgart.citydoctor2.utils.ArchivePacker; -import de.hft.stuttgart.citydoctor2.utils.Localization; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.Serial; -import java.io.Serializable; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; - -/** - * This class serves as a representation of a ZIP-archive containing CityGml files. The contained files are represented - * by {@link CityGmlZipEntry} objects. - */ -public class CityGmlZipArchive implements Serializable { - - private static final Logger logger = LogManager.getLogger(CityGmlZipArchive.class); - - @Serial - private static final long serialVersionUID = 2168389511043362615L; - - private List entries; - private final Path archivePath; - private final String archiveNameRE; - - /** - * Registers a ZIP-file as an archive. All contained XML and GML files will be registered as {@link CityGmlZipEntry} objects. - * - * @param zipFile path of the ZIP-file - * @return A {@link CityGmlZipArchive} object representing the archive, or null if read-access for the ZIP-file was denied - * @throws MalformedZipFileException if the ZIP-file has encoding errors - */ - public static CityGmlZipArchive register(String zipFile) throws MalformedZipFileException { - ArrayList archiveEntries = new ArrayList<>(); - CityGmlZipArchive cgmlArchive = new CityGmlZipArchive(Path.of(zipFile)); - try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) { - ZipEntry ze; - while ((ze = zis.getNextEntry()) != null) { - if (ze.isDirectory()) { - continue; - } - if (ze.getName().endsWith(".gml") || ze.getName().endsWith(".xml")) { - // Ignore report files generated by CityDoctor - if (ze.getName().endsWith("_report.xml")) { - continue; - } - archiveEntries.add(CityGmlZipEntry.register(ze, cgmlArchive)); - } - } - cgmlArchive.setEntries(archiveEntries); - return cgmlArchive; - } catch (IOException e) { - logger.error(Localization.getText("CityGmlZipArchive.ioError")); - logger.debug(e.getMessage(), e); - return null; - } catch (IllegalArgumentException e) { - logger.error(Localization.getText("CityGmlZipArchive.malformedZipFile")); - throw new MalformedZipFileException(Localization.getText("CityGmlZipArchive.malformedZipFile")); - } catch (Exception e) { - logger.error(Localization.getText("CityGmlZipArchive.generalException")); - logger.debug(e.getMessage(), e); - throw (e); - } - } - - /** - * Attempts to load all contained {@link CityGmlZipEntry} objects into memory. - * - * @param config Parser configuration for the entries - */ - public void mountArchive(ParserConfiguration config) { - try (ZipFile zip = new ZipFile(archivePath.toFile())) { - for (CityGmlZipEntry entry : entries) { - entry.loadEntry(config); - } - } catch (IOException e) { - logger.error(e); - } - } - - private CityGmlZipArchive(Path archivePath) { - this.archivePath = archivePath; - this.archiveNameRE = archivePath.getFileName().toString().replace(".zip", "") + File.separator; - } - - private void setEntries(List entries) { - this.entries = entries; - } - - /** - * Saves this archive as a ZIP-file. - * @param path target path for the export - */ - public void exportToZipFile(String path) { - ArchivePacker.packArchive(path, this); - } - - /** - * Gets an entry in this archive by its subpath. - * - * @param subpath subpath to the entry from this archive's root directory - * @return the corresponding entry, or null if entry could not be found - */ - public CityGmlZipEntry getEntry(String subpath) { - subpath = stripArchivePath(subpath); - for (CityGmlZipEntry entry : entries) { - String entryName = stripArchivePath(entry.getEntrySubPath()); - if (entryName.equals(subpath)) { - return entry; - } - } - return null; - } - - /** - * Strips the root directory name of this archive from an entry subpath. - * @param path the relative path to the entry - * @return String of the path without the root directory - */ - private String stripArchivePath(String path) { - Path systemPath = Path.of(path); - return systemPath.toString().replace(archiveNameRE, ""); - } - - /** - * Gets the path to the ZIP-file associated with this archive. - * @return path of the ZIP-file - */ - public Path getArchivePath() { - return archivePath; - } - - /** - * Gets the entry list of this archive. - * @return the entry list - */ - public List getEntries() { - return entries; - } - -} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java deleted file mode 100644 index 81f8f122..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java +++ /dev/null @@ -1,219 +0,0 @@ -package de.hft.stuttgart.citydoctor2.zip; - -import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; -import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; -import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; -import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; -import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; -import de.hft.stuttgart.citydoctor2.parser.ProgressListener; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.IOException; -import java.io.Serial; -import java.io.Serializable; -import java.nio.file.Path; -import java.util.zip.ZipEntry; - -/** - * This class serves as a representation of a CityGml file in a ZIP archive. - *

- * The {@link CityDoctorModel} of the associated file can only be accessed after loading the corresponding entry. - */ -public class CityGmlZipEntry implements Serializable { - - private static final Logger logger = LogManager.getLogger(CityGmlZipEntry.class); - @Serial - private static final long serialVersionUID = -5732913269959043262L; - - private final Path filePath; - private final String fileName; - private final String displayName; - private transient CityDoctorModel model; - private CityGmlZipArchive parentArchive; - private boolean inMemory; - private long fileSize = -1L; - private static final long MB = 1024 * 1024L; - private ZipEntryErrorType errorType = null; - - - /** - * Attempts to parse the CityGML model associated with this entry and loads it into memory. - *

- * In the event of a parsing error, the entry will be flagged by assigning the appropriate - * {@link ZipEntryErrorType} to the errorType field. - * - * @param config Parser configuration for this model - */ - public void loadEntry(ParserConfiguration config) { - loadEntry(config, null, false); - } - - public void loadEntry(ParserConfiguration config, boolean nested) { - loadEntry(config, null, nested); - } - - public void loadEntry(ParserConfiguration config, ProgressListener progressListener) { - loadEntry(config, progressListener, false); - } - - /** - * Attempts to parse the CityGML model associated with this entry and loads it into memory. - *

- * In the event of a parsing error, the entry will be flagged by assigning the appropriate - * {@link ZipEntryErrorType} to the errorType field. - * - * @param config Parser configuration for this model - * @param l ProgressListener for tracking parsing progress - */ - public void loadEntry(ParserConfiguration config, ProgressListener l, boolean nested) { - if (inMemory) { - return; - } - if (errorType != null) { - logger.warn("Tried loading erroneous CityGmlZipEntry"); - return; - } - try { - this.model = CityGmlParser.parseCityGmlZipEntry(this, config, l, nested); - this.inMemory = true; - } catch (CityGmlParseException | InvalidGmlFileException e) { - logger.error(e); - this.errorType = ZipEntryErrorType.INVALID_CITY_GML_FILE; - } catch (IOException e) { - logger.error(e); - this.errorType = ZipEntryErrorType.IO_ERROR; - } - } - - /** - * Registers a {@link ZipEntry ZipEntry} in a ZIP-archive as a {@link CityGmlZipEntry}. - *

- * If the filesize of the associated file exceeds the total available system memory, or read access is denied, - * the entry will be flagged by assigning the appropriate {@link ZipEntryErrorType} to the errorType field. - * - * @param entry The ZipEntry object - * @param parentArchive The parent archive for this entry - * @return A corresponding CityGmlZipEntry object - */ - public static CityGmlZipEntry register(ZipEntry entry, CityGmlZipArchive parentArchive) { - CityGmlZipEntry cgzEntry = new CityGmlZipEntry(entry, parentArchive, false); - try { - if (!cgzEntry.entrySizeWithinMemoryLimits()) { - cgzEntry.errorType = ZipEntryErrorType.EXCESSIVE_FILESIZE; - } - } catch (IOException e) { - logger.error(e); - cgzEntry.errorType = ZipEntryErrorType.IO_ERROR; - } - return cgzEntry; - } - - /** - * Checks if the filesize of this entry would exceed the system's available memory. - * - * @return true if within limits, false otherwise - * @throws IOException if read access to the ZIP-archive is denied - */ - private boolean entrySizeWithinMemoryLimits() throws IOException { - long memoryLimit = (long) Math.ceil(((double) Runtime.getRuntime().maxMemory() / MB) * 0.9); - if (fileSize == -1L) { - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(this)) { - long filesize = entryFile.getFileSize(); - if (filesize != -1) { - this.fileSize = filesize; - } else { - return false; - } - } catch (Exception e) { - throw new IOException(e); - } - } - return memoryLimit > (fileSize / MB); - } - - private CityGmlZipEntry(ZipEntry entry, CityGmlZipArchive parentArchive, boolean inMemory) { - this.fileName = entry.getName(); - this.filePath = Path.of(entry.getName()); - this.displayName = filePath.getFileName().toString(); - if (entry.getSize() != -1) { - this.fileSize = entry.getSize(); - } - this.model = null; - this.inMemory = inMemory; - this.parentArchive = parentArchive; - } - - /** - * Gets the parent archive of this entry. - * @return the archive - */ - public CityGmlZipArchive getArchive() { - return parentArchive; - } - - /** - * Gets the sub-path in the archive of the file associated with this entry. - * - * @return the relative sub-path from the archive root directory - */ - public String getEntrySubPath() { - return fileName; - } - - /** - * Gets the name of the file associated with this entry. - * @return the filename - */ - public String getDisplayName() { - return displayName; - } - - /** - * Gets the {@link ZipEntryErrorType} of this entry. - * @return the error type, or null if this entry is not erroneous - */ - public ZipEntryErrorType getErrorType() { - return errorType; - } - - /** - * Gets the {@link CityDoctorModel} of this entry. - * @return The model, or null if the entry has not been loaded or is erroneous - */ - public CityDoctorModel getModel() { - return model; - } - - public void setFileSize(long size) { - fileSize = size; - } - - /** - * Resolves the reference path of a {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject LibraryObject} - * in the {@link CityDoctorModel} of this entry. - * @param libraryObjectPath The reference path to the LibraryObject's CityGml file - * @return Relative sub-path within the archive to the CityGml file - */ - public Path resolveLibraryObjectPath(String libraryObjectPath) { - return filePath.getParent().resolve(libraryObjectPath); - } - - /** - * Checks if the {@link CityDoctorModel} of this entry has been loaded into memory. - * - * @return true if it is loaded, false otherwise - */ - public boolean isLoaded() { - return inMemory; - } - - /** - * Returns the estimated uncompressed filesize of this entry. - * - * @return filesize in bytes, -1L if filesize is unknown - */ - public long getFileSize() { - return fileSize; - } -} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntryFile.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntryFile.java deleted file mode 100644 index 3bf82fe0..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntryFile.java +++ /dev/null @@ -1,79 +0,0 @@ -package de.hft.stuttgart.citydoctor2.zip; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * This class serves as a resource for read-access to the file that is being represented by - * a CityGmlZipEntry object. - *

- * Does not decompress the file or the parent ZIP-archive. - */ -public class CityGmlZipEntryFile implements AutoCloseable { - - private final ZipFile zip; - private final ZipEntry zipEntry; - private final CityGmlZipEntry cgmlZipEntry; - private boolean closed = false; - - /** - * Creates a {@link CityGmlZipEntryFile} for a {@link CityGmlZipEntry} object's referenced file. - * - * @param entry the requested entry - * @throws IOException if read access to the ZIP-archive is denied. - */ - public CityGmlZipEntryFile(CityGmlZipEntry entry) throws IOException { - CityGmlZipArchive archive = entry.getArchive(); - zip = new ZipFile(archive.getArchivePath().toFile()); - zipEntry = zip.getEntry(entry.getEntrySubPath()); - this.cgmlZipEntry = entry; - } - - /** - * Gets a new InputStream of this file. - *

- * Returned streams are independent of each other, but will all be closed when the {@link CityGmlZipEntryFile} object - * is closed. - * @return an InputStream of this file. - * @throws IOException when this method is called after the file has been closed. - */ - public InputStream getInputStream() throws IOException { - if (closed){ - throw new IOException("CityGmlZipEntryFile closed"); - } - return zip.getInputStream(zipEntry); - } - - /** - * Gets an estimation of this file's filesize. - * @return filesize in bytes, -1L if unknown - * @throws IOException when this method is called after the file has been closed. - */ - public long getFileSize() throws IOException { - if (closed){ - throw new IOException("CityGmlZipEntryFile closed"); - } - if (cgmlZipEntry.getFileSize() == -1L) { - if (zipEntry.getSize() == -1) { - long bytes = 0; - InputStream is = new BufferedInputStream(this.getInputStream()); - for (int i = is.read(); i != -1; i = is.read()) { - bytes++; - } - cgmlZipEntry.setFileSize(bytes); - } else { - cgmlZipEntry.setFileSize(zipEntry.getSize()); - } - } - return cgmlZipEntry.getFileSize(); - } - - @Override - public void close() throws IOException { - zip.close(); - closed = true; - } -} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/MalformedZipFileException.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/MalformedZipFileException.java deleted file mode 100644 index 759fbe6b..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/MalformedZipFileException.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.hft.stuttgart.citydoctor2.zip; - -import java.util.zip.ZipException; - -/** - * Signals that a ZIP-file is corrupted and couldn't be parsed. - */ -public class MalformedZipFileException extends ZipException { - - public MalformedZipFileException() { - super(); - } - - public MalformedZipFileException(String message) { - super(message); - } -} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/ZipEntryErrorType.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/ZipEntryErrorType.java deleted file mode 100644 index 7dec72b4..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/ZipEntryErrorType.java +++ /dev/null @@ -1,8 +0,0 @@ -package de.hft.stuttgart.citydoctor2.zip; - -/** - * Error types for {@link CityGmlZipEntry} objects. - */ -public enum ZipEntryErrorType { - INVALID_CITY_GML_FILE, EXCESSIVE_FILESIZE, IO_ERROR -} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/ZipParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/ZipParser.java deleted file mode 100644 index e65d96b8..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/ZipParser.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.hft.stuttgart.citydoctor2.zip; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class ZipParser { - - private static final Logger logger = LogManager.getLogger(ZipParser.class); - - - -} diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 78349133..4ec75d73 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -113,27 +113,6 @@ CheckDialog.outputPathBtn=Select CheckDialog.pdfReportsOption=Create PDF reports CheckDialog.xmlReportsOption=Create XML reports CheckDialog.noPathError=Output path is missing! -ZipEntryManager.title=Zip-entry manager -ZipEntryManager.entryListTitle=Zip-entries -ZipEntryManager.metadata=Metadata -ZipEntryManager.subpathLbl=Subpath: -ZipEntryManager.erroneousLbl=Erroneous: -ZipEntryManager.excessiveFileSize=Filesize exceeds memory -ZipEntryManager.invalidCityGml=Invalid CityGML file -ZipEntryManager.ioError=Unable to access file -ZipEntryManager.filesizeLbl=Filesize: -ZipEntryManager.validatedLbl=Validated: -ZipEntryManager.objectCountLbl=Object count: -ZipEntryManager.loadBtn=Load CityGML model -ZipEntryManager.decompressBtn=Unpack file -ZipEntryManager.decompressAllBtn=Unpack all files -ZipEntryManager.checkAllBtn=Check all files -ZipEntryManager.showReportBtn=Show error statistic -ZipEntryManager.saveBtn=Export validation results -ZipEntryManager.cancelBtn=Cancel -ZipEntryManager.unknownValue=N/A -ZipEntryManager.yes=Yes -ZipEntryManager.no=No CityGmlZipArchive.ioError=Accessing the ZIP archive failed CityGmlZipArchive.malformedZipFile=Parsing of ZIP archive aborted, ZIP file is malformed! CityGmlZipArchive.generalException=Encountered an unexpected error while reading the ZIP archive diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 864c13b7..d76c005b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -111,27 +111,6 @@ CheckDialog.outputPathBtn=Ausw\u00e4hlen CheckDialog.pdfReportsOption=PDF Berichte erstellen CheckDialog.xmlReportsOption=XML Berichte erstellen CheckDialog.noPathError=Ausgabepfad wurde nicht gesetzt! -ZipEntryManager.title=Zipeintrag Manager -ZipEntryManager.entryListTitle=Zipeintr\u00e4ge -ZipEntryManager.metadata=Metadaten -ZipEntryManager.subpathLbl=Unterpfad: -ZipEntryManager.erroneousLbl=Fehlerhaft: -ZipEntryManager.excessiveFileSize=Datei zu gro\u00DF -ZipEntryManager.invalidCityGml=Ung\u00fcltige CityGML Datei -ZipEntryManager.ioError=Dateizugriff fehlgeschlagen -ZipEntryManager.filesizeLbl=Dateigr\u00f6\u00DFe: -ZipEntryManager.validatedLbl=Validiert: -ZipEntryManager.objectCountLbl=Objektanzahl: -ZipEntryManager.loadBtn=CityGML Modell laden -ZipEntryManager.decompressBtn=Datei entpacken -ZipEntryManager.decompressAllBtn=Alle Dateien entpacken -ZipEntryManager.checkAllBtn=Alle Dateien pr\u00fcfen -ZipEntryManager.showReportBtn=Fehlerstatistik anzeigen -ZipEntryManager.saveBtn=Validierungsergebnisse exportieren -ZipEntryManager.cancelBtn=Abbruch -ZipEntryManager.unknownValue=Unbekannt -ZipEntryManager.yes=Ja -ZipEntryManager.no=Nein CityGmlZipArchive.ioError=Zugriff auf ZIP-Datei fehlgeschlagen CityGmlZipArchive.malformedZipFile=Einlesen des ZIP-Archivs abgebrochen, ZIP-Datei ist fehlerhaft! CityGmlZipArchive.generalException=Unerwarteter Fehler beim Einlesen des ZIP-Archivs diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java index 5b73190b..9ae9a38e 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java @@ -21,10 +21,8 @@ package de.hft.stuttgart.citydoctor2; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.nio.file.Path; import java.util.List; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.ade.ADEException; @@ -162,8 +160,6 @@ public class CityDoctorValidation { } if (inputFile.getName().endsWith(".gml") || inputFile.getName().endsWith(".xml")) { startFileValidation(inputFile, xmlOutput, pdfOutput, config, outputFile); - } else if (inputFile.getName().endsWith(".zip")) { - startZipValidation(input, xmlOutput, pdfOutput, config, outputFile); } else { logger.error("Input file '{}' does not end with .gml or .xml and is not a zip archive.", input); System.exit(1); @@ -201,29 +197,6 @@ public class CityDoctorValidation { } } - public static void startZipValidation(String inputZipFile, String xmlOutput, String pdfOutput, - ValidationConfiguration config, String outputFile) throws CityGmlParseException, IOException { - xmlOutput = toDirectoryPath(xmlOutput); - pdfOutput = toDirectoryPath(pdfOutput); - outputFile = toDirectoryPath(outputFile); - if (xmlOutput == null && pdfOutput == null && outputFile == null) { - logger.warn("No output locations specified, results of validation will not be saved"); - } - CityGmlZipArchive archive = CityGmlZipArchive.register(inputZipFile); - Checker.streamCheck(archive, xmlOutput, pdfOutput, config, outputFile); - } - - public static String toDirectoryPath(String input) { - if (input == null) { - return null; - } - String parentDirectory = Path.of(input).getParent().toString() + File.separator; - String name = Path.of(input).getFileName().toString().replaceFirst("\\..+", "") + File.separator; - File f = new File(parentDirectory + name); - f.mkdir(); - return f.getPath() + File.separator; - } - public static String getPdfOutput(ArgumentParser argParser) { if (argParser.containsOption("pdfreport")) { List reportFiles = argParser.getValues("pdfreport"); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index c5288bac..0f2ad0ae 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -44,11 +44,7 @@ import de.hft.stuttgart.citydoctor2.reporting.XmlStreamReporter; import de.hft.stuttgart.citydoctor2.reporting.XmlValidationReporter; import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfReporter; import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfStreamReporter; -import de.hft.stuttgart.citydoctor2.utils.ArchivePacker; import de.hft.stuttgart.citydoctor2.utils.Localization; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile; import de.hft.stuttgart.quality.model.enums.RequirementId; import de.hft.stuttgart.quality.model.enums.TopLevelFeatureType; import de.hft.stuttgart.quality.model.properties.CheckingProperty; @@ -59,7 +55,6 @@ import de.hft.stuttgart.quality.model.properties.RequirementProperty; import de.hft.stuttgart.quality.model.types.Checking; import de.hft.stuttgart.quality.model.types.Parameter; import de.hft.stuttgart.quality.model.types.ValidationPlan; -import org.apache.commons.io.FilenameUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -825,73 +820,6 @@ public class Checker { return hasUnusedDependency; } - public static void streamCheck(CityGmlZipArchive archive, String xmlDirectory, String pdfDirectory, ValidationConfiguration config, - String outputDirectory) throws IOException, CityGmlParseException { - streamCheck(archive, xmlDirectory, pdfDirectory, config, null, outputDirectory); - } - - public static void streamCheck(CityGmlZipArchive archive, String xmlDirectory, String pdfDirectory, ValidationConfiguration config, - FeatureCheckedListener l, String outputDirectory) throws IOException, CityGmlParseException { - - - for (CityGmlZipEntry entry : archive.getEntries()) { - String entryName = FilenameUtils.removeExtension(entry.getDisplayName()); - String xmlOutput = null; - String pdfOutput = null; - String outputFile = null; - if (xmlDirectory != null) { - xmlOutput = Path.of(xmlDirectory).resolve(entryName + "_report.xml").toString(); - } - if (pdfDirectory != null) { - pdfOutput = Path.of(pdfDirectory).resolve(entryName + "_report.pdf").toString(); - } - if (outputDirectory != null) { - outputFile = Path.of(outputDirectory).resolve(entryName + "_validated.gml").toString(); - } - - streamCheck(entry, xmlOutput, pdfOutput, config, l, outputFile); - - - } - if (outputDirectory != null) { - ArchivePacker.packAndDeleteDirectory(outputDirectory); - } - } - - - public static void streamCheck(CityGmlZipEntry entry, String xmlOutput, String pdfOutput, ValidationConfiguration config, - String outputFile) throws IOException, CityGmlParseException { - streamCheck(entry, xmlOutput, pdfOutput, config, null, outputFile); - } - - public static void streamCheck(CityGmlZipEntry entry, String xmlOutput, String pdfOutput, ValidationConfiguration config, - FeatureCheckedListener l, String outputFile) throws IOException, CityGmlParseException { - try (BufferedOutputStream xmlBos = getXmlOutputMaybe(xmlOutput); - BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) { - Checker c = new Checker(config, null); - String fileName = entry.getEntrySubPath(); - - // create reporter if available - XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName); - PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName); - - // execute schematron first - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { - SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, entryFile.getInputStream()); - - CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, l); - // parse and validate - CityGmlParser.streamCityGml(entry, config.getParserConfiguration(), con, outputFile); - - // write reports if available - writeReport(xmlReporter); - writeReport(pdfReporter); - } - } catch (CheckReportWriteException e) { - logger.error(Localization.getText("Checker.failReports"), e); - } - } - public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config, String outputFile) throws IOException, CityGmlParseException { streamCheck(inputFile, xmlOutput, pdfOutput, config, null, outputFile); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java index 311f19b6..623153e5 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java @@ -128,76 +128,6 @@ public class CheckerTest { } } - @Test - public void testCliZipChecking() throws IOException, CityDoctorWriteException, CityGmlParseException, InvalidGmlFileException { - String output = folder.getRoot().toPath() + File.separator + "test"; - String pdfOutput = folder.getRoot().toPath() + File.separator + "pdf" + File.separator; - String xmlOutput = folder.getRoot().toPath() + File.separator + "xml" + File.separator; - try { - String[] args = new String[10]; - args[0] = "-in"; - args[1] = "src/test/resources/zipArchive.zip"; - args[2] = "-config"; - args[3] = "src/test/resources/testConfigWithStreaming.yml"; - args[4] = "-pdfReport"; - args[5] = pdfOutput; - args[6] = "-xmlReport"; - args[7] = xmlOutput; - args[8] = "-out"; - args[9] = output + File.separator; - CityDoctorValidation.main(args); - File zipFile = new File(output + ".zip"); - File pdfDir = new File(pdfOutput); - File xmlDir = new File(xmlOutput); - assertTrue(zipFile.exists()); - assertTrue(pdfDir.exists()); - assertTrue(xmlDir.exists()); - - assertTrue(pdfDir.isDirectory()); - assertTrue(xmlDir.isDirectory()); - assertEquals(5, pdfDir.listFiles().length); - assertEquals(5, xmlDir.listFiles().length); - - CityGmlZipArchive cgmlArch = CityGmlZipArchive.register(output + ".zip"); - assertNotNull(cgmlArch); - cgmlArch.mountArchive(new ParserConfiguration(8, true)); - assertEquals(5, cgmlArch.getEntries().size()); - for (CityGmlZipEntry entry : cgmlArch.getEntries()) { - assertNotNull(entry); - assertNull(entry.getErrorType()); - } - - } catch (CityGmlParseException | IOException | InvalidGmlFileException | CityDoctorWriteException e) { - throw e; - } - } - - @Test - public void testZipEntryChecking() throws CityGmlParseException, IOException { - CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zipArchive.zip"); - assertNotNull(cgmlArch); - cgmlArch.mountArchive(new ParserConfiguration(8, false)); - ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); - FeatureCheckedListener l = co -> { - assertTrue("CityObject should be validated", co.isValidated()); - }; - for (CityGmlZipEntry entry : cgmlArch.getEntries()) { - Checker.streamCheck(entry, null, null, config, l, null); - } - } - - @Test - public void testZipArchiveChecking() throws CityGmlParseException, IOException { - CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zipArchive.zip"); - assertNotNull(cgmlArch); - cgmlArch.mountArchive(new ParserConfiguration(8, false)); - ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); - FeatureCheckedListener l = co -> { - assertTrue(co.isValidated()); - }; - Checker.streamCheck(cgmlArch, null, null, config, l, null); - } - @Test public void testReportCreation() throws IOException, CityGmlParseException, InvalidGmlFileException { Path tmpDir = null; diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java index 23ae6271..098bcaeb 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java @@ -139,8 +139,6 @@ public class CheckDialog { @FXML private Label schematronFileLabel; - @FXML - private VBox zipArchiveOptionsVBox; @FXML private CheckBox streamCheckOption; @FXML @@ -161,7 +159,6 @@ public class CheckDialog { private CityDoctorController controller; private FilterPane filterPane; private MainWindow window; - private boolean zipArchiveMode; public CheckDialog(MainWindow window, Window parent, CityDoctorController controller) throws IOException { this.window = window; @@ -181,7 +178,6 @@ public class CheckDialog { } public void initialize() { - setupZipArchiveOptions(); createEnableColumn(geometricTable); createNameColumn(geometricTable); @@ -286,46 +282,6 @@ public class CheckDialog { }); } - private void setupZipArchiveOptions() { - streamCheckOption.selectedProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - streamCheckParametersVBox.setDisable(!streamCheckOption.isSelected()); - } - }); - - outputPathBtn.setOnAction(e -> { - DirectoryChooser directoryChooser = new DirectoryChooser(); - directoryChooser.setInitialDirectory(Path.of(Settings.get(Settings.LAST_OPEN_FOLDER)).getParent().toFile()); - File f = directoryChooser.showDialog(stage.getOwner()); - if (f != null) { - outputPathTextField.setText(f.getAbsolutePath()); - Settings.set(Settings.LAST_OPEN_FOLDER, f.getAbsolutePath()); - } - - - }); - - streamCheckOption.setText(Localization.getText("CheckDialog.streamCheckOption")); - streamCheckLabel.setTooltip(new Tooltip(Localization.getText("CheckDialog.streamCheckLabel"))); - outputPathLabel.setText(Localization.getText("CheckDialog.outputPathLabel")); - outputPathBtn.setText(Localization.getText("CheckDialog.outputPathBtn")); - pdfReportsOption.setText(Localization.getText("CheckDialog.pdfReportsOption")); - xmlReportsOption.setText(Localization.getText("CheckDialog.xmlReportsOption")); - - showZipArchiveOptions(false); - } - - /** - * Shows the option panel for ZIP-Archive checking and enables the ZIP-Archive checking mode for this dialog. - * - * @param enabled - */ - public void showZipArchiveOptions(boolean enabled) { - zipArchiveOptionsVBox.setDisable(!enabled); - zipArchiveOptionsVBox.setVisible(enabled); - zipArchiveMode = enabled; - } private void setupSelectButton() { selectBtn.setOnAction(ae -> { @@ -494,25 +450,7 @@ public class CheckDialog { String startingChecks = Localization.getText("CheckDialog.startingChecks"); logger.info(startingChecks); } - if (zipArchiveMode) { - Path outputPath = null; - if (streamCheckOption.isSelected()) { - if (outputPathTextField.getText().isEmpty()) { - Alert warning = new Alert(Alert.AlertType.ERROR); - warning.setTitle("Error"); - warning.setContentText(Localization.getText("CheckDialog.noPathError")); - warning.showAndWait(); - return; - } - outputPath = Path.of(outputPathTextField.getText()); - Platform.runLater(() -> progress.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS)); - } - controller.startZipFileChecks(config, progress::setProgress, streamCheckOption.isSelected(), - outputPath, pdfReportsOption.isSelected(), - xmlReportsOption.isSelected()); - } else { - controller.startChecks(config, progress::setProgress); - } + controller.startChecks(config, progress::setProgress); if (logger.isInfoEnabled()) { String checksDone = Localization.getText("CheckDialog.checksDone"); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index f2ece4ff..b3e571b1 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -17,15 +17,10 @@ import de.hft.stuttgart.citydoctor2.gui.tree.node.OpeningNode; import de.hft.stuttgart.citydoctor2.gui.tree.node.TopLevelCityObjectNode; import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; import de.hft.stuttgart.citydoctor2.parser.*; -import de.hft.stuttgart.citydoctor2.utils.ArchivePacker; import de.hft.stuttgart.citydoctor2.utils.Localization; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; -import de.hft.stuttgart.citydoctor2.zip.MalformedZipFileException; import javafx.application.Platform; import javafx.scene.chart.XYChart.Data; import javafx.scene.chart.XYChart.Series; -import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import javafx.scene.control.Dialog; import javafx.scene.control.TreeItem; @@ -33,18 +28,12 @@ import javafx.scene.control.TreeView; import javafx.scene.paint.Color; import javafx.stage.FileChooser; import javafx.stage.FileChooser.ExtensionFilter; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.core.CityModel; import org.xml.sax.SAXParseException; -import java.awt.*; import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.*; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -63,9 +52,7 @@ public class CityDoctorController { private CityDoctorModel model; private ParserConfiguration currentConfig; private String sourceFile; - private CityGmlZipArchive zipArchive; - private ZipEntryManager currentZipEntryManager; - private HashMap previousCheckers = null; + private Checker currentChecker; private HighlightController highlightController; @@ -100,7 +87,7 @@ public class CityDoctorController { try { Platform.runLater(() -> { mainWindow.getOpenBtn().setDisable(true); - mainWindow.getZipManagerBtn().setDisable(true); + mainWindow.getWriteReportButton().setDisable(true); mainWindow.getMeshGroup().getChildren().clear(); @@ -108,12 +95,6 @@ public class CityDoctorController { mainWindow.resetSearchBar(); mainWindow.resetFilterComboBox(); }); - if (path.endsWith(".zip")) { - loadCityGmlZipFile(path, numberOfRoundingPlaces, useValidation, lowMemory); - return; - } - currentZipEntryManager = null; - previousCheckers = null; currentChecker = null; currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory); GMLValidationHandler handler = null; @@ -160,7 +141,6 @@ public class CityDoctorController { mainWindow.getSaveBtn().setDisable(false); mainWindow.getResetCameraBtn().setDisable(false); mainWindow.getHideRoofBtn().setDisable(false); - mainWindow.getZipManagerBtn().setDisable(true); mainWindow.getNorthArrow().setVisible(true); mainWindow.alignNorthArrow(); setupFeatureTabs(); @@ -171,103 +151,10 @@ public class CityDoctorController { } } - private void loadCityGmlZipFile(String path, int numberOfRoundingPlaces, boolean useValidation, - boolean lowMemory) { - - currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory); - try { - zipArchive = CityGmlZipArchive.register(path); - } catch (MalformedZipFileException e) { - Platform.runLater(() -> { - Alert alert = new Alert(Alert.AlertType.ERROR, e.getMessage()); - alert.showAndWait(); - }); - } catch (Exception e) { - Platform.runLater(() -> { - ExceptionDialog exDialog = new ExceptionDialog(); - exDialog.show(e); - }); - } - - if (zipArchive != null) { - previousCheckers = new HashMap<>(); - Platform.runLater(() -> { - try { - currentZipEntryManager = new ZipEntryManager(mainWindow.getMainStage(), this); - currentZipEntryManager.show(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } - } - - public void decompressZipEntry(CityGmlZipEntry entry, ProgressListener l) { - entry.loadEntry(currentConfig, l); - } - - public CityGmlZipArchive getZipArchive() { - return zipArchive; - } - public static int getMaxFeaturesPerChunk(){ return MAX_FEATURES_PER_CHUNK; } - public void loadZipEntry(CityGmlZipEntry entry) { - if (!entry.isLoaded()) { - entry.loadEntry(currentConfig); - } - if (entry.getErrorType() != null) { - throw new IllegalArgumentException("Attempted to load erroneous zip entry"); - } - try { - Platform.runLater(() -> { - mainWindow.getOpenBtn().setDisable(true); - mainWindow.getZipManagerBtn().setDisable(true); - mainWindow.getWriteReportButton().setDisable(true); - mainWindow.getMeshGroup().getChildren().clear(); - clearTrees(); - mainWindow.resetSearchBar(); - mainWindow.resetFilterComboBox(); - }); - - - model = entry.getModel(); - currentChecker = previousCheckers.getOrDefault(model, null); - mainWindow.getClickHandler().setConfig(currentConfig); - sourceFile = entry.getArchive().getArchivePath().toString(); - renderer.reset(); - - Platform.runLater(() -> { - mainWindow.addFileNameToTitle(entry.getArchive().getArchivePath().toString()); - mainWindow.getCheckButton().setDisable(false); - mainWindow.getLod1Btn().setDisable(false); - mainWindow.getLod2Btn().setDisable(false); - mainWindow.getLod3Btn().setDisable(false); - mainWindow.getLod4Btn().setDisable(false); - mainWindow.getWorldBtn().setDisable(false); - mainWindow.getSaveBtn().setDisable(false); - mainWindow.getResetCameraBtn().setDisable(false); - mainWindow.getHideRoofBtn().setDisable(false); - mainWindow.getZipManagerBtn().setDisable(false); - mainWindow.getNorthArrow().setVisible(true); - mainWindow.alignNorthArrow(); - setupFeatureTabs(); - buildTrees(); - updateFeatureTrees(); - mainWindow.getWriteReportButton().setDisable(currentChecker == null); - }); - } finally { - Platform.runLater(() -> mainWindow.getOpenBtn().setDisable(false)); - } - - } - - public void showZipManager() { - Platform.runLater(() -> currentZipEntryManager.show()); - } - private void setupFeatureTabs() { mainWindow.setDisableOfFeatureTab(FeatureType.BUILDING, model.getBuildingCount() == 0); mainWindow.setDisableOfFeatureTab(FeatureType.VEGETATION, model.getVegetationCount() == 0); @@ -1190,9 +1077,6 @@ public class CityDoctorController { config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces()); config.setParserConfig(currentConfig); currentChecker = new Checker(config, model); - if (previousCheckers != null) { - previousCheckers.put(model, currentChecker); - } currentChecker.runChecks(l); Platform.runLater(() -> { @@ -1213,138 +1097,6 @@ public class CityDoctorController { } } - public void startZipFileChecks(ValidationConfiguration config, ProgressListener l, boolean lowMemoryMode, - Path outputPath, boolean pdfReports, boolean xmlReports) { - if (zipArchive == null && logger.isWarnEnabled()) { - logger.warn(Localization.getText("CityDoctorController.noZipFile")); - } - - if (lowMemoryMode) { - startZipFileCheckStream(config, outputPath, pdfReports, xmlReports); - return; - } - try { - Platform.runLater(() -> { - mainWindow.getCheckButton().setDisable(true); - mainWindow.getErrorTree().getRoot().getChildren().clear(); - mainWindow.getGlobalErrorsView().getItems().clear(); - }); - previousCheckers = new HashMap<>(); - zipArchive.mountArchive(currentConfig); - config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces()); - config.setParserConfig(currentConfig); - int totalEntries = zipArchive.getEntries().size(); - int checkedEntries = 0; - for (CityGmlZipEntry entry : zipArchive.getEntries()) { - if (entry.getErrorType() != null && entry.getModel() == null) { - continue; - } - model = entry.getModel(); - currentChecker = new Checker(config, model); - currentChecker.runChecks(); - previousCheckers.put(model, currentChecker); - checkedEntries++; - if (l != null) { - l.updateProgress((float) checkedEntries / totalEntries); - } - - } - Platform.runLater(() -> { - // apply check results to tree views - buildTrees(); - updateFeatureTrees(); - updateTree(mainWindow.getPolygonsView().getRoot()); - for (CheckError e : model.getGlobalErrors()) { - if (e instanceof SchematronError se) { - mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString() + " - " + se.getNameOfAttribute()); - } - } - renderer.refresh(); - mainWindow.getWriteReportButton().setDisable(false); - currentZipEntryManager.refresh(); - }); - } finally { - Platform.runLater(() -> mainWindow.getCheckButton().setDisable(false)); - } - - } - - private void startZipFileCheckStream(ValidationConfiguration valConfig, Path outputPath, boolean pdfReports, - boolean xmlReports) { - try { - Path outputZipDirectory = null; - if (outputPath.toFile().isDirectory()) { - - String filename = FilenameUtils.removeExtension(zipArchive.getArchivePath().getFileName().toString()); - filename = filename + "_validated"; - outputZipDirectory = outputPath.resolve(filename); - Files.createDirectories(outputZipDirectory); - } else { - String filename = FilenameUtils.removeExtension(outputPath.toAbsolutePath().getFileName().toString()); - outputZipDirectory = Path.of(filename); - } - - String xmlReportsDirectory = null; - String pdfReportsDirectory = null; - if (xmlReports) { - xmlReportsDirectory = Files.createDirectories( - outputZipDirectory.resolve("xml_reports/")).toAbsolutePath().toString(); - } - if (pdfReports) { - pdfReportsDirectory = Files.createDirectories( - outputZipDirectory.resolve("pdf_reports/")).toAbsolutePath().toString(); - } - Checker.streamCheck(zipArchive, xmlReportsDirectory, pdfReportsDirectory, valConfig, outputZipDirectory.toString()); - Desktop desktop = Desktop.getDesktop(); - desktop.open(outputPath.toFile()); - } catch (CityGmlParseException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - - } - - public void saveZipArchiveReports(File outputZipDirectory) { - if (zipArchive == null) { - return; - } - String directoryName = FilenameUtils.removeExtension(outputZipDirectory.getAbsolutePath()); - Path tempOutput = Path.of(directoryName + File.separator); - File temp = null; - try { - temp = tempOutput.toFile(); - for (CityGmlZipEntry entry : zipArchive.getEntries()) { - if (entry.getErrorType() != null || entry.getModel() == null) { - continue; - } - model = entry.getModel(); - if (model.isValidated()) { - Checker checker = previousCheckers.get(model); - Files.createDirectories(tempOutput.resolve(entry.getEntrySubPath()).getParent()); - checker.writeXmlReport(tempOutput.resolve(entry.getDisplayName() + "_report.xml").toString()); - checker.writePdfReport(tempOutput.resolve(entry.getDisplayName() + "_report.pdf").toString()); - } - } - ArchivePacker.packAndDeleteDirectory(tempOutput.toString()); - temp = null; - Alert alert = new Alert(Alert.AlertType.INFORMATION); - alert.setTitle("Export Report"); - alert.setContentText(Localization.getText("CityDoctorController.saveZipArchiveReports")); - alert.showAndWait(); - } catch (IOException e) { - logger.error(e); - } finally { - try { - if (temp != null) { - FileUtils.deleteDirectory(temp); - } - } catch (IOException e) { - logger.error(e); - } - } - } - void updateFeatureTrees() { updateTree(mainWindow.getBuildingsView().getRoot()); updateTree(mainWindow.getVegetationView().getRoot()); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java index 78fc8073..3993c617 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java @@ -85,12 +85,6 @@ public class MainToolBar { @FXML private ImageView lod4View; - @FXML - private Button zipManagerBtn; - - @FXML - private ImageView zipManagerView; - @FXML private Button aboutBtn; @@ -153,7 +147,6 @@ public class MainToolBar { setupReportButton(); setupResetCameraButton(); setupHideRoofButton(); - setupZipManagerButton(); loadImages(); gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected())); @@ -232,10 +225,6 @@ public class MainToolBar { Image img = new Image(inStream); hideRoofView.setImage(img); } - try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/zip.png")) { - Image img = new Image(inStream); - zipManagerView.setImage(img); - } } catch (IOException e) { // ignore close exception } @@ -347,14 +336,6 @@ public class MainToolBar { } - private void setupZipManagerButton() { - zipManagerBtn.setDisable(true); - zipManagerBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.zip"))); - zipManagerBtn.setOnAction(ae -> { - mainWindow.showZipManager(); - }); - } - public Button getCheckButton() { return checkBtn; } @@ -407,10 +388,6 @@ public class MainToolBar { return resetCameraBtn; } - public Button getZipManagerBtn() { - return zipManagerBtn; - } - public HBox getToolBar() { return toolBar; } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index d1fca153..eabe443f 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -1181,13 +1181,6 @@ public class MainWindow extends Application { return mainToolBar.getOpenBtn(); } - public void showZipManager() { - controller.showZipManager(); - } - - public Button getZipManagerBtn() { - return mainToolBar.getZipManagerBtn(); - } public VertexClickHandler getClickHandler() { return clickHandler; diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/OpenFileDialog.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/OpenFileDialog.java index 8e3b6c55..d5857b21 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/OpenFileDialog.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/OpenFileDialog.java @@ -120,7 +120,7 @@ public class OpenFileDialog { if (fc == null) { fc = new FileChooser(); fc.setTitle(Localization.getText("OpenFileDialog.select")); - fc.getExtensionFilters().add(new ExtensionFilter("GML/XML/ZIP", "*.gml", "*.xml", "*.zip")); + fc.getExtensionFilters().add(new ExtensionFilter("GML/XML", "*.gml", "*.xml")); fc.getExtensionFilters().add(new ExtensionFilter(Localization.getText("MainWindow.all"), "*.*")); } File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, "")); @@ -153,9 +153,6 @@ public class OpenFileDialog { Thread t = new Thread(() -> { try { - if (path.endsWith(".zip")) { - Platform.runLater(() -> progress.setProgress(-1)); - } controller.loadCityGml(path, numberOfRoundingPlaces, progress::setProgress, useValidation, lowMemory); Platform.runLater(() -> stage.close()); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ZipEntryManager.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ZipEntryManager.java deleted file mode 100644 index b27a3f37..00000000 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ZipEntryManager.java +++ /dev/null @@ -1,439 +0,0 @@ -package de.hft.stuttgart.citydoctor2.gui; - -import de.hft.stuttgart.citydoctor2.gui.tree.ZipEntryListCell; -import de.hft.stuttgart.citydoctor2.gui.tree.node.ZipEntryNode; -import de.hft.stuttgart.citydoctor2.utils.Localization; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; -import de.hft.stuttgart.citydoctor2.zip.ZipEntryErrorType; -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.ListView; -import javafx.scene.control.ProgressBar; -import javafx.scene.control.TextField; -import javafx.scene.control.TitledPane; -import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyEvent; -import javafx.scene.layout.VBox; -import javafx.stage.FileChooser; -import javafx.stage.Modality; -import javafx.stage.Stage; -import javafx.stage.Window; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.function.Predicate; - -/** - * This class implements a GUI for managing a loaded {@link CityGmlZipArchive} in CityDoctor. - */ -public class ZipEntryManager { - - private static final Logger logger = LogManager.getLogger(ZipEntryManager.class); - - private Stage stage; - private CityGmlZipArchive archive; - - @FXML - private TitledPane entriesPane; - @FXML - private TitledPane metadataPane; - - @FXML - private Label subpathLbl; - @FXML - private TextField subpathValueTxt; - - @FXML - private Label erroneousLbl; - @FXML - private Label erroneousValue; - - @FXML - private Label filesizeLbl; - @FXML - private Label filesizeValue; - - @FXML - private Label validatedLbl; - @FXML - private Label validatedValue; - - @FXML - private Label objectCountLbl; - @FXML - private Label objectCountValue; - - @FXML - private Button cancelBtn; - - @FXML - private Button loadBtn; - @FXML - private ImageView loadImageView; - - @FXML - private Button decompressBtn; - @FXML - private ImageView decompressImageView; - - @FXML - private Button decompressAllBtn; - @FXML - private ImageView decompressAllImageView; - - @FXML - private Button checkAllBtn; - @FXML - private ImageView checkAllImageView; - - @FXML - private Button showReportBtn; - @FXML - private ImageView showReportImageView; - - @FXML - private Button saveBtn; - @FXML - private ImageView saveImageView; - - @FXML - private ListView entryList; - @FXML - private ProgressBar progress; - @FXML - private ProgressBar subProgress; - - private CityDoctorController controller; - private int currentlyLoadedEntry = -1; - private CheckDialog dialog; - - private final String unknownValueText = Localization.getText("ZipEntryManager.unknownValue"); - - private static final Predicate entryDecompressed = node -> - (node.getEntry().isLoaded() || node.getEntry().getErrorType() != null); - private static final Predicate entryValidated = entryDecompressed.and(node -> - (node.getEntry().getModel() != null && node.getEntry().getModel().isValidated())); - - public ZipEntryManager(Window parent, CityDoctorController controller) throws IOException { - FXMLLoader loader = new FXMLLoader(ZipEntryManager.class.getResource("ZipEntryManager.fxml")); - loader.setController(this); - this.controller = controller; - archive = controller.getZipArchive(); - VBox box = loader.load(); - stage = new Stage(); - stage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("icons/CityDoctor-Logo-rot_klein.jpg"))); - stage.setScene(new Scene(box)); - stage.initOwner(parent); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setTitle(Localization.getText("ZipEntryManager.title")); - stage.getScene().addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent event) -> { - if (event.getCode() == KeyCode.ESCAPE) { - stage.close(); - } - }); - dialog = new CheckDialog(null, parent, controller); - dialog.showZipArchiveOptions(true); - } - - public void initialize() { - subProgress.setVisible(false); - entryList.getSelectionModel().selectedItemProperty().addListener((obs, oldI, newI) -> { - if (newI != null) { - showMetadata(newI.getEntry()); - } - }); - entryList.setCellFactory(param -> new ZipEntryListCell()); - setupButtons(); - applyLocalization(); - populateZipEntryList(); - } - - private void setupButtons() { - try { - try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/decompress_zipEntry.png")) { - Image img = new Image(inStream); - decompressImageView.setImage(img); - } - try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/view_model.png")) { - Image img = new Image(inStream); - loadImageView.setImage(img); - } - try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/decompress_all_zipEntries.png")) { - Image img = new Image(inStream); - decompressAllImageView.setImage(img); - } - try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/check_all_zipEntries.png")) { - Image img = new Image(inStream); - checkAllImageView.setImage(img); - } - try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/error_stat32x32.png")) { - Image img = new Image(inStream); - showReportImageView.setImage(img); - } - try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/save.png")) { - Image img = new Image(inStream); - saveImageView.setImage(img); - } - } catch (IOException ignored) { - //ignore exceptions - } - - decompressBtn.setOnAction(e -> { - disableTaskButtons(); - setEntryListLocked(true); - Thread t = new Thread(() -> { - CityGmlZipEntry entry = entryList.getSelectionModel().getSelectedItem().getEntry(); - logger.info("Decompressing entry \"{}\"", entry.getDisplayName()); - controller.decompressZipEntry(entry, progressValue -> - Platform.runLater(() -> progress.setProgress(progressValue)) - ); - Platform.runLater(() -> { - refresh(); - progress.setProgress(0); - setEntryListLocked(false); - }); - }); - t.setDaemon(true); - t.start(); - - }); - - loadBtn.setOnAction(e -> { - disableTaskButtons(); - setEntryListLocked(true); - try { - int selectedIndex = entryList.getSelectionModel().getSelectedIndex(); - if (selectedIndex == currentlyLoadedEntry) { - stage.hide(); - return; - } - CityGmlZipEntry entry = entryList.getSelectionModel().getSelectedItem().getEntry(); - if (entry.getErrorType() != null) { - logger.debug("IllegalState: loadBtn was pressed while an erroneous entry was selected"); - populateZipEntryList(selectedIndex); - return; - } - controller.loadZipEntry(entry); - currentlyLoadedEntry = selectedIndex; - stage.hide(); - } finally { - setEntryListLocked(false); - } - - }); - - decompressAllBtn.setOnAction(e -> { - disableTaskButtons(); - setEntryListLocked(true); - subProgress.setVisible(true); - subProgress.setProgress(0); - Thread t = new Thread(() -> { - progress.setProgress(0); - int count = 0; - int total = entryList.getItems().size(); - for (ZipEntryNode node : entryList.getItems()) { - logger.info("Decompressing entry \"{}\"", node.getEntry().getDisplayName()); - controller.decompressZipEntry(node.getEntry(), progressValue -> - Platform.runLater(() -> subProgress.setProgress(progressValue)) - ); - count++; - double progressValue = count * 1.0 / total; - Platform.runLater(() -> progress.setProgress(progressValue)); - } - Platform.runLater(() -> { - subProgress.setVisible(false); - refresh(); - progress.setProgress(0); - setEntryListLocked(false); - }); - }); - t.setDaemon(true); - t.start(); - }); - - checkAllBtn.setOnAction(e -> { - disableTaskButtons(); - dialog.show(); - refresh(); - }); - - showReportBtn.setOnAction(e -> { - disableTaskButtons(); - setEntryListLocked(true); - - try { - controller.loadZipEntry(entryList.getSelectionModel().getSelectedItem().getEntry()); - if (currentlyLoadedEntry == -1) { - currentlyLoadedEntry = entryList.getSelectionModel().getSelectedIndex(); - } - WriteReportDialog writeDialog = new WriteReportDialog(stage, controller, null); - writeDialog.show(); - } catch (IOException ex) { - throw new RuntimeException(ex); - } finally { - controller.loadZipEntry(entryList.getItems().get(currentlyLoadedEntry).getEntry()); - setEntryListLocked(false); - refresh(); - } - }); - showReportBtn.setDisable(true); - - saveBtn.setOnAction(e -> { - disableTaskButtons(); - setEntryListLocked(true); - try { - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle("Export Zip file"); - FileChooser.ExtensionFilter zipFilter = new FileChooser.ExtensionFilter("Zip File", "*.zip"); - fileChooser.getExtensionFilters().add(zipFilter); - File f = fileChooser.showSaveDialog(stage.getOwner()); - if (f != null) { - controller.saveZipArchiveReports(f); - } - } finally { - setEntryListLocked(false); - refresh(); - } - }); - saveBtn.setDisable(true); - - cancelBtn.setOnAction(e -> stage.close()); - } - - private void applyLocalization() { - entriesPane.setText(Localization.getText("ZipEntryManager.entryListTitle")); - metadataPane.setText(Localization.getText("ZipEntryManager.metadata")); - subpathLbl.setText(Localization.getText("ZipEntryManager.subpathLbl")); - erroneousLbl.setText(Localization.getText("ZipEntryManager.erroneousLbl")); - filesizeLbl.setText(Localization.getText("ZipEntryManager.filesizeLbl")); - validatedLbl.setText(Localization.getText("ZipEntryManager.validatedLbl")); - objectCountLbl.setText(Localization.getText("ZipEntryManager.objectCountLbl")); - - subpathValueTxt.setText(unknownValueText); - erroneousValue.setText(unknownValueText); - filesizeValue.setText(unknownValueText); - validatedValue.setText(unknownValueText); - objectCountValue.setText(unknownValueText); - - loadBtn.setTooltip(new Tooltip(Localization.getText("ZipEntryManager.loadBtn"))); - decompressBtn.setTooltip(new Tooltip(Localization.getText("ZipEntryManager.decompressBtn"))); - decompressAllBtn.setTooltip(new Tooltip(Localization.getText("ZipEntryManager.decompressAllBtn"))); - checkAllBtn.setTooltip(new Tooltip(Localization.getText("ZipEntryManager.checkAllBtn"))); - showReportBtn.setTooltip(new Tooltip(Localization.getText("ZipEntryManager.showReportBtn"))); - saveBtn.setTooltip(new Tooltip(Localization.getText("ZipEntryManager.saveBtn"))); - cancelBtn.setText(Localization.getText("ZipEntryManager.cancelBtn")); - } - - private void populateZipEntryList() { - populateZipEntryList(0); - } - - private void populateZipEntryList(int selectionIndex) { - entryList.getItems().clear(); - for (CityGmlZipEntry entry : archive.getEntries()) { - entryList.getItems().add(new ZipEntryNode(entry)); - } - entryList.getSelectionModel().clearSelection(); - entryList.getSelectionModel().select(selectionIndex); - showMetadata(entryList.getSelectionModel().getSelectedItem().getEntry()); - decompressAllBtn.setDisable(entryList.getItems().stream().allMatch(entryDecompressed)); - saveBtn.setDisable(entryList.getItems().stream().noneMatch(entryValidated)); - } - - private void showMetadata(CityGmlZipEntry entry) { - subpathValueTxt.setText(entry.getEntrySubPath()); - if (entry.getFileSize() != -1L) { - long fileSize = entry.getFileSize(); - long kb = 1024L; - long mb = kb * kb; - long gb = mb * kb; - - if (fileSize < kb) { - filesizeValue.setText("< 1 KB"); - } else if (fileSize < mb) { - filesizeValue.setText(String.format("%s KB", fileSize / kb)); - } else if (fileSize < gb) { - filesizeValue.setText(String.format("%s MB", fileSize / mb)); - } else { - double gigabytes = (double) fileSize / gb; - filesizeValue.setText(String.format("%.2f GB", gigabytes)); - } - } else { - filesizeValue.setText(unknownValueText); - } - if (entry.getErrorType() != null) { - erroneousValue.setText(getErrorText(entry.getErrorType())); - validatedValue.setText(unknownValueText); - objectCountValue.setText(unknownValueText); - loadBtn.setDisable(true); - decompressBtn.setDisable(true); - showReportBtn.setDisable(true); - } else { - erroneousValue.setText(Localization.getText("ZipEntryManager.no")); - - loadBtn.setDisable(!entry.isLoaded()); - decompressBtn.setDisable(entry.isLoaded()); - if (entry.getModel() == null) { - validatedValue.setText(unknownValueText); - objectCountValue.setText(unknownValueText); - return; - } - if (entry.getModel().isValidated()) { - validatedValue.setText(Localization.getText("ZipEntryManager.yes")); - showReportBtn.setDisable(false); - } else { - validatedValue.setText(Localization.getText("ZipEntryManager.no")); - showReportBtn.setDisable(true); - } - objectCountValue.setText(String.valueOf(entry.getModel().getNumberOfFeatures())); - } - - } - - private void disableTaskButtons() { - decompressBtn.setDisable(true); - loadBtn.setDisable(true); - decompressAllBtn.setDisable(true); - showReportBtn.setDisable(true); - saveBtn.setDisable(true); - } - - public void refresh() { - populateZipEntryList(entryList.getSelectionModel().getSelectedIndex()); - } - - private String getErrorText(ZipEntryErrorType error) { - switch (error) { - case EXCESSIVE_FILESIZE -> { - return Localization.getText("ZipEntryManager.excessiveFileSize"); - } - case INVALID_CITY_GML_FILE -> { - return Localization.getText("ZipEntryManager.invalidCityGml"); - } - default -> { - return Localization.getText("ZipEntryManager.ioError"); - } - } - } - - private void setEntryListLocked(boolean locked) { - entryList.setMouseTransparent(locked); - entryList.setFocusTraversable(!locked); - } - - public void show() { - Platform.runLater(() -> stage.showAndWait()); - refresh(); - } -} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ZipEntryListCell.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ZipEntryListCell.java deleted file mode 100644 index 74547188..00000000 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ZipEntryListCell.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.hft.stuttgart.citydoctor2.gui.tree; - -import de.hft.stuttgart.citydoctor2.gui.tree.node.ZipEntryNode; -import javafx.scene.control.ListCell; - -public class ZipEntryListCell extends ListCell { - - @Override - protected void updateItem(ZipEntryNode item, boolean empty) { - super.updateItem(item, empty); - if (!empty || item != null) { - setText(item.getText()); - updateColor(); - } else { - setText(null); - } - } - - public void updateColor() { - setTextFill(getItem().getTextColor()); - } -} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/node/ZipEntryNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/node/ZipEntryNode.java deleted file mode 100644 index f4ba0875..00000000 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/node/ZipEntryNode.java +++ /dev/null @@ -1,43 +0,0 @@ -package de.hft.stuttgart.citydoctor2.gui.tree.node; - -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; -import javafx.scene.paint.Color; - -public class ZipEntryNode { - - private final CityGmlZipEntry entry; - - public ZipEntryNode(CityGmlZipEntry entry) { - this.entry = entry; - } - - public Color getTextColor() { - if (entry.getErrorType() != null) { - return Color.DARKRED; - } else if (entry.getModel() != null) { - if (entry.getModel().getNumberOfFeatures() == 0) { - return Color.GRAY; - } - if (entry.getModel().isValidated()) { - if (entry.getModel().collectErrors().isEmpty()) { - return Color.GREEN; - } - return Color.RED; - } - return Color.BLUE; - } else if (entry.isLoaded()) { - return Color.BLUE; - } else { - return Color.BLACK; - } - } - - public String getText() { - return entry.getDisplayName(); - } - - public CityGmlZipEntry getEntry() { - return entry; - } - -} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CheckDialog.fxml b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CheckDialog.fxml index 5261641c..e0924a24 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CheckDialog.fxml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CheckDialog.fxml @@ -2,7 +2,6 @@ - @@ -52,78 +51,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml index 4cf90fff..17d20351 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml @@ -16,13 +16,7 @@ - - + - + @@ -86,7 +80,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- GitLab From 950fa708e586d33650cc23e936c30a158c92d8a3 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Wed, 5 Nov 2025 16:17:11 +0100 Subject: [PATCH 056/118] Test: Remove deleted import --- .../java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java index 623153e5..cbc489ab 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java @@ -24,7 +24,6 @@ import de.hft.stuttgart.citydoctor2.checkresult.GlobalErrorStatistics; import de.hft.stuttgart.citydoctor2.checkresult.GlobalStatistics; import de.hft.stuttgart.citydoctor2.checkresult.ModelStatistics; import de.hft.stuttgart.citydoctor2.checkresult.utility.CheckReportParseException; -import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; @@ -32,8 +31,6 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.Test; -- GitLab From d4d3201850b06575694b08156321aea9b85b4a01 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 6 Nov 2025 10:38:49 +0100 Subject: [PATCH 057/118] Build: Add IntelliJ run configurations --- .run/CityDoctorGUIStarter.run.xml | 15 +++++++++++++ .run/Maven Verify.run.xml | 33 +++++++++++++++++++++++++++++ .run/Run all tests.run.xml | 17 +++++++++++++++ .run/Test Docker deployment.run.xml | 25 ++++++++++++++++++++++ 4 files changed, 90 insertions(+) create mode 100644 .run/CityDoctorGUIStarter.run.xml create mode 100644 .run/Maven Verify.run.xml create mode 100644 .run/Run all tests.run.xml create mode 100644 .run/Test Docker deployment.run.xml diff --git a/.run/CityDoctorGUIStarter.run.xml b/.run/CityDoctorGUIStarter.run.xml new file mode 100644 index 00000000..16dc321b --- /dev/null +++ b/.run/CityDoctorGUIStarter.run.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.run/Maven Verify.run.xml b/.run/Maven Verify.run.xml new file mode 100644 index 00000000..c2ca0a34 --- /dev/null +++ b/.run/Maven Verify.run.xml @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/.run/Run all tests.run.xml b/.run/Run all tests.run.xml new file mode 100644 index 00000000..fa53e124 --- /dev/null +++ b/.run/Run all tests.run.xml @@ -0,0 +1,17 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.run/Test Docker deployment.run.xml b/.run/Test Docker deployment.run.xml new file mode 100644 index 00000000..caa12557 --- /dev/null +++ b/.run/Test Docker deployment.run.xml @@ -0,0 +1,25 @@ + + + + + + + + + + \ No newline at end of file -- GitLab From 6fce842e1836581024bb007be9d92af8de005c58 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 7 Nov 2025 09:34:31 +0100 Subject: [PATCH 058/118] Refactor: Remove obsolete code --- .../citydoctor2/parser/CityGmlParser.java | 17 --- .../stuttgart/citydoctor2/zip/ZipTest.java | 106 ------------------ 2 files changed, 123 deletions(-) delete mode 100644 CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 963ac642..252d7a8c 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -487,23 +487,6 @@ public class CityGmlParser { } } } -// cgmlModel = mapper.getModel().getCityModel(); -// -// // remove those that should have been parsed -// if (logger.isInfoEnabled() && verbose){ -// logger.info("Resolving HRefs"); -// } -//// List parsedCityObjects = mapper.getModel().createFeatureStream().filter(Objects::nonNull) -//// .map(CityObject::getGmlObject).toList(); -//// mapper.getModel().createFeatureStream().filter(Objects::nonNull) -//// .map(CityObject::getGmlObject).forEach(acos::remove); -// mapper.getModel().getFeatureIds().forEach(id -> {acos.removeIf(aco -> aco.getId().equals(id.getGmlString()));}); -//// acos.removeAll(parsedCityObjects); -// // re-add all not parsed objects -// for (AbstractCityObject aco : acos) { -// cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); -// } - // After parsing is complete, try to resolve deferred RelativeGeometries ImplicitGeometryHolder.resolveDeferredObjects(mapper.getModel().getCache()); if (logger.isInfoEnabled() && verbose) { diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java deleted file mode 100644 index 42afda9c..00000000 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package de.hft.stuttgart.citydoctor2.zip; - -import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; -import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; -import org.apache.commons.io.FileUtils; -import org.junit.Test; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -public class ZipTest { - - ParserConfiguration config = new ParserConfiguration(8, false); - - - @Test - public void testUnzipping() throws MalformedZipFileException { - CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/mock_archive.zip"); - assertNotNull(cgmlArch); - cgmlArch.mountArchive(config); - - checkMockArchive(cgmlArch); - } - - private void checkMockArchive(CityGmlZipArchive cgmlArch) { - assertEquals(5, cgmlArch.getEntries().size()); - for (CityGmlZipEntry entry : cgmlArch.getEntries()) { - assertNotNull(entry); - assertTrue(entry.getEntrySubPath().matches("^mock[1-5].gml$")); - assertNull(entry.getErrorType()); - assertNotNull(entry.getModel()); - assertEquals(1, entry.getModel().getNumberOfFeatures()); - } - } - - @Test - public void testZipping() throws IOException { - CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/mock_archive.zip"); - assertNotNull(cgmlArch); - cgmlArch.mountArchive(config); - - Path tmpDir = null; - try { - tmpDir = Files.createTempDirectory("testTmp"); - tmpDir.toFile().deleteOnExit(); - String expPath = tmpDir + "/export.zip"; - - cgmlArch.exportToZipFile(expPath); - CityGmlZipArchive cgmlExport = CityGmlZipArchive.register(expPath); - assertNotNull(cgmlExport); - cgmlExport.mountArchive(config); - - checkMockArchive(cgmlExport); - } finally { - if (tmpDir != null) { - FileUtils.deleteDirectory(tmpDir.toFile()); - } - } - } - - @Test - public void testEpsgParsing() throws MalformedZipFileException { - CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/epsg.zip"); - assertNotNull(cgmlArch); - cgmlArch.mountArchive(config); - - CityGmlZipEntry entry = cgmlArch.getEntry("epsg1.gml"); - assertNotNull(entry); - String srsName = entry.getModel().getCityModel().getBoundedBy().getEnvelope().getSrsName().split(":")[6]; - assertEquals("25832", srsName); - - entry = cgmlArch.getEntry("epsg2.gml"); - assertNotNull(entry); - srsName = entry.getModel().getCityModel().getBoundedBy().getEnvelope().getSrsName().split(":")[6]; - assertEquals("7415", srsName); - } - - @Test - public void testXMLValidation() throws MalformedZipFileException { - ParserConfiguration valConfig = new ParserConfiguration(8, true); - CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/validate.zip"); - assertNotNull(cgmlArch); - cgmlArch.mountArchive(valConfig); - - assertNull(cgmlArch.getEntry("valCorrect.gml").getErrorType()); - assertEquals(ZipEntryErrorType.INVALID_CITY_GML_FILE, cgmlArch.getEntry("valFaulty.gml").getErrorType()); - } - - - @Test - public void testImplicitParsing() throws MalformedZipFileException { - CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/implicit.zip"); - assertNotNull(cgmlArch); - cgmlArch.mountArchive(config); - - CityDoctorModel mainModel = cgmlArch.getEntry("Main_model.gml").getModel(); - assertEquals(18, mainModel.getGenericCityObjects().toList().size()); - } - -} -- GitLab From 0a833640a1027d0a4eec92d25d961ce7c02cc009 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 7 Nov 2025 09:36:42 +0100 Subject: [PATCH 059/118] Refactor: Add querying for Features with errors --- .../citydoctor2/database/CityObjectCache.java | 2 ++ .../citydoctor2/database/DatabaseHandler.java | 36 ++++++++++++++++--- .../citydoctor2/database/FeatureCache.java | 17 ++++----- .../database/UnconnectedCache.java | 12 +++++++ 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java index 1b4a0344..178001e0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -21,4 +21,6 @@ public interface CityObjectCache { void resetCache(); + List getErrorFeatureIds(); + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 381ff125..60a2bbf5 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -68,7 +68,7 @@ public class DatabaseHandler { } } try (PreparedStatement createPs = con.prepareStatement("CREATE TABLE features (gmlid VARCHAR(255)" + - " PRIMARY KEY, bbox GEOMETRY, data BLOB)")) { + " PRIMARY KEY, bbox GEOMETRY, data BLOB, errors BOOLEAN);")) { createPs.executeUpdate(); if (logger.isDebugEnabled()) { logger.debug("Created features table"); @@ -88,10 +88,16 @@ public class DatabaseHandler { */ public void marshallCityObject(CityObject co) { try (Connection con = dataSource.getConnection()) { - try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?)")) { + try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?, ?)")) { ps.setString(1, co.getGmlId().toString()); String bbox = BoundingBox.of(co).to2DWkt(); ps.setString(2, bbox); + if (co.isValidated()) { + ps.setBoolean(4, co.hasAnyError()); + } + else { + ps.setNull(4, java.sql.Types.BOOLEAN); + } if (logger.isDebugEnabled()) { logger.debug("Attempting to serialize CityObject: {} ", co); } @@ -116,7 +122,7 @@ public class DatabaseHandler { // Id String for error logging String id = ""; try (Connection con = dataSource.getConnection()) { - try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?)")) { + try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?,?)")) { con.setAutoCommit(false); for (Map.Entry entry : map.entrySet()) { CityObject co = entry.getValue(); @@ -127,6 +133,12 @@ public class DatabaseHandler { if (logger.isTraceEnabled()) { logger.trace("Attempting to serialize CityObject: {} ", co); } + if (co.isValidated()) { + ps.setBoolean(4, co.hasAnyError()); + } + else { + ps.setNull(4, java.sql.Types.BOOLEAN); + } try { byte[] bytes = SerializationUtils.serialize(co); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); @@ -161,7 +173,7 @@ public class DatabaseHandler { public CityObject unmarshallCityObject(GmlId id) { try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement( - "SELECT gmlid, bbox, data FROM features WHERE gmlid = ?")) { + "SELECT data FROM features WHERE gmlid = ?")) { ps.setString(1, id.toString()); ResultSet rs = ps.executeQuery(); if (rs.next()) { @@ -188,6 +200,22 @@ public class DatabaseHandler { return null; } + public List getIdsOfAllCityObjectsWithErrors(){ + List ids = new ArrayList<>(); + try (Connection con = dataSource.getConnection()){ + try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features WHERE errors = TRUE")) { + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + ids.add(new GmlId(rs.getString("gmlid"))); + } + } + } catch (SQLException e) { + logger.error("Fetching ids of all features with errors failed!"); + logger.error(e.getMessage()); + } + return ids; + } + public List getAllCityObjects() { List cos = new ArrayList<>(); try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index f99dfd27..3d0c0fa2 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -25,14 +25,12 @@ public class FeatureCache implements CityObjectCache{ private static final Logger logger = LogManager.getLogger(FeatureCache.class); private final LoadingCache cache; - private DatabaseHandler handler; - private final Map handlerList = new HashMap<>(); + private final DatabaseHandler handler; private AtomicInteger putsCounter = new AtomicInteger(0); private boolean parsingFinished = false; FeatureCache() { handler = new DatabaseHandler(); - handler.setupFeaturesTable(); cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> { if (value != null && cause.wasEvicted()) { handler.marshallCityObject(value); @@ -84,14 +82,6 @@ public class FeatureCache implements CityObjectCache{ - public void changeFileName(String fileName){ - handler = handlerList.computeIfAbsent(fileName, k -> { - DatabaseHandler handler1 = new DatabaseHandler(); - handler1.setupFeaturesTable(); - return handler1; - }); - } - public void preCacheFeatures(List ids) { cache.getAll(ids); } @@ -120,6 +110,11 @@ public class FeatureCache implements CityObjectCache{ parsingFinished = false; } + @Override + public List getErrorFeatureIds() { + return handler.getIdsOfAllCityObjectsWithErrors(); + } + public void finishParsing() { if (!parsingFinished) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java index a668099d..b0b54568 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java @@ -4,6 +4,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,4 +49,15 @@ public class UnconnectedCache implements CityObjectCache{ public void resetCache(){ // } + + @Override + public List getErrorFeatureIds() { + List errorFeatureIds = new ArrayList<>(); + cache.forEach((gmlId, cityObject) ->{ + if (cityObject.containsAnyError()){ + errorFeatureIds.add(cityObject.getGmlId()); + } + }); + return errorFeatureIds; + } } -- GitLab From 3da98b3e30e317e2995eecea3f491667016c526d Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 7 Nov 2025 09:37:30 +0100 Subject: [PATCH 060/118] Test: Adjust testcase to reflect current Implementation --- .../database/DataBaseHandlerTest.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index 8381e96c..4ab84ffd 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -44,12 +44,6 @@ public class DataBaseHandlerTest { } } - @Test - public void testSetup() throws SQLException { - try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { - assertNotNull(con); - } - } @Test public void testGetAllFeatures() { @@ -235,8 +229,6 @@ public class DataBaseHandlerTest { @Test public void testMarshallingRoundTripSimpleSolid() throws InvalidGmlFileException, CityGmlParseException { - try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { - assertNotNull(con); ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/SimpleSolid_SrefBS.gml", config); @@ -251,16 +243,11 @@ public class DataBaseHandlerTest { assertCityObjectEquality(b, b2); - } catch (SQLException e) { - throw new RuntimeException(e); - } } @Test public void testMarshallingRoundTripFzk() throws InvalidGmlFileException, CityGmlParseException { - try (Connection con = H2GISDBFactory.openSpatialDataBase("cd_db")) { - assertNotNull(con); ParserConfiguration config = new ParserConfiguration(8, false); CityDoctorModel model = CityGmlParser.parseCityGmlFile("src/test/resources/FZK_haus.gml", config); @@ -276,9 +263,6 @@ public class DataBaseHandlerTest { assertCityObjectEquality(b, b2); - } catch (SQLException e) { - throw new RuntimeException(e); - } } } -- GitLab From 58f0a808898648d98fb20e18040c28b56871400b Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 7 Nov 2025 09:38:12 +0100 Subject: [PATCH 061/118] Refactor: Add cleanup hook --- .../de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 60a2bbf5..580c7edb 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -36,7 +36,6 @@ public class DatabaseHandler { public DatabaseHandler(){ try { -// Cleanup hook Runtime.getRuntime().addShutdownHook(new Thread(() -> {})); String jdbcurl = H2GISDBFactory.createDataSource(DB_NAME, true, ";AUTO_SERVER=TRUE").getConnection().getMetaData().getURL(); HikariDataSource ds = new HikariDataSource(); @@ -50,6 +49,8 @@ public class DatabaseHandler { dataSource = new DataSourceWrapper(ds); + //Cleanup hook + Runtime.getRuntime().addShutdownHook(new Thread(ds::close)); } catch (Exception e) { logger.fatal("FATAL ERROR: Creation of embedded database failed!"); logger.fatal(e.getMessage()); -- GitLab From 76cdabcbeebb0b476b66a7f5db2e26bbda49819a Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 7 Nov 2025 15:57:12 +0100 Subject: [PATCH 062/118] Refactor: Make Schematron run in background thread --- .../stuttgart/citydoctor2/check/Checker.java | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 0f2ad0ae..5eecc3d5 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -191,13 +191,26 @@ public class Checker { if (config == null) { config = ValidationConfiguration.loadStandardValidationConfig(); } + Thread schematronThread = new Thread(() -> { + SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile()); + if (handler != null) { + handleSchematronResults(handler); + } + }); + schematronThread.start(); checkCityModel(model, l); if (logger.isInfoEnabled()) { logger.info(Localization.getText("Checker.checksFinished")); } - SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile()); - if (handler != null) { - handleSchematronResults(handler); + if (schematronThread.isAlive()) { + //TODO: Localize String + logger.info("Schematron thread still running, waiting for completion. This may take some time"); + try{ + schematronThread.join(); + } catch (InterruptedException e) { + logger.error("Main Thread was interrupted while waiting for Schematron", e); + schematronThread.interrupt(); + } } model.setValidated(createValidationPlan()); } @@ -443,6 +456,7 @@ public class Checker { logger.info(Localization.getText("Checker.schematronValidation")); } if (!Files.exists(Path.of(config.getSchematronFilePath()))) { + //TODO: Localize String logger.warn("Schematron file {} could not be found", config.getSchematronFilePath()); return null; } @@ -563,6 +577,7 @@ public class Checker { for (Entry e : config.getRequirements().entrySet()) { de.hft.stuttgart.citydoctor2.check.Requirement req = Checks.getAvailableRequirements().get(e.getKey()); if (req == null) { + //TODO: Localize String logger.warn("Could not find any check that satisfies requirement {}, it will not be checked", e.getKey()); } else { @@ -638,7 +653,6 @@ public class Checker { AtomicInteger missingCount = new AtomicInteger(0); // clear global errors model.getGlobalErrors().clear(); - CheckerThreadsSynchronizer sync = new CheckerThreadsSynchronizer(model, l); int threadCount = Runtime.getRuntime().availableProcessors() * 5; ExecutorService exec = new ThreadPoolExecutor @@ -669,9 +683,13 @@ public class Checker { try { exec.awaitTermination(30, TimeUnit.DAYS); long endTime = System.nanoTime(); - logger.info("Checks finished in " + (endTime -startTime) / 1_000_000 + " ms"); + if (logger.isDebugEnabled()){ + logger.debug("Checks finished in {} ms", (endTime - startTime) / 1_000_000); + } + if (missingCount.get() > 0) { int missing = missingCount.get(); + //TODO: Localize String String msg = String.format("Checker could not load %d/%.0f features (%.2f%%)", missing, featureSum, (missing/featureSum) * 100); logger.warn(msg); } -- GitLab From 6607295e09475533b10e037f8d14610f57373f16 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 10 Nov 2025 15:47:28 +0100 Subject: [PATCH 063/118] Refactor: Use DB for error filtering in NodeTrees --- .run/CityDoctorGUIStarter.run.xml | 1 + .../citydoctor2/database/CacheManager.java | 6 ++ .../citydoctor2/database/DatabaseHandler.java | 43 +++++++------ .../citydoctor2/database/FeatureCache.java | 25 +++++--- .../citydoctor2/parser/CityGmlParser.java | 1 + .../CityDoctorLocalization.properties | 2 +- .../stuttgart/citydoctor2/check/Checker.java | 2 + .../citydoctor2/gui/CityDoctorController.java | 64 +++++++++++++------ 8 files changed, 97 insertions(+), 47 deletions(-) diff --git a/.run/CityDoctorGUIStarter.run.xml b/.run/CityDoctorGUIStarter.run.xml index 16dc321b..b803b46b 100644 --- a/.run/CityDoctorGUIStarter.run.xml +++ b/.run/CityDoctorGUIStarter.run.xml @@ -2,6 +2,7 @@

+ * Requires Validation to be run first, as unvalidated CityObjects are of unknown state. + * @return list of GmlIDs which were found to contain an error + */ List getErrorFeatureIds(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 68f9d2f0..8aecb706 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -28,7 +28,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; - +/** + * Utility class for setup and access of the embedded database. + */ public class DatabaseHandler { private static final Logger logger = LogManager.getLogger(DatabaseHandler.class); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index 30273216..c9809b3d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -16,7 +16,16 @@ import java.util.stream.Stream; /** - * Cache for CityObjects + * A managed cache for CityObjects. This cache is connected to the embedded database and will automatically load missing + * CityObjects from it. Has a hard limit on concurrently held CityObjects and will write them back to the database upon + * eviction. + *

+ * This cache may only have a single instance, use {@link CacheManager} for instantiation. + *

+ *

+ * Avoid holding long-time strong references to CityObjects returned by the cache, as eviction will sever the + * connection between reference and cache, which can lead to conflicting Object states. + *

*/ public class FeatureCache implements CityObjectCache{ @@ -115,7 +124,7 @@ public class FeatureCache implements CityObjectCache{ } /** - * Synchronizes cache and database + * Synchronizes CityObject states by updating Database with cache content. */ public void synchronize(){ HashMap cacheContent= new HashMap<>(cache.asMap()); @@ -129,6 +138,9 @@ public class FeatureCache implements CityObjectCache{ } } + /** + * Signals the cache that parsing has finished. + */ public void finishParsing() { if (!parsingFinished) { synchronize(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java index b0b54568..9fddf857 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java @@ -2,7 +2,6 @@ package de.hft.stuttgart.citydoctor2.database; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; -import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import java.util.ArrayList; import java.util.HashMap; -- GitLab From c862f733cf6a411659d2d1caf2734a4e89a39583 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 13:50:38 +0100 Subject: [PATCH 069/118] Style: Remove comments --- .../de/hft/stuttgart/citydoctor2/database/FeatureCache.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index c9809b3d..f7c92392 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -71,8 +71,7 @@ public class FeatureCache implements CityObjectCache{ logger.trace("Entered batch Marshaller"); boolean success = handler.tryMarshallCityObjectMap(cacheContent); if (!success) { - logger.trace("Batch marshalling failed, falling back to one-by-one marshalling"); - // Batch marshalling failed, marshall them one-by-one + logger.trace("Batch marshalling in Thread failed, falling back to one-by-one marshalling"); for (Map.Entry entry : cacheContent.entrySet()) { handler.marshallCityObject(entry.getValue()); } @@ -131,7 +130,6 @@ public class FeatureCache implements CityObjectCache{ boolean success = handler.tryMarshallCityObjectMap(cacheContent); if (!success) { logger.trace("Batch marshalling failed, falling back to one-by-one marshalling"); - // Batch marshalling failed, marshall them one-by-one for (Map.Entry entry : cacheContent.entrySet()) { handler.marshallCityObject(entry.getValue()); } -- GitLab From 944893205e06c5e6a31d2a284862c56227d4ac7f Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 13:51:20 +0100 Subject: [PATCH 070/118] Refactor: Use Hikari connection pool in all queries --- .../citydoctor2/database/DatabaseHandler.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 8aecb706..ad527e3d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -41,8 +41,8 @@ public class DatabaseHandler { try { // String jdbcurl = H2GISDBFactory.createDataSource(DB_NAME, // true, ";AUTO_SERVER=TRUE").getConnection().getMetaData().getURL(); - String jdbcurl = H2GISDBFactory.createSpatialDataBase(DB_NAME, true, ";AUTO_SERVER=TRUE").getMetaData().getURL(); - logger.info(jdbcurl); + + logger.info("2] {}",jdbcurl); HikariDataSource ds = new HikariDataSource(); ds.setJdbcUrl(jdbcurl); @@ -216,7 +216,7 @@ public class DatabaseHandler { public List getAllCityObjects() { List cos = new ArrayList<>(); - try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data FROM features")) { ResultSet rs = ps.executeQuery(); cos.addAll(retrieveCityObjects(rs)); @@ -235,7 +235,7 @@ public class DatabaseHandler { public List getBufferIntersectingObjects(GmlId gmlID, double bufferSize) { List intersectingObjects = new ArrayList<>(); - try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + " WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM features WHERE gmlid = ?)) " + "AND tab1.gmlid <> ?")) { @@ -261,7 +261,7 @@ public class DatabaseHandler { public List getBBoxIntersectingObjects(GmlId id) { List intersectingObjects = new ArrayList<>(); - try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + " WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { @@ -283,7 +283,7 @@ public class DatabaseHandler { public int getFeatureCount() { int count = -1; - try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) { + try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(gmlid) AS nr FROM features")) { ResultSet rs = ps.executeQuery(); if (rs.next()) { -- GitLab From 0b9816105be3de75281801841f1247d350b671ef Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 14:37:57 +0100 Subject: [PATCH 071/118] Style: Sort Localization lines --- .../CityDoctorLocalization.properties | 250 +++++++++--------- .../CityDoctorLocalization_de.properties | 248 ++++++++--------- 2 files changed, 249 insertions(+), 249 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 6752f00e..aaf522cf 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -1,164 +1,164 @@ -CityDoctorValidation.Version=${versionString} -DistanceError.distanceFromPlane=distance from plane -AboutDialog.developedBy=Developed by +AboutDialog.closeBtn=Close AboutDialog.contact=Contact +AboutDialog.developedBy=Developed by AboutDialog.title=CityDoctor 3 - About -AboutDialog.closeBtn=Close -CheckDialog.title=Check Configuration -CheckDialog.schematronFiles=Schematron Files -CheckDialog.schematronChooserTitle=Choose schematron file.. -CheckDialog.validationConfiguration=Validation Configuration -CheckDialog.startingChecks=Starting checks -CheckDialog.checksDone=Checks done -CheckDialog.failedChecks=Failed to execute checks -CheckDialog.schematronFileNotExisting=Schematron file does not exist, ignoring +CheckContainer.error=Unexpected exception while executing check: {} +CheckDialog.availableChecksLabel=Available Checks +CheckDialog.cancelBtn=Cancel +CheckDialog.checkBtn=Check CheckDialog.checkEnabled=Enabled CheckDialog.checkName=Name -CheckDialog.parameterValue=Value +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.checksTab=Checks +CheckDialog.failedChecks=Failed to execute checks +CheckDialog.filterTab=Filter +CheckDialog.geometricChecksLabel=Geometric Checks +CheckDialog.globalParametersLabel=Global Parameters +CheckDialog.noPathError=Output path is missing! +CheckDialog.outputPathBtn=Select +CheckDialog.outputPathLabel=Output directory (Required): CheckDialog.parameterUnit=Unit +CheckDialog.parameterValue=Value +CheckDialog.pdfReportsOption=Create PDF reports +CheckDialog.schematronChooserTitle=Choose schematron file.. +CheckDialog.schematronFileLabel=Schematron File: +CheckDialog.schematronFileNotExisting=Schematron file does not exist, ignoring +CheckDialog.schematronFiles=Schematron Files +CheckDialog.selectBtn=Select +CheckDialog.semanticChecksLabel=Semantic 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.streamCheckOption=Use low memory mode +CheckDialog.title=Check Configuration +CheckDialog.validationConfiguration=Validation Configuration +CheckDialog.xmlReportsOption=Create XML reports +Checker.checkFeature=Checking feature: {} +Checker.checksFinished=Finished executing checks +Checker.executeCheck=Executing check: {} +Checker.failPdf=Failed to write PDF Report. +Checker.failReports=Failed to write reports. +Checker.failXml=Failed to write XML Error Report. +Checker.finishedSchematron=Finished Schematron validation +Checker.schematronValidation=Schematron-validation thread started +Checks.missingCheck=Could not find check for id: {} +CityDoctorController.exportSuccess=Successfully exported feature CityDoctorController.noDatamodel=Datamodel is null, no checks could be done CityDoctorController.noSourceFile=Source file is null, no checks could be done CityDoctorController.noZipFile=ZIP archive is null, no checks could be done -CityDoctorController.exportSuccess=Successfully exported feature CityDoctorController.saveZipArchiveReports=Successfully exported validation reports! +CityDoctorValidation.Version=${versionString} +CityGmlParser.chunkReadFailed=Failed to read GML file in chunks, falling back to reading the complete file +CityGmlParser.errorReadingGmlFile=Error while reading city gml file\n{} +CityGmlParser.errorWritingGmlFile=Error while writing city gml file\n{} +CityGmlParser.failedEPSGParse=Could not read EPSG code, fallback to metric system +CityGmlParser.missingEPSGCode=No EPSG code found, assuming metric system +CityGmlParser.noConversionNeeded=Coordinate system is in meters, no conversion done +CityGmlParser.notValidGmlFile=This is not a valid GML-File\n +CityGmlParser.parsedObjects=Parsed model with {} objects +CityGmlZipArchive.generalException=Encountered an unexpected error while reading the ZIP archive +CityGmlZipArchive.ioError=Accessing the ZIP archive failed +CityGmlZipArchive.malformedZipFile=Parsing of ZIP archive aborted, ZIP file is malformed! +DatabaseHandler.batchCommitFailure=Batch-commit aborted: Critical failure while trying to write CityObject {} to database! +DatabaseHandler.connectionFailure=Connection to embedded database could not be opened! +DatabaseHandler.fetchErrorIdsFailure=Fetching list of GmlIDs of CityObjects with errors failed! +DatabaseHandler.getAllCityObjectsFailure=Fetching of all CityObjects failed! +DatabaseHandler.getBBoxIntersectFailure= Connection to database failed while trying to fetch bbox intersecting objects of CityObject {} +DatabaseHandler.getBufferIntersectFailure=Connection to database failed while trying to fetch buffer intersecting objects of CityObject {} +DatabaseHandler.getFeatureCountFailure=Connection to database failed while trying to get count of CityObjects! +DatabaseHandler.marshallingFailure=Writing CityObject {} to database failed! +DatabaseHandler.serializationFailure=Serialization of CityObject {} failed! +DatabaseHandler.setupFailure=FATAL ERROR: Creation of embedded database failed! +DatabaseHandler.tableFailure=FATAL ERROR: Creation of features table failed! +DatabaseHandler.unmarshallingFailure=Loading of CityObject {} from embedded database failed! +DistanceError.distanceFromPlane=distance from plane +ErrorItemVisitor.childId=Child id +ErrorItemVisitor.component=Component +ErrorItemVisitor.degeneratedRing=Degenerated ring +ErrorItemVisitor.deviation=Deviation +ErrorItemVisitor.distance=Distance +ErrorItemVisitor.intersection=Intersection +ErrorItemVisitor.nameOfAttribute=Name of attribute +ErrorItemVisitor.pointTouchesEdge=Point touches edge +ErrorItemVisitor.triangles=Triangles ExceptionDialog.stacktrace=The exception stacktrace was: -FilterPane.buildings=Buildings +FeatureMapper.polygonUnreferenced=Polygon {} is referenced but not found in feature polygons FilterPane.bridges=Bridges +FilterPane.buildings=Buildings FilterPane.landUse=Land Use FilterPane.transportation=Transportation FilterPane.vegetation=Vegetation FilterPane.water=Water -MainToolBar.wireframe=Show/Hide Wireframe +GeometryMapper.emptyPolygon=Found polygon without exterior ring, ignoring MainToolBar.culling=Enable/Disable Culling -MainToolBar.writeReports=Write Reports MainToolBar.executeChecks=Execute Checks -MainToolBar.showWorld=Show entire city model -MainToolBar.resetCamera=Reset view MainToolBar.hideRoof=Show/Hide roof BoundarySurfaces +MainToolBar.resetCamera=Reset view +MainToolBar.showWorld=Show entire city model +MainToolBar.wireframe=Show/Hide Wireframe +MainToolBar.writeReports=Write Reports MainToolBar.zip=Open zip-entry selection window -MainWindow.missingConfig=Could not find configuration file. -MainWindow.loadGivenFile=Loading given file, please wait -MainWindow.finishedLoading=Finished loading -MainWindow.checking=Checking, please wait -MainWindow.writeXml=Writing xml report -MainWindow.finishedXml=Finished xml report -MainWindow.writePdf=Writing pdf report -MainWindow.finishedPdf=Finished pdf report -MainWindow.loadFailed=Failed to load given gml file: {} MainWindow.all=All -MainWindow.withErrors=With errors -MainWindow.export=Export feature +MainWindow.attributeTab=Attributes +MainWindow.availableLabel=Available: +MainWindow.bridgeTab=Bridges +MainWindow.buildingsTab=Buildings +MainWindow.checking=Checking, please wait +MainWindow.cityfurnitureTab=CityFurniture +MainWindow.clearBtn=Clear MainWindow.copyId=Copy GML-ID MainWindow.delete=Delete feature -MainWindow.infoTopLevelExport=Only top-level object can be exported! +MainWindow.edgesTab=Edges +MainWindow.errorsTab=Errors +MainWindow.export=Export feature +MainWindow.finishedLoading=Finished loading +MainWindow.finishedPdf=Finished pdf report +MainWindow.finishedXml=Finished xml report +MainWindow.focusCityObject=Focus object +MainWindow.globalErrorsTab=Global Errors MainWindow.infoTopLevelCopyID=GML-ID can only be copied from top-level object! -mainWindow.warnCopyIdFailed=Could not copy GML-ID! -OpenFileDialog.select=Select CityGML file +MainWindow.infoTopLevelExport=Only top-level object can be exported! MainWindow.languageChange=For the change in language to apply, restart CityDoctor2. -MainWindow.buildingsTab=Buildings -MainWindow.vegetationTab=Vegetation -MainWindow.transportationTab=Transportation -MainWindow.tunnelTab=Tunnels -MainWindow.bridgeTab=Bridges -MainWindow.waterTab=Water -MainWindow.terrainTab=Terrain -MainWindow.cityfurnitureTab=CityFurniture +MainWindow.loadFailed=Failed to load given gml file: {} +MainWindow.loadGivenFile=Loading given file, please wait +MainWindow.logTab=Log +MainWindow.memoryLabel=Memory: +MainWindow.missingConfig=Could not find configuration file. MainWindow.otherObjectsTab=Other -MainWindow.viewLabel=View -MainWindow.showLabel=Show: -MainWindow.searchLabel=Search: -MainWindow.searchBtn=Search -MainWindow.clearBtn=Clear -MainWindow.errorsTab=Errors MainWindow.polygonsTab=Polygons -MainWindow.edgesTab=Edges +MainWindow.searchBtn=Search +MainWindow.searchLabel=Search: +MainWindow.showLabel=Show: +MainWindow.terrainTab=Terrain +MainWindow.transportationTab=Transportation +MainWindow.tunnelTab=Tunnels +MainWindow.vegetationTab=Vegetation MainWindow.verticesTab=Vertices -MainWindow.attributeTab=Attributes -MainWindow.logTab=Log -MainWindow.globalErrorsTab=Global Errors -MainWindow.focusCityObject=Focus object +MainWindow.viewLabel=View +MainWindow.warnCopyIdFailed=Could not copy GML-ID! +MainWindow.waterTab=Water +MainWindow.withErrors=With errors +MainWindow.writePdf=Writing pdf report +MainWindow.writeXml=Writing xml report +OpenFileDialog.cancelBtn=Cancel OpenFileDialog.fileLabel=File: -OpenFileDialog.selectBtn=Select OpenFileDialog.loadBtn=Load -OpenFileDialog.settingsPane=Parser Preferences +OpenFileDialog.loadFailed=Failed to load CityGML File +OpenFileDialog.lowMemoryLabel=Low Memory Consumption Mode OpenFileDialog.roundingPlacesLabel=NumberOfRoundingPlaces +OpenFileDialog.select=Select CityGML file +OpenFileDialog.selectBtn=Select +OpenFileDialog.settingsPane=Parser Preferences OpenFileDialog.xmlValidationLabel=Use XML Validation -Checker.failXml=Failed to write XML Error Report. -Checker.failPdf=Failed to write PDF Report. -Checker.checksFinished=Finished executing checks -Checker.schematronValidation=Schematron-validation thread started -Checker.finishedSchematron=Finished Schematron validation -Checker.failReports=Failed to write reports. -Checker.checkFeature=Checking feature: {} -Checker.executeCheck=Executing check: {} -ValidationConfiguration.reenable={} has been disabled but {} depends on it, reenabling +Unit.Degree=Degree +Unit.Radian=Radian ValidationConfiguration.missingSchematron={} is not an existing file, disabling schematron check -CheckContainer.error=Unexpected exception while executing check: {} -Checks.missingCheck=Could not find check for id: {} -OpenFileDialog.cancelBtn=Cancel -CheckDialog.checksTab=Checks -CheckDialog.filterTab=Filter -CheckDialog.globalParametersLabel=Global Parameters -CheckDialog.availableChecksLabel=Available Checks -CheckDialog.geometricChecksLabel=Geometric Checks -CheckDialog.semanticChecksLabel=Semantic Checks -CheckDialog.schematronFileLabel=Schematron File: -CheckDialog.selectBtn=Select -CheckDialog.checkBtn=Check -CheckDialog.cancelBtn=Cancel -CheckDialog.streamCheckOption=Use low memory mode -CheckDialog.streamCheckLabel=Citymodels are not loaded and kept in memory. Check results are saved to the QualityADE in copies of the input files. -CheckDialog.outputPathLabel=Output directory (Required): -CheckDialog.outputPathBtn=Select -CheckDialog.pdfReportsOption=Create PDF reports -CheckDialog.xmlReportsOption=Create XML reports -CheckDialog.noPathError=Output path is missing! -CityGmlZipArchive.ioError=Accessing the ZIP archive failed -CityGmlZipArchive.malformedZipFile=Parsing of ZIP archive aborted, ZIP file is malformed! -CityGmlZipArchive.generalException=Encountered an unexpected error while reading the ZIP archive -WriteReportDialog.writeBtn=Save +ValidationConfiguration.reenable={} has been disabled but {} depends on it, reenabling WriteReportDialog.cancelBtn=Cancel WriteReportDialog.errorStatisticsLabel=Error Statistics WriteReportDialog.saveImageBtn=Save Image... WriteReportDialog.selectPdfBtn=Select WriteReportDialog.selectXmlFile=Select +WriteReportDialog.writeBtn=Save WriteReportDialog.xAxisLabel=Error WriteReportDialog.yAxisLabel=Count -Unit.Radian=Radian -Unit.Degree=Degree -FeatureMapper.polygonUnreferenced=Polygon {} is referenced but not found in feature polygons -GeometryMapper.emptyPolygon=Found polygon without exterior ring, ignoring -CityGmlParser.parsedObjects=Parsed model with {} objects -CityGmlParser.chunkReadFailed=Failed to read GML file in chunks, falling back to reading the complete file -CityGmlParser.notValidGmlFile=This is not a valid GML-File\n -CityGmlParser.errorReadingGmlFile=Error while reading city gml file\n{} -CityGmlParser.errorWritingGmlFile=Error while writing city gml file\n{} -CityGmlParser.noConversionNeeded=Coordinate system is in meters, no conversion done -CityGmlParser.failedEPSGParse=Could not read EPSG code, fallback to metric system -CityGmlParser.missingEPSGCode=No EPSG code found, assuming metric system -OpenFileDialog.loadFailed=Failed to load CityGML File -MainWindow.memoryLabel=Memory: -CheckDialog.checksReenabledAlert=Some checks have been reenabled so that other wanted checks can be executed\nSee the log for more information. -MainWindow.availableLabel=Available: -OpenFileDialog.lowMemoryLabel=Low Memory Consumption Mode -ErrorItemVisitor.intersection=Intersection -ErrorItemVisitor.component=Component -ErrorItemVisitor.nameOfAttribute=Name of attribute -ErrorItemVisitor.childId=Child id -ErrorItemVisitor.distance=Distance -ErrorItemVisitor.pointTouchesEdge=Point touches edge -ErrorItemVisitor.degeneratedRing=Degenerated ring -ErrorItemVisitor.deviation=Deviation -ErrorItemVisitor.triangles=Triangles -DatabaseHandler.setupFailure=FATAL ERROR: Creation of embedded database failed! -DatabaseHandler.tableFailure=FATAL ERROR: Creation of features table failed! -DatabaseHandler.marshallingFailure=Writing CityObject {} to database failed! -DatabaseHandler.serializationFailure=Serialization of CityObject {} failed! -DatabaseHandler.batchCommitFailure=Batch-commit aborted: Critical failure while trying to write CityObject {} to database! -DatabaseHandler.connectionFailure=Connection to embedded database could not be opened! -DatabaseHandler.unmarshallingFailure=Loading of CityObject {} from embedded database failed! -DatabaseHandler.fetchErrorIdsFailure=Fetching list of GmlIDs of CityObjects with errors failed! -DatabaseHandler.getAllCityObjectsFailure=Fetching of all CityObjects failed! -DatabaseHandler.getBufferIntersectFailure=Connection to database failed while trying to fetch buffer intersecting objects of CityObject {} -DatabaseHandler.getBBoxIntersectFailure= Connection to database failed while trying to fetch bbox intersecting objects of CityObject {} -DatabaseHandler.getFeatureCountFailure=Connection to database failed while trying to get count of CityObjects! \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 5c0ffc29..0e04c86b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -1,162 +1,162 @@ -DistanceError.distanceFromPlane=Abstand von der Ebene -AboutDialog.developedBy=Entwickelt von +AboutDialog.closeBtn=Schlie\u00DFen AboutDialog.contact=Kontakt +AboutDialog.developedBy=Entwickelt von AboutDialog.title=CityDoctor 3 - \u00DCber -AboutDialog.closeBtn=Schlie\u00DFen -CheckDialog.title=Pr\u00fcfungskonfiguration -CheckDialog.schematronFiles=Schematron Dateien -CheckDialog.schematronChooserTitle=W\u00e4hle Schematron Datei.. -CheckDialog.validationConfiguration=Validierungs Konfiguration -CheckDialog.startingChecks=Starte Pr\u00fcfung +CheckContainer.error=Unerwarteter Fehler bei der Ausf\u00fchrung von Pr\u00fcfung: {} +CheckDialog.availableChecksLabel=Verf\u00fcgbare Pr\u00fcfungen +CheckDialog.cancelBtn=Abbrechen +CheckDialog.checkBtn=Pr\u00fcfen +CheckDialog.checkEnabled=Aktiv 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.checksTab=Pr\u00fcfungen CheckDialog.failedChecks=Konnte Pr\u00fcfungen nicht ausf\u00fchren -CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, ignoriere -CheckDialog.checkEnabled=Aktiv -CheckDialog.parameterValue=Wert +CheckDialog.filterTab=Filter +CheckDialog.geometricChecksLabel=Geometrische Pr\u00fcfungen +CheckDialog.globalParametersLabel=Globale Parameter +CheckDialog.noPathError=Ausgabepfad wurde nicht gesetzt! +CheckDialog.outputPathBtn=Ausw\u00e4hlen +CheckDialog.outputPathLabel=Ausgabeordner (Pflichtfeld): CheckDialog.parameterUnit=Einheit +CheckDialog.parameterValue=Wert +CheckDialog.pdfReportsOption=PDF Berichte erstellen +CheckDialog.schematronChooserTitle=W\u00e4hle Schematron Datei.. +CheckDialog.schematronFileLabel=Schematron Datei +CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, ignoriere +CheckDialog.schematronFiles=Schematron Dateien +CheckDialog.selectBtn=Ausw\u00e4hlen +CheckDialog.semanticChecksLabel=Semantische Pr\u00fcfungen +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.streamCheckOption=Niedrigen Arbeitsspeicherverbrauchsmodus aktivieren +CheckDialog.title=Pr\u00fcfungskonfiguration +CheckDialog.validationConfiguration=Validierungs Konfiguration +CheckDialog.xmlReportsOption=XML Berichte erstellen +Checker.checkFeature=Pr\u00fcfe Feature: {} +Checker.checksFinished=Pr\u00fcfungen beendet +Checker.executeCheck=F\u00fchre Pr\u00fcfung aus: {} +Checker.failPdf=Konnte PDF Fehlerbericht nicht schreiben. +Checker.failReports=Konnte Pr\u00fcfberichte nicht schreiben. +Checker.failXml=Konnte XML Fehlerbericht nicht schreiben. +Checker.finishedSchematron=Schematron Pr\u00fcfung abgeschlossen +Checker.schematronValidation=Starte Schematron Pr\u00fcfung +Checks.missingCheck=Konnte keine Pr\u00fcfung f\u00fcr id {} finden +CityDoctorController.exportSuccess=Feature export erfolgreich abgeschlossen CityDoctorController.noDatamodel=Datenmodell ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden CityDoctorController.noSourceFile=Quelldatei ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden CityDoctorController.noZipFile=ZIP-Archiv ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden -CityDoctorController.exportSuccess=Feature export erfolgreich abgeschlossen CityDoctorController.saveZipArchiveReports=Validierungsberichte erfolgreich exportiert! +CityGmlParser.chunkReadFailed=Konnte Datei nicht in St\u00fccken lesen, versuche komplett zu lesen +CityGmlParser.errorReadingGmlFile=Fehler beim lesen der CityGML Datei\n{} +CityGmlParser.errorWritingGmlFile=Fehler beim schreiben der CityGML Datei\n{} +CityGmlParser.failedEPSGParse=Lesen des EPSG Codes fehlgeschlagen, greife auf metrisches System zur\u00fcck +CityGmlParser.missingEPSGCode=Kein EPSG Code gefunden, nehme metrisches System an +CityGmlParser.noConversionNeeded=Koordinatensystem in Metern, keine Konvertierung notwendig +CityGmlParser.notValidGmlFile=Dies ist keine korrekte CityGML Datei\n +CityGmlParser.parsedObjects=Modell mit {} Objekten gelesen +CityGmlZipArchive.generalException=Unerwarteter Fehler beim Einlesen des ZIP-Archivs +CityGmlZipArchive.ioError=Zugriff auf ZIP-Datei fehlgeschlagen +CityGmlZipArchive.malformedZipFile=Einlesen des ZIP-Archivs abgebrochen, ZIP-Datei ist fehlerhaft! +DatabaseHandler.batchCommitFailure=Batch-commit abgebrochen: Kritischer Fehler beim Versuch CityObject {} in die Datenbank zu schreiben! +DatabaseHandler.connectionFailure=Verbindung zur eingebetteten Datenbank konnte nicht hergestellt werden! +DatabaseHandler.fetchErrorIdsFailure=Abfragen der Liste von CityObjects mit Fehlern ist fehlgeschlagen! +DatabaseHandler.getAllCityObjectsFailure=Abfrage nach allen CityObjects ist fehlgeschlagen! +DatabaseHandler.getBBoxIntersectFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der BBox-Intersections von CityObject {} fehlgeschlagen! +DatabaseHandler.getBufferIntersectFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der Buffer-Intersections von CityObject {} fehlgeschlagen! +DatabaseHandler.getFeatureCountFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der Anzahl an CityObjects fehlgeschlagen! +DatabaseHandler.marshallingFailure=Schreiben von CityObject {} in die Datenbank fehlgeschlagen! +DatabaseHandler.serializationFailure=Serialisierung von CityObject {} fehlgeschlagen! +DatabaseHandler.setupFailure=FATAL ERROR: Erstellen der eingebetteten Datenbank fehlgeschlagen! +DatabaseHandler.tableFailure=FATAL ERROR: Erstellen der features Tabelle fehlgeschlagen! +DatabaseHandler.unmarshallingFailure=Laden von CityObject {} aus der Datenbank fehlgeschlagen! +DistanceError.distanceFromPlane=Abstand von der Ebene +ErrorItemVisitor.childId=Kind ID +ErrorItemVisitor.component=Komponente +ErrorItemVisitor.degeneratedRing=Degenerierter Ring +ErrorItemVisitor.deviation=Abweichung +ErrorItemVisitor.distance=Distanz +ErrorItemVisitor.intersection=Verschneidung +ErrorItemVisitor.nameOfAttribute=Name des Attributs +ErrorItemVisitor.pointTouchesEdge=Punkt ber\u00fchrt Kante +ErrorItemVisitor.triangles=Dreiecke ExceptionDialog.stacktrace=Der Stacktrace des Fehlers war: -FilterPane.buildings=Geb\u00e4ude +FeatureMapper.polygonUnreferenced=Polygon {} ist referenziert, konnte aber nicht gefunden werden FilterPane.bridges=Br\u00fccken +FilterPane.buildings=Geb\u00e4ude FilterPane.landUse=Landnutzung FilterPane.transportation=Transport FilterPane.vegetation=Vegetation FilterPane.water=Wasser -MainToolBar.wireframe=Zeige/Verstecke Gitternetz +GeometryMapper.emptyPolygon=Polygon ohne externen Ring gefunden, ignoriere MainToolBar.culling=Aktiviere/Deaktiviere Entfernen der R\u00fcckseiten -MainToolBar.writeReports=Schreibe Reports MainToolBar.executeChecks=F\u00fchre Pr\u00fcfungen aus -MainToolBar.showWorld=Gesamtes Stadtmodell anzeigen -MainToolBar.resetCamera=Ansicht zur\u00fccksetzen MainToolBar.hideRoof=Zeige/Verstecke Dach BoundarySurfaces +MainToolBar.resetCamera=Ansicht zur\u00fccksetzen +MainToolBar.showWorld=Gesamtes Stadtmodell anzeigen +MainToolBar.wireframe=Zeige/Verstecke Gitternetz +MainToolBar.writeReports=Schreibe Reports MainToolBar.zip=Zip-eintrag Auswahlfenster \u00f6ffnen -MainWindow.missingConfig=Konnte Konfigurationsdatei nicht finden. -MainWindow.loadGivenFile=Lade vorhandene Datei, bitte warten -MainWindow.finishedLoading=Fertig geladen -MainWindow.checking=Pr\u00fcfe, bitte warten -MainWindow.writeXml=Schreibe Xml Report -MainWindow.finishedXml=Xml Report abgeschlossen -MainWindow.writePdf=Schreibe Pdf Report -MainWindow.finishedPdf=Pdf Report abgeschlossen -MainWindow.loadFailed=Konnte GML-Datei nicht laden: {} MainWindow.all=Alle -MainWindow.withErrors=Mit Fehlern -MainWindow.export=Feature Exportieren +MainWindow.attributeTab=Attribute +MainWindow.availableLabel=Verf\u00fcgbar: +MainWindow.bridgeTab=Br\u00fccken +MainWindow.buildingsTab=Geb\u00e4ude +MainWindow.checking=Pr\u00fcfe, bitte warten +MainWindow.cityfurnitureTab=Stadtm\u00f6bel +MainWindow.clearBtn=Leeren MainWindow.copyId=GML-ID kopieren MainWindow.delete=Feature L\u00f6schen -MainWindow.infoTopLevelExport=Export ist nur f\u00fcr Featureobjekt basis m\u00f6glich! +MainWindow.edgesTab=Kanten +MainWindow.errorsTab=Fehler +MainWindow.export=Feature Exportieren +MainWindow.finishedLoading=Fertig geladen +MainWindow.finishedPdf=Pdf Report abgeschlossen +MainWindow.finishedXml=Xml Report abgeschlossen +MainWindow.focusCityObject=Objekt fokussieren +MainWindow.globalErrorsTab=Globale Fehler MainWindow.infoTopLevelCopyID=GML-ID kann nur von Featureobjekt basis kopiert werden! -mainWindow.warnCopyIdFailed=Kopieren der ID fehlgeschlagen! -OpenFileDialog.select=W\u00e4hle CityGML Datei aus +MainWindow.infoTopLevelExport=Export ist nur f\u00fcr Featureobjekt basis m\u00f6glich! MainWindow.languageChange=Um die Spracheinstellung zu \u00fcbernehmen muss CityDoctor2 neugestartet werden. -MainWindow.buildingsTab=Geb\u00e4ude -MainWindow.vegetationTab=Vegetation -MainWindow.transportationTab=Verkehrsobjekte -MainWindow.tunnelTab=Tunnel -MainWindow.bridgeTab=Br\u00fccken -MainWindow.waterTab=Gew\u00e4sser -MainWindow.terrainTab=Gel\u00e4nde -MainWindow.cityfurnitureTab=Stadtm\u00f6bel +MainWindow.loadFailed=Konnte GML-Datei nicht laden: {} +MainWindow.loadGivenFile=Lade vorhandene Datei, bitte warten +MainWindow.logTab=Log +MainWindow.memoryLabel=Speicher: +MainWindow.missingConfig=Konnte Konfigurationsdatei nicht finden. MainWindow.otherObjectsTab=Andere -MainWindow.viewLabel=Ansicht -MainWindow.showLabel=Zeige: -MainWindow.searchLabel=Suche: -MainWindow.searchBtn=Suche -MainWindow.clearBtn=Leeren -MainWindow.errorsTab=Fehler MainWindow.polygonsTab=Polygone -MainWindow.edgesTab=Kanten +MainWindow.searchBtn=Suche +MainWindow.searchLabel=Suche: +MainWindow.showLabel=Zeige: +MainWindow.terrainTab=Gel\u00e4nde +MainWindow.transportationTab=Verkehrsobjekte +MainWindow.tunnelTab=Tunnel +MainWindow.vegetationTab=Vegetation MainWindow.verticesTab=Punkte -MainWindow.attributeTab=Attribute -MainWindow.logTab=Log -MainWindow.globalErrorsTab=Globale Fehler -MainWindow.focusCityObject=Objekt fokussieren +MainWindow.viewLabel=Ansicht +MainWindow.warnCopyIdFailed=Kopieren der ID fehlgeschlagen! +MainWindow.waterTab=Gew\u00e4sser +MainWindow.withErrors=Mit Fehlern +MainWindow.writePdf=Schreibe Pdf Report +MainWindow.writeXml=Schreibe Xml Report +OpenFileDialog.cancelBtn=Abbrechen OpenFileDialog.fileLabel=Datei: -OpenFileDialog.selectBtn=Ausw\u00e4hlen OpenFileDialog.loadBtn=Laden -OpenFileDialog.settingsPane=Parser Einstellungen +OpenFileDialog.loadFailed=Konnte CityGML Datei nicht laden +OpenFileDialog.lowMemoryLabel=Reduzierter Speicherverbrauchsmodus OpenFileDialog.roundingPlacesLabel=NumberOfRoundingPlaces +OpenFileDialog.select=W\u00e4hle CityGML Datei aus +OpenFileDialog.selectBtn=Ausw\u00e4hlen +OpenFileDialog.settingsPane=Parser Einstellungen OpenFileDialog.xmlValidationLabel=F\u00fchre XML-Validierung aus -Checker.failXml=Konnte XML Fehlerbericht nicht schreiben. -Checker.failPdf=Konnte PDF Fehlerbericht nicht schreiben. -Checker.checksFinished=Pr\u00fcfungen beendet -Checker.schematronValidation=Starte Schematron Pr\u00fcfung -Checker.finishedSchematron=Schematron Pr\u00fcfung abgeschlossen -Checker.failReports=Konnte Pr\u00fcfberichte nicht schreiben. -Checker.checkFeature=Pr\u00fcfe Feature: {} -Checker.executeCheck=F\u00fchre Pr\u00fcfung aus: {} -ValidationConfiguration.reenable={} ist deaktiviert wird aber von {} ben\u00f6tigt, reaktiviere +Unit.Degree=Grad +Unit.Radian=Radiant ValidationConfiguration.missingSchematron={} existiert nicht, deaktiviere Schematron Pr\u00fcfung -CheckContainer.error=Unerwarteter Fehler bei der Ausf\u00fchrung von Pr\u00fcfung: {} -Checks.missingCheck=Konnte keine Pr\u00fcfung f\u00fcr id {} finden -OpenFileDialog.cancelBtn=Abbrechen -CheckDialog.checksTab=Pr\u00fcfungen -CheckDialog.filterTab=Filter -CheckDialog.globalParametersLabel=Globale Parameter -CheckDialog.availableChecksLabel=Verf\u00fcgbare Pr\u00fcfungen -CheckDialog.geometricChecksLabel=Geometrische Pr\u00fcfungen -CheckDialog.semanticChecksLabel=Semantische Pr\u00fcfungen -CheckDialog.schematronFileLabel=Schematron Datei -CheckDialog.selectBtn=Ausw\u00e4hlen -CheckDialog.checkBtn=Pr\u00fcfen -CheckDialog.cancelBtn=Abbrechen -CheckDialog.streamCheckOption=Niedrigen Arbeitsspeicherverbrauchsmodus aktivieren -CheckDialog.streamCheckLabel=Stadtmodelle werden nicht in den Arbeitsspeicher geladen. Ergebnisse werden in der QualityADE in Kopien der Eingangsdateien gespeichert. -CheckDialog.outputPathLabel=Ausgabeordner (Pflichtfeld): -CheckDialog.outputPathBtn=Ausw\u00e4hlen -CheckDialog.pdfReportsOption=PDF Berichte erstellen -CheckDialog.xmlReportsOption=XML Berichte erstellen -CheckDialog.noPathError=Ausgabepfad wurde nicht gesetzt! -CityGmlZipArchive.ioError=Zugriff auf ZIP-Datei fehlgeschlagen -CityGmlZipArchive.malformedZipFile=Einlesen des ZIP-Archivs abgebrochen, ZIP-Datei ist fehlerhaft! -CityGmlZipArchive.generalException=Unerwarteter Fehler beim Einlesen des ZIP-Archivs -WriteReportDialog.writeBtn=Speichern +ValidationConfiguration.reenable={} ist deaktiviert wird aber von {} ben\u00f6tigt, reaktiviere WriteReportDialog.cancelBtn=Abbrechen WriteReportDialog.errorStatisticsLabel=Fehler Statistik WriteReportDialog.saveImageBtn=Bild Speichern WriteReportDialog.selectPdfBtn=Ausw\u00e4hlen WriteReportDialog.selectXmlFile=Ausw\u00e4hlen +WriteReportDialog.writeBtn=Speichern WriteReportDialog.xAxisLabel=Fehler -WriteReportDialog.yAxisLabel=Anzahl -Unit.Radian=Radiant -Unit.Degree=Grad -FeatureMapper.polygonUnreferenced=Polygon {} ist referenziert, konnte aber nicht gefunden werden -GeometryMapper.emptyPolygon=Polygon ohne externen Ring gefunden, ignoriere -CityGmlParser.parsedObjects=Modell mit {} Objekten gelesen -CityGmlParser.chunkReadFailed=Konnte Datei nicht in St\u00fccken lesen, versuche komplett zu lesen -CityGmlParser.notValidGmlFile=Dies ist keine korrekte CityGML Datei\n -CityGmlParser.errorReadingGmlFile=Fehler beim lesen der CityGML Datei\n{} -CityGmlParser.errorWritingGmlFile=Fehler beim schreiben der CityGML Datei\n{} -CityGmlParser.noConversionNeeded=Koordinatensystem in Metern, keine Konvertierung notwendig -CityGmlParser.failedEPSGParse=Lesen des EPSG Codes fehlgeschlagen, greife auf metrisches System zur\u00fcck -CityGmlParser.missingEPSGCode=Kein EPSG Code gefunden, nehme metrisches System an -OpenFileDialog.loadFailed=Konnte CityGML Datei nicht laden -MainWindow.memoryLabel=Speicher: -CheckDialog.checksReenabledAlert=Manche Pr\u00fcfungen wurden reaktiviert damit andere gewollte Pr\u00fcfungen durchgef\u00fchrt werden k\u00f6nnen\nMehr Details sind im Log geschrieben -MainWindow.availableLabel=Verf\u00fcgbar: -OpenFileDialog.lowMemoryLabel=Reduzierter Speicherverbrauchsmodus -ErrorItemVisitor.intersection=Verschneidung -ErrorItemVisitor.component=Komponente -ErrorItemVisitor.nameOfAttribute=Name des Attributs -ErrorItemVisitor.childId=Kind ID -ErrorItemVisitor.distance=Distanz -ErrorItemVisitor.pointTouchesEdge=Punkt ber\u00fchrt Kante -ErrorItemVisitor.degeneratedRing=Degenerierter Ring -ErrorItemVisitor.deviation=Abweichung -ErrorItemVisitor.triangles=Dreiecke -DatabaseHandler.setupFailure=FATAL ERROR: Erstellen der eingebetteten Datenbank fehlgeschlagen! -DatabaseHandler.tableFailure=FATAL ERROR: Erstellen der features Tabelle fehlgeschlagen! -DatabaseHandler.marshallingFailure=Schreiben von CityObject {} in die Datenbank fehlgeschlagen! -DatabaseHandler.serializationFailure=Serialisierung von CityObject {} fehlgeschlagen! -DatabaseHandler.batchCommitFailure=Batch-commit abgebrochen: Kritischer Fehler beim Versuch CityObject {} in die Datenbank zu schreiben! -DatabaseHandler.connectionFailure=Verbindung zur eingebetteten Datenbank konnte nicht hergestellt werden! -DatabaseHandler.unmarshallingFailure=Laden von CityObject {} aus der Datenbank fehlgeschlagen! -DatabaseHandler.fetchErrorIdsFailure=Abfragen der Liste von CityObjects mit Fehlern ist fehlgeschlagen! -DatabaseHandler.getAllCityObjectsFailure=Abfrage nach allen CityObjects ist fehlgeschlagen! -DatabaseHandler.getBufferIntersectFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der Buffer-Intersections von CityObject {} fehlgeschlagen! -DatabaseHandler.getBBoxIntersectFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der BBox-Intersections von CityObject {} fehlgeschlagen! -DatabaseHandler.getFeatureCountFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der Anzahl an CityObjects fehlgeschlagen! \ No newline at end of file +WriteReportDialog.yAxisLabel=Anzahl \ No newline at end of file -- GitLab From 87fe26ccde287376859990a582206b6b52c04f8f Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 14:38:54 +0100 Subject: [PATCH 072/118] Style: Add Localization for CityDoctorValidation --- .../CityDoctorLocalization.properties | 11 ++++++++ .../CityDoctorLocalization_de.properties | 11 ++++++++ .../citydoctor2/CityDoctorValidation.java | 26 +++++++++---------- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index aaf522cf..fdabac15 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -48,6 +48,17 @@ CityDoctorController.noSourceFile=Source file is null, no checks could be done CityDoctorController.noZipFile=ZIP archive is null, no checks could be done CityDoctorController.saveZipArchiveReports=Successfully exported validation reports! CityDoctorValidation.Version=${versionString} +CityDoctorValidation.inputFileNotExisting= Input file '{}' does not exist. +CityDoctorValidation.loadingDefaultConfig=No configuration file given, loading default configuration +CityDoctorValidation.noConfig=No configuration file specified with -config [FILE] +CityDoctorValidation.noInputFile=No input file specified. (-in [FILE]) +CityDoctorValidation.noOutputFile=No output file specified. (-out [FILE]) +CityDoctorValidation.notExactlyOneConfigFile=Specify exactly one file as configuration +CityDoctorValidation.notExactlyOneInputFile=Specify exactly one file as input. +CityDoctorValidation.notExactlyOneOutputFile=Specify exactly one file as output. +CityDoctorValidation.notExactlyOnePDFOutput=Specify exactly one file as pdfReport +CityDoctorValidation.notExactlyOneXMLOutput=Specify exactly one file as xmlReport +CityDoctorValidation.wrongInputFileFormat=Input file '{}' does not end with .gml or .xml CityGmlParser.chunkReadFailed=Failed to read GML file in chunks, falling back to reading the complete file CityGmlParser.errorReadingGmlFile=Error while reading city gml file\n{} CityGmlParser.errorWritingGmlFile=Error while writing city gml file\n{} diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 0e04c86b..d557f08b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -46,6 +46,17 @@ CityDoctorController.noDatamodel=Datenmodell ist null, keine Pr\u00fcfungen konn CityDoctorController.noSourceFile=Quelldatei ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden CityDoctorController.noZipFile=ZIP-Archiv ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden CityDoctorController.saveZipArchiveReports=Validierungsberichte erfolgreich exportiert! +CityDoctorValidation.inputFileNotExisting=Eingabedatei '{}' existiert nicht. +CityDoctorValidation.loadingDefaultConfig=Keine Konfigurationsdatei angegeben, Standardkonfiguration wird geladen. +CityDoctorValidation.noConfig=Keine Konfigurationsdatei angegeben. (-config [DATEIPFAD]) +CityDoctorValidation.noInputFile=Keine Eingabedatei angegeben. (-in [DATEIPFAD]) +CityDoctorValidation.noOutputFile=Keinen Ausgabedateipfad angegeben. (-out [DATEIPFAD]) +CityDoctorValidation.notExactlyOneConfigFile=Es muss exakt eine Konfigurationsdatei angegeben werden. +CityDoctorValidation.notExactlyOneInputFile=Es muss exakt eine Eingabedatei angegeben werden. +CityDoctorValidation.notExactlyOneOutputFile=Es muss exakt ein Ausgabedateipfad angegeben werden. +CityDoctorValidation.notExactlyOnePDFOutput=Es muss exakt ein Ausgabedateipfad für den PDF-Bericht angegeben werden. +CityDoctorValidation.notExactlyOneXMLOutput=Es muss exakt ein Ausgabedateipfad für den XML-Bericht angegeben werden. +CityDoctorValidation.wrongInputFileFormat=Eingabedatei '{}' ist keine .gml oder .xml Datei. CityGmlParser.chunkReadFailed=Konnte Datei nicht in St\u00fccken lesen, versuche komplett zu lesen CityGmlParser.errorReadingGmlFile=Fehler beim lesen der CityGML Datei\n{} CityGmlParser.errorWritingGmlFile=Fehler beim schreiben der CityGML Datei\n{} diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java index ade4933d..ec2d0df0 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidation.java @@ -23,9 +23,9 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; +import de.hft.stuttgart.citydoctor2.utils.Localization; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.citygml4j.core.ade.ADEException; import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; @@ -81,12 +81,12 @@ public class CityDoctorValidation { if (optional) { return null; } - logger.error("No output file specified. (-out [FILE])"); + logger.error(Localization.getText("CityDoctorValidation.noOutputFile")); System.exit(11); } List outFiles = argParser.getValues("out"); if (outFiles.size() != 1) { - logger.error("Specify exactly one file as output."); + logger.error(Localization.getText("CityDoctorValidation.notExactlyOneOutputFile")); System.exit(12); } outputFile = outFiles.get(0); @@ -155,13 +155,13 @@ public class CityDoctorValidation { ValidationConfiguration config, String outputFile) throws CityDoctorWriteException, CityGmlParseException, IOException, InvalidGmlFileException { File inputFile = new File(input); if (!inputFile.exists()) { - logger.error("Input file '{}' does not exist.", input); + logger.error(Localization.getText("CityDoctorValidation.inputFileNotExisting"), input); System.exit(1); } if (inputFile.getName().endsWith(".gml") || inputFile.getName().endsWith(".xml")) { startFileValidation(inputFile, xmlOutput, pdfOutput, config, outputFile); } else { - logger.error("Input file '{}' does not end with .gml or .xml and is not a zip archive.", input); + logger.error(Localization.getText("CityDoctorValidation.wrongInputFileFormat"), input); System.exit(1); } } @@ -202,7 +202,7 @@ public class CityDoctorValidation { if (argParser.containsOption("pdfreport")) { List reportFiles = argParser.getValues("pdfreport"); if (reportFiles.size() != 1) { - logger.error("Specify exactly one file as pdfReport"); + logger.error(Localization.getText("CityDoctorValidation.notExactlyOnePDFOutput")); System.exit(6); } return reportFiles.get(0); @@ -214,7 +214,7 @@ public class CityDoctorValidation { if (argParser.containsOption("xmlreport")) { List reportFiles = argParser.getValues("xmlreport"); if (reportFiles.size() != 1) { - logger.error("Specify exactly one file as xmlReport"); + logger.error(Localization.getText("CityDoctorValidation.notExactlyOneXMLOutput")); System.exit(5); } return reportFiles.get(0); @@ -231,16 +231,16 @@ public class CityDoctorValidation { if (argParser.containsOption("config")) { List configFiles = argParser.getValues("config"); if (configFiles.size() != 1) { - logger.error("Specify exactly one file as configuration"); + logger.error(Localization.getText("CityDoctorValidation.notExactlyOneConfigFile")); System.exit(4); } return ValidationConfiguration.loadValidationConfig(configFiles.get(0)); } else { if (optional) { - logger.warn("No configuration file given, loading default configuration"); + logger.warn(Localization.getText("CityDoctorValidation.loadingDefaultConfig")); return ValidationConfiguration.loadStandardValidationConfig(); } else { - logger.error("No configuration file specified with -config [FILE]"); + logger.error(Localization.getText("CityDoctorValidation.noConfig")); System.exit(7); return null; } @@ -252,7 +252,7 @@ public class CityDoctorValidation { if (argParser.containsOption("out")) { List outFiles = argParser.getValues("out"); if (outFiles.size() != 1) { - logger.error("Specify exactly one file as output."); + logger.error(Localization.getText("CityDoctorValidation.multipleOutputFiles")); System.exit(3); } return outFiles.get(0); @@ -271,12 +271,12 @@ public class CityDoctorValidation { if (optional) { return null; } - logger.error("No input file specified. (-in [FILE])"); + logger.error(Localization.getText("CityDoctorValidation.noInputFile")); System.exit(1); } List inFiles = argParser.getValues("in"); if (inFiles.size() != 1) { - logger.error("Specify exactly one file as input."); + logger.error(Localization.getText("CityDoctorValidation.notExactlyOneInputFile")); System.exit(2); } inputFile = inFiles.get(0); -- GitLab From 1d1436bc082f39926f85c7fccad7922a4930d5fc Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 14:50:45 +0100 Subject: [PATCH 073/118] Style: Unify Localization files --- .../main/resources/CityDoctorLocalization.properties | 6 +++--- .../resources/CityDoctorLocalization_de.properties | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index fdabac15..77fa1639 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -21,9 +21,9 @@ CheckDialog.outputPathLabel=Output directory (Required): CheckDialog.parameterUnit=Unit CheckDialog.parameterValue=Value CheckDialog.pdfReportsOption=Create PDF reports -CheckDialog.schematronChooserTitle=Choose schematron file.. +CheckDialog.schematronChooserTitle=Choose schematron file ... CheckDialog.schematronFileLabel=Schematron File: -CheckDialog.schematronFileNotExisting=Schematron file does not exist, ignoring +CheckDialog.schematronFileNotExisting=Schematron file does not exist, Schematron will be skipped CheckDialog.schematronFiles=Schematron Files CheckDialog.selectBtn=Select CheckDialog.semanticChecksLabel=Semantic Checks @@ -172,4 +172,4 @@ WriteReportDialog.selectPdfBtn=Select WriteReportDialog.selectXmlFile=Select WriteReportDialog.writeBtn=Save WriteReportDialog.xAxisLabel=Error -WriteReportDialog.yAxisLabel=Count +WriteReportDialog.yAxisLabel=Count \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index d557f08b..5584a2e4 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -7,6 +7,7 @@ CheckDialog.availableChecksLabel=Verf\u00fcgbare Pr\u00fcfungen CheckDialog.cancelBtn=Abbrechen CheckDialog.checkBtn=Pr\u00fcfen CheckDialog.checkEnabled=Aktiv +CheckDialog.checkName=Name 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.checksTab=Pr\u00fcfungen @@ -20,9 +21,9 @@ CheckDialog.outputPathLabel=Ausgabeordner (Pflichtfeld): CheckDialog.parameterUnit=Einheit CheckDialog.parameterValue=Wert CheckDialog.pdfReportsOption=PDF Berichte erstellen -CheckDialog.schematronChooserTitle=W\u00e4hle Schematron Datei.. -CheckDialog.schematronFileLabel=Schematron Datei -CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, ignoriere +CheckDialog.schematronChooserTitle=W\u00e4hle Schematron Datei ... +CheckDialog.schematronFileLabel=Schematron Datei: +CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, Schematron wird übersprungen CheckDialog.schematronFiles=Schematron Dateien CheckDialog.selectBtn=Ausw\u00e4hlen CheckDialog.semanticChecksLabel=Semantische Pr\u00fcfungen @@ -30,7 +31,7 @@ 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.streamCheckOption=Niedrigen Arbeitsspeicherverbrauchsmodus aktivieren CheckDialog.title=Pr\u00fcfungskonfiguration -CheckDialog.validationConfiguration=Validierungs Konfiguration +CheckDialog.validationConfiguration=Validierungskonfiguration CheckDialog.xmlReportsOption=XML Berichte erstellen Checker.checkFeature=Pr\u00fcfe Feature: {} Checker.checksFinished=Pr\u00fcfungen beendet @@ -39,13 +40,14 @@ Checker.failPdf=Konnte PDF Fehlerbericht nicht schreiben. Checker.failReports=Konnte Pr\u00fcfberichte nicht schreiben. Checker.failXml=Konnte XML Fehlerbericht nicht schreiben. Checker.finishedSchematron=Schematron Pr\u00fcfung abgeschlossen -Checker.schematronValidation=Starte Schematron Pr\u00fcfung +Checker.schematronValidation=Starte Schematron Pr\u00fcfung parallel Checks.missingCheck=Konnte keine Pr\u00fcfung f\u00fcr id {} finden CityDoctorController.exportSuccess=Feature export erfolgreich abgeschlossen CityDoctorController.noDatamodel=Datenmodell ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden CityDoctorController.noSourceFile=Quelldatei ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden CityDoctorController.noZipFile=ZIP-Archiv ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden CityDoctorController.saveZipArchiveReports=Validierungsberichte erfolgreich exportiert! +CityDoctorValidation.Version=${versionString} CityDoctorValidation.inputFileNotExisting=Eingabedatei '{}' existiert nicht. CityDoctorValidation.loadingDefaultConfig=Keine Konfigurationsdatei angegeben, Standardkonfiguration wird geladen. CityDoctorValidation.noConfig=Keine Konfigurationsdatei angegeben. (-config [DATEIPFAD]) -- GitLab From 0305b2bcee40bf52624be5a52c9452044ed6efed Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 15:06:37 +0100 Subject: [PATCH 074/118] Style: Add Localization to CityGMLWriterUtils --- .../de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java | 2 +- .../src/main/resources/CityDoctorLocalization.properties | 1 + .../src/main/resources/CityDoctorLocalization_de.properties | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java index d58b4b67..fc76e1ff 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java @@ -135,7 +135,7 @@ public class CityGMLWriterUtils { de.hft.stuttgart.quality.model.enums.ErrorId adeId = QualityADEUtils.mapErrorIdToAdeId(e.getKey()); if (adeId == null) { // error that is not part of the ade standard - logger.warn("Found error {} that cannot be converted to ADE error. It will not be written", e.getKey()); + logger.warn(Localization.getText("CityGmlWriterUtils.unknownADEError"), e.getKey()); continue; } stats.setName(adeId); diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 77fa1639..709baeca 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -67,6 +67,7 @@ CityGmlParser.missingEPSGCode=No EPSG code found, assuming metric system CityGmlParser.noConversionNeeded=Coordinate system is in meters, no conversion done CityGmlParser.notValidGmlFile=This is not a valid GML-File\n CityGmlParser.parsedObjects=Parsed model with {} objects +CityGmlWriterUtils.unknownADEError= Found error {} that cannot be converted to ADE error. It will not be written CityGmlZipArchive.generalException=Encountered an unexpected error while reading the ZIP archive CityGmlZipArchive.ioError=Accessing the ZIP archive failed CityGmlZipArchive.malformedZipFile=Parsing of ZIP archive aborted, ZIP file is malformed! diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 5584a2e4..1e4e5a46 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -67,6 +67,7 @@ CityGmlParser.missingEPSGCode=Kein EPSG Code gefunden, nehme metrisches System a CityGmlParser.noConversionNeeded=Koordinatensystem in Metern, keine Konvertierung notwendig CityGmlParser.notValidGmlFile=Dies ist keine korrekte CityGML Datei\n CityGmlParser.parsedObjects=Modell mit {} Objekten gelesen +CityGmlWriterUtils.unknownADEError= Fehler {} kann nicht zu einem ADE Fehler konvertiert werden. Fehler wird nicht ausgegeben CityGmlZipArchive.generalException=Unerwarteter Fehler beim Einlesen des ZIP-Archivs CityGmlZipArchive.ioError=Zugriff auf ZIP-Datei fehlgeschlagen CityGmlZipArchive.malformedZipFile=Einlesen des ZIP-Archivs abgebrochen, ZIP-Datei ist fehlerhaft! -- GitLab From abd380e21122109bfc65c5e260e3dad168242c70 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 15:33:05 +0100 Subject: [PATCH 075/118] Style: Update Localization of CityGmlParser --- .../stuttgart/citydoctor2/parser/CityGmlParser.java | 11 +++++------ .../main/resources/CityDoctorLocalization.properties | 5 +++++ .../resources/CityDoctorLocalization_de.properties | 5 +++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 35ef6856..b7c42265 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -158,10 +158,10 @@ public class CityGmlParser { ADERegistry adeRegistry = ADERegistry.getInstance(); adeRegistry.loadADE(new QualityADEContext()); } catch (CityGMLContextException e) { - logger.fatal("Unable to create citygml4j context", e); + logger.fatal(Localization.getText("CityGmlParser.creating4jContextFailed"), e); throw new IllegalStateException("Unable to create citygml4j context"); } catch (ADEException e) { - logger.fatal("Unable to add ADE plugins to citygml4j", e); + logger.fatal(Localization.getText("CityGmlParser.creatingAdePluginsFailed"), e); throw new IllegalStateException("Unable to add ADE plugins to citygml4j"); } } @@ -282,7 +282,6 @@ public class CityGmlParser { return readAndKeepFeatures(config, file, in, ois, verbose, nested); } } catch (CityGMLReadException | IOException e) { - //TODO: Localize String throw new CityGmlParseException("Failed to read CityGML file", e); } } @@ -480,7 +479,7 @@ public class CityGmlParser { if (mapper.getModel().getCityModel() == null) { // file does not contain a city model? // create it for now - logger.warn("File does not contain a CityModel definition! Temporary CityModel has been created."); + logger.warn(Localization.getText("CityGmlParser.missingCityModel")); mapper.setCityModel(new CityModel()); for (AbstractCityObject aco : queuedObjects) { if (!cdModel.containsGmlId(aco.getId())) { @@ -603,11 +602,11 @@ public class CityGmlParser { CoordinateReferenceSystem utm; if (centerLat < 0) { // south - logger.info("Converting coordiante system to UTM zone {}S", zone); + logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneS"), zone); utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south"); } else { // north - logger.info("Converting coordiante system to UTM zone {}N", zone); + logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneN"), zone); utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone); } config.setCoordinateSystem(crs, utm); diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 709baeca..acfbe050 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -60,9 +60,14 @@ CityDoctorValidation.notExactlyOnePDFOutput=Specify exactly one file as pdfRepor CityDoctorValidation.notExactlyOneXMLOutput=Specify exactly one file as xmlReport CityDoctorValidation.wrongInputFileFormat=Input file '{}' does not end with .gml or .xml 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.convertCrsToUtmZoneS=Converting coordinate system to UTM zone {}S +CityGmlParser.creating4jContextFailed=Unable to create citygml4j context +CityGmlParser.creatingAdePluginsFailed=Unable to add ADE plugins to citygml4j CityGmlParser.errorReadingGmlFile=Error while reading city gml file\n{} CityGmlParser.errorWritingGmlFile=Error while writing city gml file\n{} CityGmlParser.failedEPSGParse=Could not read EPSG code, fallback to metric system +CityGmlParser.missingCityModel=File does not contain a CityModel definition! Temporary CityModel will be created. CityGmlParser.missingEPSGCode=No EPSG code found, assuming metric system CityGmlParser.noConversionNeeded=Coordinate system is in meters, no conversion done CityGmlParser.notValidGmlFile=This is not a valid GML-File\n diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 1e4e5a46..4cdf02f8 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -60,9 +60,14 @@ CityDoctorValidation.notExactlyOnePDFOutput=Es muss exakt ein Ausgabedateipfad f CityDoctorValidation.notExactlyOneXMLOutput=Es muss exakt ein Ausgabedateipfad für den XML-Bericht angegeben werden. CityDoctorValidation.wrongInputFileFormat=Eingabedatei '{}' ist keine .gml oder .xml Datei. CityGmlParser.chunkReadFailed=Konnte Datei nicht in St\u00fccken lesen, versuche komplett zu lesen +CityGmlParser.convertCrsToUtmZoneN=Konvertiere Koordinatensystem zu UTM Zone {}N +CityGmlParser.convertCrsToUtmZoneS=Konvertiere Koordinatensystem zu UTM Zone {}S +CityGmlParser.creating4jContextFailed=Citygml4j Kontext konnte nicht erstellt werden +CityGmlParser.creatingAdePluginsFailed=ADE Plugin f\u00fcr citygml4j konnte nicht erstellt werden CityGmlParser.errorReadingGmlFile=Fehler beim lesen der CityGML Datei\n{} CityGmlParser.errorWritingGmlFile=Fehler beim schreiben der CityGML Datei\n{} CityGmlParser.failedEPSGParse=Lesen des EPSG Codes fehlgeschlagen, greife auf metrisches System zur\u00fcck +CityGmlParser.missingCityModel=Datei enth\u00e4lt kein CityModel! Tempor\u00e4res CityModel wird erstellt. CityGmlParser.missingEPSGCode=Kein EPSG Code gefunden, nehme metrisches System an CityGmlParser.noConversionNeeded=Koordinatensystem in Metern, keine Konvertierung notwendig CityGmlParser.notValidGmlFile=Dies ist keine korrekte CityGML Datei\n -- GitLab From c91da393099d1447db3bfe4aa53aa59dab014718 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 16:24:50 +0100 Subject: [PATCH 076/118] Fix: Use correct error collection function --- .../hft/stuttgart/citydoctor2/database/DatabaseHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index ad527e3d..db2443f4 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -98,7 +98,7 @@ public class DatabaseHandler { ps.setString(1, co.getGmlId().toString()); String bbox = BoundingBox.of(co).to2DWkt(); ps.setString(2, bbox); - ps.setBoolean(4, co.hasAnyError()); + ps.setBoolean(4, co.containsAnyError()); if (logger.isDebugEnabled()) { logger.debug("Attempting to serialize CityObject: {} ", co); @@ -135,7 +135,7 @@ public class DatabaseHandler { if (logger.isTraceEnabled()) { logger.trace("Attempting to serialize CityObject: {} ", co); } - ps.setBoolean(4, co.hasAnyError()); + ps.setBoolean(4, co.containsAnyError()); try { byte[] bytes = SerializationUtils.serialize(co); -- GitLab From b4d45aa0104aeb3e46236e8994bfec9a8e12786b Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 16:34:40 +0100 Subject: [PATCH 077/118] Fix: Re-enable database creation --- .../hft/stuttgart/citydoctor2/database/DatabaseHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index db2443f4..78be4f78 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -39,10 +39,10 @@ public class DatabaseHandler { public DatabaseHandler(){ try { -// String jdbcurl = H2GISDBFactory.createDataSource(DB_NAME, -// true, ";AUTO_SERVER=TRUE").getConnection().getMetaData().getURL(); + String jdbcurl = H2GISDBFactory.createDataSource(DB_NAME, + true, ";AUTO_SERVER=TRUE").getConnection().getMetaData().getURL(); - logger.info("2] {}",jdbcurl); + logger.debug("1] {}",jdbcurl); HikariDataSource ds = new HikariDataSource(); ds.setJdbcUrl(jdbcurl); -- GitLab From dbc4750b7188e1f03149655c83ce1ff12a4165c7 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 11 Nov 2025 16:45:32 +0100 Subject: [PATCH 078/118] Fix: Use Unicode encoding for Umlaute in Localization --- .../resources/CityDoctorLocalization_de.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 4cdf02f8..196b4d8a 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -23,7 +23,7 @@ CheckDialog.parameterValue=Wert CheckDialog.pdfReportsOption=PDF Berichte erstellen CheckDialog.schematronChooserTitle=W\u00e4hle Schematron Datei ... CheckDialog.schematronFileLabel=Schematron Datei: -CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, Schematron wird übersprungen +CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, Schematron wird \u00fcbersprungen CheckDialog.schematronFiles=Schematron Dateien CheckDialog.selectBtn=Ausw\u00e4hlen CheckDialog.semanticChecksLabel=Semantische Pr\u00fcfungen @@ -56,8 +56,8 @@ CityDoctorValidation.noOutputFile=Keinen Ausgabedateipfad angegeben. (-out [DATE CityDoctorValidation.notExactlyOneConfigFile=Es muss exakt eine Konfigurationsdatei angegeben werden. CityDoctorValidation.notExactlyOneInputFile=Es muss exakt eine Eingabedatei angegeben werden. CityDoctorValidation.notExactlyOneOutputFile=Es muss exakt ein Ausgabedateipfad angegeben werden. -CityDoctorValidation.notExactlyOnePDFOutput=Es muss exakt ein Ausgabedateipfad für den PDF-Bericht angegeben werden. -CityDoctorValidation.notExactlyOneXMLOutput=Es muss exakt ein Ausgabedateipfad für den XML-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.wrongInputFileFormat=Eingabedatei '{}' ist keine .gml oder .xml Datei. CityGmlParser.chunkReadFailed=Konnte Datei nicht in St\u00fccken lesen, versuche komplett zu lesen CityGmlParser.convertCrsToUtmZoneN=Konvertiere Koordinatensystem zu UTM Zone {}N @@ -70,7 +70,7 @@ CityGmlParser.failedEPSGParse=Lesen des EPSG Codes fehlgeschlagen, greife auf me CityGmlParser.missingCityModel=Datei enth\u00e4lt kein CityModel! Tempor\u00e4res CityModel wird erstellt. CityGmlParser.missingEPSGCode=Kein EPSG Code gefunden, nehme metrisches System an CityGmlParser.noConversionNeeded=Koordinatensystem in Metern, keine Konvertierung notwendig -CityGmlParser.notValidGmlFile=Dies ist keine korrekte CityGML Datei\n +CityGmlParser.notValidGmlFile=Dies ist keine korrekte CityGML Datei CityGmlParser.parsedObjects=Modell mit {} Objekten gelesen CityGmlWriterUtils.unknownADEError= Fehler {} kann nicht zu einem ADE Fehler konvertiert werden. Fehler wird nicht ausgegeben CityGmlZipArchive.generalException=Unerwarteter Fehler beim Einlesen des ZIP-Archivs @@ -140,7 +140,7 @@ MainWindow.loadFailed=Konnte GML-Datei nicht laden: {} MainWindow.loadGivenFile=Lade vorhandene Datei, bitte warten MainWindow.logTab=Log MainWindow.memoryLabel=Speicher: -MainWindow.missingConfig=Konnte Konfigurationsdatei nicht finden. +MainWindow.missingConfig=Konnte Konfigurationsdatei nicht finden. MainWindow.otherObjectsTab=Andere MainWindow.polygonsTab=Polygone MainWindow.searchBtn=Suche -- GitLab From 8b2305a7496e56079c1ad7627b87e461c57abe7c Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Wed, 12 Nov 2025 14:35:43 +0100 Subject: [PATCH 079/118] Refactor: Simplify error filtering --- .../citydoctor2/gui/CityDoctorController.java | 103 +++--------------- 1 file changed, 13 insertions(+), 90 deletions(-) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index a4cfb7c3..84949209 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -1391,7 +1391,6 @@ public class CityDoctorController { List idList = model.getBuildingIds(); foundBuildings.removeIf(gmlId -> (!idList.contains(gmlId))); -// model.getBuildings().filter(Checkable::containsAnyError).forEach(co -> foundBuildings.add(co.getGmlId())); Supplier> supply = () -> model.getCache().getStream(foundBuildings).map(co -> (Building) co); buildBuildingTreeFromList(supply, root); updateTree(root); @@ -1414,7 +1413,6 @@ public class CityDoctorController { List idList = model.getVegetationIds(); foundVegetation.removeIf(gmlId -> (!idList.contains(gmlId))); -// model.getVegetation().filter(Checkable::containsAnyError).forEach(co -> foundVegetation.add(co.getGmlId())); Supplier> supply = () -> model.getCache().getStream(foundVegetation).map(co -> (Vegetation) co); buildVegetationTreeFromList(supply, root); updateTree(root); @@ -1437,7 +1435,6 @@ public class CityDoctorController { List idList = model.getBridgeIds(); foundBridge.removeIf(gmlId -> (!idList.contains(gmlId))); -// model.getBridges().filter(Checkable::containsAnyError).forEach(co -> foundBridge.add(co.getGmlId())); Supplier> supply = () -> model.getCache().getStream(foundBridge).map(co -> (BridgeObject) co); buildBridgeTreeFromList(supply, root); updateTree(root); @@ -1460,7 +1457,6 @@ public class CityDoctorController { List idList = model.getLandIds(); foundLand.removeIf(gmlId -> (!idList.contains(gmlId))); -// model.getLand().filter(Checkable::containsAnyError).forEach(co -> foundLand.add(co.getGmlId())); Supplier> supply = () -> model.getCache().getStream(foundLand).map(co -> (CityObject) co); buildLandTreeFromList(supply, root); updateTree(root); @@ -1483,7 +1479,6 @@ public class CityDoctorController { List idList = model.getCityFurnitureIds(); foundCityFurniture.removeIf(gmlId -> (!idList.contains(gmlId))); -// model.getCityFurniture().filter(Checkable::containsAnyError).forEach(co -> foundCityFurniture.add(co.getGmlId())); Supplier> supply = () -> model.getCache().getStream(foundCityFurniture).map(co -> (CityFurniture) co); buildCityFurnitureTreeFromList(supply, root); updateTree(root); @@ -1506,7 +1501,6 @@ public class CityDoctorController { List idList = model.getGenericCityObjectIds(); foundGenericCityObjects.removeIf(gmlId -> (!idList.contains(gmlId))); -// model.getGenericCityObjects().filter(Checkable::containsAnyError).forEach(co -> foundGenericCityObjects.add(co.getGmlId())); Supplier> supply = () -> model.getCache().getStream(foundGenericCityObjects).map(co -> (GenericCityObject) co); buildGenericCityObjectTreeFromList(supply, root); updateTree(root); @@ -1529,7 +1523,6 @@ public class CityDoctorController { List idList = model.getTransportationIds(); foundTransportation.removeIf(gmlId -> (!idList.contains(gmlId))); -// model.getTransportation().filter(Checkable::containsAnyError).forEach(co -> foundTransportation.add(co.getGmlId())); Supplier> supply = () -> model.getCache().getStream(foundTransportation).map(co -> (TransportationObject) co); buildTransportationTreeFromList(supply, root); updateTree(root); @@ -1552,7 +1545,6 @@ public class CityDoctorController { List idList = model.getTunnelIds(); foundTunnel.removeIf(gmlId -> (!idList.contains(gmlId))); -// model.getTunnels().filter(Checkable::containsAnyError).forEach(co -> foundTunnel.add(co.getGmlId())); Supplier> supply = () -> model.getCache().getStream(foundTunnel).map(co -> (Tunnel) co); buildTunnelTreeFromList(supply, root); updateTree(root); @@ -1576,60 +1568,12 @@ public class CityDoctorController { List idList = model.getWaterIds(); foundWater.removeIf(gmlId -> (!idList.contains(gmlId))); -// model.getWater().filter(Checkable::containsAnyError).forEach(co -> foundWater.add(co.getGmlId())); Supplier> supply = () -> model.getCache().getStream(foundWater).map(co -> (WaterObject) co); buildWaterTreeFromList(supply, root); updateTree(root); addMoreButtonForWater(waterView, root, tunnelChunkNr, supply, foundWater.size()); } - public void fillTreeViewFromFeatures(FeatureType selectedTab) { - if (model == null) { - return; - } - mainWindow.resetSearchBar(); - switch (selectedTab) { - case BUILDING: - buildBuildings(model); - updateTree(mainWindow.getBuildingsView().getRoot()); - break; - case VEGETATION: - buildVegetation(model); - updateTree(mainWindow.getVegetationView().getRoot()); - break; - case BRIDGE: - buildBridges(model); - updateTree(mainWindow.getBridgeView().getRoot()); - break; - case LAND: - buildLand(model); - updateTree(mainWindow.getTerrainView().getRoot()); - break; - case TRANSPORTATION: - buildTransportation(model); - updateTree(mainWindow.getTransportationView().getRoot()); - break; - case TUNNEL: - buildTunnel(model); - updateTree(mainWindow.getTunnelView().getRoot()); - break; - case WATER: - buildWater(model); - updateTree(mainWindow.getWaterView().getRoot()); - break; - case CITY_FURNITURE: - buildCityFurniture(model); - updateTree(mainWindow.getCityFurnitureView().getRoot()); - break; - case GENERIC_CITY_OBJECT: - buildGenericCityObjects(model); - updateTree(mainWindow.getGenericCityObjectView().getRoot()); - break; - default: - throw new IllegalStateException(); - } - } - public CityDoctorModel getModel() { return model; } @@ -1692,40 +1636,19 @@ public class CityDoctorController { public void errorFilterIndexChanged(Number newV) { mainWindow.getMeshGroup().getChildren().clear(); mainWindow.unselectEverything(); - if (newV.intValue() == 0) { - fillTreeViewFromFeatures(mainWindow.getSelectedTab()); - } else if (newV.intValue() == 1) { - switch (mainWindow.getSelectedTab()) { - case BUILDING: - fillTreeViewWithErrorBuildings(); - break; - case VEGETATION: - fillTreeViewWithErrorVegetation(); - break; - case BRIDGE: - fillTreeViewWithErrorBridges(); - break; - case LAND: - fillTreeViewWithErrorLand(); - break; - case TRANSPORTATION: - fillTreeViewWithErrorTransportation(); - break; - case TUNNEL: - fillTreeViewWithErrorTunnel(); - break; - case WATER: - fillTreeViewWithErrorWater(); - break; - case CITY_FURNITURE: - fillTreeViewWithErrorCityFurniture(); - break; - case GENERIC_CITY_OBJECT: - fillTreeViewWithErrorGenericCityObjects(); - break; - default: - throw new IllegalStateException("Unknown selected feature tab: " + mainWindow.getSelectedTab()); - } + if (newV.intValue() == 0) { // Error filter was set to "All" + buildTrees(); + updateFeatureTrees(); + } else if (newV.intValue() == 1) { // Error filter was set to "With errors" + fillTreeViewWithErrorBuildings(); + fillTreeViewWithErrorVegetation(); + fillTreeViewWithErrorBridges(); + fillTreeViewWithErrorLand(); + fillTreeViewWithErrorTransportation(); + fillTreeViewWithErrorTunnel(); + fillTreeViewWithErrorWater(); + fillTreeViewWithErrorCityFurniture(); + fillTreeViewWithErrorGenericCityObjects(); } else { throw new IllegalStateException("Unknown filter index selected: " + newV); } -- GitLab From 7cafd3096a56c42d8bee03a6b240de6b2b77d9c3 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Wed, 12 Nov 2025 14:51:21 +0100 Subject: [PATCH 080/118] Refactor: Synchronize cache after Checks + Schematron --- .../java/de/hft/stuttgart/citydoctor2/check/Checker.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 3d6181e5..9b214d95 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -205,14 +205,16 @@ public class Checker { } if (schematronThread.isAlive()) { //TODO: Localize String - logger.info("Schematron thread still running, waiting for completion. This may take some time"); + logger.info("Schematron is still running. Waiting for completion, this may take some time"); try{ schematronThread.join(); + logger.info("Schematron completed."); } catch (InterruptedException e) { logger.error("Main Thread was interrupted while waiting for Schematron", e); schematronThread.interrupt(); } } + CacheManager.synchronizeCache(); model.setValidated(createValidationPlan()); } @@ -694,8 +696,6 @@ public class Checker { String msg = String.format("Checker could not load %d/%.0f features (%.2f%%)", missing, featureSum, (missing/featureSum) * 100); logger.warn(msg); } - CacheManager.synchronizeCache(); -// model.preCacheShownFeatures(); } catch (InterruptedException e) { throw new RuntimeException(e); } -- GitLab From ee6980a0cd3f6960d4e5960bbcb916346df59a6e Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 14 Nov 2025 13:32:34 +0100 Subject: [PATCH 081/118] Refactor: Add one-time retrying of checks Checker will now retry to check Features that were missed in the first checking attempt due to being unable to load the Features from the database --- .../stuttgart/citydoctor2/check/Checker.java | 136 +++++++++--------- 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 9b214d95..26329c6e 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -90,7 +90,6 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Stream; /** * The main container class for checking. It contains the logic for validation, @@ -650,27 +649,71 @@ public class Checker { private void checkCityModel(CityDoctorModel model, ProgressListener l){ List features = model.getFeatureIds(); - CityObjectCache cache = model.getCache(); - float featureSum = model.getNumberOfFeatures(); + AtomicInteger checkedCount = new AtomicInteger(0); - AtomicInteger missingCount = new AtomicInteger(0); // clear global errors model.getGlobalErrors().clear(); - + CityObjectCache cache = model.getCache(); + logger.trace("Setting up ThreadPool"); int threadCount = Runtime.getRuntime().availableProcessors() * 5; ExecutorService exec = new ThreadPoolExecutor (threadCount, threadCount, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); + try{ + long startTime = System.nanoTime(); + + List> futures = runChecksOnFeatures(exec, cache, features, checkedCount, l); + List missedFeatures = getMissedFeatures(futures); + if (!missedFeatures.isEmpty()){ + logger.warn("Checker could not load some CityObjects from database!"); + logger.info("Waiting a second before retrying..."); + Thread.sleep(1000); + futures = runChecksOnFeatures(exec, cache, missedFeatures, checkedCount, l); + missedFeatures = getMissedFeatures(futures); + if (!missedFeatures.isEmpty()){ + //TODO: Localize String + logger.error("Checker still unable to load some CityObjects from database!"); + logger.debug(missedFeatures.toString()); + } + } + long endTime = System.nanoTime(); + if (logger.isDebugEnabled()){ + logger.debug("Checks finished in {} ms", (endTime - startTime) / 1_000_000); + } + } catch (InterruptedException e){ + // No interrupts implemented, so this block should actually never be entered + //TODO: Localize String + logger.error("Validation aborted: Checker got interrupted", e); + Thread.currentThread().interrupt(); + + } finally { + exec.shutdown(); + try { + if(!exec.awaitTermination(1, TimeUnit.MINUTES)){ + exec.shutdownNow(); + } + + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + private List> runChecksOnFeatures(ExecutorService exec, CityObjectCache cache, List ids, AtomicInteger checkedCount, + ProgressListener l) + throws InterruptedException { + float featureSum = ids.size() + (float) checkedCount.get(); - long startTime = System.nanoTime(); - for (GmlId id : features) { - exec.execute(()->{ + logger.trace("Queueing up Checker tasks"); + List> tasks = new ArrayList<>(); + for (GmlId id : ids) { + tasks.add(()->{ CityObject co = cache.get(id); if (co == null) { - missingCount.incrementAndGet(); - if (l!=null){ - l.updateProgress((checkedCount.get() + missingCount.get()) / featureSum); - } - return; + return id; + } + if(Thread.interrupted()){ + Thread.currentThread().interrupt(); + return null; } co.prepareForChecking(); executeChecksForCityObject(co); @@ -678,63 +721,28 @@ public class Checker { cache.put(co); checkedCount.incrementAndGet(); if (l!=null){ - l.updateProgress((checkedCount.get() + missingCount.get()) / featureSum); + l.updateProgress((checkedCount.get()) / featureSum); } + return null; }); } - exec.shutdown(); - try { - exec.awaitTermination(30, TimeUnit.DAYS); - long endTime = System.nanoTime(); - if (logger.isDebugEnabled()){ - logger.debug("Checks finished in {} ms", (endTime - startTime) / 1_000_000); - } - - if (missingCount.get() > 0) { - int missing = missingCount.get(); - //TODO: Localize String - String msg = String.format("Checker could not load %d/%.0f features (%.2f%%)", missing, featureSum, (missing/featureSum) * 100); - logger.warn(msg); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + return exec.invokeAll(tasks); } - private void checkCityModelOLD(CityDoctorModel model, ProgressListener l) { - Stream features = model.createFeatureStream(); - float featureSum = model.getNumberOfFeatures(); - boolean lowMemoryMode = config.getParserConfiguration().useLowMemoryConsumption(); - - // clear global errors - model.getGlobalErrors().clear(); - - long startTime = System.nanoTime(); - - // stupid lamda with final variable restrictions - int[] currentFeature = new int[1]; - features.forEach(co -> { - if (lowMemoryMode) { - // no edges have been created yet, create them - co.prepareForChecking(); - } - // check every feature - executeChecksForCityObject(co); - - if (lowMemoryMode) { - // low memory consumption, remove edges again - co.clearMetaInformation(); - } - if (l != null) { - currentFeature[0]++; - l.updateProgress(currentFeature[0] / featureSum); + private List getMissedFeatures(List> futures) throws InterruptedException{ + List missedList = new ArrayList<>(); + for (Future future : futures) { + try{ + GmlId gmlId = future.get(); + if (gmlId != null) { + missedList.add(gmlId); + } + } catch (ExecutionException e){ + logger.debug("A Task failed due to an unexpected exception", e); + logger.debug(e.getCause()); } - }); - - long endTime = System.nanoTime(); - model.preCacheShownFeatures(); - logger.info("Checks finished in " + (endTime -startTime) / 1_000_000 + " ms"); - + } + return missedList; } private boolean filterObject(CityObject co) { -- GitLab From 18793c04012122f75ee30dd733ee52c2fa180b63 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 14 Nov 2025 13:52:45 +0100 Subject: [PATCH 082/118] Style: Add error log for checks --- .../hft/stuttgart/citydoctor2/check/Checker.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 26329c6e..38700e18 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -672,7 +672,9 @@ public class Checker { if (!missedFeatures.isEmpty()){ //TODO: Localize String logger.error("Checker still unable to load some CityObjects from database!"); - logger.debug(missedFeatures.toString()); + if (logger.isDebugEnabled()){ + logger.debug(missedFeatures.toString()); + } } } long endTime = System.nanoTime(); @@ -693,7 +695,8 @@ public class Checker { } } catch (InterruptedException e) { - throw new RuntimeException(e); + Thread.currentThread().interrupt(); + logger.error("Main Thread got interrupted during ThreadPool shutdown"); } } } @@ -726,11 +729,13 @@ public class Checker { return null; }); } + logger.trace("Queueing up finished, invoking all tasks"); return exec.invokeAll(tasks); } private List getMissedFeatures(List> futures) throws InterruptedException{ List missedList = new ArrayList<>(); + Set errors = new HashSet<>(); for (Future future : futures) { try{ GmlId gmlId = future.get(); @@ -740,8 +745,13 @@ public class Checker { } catch (ExecutionException e){ logger.debug("A Task failed due to an unexpected exception", e); logger.debug(e.getCause()); + errors.add(e); } } + if (!errors.isEmpty()) { + logger.error("Encountered check failures due to the following errors:"); + logger.error(errors.toString()); + } return missedList; } -- GitLab From cc9daee008a0c6e255b56241d2a70ae6ae018341 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 14 Nov 2025 14:08:28 +0100 Subject: [PATCH 083/118] Style: Add Localizations to Checker --- .../resources/CityDoctorLocalization.properties | 6 ++++++ .../resources/CityDoctorLocalization_de.properties | 6 ++++++ .../hft/stuttgart/citydoctor2/check/Checker.java | 14 ++++++-------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index acfbe050..69371abf 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -35,12 +35,18 @@ CheckDialog.validationConfiguration=Validation Configuration CheckDialog.xmlReportsOption=Create XML reports Checker.checkFeature=Checking feature: {} Checker.checksFinished=Finished executing checks +Checker.dbStillUnresponsive=Checker still unable to load some CityObjects from database! +Checker.dbUnresponsive=Checker could not load some CityObjects from database! Checker.executeCheck=Executing check: {} Checker.failPdf=Failed to write PDF Report. Checker.failReports=Failed to write reports. Checker.failXml=Failed to write XML Error Report. Checker.finishedSchematron=Finished Schematron validation +Checker.futuresCheckFailures=Encountered check failures due to the following errors: Checker.schematronValidation=Schematron-validation thread started +Checker.shutdownInterrupted=Main Thread got interrupted during ThreadPool shutdown +Checker.validationInterrupted=Validation aborted: Checker got interrupted +Checker.waitAndRetry=Waiting a second before retrying... Checks.missingCheck=Could not find check for id: {} CityDoctorController.exportSuccess=Successfully exported feature CityDoctorController.noDatamodel=Datamodel is null, no checks could be done diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 196b4d8a..bd724568 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -35,12 +35,18 @@ CheckDialog.validationConfiguration=Validierungskonfiguration CheckDialog.xmlReportsOption=XML Berichte erstellen Checker.checkFeature=Pr\u00fcfe Feature: {} Checker.checksFinished=Pr\u00fcfungen beendet +Checker.dbStillUnresponsive=Pr\u00fcfung konnte weiterhin einige CityObjects nicht aus der Datenbank laden! +Checker.dbUnresponsive=Pr\u00fcfung konnte einige CityObjects nicht aus der Datenbank laden! Checker.executeCheck=F\u00fchre Pr\u00fcfung aus: {} Checker.failPdf=Konnte PDF Fehlerbericht nicht schreiben. Checker.failReports=Konnte Pr\u00fcfberichte nicht schreiben. Checker.failXml=Konnte XML Fehlerbericht nicht schreiben. Checker.finishedSchematron=Schematron Pr\u00fcfung abgeschlossen +Checker.futuresCheckFailures=Einige Pr\u00fcfungen sind aufgrund folgender Fehler fehlgeschlagen: Checker.schematronValidation=Starte Schematron Pr\u00fcfung parallel +Checker.shutdownInterrupted=Main-Thread hat einen Interrupt-Befehl während dem ThreadPool shutdown erhalten +Checker.validationInterrupted=Validierung abgebrochen: Pr\u00fcfung hat Interrupt-Befehl erhalten +Checker.waitAndRetry=Wiederhole Pr\u00fcfung nach kurzer Wartezeit ... Checks.missingCheck=Konnte keine Pr\u00fcfung f\u00fcr id {} finden CityDoctorController.exportSuccess=Feature export erfolgreich abgeschlossen CityDoctorController.noDatamodel=Datenmodell ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 38700e18..8fc800b1 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -664,14 +664,13 @@ public class Checker { List> futures = runChecksOnFeatures(exec, cache, features, checkedCount, l); List missedFeatures = getMissedFeatures(futures); if (!missedFeatures.isEmpty()){ - logger.warn("Checker could not load some CityObjects from database!"); - logger.info("Waiting a second before retrying..."); + logger.warn(Localization.getText("Checker.dbUnresponsive")); + logger.info(Localization.getText("Checker.waitAndRetry")); Thread.sleep(1000); futures = runChecksOnFeatures(exec, cache, missedFeatures, checkedCount, l); missedFeatures = getMissedFeatures(futures); if (!missedFeatures.isEmpty()){ - //TODO: Localize String - logger.error("Checker still unable to load some CityObjects from database!"); + logger.error(Localization.getText("Checker.dbStillUnresponsive")); if (logger.isDebugEnabled()){ logger.debug(missedFeatures.toString()); } @@ -683,8 +682,7 @@ public class Checker { } } catch (InterruptedException e){ // No interrupts implemented, so this block should actually never be entered - //TODO: Localize String - logger.error("Validation aborted: Checker got interrupted", e); + logger.error(Localization.getText("Checker.validationInterrupted"), e); Thread.currentThread().interrupt(); } finally { @@ -696,7 +694,7 @@ public class Checker { } catch (InterruptedException e) { Thread.currentThread().interrupt(); - logger.error("Main Thread got interrupted during ThreadPool shutdown"); + logger.error(Localization.getText("Checker.shutdownInterrupted")); } } } @@ -749,7 +747,7 @@ public class Checker { } } if (!errors.isEmpty()) { - logger.error("Encountered check failures due to the following errors:"); + logger.error(Localization.getText("Checker.futuresCheckFailures")); logger.error(errors.toString()); } return missedList; -- GitLab From ec4ac8d6ca163d03fb3a53615181279bdeaaa6e8 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 17 Nov 2025 10:20:38 +0100 Subject: [PATCH 084/118] Style: Add Localizations to Checker --- .../resources/CityDoctorLocalization.properties | 5 +++++ .../resources/CityDoctorLocalization_de.properties | 5 +++++ .../de/hft/stuttgart/citydoctor2/check/Checker.java | 13 +++++-------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 69371abf..9228fa2b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -43,6 +43,11 @@ Checker.failReports=Failed to write reports. Checker.failXml=Failed to write XML Error Report. Checker.finishedSchematron=Finished Schematron validation Checker.futuresCheckFailures=Encountered check failures due to the following errors: +Checker.noCheckSatisfiesRequirement=Could not find any check that satisfies requirement {}, it will not be checked +Checker.schematronCompleted=Schematron completed +Checker.schematronFileNotFound=Schematron file {} could not be found +Checker.schematronInterrupted=Main Thread was interrupted while waiting for Schematron +Checker.schematronStillRunning=Schematron is still running. Waiting for completion, this may take some time Checker.schematronValidation=Schematron-validation thread started Checker.shutdownInterrupted=Main Thread got interrupted during ThreadPool shutdown Checker.validationInterrupted=Validation aborted: Checker got interrupted diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index bd724568..1e6fd3d9 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -43,6 +43,11 @@ Checker.failReports=Konnte Pr\u00fcfberichte nicht schreiben. Checker.failXml=Konnte XML Fehlerbericht nicht schreiben. Checker.finishedSchematron=Schematron Pr\u00fcfung abgeschlossen Checker.futuresCheckFailures=Einige Pr\u00fcfungen sind aufgrund folgender Fehler fehlgeschlagen: +Checker.noCheckSatisfiesRequirement=Keine Pr\u00fcfung erf\u00fcllt die Anforderung {}, Anforderung wird nicht geprüft +Checker.schematronCompleted=Schematron Pr\u00fcfung abgeschlossen +Checker.schematronFileNotFound=Schematron Datei {} konnte nicht gefunden werden +Checker.schematronInterrupted=Main Thread hat Interrupt-Befehl w\u00e4hrend dem Warten auf Schematron Abschluss erhalten! +Checker.schematronStillRunning=Schematron Pr\u00fcfung noch nicht abgeschlossen. Warten auf Abschluss, dies kann einiges an Zeit beanspruchen Checker.schematronValidation=Starte Schematron Pr\u00fcfung parallel Checker.shutdownInterrupted=Main-Thread hat einen Interrupt-Befehl während dem ThreadPool shutdown erhalten Checker.validationInterrupted=Validierung abgebrochen: Pr\u00fcfung hat Interrupt-Befehl erhalten diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 8fc800b1..9a1767c8 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -203,13 +203,12 @@ public class Checker { logger.info(Localization.getText("Checker.checksFinished")); } if (schematronThread.isAlive()) { - //TODO: Localize String - logger.info("Schematron is still running. Waiting for completion, this may take some time"); + logger.info(Localization.getText("Checker.schematronStillRunning")); try{ schematronThread.join(); - logger.info("Schematron completed."); + logger.info(Localization.getText("Checker.schematronCompleted")); } catch (InterruptedException e) { - logger.error("Main Thread was interrupted while waiting for Schematron", e); + logger.error(Localization.getText("Checker.schematronInterrupted"), e); schematronThread.interrupt(); } } @@ -458,8 +457,7 @@ public class Checker { logger.info(Localization.getText("Checker.schematronValidation")); } if (!Files.exists(Path.of(config.getSchematronFilePath()))) { - //TODO: Localize String - logger.warn("Schematron file {} could not be found", config.getSchematronFilePath()); + logger.warn(Localization.getText("Checker.schematronFileNotFound"), config.getSchematronFilePath()); return null; } @@ -579,8 +577,7 @@ public class Checker { for (Entry e : config.getRequirements().entrySet()) { de.hft.stuttgart.citydoctor2.check.Requirement req = Checks.getAvailableRequirements().get(e.getKey()); if (req == null) { - //TODO: Localize String - logger.warn("Could not find any check that satisfies requirement {}, it will not be checked", + logger.warn(Localization.getText("Checker.noCheckSatisfiesRequirement"), e.getKey()); } else { if (e.getValue().isEnabled()) { -- GitLab From 3ba87aecf1c10a83c3ea1714c53371bbd616e25d Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 17 Nov 2025 10:33:39 +0100 Subject: [PATCH 085/118] Style: Add Localizations to LibraryObject --- .../citydoctor2/datastructure/LibraryObject.java | 11 +++-------- .../main/resources/CityDoctorLocalization.properties | 2 ++ .../resources/CityDoctorLocalization_de.properties | 2 ++ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java index 4051504c..b81ca0f9 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java @@ -4,6 +4,7 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.Localization; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -54,31 +55,25 @@ public class LibraryObject extends Geometry { try { geo = getProtoGeometry(CityGmlParser.nestedCityGmlFileParse(path.toString(), config)); } catch (CityGmlParseException e) { - //TODO: Localize Strings - logger.error(String.format( - "Encountered an error while parsing library object %s", path)); + logger.error(Localization.getText("LibraryObject.parsingError"), path); logger.error(e.getStackTrace()); } catch (InvalidGmlFileException e) { logger.error(e.getStackTrace()); } } else { - //TODO: Localize Strings - logger.error(String.format("Implicit geometry references non-existing library object file %s.", path)); + logger.error(Localization.getText("LibraryObject.fileMissing"), path); } return geo; } private static Geometry getProtoGeometry(CityDoctorModel model) throws InvalidGmlFileException, CityGmlParseException { if (model == null) { - //TODO: Localize Strings throw new CityGmlParseException("CityDoctorModel of referenced LibraryObject is null"); } List objects = model.createFeatureStream().toList(); if (objects.isEmpty()) { - //TODO: Localize Strings throw new InvalidGmlFileException("Referenced library-object gml file does not contain any CityGML objects!"); } else if (objects.size() > 1) { - //TODO: Localize Strings throw new InvalidGmlFileException("Referenced library-object gml file contains more than one CityGML object!"); } return objects.get(0).getHighestLodGeometry(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 9228fa2b..4bc92d6c 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -118,6 +118,8 @@ FilterPane.transportation=Transportation FilterPane.vegetation=Vegetation FilterPane.water=Water GeometryMapper.emptyPolygon=Found polygon without exterior ring, ignoring +LibraryObject.fileMissing=Implicit geometry references non-existing library object file {} +LibraryObject.parsingError=Encountered an error while parsing library object {} MainToolBar.culling=Enable/Disable Culling MainToolBar.executeChecks=Execute Checks MainToolBar.hideRoof=Show/Hide roof BoundarySurfaces diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 1e6fd3d9..14468c5f 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -118,6 +118,8 @@ FilterPane.transportation=Transport FilterPane.vegetation=Vegetation FilterPane.water=Wasser GeometryMapper.emptyPolygon=Polygon ohne externen Ring gefunden, ignoriere +LibraryObject.fileMissing=Implicit Geometry verweist auf nicht vorhandene Library-Object Datei {} +LibraryObject.parsingError=Library-Object {} konnte aufgrund eines Fehlers nicht geparst werden MainToolBar.culling=Aktiviere/Deaktiviere Entfernen der R\u00fcckseiten MainToolBar.executeChecks=F\u00fchre Pr\u00fcfungen aus MainToolBar.hideRoof=Zeige/Verstecke Dach BoundarySurfaces -- GitLab From eaae85d9e2bb5ff82de27229bc6c1b7d2c48f967 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 17 Nov 2025 15:59:07 +0100 Subject: [PATCH 086/118] Refactor: Delete useless class --- .../check/CheckerThreadsSynchronizer.java | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckerThreadsSynchronizer.java diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckerThreadsSynchronizer.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckerThreadsSynchronizer.java deleted file mode 100644 index e7aea60d..00000000 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckerThreadsSynchronizer.java +++ /dev/null @@ -1,66 +0,0 @@ -package de.hft.stuttgart.citydoctor2.check; - -import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; -import de.hft.stuttgart.citydoctor2.datastructure.CityObject; -import de.hft.stuttgart.citydoctor2.datastructure.GmlId; -import de.hft.stuttgart.citydoctor2.parser.ProgressListener; - -import java.util.EmptyStackException; -import java.util.Stack; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; - -public class CheckerThreadsSynchronizer { - - - private AtomicInteger checkedCount = new AtomicInteger(0); - private final float featureCount; - private CityDoctorModel model; - private ProgressListener listener; - private ReentrantLock lock = new ReentrantLock(); - private Stack ids; - - public CheckerThreadsSynchronizer(CityDoctorModel model, ProgressListener listener) { - this.model = model; - this.listener = listener; - featureCount = model.getNumberOfFeatures(); - ids = new Stack<>(); - ids.addAll(model.getFeatureIds()); - } - - public CityObject getNext() { -// lock.lock(); -// try { - if (!ids.isEmpty()){ - return model.getCache().get(ids.pop()); - } - return null; -// } finally { -// lock.unlock(); -// } - }; - - public void updateFeature(CityObject feature) { - model.getCache().put(feature); - } - - public boolean hasNext(){ -// lock.lock(); - try { - ids.peek(); - return true; - } catch (EmptyStackException e) { - return false; - } finally { -// lock.unlock(); - } - } - - public void updateCheckedCount(){ - checkedCount.getAndIncrement(); - if (listener!=null){ - listener.updateProgress(checkedCount.get() / featureCount); - } - } - -} -- GitLab From 07961c2314a2ef663e29c7a2b486b3af5b2f6a04 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 18 Nov 2025 15:15:30 +0100 Subject: [PATCH 087/118] Style: Make progressbar indeterminate during Schematron --- .../java/de/hft/stuttgart/citydoctor2/check/Checker.java | 3 +++ .../de/hft/stuttgart/citydoctor2/gui/CheckDialog.java | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 9a1767c8..5f660ad1 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -204,6 +204,9 @@ public class Checker { } if (schematronThread.isAlive()) { logger.info(Localization.getText("Checker.schematronStillRunning")); + if(l !=null){ + l.updateProgress(-0.5f); + } try{ schematronThread.join(); logger.info(Localization.getText("Checker.schematronCompleted")); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java index 098bcaeb..fdfd46aa 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import de.hft.stuttgart.citydoctor2.parser.ProgressListener; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.scene.control.Alert; @@ -450,7 +451,13 @@ public class CheckDialog { String startingChecks = Localization.getText("CheckDialog.startingChecks"); logger.info(startingChecks); } - controller.startChecks(config, progress::setProgress); + ProgressListener l = new ProgressListener() { + @Override + public void updateProgress(float state) { + Platform.runLater(() -> progress.setProgress(state)); + } + }; + controller.startChecks(config, l); if (logger.isInfoEnabled()) { String checksDone = Localization.getText("CheckDialog.checksDone"); -- GitLab From 565a316958e4d676dc1e9b013979df026f89a7b2 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 18 Nov 2025 15:17:24 +0100 Subject: [PATCH 088/118] Fix: Turn autocommit back on after batched commit --- .../de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 78be4f78..24ed358f 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -154,6 +154,8 @@ public class DatabaseHandler { logger.error(Localization.getText("DatabaseHandler.batchCommitFailure"), id); logger.error(e.getMessage()); return false; + } finally{ + con.setAutoCommit(true); } } catch (SQLException e){ logger.error(Localization.getText("DatabaseHandler.connectionFailure")); -- GitLab From ed20282cd21271fc0cd546c65c61c1eafedfc297 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 18 Nov 2025 15:17:42 +0100 Subject: [PATCH 089/118] Refactor: Make field final --- .../de/hft/stuttgart/citydoctor2/database/FeatureCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index f7c92392..99d8897c 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -34,7 +34,7 @@ public class FeatureCache implements CityObjectCache{ private final LoadingCache cache; private final DatabaseHandler handler; - private AtomicInteger putsCounter = new AtomicInteger(0); + private final AtomicInteger putsCounter = new AtomicInteger(0); private boolean parsingFinished = false; FeatureCache() { -- GitLab From 239d97dee115a1ad8b7935459320929c5119532d Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 25 Nov 2025 15:30:08 +0100 Subject: [PATCH 090/118] Style: Log total time needed for check execution --- .../citydoctor2/utils/TimeFormatter.java | 62 +++++++++++++++++++ .../CityDoctorLocalization.properties | 9 ++- .../CityDoctorLocalization_de.properties | 9 ++- .../stuttgart/citydoctor2/check/Checker.java | 34 +++++----- 4 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/TimeFormatter.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/TimeFormatter.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/TimeFormatter.java new file mode 100644 index 00000000..509bdfaa --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/utils/TimeFormatter.java @@ -0,0 +1,62 @@ +package de.hft.stuttgart.citydoctor2.utils; + +import java.text.ChoiceFormat; + +public final class TimeFormatter { + + private TimeFormatter(){} + + private static final String LESS_THAN_A_SECOND = + Localization.getText("TimeFormatter.lessThanASecond"); + + private static final ChoiceFormat SECONDS = + new ChoiceFormat( + new double[]{1, 2}, + new String[]{Localization.getText("TimeFormatter.oneSecond"), + Localization.getText("TimeFormatter.seconds")} + ); + + private static final ChoiceFormat MINUTES = + new ChoiceFormat( + new double[]{1, 2}, + new String[]{Localization.getText("TimeFormatter.oneMinute"), + Localization.getText("TimeFormatter.minutes")} + ); + + private static final ChoiceFormat HOURS = + new ChoiceFormat( + new double[] {1, 2}, + new String[]{Localization.getText("TimeFormatter.oneHour"), + Localization.getText("TimeFormatter.hours")} + ); + + /** + * Formats a time measurement in milliseconds as a localized String representation. + * + *

+ * The String representation will return the time as a rounded down integer of the biggest fitting TimeUnit + * (seconds, minutes or hours), returning "< 1 second" for times under a second. + *

+ * @param millis + * @return A localized String representation of the timespan + */ + public static String format(long millis) { + if (millis < 1000) { + return LESS_THAN_A_SECOND; + }else if (millis < 60_000) { + long seconds = millis / 1000; + String pattern = SECONDS.format(seconds); + return pattern.replace("{0}", Long.toString(seconds)); + }else if (millis < 3_600_000){ + long minutes = millis / 60_000; + String pattern = MINUTES.format(minutes); + return pattern.replace("{0}", Long.toString(minutes)); + } else { + long hours = millis / 3_600_000; + String pattern = HOURS.format(hours); + return pattern.replace("{0}", Long.toString(hours)); + } + + + } +} \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 4bc92d6c..59ecf4ef 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -34,7 +34,7 @@ CheckDialog.title=Check Configuration CheckDialog.validationConfiguration=Validation Configuration CheckDialog.xmlReportsOption=Create XML reports Checker.checkFeature=Checking feature: {} -Checker.checksFinished=Finished executing checks +Checker.checksFinished=Finished executing checks. Total time: {0} Checker.dbStillUnresponsive=Checker still unable to load some CityObjects from database! Checker.dbUnresponsive=Checker could not load some CityObjects from database! Checker.executeCheck=Executing check: {} @@ -180,6 +180,13 @@ OpenFileDialog.select=Select CityGML file OpenFileDialog.selectBtn=Select OpenFileDialog.settingsPane=Parser Preferences OpenFileDialog.xmlValidationLabel=Use XML Validation +TimeFormatter.hours={0} hours +TimeFormatter.lessThanASecond=< 1 second +TimeFormatter.minutes={0} minutes +TimeFormatter.oneHour=1 hour +TimeFormatter.oneMinute=1 minute +TimeFormatter.oneSecond=1 second +TimeFormatter.seconds={0} seconds Unit.Degree=Degree Unit.Radian=Radian ValidationConfiguration.missingSchematron={} is not an existing file, disabling schematron check diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 14468c5f..7a5ae934 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -34,7 +34,7 @@ CheckDialog.title=Pr\u00fcfungskonfiguration CheckDialog.validationConfiguration=Validierungskonfiguration CheckDialog.xmlReportsOption=XML Berichte erstellen Checker.checkFeature=Pr\u00fcfe Feature: {} -Checker.checksFinished=Pr\u00fcfungen beendet +Checker.checksFinished=Durchf\u00fchrung der Pr\u00fcfungen abgeschlossen. Gesamtzeit: {0} Checker.dbStillUnresponsive=Pr\u00fcfung konnte weiterhin einige CityObjects nicht aus der Datenbank laden! Checker.dbUnresponsive=Pr\u00fcfung konnte einige CityObjects nicht aus der Datenbank laden! Checker.executeCheck=F\u00fchre Pr\u00fcfung aus: {} @@ -180,6 +180,13 @@ OpenFileDialog.select=W\u00e4hle CityGML Datei aus OpenFileDialog.selectBtn=Ausw\u00e4hlen OpenFileDialog.settingsPane=Parser Einstellungen OpenFileDialog.xmlValidationLabel=F\u00fchre XML-Validierung aus +TimeFormatter.hours={0} Stunden +TimeFormatter.lessThanASecond=< 1 Sekunde +TimeFormatter.minutes={0} Minuten +TimeFormatter.oneHour=1 Stunde +TimeFormatter.oneMinute=1 Minute +TimeFormatter.oneSecond=1 Sekunde +TimeFormatter.seconds={0} Sekunden Unit.Degree=Grad Unit.Radian=Radiant ValidationConfiguration.missingSchematron={} existiert nicht, deaktiviere Schematron Pr\u00fcfung diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 5f660ad1..bd8cc2aa 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -30,6 +30,7 @@ import de.hft.stuttgart.citydoctor2.checks.SvrlContentHandler; import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener; import de.hft.stuttgart.citydoctor2.database.CacheManager; import de.hft.stuttgart.citydoctor2.database.CityObjectCache; +import de.hft.stuttgart.citydoctor2.database.FeatureCache; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; @@ -46,6 +47,7 @@ import de.hft.stuttgart.citydoctor2.reporting.XmlValidationReporter; import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfReporter; import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfStreamReporter; import de.hft.stuttgart.citydoctor2.utils.Localization; +import de.hft.stuttgart.citydoctor2.utils.TimeFormatter; import de.hft.stuttgart.quality.model.enums.RequirementId; import de.hft.stuttgart.quality.model.enums.TopLevelFeatureType; import de.hft.stuttgart.quality.model.properties.CheckingProperty; @@ -79,6 +81,8 @@ import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.text.ChoiceFormat; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -199,9 +203,6 @@ public class Checker { }); schematronThread.start(); checkCityModel(model, l); - if (logger.isInfoEnabled()) { - logger.info(Localization.getText("Checker.checksFinished")); - } if (schematronThread.isAlive()) { logger.info(Localization.getText("Checker.schematronStillRunning")); if(l !=null){ @@ -221,14 +222,13 @@ public class Checker { private void handleSchematronResults(SvrlContentHandler handler) { model.addGlobalErrors(handler.getGeneralErrors()); - Map featureMap = new HashMap<>(); - model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f)); + FeatureCache cache = CacheManager.getFeatureCache(); handler.getFeatureErrors().forEach((k, v) -> { if (k.trim().isEmpty()) { // missing gml id, ignore? return; } - CityObject co = featureMap.get(k); + CityObject co = cache.get(new GmlId(k)); if (co == null) { // gml id reported by schematron was not found, add to general errors for (SchematronError se : v) { @@ -663,22 +663,20 @@ public class Checker { List> futures = runChecksOnFeatures(exec, cache, features, checkedCount, l); List missedFeatures = getMissedFeatures(futures); + if (!missedFeatures.isEmpty()){ - logger.warn(Localization.getText("Checker.dbUnresponsive")); - logger.info(Localization.getText("Checker.waitAndRetry")); - Thread.sleep(1000); - futures = runChecksOnFeatures(exec, cache, missedFeatures, checkedCount, l); - missedFeatures = getMissedFeatures(futures); - if (!missedFeatures.isEmpty()){ - logger.error(Localization.getText("Checker.dbStillUnresponsive")); - if (logger.isDebugEnabled()){ - logger.debug(missedFeatures.toString()); - } + logger.error(Localization.getText("Checker.dbUnresponsive")); + if (logger.isDebugEnabled()){ + logger.debug(missedFeatures.toString()); } } long endTime = System.nanoTime(); - if (logger.isDebugEnabled()){ - logger.debug("Checks finished in {} ms", (endTime - startTime) / 1_000_000); + if (logger.isInfoEnabled()){ + long totalTime = (endTime - startTime) / 1_000_000; // Convert ns to ms + String messageString = Localization.getText("Checker.checksFinished"); + String timeString = TimeFormatter.format(totalTime); + messageString = messageString.replace("{0}", timeString); + logger.info(messageString); } } catch (InterruptedException e){ // No interrupts implemented, so this block should actually never be entered -- GitLab From 96c59f978f99810ea4b6199a113f0298516e0113 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 25 Nov 2025 16:46:34 +0100 Subject: [PATCH 091/118] Style: Improve german Localization --- .../CityDoctorLocalization_de.properties | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 7a5ae934..932c673e 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -20,10 +20,10 @@ CheckDialog.outputPathBtn=Ausw\u00e4hlen CheckDialog.outputPathLabel=Ausgabeordner (Pflichtfeld): CheckDialog.parameterUnit=Einheit CheckDialog.parameterValue=Wert -CheckDialog.pdfReportsOption=PDF Berichte erstellen +CheckDialog.pdfReportsOption=PDF-Berichte erstellen CheckDialog.schematronChooserTitle=W\u00e4hle Schematron Datei ... CheckDialog.schematronFileLabel=Schematron Datei: -CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, Schematron wird \u00fcbersprungen +CheckDialog.schematronFileNotExisting=Schematron Datei existiert nicht, Schematron-Pr\u00fcfung wird \u00fcbersprungen CheckDialog.schematronFiles=Schematron Dateien CheckDialog.selectBtn=Ausw\u00e4hlen CheckDialog.semanticChecksLabel=Semantische Pr\u00fcfungen @@ -32,31 +32,31 @@ CheckDialog.streamCheckLabel=Stadtmodelle werden nicht in den Arbeitsspeicher ge CheckDialog.streamCheckOption=Niedrigen Arbeitsspeicherverbrauchsmodus aktivieren CheckDialog.title=Pr\u00fcfungskonfiguration CheckDialog.validationConfiguration=Validierungskonfiguration -CheckDialog.xmlReportsOption=XML Berichte erstellen +CheckDialog.xmlReportsOption=XML-Berichte erstellen Checker.checkFeature=Pr\u00fcfe Feature: {} Checker.checksFinished=Durchf\u00fchrung der Pr\u00fcfungen abgeschlossen. Gesamtzeit: {0} Checker.dbStillUnresponsive=Pr\u00fcfung konnte weiterhin einige CityObjects nicht aus der Datenbank laden! Checker.dbUnresponsive=Pr\u00fcfung konnte einige CityObjects nicht aus der Datenbank laden! Checker.executeCheck=F\u00fchre Pr\u00fcfung aus: {} -Checker.failPdf=Konnte PDF Fehlerbericht nicht schreiben. +Checker.failPdf=Konnte PDF-Fehlerbericht nicht schreiben. Checker.failReports=Konnte Pr\u00fcfberichte nicht schreiben. -Checker.failXml=Konnte XML Fehlerbericht nicht schreiben. -Checker.finishedSchematron=Schematron Pr\u00fcfung abgeschlossen +Checker.failXml=Konnte XML-Fehlerbericht nicht schreiben. +Checker.finishedSchematron=Schematron-Pr\u00fcfung abgeschlossen Checker.futuresCheckFailures=Einige Pr\u00fcfungen sind aufgrund folgender Fehler fehlgeschlagen: Checker.noCheckSatisfiesRequirement=Keine Pr\u00fcfung erf\u00fcllt die Anforderung {}, Anforderung wird nicht geprüft -Checker.schematronCompleted=Schematron Pr\u00fcfung abgeschlossen +Checker.schematronCompleted=Schematron-Pr\u00fcfung abgeschlossen Checker.schematronFileNotFound=Schematron Datei {} konnte nicht gefunden werden Checker.schematronInterrupted=Main Thread hat Interrupt-Befehl w\u00e4hrend dem Warten auf Schematron Abschluss erhalten! -Checker.schematronStillRunning=Schematron Pr\u00fcfung noch nicht abgeschlossen. Warten auf Abschluss, dies kann einiges an Zeit beanspruchen -Checker.schematronValidation=Starte Schematron Pr\u00fcfung parallel +Checker.schematronStillRunning=Schematron-Pr\u00fcfung ist noch nicht abgeschlossen. Warten auf Abschluss der Pr\u00fcung, dies kann einiges an Zeit beanspruchen +Checker.schematronValidation=Schematron-Pr\u00fcfung gestartet Checker.shutdownInterrupted=Main-Thread hat einen Interrupt-Befehl während dem ThreadPool shutdown erhalten Checker.validationInterrupted=Validierung abgebrochen: Pr\u00fcfung hat Interrupt-Befehl erhalten Checker.waitAndRetry=Wiederhole Pr\u00fcfung nach kurzer Wartezeit ... Checks.missingCheck=Konnte keine Pr\u00fcfung f\u00fcr id {} finden -CityDoctorController.exportSuccess=Feature export erfolgreich abgeschlossen -CityDoctorController.noDatamodel=Datenmodell ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden -CityDoctorController.noSourceFile=Quelldatei ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden -CityDoctorController.noZipFile=ZIP-Archiv ist null, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden +CityDoctorController.exportSuccess=Exportieren des Features erfolgreich abgeschlossen +CityDoctorController.noDatamodel=Datenmodell ist NULL, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden +CityDoctorController.noSourceFile=Quelldatei ist NULL, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden +CityDoctorController.noZipFile=ZIP-Archiv ist NULL, keine Pr\u00fcfungen konnten ausgef\u00fchrt werden CityDoctorController.saveZipArchiveReports=Validierungsberichte erfolgreich exportiert! CityDoctorValidation.Version=${versionString} CityDoctorValidation.inputFileNotExisting=Eingabedatei '{}' existiert nicht. @@ -73,32 +73,32 @@ CityDoctorValidation.wrongInputFileFormat=Eingabedatei '{}' ist keine .gml oder CityGmlParser.chunkReadFailed=Konnte Datei nicht in St\u00fccken lesen, versuche komplett zu lesen CityGmlParser.convertCrsToUtmZoneN=Konvertiere Koordinatensystem zu UTM Zone {}N CityGmlParser.convertCrsToUtmZoneS=Konvertiere Koordinatensystem zu UTM Zone {}S -CityGmlParser.creating4jContextFailed=Citygml4j Kontext konnte nicht erstellt werden -CityGmlParser.creatingAdePluginsFailed=ADE Plugin f\u00fcr citygml4j konnte nicht erstellt werden +CityGmlParser.creating4jContextFailed=Erstellen des Citygml4j Kontext fehlgeschlagen +CityGmlParser.creatingAdePluginsFailed=Erstellen des ADE Plugins f\u00fcr citygml4j fehlgeschlagen CityGmlParser.errorReadingGmlFile=Fehler beim lesen der CityGML Datei\n{} CityGmlParser.errorWritingGmlFile=Fehler beim schreiben der CityGML Datei\n{} -CityGmlParser.failedEPSGParse=Lesen des EPSG Codes fehlgeschlagen, greife auf metrisches System zur\u00fcck +CityGmlParser.failedEPSGParse=Lesen des EPSG-Codes fehlgeschlagen, greife auf metrisches System zur\u00fcck CityGmlParser.missingCityModel=Datei enth\u00e4lt kein CityModel! Tempor\u00e4res CityModel wird erstellt. -CityGmlParser.missingEPSGCode=Kein EPSG Code gefunden, nehme metrisches System an +CityGmlParser.missingEPSGCode=Kein EPSG-Code wurde gefunden! Gehe von metrischem System aus CityGmlParser.noConversionNeeded=Koordinatensystem in Metern, keine Konvertierung notwendig CityGmlParser.notValidGmlFile=Dies ist keine korrekte CityGML Datei CityGmlParser.parsedObjects=Modell mit {} Objekten gelesen -CityGmlWriterUtils.unknownADEError= Fehler {} kann nicht zu einem ADE Fehler konvertiert werden. Fehler wird nicht ausgegeben +CityGmlWriterUtils.unknownADEError= Fehler {} kann nicht zu einem ADE-Fehler konvertiert werden. Fehler wird nicht ausgegeben CityGmlZipArchive.generalException=Unerwarteter Fehler beim Einlesen des ZIP-Archivs CityGmlZipArchive.ioError=Zugriff auf ZIP-Datei fehlgeschlagen CityGmlZipArchive.malformedZipFile=Einlesen des ZIP-Archivs abgebrochen, ZIP-Datei ist fehlerhaft! -DatabaseHandler.batchCommitFailure=Batch-commit abgebrochen: Kritischer Fehler beim Versuch CityObject {} in die Datenbank zu schreiben! +DatabaseHandler.batchCommitFailure=Batch-commit abgebrochen: Auftreten eines kritischen Fehlers beim Versuch CityObject {} in die Datenbank zu schreiben! DatabaseHandler.connectionFailure=Verbindung zur eingebetteten Datenbank konnte nicht hergestellt werden! DatabaseHandler.fetchErrorIdsFailure=Abfragen der Liste von CityObjects mit Fehlern ist fehlgeschlagen! -DatabaseHandler.getAllCityObjectsFailure=Abfrage nach allen CityObjects ist fehlgeschlagen! -DatabaseHandler.getBBoxIntersectFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der BBox-Intersections von CityObject {} fehlgeschlagen! -DatabaseHandler.getBufferIntersectFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der Buffer-Intersections von CityObject {} fehlgeschlagen! +DatabaseHandler.getAllCityObjectsFailure=Abfragen der Liste aller CityObjects ist fehlgeschlagen! +DatabaseHandler.getBBoxIntersectFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der BoundingBox-\u00dcberschneidungen von CityObject {} fehlgeschlagen! +DatabaseHandler.getBufferIntersectFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der Buffer-\u00dcberschneidungen von CityObject {} fehlgeschlagen! DatabaseHandler.getFeatureCountFailure=Verbindung zur Datenbank konnte nicht hergestellt werden, abfragen der Anzahl an CityObjects fehlgeschlagen! -DatabaseHandler.marshallingFailure=Schreiben von CityObject {} in die Datenbank fehlgeschlagen! -DatabaseHandler.serializationFailure=Serialisierung von CityObject {} fehlgeschlagen! -DatabaseHandler.setupFailure=FATAL ERROR: Erstellen der eingebetteten Datenbank fehlgeschlagen! -DatabaseHandler.tableFailure=FATAL ERROR: Erstellen der features Tabelle fehlgeschlagen! -DatabaseHandler.unmarshallingFailure=Laden von CityObject {} aus der Datenbank fehlgeschlagen! +DatabaseHandler.marshallingFailure=Schreiben des CityObject {} in die Datenbank fehlgeschlagen! +DatabaseHandler.serializationFailure=Serialisierung des CityObject {} fehlgeschlagen! +DatabaseHandler.setupFailure=KRITISCHER FEHLER: Erstellen der eingebetteten Datenbank fehlgeschlagen! +DatabaseHandler.tableFailure=KRITISCHER FEHLER: Erstellen der "features" Tabelle fehlgeschlagen! +DatabaseHandler.unmarshallingFailure=Laden des CityObject {} aus der Datenbank fehlgeschlagen! DistanceError.distanceFromPlane=Abstand von der Ebene ErrorItemVisitor.childId=Kind ID ErrorItemVisitor.component=Komponente @@ -110,23 +110,23 @@ ErrorItemVisitor.nameOfAttribute=Name des Attributs ErrorItemVisitor.pointTouchesEdge=Punkt ber\u00fchrt Kante ErrorItemVisitor.triangles=Dreiecke ExceptionDialog.stacktrace=Der Stacktrace des Fehlers war: -FeatureMapper.polygonUnreferenced=Polygon {} ist referenziert, konnte aber nicht gefunden werden +FeatureMapper.polygonUnreferenced=Polygon {} wurde referenziert, konnte aber nicht gefunden werden! FilterPane.bridges=Br\u00fccken FilterPane.buildings=Geb\u00e4ude FilterPane.landUse=Landnutzung FilterPane.transportation=Transport FilterPane.vegetation=Vegetation FilterPane.water=Wasser -GeometryMapper.emptyPolygon=Polygon ohne externen Ring gefunden, ignoriere -LibraryObject.fileMissing=Implicit Geometry verweist auf nicht vorhandene Library-Object Datei {} -LibraryObject.parsingError=Library-Object {} konnte aufgrund eines Fehlers nicht geparst werden -MainToolBar.culling=Aktiviere/Deaktiviere Entfernen der R\u00fcckseiten +GeometryMapper.emptyPolygon=Polygon ohne externen Ring gefunden. Polygon wird ignoriert +LibraryObject.fileMissing=Die ImplicitGeometry eines Features verweist auf eine nicht vorhandene LibraryObject-Datei {} +LibraryObject.parsingError=LibraryObject-Datei {} konnte aufgrund eines Fehlers nicht ausgelesen werden +MainToolBar.culling=Aktiviere/Deaktiviere Entfernen der Polygon-R\u00fcckseiten MainToolBar.executeChecks=F\u00fchre Pr\u00fcfungen aus -MainToolBar.hideRoof=Zeige/Verstecke Dach BoundarySurfaces -MainToolBar.resetCamera=Ansicht zur\u00fccksetzen -MainToolBar.showWorld=Gesamtes Stadtmodell anzeigen -MainToolBar.wireframe=Zeige/Verstecke Gitternetz -MainToolBar.writeReports=Schreibe Reports +MainToolBar.hideRoof=Zeige/Verstecke Dach-BoundarySurfaces +MainToolBar.resetCamera=Kamera-Ansicht zur\u00fccksetzen +MainToolBar.showWorld=Zeige gesamtes Stadtmodell an +MainToolBar.wireframe=Aktiviere/Deaktiviere Gitternetz-Modus +MainToolBar.writeReports=Schreibe Berichte MainToolBar.zip=Zip-eintrag Auswahlfenster \u00f6ffnen MainWindow.all=Alle MainWindow.attributeTab=Attribute @@ -142,8 +142,8 @@ MainWindow.edgesTab=Kanten MainWindow.errorsTab=Fehler MainWindow.export=Feature Exportieren MainWindow.finishedLoading=Fertig geladen -MainWindow.finishedPdf=Pdf Report abgeschlossen -MainWindow.finishedXml=Xml Report abgeschlossen +MainWindow.finishedPdf=Pdf-Bericht abgeschlossen +MainWindow.finishedXml=Xml-Bericht abgeschlossen MainWindow.focusCityObject=Objekt fokussieren MainWindow.globalErrorsTab=Globale Fehler MainWindow.infoTopLevelCopyID=GML-ID kann nur von Featureobjekt basis kopiert werden! @@ -168,14 +168,14 @@ MainWindow.viewLabel=Ansicht MainWindow.warnCopyIdFailed=Kopieren der ID fehlgeschlagen! MainWindow.waterTab=Gew\u00e4sser MainWindow.withErrors=Mit Fehlern -MainWindow.writePdf=Schreibe Pdf Report -MainWindow.writeXml=Schreibe Xml Report +MainWindow.writePdf=Schreibe Pdf-Bericht +MainWindow.writeXml=Schreibe Xml-Bericht OpenFileDialog.cancelBtn=Abbrechen OpenFileDialog.fileLabel=Datei: OpenFileDialog.loadBtn=Laden OpenFileDialog.loadFailed=Konnte CityGML Datei nicht laden OpenFileDialog.lowMemoryLabel=Reduzierter Speicherverbrauchsmodus -OpenFileDialog.roundingPlacesLabel=NumberOfRoundingPlaces +OpenFileDialog.roundingPlacesLabel=Anzahl der Rundungsstellen OpenFileDialog.select=W\u00e4hle CityGML Datei aus OpenFileDialog.selectBtn=Ausw\u00e4hlen OpenFileDialog.settingsPane=Parser Einstellungen -- GitLab From 6310cfc39b863565ef84fe2e629ce481e6335198 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Wed, 26 Nov 2025 10:49:33 +0100 Subject: [PATCH 092/118] Fix: Force '.' as decimal separator for BBox-WKT --- .../hft/stuttgart/citydoctor2/datastructure/BoundingBox.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java index 2d37f4af..bf5ac326 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundingBox.java @@ -24,6 +24,7 @@ import de.hft.stuttgart.citydoctor2.utils.visitors.MinMaxExtentVisitor; import java.util.Collection; import java.util.List; +import java.util.Locale; /** * An axis aligned bounding box represented by its two corners @@ -201,7 +202,7 @@ public class BoundingBox { double maxX = Math.max(x1, x2); double maxY = Math.max(y1, y2); - return String.format("POLYGON((%.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f))", + return String.format(Locale.ROOT,"POLYGON((%.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f))", minX, minY, maxX, minY, maxX, maxY, -- GitLab From fe9cb4bb4aadb97301c47c6c2b3c051835b40092 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 27 Nov 2025 09:39:35 +0100 Subject: [PATCH 093/118] Refactor: Remove obsolete function --- .../citydoctor2/database/CacheManager.java | 4 ---- .../citydoctor2/database/DatabaseHandler.java | 1 - .../citydoctor2/database/FeatureCache.java | 4 ---- .../citydoctor2/datastructure/CityDoctorModel.java | 13 ------------- 4 files changed, 22 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java index fd104340..56f471b4 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -52,8 +52,4 @@ public class CacheManager { return new UnconnectedCache(); } - - public static void preCacheFeatures(List features) { - cache.preCacheFeatures(features); - } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java index 24ed358f..9c9b4e38 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java @@ -14,7 +14,6 @@ import org.apache.commons.lang3.SerializationUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.h2gis.functions.factory.H2GISDBFactory; -import org.h2gis.utilities.wrapper.ConnectionWrapper; import org.h2gis.utilities.wrapper.DataSourceWrapper; import java.io.ByteArrayInputStream; diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index 99d8897c..4011b088 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -89,10 +89,6 @@ public class FeatureCache implements CityObjectCache{ - public void preCacheFeatures(List ids) { - cache.getAll(ids); - } - @Override public void replace(GmlId id, CityObject cityObject) { cache.invalidate(id); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index f06e2672..b2b353bc 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -199,19 +199,6 @@ public class CityDoctorModel { } } - public void preCacheShownFeatures(){ - List features = new ArrayList<>(); - features.addAll(buildings.subList(0,Math.min(buildings.size(),200))); - features.addAll(vegetation.subList(0,Math.min(vegetation.size(),200))); - features.addAll(bridges.subList(0,Math.min(bridges.size(),200))); - features.addAll(land.subList(0,Math.min(land.size(),200))); - features.addAll(roads.subList(0,Math.min(roads.size(),200))); - features.addAll(tunnels.subList(0,Math.min(tunnels.size(),200))); - features.addAll(water.subList(0,Math.min(water.size(),200))); - features.addAll(cityfurniture.subList(0,Math.min(cityfurniture.size(),200))); - features.addAll(genericObjects.subList(0,Math.min(genericObjects.size(),200))); - CacheManager.preCacheFeatures(features); - } private void exportAsGML(String file) throws CityDoctorWriteException { CityGMLWriterUtils.writeCityModel(file, this); -- GitLab From cee896c7b321c0f124d3b4874d67b79647ee5280 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 27 Nov 2025 16:03:31 +0100 Subject: [PATCH 094/118] Refactor: Rename class --- ...andler.java => EmbeddedDatabaseHandler.java} | 17 +++++++++++++---- .../citydoctor2/database/FeatureCache.java | 4 ++-- .../database/DataBaseHandlerTest.java | 4 ++-- .../citydoctor2/database/FeatureCacheTest.java | 2 +- .../stuttgart/citydoctor2/gui/MainWindow.java | 1 + 5 files changed, 19 insertions(+), 9 deletions(-) rename CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/{DatabaseHandler.java => EmbeddedDatabaseHandler.java} (94%) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java similarity index 94% rename from CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java rename to CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java index 9c9b4e38..2a283cdf 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java @@ -30,13 +30,13 @@ import java.util.Map; /** * Utility class for setup and access of the embedded database. */ -public class DatabaseHandler { +public class EmbeddedDatabaseHandler { - private static final Logger logger = LogManager.getLogger(DatabaseHandler.class); + private static final Logger logger = LogManager.getLogger(EmbeddedDatabaseHandler.class); private static final String DB_NAME = "/database/cd_db"; private DataSourceWrapper dataSource; - public DatabaseHandler(){ + public EmbeddedDatabaseHandler(){ try { String jdbcurl = H2GISDBFactory.createDataSource(DB_NAME, true, ";AUTO_SERVER=TRUE").getConnection().getMetaData().getURL(); @@ -88,7 +88,7 @@ public class DatabaseHandler { } /** - * Saves a CityObject to the embedded database, updating an existing entry if it exists. + * Saves a CityObject to the embedded database, updating the entry if it already exists. * @param co the CityObject */ public void marshallCityObject(CityObject co) { @@ -119,6 +119,11 @@ public class DatabaseHandler { } } + /** + * Tries to save all CityObjects in a Map to the embedded database as a batched statement, updating already existing entries. + * @param map a Map containing the CityObjects + * @return true if the batched statement was successfully created and commited, false otherwise + */ public boolean tryMarshallCityObjectMap(Map map) { // Id String for error logging String id = ""; @@ -199,6 +204,10 @@ public class DatabaseHandler { return null; } + /** + * Retrieves a List of GmlIds of CityObjects that contain any {@link de.hft.stuttgart.citydoctor2.check.CheckError CheckError}. + * @return a List of GmlIds of CityObjects containing any CheckError + */ public List getIdsOfAllCityObjectsWithErrors(){ List ids = new ArrayList<>(); try (Connection con = dataSource.getConnection()){ diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index 4011b088..7a779649 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -33,12 +33,12 @@ public class FeatureCache implements CityObjectCache{ private static final Logger logger = LogManager.getLogger(FeatureCache.class); private final LoadingCache cache; - private final DatabaseHandler handler; + private final EmbeddedDatabaseHandler handler; private final AtomicInteger putsCounter = new AtomicInteger(0); private boolean parsingFinished = false; FeatureCache() { - handler = new DatabaseHandler(); + handler = new EmbeddedDatabaseHandler(); cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> { if (value != null && cause.wasEvicted()) { handler.marshallCityObject(value); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index fe85052a..1ae85782 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -32,12 +32,12 @@ import static org.junit.Assert.fail; public class DataBaseHandlerTest { - private DatabaseHandler handler; + private EmbeddedDatabaseHandler handler; @Before public void resetDatabase() { try { - handler = new DatabaseHandler(); + handler = new EmbeddedDatabaseHandler(); handler.setupFeaturesTable(); } catch (Exception e) { fail(e.getMessage()); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java index 902ca8f8..ba690ffe 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java @@ -28,7 +28,7 @@ public class FeatureCacheTest { @Test public void testGet(){ FeatureCache cache = new FeatureCache(); - DatabaseHandler handler = new DatabaseHandler(); + EmbeddedDatabaseHandler handler = new EmbeddedDatabaseHandler(); Building b = new Building(); b.setGmlId(new GmlId("building")); b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index eabe443f..a578de92 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -453,6 +453,7 @@ public class MainWindow extends Application { }); } + @SuppressWarnings("java:S2629") private void checkForStartupLoading() { if (loadFileAtStartup) { logger.info(Localization.getText("MainWindow.loadGivenFile")); -- GitLab From cc30db8ae3e7b296452854bc42dc48bf8c63bbf7 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 27 Nov 2025 16:04:16 +0100 Subject: [PATCH 095/118] Refactor: Remove obsolete function --- .../database/EmbeddedDatabaseHandler.java | 13 ---------- .../database/DataBaseHandlerTest.java | 24 ------------------- 2 files changed, 37 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java index 2a283cdf..1645d61d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java @@ -224,19 +224,6 @@ public class EmbeddedDatabaseHandler { return ids; } - public List getAllCityObjects() { - List cos = new ArrayList<>(); - try (Connection con = dataSource.getConnection()) { - try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data FROM features")) { - ResultSet rs = ps.executeQuery(); - cos.addAll(retrieveCityObjects(rs)); - } - } catch (SQLException e) { - logger.error(Localization.getText("DatabaseHandler.getAllCityObjectsFailure")); - logger.debug(e.getMessage()); - } - return cos; - } public List getBufferIntersectingObjects(CityObject co, double bufferSize) { diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index 1ae85782..6b2067e4 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -16,13 +16,10 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import org.citygml4j.core.model.building.AbstractBuilding; -import org.h2gis.functions.factory.H2GISDBFactory; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import java.sql.Connection; -import java.sql.SQLException; import java.util.List; import static de.hft.stuttgart.citydoctor2.database.SerializationTest.assertCityObjectEquality; @@ -45,27 +42,6 @@ public class DataBaseHandlerTest { } - @Test - public void testGetAllFeatures() { - Building b = new Building(); - b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); - Building b2 = new Building(); - b2.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD2)); - Building b3 = new Building(); - b3.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD3)); - - handler.marshallCityObject(b); - handler.marshallCityObject(b2); - handler.marshallCityObject(b3); - assertEquals(3, handler.getFeatureCount()); - - List objects = handler.getAllCityObjects(); - assertEquals(3, objects.size()); - assertEquals(3, handler.getFeatureCount()); - - - } - @Test public void testGetBboxIntersectingObjects() { Building b = new Building(); -- GitLab From 3152a987f797949d9a9c41552890804ed525ef8f Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 27 Nov 2025 16:23:13 +0100 Subject: [PATCH 096/118] Refactor: Rename functions --- .../citydoctor2/database/EmbeddedDatabaseHandler.java | 10 ++++++---- .../citydoctor2/database/DataBaseHandlerTest.java | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java index 1645d61d..d8c6776b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java @@ -228,9 +228,11 @@ public class EmbeddedDatabaseHandler { public List getBufferIntersectingObjects(CityObject co, double bufferSize) { return getBufferIntersectingObjects(co.getGmlId(), bufferSize); + public List getBufferCollisionCandidates(CityObject co, double bufferSize) { + return getBufferCollisionCandidates(co.getGmlId(), bufferSize); } - public List getBufferIntersectingObjects(GmlId gmlID, double bufferSize) { + public List getBufferCollisionCandidates(GmlId gmlID, double bufferSize) { List intersectingObjects = new ArrayList<>(); try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + @@ -251,11 +253,11 @@ public class EmbeddedDatabaseHandler { return intersectingObjects; } - public List getBBoxIntersectingObjects(CityObject co) { - return getBBoxIntersectingObjects(co.getGmlId()); + public List getBBoxCollisionCandidates(CityObject co) { + return getBBoxCollisionCandidates(co.getGmlId()); } - public List getBBoxIntersectingObjects(GmlId id) { + public List getBBoxCollisionCandidates(GmlId id) { List intersectingObjects = new ArrayList<>(); try (Connection con = dataSource.getConnection()) { diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java index 6b2067e4..0184f544 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java @@ -112,7 +112,7 @@ public class DataBaseHandlerTest { handler.marshallCityObject(b4); assertEquals(4, handler.getFeatureCount()); - List objects = handler.getBBoxIntersectingObjects(b); + List objects = handler.getBBoxCollisionCandidates(b); assertEquals(1, objects.size()); assertEquals(4, handler.getFeatureCount()); @@ -193,7 +193,7 @@ public class DataBaseHandlerTest { handler.marshallCityObject(b4); assertEquals(4, handler.getFeatureCount()); - List objects = handler.getBufferIntersectingObjects(b, Math.sqrt(2)); + List objects = handler.getBufferCollisionCandidates(b, Math.sqrt(2)); assertEquals(1, objects.size()); assertEquals(4, handler.getFeatureCount()); -- GitLab From 1fc540d983a4591a1dda506e1aa225edbe80632c Mon Sep 17 00:00:00 2001 From: Matthias Betz Date: Fri, 28 Nov 2025 10:39:55 +0100 Subject: [PATCH 097/118] fix solid self intersection false positives --- .../check/error/SchematronError.java | 1 + .../datastructure/BridgeObject.java | 8 +- .../BuildingConstructiveElement.java | 19 ++-- .../citydoctor2/datastructure/Opening.java | 4 - .../citydoctor2/datastructure/TunnelPart.java | 4 +- .../citydoctor2/math/Triangle3d.java | 31 +++++-- .../tesselation/EarcutTesselator.java | 18 +++- .../stuttgart/citydoctor2/check/Checker.java | 49 ++++++++-- .../checks/SvrlContentHandler.java | 2 +- .../checks/geometry/PlanarCheck.java | 10 +- .../checks/geometry/SolidSelfIntCheck.java | 28 +++--- .../checks/util/SelfIntersectionUtil.java | 44 +++++++-- .../geometry/SolidSelfIntCheckTest.java | 20 ++++ .../citydoctor2/systemtest/PlanarTest.java | 3 +- .../healer/gui/HealerController.java | 93 ++++++++----------- 15 files changed, 205 insertions(+), 129 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java index a3dcf510..a8fc1591 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java @@ -59,6 +59,7 @@ public class SchematronError implements CheckError { @Override public void report(ErrorReport report) { report.add("errorId", errorId); + report.add("message", nameOfAttribute); } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObject.java index b87b232e..f87d6fea 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObject.java @@ -61,15 +61,11 @@ public class BridgeObject extends CityObject { private BridgeObject parent; public BridgeObject(AbstractBridge ab) { - this.ab = ab; - this.type = BridgeType.BRIDGE; - this.parent = null; + this(BridgeType.BRIDGE, ab, null); } public BridgeObject(AbstractBridge ab, BridgeObject parent) { - this.ab = ab; - this.type = BridgeType.BRIDGE_PART; - this.parent = parent; + this(BridgeType.BRIDGE_PART, ab, parent); } private BridgeObject(BridgeType type, AbstractBridge ab, BridgeObject parent) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingConstructiveElement.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingConstructiveElement.java index 06763aeb..8ac60a11 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingConstructiveElement.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingConstructiveElement.java @@ -1,25 +1,22 @@ package de.hft.stuttgart.citydoctor2.datastructure; -import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; -import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; -import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; -import javafx.scene.paint.Color; +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty; -import org.citygml4j.core.model.deprecated.bridge.DeprecatedPropertiesOfBridgeConstructiveElement; import org.citygml4j.core.util.geometry.GeometryFactory; -import org.xmlobjects.gml.model.geometry.GeometryProperty; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; -import org.xmlobjects.gml.model.geometry.complexes.CompositeSurface; import org.xmlobjects.gml.model.geometry.primitives.Solid; import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; -import org.xmlobjects.gml.model.geometry.primitives.SurfaceProperty; -import java.io.Serial; -import java.util.ArrayList; -import java.util.List; +import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; +import javafx.scene.paint.Color; public class BuildingConstructiveElement extends CityObject { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Opening.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Opening.java index b64487de..d4d1f20c 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Opening.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Opening.java @@ -48,10 +48,6 @@ public class Opening extends CityObject { private AbstractFillingSurface ao; - private Opening(OpeningType type) { - this.type = type; - } - public Opening(OpeningType type, SurfaceFeatureType featureType, BoundarySurface partOf, AbstractFillingSurface ao) { this.featureType = featureType; diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java index e6c02a99..4584a999 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java @@ -9,6 +9,8 @@ public class TunnelPart extends AbstractTunnel { private Tunnel parent; + private TunnelPart() { + } public TunnelPart(Tunnel parent) { this.parent = parent; @@ -34,6 +36,4 @@ public class TunnelPart extends AbstractTunnel { return "TunnelPart [id=" + getGmlId() + "]"; } - private TunnelPart() { - } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Triangle3d.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Triangle3d.java index 96ec193d..4f3c2920 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Triangle3d.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/Triangle3d.java @@ -87,8 +87,13 @@ public class Triangle3d implements Serializable { Vector3d n2 = v1.cross(v2); return new Plane(n2, p1); } - + public boolean doesIntersect(Triangle3d other) { + return doesIntersect(other, EPSILON); + } + + + public boolean doesIntersect(Triangle3d other, double epsilon) { // plane of other triangle Plane planeT2 = other.getPlane(); // check if all points are on one side of the plane @@ -96,13 +101,13 @@ public class Triangle3d implements Serializable { double distanceP2T2 = planeT2.getSignedDistance(p2); double distanceP3T2 = planeT2.getSignedDistance(p3); - if (Math.abs(distanceP1T2) < EPSILON) { + if (Math.abs(distanceP1T2) < epsilon) { distanceP1T2 = 0.0; } - if (Math.abs(distanceP2T2) < EPSILON) { + if (Math.abs(distanceP2T2) < epsilon) { distanceP2T2 = 0.0; } - if (Math.abs(distanceP3T2) < EPSILON) { + if (Math.abs(distanceP3T2) < epsilon) { distanceP3T2 = 0.0; } @@ -123,13 +128,13 @@ public class Triangle3d implements Serializable { double distanceP2T1 = planeT1.getSignedDistance(other.getP2()); double distanceP3T1 = planeT1.getSignedDistance(other.getP3()); - if (Math.abs(distanceP1T1) < EPSILON) { + if (Math.abs(distanceP1T1) < epsilon) { distanceP1T1 = 0.0; } - if (Math.abs(distanceP2T1) < EPSILON) { + if (Math.abs(distanceP2T1) < epsilon) { distanceP2T1 = 0.0; } - if (Math.abs(distanceP3T1) < EPSILON) { + if (Math.abs(distanceP3T1) < epsilon) { distanceP3T1 = 0.0; } @@ -139,11 +144,15 @@ public class Triangle3d implements Serializable { return false; } - return checkTriangleLineIntersection(other, p1, p2) || checkTriangleLineIntersection(other, p1, p3) + boolean intersects = checkTriangleLineIntersection(other, p1, p2) || checkTriangleLineIntersection(other, p1, p3) || checkTriangleLineIntersection(other, p2, p3) || checkTriangleLineIntersection(this, other.p1, other.p2) || checkTriangleLineIntersection(this, other.p1, other.p3) || checkTriangleLineIntersection(this, other.p2, other.p3); + if (intersects) { + System.out.println(); + } + return intersects; } private boolean checkTriangleLineIntersection(Triangle3d other, Vector3d a, Vector3d b) { @@ -171,7 +180,11 @@ public class Triangle3d implements Serializable { private boolean doesIntersectCoplanarTriangle(Triangle3d other) { Triangle2d t1 = projectTo2d(); Triangle2d t2 = other.projectTo2d(); - return t1.intersects(t2); + boolean intersects = t1.intersects(t2); + if (intersects) { + System.out.println(); + } + return intersects; } public Triangle2d projectTo2d() { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/EarcutTesselator.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/EarcutTesselator.java index acfc90f7..a3cfb759 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/EarcutTesselator.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/EarcutTesselator.java @@ -49,6 +49,15 @@ public class EarcutTesselator { start = addRingToArray(innerRing, vertices, start, axis); } + // center vertices? +// double[] projectedCenter = new double[2]; +// Geometry geometry = p.getParent(); +// Vector3d center = geometry.getCenter(); +// axis.writeCoordinatesOfVectorInArray(center, projectedCenter, 0); +// for (int i = 0; i < vertices.length; i++) { +// vertices[i] = vertices[i] - projectedCenter[i % 2]; +// } + // triangulation List indices = Earcut.earcut(vertices, holeStart, 2); List triangles = new ArrayList<>(); @@ -56,14 +65,15 @@ public class EarcutTesselator { throw new IllegalStateException(); } for (int i = 0; i < indices.size(); i = i + 3) { - Triangle3d t = new Triangle3d(vertexObjects.get(indices.get(i + 0)), - vertexObjects.get(indices.get(i + 1)), - vertexObjects.get(indices.get(i + 2))); + Vertex v1 = vertexObjects.get(indices.get(i + 0)); + Vertex v2 = vertexObjects.get(indices.get(i + 1)); + Vertex v3 = vertexObjects.get(indices.get(i + 2)); + Triangle3d t = new Triangle3d(v1, v2, v3); triangles.add(t); } return new TesselatedPolygon(triangles, p); } - + private static void addVerticesToList(LinearRing ring, List vertexObjects) { List vertices = ring.getVertices(); for (int i = 0; i < vertices.size() - 1; i++) { diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 061e4184..95101505 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -203,26 +203,60 @@ public class Checker { } private void handleSchematronResults(SvrlContentHandler handler) { - model.addGlobalErrors(handler.getGeneralErrors()); + handleSchematronErrorsGlobal(handler.getGeneralErrors()); Map featureMap = new HashMap<>(); - model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f)); + boolean onlySchematron = execLayers.isEmpty(); + if (onlySchematron) { + CheckableUtilsVisitor visitor = new CheckableUtilsVisitor() { + @Override + public void check(Checkable checkable) { + checkable.setValidated(true); + } + }; + model.createFeatureStream().forEach(f -> { + featureMap.put(f.getGmlId().getGmlString(), f); + f.accept(visitor); + }); + } else { + model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f)); + } handler.getFeatureErrors().forEach((k, v) -> { if (k.trim().isEmpty()) { - // missing gml id, ignore? - return; + handleSchematronErrorsGlobal(v); + return; } CityObject co = featureMap.get(k); if (co == null) { // gml id reported by schematron was not found, add to general errors - for (SchematronError se : v) { - model.addGlobalError(se); - } + handleSchematronErrorsGlobal(v); + +// for (SchematronError se : v) { +// model.addGlobalError(se); +// } } else { handleSchematronErrorsForCityObject(v, co); + co.setValidated(true); } }); } + private void handleSchematronErrorsGlobal(List v) { + for (SchematronError se : v) { +// CheckError err; +// if (AttributeMissingError.ID.getIdString().equals(se.getErrorIdString())) { +// err = new AttributeMissingError(null, se.getChildId(), se.getNameOfAttribute()); +// } else if (AttributeValueWrongError.ID.getIdString().equals(se.getErrorIdString())) { +// err = new AttributeValueWrongError(null, se.getChildId(), se.getNameOfAttribute()); +// } else if (AttributeInvalidError.ID.getIdString().equals(se.getErrorIdString())) { +// err = new AttributeInvalidError(null, se.getChildId(), se.getNameOfAttribute()); +// } else { +// throw new IllegalStateException( +// "Unknown error ID was given in schematron file: " + se.getErrorIdString()); +// } + model.addGlobalError(se); + } + } + public static void handleSchematronErrorsForCityObject(List v, CityObject co) { int count = 0; for (SchematronError se : v) { @@ -435,7 +469,6 @@ public class Checker { } } - @SuppressWarnings("resource") public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, InputStream in) { if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) { diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java index 78189eb9..754a8bc4 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java @@ -106,7 +106,7 @@ public class SvrlContentHandler implements ContentHandler { throw new IllegalStateException( "Schematron File is not formed according to specification for CityDoctor."); } - String gmlId = split[0]; + String gmlId = split[0].strip(); String childId = split[1]; String errorId = split[2]; String nameOfAttribute = split[3]; diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java index 560f78b5..af6dd4cc 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/PlanarCheck.java @@ -57,10 +57,12 @@ import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon; */ public class PlanarCheck extends Check { - private static final String DISTANCE = "distance"; - private static final String DISTANCE_TOLERANCE = "distanceTolerance"; + public static final String ANGLE = "angle"; + public static final String DISTANCE = "distance"; + public static final String BOTH = "both"; + public static final String DISTANCE_TOLERANCE = "distanceTolerance"; private static final String ANGLE_TOLERANCE = "angleTolerance"; - private static final String TYPE = "type"; + public static final String TYPE = "type"; private static final List dependencies; @@ -97,7 +99,7 @@ public class PlanarCheck extends Check { public void check(Polygon p) { if (DISTANCE.equals(planarCheckType)) { planarDistance(p); - } else if ("angle".equals(planarCheckType)) { + } else if (ANGLE.equals(planarCheckType)) { planarNormalDeviation(p); } else if ("both".equals(planarCheckType)) { planarDistance(p); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java index 0eefd349..c911a1d7 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java @@ -21,13 +21,13 @@ package de.hft.stuttgart.citydoctor2.checks.geometry; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import de.hft.stuttgart.citydoctor2.check.Check; import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.CheckResult; -import de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection; import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.RequirementType; import de.hft.stuttgart.citydoctor2.check.ResultStatus; @@ -36,6 +36,7 @@ import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils; import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection; /** @@ -47,7 +48,9 @@ import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection; public class SolidSelfIntCheck extends Check { private static final List dependencies; - + + private double delta = 0.01; + static { ArrayList deps = new ArrayList<>(); dependencies = Collections.unmodifiableList(deps); @@ -68,6 +71,13 @@ public class SolidSelfIntCheck extends Check { deps.add(CheckId.C_GE_S_NON_MANIFOLD_VERTEX); deps.add(CheckId.C_GE_S_POLYGON_WRONG_ORIENTATION); } + + @Override + public void init(Map parameters, ParserConfiguration config) { + if (parameters.containsKey(PlanarCheck.DISTANCE_TOLERANCE)) { + delta = Double.parseDouble(parameters.get(PlanarCheck.DISTANCE_TOLERANCE)); + } + } @Override public void check(Geometry g) { @@ -75,7 +85,7 @@ public class SolidSelfIntCheck extends Check { return; } CheckResult cr; - List intersections = SelfIntersectionUtil.calculateSolidSelfIntersection(g); + List intersections = SelfIntersectionUtil.calculateSolidSelfIntersection(g, delta); if (intersections.isEmpty()) { cr = new CheckResult(this, ResultStatus.OK, null); } else { @@ -85,18 +95,6 @@ public class SolidSelfIntCheck extends Check { g.addCheckResult(cr); } - @SuppressWarnings("unused") - private CheckResult oldIntersectionAlgorithm(Geometry g) { - CheckResult cr; - GeometrySelfIntersection intersect = SelfIntersectionUtil.doesSolidSelfIntersect(g); - if (intersect != null) { - cr = new CheckResult(this, ResultStatus.ERROR, null); - } else { - cr = new CheckResult(this, ResultStatus.OK, null); - } - return cr; - } - @Override public List getDependencies() { return dependencies; diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/SelfIntersectionUtil.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/SelfIntersectionUtil.java index 602eb045..30c57878 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/SelfIntersectionUtil.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/SelfIntersectionUtil.java @@ -21,6 +21,7 @@ package de.hft.stuttgart.citydoctor2.checks.util; import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -39,6 +40,8 @@ import org.locationtech.jts.geom.impl.CoordinateArraySequence; import org.locationtech.jts.operation.overlay.OverlayOp; import org.locationtech.jts.operation.overlay.snap.SnapIfNeededOverlayOp; +import Jama.EigenvalueDecomposition; +import Jama.Matrix; import de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection; import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; @@ -50,8 +53,10 @@ import de.hft.stuttgart.citydoctor2.edge.EdgePolygon; import de.hft.stuttgart.citydoctor2.edge.IntersectPlanarPolygons; import de.hft.stuttgart.citydoctor2.edge.MeshSurface; import de.hft.stuttgart.citydoctor2.edge.PolygonPolygonIntersection; +import de.hft.stuttgart.citydoctor2.math.CovarianceMatrix; import de.hft.stuttgart.citydoctor2.math.MovedPolygon; import de.hft.stuttgart.citydoctor2.math.MovedRing; +import de.hft.stuttgart.citydoctor2.math.OrthogonalRegressionPlane; import de.hft.stuttgart.citydoctor2.math.Plane; import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection; import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection.Type; @@ -83,18 +88,37 @@ public class SelfIntersectionUtil { private SelfIntersectionUtil() { } - - public static List calculateSolidSelfIntersection(Geometry g) { + + public static List calculateSolidSelfIntersection(Geometry g, double delta) { List tesselatedPolygons = new ArrayList<>(); for (Polygon p : g.getPolygons()) { - tesselatedPolygons.add(EarcutTesselator.tesselatePolygon(p)); + TesselatedPolygon tessPolygon = EarcutTesselator.tesselatePolygon(p); + for (Iterator iterator = tessPolygon.getTriangles().iterator(); iterator.hasNext();) { + Triangle3d t = iterator.next(); + List vertices = new ArrayList<>(3); + vertices.add(t.getP1()); + vertices.add(t.getP2()); + vertices.add(t.getP3()); + Vector3d centroid = CovarianceMatrix.getCentroid(vertices); + EigenvalueDecomposition ed = OrthogonalRegressionPlane.decompose(vertices, centroid); + Matrix eigenValues = ed.getD(); + double[] eigenValuesArray = new double[3]; + eigenValuesArray[0] = eigenValues.get(0, 0); + eigenValuesArray[1] = eigenValues.get(1, 1); + eigenValuesArray[2] = eigenValues.get(2, 2); + if (eigenValuesArray[1] < delta) { + iterator.remove(); + } + } + tesselatedPolygons.add(tessPolygon); } + List intersections = new ArrayList<>(); for (int i = 0; i < tesselatedPolygons.size() - 1; i++) { TesselatedPolygon p1 = tesselatedPolygons.get(i); for (int j = i + 1; j < tesselatedPolygons.size(); j++) { TesselatedPolygon p2 = tesselatedPolygons.get(j); - GeometrySelfIntersection intersection = doPolygonsIntersect(p1, p2); + GeometrySelfIntersection intersection = doPolygonsIntersect(p1, p2, delta); if (intersection != null) { intersections.add(PolygonIntersection.triangles(intersection.t1(), intersection.t2())); } @@ -103,8 +127,8 @@ public class SelfIntersectionUtil { return intersections; } - public static GeometrySelfIntersection doesSolidSelfIntersect(Geometry g) { - return selfIntersectionJava(g); + public static GeometrySelfIntersection doesSolidSelfIntersect(Geometry g, double epsilon) { + return selfIntersectionJava(g, epsilon); } public static List doesSolidSelfIntersect2(Geometry g) { @@ -404,7 +428,7 @@ public class SelfIntersectionUtil { return sign != 0; } - private static GeometrySelfIntersection selfIntersectionJava(Geometry g) { + private static GeometrySelfIntersection selfIntersectionJava(Geometry g, double epsilon) { List tessPolys = new ArrayList<>(); for (Polygon p : g.getPolygons()) { tessPolys.add(JoglTesselator.tesselatePolygon(p)); @@ -413,7 +437,7 @@ public class SelfIntersectionUtil { TesselatedPolygon p1 = tessPolys.get(i); for (int j = i + 1; j < tessPolys.size(); j++) { TesselatedPolygon p2 = tessPolys.get(j); - GeometrySelfIntersection intersection = doPolygonsIntersect(p1, p2); + GeometrySelfIntersection intersection = doPolygonsIntersect(p1, p2, epsilon); if (intersection != null) { return intersection; } @@ -422,12 +446,12 @@ public class SelfIntersectionUtil { return null; } - private static GeometrySelfIntersection doPolygonsIntersect(TesselatedPolygon p1, TesselatedPolygon p2) { + private static GeometrySelfIntersection doPolygonsIntersect(TesselatedPolygon p1, TesselatedPolygon p2, double epsilon) { for (int p1Index = 0; p1Index < p1.getTriangles().size(); p1Index++) { for (int p2Index = 0; p2Index < p2.getTriangles().size(); p2Index++) { Triangle3d t1 = p1.getTriangles().get(p1Index); Triangle3d t2 = p2.getTriangles().get(p2Index); - if (t1.doesIntersect(t2)) { + if (t1.doesIntersect(t2, epsilon)) { logger.trace("{} intersects {}", t1, t2); logger.trace("GML-ID: {} intersects {}", t1.getPartOf().getOriginal().getGmlId(), t2.getPartOf().getOriginal().getGmlId()); diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java index 847de23f..d31e6c8b 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java @@ -25,14 +25,18 @@ import static org.junit.Assert.assertNotNull; import org.junit.Assert; import org.junit.Test; +import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; +import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError; import de.hft.stuttgart.citydoctor2.checks.util.GeometryTestUtils; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; +import de.hft.stuttgart.citydoctor2.datastructure.Geometry.Orientation; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; import de.hft.stuttgart.citydoctor2.datastructure.Lod; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; @@ -45,6 +49,22 @@ import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; * */ public class SolidSelfIntCheckTest { + + @Test + public void testDependencyChecking() { + Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD2, Orientation.OUTWARD); + + ConcretePolygon p = new ConcretePolygon(); + geom.addPolygon(p); + NonPlanarPolygonDistancePlaneError err = new NonPlanarPolygonDistancePlaneError(p, 2, null, null); + CheckResult cr = new CheckResult(CheckId.C_GE_P_NON_PLANAR, ResultStatus.ERROR, err); + p.addCheckResult(cr); + + SolidSelfIntCheck selfIntCheck = new SolidSelfIntCheck(); + + boolean canExecute = selfIntCheck.canExecute(geom); + assertFalse(canExecute); + } @Test public void testGoodGeometry() { diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java index f15f1289..0b3f2c72 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/PlanarTest.java @@ -31,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.ErrorId; import de.hft.stuttgart.citydoctor2.check.ResultStatus; +import de.hft.stuttgart.citydoctor2.checks.geometry.PlanarCheck; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; @@ -142,7 +143,7 @@ public class PlanarTest { public void testPlanarPolygon6() throws CityGmlParseException, IOException, InvalidGmlFileException { Map> paramMap = new HashMap<>(); Map parameter = new HashMap<>(); - parameter.put("type", "both"); + parameter.put(PlanarCheck.TYPE, PlanarCheck.BOTH); parameter.put("distanceTolerance", "0.5"); paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0002-T0002.gml", paramMap); diff --git a/CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerController.java b/CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerController.java index 4cb6311c..62eccfd8 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerController.java +++ b/CityDoctorParent/Extensions/CityDoctorHealerGUI/src/main/java/de/hft/stuttgart/citydoctor2/healer/gui/HealerController.java @@ -538,60 +538,45 @@ public class HealerController { } public void injectSolid(boolean useBs, boolean useBi, boolean useLod2, boolean useLod3, boolean useLod4) { - if (model == null || currentFeature == null) { - return; - } - CityObject oldFeature = currentFeature; - currentFeature.prepareForChecking(); - if (currentFeature instanceof BoundarySurface) { - BoundarySurface surface = (BoundarySurface) currentFeature; - currentFeature = surface.getParent(); - } - if (!(currentFeature instanceof Building)) { - return; - } - // collect polygons - Map> availablePolygons = new EnumMap<>(Lod.class); - Map> existingPolygons = new EnumMap<>(Lod.class); - Building b = (Building) currentFeature; - collectPolygons(useBs, useBi, availablePolygons, existingPolygons, b); - if (useLod2) { - insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD2); - } - if (useLod3) { - insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD3); - } - if (useLod4) { - insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD4); +// if (model == null || currentFeature == null) { +// return; +// } + + for (Building b : model.getBuildings()) { + // collect polygons + Map> availablePolygons = new EnumMap<>(Lod.class); + Map> existingPolygons = new EnumMap<>(Lod.class); + collectPolygons(useBs, useBi, availablePolygons, existingPolygons, b); + if (useLod2) { + insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD2); + } + if (useLod3) { + insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD3); + } + if (useLod4) { + insertSolidGeometry(availablePolygons, b, existingPolygons, Lod.LOD4); + } + for (BuildingPart part : b.getBuildingParts()) { + // collect polygons + availablePolygons = new EnumMap<>(Lod.class); + existingPolygons = new EnumMap<>(Lod.class); + collectPolygons(useBs, useBi, availablePolygons, existingPolygons, part); + if (useLod2) { + insertSolidGeometry(availablePolygons, part, existingPolygons, Lod.LOD2); + } + if (useLod3) { + insertSolidGeometry(availablePolygons, part, existingPolygons, Lod.LOD3); + } + if (useLod4) { + insertSolidGeometry(availablePolygons, part, existingPolygons, Lod.LOD4); + } + } } - nextFeature = null; - nextGeometry = null; - Lod lod = currentGeometry.getLod(); - GeometryType type = GeometryType.SOLID; - currentGeometry = Objects.requireNonNull(currentFeature.getGeometry(type, lod)); - checker.executeChecksForCheckable(currentFeature); - healerView.getNextErrorList().getItems().clear(); - healerView.getNextMeshGroup().getChildren().clear(); - updateCurrentErrors(); - currentTriangulatedGeometry = TriangulatedGeometry.of(currentGeometry); - currentErrorVisitor.setGeometry(currentTriangulatedGeometry); - currentTriangulatedGeometry.setCullFace(culling); - currentTriangulatedGeometry.setDrawMode(drawMode); - healerView.zoomOutForBoundingBox(currentGeometry.calculateBoundingBox()); - - healerView.getCurrentMeshGroup().getChildren().clear(); - healerView.getCurrentMeshGroup().getChildren().addAll(currentTriangulatedGeometry.getMeshes()); - healerView.getNextStepBtn().setDisable(false); - healerView.getHealCompleteBtn().setDisable(false); - healerView.getAcceptBtn().setDisable(true); - healerView.getCancelBtn().setDisable(true); - healerView.getSolidInjectorBtn().setDisable(true); - currentFeature = oldFeature; } private void collectPolygons(boolean useBs, boolean useBi, Map> availablePolygons, - Map> existingPolygons, Building b) { + Map> existingPolygons, AbstractBuilding b) { if (useBs) { collectPolygons(availablePolygons, b.getBoundarySurfaces()); } @@ -604,13 +589,13 @@ public class HealerController { collectPolygons(availablePolygons, bi.getBoundarySurfaces()); } } - for (Geometry geom : b.getGeometries()) { - Set polygons = existingPolygons.computeIfAbsent(geom.getLod(), l -> new HashSet<>()); - polygons.addAll(geom.getPolygons()); - } +// for (Geometry geom : b.getGeometries()) { +// Set polygons = existingPolygons.computeIfAbsent(geom.getLod(), l -> new HashSet<>()); +// polygons.addAll(geom.getPolygons()); +// } } - private void insertSolidGeometry(Map> availablePolygons, Building b, + private void insertSolidGeometry(Map> availablePolygons, AbstractBuilding b, Map> existingPolygons, Lod lod) { List newPolygons = availablePolygons.get(lod); if (newPolygons != null) { -- GitLab From 2fe401b32ce175ff3401aca53bca490cd7e1f849 Mon Sep 17 00:00:00 2001 From: Matthias Betz Date: Fri, 28 Nov 2025 13:40:18 +0100 Subject: [PATCH 098/118] add different handling of global schematron errors to be more specific with the error type --- .../stuttgart/citydoctor2/check/Checker.java | 35 ++- .../checks/SvrlContentHandler.java | 2 +- .../citydoctor2/gui/CityDoctorController.java | 20 +- .../citydoctor2/gui/GlobalErrorVisitor.java | 225 ++++++++++++++++++ 4 files changed, 250 insertions(+), 32 deletions(-) create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/GlobalErrorVisitor.java diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 95101505..38bd869f 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -221,18 +221,11 @@ public class Checker { model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f)); } handler.getFeatureErrors().forEach((k, v) -> { - if (k.trim().isEmpty()) { - handleSchematronErrorsGlobal(v); - return; - } - CityObject co = featureMap.get(k); + String trimmedId = k.trim(); + CityObject co = featureMap.get(trimmedId); if (co == null) { // gml id reported by schematron was not found, add to general errors handleSchematronErrorsGlobal(v); - -// for (SchematronError se : v) { -// model.addGlobalError(se); -// } } else { handleSchematronErrorsForCityObject(v, co); co.setValidated(true); @@ -242,18 +235,18 @@ public class Checker { private void handleSchematronErrorsGlobal(List v) { for (SchematronError se : v) { -// CheckError err; -// if (AttributeMissingError.ID.getIdString().equals(se.getErrorIdString())) { -// err = new AttributeMissingError(null, se.getChildId(), se.getNameOfAttribute()); -// } else if (AttributeValueWrongError.ID.getIdString().equals(se.getErrorIdString())) { -// err = new AttributeValueWrongError(null, se.getChildId(), se.getNameOfAttribute()); -// } else if (AttributeInvalidError.ID.getIdString().equals(se.getErrorIdString())) { -// err = new AttributeInvalidError(null, se.getChildId(), se.getNameOfAttribute()); -// } else { -// throw new IllegalStateException( -// "Unknown error ID was given in schematron file: " + se.getErrorIdString()); -// } - model.addGlobalError(se); + CheckError err; + if (AttributeMissingError.ID.getIdString().equals(se.getErrorIdString())) { + err = new AttributeMissingError(null, se.getChildId(), se.getNameOfAttribute()); + } else if (AttributeValueWrongError.ID.getIdString().equals(se.getErrorIdString())) { + err = new AttributeValueWrongError(null, se.getChildId(), se.getNameOfAttribute()); + } else if (AttributeInvalidError.ID.getIdString().equals(se.getErrorIdString())) { + err = new AttributeInvalidError(null, se.getChildId(), se.getNameOfAttribute()); + } else { + throw new IllegalStateException( + "Unknown error ID was given in schematron file: " + se.getErrorIdString()); + } + model.addGlobalError(err); } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java index 754a8bc4..07c583dc 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java @@ -111,7 +111,7 @@ public class SvrlContentHandler implements ContentHandler { String errorId = split[2]; String nameOfAttribute = split[3]; SchematronError err = new SchematronError(errorId, gmlId, childId, nameOfAttribute); - if (gmlId == null || gmlId.isEmpty()) { + if (gmlId == null || gmlId.isBlank()) { // general error generalErrors.add(err); } else { diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index fd4320d1..cda811c7 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -21,6 +21,7 @@ import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive; import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; import de.hft.stuttgart.citydoctor2.zip.MalformedZipFileException; +import de.hft.stuttgart.quality.adapter.types.ErrorAdapter; import javafx.application.Platform; import javafx.scene.chart.XYChart.Data; import javafx.scene.chart.XYChart.Series; @@ -1044,11 +1045,7 @@ public class CityDoctorController { // apply check results to tree views updateFeatureTrees(); updateTree(mainWindow.getPolygonsView().getRoot()); - for (CheckError e : model.getGlobalErrors()) { - if (e instanceof SchematronError se) { - mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString() + " - " + se.getNameOfAttribute()); - } - } + updateGlobalErrors(); renderer.refresh(); mainWindow.getWriteReportButton().setDisable(false); }); @@ -1057,6 +1054,13 @@ public class CityDoctorController { } } + private void updateGlobalErrors() { + GlobalErrorVisitor globErrVisitor = new GlobalErrorVisitor(mainWindow); + for (CheckError e : model.getGlobalErrors()) { + e.accept(globErrVisitor); + } + } + public void startZipFileChecks(ValidationConfiguration config, ProgressListener l, boolean lowMemoryMode, Path outputPath, boolean pdfReports, boolean xmlReports) { if (zipArchive == null && logger.isWarnEnabled()) { @@ -1097,11 +1101,7 @@ public class CityDoctorController { // apply check results to tree views updateFeatureTrees(); updateTree(mainWindow.getPolygonsView().getRoot()); - for (CheckError e : model.getGlobalErrors()) { - if (e instanceof SchematronError se) { - mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString() + " - " + se.getNameOfAttribute()); - } - } + updateGlobalErrors(); renderer.refresh(); mainWindow.getWriteReportButton().setDisable(false); currentZipEntryManager.refresh(); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/GlobalErrorVisitor.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/GlobalErrorVisitor.java new file mode 100644 index 00000000..287ea14d --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/GlobalErrorVisitor.java @@ -0,0 +1,225 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.ErrorVisitor; +import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError; +import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError; +import de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError; +import de.hft.stuttgart.citydoctor2.check.error.DependenciesNotMetError; +import de.hft.stuttgart.citydoctor2.check.error.MultipleConnectedComponentsError; +import de.hft.stuttgart.citydoctor2.check.error.NestedRingError; +import de.hft.stuttgart.citydoctor2.check.error.NonManifoldEdgeError; +import de.hft.stuttgart.citydoctor2.check.error.NonManifoldVertexError; +import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError; +import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonNormalsDeviation; +import de.hft.stuttgart.citydoctor2.check.error.NotCeilingError; +import de.hft.stuttgart.citydoctor2.check.error.NotFloorError; +import de.hft.stuttgart.citydoctor2.check.error.NotGroundError; +import de.hft.stuttgart.citydoctor2.check.error.NotWallError; +import de.hft.stuttgart.citydoctor2.check.error.NullAreaError; +import de.hft.stuttgart.citydoctor2.check.error.PlanarityError; +import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError; +import de.hft.stuttgart.citydoctor2.check.error.PolygonError; +import de.hft.stuttgart.citydoctor2.check.error.PolygonHoleOutsideError; +import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError; +import de.hft.stuttgart.citydoctor2.check.error.PolygonIntersectingRingsError; +import de.hft.stuttgart.citydoctor2.check.error.PolygonSameOrientationError; +import de.hft.stuttgart.citydoctor2.check.error.PolygonWithoutSurfaceError; +import de.hft.stuttgart.citydoctor2.check.error.PolygonWrongOrientationError; +import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError; +import de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError; +import de.hft.stuttgart.citydoctor2.check.error.RingError; +import de.hft.stuttgart.citydoctor2.check.error.RingNotClosedError; +import de.hft.stuttgart.citydoctor2.check.error.RingSelfIntersectionError; +import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError; +import de.hft.stuttgart.citydoctor2.check.error.SchematronError; +import de.hft.stuttgart.citydoctor2.check.error.SolidError; +import de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError; +import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError; +import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError; +import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError; +import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError; + +public class GlobalErrorVisitor implements ErrorVisitor { + + private MainWindow mainWindow; + + public GlobalErrorVisitor(MainWindow mainWindow) { + this.mainWindow = mainWindow; + } + + @Override + public void visit(RingError err) { + } + + @Override + public void visit(PolygonError err) { + } + + @Override + public void visit(RingSelfIntersectionError err) { + } + + @Override + public void visit(PlanarityError err) { + } + + @Override + public void visit(PolygonHoleOutsideError err) { + } + + @Override + public void visit(NonManifoldEdgeError err) { + } + + @Override + public void visit(MultipleConnectedComponentsError err) { + } + + @Override + public void visit(NestedRingError err) { + } + + @Override + public void visit(NonManifoldVertexError err) { + } + + @Override + public void visit(PolygonWrongOrientationError err) { + } + + @Override + public void visit(PolygonSameOrientationError err) { + } + + @Override + public void visit(SolidNotClosedError err) { + } + + @Override + public void visit(DependenciesNotMetError err) { + } + + @Override + public void visit(UnknownCheckError err) { + } + + @Override + public void visit(RingNotClosedError err) { + } + + @Override + public void visit(ConsecutivePointSameError err) { + } + + @Override + public void visit(AllPolygonsWrongOrientationError err) { + } + + @Override + public void visit(PolygonInteriorDisconnectedError err) { + } + + @Override + public void visit(NullAreaError err) { + } + + @Override + public void visit(RingTooFewPointsError err) { + } + + @Override + public void visit(NonPlanarPolygonNormalsDeviation err) { + } + + @Override + public void visit(NonPlanarPolygonDistancePlaneError err) { + } + + @Override + public void visit(PolygonIntersectingRingsError err) { + } + + @Override + public void visit(SolidSelfIntError err) { + } + + @Override + public void visit(TooFewPolygonsError err) { + } + + @Override + public void visit(RingDuplicatePointError err) { + } + + @Override + public void visit(RingEdgeIntersectionError err) { + } + + @Override + public void visit(PointTouchesEdgeError err) { + } + + @Override + public void visit(NotCeilingError err) { + } + + @Override + public void visit(NotFloorError err) { + } + + @Override + public void visit(NotWallError err) { + } + + @Override + public void visit(NotGroundError err) { + } + + @Override + public void visit(SchematronError se) { + mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString() + " - " + se.getNameOfAttribute()); + } + + @Override + public void visit(SurfaceUnfragmentedError err) { + } + + @Override + public void visit(DegeneratedRingError err) { + } + + @Override + public void visit(AttributeMissingError err) { + mainWindow.getGlobalErrorsView().getItems().add(err.getErrorId().getIdString() + + " - " + err.getNameOfAttribute()); + } + + @Override + public void visit(AttributeValueWrongError err) { + mainWindow.getGlobalErrorsView().getItems().add(err.getErrorId().getIdString() + + " - " + err.getNameOfAttribute()); + } + + @Override + public void visit(AttributeInvalidError err) { + mainWindow.getGlobalErrorsView().getItems().add(err.getErrorId().getIdString() + + " - " + err.getNameOfAttribute()); + } + + @Override + public void visit(PolygonWithoutSurfaceError err) { + } + + @Override + public void visit(CheckError err) { + } + + @Override + public void visit(SolidError err) { + } + +} -- GitLab From 6d80a97fa169cd3b9a9317944e9e9c8dd0a5b026 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 1 Dec 2025 09:42:18 +0100 Subject: [PATCH 099/118] Fix: Remove race condition in Schematron thread --- .../stuttgart/citydoctor2/check/Checker.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index bd8cc2aa..06870162 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -81,8 +81,6 @@ import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; -import java.text.ChoiceFormat; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -195,26 +193,31 @@ public class Checker { if (config == null) { config = ValidationConfiguration.loadStandardValidationConfig(); } - Thread schematronThread = new Thread(() -> { - SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile()); - if (handler != null) { - handleSchematronResults(handler); - } - }); - schematronThread.start(); + + Callable schematronThread = () -> executeSchematronValidationIfAvailable(config, model.getFile()); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future handlerFuture = executor.submit(schematronThread); + executor.shutdown(); + checkCityModel(model, l); - if (schematronThread.isAlive()) { - logger.info(Localization.getText("Checker.schematronStillRunning")); + if (!handlerFuture.isDone()) { + if (logger.isInfoEnabled()) { + logger.info(Localization.getText("Checker.schematronStillRunning")); + } if(l !=null){ l.updateProgress(-0.5f); } - try{ - schematronThread.join(); - logger.info(Localization.getText("Checker.schematronCompleted")); - } catch (InterruptedException e) { - logger.error(Localization.getText("Checker.schematronInterrupted"), e); - schematronThread.interrupt(); + } + try { + SvrlContentHandler handler = handlerFuture.get(); + if (handler != null) { + handleSchematronResults(handler); } + } catch (ExecutionException e) { + logger.error(e); + } catch (InterruptedException e) { + logger.error(Localization.getText("Checker.schematronInterrupted"), e); + Thread.currentThread().interrupt(); } CacheManager.synchronizeCache(); model.setValidated(createValidationPlan()); -- GitLab From 670b2a9f2e65177d4a17af21425967c24a747679 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 1 Dec 2025 09:42:58 +0100 Subject: [PATCH 100/118] Refactor: Remove obsolete function --- .../citydoctor2/database/EmbeddedDatabaseHandler.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java index d8c6776b..19742ea1 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java @@ -296,16 +296,5 @@ public class EmbeddedDatabaseHandler { return count; } - private List retrieveCityObjects(ResultSet rs) throws SQLException { - List cos = new ArrayList<>(); - while (rs.next()) { - try (InputStream is = rs.getBinaryStream("data")) { - cos.add(SerializationUtils.deserialize(is)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return cos; - } } -- GitLab From a85b7b19bad4585d2ade2841da14ebb868ea0244 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 1 Dec 2025 09:45:05 +0100 Subject: [PATCH 101/118] Style: Rename functions and add JavaDoc --- .../database/EmbeddedDatabaseHandler.java | 58 ++++++++++++++----- ....java => EmbeddedDataBaseHandlerTest.java} | 4 +- 2 files changed, 44 insertions(+), 18 deletions(-) rename CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/{DataBaseHandlerTest.java => EmbeddedDataBaseHandlerTest.java} (98%) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java index 19742ea1..2f33db50 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java @@ -225,60 +225,86 @@ public class EmbeddedDatabaseHandler { } - - public List getBufferIntersectingObjects(CityObject co, double bufferSize) { - return getBufferIntersectingObjects(co.getGmlId(), bufferSize); - public List getBufferCollisionCandidates(CityObject co, double bufferSize) { - return getBufferCollisionCandidates(co.getGmlId(), bufferSize); + /** + * Retrieves a List of GmlIds of CityObjects that are candidates for a buffer collision with a given CityObject(subject), + * meaning the candidate's bbox intersects the subject's buffered bbox. + * @param subject the subject CityObject + * @param bufferSize the buffer distance + * @return a List of GmlIds of buffer collision candidates + */ + public List getBufferCollisionCandidates(CityObject subject, double bufferSize) { + return getBufferCollisionCandidates(subject.getGmlId(), bufferSize); } - public List getBufferCollisionCandidates(GmlId gmlID, double bufferSize) { + /** + * Retrieves a List of GmlIds of CityObjects that are candidates for a buffer collision with a given CityObject(subject), + * meaning the candidate's bbox intersects the subject's buffered bbox. + * @param subjectId the subject CityObject's GmlId + * @param bufferSize the buffer distance + * @return a List of GmlIds of buffer collision candidates + */ + public List getBufferCollisionCandidates(GmlId subjectId, double bufferSize) { List intersectingObjects = new ArrayList<>(); try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + " WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM features WHERE gmlid = ?)) " + "AND tab1.gmlid <> ?")) { ps.setDouble(1, bufferSize); - ps.setString(2, gmlID.toString()); - ps.setString(3, gmlID.toString()); + ps.setString(2, subjectId.toString()); + ps.setString(3, subjectId.toString()); ResultSet rs = ps.executeQuery(); while (rs.next()) { intersectingObjects.add(new GmlId(rs.getString("gmlid"))); } } } catch (SQLException e) { - logger.error(Localization.getText("DatabaseHandler.getBufferIntersectFailure"),gmlID); + logger.error(Localization.getText("DatabaseHandler.getBufferIntersectFailure"),subjectId); logger.debug(e.getMessage()); } return intersectingObjects; } - public List getBBoxCollisionCandidates(CityObject co) { - return getBBoxCollisionCandidates(co.getGmlId()); + /** + * Retrieves a List of GmlIds of CityObjects that are candidates for a geometry collision with a given CityObject(subject), + * meaning the bounding boxes of candidate and subject intersect. + * @param subject the subject CityObject + * @return a List of GmlIds of buffer collision candidates + */ + public List getGeometryCollisionCandidates(CityObject subject) { + return getGeometryCollisionCandidates(subject.getGmlId()); } - public List getBBoxCollisionCandidates(GmlId id) { + /** + * Retrieves a List of GmlIds of CityObjects that are candidates for a geometry collision with a given CityObject(subject), + * meaning the bounding boxes of candidate and subject intersect. + * @param subjectId the subject CityObject + * @return a List of GmlIds of buffer collision candidates + */ + public List getGeometryCollisionCandidates(GmlId subjectId) { List intersectingObjects = new ArrayList<>(); try (Connection con = dataSource.getConnection()) { try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" + " WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?")) { - ps.setString(1, id.toString()); - ps.setString(2, id.toString()); + ps.setString(1, subjectId.toString()); + ps.setString(2, subjectId.toString()); ResultSet rs = ps.executeQuery(); while (rs.next()) { intersectingObjects.add(new GmlId(rs.getString("gmlid"))); } } } catch (SQLException e) { - logger.error(Localization.getText("DatabaseHandler.getBBoxIntersectFailure"),id); + logger.error(Localization.getText("DatabaseHandler.getBBoxIntersectFailure"),subjectId); logger.debug(e.getMessage()); } return intersectingObjects; } - + /** + * Returns the number of top-level CityObjects entries that are currently in the embedded database. + * @return the count of top-level CityObjects in the database + */ public int getFeatureCount() { int count = -1; diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDataBaseHandlerTest.java similarity index 98% rename from CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java rename to CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDataBaseHandlerTest.java index 0184f544..06c0bb6e 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/DataBaseHandlerTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDataBaseHandlerTest.java @@ -27,7 +27,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -public class DataBaseHandlerTest { +public class EmbeddedDataBaseHandlerTest { private EmbeddedDatabaseHandler handler; @@ -112,7 +112,7 @@ public class DataBaseHandlerTest { handler.marshallCityObject(b4); assertEquals(4, handler.getFeatureCount()); - List objects = handler.getBBoxCollisionCandidates(b); + List objects = handler.getGeometryCollisionCandidates(b); assertEquals(1, objects.size()); assertEquals(4, handler.getFeatureCount()); -- GitLab From fd7858f876d12ff363d210b0377da1be4ec66fa7 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 1 Dec 2025 09:47:07 +0100 Subject: [PATCH 102/118] Style: Fix typo in localization --- .../src/main/resources/CityDoctorLocalization_de.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 932c673e..57f026e9 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -47,7 +47,7 @@ Checker.noCheckSatisfiesRequirement=Keine Pr\u00fcfung erf\u00fcllt die Anforder Checker.schematronCompleted=Schematron-Pr\u00fcfung abgeschlossen Checker.schematronFileNotFound=Schematron Datei {} konnte nicht gefunden werden Checker.schematronInterrupted=Main Thread hat Interrupt-Befehl w\u00e4hrend dem Warten auf Schematron Abschluss erhalten! -Checker.schematronStillRunning=Schematron-Pr\u00fcfung ist noch nicht abgeschlossen. Warten auf Abschluss der Pr\u00fcung, dies kann einiges an Zeit beanspruchen +Checker.schematronStillRunning=Schematron-Pr\u00fcfung ist noch nicht abgeschlossen. Warten auf Abschluss der Pr\u00fcfung, dies kann einiges an Zeit beanspruchen Checker.schematronValidation=Schematron-Pr\u00fcfung gestartet Checker.shutdownInterrupted=Main-Thread hat einen Interrupt-Befehl während dem ThreadPool shutdown erhalten Checker.validationInterrupted=Validierung abgebrochen: Pr\u00fcfung hat Interrupt-Befehl erhalten -- GitLab From 0cb2ed29757223f22688524df630bcb36cc51e14 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 1 Dec 2025 16:24:29 +0100 Subject: [PATCH 103/118] Refactor: Add state management to CityObjectCaches --- .../citydoctor2/database/CacheManager.java | 33 +-- .../citydoctor2/database/CityObjectCache.java | 14 +- .../citydoctor2/database/FeatureCache.java | 228 ++++++++++++++++-- .../database/UnconnectedCache.java | 31 ++- .../datastructure/CityDoctorModel.java | 1 + .../citygml3/Citygml3FeatureMapper.java | 8 +- .../citydoctor2/parser/CityGmlParser.java | 100 ++++---- .../database/FeatureCacheTest.java | 19 +- .../stuttgart/citydoctor2/check/Checker.java | 22 +- 9 files changed, 333 insertions(+), 123 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java index 56f471b4..36ed095e 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java @@ -1,17 +1,14 @@ package de.hft.stuttgart.citydoctor2.database; -import de.hft.stuttgart.citydoctor2.datastructure.GmlId; - - -import java.util.List; /** - * Utility class for accessing the FeatureCache instance. Enables access to the cache in contexts without reference or - * pass through of the {@link de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel CityDoctorModel}. + * Utility class for accessing and managing the FeatureCache instance. Enables access to the cache in contexts without + * reference or pass through of the {@link de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel CityDoctorModel}. */ public class CacheManager { - private static FeatureCache cache = null; + + private static final FeatureCache cache = new FeatureCache(); private CacheManager() {} @@ -19,31 +16,9 @@ public class CacheManager { * Returns the FeatureCache instance */ public static FeatureCache getFeatureCache() { - - if (cache == null){ - cache = new FeatureCache(); - } return cache; } - /** - * Signals the cache that parsing has finished - */ - public static void finishParsing(){ - if (cache != null){ - cache.finishParsing(); - } - } - - /** - * Signals the cache to synchronize its content with the database. - */ - public static void synchronizeCache(){ - if (cache != null){ - cache.synchronize(); - } - } - /** * Returns a new UnconnectedCache Object. * @return diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java index 891058bf..30b5fc69 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java @@ -9,7 +9,7 @@ import java.util.stream.Stream; public interface CityObjectCache { /** - * Adds a CityObject to this cache, overwriting existing CityObjects with the same GmlID + * Inserts a CityObject into this cache. Overwrites any existing CityObject with the same GmlID. * @param cityObject */ void put(CityObject cityObject); @@ -54,4 +54,16 @@ public interface CityObjectCache { */ List getErrorFeatureIds(); + /** + * Sets this cache into parsing mode, returning an AutoClosable representing the scoped-state of parsing operation. + * @return an AutoClosable representing the scoped-state for parsing + */ + AutoCloseable startParsingMode(); + + /** + * Sets this cache into checking mode, returning an AutoClosable representing the scoped-state of checking operation. + * @return an AutoClosable representing the scoped-state for checking + */ + AutoCloseable startCheckingMode(); + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index 7a779649..d93e16b3 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -11,20 +11,21 @@ import org.apache.logging.log4j.Logger; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; /** - * A managed cache for CityObjects. This cache is connected to the embedded database and will automatically load missing - * CityObjects from it. Has a hard limit on concurrently held CityObjects and will write them back to the database upon - * eviction. + * A managed cache for top-level CityObjects (Features). This cache is connected to the embedded database and will + * automatically load missing CityObjects from it. This cache is size limited and will evict entries using the + * Window Tiny LFU policy. *

- * This cache may only have a single instance, use {@link CacheManager} for instantiation. + * This cache may only have a single instance, use {@link CacheManager} for instantiation and access. *

*

* Avoid holding long-time strong references to CityObjects returned by the cache, as eviction will sever the - * connection between reference and cache, which can lead to conflicting Object states. + * connection between reference and cache, which will lead to conflicting Object states. *

*/ public class FeatureCache implements CityObjectCache{ @@ -35,32 +36,50 @@ public class FeatureCache implements CityObjectCache{ private final LoadingCache cache; private final EmbeddedDatabaseHandler handler; private final AtomicInteger putsCounter = new AtomicInteger(0); - private boolean parsingFinished = false; + private CacheState state = CacheState.IDLE; + private final ConcurrentHashMap evictionLocks = new ConcurrentHashMap<>(); FeatureCache() { handler = new EmbeddedDatabaseHandler(); cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> { - if (value != null && cause.wasEvicted()) { - handler.marshallCityObject(value); - } - }).build(handler::unmarshallCityObject); + if (value != null && cause.wasEvicted() && isFeatureMarshallable(value)){ + handler.marshallCityObject(value); + } + }).build(handler::unmarshallCityObject); + } + + /** + * Checks if the CityObject is currently being checked by a Checker-thread to prevent concurrent modifications by the + * Marshaller on eviction. + * @param co the CityObject + * @return true if the CityObject can be marshalled, false otherwise + */ + private boolean isFeatureMarshallable(CityObject co) { + return switch (state){ + case IDLE, PARSING -> true; + case CHECKING -> ! (evictionLocks.containsValue(co.getGmlId())); + }; } @Override public void put(CityObject cityObject) { cache.put(cityObject.getGmlId(), cityObject); - /* Batch marshall during parsing to improve performance - */ - if (!parsingFinished){ - int count = putsCounter.incrementAndGet(); - if (count >= 2500) { - batchMarshall(); - } + switch (state){ + case IDLE: + break; + case PARSING: + int count = putsCounter.incrementAndGet(); + if (count >= 2500) { + batchMarshall(); + } + break; + case CHECKING: + evictionLocks.remove(Thread.currentThread()); + break; } } - private void batchMarshall(){ logger.trace("Setting up batch Marshaller"); Map cacheContent= new HashMap<>(cache.asMap()); @@ -84,6 +103,9 @@ public class FeatureCache implements CityObjectCache{ @Override public CityObject get(GmlId id) { + if (state == CacheState.CHECKING){ + evictionLocks.put(Thread.currentThread(), id); + } return cache.get(id); } @@ -92,7 +114,7 @@ public class FeatureCache implements CityObjectCache{ @Override public void replace(GmlId id, CityObject cityObject) { cache.invalidate(id); - put(cityObject); + cache.put(cityObject.getGmlId(), cityObject); } @Override @@ -110,12 +132,12 @@ public class FeatureCache implements CityObjectCache{ logger.trace("Resetting cache"); cache.invalidateAll(); putsCounter.set(0); - parsingFinished = false; } @Override public List getErrorFeatureIds() { return handler.getIdsOfAllCityObjectsWithErrors(); + } /** @@ -132,15 +154,173 @@ public class FeatureCache implements CityObjectCache{ } } + /** + * Signals the cache that parsing is beginning. + */ + private boolean beginParsing(){ + return state.changeState(this, CacheState.PARSING); + } + /** * Signals the cache that parsing has finished. */ - public void finishParsing() { - if (!parsingFinished) { - synchronize(); - parsingFinished = true; + private void finishParsing() { + if (state == CacheState.PARSING && state.changeState(this, CacheState.IDLE)){ + return; } + throw new IllegalStateException("Cache failed to exit parsing mode"); + } + + /** + * Signals the cache that checking is beginning. + */ + private boolean beginChecking(){ + return state.changeState(this, CacheState.CHECKING); + } + + /** + * Signals the cache that checking has finished. + */ + private void finishChecking() { + if (state == CacheState.CHECKING && state.changeState(this, CacheState.IDLE)){ + return; + } + throw new IllegalStateException("Cache failed to exit checking mode"); + } + + @Override + public AutoCloseable startParsingMode(){ + if (this.beginParsing()){ + return this::finishParsing; + } else throw new IllegalStateException("Cache is not in IDLE state, cannot start parsing mode"); + } + + @Override + public AutoCloseable startCheckingMode(){ + if (this.beginChecking()){ + return this::finishChecking; + } else throw new IllegalStateException("Cache is not in IDLE state, cannot start checking mode"); + } + + protected void setState(CacheState state) { + this.state = state; + } + + public CacheState getState() { + return state; } + public enum CacheState { + IDLE { + @Override + protected boolean changeState(FeatureCache cache, CacheState nextState) { + if (cache.getState() != this){ + logger.debug("FeatureCache invoked changeState() of IDLE state while cache state is {}", cache.getState()); + return false; + } + this.onExit(cache); + cache.setState(nextState); + nextState.onEntry(cache); + return true; + } + + @Override + public void onEntry(FeatureCache cache) { + logger.trace("FeatureCache: Entering IDLE state"); + // Idle state does nothing on entry + } + + @Override + protected void onExit(FeatureCache cache) { + logger.trace("FeatureCache: Leaving IDLE state"); + // Idle state does nothing on exit + } + + }, PARSING { + @Override + protected boolean changeState(FeatureCache cache, CacheState nextState) { + if (cache.getState() != this){ + logger.debug("FeatureCache invoked changeState() of PARSING state while cache state is {}", cache.getState()); + return false; + } + boolean success = false; + switch (nextState){ + case IDLE: + this.onExit(cache); + cache.setState(CacheState.IDLE); + success = true; + break; + case PARSING: + logger.debug("Tried to set cache to PARSING state while already parsing"); + break; + case CHECKING: + logger.debug("Tried to set cache to CHECKING state while parsing"); + break; + } + return success; + } + + @Override + public void onEntry(FeatureCache cache) { + logger.trace("FeatureCache: Entering PARSING state"); + cache.resetCache(); + } + + @Override + protected void onExit(FeatureCache cache) { + logger.trace("FeatureCache: Leaving PARSING state"); + cache.synchronize(); + } + + }, CHECKING { + @Override + protected boolean changeState(FeatureCache cache, CacheState nextState) { + if (cache.getState() != this){ + logger.debug("FeatureCache invoked changeState() of CHECKING state while cache state is {}", cache.getState()); + return false; + } + boolean success = false; + switch (nextState){ + case IDLE: + this.onExit(cache); + cache.setState(CacheState.IDLE); + success = true; + break; + case PARSING: + logger.debug("Tried to set cache to PARSING state while checking"); + break; + case CHECKING: + logger.debug("Tried to set cache to CHECKING state while already checking"); + break; + } + return success; + } + + @Override + public void onEntry(FeatureCache cache) { + logger.trace("FeatureCache: Entering CHECKING state"); + + } + + @Override + protected void onExit(FeatureCache cache) { + logger.trace("FeatureCache: Leaving CHECKING state"); + cache.synchronize(); + } + + + }; + + /** + * Signals the statemachine to change the state of the FeatureCache. Returns true if the state successfully + * changed, false otherwise with the FeatureCache remaining in its current state. + * @param cache the FeatureCache instance + * @param nextState the requested next state + * @return true if the cache successfully changed state, false otherwise + */ + protected abstract boolean changeState(FeatureCache cache, CacheState nextState); + protected abstract void onEntry(FeatureCache cache); + protected abstract void onExit(FeatureCache cache); + } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java index 9fddf857..8033e634 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java @@ -1,5 +1,6 @@ package de.hft.stuttgart.citydoctor2.database; +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; @@ -10,13 +11,19 @@ import java.util.Map; import java.util.stream.Stream; /** - * A lightweight, unmanaged CityObjectCache that is not connected with the embedded Database. + * An adapter implementation for CityObjectCache, wrapping the Interface around a simple HashMap. + *

+ * This class is intended to allow parsing or checking {@link CityDoctorModel CityDoctorModels} in specific cases where the model + * must not use the {@link FeatureCache}, like resolving of {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject LibraryObject} + * files or mocking of the Interface in unit tests. + * */ public class UnconnectedCache implements CityObjectCache{ private final Map cache; + private boolean idle = true; - UnconnectedCache() { + public UnconnectedCache() { cache = new HashMap<>(); } @@ -32,7 +39,7 @@ public class UnconnectedCache implements CityObjectCache{ @Override public void replace(GmlId id, CityObject cityObject) { - // Not needed + cache.put(id, cityObject); } @Override @@ -46,7 +53,7 @@ public class UnconnectedCache implements CityObjectCache{ } public void resetCache(){ - // + cache.clear(); } @Override @@ -59,4 +66,20 @@ public class UnconnectedCache implements CityObjectCache{ }); return errorFeatureIds; } + + @Override + public AutoCloseable startParsingMode() { + if (idle){ + idle = false; + return ()-> idle=true; + } else throw new IllegalStateException("Cache is not idle, could not start parsing mode"); + } + + @Override + public AutoCloseable startCheckingMode() { + if (idle){ + idle = false; + return ()-> idle=true; + } else throw new IllegalStateException("Cache is not idle, could not start checking mode"); + } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index b2b353bc..0de518b0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -92,6 +92,7 @@ public class CityDoctorModel { genericObjects = new ArrayList<>(); globalErrors = new ArrayList<>(); this.cache = cache; + cache.resetCache(); } public boolean isValidated() { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index 0216f946..b2502be2 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import de.hft.stuttgart.citydoctor2.database.CacheManager; +import de.hft.stuttgart.citydoctor2.database.UnconnectedCache; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.CityGMLVersion; @@ -172,11 +173,12 @@ public class Citygml3FeatureMapper extends ObjectWalker { private List references = new ArrayList<>(); private Map vertexMap = new HashMap<>(); - public Citygml3FeatureMapper(ParserConfiguration config, Path path, boolean nested) { + public Citygml3FeatureMapper(ParserConfiguration config, Path path, boolean unconnected) { this.config = config; this.directory = path.getParent(); - if (nested){ - model = new CityDoctorModel(config, path.toFile(), CacheManager.getUnconnectedCache()); + + if (unconnected){ + model = new CityDoctorModel(config, path.toFile(), new UnconnectedCache()); } else { model = new CityDoctorModel(config, path.toFile()); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index b7c42265..1d4d67c0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -44,7 +44,6 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; -import de.hft.stuttgart.citydoctor2.database.CacheManager; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; import org.apache.logging.log4j.Level; @@ -254,13 +253,14 @@ public class CityGmlParser { * @param l Listener hook for tracking of parsing progress * @param handler Validation handler for GML-file validation * @param verbose If verbosity is enabled the parser will write info logs to the logger - * @param nested If nested is enabled the parser will use an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache} + * @param unconnected If true the parser will use an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache} + * to collect the parsed CityObjects * @return A {@link CityDoctorModel} object with the parsed Features * @throws CityGmlParseException if parsing of the CityGML-file failed * @throws InvalidGmlFileException if the GMLValidationHandler found an error */ private static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l, - GMLValidationHandler handler, boolean verbose, boolean nested) + GMLValidationHandler handler, boolean verbose, boolean unconnected) throws CityGmlParseException, InvalidGmlFileException { CityGMLContext context = getContext(); Path file = Paths.get(filePath); @@ -279,7 +279,7 @@ public class CityGmlParser { if (l != null) { ois.addListener(l::updateProgress); } - return readAndKeepFeatures(config, file, in, ois, verbose, nested); + return readAndKeepFeatures(config, file, in, ois, verbose, unconnected); } } catch (CityGMLReadException | IOException e) { throw new CityGmlParseException("Failed to read CityGML file", e); @@ -431,8 +431,8 @@ public class CityGmlParser { private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file, CityGMLInputFactory inputFactory, ObservedInputStream ois, - boolean verbose, boolean nested) throws CityGMLReadException { - return readAndKeepModel(new Citygml3FeatureMapper(config, file, nested), inputFactory, ois, verbose); + boolean verbose, boolean unconnected) throws CityGMLReadException { + return readAndKeepModel(new Citygml3FeatureMapper(config, file, unconnected), inputFactory, ois, verbose); } private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory, @@ -445,57 +445,61 @@ public class CityGmlParser { List acos = new ArrayList<>(); CityModel cgmlModel = null; CityDoctorModel cdModel = mapper.getModel(); - cdModel.getCache().resetCache(); - List queuedObjects = new ArrayList<>(); - while (reader.hasNext()) { - AbstractFeature chunk = reader.next(); - version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); - if (chunk instanceof CityModel cModel) { - cModel.setCityObjectMembers(null); - mapper.setCityModel(cModel); - mapper.setCityGMLVersion(version); - cgmlModel = cModel; - - if (!queuedObjects.isEmpty()) { - for (AbstractCityObject aco : queuedObjects) { - if (!cdModel.containsGmlId(aco.getId())) { - cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + AutoCloseable parsingContext = cdModel.getCache().startParsingMode(); + try (parsingContext){ + List queuedObjects = new ArrayList<>(); + while (reader.hasNext()) { + AbstractFeature chunk = reader.next(); + version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); + if (chunk instanceof CityModel cModel) { + cModel.setCityObjectMembers(null); + mapper.setCityModel(cModel); + mapper.setCityGMLVersion(version); + cgmlModel = cModel; + + if (!queuedObjects.isEmpty()) { + for (AbstractCityObject aco : queuedObjects) { + if (!cdModel.containsGmlId(aco.getId())) { + cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + } } } - } - } else if (chunk instanceof AbstractCityObject aco) { - acos.add(aco); // getid - String id = aco.getId(); - aco.accept(mapper); - if (cgmlModel == null) { - // Add object to queue till the CityModel has been parsed - queuedObjects.add(aco); - }else if (!cdModel.containsGmlId(id)){ - cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + } else if (chunk instanceof AbstractCityObject aco) { + acos.add(aco); // getid + String id = aco.getId(); + aco.accept(mapper); + if (cgmlModel == null) { + // Add object to queue till the CityModel has been parsed + queuedObjects.add(aco); + }else if (!cdModel.containsGmlId(id)){ + cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + } } } - } - if (mapper.getModel().getCityModel() == null) { - // 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) { - if (!cdModel.containsGmlId(aco.getId())) { - mapper.getModel().getCityModel().getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + if (mapper.getModel().getCityModel() == null) { + // 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) { + if (!cdModel.containsGmlId(aco.getId())) { + mapper.getModel().getCityModel().getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + } } } - } - ImplicitGeometryHolder.resolveDeferredObjects(mapper.getModel().getCache()); - if (logger.isInfoEnabled() && verbose) { - logger.info(Localization.getText("CityGmlParser.parsedObjects"), - mapper.getModel().getNumberOfFeatures()); + ImplicitGeometryHolder.resolveDeferredObjects(mapper.getModel().getCache()); + if (logger.isInfoEnabled() && verbose) { + logger.info(Localization.getText("CityGmlParser.parsedObjects"), + mapper.getModel().getNumberOfFeatures()); + } + mapper.setCityGMLVersion(version); + return mapper.getModel(); + } catch (Exception e) { + throw new CityGMLReadException(e); } - mapper.setCityGMLVersion(version); - CacheManager.finishParsing(); - return mapper.getModel(); + } } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java index ba690ffe..711ee15b 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java @@ -5,15 +5,16 @@ import org.citygml4j.core.model.building.AbstractBuilding; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; public class FeatureCacheTest { - @Test public void testPut(){ - FeatureCache cache = new FeatureCache(); + FeatureCache cache = CacheManager.getFeatureCache(); + Building b = new Building(); b.setGmlObject(mock(AbstractBuilding.class)); b.setGmlId(new GmlId("test")); @@ -21,18 +22,22 @@ public class FeatureCacheTest { assertEquals(0, cache.getTotalFeatureCount()); cache.put(b); - cache.finishParsing(); + + assertEquals(1, cache.getTotalFeatureCount()); } @Test public void testGet(){ - FeatureCache cache = new FeatureCache(); - EmbeddedDatabaseHandler handler = new EmbeddedDatabaseHandler(); + FeatureCache cache = CacheManager.getFeatureCache(); + assertNull(cache.get(new GmlId("building"))); Building b = new Building(); - b.setGmlId(new GmlId("building")); + + b.setGmlObject(mock(AbstractBuilding.class)); + b.setGmlId(new GmlId("test")); b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); - handler.marshallCityObject(b); + assertEquals(0, cache.getTotalFeatureCount()); + cache.put(b); CityObject b2 = cache.get(new GmlId("building")); assertEquals(b.getGmlId(), b2.getGmlId()); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 06870162..91f4db4f 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -193,13 +193,23 @@ public class Checker { if (config == null) { config = ValidationConfiguration.loadStandardValidationConfig(); } + AutoCloseable checkingContext = model.getCache().startCheckingMode(); + try(checkingContext){ + Callable schematronThread = () -> executeSchematronValidationIfAvailable(config, model.getFile()); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future handlerFuture = executor.submit(schematronThread); + executor.shutdown(); - Callable schematronThread = () -> executeSchematronValidationIfAvailable(config, model.getFile()); - ExecutorService executor = Executors.newSingleThreadExecutor(); - Future handlerFuture = executor.submit(schematronThread); - executor.shutdown(); + checkCityModel(model, l); + getSchematronResults(handlerFuture, l); - checkCityModel(model, l); + model.setValidated(createValidationPlan()); + } catch (Exception e){ + logger.error("Validation failed due to encountering error", e); + } + } + + private void getSchematronResults(Future handlerFuture, ProgressListener l) { if (!handlerFuture.isDone()) { if (logger.isInfoEnabled()) { logger.info(Localization.getText("Checker.schematronStillRunning")); @@ -219,8 +229,6 @@ public class Checker { logger.error(Localization.getText("Checker.schematronInterrupted"), e); Thread.currentThread().interrupt(); } - CacheManager.synchronizeCache(); - model.setValidated(createValidationPlan()); } private void handleSchematronResults(SvrlContentHandler handler) { -- GitLab From 3322587168fd9342cf4a73849d0f74f86d8ba848 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 1 Dec 2025 16:28:38 +0100 Subject: [PATCH 104/118] Fix: Correct typo in AppenderRef for Hikari logging --- CityDoctorParent/CityDoctorModel/src/main/resources/log4j2.xml | 2 +- .../CityDoctorValidation/src/main/resources/log4j2.xml | 2 +- .../Extensions/CityDoctorGUI/src/main/resources/log4j2.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/log4j2.xml b/CityDoctorParent/CityDoctorModel/src/main/resources/log4j2.xml index 9f04f6cb..d398aaaa 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/log4j2.xml +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/log4j2.xml @@ -15,7 +15,7 @@ - + \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorValidation/src/main/resources/log4j2.xml b/CityDoctorParent/CityDoctorValidation/src/main/resources/log4j2.xml index b1b870e0..1b1a09f3 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/resources/log4j2.xml +++ b/CityDoctorParent/CityDoctorValidation/src/main/resources/log4j2.xml @@ -12,7 +12,7 @@ - + \ No newline at end of file diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/log4j2.xml b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/log4j2.xml index 88b67835..e2d7657f 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/log4j2.xml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/log4j2.xml @@ -20,7 +20,7 @@ - + \ No newline at end of file -- GitLab From 92332c37a6c9504bae20fb8f3cbb0a551d3def68 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 1 Dec 2025 18:01:01 +0100 Subject: [PATCH 105/118] Refactor: Decouple FeatureCache from other classes --- .../citydoctor2/database/CacheManager.java | 30 --------- .../citydoctor2/database/FeatureCache.java | 7 ++- .../datastructure/CityDoctorModel.java | 6 -- .../citygml3/Citygml3FeatureMapper.java | 6 +- .../database/FeatureCacheTest.java | 62 +++++++++++++++++-- .../datastructure/CityDoctorModelTest.java | 51 +++------------ .../stuttgart/citydoctor2/check/Checker.java | 4 +- .../geometry/DegeneratedPolygonCheckTest.java | 3 +- .../citydoctor2/gui/CityDoctorController.java | 5 +- 9 files changed, 80 insertions(+), 94 deletions(-) delete mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java deleted file mode 100644 index 36ed095e..00000000 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CacheManager.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.hft.stuttgart.citydoctor2.database; - - -/** - * Utility class for accessing and managing the FeatureCache instance. Enables access to the cache in contexts without - * reference or pass through of the {@link de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel CityDoctorModel}. - */ -public class CacheManager { - - - private static final FeatureCache cache = new FeatureCache(); - - private CacheManager() {} - - /** - * Returns the FeatureCache instance - */ - public static FeatureCache getFeatureCache() { - return cache; - } - - /** - * Returns a new UnconnectedCache Object. - * @return - */ - public static UnconnectedCache getUnconnectedCache(){ - return new UnconnectedCache(); - } - -} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java index d93e16b3..b797f208 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java @@ -21,7 +21,9 @@ import java.util.stream.Stream; * automatically load missing CityObjects from it. This cache is size limited and will evict entries using the * Window Tiny LFU policy. *

- * This cache may only have a single instance, use {@link CacheManager} for instantiation and access. + * While this class is not a singleton, it is advised to keep an Instance of this class for the whole lifecycle and + * reset and reuse it instead of creating new Objects, as the initialization is quite heavy and resets the embedded + * database. *

*

* Avoid holding long-time strong references to CityObjects returned by the cache, as eviction will sever the @@ -39,7 +41,7 @@ public class FeatureCache implements CityObjectCache{ private CacheState state = CacheState.IDLE; private final ConcurrentHashMap evictionLocks = new ConcurrentHashMap<>(); - FeatureCache() { + public FeatureCache() { handler = new EmbeddedDatabaseHandler(); cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> { if (value != null && cause.wasEvicted() && isFeatureMarshallable(value)){ @@ -132,6 +134,7 @@ public class FeatureCache implements CityObjectCache{ logger.trace("Resetting cache"); cache.invalidateAll(); putsCounter.set(0); + handler.setupFeaturesTable(); } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 0de518b0..10cacbec 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -21,7 +21,6 @@ package de.hft.stuttgart.citydoctor2.datastructure; import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; -import de.hft.stuttgart.citydoctor2.database.CacheManager; import de.hft.stuttgart.citydoctor2.database.CityObjectCache; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; @@ -67,10 +66,6 @@ public class CityDoctorModel { private CityGMLVersion cityGMLVersion; private final CityObjectCache cache; - public CityDoctorModel(ParserConfiguration config, File file) { - this(config, file, CacheManager.getFeatureCache()); - } - public CityDoctorModel(ParserConfiguration config, File file, CityObjectCache cache) { if (config == null) { throw new IllegalArgumentException("Parser configuration may not be null"); @@ -92,7 +87,6 @@ public class CityDoctorModel { genericObjects = new ArrayList<>(); globalErrors = new ArrayList<>(); this.cache = cache; - cache.resetCache(); } public boolean isValidated() { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index b2502be2..249a2488 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -24,7 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import de.hft.stuttgart.citydoctor2.database.CacheManager; +import de.hft.stuttgart.citydoctor2.database.FeatureCache; import de.hft.stuttgart.citydoctor2.database.UnconnectedCache; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -164,6 +164,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { private static final Logger logger = LogManager.getLogger(Citygml3FeatureMapper.class); + private static FeatureCache featureCache = new FeatureCache(); private final CityDoctorModel model; private final ParserConfiguration config; private final Path directory; @@ -180,7 +181,8 @@ public class Citygml3FeatureMapper extends ObjectWalker { if (unconnected){ model = new CityDoctorModel(config, path.toFile(), new UnconnectedCache()); } else { - model = new CityDoctorModel(config, path.toFile()); + featureCache.resetCache(); + model = new CityDoctorModel(config, path.toFile(), featureCache); } neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces()); } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java index 711ee15b..b17640f6 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/FeatureCacheTest.java @@ -6,14 +6,16 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.mock; public class FeatureCacheTest { + @Test public void testPut(){ - FeatureCache cache = CacheManager.getFeatureCache(); + FeatureCache cache = new FeatureCache(); Building b = new Building(); b.setGmlObject(mock(AbstractBuilding.class)); @@ -22,15 +24,16 @@ public class FeatureCacheTest { assertEquals(0, cache.getTotalFeatureCount()); cache.put(b); - + cache.synchronize(); assertEquals(1, cache.getTotalFeatureCount()); } @Test public void testGet(){ - FeatureCache cache = CacheManager.getFeatureCache(); - assertNull(cache.get(new GmlId("building"))); + FeatureCache cache = new FeatureCache(); + + assertNull(cache.get(new GmlId("test"))); Building b = new Building(); b.setGmlObject(mock(AbstractBuilding.class)); @@ -38,10 +41,59 @@ public class FeatureCacheTest { b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); assertEquals(0, cache.getTotalFeatureCount()); cache.put(b); + cache.synchronize(); - CityObject b2 = cache.get(new GmlId("building")); + CityObject b2 = cache.get(new GmlId("test")); assertEquals(b.getGmlId(), b2.getGmlId()); } + @Test + public void testStateHandlingFromIdle(){ + FeatureCache cache = new FeatureCache(); + assertEquals(FeatureCache.CacheState.IDLE, cache.getState()); + + AutoCloseable parsingContext = cache.startParsingMode(); + try(parsingContext){ + assertEquals(FeatureCache.CacheState.PARSING, cache.getState()); + } catch (Exception e){ + throw new RuntimeException(e); + } + assertEquals(FeatureCache.CacheState.IDLE, cache.getState()); + + AutoCloseable checkingContext = cache.startCheckingMode(); + try(checkingContext){ + assertEquals(FeatureCache.CacheState.CHECKING, cache.getState()); + } catch (Exception e){ + throw new RuntimeException(e); + } + assertEquals(FeatureCache.CacheState.IDLE, cache.getState()); + } + + @Test + public void testCheckingModeStateCollision(){ + FeatureCache cache = new FeatureCache(); + assertEquals(FeatureCache.CacheState.IDLE, cache.getState()); + AutoCloseable checkingContext = cache.startCheckingMode(); + try(checkingContext){ + assertEquals(FeatureCache.CacheState.CHECKING, cache.getState()); + assertThrows(IllegalStateException.class, cache::startParsingMode); + } catch (Exception e){ + throw new RuntimeException(e); + } + } + + @Test + public void testParsingModeStateCollision(){ + FeatureCache cache = new FeatureCache(); + assertEquals(FeatureCache.CacheState.IDLE, cache.getState()); + AutoCloseable parsingContext = cache.startParsingMode(); + try(parsingContext){ + assertEquals(FeatureCache.CacheState.PARSING, cache.getState()); + assertThrows(IllegalStateException.class, cache::startCheckingMode); + } catch (Exception e){ + throw new RuntimeException(e); + } + } + } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java index b32e4d39..edc51f6c 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java @@ -27,6 +27,8 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import de.hft.stuttgart.citydoctor2.database.FeatureCache; +import de.hft.stuttgart.citydoctor2.database.UnconnectedCache; import org.citygml4j.core.ade.ADEException; import org.citygml4j.core.model.CityGMLVersion; import org.citygml4j.core.model.core.AbstractFeatureProperty; @@ -66,12 +68,15 @@ public class CityDoctorModelTest { private File mockFile; private ParserConfiguration config; - + private CityDoctorModel model; + private final FeatureCache featureCache = new FeatureCache(); @Before public void setUp(){ mockFile = Mockito.mock(File.class); Mockito.when(mockFile.getName()).thenReturn("test"); config = new ParserConfiguration(8, false); + featureCache.resetCache(); + model = new CityDoctorModel(config, mockFile, featureCache); } @Rule @@ -79,24 +84,22 @@ public class CityDoctorModelTest { @Test(expected = IllegalArgumentException.class) public void testCityDoctorModelNoConfig() { - new CityDoctorModel(null, mockFile); + new CityDoctorModel(null, mockFile, new UnconnectedCache()); } @Test(expected = IllegalArgumentException.class) public void testCityDoctorModelNoFile() { - new CityDoctorModel(config, null); + new CityDoctorModel(config, null, new UnconnectedCache()); } @Test public void testSetValidated() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); model.setValidated(Mockito.mock(ValidationPlan.class)); assertTrue(model.isValidated()); } @Test public void testAddGlobalError() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); CheckError err = Mockito.mock(CheckError.class); model.addGlobalError(err); assertEquals(err, model.getGlobalErrors().get(0)); @@ -104,7 +107,6 @@ public class CityDoctorModelTest { @Test public void testAddGlobalErrors() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); CheckError err1 = Mockito.mock(CheckError.class); CheckError err2 = Mockito.mock(CheckError.class); List errors = new ArrayList<>(); @@ -117,13 +119,11 @@ public class CityDoctorModelTest { @Test public void testGetFile() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); assertEquals(mockFile, model.getFile()); } @Test public void testCreateFeatureStream() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b = Mockito.mock(Building.class); Mockito.when(b.getGmlId()).thenReturn(new GmlId("b")); Vegetation v = Mockito.mock(Vegetation.class); @@ -137,7 +137,6 @@ public class CityDoctorModelTest { @Test public void testSaveAsWithoutValidation() throws CityGMLWriteException, ADEException, IOException, CityGmlParseException, InvalidGmlFileException, CityDoctorWriteException { - CityDoctorModel model = new CityDoctorModel(config, mockFile); model.setParsedCityGMLVersion(CityGMLVersion.v2_0); CityModel cModel = new CityModel(); model.setCityModel(cModel); @@ -161,7 +160,7 @@ public class CityDoctorModelTest { @Test public void testSaveAsWithValidation() throws CityGMLWriteException, ADEException, IOException, CityGmlParseException, InvalidGmlFileException, CityDoctorWriteException { - CityDoctorModel model = new CityDoctorModel(config, mockFile); + model.setParsedCityGMLVersion(CityGMLVersion.v2_0); CityModel cModel = new CityModel(); ValidationPlan plan = new ValidationPlan(); @@ -238,7 +237,6 @@ public class CityDoctorModelTest { @Test public void testCollectErrors() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b = new Building(); model.addBuilding(b); CheckError error = Mockito.mock(CheckError.class); @@ -249,14 +247,12 @@ public class CityDoctorModelTest { @Test public void testGetFileName() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); assertEquals("test", model.getFileName()); assertEquals(mockFile, model.getFile()); } @Test public void testSetCityModel() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); CityModel cModel = Mockito.mock(CityModel.class); model.setCityModel(cModel); assertEquals(cModel, model.getCityModel()); @@ -264,7 +260,6 @@ public class CityDoctorModelTest { @Test public void testAddBuilding() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b = Mockito.mock(Building.class); Mockito.when(b.getGmlId()).thenReturn(new GmlId("b")); model.addBuilding(b); @@ -273,7 +268,6 @@ public class CityDoctorModelTest { @Test public void testAddBridge() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); BridgeObject b = Mockito.mock(BridgeObject.class); Mockito.when(b.getGmlId()).thenReturn(new GmlId("b")); model.addBridge(b); @@ -282,7 +276,6 @@ public class CityDoctorModelTest { @Test public void testAddVegetation() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Vegetation veg = Mockito.mock(Vegetation.class); Mockito.when(veg.getGmlId()).thenReturn(new GmlId("v")); model.addVegetation(veg); @@ -291,7 +284,6 @@ public class CityDoctorModelTest { @Test public void testAddTunnel() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Tunnel tunnel = Mockito.mock(Tunnel.class); Mockito.when(tunnel.getGmlId()).thenReturn(new GmlId("t")); model.addTunnel(tunnel); @@ -301,7 +293,6 @@ public class CityDoctorModelTest { @Test public void testAddTransportation() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); TransportationObject trans = Mockito.mock(TransportationObject.class); Mockito.when(trans.getGmlId()).thenReturn(new GmlId("t")); model.addTransportation(trans); @@ -311,7 +302,6 @@ public class CityDoctorModelTest { @Test public void testAddLand() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); LandObject land = Mockito.mock(LandObject.class); Mockito.when(land.getGmlId()).thenReturn(new GmlId("l")); model.addLand(land); @@ -320,7 +310,6 @@ public class CityDoctorModelTest { @Test public void testAddWater() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); WaterObject water = Mockito.mock(WaterObject.class); Mockito.when(water.getGmlId()).thenReturn(new GmlId("w")); model.addWater(water); @@ -329,7 +318,6 @@ public class CityDoctorModelTest { @Test public void testAddCityFurniture() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); CityFurniture cf = Mockito.mock(CityFurniture.class); Mockito.when(cf.getGmlId()).thenReturn(new GmlId("cf")); model.addCityFurniture(cf); @@ -338,7 +326,6 @@ public class CityDoctorModelTest { @Test public void testAddGenericCityObject() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); GenericCityObject gen = Mockito.mock(GenericCityObject.class); Mockito.when(gen.getGmlId()).thenReturn(new GmlId("g")); model.addGenericCityObject(gen); @@ -347,7 +334,6 @@ public class CityDoctorModelTest { @Test public void testGetNumberOfFeatures() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b = Mockito.mock(Building.class); Mockito.when(b.getGmlId()).thenReturn(new GmlId("b")); @@ -384,13 +370,11 @@ public class CityDoctorModelTest { @Test public void testGetParserConfig() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); assertEquals(config, model.getParserConfig()); } @Test public void testReplaceBuilding() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b1 = Mockito.mock(Building.class); Mockito.when(b1.getGmlId()).thenReturn(new GmlId("b1")); Building b2 = Mockito.mock(Building.class); @@ -405,7 +389,6 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceBuildingMissing() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Building b1 = Mockito.mock(Building.class); Building b2 = Mockito.mock(Building.class); model.replaceFeature(b1, b2); @@ -413,7 +396,6 @@ public class CityDoctorModelTest { @Test public void testReplaceBridge() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); BridgeObject b1 = Mockito.mock(BridgeObject.class); Mockito.when(b1.getGmlId()).thenReturn(new GmlId("b1")); BridgeObject b2 = Mockito.mock(BridgeObject.class); @@ -427,7 +409,6 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceBridgeMissing() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); BridgeObject b1 = Mockito.mock(BridgeObject.class); BridgeObject b2 = Mockito.mock(BridgeObject.class); model.replaceFeature(b1, b2); @@ -435,7 +416,6 @@ public class CityDoctorModelTest { @Test public void testReplaceVegetation() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Vegetation v1 = Mockito.mock(Vegetation.class); Mockito.when(v1.getGmlId()).thenReturn(new GmlId("v1")); Vegetation v2 = Mockito.mock(Vegetation.class); @@ -450,7 +430,6 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceVegetationMissing() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Vegetation v1 = Mockito.mock(Vegetation.class); Vegetation v2 = Mockito.mock(Vegetation.class); model.replaceFeature(v1, v2); @@ -458,7 +437,6 @@ public class CityDoctorModelTest { @Test public void testReplaceTunnel() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Tunnel t1 = Mockito.mock(Tunnel.class); Mockito.when(t1.getGmlId()).thenReturn(new GmlId("t1")); Tunnel t2 = Mockito.mock(Tunnel.class); @@ -473,7 +451,6 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceTunnelMissing() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); Tunnel t1 = Mockito.mock(Tunnel.class); Tunnel t2 = Mockito.mock(Tunnel.class); model.replaceFeature(t1, t2); @@ -481,7 +458,6 @@ public class CityDoctorModelTest { @Test public void testReplaceTransportation() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); TransportationObject t1 = Mockito.mock(TransportationObject.class); Mockito.when(t1.getGmlId()).thenReturn(new GmlId("t1")); TransportationObject t2 = Mockito.mock(TransportationObject.class); @@ -496,7 +472,6 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceTransportationMissing() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); TransportationObject t1 = Mockito.mock(TransportationObject.class); TransportationObject t2 = Mockito.mock(TransportationObject.class); model.replaceFeature(t1, t2); @@ -504,7 +479,6 @@ public class CityDoctorModelTest { @Test public void testReplaceLand() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); LandObject lo1 = Mockito.mock(LandObject.class); Mockito.when(lo1.getGmlId()).thenReturn(new GmlId("lo1")); LandObject lo2 = Mockito.mock(LandObject.class); @@ -519,7 +493,6 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceLandMissing() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); LandObject lo1 = Mockito.mock(LandObject.class); LandObject lo2 = Mockito.mock(LandObject.class); model.replaceFeature(lo1, lo2); @@ -527,7 +500,6 @@ public class CityDoctorModelTest { @Test public void testReplaceWater() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); WaterObject t1 = Mockito.mock(WaterObject.class); Mockito.when(t1.getGmlId()).thenReturn(new GmlId("t1")); WaterObject t2 = Mockito.mock(WaterObject.class); @@ -542,7 +514,6 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceWaterMissing() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); WaterObject t1 = Mockito.mock(WaterObject.class); WaterObject t2 = Mockito.mock(WaterObject.class); model.replaceFeature(t1, t2); @@ -550,7 +521,6 @@ public class CityDoctorModelTest { @Test public void testReplaceCityFurniture() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); CityFurniture cf1 = Mockito.mock(CityFurniture.class); Mockito.when(cf1.getGmlId()).thenReturn(new GmlId("cf1")); CityFurniture cf2 = Mockito.mock(CityFurniture.class); @@ -565,7 +535,6 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceCityFurnitureMissing() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); CityFurniture cf1 = Mockito.mock(CityFurniture.class); CityFurniture cf2 = Mockito.mock(CityFurniture.class); model.replaceFeature(cf1, cf2); @@ -573,7 +542,6 @@ public class CityDoctorModelTest { @Test public void testReplaceGenericCityObject() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); GenericCityObject gco1 = Mockito.mock(GenericCityObject.class); Mockito.when(gco1.getGmlId()).thenReturn(new GmlId("gco1")); GenericCityObject gco2 = Mockito.mock(GenericCityObject.class); @@ -588,7 +556,6 @@ public class CityDoctorModelTest { @Test(expected = IllegalStateException.class) public void testReplaceGenericCityObjectMissing() { - CityDoctorModel model = new CityDoctorModel(config, mockFile); GenericCityObject gco1 = Mockito.mock(GenericCityObject.class); GenericCityObject gco2 = Mockito.mock(GenericCityObject.class); model.replaceFeature(gco1, gco2); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index 91f4db4f..a1dd7bcf 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -28,9 +28,7 @@ import de.hft.stuttgart.citydoctor2.checks.CheckPrototype; import de.hft.stuttgart.citydoctor2.checks.Checks; import de.hft.stuttgart.citydoctor2.checks.SvrlContentHandler; import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener; -import de.hft.stuttgart.citydoctor2.database.CacheManager; import de.hft.stuttgart.citydoctor2.database.CityObjectCache; -import de.hft.stuttgart.citydoctor2.database.FeatureCache; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; @@ -233,7 +231,7 @@ public class Checker { private void handleSchematronResults(SvrlContentHandler handler) { model.addGlobalErrors(handler.getGeneralErrors()); - FeatureCache cache = CacheManager.getFeatureCache(); + CityObjectCache cache = model.getCache(); handler.getFeatureErrors().forEach((k, v) -> { if (k.trim().isEmpty()) { // missing gml id, ignore? diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java index 152790e5..7859eb72 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/DegeneratedPolygonCheckTest.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import de.hft.stuttgart.citydoctor2.database.UnconnectedCache; import org.junit.Test; import de.hft.stuttgart.citydoctor2.check.CheckError; @@ -72,7 +73,7 @@ public class DegeneratedPolygonCheckTest { File file = Mockito.mock(File.class); Mockito.when(file.getName()).thenReturn("building"); ParserConfiguration config = new ParserConfiguration(8, false); - CityDoctorModel model = new CityDoctorModel(config, file); + CityDoctorModel model = new CityDoctorModel(config, file, new UnconnectedCache()); model.addBuilding(b); ValidationConfiguration valConfig = ValidationConfiguration.loadStandardValidationConfig(); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index 84949209..b3af18a3 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -1,12 +1,11 @@ package de.hft.stuttgart.citydoctor2.gui; import de.hft.stuttgart.citydoctor2.check.CheckError; -import de.hft.stuttgart.citydoctor2.check.Checkable; import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.ErrorId; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.check.error.SchematronError; -import de.hft.stuttgart.citydoctor2.database.CacheManager; +import de.hft.stuttgart.citydoctor2.database.UnconnectedCache; import de.hft.stuttgart.citydoctor2.datastructure.*; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.gui.table.ErrorStat; @@ -1174,7 +1173,7 @@ public class CityDoctorController { if (model == null) { return; } - CityDoctorModel newModel = new CityDoctorModel(model.getParserConfig(), model.getFile()); + CityDoctorModel newModel = new CityDoctorModel(model.getParserConfig(), model.getFile(), new UnconnectedCache()); newModel.setCityModel(new CityModel()); newModel.addCityObject(co); newModel.setParsedCityGMLVersion(model.getCityGMLVersion()); -- GitLab From 63e0db04bbb6feb66fd21d01794405ac3e3eed63 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 2 Dec 2025 15:48:07 +0100 Subject: [PATCH 106/118] Fix: Use Unicode in localization --- .../src/main/resources/CityDoctorLocalization_de.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 57f026e9..e9c1f7b6 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -43,13 +43,13 @@ Checker.failReports=Konnte Pr\u00fcfberichte nicht schreiben. Checker.failXml=Konnte XML-Fehlerbericht nicht schreiben. Checker.finishedSchematron=Schematron-Pr\u00fcfung abgeschlossen Checker.futuresCheckFailures=Einige Pr\u00fcfungen sind aufgrund folgender Fehler fehlgeschlagen: -Checker.noCheckSatisfiesRequirement=Keine Pr\u00fcfung erf\u00fcllt die Anforderung {}, Anforderung wird nicht geprüft +Checker.noCheckSatisfiesRequirement=Keine Pr\u00fcfung erf\u00fcllt die Anforderung {}, Anforderung wird nicht gepr\u00fcft Checker.schematronCompleted=Schematron-Pr\u00fcfung abgeschlossen Checker.schematronFileNotFound=Schematron Datei {} konnte nicht gefunden werden Checker.schematronInterrupted=Main Thread hat Interrupt-Befehl w\u00e4hrend dem Warten auf Schematron Abschluss erhalten! Checker.schematronStillRunning=Schematron-Pr\u00fcfung ist noch nicht abgeschlossen. Warten auf Abschluss der Pr\u00fcfung, dies kann einiges an Zeit beanspruchen Checker.schematronValidation=Schematron-Pr\u00fcfung gestartet -Checker.shutdownInterrupted=Main-Thread hat einen Interrupt-Befehl während dem ThreadPool shutdown erhalten +Checker.shutdownInterrupted=Main-Thread hat einen Interrupt-Befehl w\u00e4hrend dem ThreadPool shutdown erhalten Checker.validationInterrupted=Validierung abgebrochen: Pr\u00fcfung hat Interrupt-Befehl erhalten Checker.waitAndRetry=Wiederhole Pr\u00fcfung nach kurzer Wartezeit ... Checks.missingCheck=Konnte keine Pr\u00fcfung f\u00fcr id {} finden -- GitLab From 8129283a5016855abe26005257b4bf87ae185283 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 2 Dec 2025 15:48:54 +0100 Subject: [PATCH 107/118] Feat: Implement PicoCLI interface --- .../citydoctor2/CityDoctorValidationCLI.java | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidationCLI.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidationCLI.java index 505c5eb5..9003eb7b 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidationCLI.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/CityDoctorValidationCLI.java @@ -1,12 +1,19 @@ package de.hft.stuttgart.citydoctor2; -import java.util.concurrent.Callable; +import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; +import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; +import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; +import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.Option; +import java.io.File; +import java.io.IOException; + @Command(name = "citydoctor2", mixinStandardHelpOptions = true, description = "Validates CityGML files according to a provided validation-plan.") -public class CityDoctorValidationCLI implements Callable { +public class CityDoctorValidationCLI implements Runnable { @Option(names = {"-i" , "--in" , "--input"}, description = "Filepath of the CityGML file.", @@ -15,23 +22,41 @@ public class CityDoctorValidationCLI implements Callable { @Option(names = {"-c" , "--config" , "--configuration", "--plan"}, description = "Filepath of the validation configuration .yml", - required = true) - private String config; + defaultValue = Option.NULL_VALUE) + private File config; @Option(names = {"-x" , "--xml" , "--xmlReport"}, - description = "Create a XML validation report at target location") + description = "Create a XML validation report at target location", + defaultValue = Option.NULL_VALUE) private String xmlReport; @Option(names = {"-p" , "--pdf" , "--pdfReport"}, - description = "Create a PDF validation report at target location") + description = "Create a PDF validation report at target location", + defaultValue = Option.NULL_VALUE) private String pdfReport; @Option(names = {"-o" , "--out" , "--output"}, - description = "Create a validated copy of the CityGML input with QualityADE at target location.") + description = "Create a validated copy of the CityGML input with QualityADE at target location.", + defaultValue = Option.NULL_VALUE) private String output; @Override - public Integer call() throws Exception { - return 0; + public void run(){ + try { + ValidationConfiguration valConfig; + if(config == null){ + valConfig=ValidationConfiguration.loadStandardValidationConfig(); + } else{ + valConfig=ValidationConfiguration.loadValidationConfig(config.getAbsolutePath()); + } + CityDoctorValidation.startValidationProcess(input, xmlReport, pdfReport, valConfig,output); + } catch (CityDoctorWriteException | CityGmlParseException | IOException | InvalidGmlFileException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new CityDoctorValidationCLI()).execute(args); + System.exit(exitCode); } } -- GitLab From 8087b4bb4a0e2effe95d2eaecf0fd9547178db5a Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 2 Dec 2025 15:52:44 +0100 Subject: [PATCH 108/118] Fix: Clear ImplicitGeometry Object maps before parsing --- .../datastructure/ImplicitGeometryHolder.java | 15 +++++++++------ .../citydoctor2/datastructure/LibraryObject.java | 3 +++ .../datastructure/RelativeGeometry.java | 4 ++++ .../mapper/citygml3/Citygml3FeatureMapper.java | 1 + 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java index 75fe723c..e7405894 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java @@ -24,6 +24,9 @@ public class ImplicitGeometryHolder extends Geometry { private static final long serialVersionUID = -8938931081577196349L; private static final Logger logger = LogManager.getLogger(ImplicitGeometryHolder.class); + public enum PrototypeGeometryType { + LIBRARY_OBJECT, RELATIVE_GEOMETRY + } private ImplicitGeometry cgmlImplicitGeometry = null; private static ConcurrentHashMap deferredRelGeomObjects = new ConcurrentHashMap<>(); @@ -101,9 +104,12 @@ public class ImplicitGeometryHolder extends Geometry { return type; } - @Override - public void accept(CheckableVisitor c) { - prototypeGeometry.accept(c); + /** + * Resets the Object maps of {@link RelativeGeometry} and {@link LibraryObject} to prevent memory and data leaks + */ + public static void resetObjectMaps() { + RelativeGeometry.resetObjectMap(); + LibraryObject.resetObjectMap(); } @Override @@ -111,8 +117,5 @@ public class ImplicitGeometryHolder extends Geometry { return prototypeGeometry.isValidated(); } - public enum PrototypeGeometryType { - LIBRARY_OBJECT, RELATIVE_GEOMETRY - } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java index b81ca0f9..711d6f70 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java @@ -80,4 +80,7 @@ public class LibraryObject extends Geometry { } + public static void resetObjectMap() { + libraryObjects.clear(); + } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java index 48f8ccd4..acd79bc7 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java @@ -34,4 +34,8 @@ public class RelativeGeometry extends Geometry { private RelativeGeometry(GeometryType type, Lod lod, Orientation orientation) { super(type, lod, orientation); } + + public static void resetObjectMap() { + relativeGeometries.clear(); + } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index 249a2488..b73a15b7 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -182,6 +182,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { model = new CityDoctorModel(config, path.toFile(), new UnconnectedCache()); } else { featureCache.resetCache(); + ImplicitGeometryHolder.resetObjectMaps(); model = new CityDoctorModel(config, path.toFile(), featureCache); } neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces()); -- GitLab From a42107d438052ce972442b77ac23a556e3225d57 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 2 Dec 2025 15:54:18 +0100 Subject: [PATCH 109/118] Fix: Resolve deferred RelativeGeometries with cache --- .../citydoctor2/datastructure/ImplicitGeometryHolder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java index e7405894..d7babe0d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java @@ -68,7 +68,7 @@ public class ImplicitGeometryHolder extends Geometry { RelativeGeometry relGeom = RelativeGeometry.of(gmlId); if (relGeom != null) { ImplicitGeometryHolder igh = new ImplicitGeometryHolder(ig, relGeom); - CityObject parent = key.getParent(); + CityObject parent = cache.get(key.getParent().getGmlId()); parent.removeGeometry(igh.getLod(), igh.getType()); parent.addGeometry(igh); } else { -- GitLab From 502a913996d36a7ab9fcd6e664c1b7fdf9cbeac1 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 2 Dec 2025 16:47:04 +0100 Subject: [PATCH 110/118] Fix: Remove race condition in ImplicitGeometries --- .../datastructure/ImplicitGeometryHolder.java | 22 +++++++++++++++ .../datastructure/LibraryObject.java | 27 ++++++++++++++++++- .../datastructure/RelativeGeometry.java | 25 +++++++++++++++++ .../stuttgart/citydoctor2/check/Checker.java | 3 +++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java index d7babe0d..8285eb93 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java @@ -28,6 +28,7 @@ public class ImplicitGeometryHolder extends Geometry { LIBRARY_OBJECT, RELATIVE_GEOMETRY } + private ImplicitGeometry cgmlImplicitGeometry = null; private static ConcurrentHashMap deferredRelGeomObjects = new ConcurrentHashMap<>(); private final PrototypeGeometryType type; @@ -112,6 +113,27 @@ public class ImplicitGeometryHolder extends Geometry { LibraryObject.resetObjectMap(); } + /** + * Prepares the prototype geometries for checking and sets a lock to prevent clearing of metadata + */ + public static void preparePrototypeGeometriesForChecking(){ + RelativeGeometry.preparePrototypeGeometriesForChecking(); + LibraryObject.preparePrototypeGeometriesForChecking(); + } + + /** + * Clears metadata creating for checking of prototype geometries and releases the metadata lock + */ + public static void clearMetaDataOfPrototypeGeometries(){ + RelativeGeometry.clearMetaDataOfPrototypeGeometries(); + LibraryObject.clearMetaDataOfPrototypeGeometries(); + } + + @Override + public void accept(CheckableVisitor c) { + prototypeGeometry.accept(c); + } + @Override public boolean isValidated() { return prototypeGeometry.isValidated(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java index 711d6f70..557cd3ef 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java @@ -26,7 +26,7 @@ public class LibraryObject extends Geometry { private static final Logger logger = LogManager.getLogger(LibraryObject.class); @Serial private static final long serialVersionUID = -50293435187454911L; - + private static boolean metadataLocked = false; private static final Map libraryObjects = new ConcurrentHashMap<>(); public static LibraryObject of(Path path, ParserConfiguration config) { @@ -83,4 +83,29 @@ public class LibraryObject extends Geometry { public static void resetObjectMap() { libraryObjects.clear(); } + + public static void preparePrototypeGeometriesForChecking() { + libraryObjects.values().forEach(LibraryObject::updateEdgesAndVertices); + metadataLocked = true; + } + + public static void clearMetaDataOfPrototypeGeometries() { + metadataLocked = false; + libraryObjects.values().forEach(LibraryObject::clearMetaData); + } + + @Override + public void updateEdgesAndVertices(){ + if (!metadataLocked) { + super.updateEdgesAndVertices(); + } + } + + @Override + public void clearMetaData(){ + if (!metadataLocked) { + super.clearMetaData(); + } + } + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java index acd79bc7..489fa054 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java @@ -15,6 +15,7 @@ public class RelativeGeometry extends Geometry { private static final long serialVersionUID = -686112245455298977L; private static final Map relativeGeometries = new ConcurrentHashMap<>(); + private static boolean metadataLocked = false; public static RelativeGeometry of(GmlId gmlId, Geometry geom) { if (relativeGeometries.containsKey(gmlId)) { @@ -38,4 +39,28 @@ public class RelativeGeometry extends Geometry { public static void resetObjectMap() { relativeGeometries.clear(); } + + public static void preparePrototypeGeometriesForChecking() { + relativeGeometries.values().forEach(RelativeGeometry::updateEdgesAndVertices); + metadataLocked = true; + } + + public static void clearMetaDataOfPrototypeGeometries() { + metadataLocked = false; + relativeGeometries.values().forEach(RelativeGeometry::clearMetaData); + } + + @Override + public void updateEdgesAndVertices(){ + if (!metadataLocked) { + super.updateEdgesAndVertices(); + } + } + + @Override + public void clearMetaData(){ + if (!metadataLocked) { + super.clearMetaData(); + } + } } diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index a1dd7bcf..0bc1fcd9 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -33,6 +33,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; +import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; import de.hft.stuttgart.citydoctor2.parser.CityGmlConsumer; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; @@ -669,10 +670,12 @@ public class Checker { (threadCount, threadCount, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); try{ long startTime = System.nanoTime(); + ImplicitGeometryHolder.preparePrototypeGeometriesForChecking(); List> futures = runChecksOnFeatures(exec, cache, features, checkedCount, l); List missedFeatures = getMissedFeatures(futures); + ImplicitGeometryHolder.clearMetaDataOfPrototypeGeometries(); if (!missedFeatures.isEmpty()){ logger.error(Localization.getText("Checker.dbUnresponsive")); if (logger.isDebugEnabled()){ -- GitLab From 2c676fd53a48263a3789527efcf2aa56d72579e5 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 2 Dec 2025 16:47:39 +0100 Subject: [PATCH 111/118] Refactor: Remove obsolete function --- .../citydoctor2/parser/CityGmlParser.java | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 1d4d67c0..14f3cd91 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -532,33 +532,6 @@ public class CityGmlParser { } - private static void parseEpsgCodeFromBuffer(byte[] buffer, ParserConfiguration config) - throws ParserConfigurationException, SAXException { - InputStream is = new ByteArrayInputStream(buffer); - SAXParser parser = FACTORY.newSAXParser(); - CityGmlHandler handler = new CityGmlHandler(); - try { - parser.parse(new InputSource(is), handler); - } catch (EnvelopeFoundException e) { - try { - parseCoordinateSystem(config, handler); - - } catch (Exception e2) { - logEpsgParseError(e2); - } - } catch (SAXParseException spe) { - // suppress XML document structure warning - if (!spe.getMessage().matches("XML document structures must start and end within the same entity.")) { - logEpsgParseError(spe); - } - } catch (Exception e) { - logEpsgParseError(e); - } - if (handler.getEpsg() == null && logger.isInfoEnabled()) { - logger.info(Localization.getText("CityGmlParser.missingEPSGCode")); - } - } - private static void logEpsgParseError(Exception e) { logger.debug("Exception while parsing for EPSG code", e); if (logger.isWarnEnabled()) { -- GitLab From 7fc7500d934ec8e2dac8bacca6cc2034c7f01a66 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 2 Dec 2025 16:48:41 +0100 Subject: [PATCH 112/118] Refactor: Suppress EPSG logs during nested parsing --- .../citydoctor2/parser/CityGmlParser.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 14f3cd91..d94ab7ee 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -19,7 +19,6 @@ package de.hft.stuttgart.citydoctor2.parser; import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -80,7 +79,6 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.SAXParseException; import org.xmlobjects.schema.SchemaHandler; import org.xmlobjects.schema.SchemaHandlerException; import org.xmlobjects.stream.XMLReader; @@ -272,7 +270,7 @@ public class CityGmlParser { } try { - parseEpsgCodeFromFile(file, config); + parseEpsgCodeFromFile(file, config, verbose); CityGMLInputFactory in = context.createCityGMLInputFactory() .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { @@ -301,7 +299,7 @@ public class CityGmlParser { public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l, CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException { - parseEpsgCodeFromFile(file, config); + parseEpsgCodeFromFile(file, config, true); startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile); } @@ -503,15 +501,15 @@ public class CityGmlParser { } } - private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config) throws CityGmlParseException { + private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config, boolean verbose) throws CityGmlParseException { try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) { - parseEpsgCodeFromStream(bis, config); + parseEpsgCodeFromStream(bis, config, verbose); } catch (ParserConfigurationException | SAXException | IOException e) { throw new CityGmlParseException("Failed to read CityGML file", e); } } - private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config) + private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config, boolean verbose) throws ParserConfigurationException, SAXException { SAXParser parser = FACTORY.newSAXParser(); CityGmlHandler handler = new CityGmlHandler(); @@ -519,14 +517,14 @@ public class CityGmlParser { parser.parse(new InputSource(is), handler); } catch (EnvelopeFoundException e) { try { - parseCoordinateSystem(config, handler); + parseCoordinateSystem(config, handler, verbose); } catch (Exception e2) { logEpsgParseError(e2); } } catch (Exception e) { logEpsgParseError(e); } - if (handler.getEpsg() == null && logger.isInfoEnabled()) { + if (handler.getEpsg() == null && logger.isInfoEnabled() && verbose) { logger.info(Localization.getText("CityGmlParser.missingEPSGCode")); } @@ -539,7 +537,7 @@ public class CityGmlParser { } } - private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler) { + private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler, boolean verbose) { if (handler.getEpsg() == null) { return; } @@ -547,14 +545,14 @@ public class CityGmlParser { if (crs == null) { // could not find a coordinate system for srsName // assuming metric system - if (logger.isInfoEnabled()) { + if (logger.isInfoEnabled() && verbose) { logger.info(Localization.getText("CityGmlParser.missingEPSGCode")); } return; } if (crs.getProjection().getUnits() == Units.METRES) { // coordinate system is in meters, do not convert - if (logger.isInfoEnabled()) { + if (logger.isInfoEnabled() && verbose) { logger.info(Localization.getText("CityGmlParser.noConversionNeeded")); } return; @@ -579,11 +577,15 @@ public class CityGmlParser { CoordinateReferenceSystem utm; if (centerLat < 0) { // south - logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneS"), zone); + if (logger.isInfoEnabled() && verbose) { + logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneS"), zone); + } utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south"); } else { // north - logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneN"), zone); + if (logger.isInfoEnabled() && verbose) { + logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneN"), zone); + } utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone); } config.setCoordinateSystem(crs, utm); -- GitLab From 059cfd0c828ce9c41eea74f242461058b23fa6e6 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Tue, 2 Dec 2025 16:49:33 +0100 Subject: [PATCH 113/118] Refactor: Add deserialization exception handling --- .../citydoctor2/database/EmbeddedDatabaseHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java index 2f33db50..e7820948 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java @@ -19,6 +19,7 @@ import org.h2gis.utilities.wrapper.DataSourceWrapper; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InvalidClassException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -191,6 +192,9 @@ public class EmbeddedDatabaseHandler { } }); return co; + } catch (InvalidClassException | ClassCastException | SerializationException e){ + String msg = String.format("Deserialization of CityObject \"%s\" failed",id); + logger.error(msg,e); } } } catch (IOException e) { -- GitLab From ab1681b7553c3c8f6ff0283002d7fb075fba11fc Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Wed, 3 Dec 2025 16:51:07 +0100 Subject: [PATCH 114/118] Refactor: Remove obsolete enum --- .../datastructure/BoundarySurface.java | 13 +- .../citydoctor2/datastructure/Opening.java | 9 +- .../mapper/citygml3/SurfaceMapper.java | 33 ++-- .../datastructure/BoundarySurfaceTest.java | 168 +++++------------- .../datastructure/BuildingTest.java | 2 +- .../datastructure/GeometryTest.java | 8 +- 6 files changed, 62 insertions(+), 171 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurface.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurface.java index 9fa5066c..c3dd9d63 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurface.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurface.java @@ -40,7 +40,7 @@ public class BoundarySurface extends CityObject { @Serial private static final long serialVersionUID = 8793865135393496408L; - private SurfaceFeatureType featureType; + private BoundarySurfaceType type; private final List openings = new ArrayList<>(2); @@ -49,18 +49,14 @@ public class BoundarySurface extends CityObject { private AbstractThematicSurface gmlObject; public BoundarySurface(AbstractThematicSurface aco) { - this(SurfaceFeatureType.BUILDING, BoundarySurfaceType.UNDEFINED, aco); + this(BoundarySurfaceType.UNDEFINED, aco); } - public BoundarySurface(SurfaceFeatureType featureType, BoundarySurfaceType type, AbstractThematicSurface aco) { - this.featureType = featureType; + public BoundarySurface(BoundarySurfaceType type, AbstractThematicSurface aco) { this.type = type; gmlObject = aco; } - public void setFeatureType(SurfaceFeatureType featureType) { - this.featureType = featureType; - } public void setType(BoundarySurfaceType type) { this.type = type; @@ -70,9 +66,6 @@ public class BoundarySurface extends CityObject { this.gmlObject = gmlObject; } - public SurfaceFeatureType getSurfaceFeatureType() { - return featureType; - } public BoundarySurfaceType getType() { return type; diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Opening.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Opening.java index b64487de..5beaea56 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Opening.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Opening.java @@ -44,7 +44,6 @@ public class Opening extends CityObject { private BoundarySurface partOf; private OpeningType type; - private SurfaceFeatureType featureType; private AbstractFillingSurface ao; @@ -52,9 +51,8 @@ public class Opening extends CityObject { this.type = type; } - public Opening(OpeningType type, SurfaceFeatureType featureType, BoundarySurface partOf, + public Opening(OpeningType type, BoundarySurface partOf, AbstractFillingSurface ao) { - this.featureType = featureType; this.partOf = partOf; this.type = type; this.ao = ao; @@ -96,11 +94,6 @@ public class Opening extends CityObject { } } - - public SurfaceFeatureType getSurfaceFeatureType() { - return featureType; - } - public BoundarySurface getPartOf() { return partOf; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/SurfaceMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/SurfaceMapper.java index c8e84952..6b3257ad 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/SurfaceMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/SurfaceMapper.java @@ -54,8 +54,7 @@ public class SurfaceMapper extends ObjectWalker { @Override public void visit(WallSurface wallSurface) { - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, - wallSurface); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.WALL, wallSurface); handleConstructionSurface(wallSurface, surface); surfaces.add(surface); } @@ -74,11 +73,11 @@ public class SurfaceMapper extends ObjectWalker { private void readAbstractFillingSurface(AbstractFillingSurface afs, BoundarySurface cdSurface) { Opening o; if (afs instanceof DoorSurface ds) { - o = new Opening(OpeningType.DOOR, SurfaceFeatureType.BUILDING, cdSurface, ds); + o = new Opening(OpeningType.DOOR, cdSurface, ds); } else if (afs instanceof WindowSurface ws) { - o = new Opening(OpeningType.WINDOW, SurfaceFeatureType.BUILDING, cdSurface, ws); + o = new Opening(OpeningType.WINDOW, cdSurface, ws); } else { - o = new Opening(OpeningType.UNKNOWN, SurfaceFeatureType.BUILDING, cdSurface, afs); + o = new Opening(OpeningType.UNKNOWN, cdSurface, afs); } cdSurface.addOpening(o); parseAndAddMultiSurface(afs.getLod0MultiSurface(), Lod.LOD0, o); @@ -144,64 +143,56 @@ public class SurfaceMapper extends ObjectWalker { @Override public void visit(FloorSurface floorSurface) { - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.FLOOR, - floorSurface); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.FLOOR, floorSurface); handleConstructionSurface(floorSurface, surface); surfaces.add(surface); } @Override public void visit(CeilingSurface ceilingSurface) { - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.CEILING, - ceilingSurface); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.CEILING, ceilingSurface); handleConstructionSurface(ceilingSurface, surface); surfaces.add(surface); } @Override public void visit(GroundSurface groundSurface) { - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.GROUND, - groundSurface); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.GROUND, groundSurface); handleConstructionSurface(groundSurface, surface); surfaces.add(surface); } @Override public void visit(InteriorWallSurface interiorWallSurface) { - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.INTERIOR_WALL, - interiorWallSurface); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.INTERIOR_WALL, interiorWallSurface); handleConstructionSurface(interiorWallSurface, surface); surfaces.add(surface); } @Override public void visit(OuterCeilingSurface outerCeilingSurface) { - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.OUTER_CEILING, - outerCeilingSurface); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.OUTER_CEILING, outerCeilingSurface); handleConstructionSurface(outerCeilingSurface, surface); surfaces.add(surface); } @Override public void visit(OuterFloorSurface outerFloorSurface) { - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.OUTER_FLOOR, - outerFloorSurface); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.OUTER_FLOOR, outerFloorSurface); handleConstructionSurface(outerFloorSurface, surface); surfaces.add(surface); } @Override public void visit(RoofSurface roofSurface) { - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.ROOF, - roofSurface); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.ROOF, roofSurface); handleConstructionSurface(roofSurface, surface); surfaces.add(surface); } @Override public void visit(ClosureSurface closureSurface) { - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.CLOSURE, - closureSurface); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.CLOSURE, closureSurface); handleThematicSurface(closureSurface, surface); surfaces.add(surface); } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurfaceTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurfaceTest.java index 9953196f..c08b1ad7 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurfaceTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BoundarySurfaceTest.java @@ -53,7 +53,7 @@ public class BoundarySurfaceTest { @Test public void testVisitorPropagation() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); Opening oMock = Mockito.mock(Opening.class); bs.addOpening(oMock); @@ -66,7 +66,7 @@ public class BoundarySurfaceTest { @Test public void testAccept() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); Opening oMock = Mockito.mock(Opening.class); bs.addOpening(oMock); AtomicInteger bsCounter = new AtomicInteger(0); @@ -84,7 +84,7 @@ public class BoundarySurfaceTest { @Test public void testAcceptWithoutFittingCheck() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); Opening oMock = Mockito.mock(Opening.class); bs.addOpening(oMock); AtomicInteger bsCounter = new AtomicInteger(0); @@ -102,7 +102,7 @@ public class BoundarySurfaceTest { @Test public void testPrepareForChecking() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD2, Orientation.OUTWARD); Geometry spyGeom = Mockito.spy(geom); bs.addGeometry(spyGeom); @@ -112,7 +112,7 @@ public class BoundarySurfaceTest { @Test public void testContainsError() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); assertFalse(bs.containsError(CheckId.C_GE_P_HOLE_OUTSIDE)); bs.addCheckResult( new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, Mockito.mock(CheckError.class))); @@ -122,9 +122,9 @@ public class BoundarySurfaceTest { @Test public void testContainsErrorInOpening() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); assertFalse(bs.containsError(CheckId.C_GE_P_HOLE_OUTSIDE)); - Opening o = new Opening(OpeningType.WINDOW, SurfaceFeatureType.BUILDING, bs, null); + Opening o = new Opening(OpeningType.WINDOW, bs, null); bs.addOpening(o); o.addCheckResult( new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, Mockito.mock(CheckError.class))); @@ -134,7 +134,7 @@ public class BoundarySurfaceTest { @Test public void testClearAllContainedCheckResults() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); Opening oMock = Mockito.mock(Opening.class); bs.addCheckResult( new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, Mockito.mock(CheckError.class))); @@ -147,7 +147,7 @@ public class BoundarySurfaceTest { @Test public void testContainsAnyError() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); assertFalse(bs.containsAnyError()); bs.addCheckResult( new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, Mockito.mock(CheckError.class))); @@ -157,19 +157,18 @@ public class BoundarySurfaceTest { @Test public void testCollectContainedErrors() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); bs.addCheckResult( new CheckResult(CheckId.C_GE_P_HOLE_OUTSIDE, ResultStatus.ERROR, Mockito.mock(CheckError.class))); List errors = new ArrayList<>(); bs.collectContainedErrors(errors); assertEquals(1, errors.size()); - ; } @Test public void testReCreateGeometriesWithoutId() { WallSurface ws = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); assertNotNull(ws.getId()); } @@ -178,7 +177,7 @@ public class BoundarySurfaceTest { public void testReCreateGeometriesWithId() { WallSurface ws = new WallSurface(); ws.setId("test"); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); // nothing has changed assertEquals("test", ws.getId()); @@ -188,89 +187,52 @@ public class BoundarySurfaceTest { } @Test - public void testReCreateGeometriesMultiSurfaceLod2() { - WallSurface ws = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); - bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE)); - bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); - assertNotNull(ws.getLod2MultiSurface()); - assertNull(ws.getLod3MultiSurface()); - assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); - } - - @Test - public void testReCreateGeometriesMultiSurfaceBridgeLod2() { - WallSurface ws = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BRIDGE, BoundarySurfaceType.WALL, ws); - bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE)); - bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); - assertNotNull(ws.getLod2MultiSurface()); - assertNull(ws.getLod3MultiSurface()); - assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); - } - - @Test - public void testReCreateGeometriesMultiSurfaceBridgeLod3() { + public void testReCreateGeometriesMultiSurfaceLod0() { WallSurface ws = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BRIDGE, BoundarySurfaceType.WALL, ws); - bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD3)); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); + bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD0)); bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); + assertNotNull(ws.getLod0MultiSurface()); + assertNull(ws.getLod1MultiSurface()); assertNull(ws.getLod2MultiSurface()); - assertNotNull(ws.getLod3MultiSurface()); + assertNull(ws.getLod3MultiSurface()); assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); } @Test - public void testReCreateGeometriesMultiSurfaceBridgeLod4() { + public void testReCreateGeometriesMultiSurfaceLod1() { WallSurface ws = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BRIDGE, BoundarySurfaceType.WALL, ws); - bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD4)); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); + bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD1)); bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); + assertNull(ws.getLod0MultiSurface()); + assertNotNull(ws.getLod1MultiSurface()); assertNull(ws.getLod2MultiSurface()); assertNull(ws.getLod3MultiSurface()); - assertNotNull(ws.getDeprecatedProperties().getLod4MultiSurface()); + assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); } @Test - public void testReCreateGeometriesMultiSurfaceTunnelLod2() { + public void testReCreateGeometriesMultiSurfaceLod2() { WallSurface ws = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.TUNNEL, BoundarySurfaceType.WALL, ws); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE)); bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); + assertNull(ws.getLod0MultiSurface()); + assertNull(ws.getLod1MultiSurface()); assertNotNull(ws.getLod2MultiSurface()); assertNull(ws.getLod3MultiSurface()); assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); } - @Test - public void testReCreateGeometriesMultiSurfaceTunnelLod3() { - WallSurface ws = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.TUNNEL, BoundarySurfaceType.WALL, ws); - bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD3)); - bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); - assertNull(ws.getLod2MultiSurface()); - assertNotNull(ws.getLod3MultiSurface()); - assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); - } - - @Test - public void testReCreateGeometriesMultiSurfaceTunnelLod4() { - WallSurface ws = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.TUNNEL, BoundarySurfaceType.WALL, ws); - bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD4)); - bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); - assertNull(ws.getLod2MultiSurface()); - assertNull(ws.getLod3MultiSurface()); - assertNotNull(ws.getDeprecatedProperties().getLod4MultiSurface()); - } - @Test public void testReCreateGeometriesMultiSurfaceLod3() { WallSurface ws = new WallSurface(); - ws.setId("test"); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); + BoundarySurface bs = new BoundarySurface( BoundarySurfaceType.WALL, ws); bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD3)); bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); + assertNull(ws.getLod0MultiSurface()); + assertNull(ws.getLod1MultiSurface()); assertNull(ws.getLod2MultiSurface()); assertNotNull(ws.getLod3MultiSurface()); assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); @@ -279,30 +241,21 @@ public class BoundarySurfaceTest { @Test public void testReCreateGeometriesMultiSurfaceLod4() { WallSurface ws = new WallSurface(); - ws.setId("test"); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD4)); bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); + assertNull(ws.getLod0MultiSurface()); + assertNull(ws.getLod1MultiSurface()); assertNull(ws.getLod2MultiSurface()); assertNull(ws.getLod3MultiSurface()); assertNotNull(ws.getDeprecatedProperties().getLod4MultiSurface()); } - @Test - public void testReCreateGeometriesMultiSurfaceLod0() { - WallSurface ws = new WallSurface(); - ws.setId("test"); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); - bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.MULTI_SURFACE, Lod.LOD0)); - bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); - assertNotNull(ws.getLod0MultiSurface()); - } @Test(expected = IllegalStateException.class) public void testReCreateGeometriesSolid() { WallSurface ws = new WallSurface(); - ws.setId("test"); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); bs.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID)); bs.reCreateGeometries(GeometryFactory.newInstance(), new ParserConfiguration(8, false)); } @@ -310,8 +263,7 @@ public class BoundarySurfaceTest { @Test public void testReCreateGeometriesOpenings() { WallSurface ws = new WallSurface(); - ws.setId("test"); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); Opening oMock = Mockito.mock(Opening.class); bs.addOpening(oMock); GeometryFactory factory = GeometryFactory.newInstance(); @@ -323,7 +275,7 @@ public class BoundarySurfaceTest { @Test public void testGetGmlObject() { WallSurface ws = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); assertEquals(ws, bs.getGmlObject()); } @@ -336,7 +288,7 @@ public class BoundarySurfaceTest { assertNotNull(ws.getLod2MultiSurface()); assertNotNull(ws.getLod3MultiSurface()); assertNotNull(ws.getDeprecatedProperties().getLod4MultiSurface()); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, ws); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, ws); Opening oMock = Mockito.mock(Opening.class); bs.addOpening(oMock); bs.accept(new UnsetGeometriesVisitor()); @@ -346,41 +298,9 @@ public class BoundarySurfaceTest { assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); } - @Test - public void testUnsetGmlGeometriesBridge() { - WallSurface ws = new WallSurface(); - ws.setLod2MultiSurface(new MultiSurfaceProperty()); - ws.setLod3MultiSurface(new MultiSurfaceProperty()); - ws.getDeprecatedProperties().setLod4MultiSurface(new MultiSurfaceProperty()); - assertNotNull(ws.getLod2MultiSurface()); - assertNotNull(ws.getLod3MultiSurface()); - assertNotNull(ws.getDeprecatedProperties().getLod4MultiSurface()); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BRIDGE, BoundarySurfaceType.WALL, ws); - bs.unsetGmlGeometries(); - assertNull(ws.getLod2MultiSurface()); - assertNull(ws.getLod3MultiSurface()); - assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); - } - - @Test - public void testUnsetGmlGeometriesTunnel() { - WallSurface ws = new WallSurface(); - ws.setLod2MultiSurface(new MultiSurfaceProperty()); - ws.setLod3MultiSurface(new MultiSurfaceProperty()); - ws.getDeprecatedProperties().setLod4MultiSurface(new MultiSurfaceProperty()); - assertNotNull(ws.getLod2MultiSurface()); - assertNotNull(ws.getLod3MultiSurface()); - assertNotNull(ws.getDeprecatedProperties().getLod4MultiSurface()); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.TUNNEL, BoundarySurfaceType.WALL, ws); - bs.unsetGmlGeometries(); - assertNull(ws.getLod2MultiSurface()); - assertNull(ws.getLod3MultiSurface()); - assertNull(ws.getDeprecatedProperties().getLod4MultiSurface()); - } - @Test public void testGetFeatureType() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.TUNNEL, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); assertEquals(FeatureType.BOUNDARY_SURFACE, bs.getFeatureType()); } @@ -390,17 +310,11 @@ public class BoundarySurfaceTest { BoundarySurface bs = new BoundarySurface(aco); assertEquals(aco, bs.getGmlObject()); } - - @Test - public void testSetFeatureType() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.TUNNEL, BoundarySurfaceType.WALL, null); - bs.setFeatureType(SurfaceFeatureType.BRIDGE); - assertEquals(SurfaceFeatureType.BRIDGE, bs.getSurfaceFeatureType()); - } + @Test public void testSetType() { - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.TUNNEL, BoundarySurfaceType.WALL, null); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, null); bs.setType(BoundarySurfaceType.ROOF); assertEquals(BoundarySurfaceType.ROOF, bs.getType()); } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingTest.java index 72698175..21d897fa 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingTest.java @@ -64,7 +64,7 @@ public class BuildingTest { b.setCityGmlBuilding(createCityGmlBuilding()); RoofSurface rs = new RoofSurface(); rs.setId("testRoof"); - BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.ROOF, rs); + BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.ROOF, rs); Geometry geomSurface = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD2, Orientation.OUTWARD); geomSurface.addPolygon(new LinkedPolygon(p, geomSurface)); geomSurface.updateEdgesAndVertices(); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/GeometryTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/GeometryTest.java index a37077ae..5c8b97cf 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/GeometryTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/GeometryTest.java @@ -96,7 +96,7 @@ public class GeometryTest { public void testRemovePolygonConcreteWithLink() { ParserConfiguration config = new ParserConfiguration(4, false); AbstractConstructionSurface abs = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, abs); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, abs); var gmlBi = new org.citygml4j.core.model.building.BuildingInstallation(); Installation bi = new Installation(); bi.setGmlObject(gmlBi); @@ -133,7 +133,7 @@ public class GeometryTest { public void testRemovePolygonOnlyConcrete() { ParserConfiguration config = new ParserConfiguration(4, false); AbstractConstructionSurface abs = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, abs); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, abs); Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD2, Orientation.OUTWARD); ConcretePolygon p = new ConcretePolygon(); @@ -156,7 +156,7 @@ public class GeometryTest { public void testRemovePolygonLinked() { ParserConfiguration config = new ParserConfiguration(4, false); AbstractConstructionSurface abs = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, abs); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, abs); var gmlBi = new org.citygml4j.core.model.building.BuildingInstallation(); Installation bi = new Installation(); bi.setGmlObject(gmlBi); @@ -193,7 +193,7 @@ public class GeometryTest { public void testReplacePolygon() { ParserConfiguration config = new ParserConfiguration(4, false); AbstractConstructionSurface abs = new WallSurface(); - BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, abs); + BoundarySurface bs = new BoundarySurface(BoundarySurfaceType.WALL, abs); var gmlBi = new org.citygml4j.core.model.building.BuildingInstallation(); Installation bi = new Installation(); bi.setGmlObject(gmlBi); -- GitLab From 8edfe8d62e1b63b5802800009adbc2f7087541b1 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Wed, 3 Dec 2025 16:53:26 +0100 Subject: [PATCH 115/118] Fix: Add OtherConstructionObject to mapper --- .../stuttgart/citydoctor2/check/Check.java | 29 +++- .../check/CheckableUtilsVisitor.java | 5 + .../citydoctor2/check/CheckableVisitor.java | 3 + .../datastructure/FeatureType.java | 2 +- .../datastructure/GenericCityObject.java | 4 +- .../OtherConstructionObject.java | 142 ++++++++++++++++++ .../citygml3/Citygml3FeatureMapper.java | 29 +++- .../citydoctor2/checks/CheckContainer.java | 27 ++++ 8 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/OtherConstructionObject.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java index e81369a1..9a3b136d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java @@ -154,6 +154,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param checkable a checkable */ + @Override public void check(Checkable checkable) { } @@ -163,6 +164,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param ab building or building part */ + @Override public void check(AbstractBuilding ab) { } @@ -172,6 +174,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param bs a boundary surface */ + @Override public void check(BoundarySurface bs) { } @@ -181,6 +184,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param bo a bridge */ + @Override public void check(BridgeObject bo) { } @@ -190,6 +194,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param b a building */ + @Override public void check(Building b) { } @@ -199,6 +204,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param bi a building installation */ + @Override public void check(Installation bi) { } @@ -208,6 +214,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param bp a building part */ + @Override public void check(BuildingPart bp) { } @@ -217,6 +224,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param co a city object */ + @Override public void check(CityObject co) { } @@ -226,6 +234,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param lo a land use object */ + @Override public void check(LandObject lo) { } @@ -235,6 +244,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param o an opening */ + @Override public void check(Opening o) { } @@ -244,6 +254,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param to a transportation object */ + @Override public void check(TransportationObject to) { } @@ -253,6 +264,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param veg a vegetation object */ + @Override public void check(Vegetation veg) { } @@ -262,6 +274,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param wo a water object */ + @Override public void check(WaterObject wo) { } @@ -271,6 +284,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param geom a geometry */ + @Override public void check(Geometry geom) { } @@ -280,6 +294,7 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param poly a polygon */ + @Override public void check(Polygon poly) { } @@ -289,26 +304,36 @@ public abstract non-sealed class Check implements CheckableVisitor { * * @param ring a linear ring */ + @Override public void check(LinearRing ring) { } - + + @Override public void check(TinObject tin) { } - + + @Override public void check(ReliefObject relief) { } + @Override public void check(CityFurniture cf){ } + @Override public void check(GenericCityObject gco) { } + @Override + public void check(OtherConstructionObject oco){ + + } + /** * The initialization method of this check. It will be called before any check * method will be executed. Override this if you want to have configurable diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckableUtilsVisitor.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckableUtilsVisitor.java index 0034c8c0..22d5d94d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckableUtilsVisitor.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckableUtilsVisitor.java @@ -31,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Installation; import de.hft.stuttgart.citydoctor2.datastructure.LandObject; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.Opening; +import de.hft.stuttgart.citydoctor2.datastructure.OtherConstructionObject; import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject; import de.hft.stuttgart.citydoctor2.datastructure.TinObject; @@ -165,4 +166,8 @@ public abstract non-sealed class CheckableUtilsVisitor implements CheckableVisit } + @Override + public void check(OtherConstructionObject oco){ + + } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckableVisitor.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckableVisitor.java index 7830c5a8..6cbc1ada 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckableVisitor.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckableVisitor.java @@ -13,6 +13,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Installation; import de.hft.stuttgart.citydoctor2.datastructure.LandObject; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.Opening; +import de.hft.stuttgart.citydoctor2.datastructure.OtherConstructionObject; import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject; import de.hft.stuttgart.citydoctor2.datastructure.TinObject; @@ -69,4 +70,6 @@ public sealed interface CheckableVisitor permits Check, CheckableUtilsVisitor { void check(GenericCityObject gco); + void check(OtherConstructionObject oco); + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java index cb49e1e0..11a8e687 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java @@ -27,5 +27,5 @@ public enum FeatureType { BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, INSTALLATION, OPENING, BUILDING_PART, BUILDING_SUBDIVISION, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE, - GENERIC_CITY_OBJECT, TUNNEL, TUNNEL_PART, TUNNEL_CONSTRUCTION_ELEMENT, TUNNEL_INSTALLATION, + OTHER_CITY_OBJECT, TUNNEL, TUNNEL_PART, TUNNEL_CONSTRUCTION_ELEMENT, TUNNEL_INSTALLATION, } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericCityObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericCityObject.java index 34a0d022..a8aa7bfb 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericCityObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericCityObject.java @@ -111,7 +111,7 @@ public class GenericCityObject extends CityObject { cgmlGos.getDeprecatedProperties().setLod4Geometry(new SolidProperty(solid)); break; default: - throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to buildings"); + throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to generic city object"); } } @Override @@ -121,7 +121,7 @@ public class GenericCityObject extends CityObject { @Override public FeatureType getFeatureType() { - return FeatureType.GENERIC_CITY_OBJECT; + return FeatureType.OTHER_CITY_OBJECT; } public List getBoundarySurfaces() { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/OtherConstructionObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/OtherConstructionObject.java new file mode 100644 index 00000000..d29145d5 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/OtherConstructionObject.java @@ -0,0 +1,142 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.citygml4j.core.model.construction.OtherConstruction; +import org.citygml4j.core.model.core.AbstractCityObject; +import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty; +import org.citygml4j.core.util.geometry.GeometryFactory; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.primitives.Solid; +import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +public class OtherConstructionObject extends CityObject{ + + @Serial + private static final long serialVersionUID = -3885206364978761701L; + + private static final Logger logger = LogManager.getLogger(OtherConstructionObject.class); + + private final List boundarySurfaceList = new ArrayList<>(); + private OtherConstruction gmlObject; + + @Override + public void accept(CheckableVisitor c) { + super.accept(c); + if (c.canExecute(this)) { + c.check(this); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.accept(c); + } + } + + public void setGmlObject(OtherConstruction gmlObject) { + this.gmlObject = gmlObject; + } + + @Override + public void rebuildGeometries(GeometryFactory factory, ParserConfiguration config) { + for (Geometry geom : getGeometries()) { + if (geom instanceof ImplicitGeometryHolder) { + continue; + } + if (geom.getType() == GeometryType.MULTI_SURFACE) { + MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config); + setMultiSurfaceAccordingToLod(geom, ms); + } else { + Solid solid = CityGmlUtils.createSolid(geom, factory, config); + setSolidAccordingToLod(geom, solid); + } + } + removeEmptyBoundarySurfaces(); + } + + private void removeEmptyBoundarySurfaces() { + for (BoundarySurface bs : boundarySurfaceList) { + if (bs.getGeometries().isEmpty()) { + for (AbstractSpaceBoundaryProperty bsp : gmlObject.getBoundaries()) { + if (bsp.getObject() != null && bsp.getObject() == bs.getGmlObject()) { + logger.warn("Found empty boundary surface: {}, removing from feature", bs.getGmlId()); + gmlObject.getBoundaries().remove(bsp); + break; + } + } + } + } + } + + private void setMultiSurfaceAccordingToLod(Geometry geom, MultiSurface ms) { + switch (geom.getLod()) { + case LOD0: + gmlObject.setLod0MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD2: + gmlObject.setLod2MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD3: + gmlObject.setLod3MultiSurface(new MultiSurfaceProperty(ms)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " multi surface to other construction object"); + } + } + + private void setSolidAccordingToLod(Geometry geom, Solid solid) { + switch (geom.getLod()) { + case LOD1: + gmlObject.setLod1Solid(new SolidProperty(solid)); + break; + case LOD2: + gmlObject.setLod2Solid(new SolidProperty(solid)); + break; + case LOD3: + gmlObject.setLod3Solid(new SolidProperty(solid)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to other construction object"); + } + } + + @Override + public OtherConstruction getGmlObject() { + return gmlObject; + } + + @Override + public void unsetGmlGeometries() { + gmlObject.setLod0MultiSurface(null); + gmlObject.setLod2MultiSurface(null); + gmlObject.setLod3MultiSurface(null); + gmlObject.setLod1Solid(null); + gmlObject.setLod2Solid(null); + gmlObject.setLod3Solid(null); + } + + @Override + public CityObject getTopLevelCityObject() { + return this; + } + + @Override + public FeatureType getFeatureType() { + return FeatureType.OTHER_CITY_OBJECT; + } + + public void addBoundarySurface(BoundarySurface bs) { + boundarySurfaceList.add(bs); + bs.setParent(this); + } + + public List getBoundarySurfaces() { + return boundarySurfaceList; + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index b73a15b7..fad5c938 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -26,6 +26,7 @@ import java.util.Map; import de.hft.stuttgart.citydoctor2.database.FeatureCache; import de.hft.stuttgart.citydoctor2.database.UnconnectedCache; +import de.hft.stuttgart.citydoctor2.datastructure.OtherConstructionObject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.CityGMLVersion; @@ -47,6 +48,7 @@ import org.citygml4j.core.model.building.BuildingRoomProperty; import org.citygml4j.core.model.building.BuildingUnit; import org.citygml4j.core.model.building.Storey; import org.citygml4j.core.model.construction.AbstractConstruction; +import org.citygml4j.core.model.construction.OtherConstruction; import org.citygml4j.core.model.core.AbstractCityObject; import org.citygml4j.core.model.core.AbstractFeatureWithLifespan; import org.citygml4j.core.model.core.AbstractGenericAttributeProperty; @@ -202,6 +204,31 @@ public class Citygml3FeatureMapper extends ObjectWalker { } + @Override + public void visit(OtherConstruction gmlOc){ + OtherConstructionObject oco = new OtherConstructionObject(); + oco.setGmlObject(gmlOc); + mapAbstractOccupiedSpace(gmlOc, oco, Orientation.OUTWARD); + + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config); + for (AbstractSpaceBoundaryProperty surfaceProp : gmlOc.getBoundaries()) { + if (!surfaceProp.isSetObject()) { + continue; + } + AbstractSpaceBoundary surface = surfaceProp.getObject(); + surface.accept(surfaceMapper); + } + for (BoundarySurface bs : surfaceMapper.getSurfaces()) { + oco.addBoundarySurface(bs); + for (Geometry geom : bs.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + p.setPartOfSurface(bs); + } + } + } + finishCityObjectConstruction(oco); + } + @Override public void visit(org.citygml4j.core.model.cityfurniture.CityFurniture gmlCityFurniture) { CityFurniture cf = new CityFurniture(); @@ -229,7 +256,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { } mapAbstractOccupiedSpace(gos, gco, Orientation.OUTWARD); finishCityObjectConstruction(gco); - model.addGenericCityObject(gco); + model.addOtherCityObject(gco); } @Override diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java index 8b6f2943..7e628940 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java @@ -230,6 +230,24 @@ public class CheckContainer extends Check { } } + @Override + public void check(TinObject to) { + try { + check.check(to); + }catch (Exception e) { + handleException(e, to); + } + } + + @Override + public void check(ReliefObject ro) { + try { + check.check(ro); + }catch (Exception e) { + handleException(e, ro); + } + } + @Override public void check(CityFurniture cf){ try { @@ -248,6 +266,15 @@ public class CheckContainer extends Check { } } + @Override + public void check(OtherConstructionObject oco) { + try { + check.check(oco); + }catch (Exception e) { + handleException(e, oco); + } + } + @Override public RequirementType getType() { return check.getType(); -- GitLab From c748a05801a8002854ee71879d2bb4d51b6c46ab Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Wed, 3 Dec 2025 16:55:18 +0100 Subject: [PATCH 116/118] Refactor: Rename functions and fields --- .../datastructure/CityDoctorModel.java | 16 ++--- .../citygml3/Citygml3FeatureMapper.java | 1 + .../writer/CityGMLWriterUtils.java | 2 +- .../datastructure/CityDoctorModelTest.java | 16 ++--- .../citydoctor2/gui/CityDoctorController.java | 62 +++++++++---------- .../stuttgart/citydoctor2/gui/MainWindow.java | 8 +-- .../citydoctor2/gui/TriangulatedGeometry.java | 2 +- 7 files changed, 54 insertions(+), 53 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 10cacbec..ba0be98f 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -125,7 +125,7 @@ public class CityDoctorModel { public Stream createFeatureStream() { return Stream.of(getBuildings(), getVegetation(), getBridges(), getLand(), getTransportation(), - getTunnels(), getWater(), getCityFurniture(), getGenericCityObjects()).flatMap(co -> co); + getTunnels(), getWater(), getCityFurniture(), getOtherCityObjects()).flatMap(co -> co); } public List getFeatureIds() { @@ -347,20 +347,20 @@ public class CityDoctorModel { return cityfurniture.size(); } - public void addGenericCityObject(GenericCityObject coGenericCityObject) { - genericObjects.add(coGenericCityObject.getGmlId()); - cache.put(coGenericCityObject); + public void addOtherCityObject(CityObject otherCityObject) { + genericObjects.add(otherCityObject.getGmlId()); + cache.put(otherCityObject); } - public Stream getGenericCityObjects() { - return genericObjects.stream().map(co -> (GenericCityObject) cache.get(co)); + public Stream getOtherCityObjects() { + return genericObjects.stream().map(cache::get); } - public List getGenericCityObjectIds() { + public List getOtherCityObjectsIds() { return genericObjects; } - public int getGenericCityObjectCount(){ + public int getOtherCityObjectsCount(){ return genericObjects.size(); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index fad5c938..e27555ca 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -227,6 +227,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { } } finishCityObjectConstruction(oco); + model.addOtherCityObject(oco); } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java index fc76e1ff..e3b9cbfb 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java @@ -92,7 +92,7 @@ public class CityGMLWriterUtils { storeCityObjects(model.getTransportation(), gmlFactory, model, cModel, val); storeCityObjects(model.getWater(), gmlFactory, model, cModel, val); storeCityObjects(model.getCityFurniture(), gmlFactory, model, cModel, val); - storeCityObjects(model.getGenericCityObjects(), gmlFactory, model, cModel, val); + storeCityObjects(model.getOtherCityObjects(), gmlFactory, model, cModel, val); storeCityObjects(model.getTunnels(), gmlFactory, model, cModel, val); writer.write(cModel); diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java index edc51f6c..b535500a 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModelTest.java @@ -325,11 +325,11 @@ public class CityDoctorModelTest { } @Test - public void testAddGenericCityObject() { + public void testAddOtherCityObject() { GenericCityObject gen = Mockito.mock(GenericCityObject.class); Mockito.when(gen.getGmlId()).thenReturn(new GmlId("g")); - model.addGenericCityObject(gen); - assertEquals(gen, model.getGenericCityObjects().toList().get(0)); + model.addOtherCityObject(gen); + assertEquals(gen, model.getOtherCityObjects().toList().get(0)); } @Test @@ -362,7 +362,7 @@ public class CityDoctorModelTest { model.addLand(land); model.addWater(water); model.addCityFurniture(cf); - model.addGenericCityObject(generic); + model.addOtherCityObject(generic); assertEquals(9, model.getNumberOfFeatures()); @@ -547,11 +547,11 @@ public class CityDoctorModelTest { GenericCityObject gco2 = Mockito.mock(GenericCityObject.class); Mockito.when(gco2.getGmlId()).thenReturn(new GmlId("gco2")); - model.addGenericCityObject(gco1); - assertEquals(gco1, model.getGenericCityObjects().toList().get(0)); + model.addOtherCityObject(gco1); + assertEquals(gco1, model.getOtherCityObjects().toList().get(0)); model.replaceFeature(gco1, gco2); - assertEquals(gco2, model.getGenericCityObjects().toList().get(0)); - assertEquals(1, model.getGenericCityObjects().toList().size()); + assertEquals(gco2, model.getOtherCityObjects().toList().get(0)); + assertEquals(1, model.getOtherCityObjects().toList().size()); } @Test(expected = IllegalStateException.class) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index b3af18a3..1ebb891c 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -67,7 +67,7 @@ public class CityDoctorController { private AtomicInteger waterChunkNr = new AtomicInteger(0); private AtomicInteger landChunkNr = new AtomicInteger(0); private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0); - private AtomicInteger genericCityObjectsChunkNr = new AtomicInteger(0); + private AtomicInteger otherCityObjectsChunkNr = new AtomicInteger(0); @@ -164,7 +164,7 @@ public class CityDoctorController { mainWindow.setDisableOfFeatureTab(FeatureType.WATER, model.getWaterCount() == 0); mainWindow.setDisableOfFeatureTab(FeatureType.LAND, model.getLandCount() == 0); mainWindow.setDisableOfFeatureTab(FeatureType.CITY_FURNITURE, model.getCityFurnitureCount() == 0); - mainWindow.setDisableOfFeatureTab(FeatureType.GENERIC_CITY_OBJECT, model.getGenericCityObjectCount() == 0); + mainWindow.setDisableOfFeatureTab(FeatureType.OTHER_CITY_OBJECT, model.getOtherCityObjectsCount() == 0); } public void buildTrees() { @@ -190,7 +190,7 @@ public class CityDoctorController { waterChunkNr.set(0); landChunkNr.set(0); cityFurnitureChunkNr.set(0); - genericCityObjectsChunkNr.set(0); + otherCityObjectsChunkNr.set(0); } private void buildLand(CityDoctorModel model) { @@ -235,18 +235,18 @@ public class CityDoctorController { } private void buildGenericCityObjects(CityDoctorModel model) { - if (model.getGenericCityObjectCount() == 0) { + if (model.getOtherCityObjectsCount() == 0) { return; } - TreeView otherObjectsView = mainWindow.getGenericCityObjectView(); + TreeView otherObjectsView = mainWindow.getOtherCityObjectView(); TreeItem otherObjectsRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Generic CityObjects", Color.DARKGRAY)); otherObjectsRoot.setExpanded(true); otherObjectsView.setRoot(otherObjectsRoot); - Supplier> supply = model::getGenericCityObjects; - buildGenericCityObjectTreeFromList(supply, otherObjectsView.getRoot()); - addMoreButtonForGenericCityObjects( otherObjectsView, otherObjectsRoot, genericCityObjectsChunkNr, supply, - model.getGenericCityObjectCount()); + Supplier> supply = model::getOtherCityObjects; + buildOtherCityObjectsTreeFromList(supply, otherObjectsView.getRoot()); + addMoreButtonForOtherCityObjects( otherObjectsView, otherObjectsRoot, otherCityObjectsChunkNr, supply, + model.getOtherCityObjectsCount()); } private void buildWater(CityDoctorModel model) { @@ -528,13 +528,13 @@ public class CityDoctorController { } } - private void addMoreButtonForGenericCityObjects(TreeView view, TreeItem root, - AtomicInteger chunkCounter, Supplier> supply, int featureCount){ + private void addMoreButtonForOtherCityObjects(TreeView view, TreeItem root, + AtomicInteger chunkCounter, Supplier> supply, int featureCount){ if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) { AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) { @Override public void buildNodeTreeCall() { - buildGenericCityObjectTreeFromList(supply, root); + buildOtherCityObjectsTreeFromList(supply, root); updateTree(root); } }; @@ -590,7 +590,7 @@ public class CityDoctorController { mainWindow.getWaterView().setRoot(null); mainWindow.getTerrainView().setRoot(null); mainWindow.getCityFurnitureView().setRoot(null); - mainWindow.getGenericCityObjectView().setRoot(null); + mainWindow.getOtherCityObjectView().setRoot(null); mainWindow.getErrorTree().getRoot().getChildren().clear(); mainWindow.getAttributeView().getRoot().getChildren().clear(); mainWindow.getGlobalErrorsView().getItems().clear(); @@ -697,8 +697,8 @@ public class CityDoctorController { }); } - private void buildGenericCityObjectTreeFromList(Supplier> supply, TreeItem root) { - int genericObjectChunk = genericCityObjectsChunkNr.get(); + private void buildOtherCityObjectsTreeFromList(Supplier> supply, TreeItem root) { + int genericObjectChunk = otherCityObjectsChunkNr.get(); supply.get().skip((long) genericObjectChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(gen ->{ buildTopLevelCityObjectNode(gen, root); }); @@ -1105,7 +1105,7 @@ public class CityDoctorController { updateTree(mainWindow.getCityFurnitureView().getRoot()); updateTree(mainWindow.getTransportationView().getRoot()); updateTree(mainWindow.getWaterView().getRoot()); - updateTree(mainWindow.getGenericCityObjectView().getRoot()); + updateTree(mainWindow.getOtherCityObjectView().getRoot()); updateTree(mainWindow.getTunnelView().getRoot()); renderer.updateErrors(); } @@ -1228,7 +1228,7 @@ public class CityDoctorController { case WATER -> filterWaterTree(searchString); case LAND -> filterLandTree(searchString); case CITY_FURNITURE -> filterCityFurnitureTree(searchString); - case GENERIC_CITY_OBJECT -> filterGenericCityObjectTree(searchString); + case OTHER_CITY_OBJECT -> filterOtherCityObjectsTree(searchString); } @@ -1325,15 +1325,15 @@ public class CityDoctorController { addMoreButtonForCityFurniture(cityFurnitureView, root, cityFurnitureChunkNr, foundCityFurniture, featureCount); } - private void filterGenericCityObjectTree(String searchString) { - Supplier> foundGenericCityObject = () -> model.getGenericCityObjects(). + private void filterOtherCityObjectsTree(String searchString) { + Supplier> foundGenericCityObject = () -> model.getOtherCityObjects(). filter(co ->featureFilter(co, searchString)); int featureCount = (int) foundGenericCityObject.get().count(); - TreeView genericCityObjectView = mainWindow.getGenericCityObjectView(); + TreeView genericCityObjectView = mainWindow.getOtherCityObjectView(); TreeItem root = genericCityObjectView.getRoot(); root.getChildren().clear(); - buildGenericCityObjectTreeFromList(foundGenericCityObject, root); - addMoreButtonForGenericCityObjects(genericCityObjectView, root, genericCityObjectsChunkNr, + buildOtherCityObjectsTreeFromList(foundGenericCityObject, root); + addMoreButtonForOtherCityObjects(genericCityObjectView, root, otherCityObjectsChunkNr, foundGenericCityObject, featureCount); } @@ -1484,8 +1484,8 @@ public class CityDoctorController { addMoreButtonForCityFurniture(cityFurnitureView, root, cityFurnitureChunkNr, supply, foundCityFurniture.size()); } - public void fillTreeViewWithErrorGenericCityObjects() { - TreeView genericCityObjectsView = mainWindow.getGenericCityObjectView(); + public void fillTreeViewWithErrorOtherCityObjects() { + TreeView genericCityObjectsView = mainWindow.getOtherCityObjectView(); TreeItem root = genericCityObjectsView.getRoot(); if (model == null) { return; @@ -1496,14 +1496,14 @@ public class CityDoctorController { mainWindow.resetSearchBar(); root.getChildren().clear(); resetFeatureChunks(); - List foundGenericCityObjects = model.getCache().getErrorFeatureIds(); - List idList = model.getGenericCityObjectIds(); - foundGenericCityObjects.removeIf(gmlId -> (!idList.contains(gmlId))); + List foundOtherCityObjects = model.getCache().getErrorFeatureIds(); + List idList = model.getOtherCityObjectsIds(); + foundOtherCityObjects.removeIf(gmlId -> (!idList.contains(gmlId))); - Supplier> supply = () -> model.getCache().getStream(foundGenericCityObjects).map(co -> (GenericCityObject) co); - buildGenericCityObjectTreeFromList(supply, root); + Supplier> supply = () -> model.getCache().getStream(foundOtherCityObjects).map(co -> (GenericCityObject) co); + buildOtherCityObjectsTreeFromList(supply, root); updateTree(root); - addMoreButtonForGenericCityObjects(genericCityObjectsView, root, genericCityObjectsChunkNr, supply, foundGenericCityObjects.size()); + addMoreButtonForOtherCityObjects(genericCityObjectsView, root, otherCityObjectsChunkNr, supply, foundOtherCityObjects.size()); } public void fillTreeViewWithErrorTransportation() { @@ -1647,7 +1647,7 @@ public class CityDoctorController { fillTreeViewWithErrorTunnel(); fillTreeViewWithErrorWater(); fillTreeViewWithErrorCityFurniture(); - fillTreeViewWithErrorGenericCityObjects(); + fillTreeViewWithErrorOtherCityObjects(); } else { throw new IllegalStateException("Unknown filter index selected: " + newV); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index a578de92..5fb958e1 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -639,7 +639,7 @@ public class MainWindow extends Application { selectedTab = FeatureType.CITY_FURNITURE; break; case 8: - selectedTab = FeatureType.GENERIC_CITY_OBJECT; + selectedTab = FeatureType.OTHER_CITY_OBJECT; break; default: throw new IllegalStateException("Unknown tab index: " + index); @@ -673,7 +673,7 @@ public class MainWindow extends Application { case CITY_FURNITURE: cityFurnitureTab.setDisable(value); break; - case GENERIC_CITY_OBJECT: + case OTHER_CITY_OBJECT: otherObjectsTab.setDisable(value); break; default: @@ -1015,7 +1015,7 @@ public class MainWindow extends Application { return attributeView; } - public TreeView getGenericCityObjectView() { + public TreeView getOtherCityObjectView() { return otherObjectsView; } @@ -1161,7 +1161,7 @@ public class MainWindow extends Application { case WATER -> new Pair<>(waterTab, waterView); case LAND -> new Pair<>(terrainTab, terrainView); case CITY_FURNITURE -> new Pair<>(cityFurnitureTab, cityFurnitureView); - case GENERIC_CITY_OBJECT -> new Pair<>(otherObjectsTab, otherObjectsView); + case OTHER_CITY_OBJECT -> new Pair<>(otherObjectsTab, otherObjectsView); default -> throw new IllegalArgumentException("unknown feature type: " + coType); }; } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java index 705a2c28..83a556d3 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java @@ -92,7 +92,7 @@ public class TriangulatedGeometry { addPolygonDataFromCityObjects(model.getVegetation(), triGeom, Color.LIGHTGREEN, filters); addPolygonDataFromCityObjects(model.getWater(), triGeom, Color.LIGHTSKYBLUE, filters); addPolygonDataFromCityObjects(model.getCityFurniture(), triGeom, Color.BLUEVIOLET, filters); - addPolygonDataFromCityObjects(model.getGenericCityObjects(), triGeom, Color.BLACK, filters); + addPolygonDataFromCityObjects(model.getOtherCityObjects(), triGeom, Color.BLACK, filters); return triGeom; } -- GitLab From e2cce980b3355946bb4e7743b12ae0f86f4b1e09 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 4 Dec 2025 15:45:27 +0100 Subject: [PATCH 117/118] Build: Update version number --- CityDoctorParent/CityDoctorCheckResult/pom.xml | 2 +- CityDoctorParent/CityDoctorEdge/pom.xml | 2 +- CityDoctorParent/CityDoctorModel/pom.xml | 2 +- CityDoctorParent/CityDoctorValidation/pom.xml | 2 +- CityDoctorParent/Extensions/CityDoctorAutoPro/pom.xml | 2 +- CityDoctorParent/Extensions/CityDoctorGUI/pom.xml | 2 +- CityDoctorParent/Extensions/CityDoctorHealer/pom.xml | 2 +- CityDoctorParent/Extensions/CityDoctorHealerGUI/pom.xml | 2 +- CityDoctorParent/Extensions/CityDoctorHealerGenetic/pom.xml | 2 +- CityDoctorParent/pom.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CityDoctorParent/CityDoctorCheckResult/pom.xml b/CityDoctorParent/CityDoctorCheckResult/pom.xml index 6a9d0d2c..e1c3b850 100644 --- a/CityDoctorParent/CityDoctorCheckResult/pom.xml +++ b/CityDoctorParent/CityDoctorCheckResult/pom.xml @@ -6,7 +6,7 @@ de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 CityDoctorCheckResult diff --git a/CityDoctorParent/CityDoctorEdge/pom.xml b/CityDoctorParent/CityDoctorEdge/pom.xml index e7a5d0ab..25e06079 100644 --- a/CityDoctorParent/CityDoctorEdge/pom.xml +++ b/CityDoctorParent/CityDoctorEdge/pom.xml @@ -6,7 +6,7 @@ de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 CityDoctorEdge diff --git a/CityDoctorParent/CityDoctorModel/pom.xml b/CityDoctorParent/CityDoctorModel/pom.xml index 231741b7..815c086a 100644 --- a/CityDoctorParent/CityDoctorModel/pom.xml +++ b/CityDoctorParent/CityDoctorModel/pom.xml @@ -6,7 +6,7 @@ de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 ${project.version}-${git.commit.id.abbrev} diff --git a/CityDoctorParent/CityDoctorValidation/pom.xml b/CityDoctorParent/CityDoctorValidation/pom.xml index 48329e28..6cd03862 100644 --- a/CityDoctorParent/CityDoctorValidation/pom.xml +++ b/CityDoctorParent/CityDoctorValidation/pom.xml @@ -4,7 +4,7 @@ de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 CityDoctorValidation CityDoctorValidation diff --git a/CityDoctorParent/Extensions/CityDoctorAutoPro/pom.xml b/CityDoctorParent/Extensions/CityDoctorAutoPro/pom.xml index 52eb3030..ec1f300c 100644 --- a/CityDoctorParent/Extensions/CityDoctorAutoPro/pom.xml +++ b/CityDoctorParent/Extensions/CityDoctorAutoPro/pom.xml @@ -6,7 +6,7 @@ de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 ../../pom.xml CityDoctorAutoPro diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/pom.xml b/CityDoctorParent/Extensions/CityDoctorGUI/pom.xml index 2b07c026..01e74c25 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/pom.xml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/pom.xml @@ -6,7 +6,7 @@ de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 ../../pom.xml CityDoctorGUI diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/pom.xml b/CityDoctorParent/Extensions/CityDoctorHealer/pom.xml index 1d031a02..f31ce66a 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealer/pom.xml +++ b/CityDoctorParent/Extensions/CityDoctorHealer/pom.xml @@ -6,7 +6,7 @@ de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 ../../pom.xml CityDoctorHealer diff --git a/CityDoctorParent/Extensions/CityDoctorHealerGUI/pom.xml b/CityDoctorParent/Extensions/CityDoctorHealerGUI/pom.xml index 8eb1cfee..7368ea56 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealerGUI/pom.xml +++ b/CityDoctorParent/Extensions/CityDoctorHealerGUI/pom.xml @@ -7,7 +7,7 @@ de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 ../../pom.xml CityDoctorHealerGUI diff --git a/CityDoctorParent/Extensions/CityDoctorHealerGenetic/pom.xml b/CityDoctorParent/Extensions/CityDoctorHealerGenetic/pom.xml index 352b8af3..280bad62 100644 --- a/CityDoctorParent/Extensions/CityDoctorHealerGenetic/pom.xml +++ b/CityDoctorParent/Extensions/CityDoctorHealerGenetic/pom.xml @@ -4,7 +4,7 @@ de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 ../../pom.xml CityDoctorHealerGenetic diff --git a/CityDoctorParent/pom.xml b/CityDoctorParent/pom.xml index 05537176..b5198f83 100644 --- a/CityDoctorParent/pom.xml +++ b/CityDoctorParent/pom.xml @@ -5,7 +5,7 @@ 4.0.0 de.hft.stuttgart CityDoctorParent - 3.17.3 + 3.18.0 pom CityDoctorParent -- GitLab From 61859d4f300757953f0f78e2dba41b50337256c6 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Fri, 5 Dec 2025 16:18:17 +0100 Subject: [PATCH 118/118] Docs: Update Changelog --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b6855d9..d4d437b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.18.0] (2025-12-05) + +**Semi-breaking change:** This MR introduces breaking changes to the CityDoctorModel and CityObject classes. Usage of the CityDoctorValidation API and CLI remain backwards compatible. + + +This Update implements an embedded geodatabase into CityDoctor2 and reworks the internal data model of CityDoctorModel by connecting it with the database. +CityObjects and their geometric data are no longer permanently kept in memory, with a size-limited cache loading Objects from the database on request, evicting entries back to the database once the size limit is reached. This significantly reduces the RAM footprint of larger CityDoctorModels, allowing for bigger CityGml files to be used with CityDoctorGUI. +Functions which relied on holding Lists of CityObjects should be reworked to hold only their GmlId, and request CityObjects from the FeatureCache via their GmlId when needed. + +Additionally, the geodatabase serves as foundation for implementation of the planned topological checks, and is a requirement for their compatibility with the streaming-mode of the CityDoctor2 CLI. + +Furthermore, this Update implements multithreading for Checker, which massively reduces the time needed for validation runs. Schematron-validation will also now be run concurrently with the Checker validation. + + +### Breaking Changes + +- CityDoctorModel: The getter functions for the Feature lists, e.g. getBuildings(), now return Streams rather than Lists. +- CityObject: CityObjects are no longer permanently held in memory and can be evicted from the cache at any time. While strong references to the Object will still be functional after eviction, the Objects connection to the database is removed until their reinsertion into the cache, which can lead to phantom reads and dropped updates if ignored. + +### Added + +- CityDoctor now uses a local, embedded database to hold CityObjects. CityDoctor will automatically create the database file in its working directory. +- Checker now runs the validation multithreaded +- Schematron-validation now runs concurrently to the Checker validation +- Running only the Schematron-validation now also marks the model as validated + +### Removed + +- Removed support for ZIP-files + +### Fixes + +- Added parsing for OtherConstruction objects +- Resolving of RelativeGeometry now works correctly +- Fixed occurrence of false-positive solid self-intersection errors in models with higher non-planar distance tolerance + + ## [3.17.3] (2025-07-09) - Fixed global errors not being cleared upon validation rerun -- GitLab