From ef413298dcef3214342b549a86091b31d63f9e71 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 4 Jun 2025 16:23:50 +0200 Subject: [PATCH 001/111] 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/111] 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/111] 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/111] 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/111] 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/111] 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/111] 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/111] 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/111] 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/111] 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/111] 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/111] 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/111] 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 aa17c02e22ca7e3a1a6ccca5bfe71c8c70eb1509 Mon Sep 17 00:00:00 2001 From: Riegel Date: Fri, 18 Jul 2025 12:32:26 +0200 Subject: [PATCH 014/111] 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 015/111] 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 016/111] 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 017/111] 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 018/111] 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 019/111] 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 020/111] 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 021/111] 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 022/111] 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 023/111] 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 024/111] 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 025/111] 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 026/111] 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 027/111] 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 028/111] 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 029/111] 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 030/111] 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 031/111] 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 032/111] 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 28c42d2de1773d5fe5502186dc47e9b87397f01a Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Thu, 2 Oct 2025 16:53:49 +0200 Subject: [PATCH 033/111] !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 034/111] 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 035/111] 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 036/111] 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 037/111] 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 038/111] 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 039/111] 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 040/111] 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 041/111] 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 042/111] 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 043/111] 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 044/111] 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 045/111] 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 046/111] 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 047/111] 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 048/111] 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 049/111] 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 050/111] 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 051/111] 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 052/111] 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 053/111] 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 054/111] 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 055/111] 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 056/111] 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 057/111] 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 058/111] 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 059/111] 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 065/111] 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 066/111] 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 067/111] 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 068/111] 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 069/111] 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 070/111] 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 071/111] 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 072/111] 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 073/111] 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 074/111] 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 075/111] 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 076/111] 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 077/111] 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 078/111] 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 079/111] 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 080/111] 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 081/111] 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 082/111] 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 083/111] 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 084/111] 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 085/111] 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 086/111] 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 087/111] 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 088/111] 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 089/111] 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 090/111] 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 091/111] 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 092/111] 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 6d80a97fa169cd3b9a9317944e9e9c8dd0a5b026 Mon Sep 17 00:00:00 2001 From: Luna Riegel Date: Mon, 1 Dec 2025 09:42:18 +0100 Subject: [PATCH 093/111] 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 094/111] 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 095/111] 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 096/111] 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 097/111] 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 098/111] 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 099/111] 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 100/111] 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 101/111] 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 102/111] 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 103/111] 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 104/111] 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 105/111] 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 106/111] 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 107/111] 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 108/111] 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 109/111] 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 110/111] 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 111/111] 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