Commit 297334ab authored by Matthias Betz's avatar Matthias Betz
Browse files

Add design spec for OpenGL 3D view



Replaces the JavaFX SubScene/MeshView rendering with an OpenGL viewport
(openglfx-jogl, GL 3.3 core), single global VBO, GPU color-picking, and
baked fake shading. Windows/Linux targets; JavaFX 3D path removed.

Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
parents 5c2847ef bd40b396
......@@ -4,4 +4,6 @@
/CityDoctorParent/.idea/jarRepositories.xml
/CityDoctorParent/.idea/misc.xml
/CityDoctorParent/.idea/vcs.xml
.idea
\ No newline at end of file
/CityDoctorParent/.idea
.idea
/GUISettings.properties
# Title: Summary, imperative, start upper case, don't end with a period
# Follow the formatting of <type>:<description>
# No more than 50 chars. #### 50 chars is here: #
# Types: Fix, Feat, Build, CI, Docs, Perf, Refactor, Style, Test
# See: https://www.conventionalcommits.org/en/v1.0.0/
# Remember blank line between title and body.
# Body: Explain *what* and *why* (not *how*). Include task ID (Jira issue).
# Wrap at 72 chars. ################################## which is here: #
# At the end: Include Co-authored-by for all contributors.
# Include at least one empty line before it. Format:
# Co-authored-by: name <user@users.noreply.github.com>
#
# Tell Git to use this template with: git config commit.template .gitlab/.gitmessage
# How to Write a Git Commit Message:
# https://chris.beams.io/posts/git-commit/
#
# 1. Separate subject from body with a blank line
# 2. Limit the subject line to 50 characters
# 3. Capitalize the subject line
# 4. Do not end the subject line with a period
# 5. Use the imperative mood in the subject line
# 6. Wrap the body at 72 characters
# 7. Use the body to explain what and why vs. how
\ No newline at end of file
# https://chris.beams.io/posts/git-commit/
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="CityDoctorGUIStarter" type="Application" factoryName="Application" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter" />
<module name="CityDoctorGUI" />
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="de.hft.stuttgart.citydoctor2.gui.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="false" name="CityDoctorGUIStarter" type="Application" factoryName="Application" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter" />
<module name="CityDoctorGUI" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/CityDoctorParent/Extensions/CityDoctorGUI" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="de.hft.stuttgart.citydoctor2.gui.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Maven Verify" type="MavenRunConfiguration" factoryName="Maven">
<MavenSettings>
<option name="myGeneralSettings" />
<option name="myRunnerSettings" />
<option name="myRunnerParameters">
<MavenRunnerParameters>
<option name="cmdOptions" />
<option name="profiles">
<set />
</option>
<option name="goals">
<list>
<option value="verify" />
<option value="-U" />
</list>
</option>
<option name="multimoduleDir" />
<option name="pomFileName" />
<option name="profilesMap">
<map />
</option>
<option name="projectsCmdOptionValues">
<list />
</option>
<option name="resolveToWorkspace" value="false" />
<option name="workingDirPath" value="$PROJECT_DIR$/CityDoctorParent" />
</MavenRunnerParameters>
</option>
</MavenSettings>
<method v="2" />
</configuration>
</component>
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run all tests" type="JUnit" factoryName="JUnit">
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
<patterns>
<pattern testClass=".*Test$" />
</patterns>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
\ No newline at end of file
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Test Docker deployment" type="docker-deploy" factoryName="docker-image" server-name="Docker">
<deployment type="docker-image">
<settings>
<option name="imageTag" value="maven:eclipse-temurin" />
<option name="command" value="mvn verify -U" />
<option name="containerName" value="deployment-test" />
<option name="commandLineOptions" value="-w /cd2/citydoctorparent" />
<option name="volumeBindings">
<list>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/cd2/" />
<option name="hostPath" value="C:\Users\hft-r\IdeaProjects\citydoctor2" />
</DockerVolumeBindingImpl>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/root/.m2" />
<option name="hostPath" value="C:\Users\hft-r\Docker Maven Repo\citydoctor2" />
</DockerVolumeBindingImpl>
</list>
</option>
</settings>
</deployment>
<method v="2" />
</configuration>
</component>
\ No newline at end of file
......@@ -5,7 +5,119 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.16.0] ((2024-11-11))
## [3.18.3] (2026-04-16)
### Fixes
- Fixed FragmentedSurface errors reporting roof BoundarySurfaces as ground BoundarySurfaces
- Fixed degenerated triangles causing false-positive detection of AllPolygonsWrongOrientation errors
- Fixed -db_location and -db_settings arguments not being parsed when starting the GUI from the CLI
## [3.18.2] (2026-02-24)
### Added
- CLI parameter "-db_location" for changing the location of the database-file of the embedded database. This parameter will
override the location settings of the DB-settings file.
- CLI parameter "-db_settings" for giving a path to a DB-settings .properties file.
- Added -db_location pointing to the system's temp directory to the start scripts for the binaries
- Added buttons for (un)checking all Geometric or Semantic requirements
- Added a Hyperlink to CityDoctor's homepage with detailed explanations for each requirement
- The GUI will now show a loading spinner while waiting for Schematron to finish.
### Fixes
- Connected CityDoctorHealer to the embedded database
- Various small fixes and improvements to performance
## [3.18.1] (2025-12-10)
### Hotfix
This hotfix removes the AUTO_SERVER flag from the embedded database, which causes a Firewall popup when the database is
started, due to opening local ports for accessing the database while CityDoctor is running.
The Firewall request can be safely denied, as CityDoctor uses the database-file for connections to the database.
Accepting the request does not open up remote connections to the database, as H2 by default only allows connections from
the local machine.
## [3.18.0] (2025-12-05)
**Semi-breaking change:** This MR introduces breaking changes to the CityDoctorModel and CityObject classes. Usage of the CityDoctorValidation API and CLI remain backwards compatible.
This Update implements an embedded geodatabase into CityDoctor2 and reworks the internal data model of CityDoctorModel by connecting it with the database.
CityObjects and their geometric data are no longer permanently kept in memory, with a size-limited cache loading Objects from the database on request, evicting entries back to the database once the size limit is reached. This significantly reduces the RAM footprint of larger CityDoctorModels, allowing for bigger CityGml files to be used with CityDoctorGUI.
Functions which relied on holding Lists of CityObjects should be reworked to hold only their GmlId, and request CityObjects from the FeatureCache via their GmlId when needed.
Additionally, the geodatabase serves as foundation for implementation of the planned topological checks, and is a requirement for their compatibility with the streaming-mode of the CityDoctor2 CLI.
Furthermore, this Update implements multithreading for Checker, which massively reduces the time needed for validation runs. Schematron-validation will also now be run concurrently with the Checker validation.
### Breaking Changes
- CityDoctorModel: The getter functions for the Feature lists, e.g. getBuildings(), now return Streams rather than Lists.
- CityObject: CityObjects are no longer permanently held in memory and can be evicted from the cache at any time. While strong references to the Object will still be functional after eviction, the Objects connection to the database is removed until their reinsertion into the cache, which can lead to phantom reads and dropped updates if ignored.
### Added
- CityDoctor now uses a local, embedded database to hold CityObjects. CityDoctor will automatically create the database file in its working directory.
- Checker now runs the validation multithreaded
- Schematron-validation now runs concurrently to the Checker validation
- Running only the Schematron-validation now also marks the model as validated
### Removed
- Removed support for ZIP-files
### Fixes
- Added parsing for OtherConstruction objects
- Resolving of RelativeGeometry now works correctly
- Fixed occurrence of false-positive solid self-intersection errors in models with higher non-planar distance tolerance
## [3.17.3] (2025-07-09)
- Fixed global errors not being cleared upon validation rerun
- Fixed ConstructiveElements of Buildings not being parsed
- Fixed PolygonPatches being added twice to Geometry
- Fixed missing uom and value for valid QualityADE output
- Reduced FOP-logging verbosity
- Added a facing property to Geometry, denoting whether it is facing inwards or outwards
## [3.17.2] (2025-06-05)
- Rework of AbstractCheck, allowing the implementation of Visitors that use the CheckEngine to traverse the internal
data model (#108)
- Reimplemented parsing of TINReliefs
- Fixed Tunnel features missing from the ErrorCollector and BBox calculatio
## [3.17.1] (2025-04-14)
### Fixes
- Fixed an oversight causing the CityDoctor CLI to ignore .xml files
## [3.17.0] (2025-03-13)
### Added
- Checking of CityGml ZIP-archives with CityDoctor (#12)
### Changed
- Changed GUI to allow loading and checking of ZIP-archives. (#12)
### Fixes
- Reworked self-intersection calculations to eliminate false-positives (#65)
- CityGml objects added in 3.16.0 are now being recorded in error statistics and validation reports (#104)
- Envelope and CRS will now be included when exporting singular features
- Fixed various minor errors and bugs in CityDoctorGUI
## [3.16.0] (2024-11-11)
CityDoctor now supports the import, export and validation of CityGML 3.0 files.
......
......@@ -6,7 +6,7 @@
<parent>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId>
<version>3.16.0</version>
<version>3.18.3</version>
</parent>
<artifactId>CityDoctorCheckResult</artifactId>
<dependencies>
......
......@@ -22,6 +22,7 @@ import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -65,7 +66,11 @@ public class CheckReport {
private Map<String, FeatureReport> featureReports;
public static CheckReport load(String file) throws CheckReportParseException {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
return load(Path.of(file));
}
public static CheckReport load(Path path) throws CheckReportParseException {
try (BufferedReader reader = new BufferedReader(new FileReader(path.toFile()))) {
CheckReport report = (CheckReport) getContext().createUnmarshaller().unmarshal(new InputSource(reader));
enrichReportStructure(report);
return report;
......@@ -105,6 +110,9 @@ public class CheckReport {
addFeatureReports(report, valResults.getTransportationReports());
addFeatureReports(report, valResults.getVegetationReports());
addFeatureReports(report, valResults.getWaterReports());
addFeatureReports(report, valResults.getTunnelReports());
addFeatureReports(report, valResults.getCityFurnitureReports());
addFeatureReports(report, valResults.getGenericCityObjectReports());
}
}
......
......@@ -43,6 +43,15 @@ public class GlobalErrorStatistics {
@XmlElement(name = "num_error_transportation")
private int numErrorTransportation;
@XmlElement(name = "num_error_tunnel_objects")
private int numErrorTunnelObjects;
@XmlElement(name = "num_error_city_furniture")
private int numErrorCityFurniture;
@XmlElement(name = "num_error_generic_city_objects")
private int numErrorGenericCityObjects;
public int getNumErrorBuildings() {
return numErrorBuildings;
}
......@@ -91,5 +100,27 @@ public class GlobalErrorStatistics {
this.numErrorTransportation = numErrorTransportation;
}
public int getNumErrorTunnelObjects() {
return numErrorTunnelObjects;
}
public void setNumErrorTunnelObjects(int numErrorTunnelObjects) {
this.numErrorTunnelObjects = numErrorTunnelObjects;
}
public int getNumErrorCityFurniture() {
return numErrorCityFurniture;
}
public void setNumErrorCityFurniture(int numErrorCityFurniture) {
this.numErrorCityFurniture = numErrorCityFurniture;
}
public int getNumErrorGenericCityObjects() {
return numErrorGenericCityObjects;
}
public void setNumErrorGenericCityObjects(int numErrorGenericCityObjects) {
this.numErrorGenericCityObjects = numErrorGenericCityObjects;
}
}
......@@ -43,6 +43,16 @@ public class ModelStatistics {
@XmlElement(name = "num_water_objects", required = true)
private int numWaterObjects;
@XmlElement(name = "num_tunnel_objects", required = true)
private int numTunnelObjects;
@XmlElement(name = "num_city_furniture", required = true)
private int numCityFurniture;
@XmlElement(name = "num_generic_city_objects", required = true)
private int numGenericCityObjects;
public int getNumBuildings() {
return numBuildings;
}
......@@ -91,4 +101,27 @@ public class ModelStatistics {
this.numWaterObjects = numWaterObjects;
}
public int getNumTunnelObjects() {
return numTunnelObjects;
}
public void setNumTunnelObjects(int numTunnelObjects) {
this.numTunnelObjects = numTunnelObjects;
}
public int getNumCityFurniture() {
return numCityFurniture;
}
public void setNumCityFurniture(int numCityFurniture) {
this.numCityFurniture = numCityFurniture;
}
public int getNumGenericCityObjects() {
return numGenericCityObjects;
}
public void setNumGenericCityObjects(int numGenericCityObjects) {
this.numGenericCityObjects = numGenericCityObjects;
}
}
......@@ -50,7 +50,15 @@ public class ValidationResults {
@XmlElement(name = "water_object_report")
private List<FeatureReport> waterReports;
@XmlElement(name = "tunnel_report")
private List<FeatureReport> tunnelReports;
@XmlElement(name = "city_furniture_report")
private List<FeatureReport> cityFurnitureReports;
@XmlElement(name = "generic_city_object_report")
private List<FeatureReport> genericCityObjectReports;
public List<FeatureReport> getBuildingReports() {
if (buildingReports == null) {
......@@ -128,7 +136,38 @@ public class ValidationResults {
public void setWaterReports(List<FeatureReport> waterReports) {
this.waterReports = waterReports;
}
public List<FeatureReport> getTunnelReports() {
if (tunnelReports == null) {
tunnelReports = new ArrayList<>();
}
return tunnelReports;
}
public void setTunnelReports(List<FeatureReport> tunnelReports) {
this.tunnelReports = tunnelReports;
}
public List<FeatureReport> getCityFurnitureReports() {
if (cityFurnitureReports == null) {
cityFurnitureReports = new ArrayList<>();
}
return cityFurnitureReports;
}
public void setCityFurnitureReports(List<FeatureReport> cityFurnitureReports) {
this.cityFurnitureReports = cityFurnitureReports;
}
public List<FeatureReport> getGenericCityObjectReports() {
if (genericCityObjectReports == null) {
genericCityObjectReports = new ArrayList<>();
}
return genericCityObjectReports;
}
public void setGenericCityObjectReports(List<FeatureReport> genericCityObjectReports) {
this.genericCityObjectReports = genericCityObjectReports;
}
}
......@@ -6,7 +6,7 @@
<parent>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId>
<version>3.16.0</version>
<version>3.18.3</version>
</parent>
<artifactId>CityDoctorEdge</artifactId>
<dependencies>
......
......@@ -32,15 +32,16 @@ import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType;
import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry.Orientation;
public class IntersectionErrorsTest {
@Test
public void testWrongIntersection() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
ConcretePolygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
LinearRing ext1 = new LinearRing(LinearRingType.EXTERIOR);
p1.setExteriorRing(ext1);
......@@ -65,6 +66,7 @@ public class IntersectionErrorsTest {
ext1.addVertex(v1);
ConcretePolygon p2 = new ConcretePolygon();
geom.addPolygon(p2);
LinearRing ext2 = new LinearRing(LinearRingType.EXTERIOR);
p2.setExteriorRing(ext2);
......@@ -87,8 +89,8 @@ public class IntersectionErrorsTest {
ext2.addVertex(v8);
ext2.addVertex(v5);
geom.addPolygon(p1);
geom.addPolygon(p2);
geom.prepareForChecking();
......
......@@ -43,12 +43,13 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry.Orientation;
public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoTrianglesOneSharedEdgeInPlane() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -161,7 +162,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsTwoSharedEdges() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -212,7 +213,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsOneSharedEdgeIntersectionPolygonPlanar() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -276,7 +277,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsOneSharedEdgeIntersectionPolygonPlanar2() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -339,7 +340,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsOneSharedEdgeCloseEdgePlanar() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -393,7 +394,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsOneSharedEdgeNotPlanar() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -447,7 +448,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsOneSharedPointNotPlanar() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -508,7 +509,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsTouchingInOnePointNoIntersectionNotPlanar() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -561,7 +562,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsTouchingInOnePointNoIntersectionNotPlanar2() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -616,7 +617,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsNotInSamePlaneBelow() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -670,7 +671,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsNotInSamePlaneAbove() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -724,7 +725,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsNotInSamePlaneAboveInverted() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -778,7 +779,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsTouchingNoPointsSameNotPlanar() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -834,7 +835,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsTouchingEdgeNoPointsSameNotPlanar() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -890,7 +891,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsTouchingEdgeInPointNoPointsSameNotPlanar() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -946,7 +947,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsTwoTouchingEdgesNotPlanar() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -1002,7 +1003,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsPlanarInnerRingsContained() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -1067,7 +1068,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsPlanarInnerRingsContainedConnected() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -1128,7 +1129,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsInnerRingsConnected() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -1191,7 +1192,7 @@ public class MeshSurfaceUtilsTest {
@Test
public void testIntersectionTwoPolygonsInnerRingsThroughHole() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1, Orientation.OUTWARD);
Polygon p1 = new ConcretePolygon();
geom.addPolygon(p1);
......@@ -1254,11 +1255,11 @@ public class MeshSurfaceUtilsTest {
assertTrue(intersectPolygons.isEmpty());
}
@Test(timeout = 10000)
@Test/*(timeout = 10000)*/
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);
......
......@@ -6,7 +6,7 @@
<parent>
<groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId>
<version>3.16.0</version>
<version>3.18.3</version>
</parent>
<properties>
<versionString>${project.version}-${git.commit.id.abbrev}</versionString>
......@@ -38,11 +38,6 @@
<groupId>org.jogamp.jogl</groupId>
<artifactId>jogl-all-main</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
......@@ -60,6 +55,15 @@
<groupId>org.locationtech.proj4j</groupId>
<artifactId>proj4j</artifactId>
</dependency>
<dependency>
<groupId>org.locationtech.proj4j</groupId>
<artifactId>proj4j-epsg</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
......@@ -68,6 +72,18 @@
<groupId>io.github.earcut4j</groupId>
<artifactId>earcut4j</artifactId>
</dependency>
<dependency>
<groupId>org.orbisgis</groupId>
<artifactId>h2gis</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
</dependencies>
<build>
<resources>
......
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.check;
import java.util.Collections;
import java.util.Set;
/**
* This is an empty implementation for a check. Can be used as a normal visitor
* for the city doctor data model
*
* @author Matthias Betz
*
*/
public class AbstractCheck extends Check {
@Override
public Set<Requirement> appliesToRequirements() {
return Collections.emptySet();
}
@Override
public CheckId getCheckId() {
return null;
}
@Override
public RequirementType getType() {
return null;
}
@Override
public Check createNewInstance() {
return null;
}
}
package de.hft.stuttgart.citydoctor2.check;
import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError;
import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError;
import de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError;
import de.hft.stuttgart.citydoctor2.check.error.DependenciesNotMetError;
import de.hft.stuttgart.citydoctor2.check.error.MultipleConnectedComponentsError;
import de.hft.stuttgart.citydoctor2.check.error.NestedRingError;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldEdgeError;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldVertexError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonNormalsDeviation;
import de.hft.stuttgart.citydoctor2.check.error.NotCeilingError;
import de.hft.stuttgart.citydoctor2.check.error.NotFloorError;
import de.hft.stuttgart.citydoctor2.check.error.NotGroundError;
import de.hft.stuttgart.citydoctor2.check.error.NotWallError;
import de.hft.stuttgart.citydoctor2.check.error.NullAreaError;
import de.hft.stuttgart.citydoctor2.check.error.PlanarityError;
import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonHoleOutsideError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonIntersectingRingsError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonSameOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWithoutSurfaceError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError;
import de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError;
import de.hft.stuttgart.citydoctor2.check.error.RingError;
import de.hft.stuttgart.citydoctor2.check.error.RingNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.RingSelfIntersectionError;
import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError;
import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import de.hft.stuttgart.citydoctor2.check.error.SolidError;
import de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceFragmentedError;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.check.error.XMLValidationError;
public abstract class AbstractErrorVisitor implements ErrorVisitor {
@Override
public void visit(RingError err) {
}
@Override
public void visit(PolygonError err) {
}
@Override
public void visit(RingSelfIntersectionError err) {
}
@Override
public void visit(PlanarityError err) {
}
@Override
public void visit(PolygonHoleOutsideError err) {
}
@Override
public void visit(NonManifoldEdgeError err) {
}
@Override
public void visit(MultipleConnectedComponentsError err) {
}
@Override
public void visit(NestedRingError err) {
}
@Override
public void visit(NonManifoldVertexError err) {
}
@Override
public void visit(PolygonWrongOrientationError err) {
}
@Override
public void visit(PolygonSameOrientationError err) {
}
@Override
public void visit(SolidNotClosedError err) {
}
@Override
public void visit(DependenciesNotMetError err) {
}
@Override
public void visit(UnknownCheckError err) {
}
@Override
public void visit(RingNotClosedError err) {
}
@Override
public void visit(ConsecutivePointSameError err) {
}
@Override
public void visit(AllPolygonsWrongOrientationError err) {
}
@Override
public void visit(PolygonInteriorDisconnectedError err) {
}
@Override
public void visit(NullAreaError err) {
}
@Override
public void visit(RingTooFewPointsError err) {
}
@Override
public void visit(NonPlanarPolygonNormalsDeviation err) {
}
@Override
public void visit(NonPlanarPolygonDistancePlaneError err) {
}
@Override
public void visit(PolygonIntersectingRingsError err) {
}
@Override
public void visit(SolidSelfIntError err) {
}
@Override
public void visit(TooFewPolygonsError err) {
}
@Override
public void visit(RingDuplicatePointError err) {
}
@Override
public void visit(RingEdgeIntersectionError err) {
}
@Override
public void visit(PointTouchesEdgeError err) {
}
@Override
public void visit(NotCeilingError err) {
}
@Override
public void visit(NotFloorError err) {
}
@Override
public void visit(NotWallError err) {
}
@Override
public void visit(NotGroundError err) {
}
@Override
public void visit(CheckError err) {
}
@Override
public void visit(SchematronError err) {
}
@Override
public void visit(SurfaceFragmentedError err) {
}
@Override
public void visit(DegeneratedRingError err) {
}
@Override
public void visit(AttributeMissingError err) {
}
@Override
public void visit(AttributeValueWrongError err) {
}
@Override
public void visit(AttributeInvalidError err) {
}
@Override
public void visit(PolygonWithoutSurfaceError err) {
}
@Override
public void visit(SolidError err) {
}
@Override
public void visit(XMLValidationError err) {
}
}
......@@ -49,10 +49,11 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
* @author Matthias Betz
*
*/
public abstract class Check {
public abstract non-sealed class Check implements CheckableVisitor {
private final List<Class<Checkable>> applicableToClasses = new ArrayList<>(2);
@SuppressWarnings("unchecked")
protected Check() {
Method[] declaredMethods = getClass().getDeclaredMethods();
......@@ -66,11 +67,6 @@ public abstract class Check {
}
}
/**
* Returns all classes for which the check needs to be executed
*
* @return a list of classes which the check applies to
*/
public List<Class<Checkable>> getApplicableToClasses() {
return applicableToClasses;
}
......@@ -103,17 +99,25 @@ public abstract class Check {
public abstract RequirementType getType();
/**
* Checks whether the check can be executed on this checkable, meaning the
* checkable or its content can not have any error of any check dependent on
* this check. <br>
* If the check cannot be executed a CheckResult will be created with the
* Returns whether this check validates a feature.
*
* @return true if it validates, false otherwise
*/
@Override
public boolean isValidator() {
return true;
}
/**
* Checks whether this Check can execute on the Checkable, meaning the Checkable contains no error from
* this Check's dependencies and is applicable to this Check. <br/>
* If the check cannot be executed due to not fulfilling the dependencies a CheckResult will be created with the
* ResultStatus = DEPENDENCIES_NOT_MET.
*
*
* @param c the checkable
* @param crc container for all check results
* @return true if the check can be executed, false if the checkable itself or
* one of its containing checkables have an error.
* @return true if the check can be executed, false otherwise
*/
@Override
public boolean canExecute(Checkable c) {
// ignore objects where this check doesn't apply to
if (!canBeApplied(c)) {
......@@ -150,6 +154,7 @@ public abstract class Check {
*
* @param checkable a checkable
*/
@Override
public void check(Checkable checkable) {
}
......@@ -159,6 +164,7 @@ public abstract class Check {
*
* @param ab building or building part
*/
@Override
public void check(AbstractBuilding ab) {
}
......@@ -168,6 +174,7 @@ public abstract class Check {
*
* @param bs a boundary surface
*/
@Override
public void check(BoundarySurface bs) {
}
......@@ -177,6 +184,7 @@ public abstract class Check {
*
* @param bo a bridge
*/
@Override
public void check(BridgeObject bo) {
}
......@@ -186,6 +194,7 @@ public abstract class Check {
*
* @param b a building
*/
@Override
public void check(Building b) {
}
......@@ -195,6 +204,7 @@ public abstract class Check {
*
* @param bi a building installation
*/
@Override
public void check(Installation bi) {
}
......@@ -204,6 +214,7 @@ public abstract class Check {
*
* @param bp a building part
*/
@Override
public void check(BuildingPart bp) {
}
......@@ -213,6 +224,7 @@ public abstract class Check {
*
* @param co a city object
*/
@Override
public void check(CityObject co) {
}
......@@ -222,6 +234,7 @@ public abstract class Check {
*
* @param lo a land use object
*/
@Override
public void check(LandObject lo) {
}
......@@ -231,6 +244,7 @@ public abstract class Check {
*
* @param o an opening
*/
@Override
public void check(Opening o) {
}
......@@ -240,6 +254,7 @@ public abstract class Check {
*
* @param to a transportation object
*/
@Override
public void check(TransportationObject to) {
}
......@@ -249,6 +264,7 @@ public abstract class Check {
*
* @param veg a vegetation object
*/
@Override
public void check(Vegetation veg) {
}
......@@ -258,6 +274,7 @@ public abstract class Check {
*
* @param wo a water object
*/
@Override
public void check(WaterObject wo) {
}
......@@ -267,6 +284,7 @@ public abstract class Check {
*
* @param geom a geometry
*/
@Override
public void check(Geometry geom) {
}
......@@ -276,6 +294,7 @@ public abstract class Check {
*
* @param poly a polygon
*/
@Override
public void check(Polygon poly) {
}
......@@ -285,26 +304,36 @@ public abstract class Check {
*
* @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
......@@ -316,7 +345,7 @@ public abstract class Check {
* @param config sometimes there are global parameters which can be used by
* checks. Those are be stored in this container
*/
public void init(Map<String, String> params, ParserConfiguration config) {
public void init(Map<CheckId, Map<String, String>> params, ParserConfiguration config) {
}
......
......@@ -83,4 +83,8 @@ public interface CheckError extends Serializable {
*/
public void report(ErrorReport report);
public default boolean isParsingError() {
return false;
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment