From 7a22ca9c2c8fb3f952751c0b48abf765a9069be4 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 30 Jul 2024 15:13:12 +0200 Subject: [PATCH 01/48] Updated .gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0419d7a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/CityDoctorParent/.idea/.gitignore +/CityDoctorParent/.idea/compiler.xml +/CityDoctorParent/.idea/encodings.xml +/CityDoctorParent/.idea/jarRepositories.xml +/CityDoctorParent/.idea/misc.xml +/CityDoctorParent/.idea/vcs.xml -- GitLab From a5a82382851142bf7d8426c94fe89a224bf7a988 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 31 Jul 2024 10:31:19 +0200 Subject: [PATCH 02/48] Open Source release of CityDoctor-GUI --- CityDoctorParent/CityDoctorGUI/.gitignore | 2 + .../CityDoctorGUI/checkForSolid.xml | 14 + CityDoctorParent/CityDoctorGUI/pom.xml | 190 +++ .../src/assembly/common/checkForSolid.xml | 14 + .../src/assembly/lin/assembly.xml | 42 + .../CityDoctorGUI/src/assembly/lin/start.sh | 2 + .../src/assembly/mac/assembly.xml | 42 + .../src/assembly/no_runtime/assembly.xml | 34 + .../src/assembly/no_runtime/start.bat | 2 + .../src/assembly/no_runtime/start.sh | 2 + .../src/assembly/win/assembly.xml | 42 + .../CityDoctorGUI/src/assembly/win/start.bat | 2 + .../citydoctor2/gui/AboutDialog.java | 101 ++ .../citydoctor2/gui/CheckDialog.java | 592 +++++++++ .../citydoctor2/gui/CheckStatus.java | 7 + .../citydoctor2/gui/CityDoctorController.java | 1086 +++++++++++++++++ .../citydoctor2/gui/CityDoctorGUIStarter.java | 27 + .../citydoctor2/gui/ClickDispatcher.java | 9 + .../citydoctor2/gui/ClickHandler.java | 13 + .../citydoctor2/gui/ExceptionDialog.java | 56 + .../stuttgart/citydoctor2/gui/FilterPane.java | 305 +++++ .../citydoctor2/gui/GlobalParameter.java | 103 ++ .../citydoctor2/gui/HighlightController.java | 223 ++++ .../citydoctor2/gui/LanguageSelectorCell.java | 49 + .../citydoctor2/gui/ListErrorVisitor.java | 263 ++++ .../citydoctor2/gui/LoadingInfoDialog.java | 38 + .../citydoctor2/gui/MainToolBar.java | 349 ++++++ .../stuttgart/citydoctor2/gui/MainWindow.java | 980 +++++++++++++++ .../citydoctor2/gui/ModelProvider.java | 17 + .../citydoctor2/gui/OpenFileDialog.java | 204 ++++ .../gui/PolygonClickDispatcher.java | 19 + .../stuttgart/citydoctor2/gui/Renderer.java | 780 ++++++++++++ .../stuttgart/citydoctor2/gui/Settings.java | 66 + .../citydoctor2/gui/TableEditCell.java | 148 +++ .../citydoctor2/gui/TreeEditCell.java | 121 ++ .../citydoctor2/gui/TriangulatedGeometry.java | 340 ++++++ .../citydoctor2/gui/ValidationView.java | 77 ++ .../gui/VertexClickDispatcher.java | 19 + .../citydoctor2/gui/VertexClickHandler.java | 71 ++ .../hft/stuttgart/citydoctor2/gui/View.java | 32 + .../citydoctor2/gui/ViewRegistration.java | 31 + .../citydoctor2/gui/WriteReportDialog.java | 262 ++++ .../gui/filter/TypeFilterSelection.java | 24 + .../citydoctor2/gui/filter/ViewFilter.java | 35 + .../citydoctor2/gui/logger/GuiLogger.java | 104 ++ .../citydoctor2/gui/table/ErrorStat.java | 65 + .../gui/tree/AllBoundarySurfacesNode.java | 46 + .../AllBridgeConstructiveElementsNode.java | 64 + .../gui/tree/AllBridgePartsNode.java | 64 + .../citydoctor2/gui/tree/AllBridgesNode.java | 30 + .../gui/tree/AllBuildingPartsNode.java | 46 + .../gui/tree/AllBuildingsNode.java | 31 + .../gui/tree/AllInstallationsNode.java | 46 + .../citydoctor2/gui/tree/AllOpeningsNode.java | 38 + .../citydoctor2/gui/tree/AllTerrainNode.java | 31 + .../citydoctor2/gui/tree/AllTinNode.java | 49 + .../gui/tree/AllTransportationNode.java | 31 + .../gui/tree/AllVegetationNode.java | 31 + .../citydoctor2/gui/tree/AllWaterNode.java | 31 + .../gui/tree/BoundarySurfaceNode.java | 35 + .../tree/BridgeConstructiveElementNode.java | 54 + .../citydoctor2/gui/tree/BridgeNode.java | 55 + .../citydoctor2/gui/tree/BuildingNode.java | 40 + .../gui/tree/BuildingPartNode.java | 36 + .../gui/tree/ButtonRenderable.java | 50 + .../citydoctor2/gui/tree/CityObjectNode.java | 36 + .../citydoctor2/gui/tree/Displayable.java | 9 + .../citydoctor2/gui/tree/EdgeNode.java | 62 + .../citydoctor2/gui/tree/ErrorCell.java | 20 + .../gui/tree/ErrorItemVisitor.java | 410 +++++++ .../citydoctor2/gui/tree/ErrorNode.java | 29 + .../citydoctor2/gui/tree/GeometryNode.java | 49 + .../gui/tree/InstallationNode.java | 35 + .../citydoctor2/gui/tree/LandUseNode.java | 54 + .../citydoctor2/gui/tree/LinearRingNode.java | 41 + .../citydoctor2/gui/tree/OpeningNode.java | 36 + .../citydoctor2/gui/tree/PolygonNode.java | 41 + .../citydoctor2/gui/tree/ReliefNode.java | 55 + .../citydoctor2/gui/tree/Renderable.java | 34 + .../gui/tree/RenderableTreeCell.java | 58 + .../citydoctor2/gui/tree/TextNode.java | 28 + .../citydoctor2/gui/tree/TinNode.java | 55 + .../citydoctor2/gui/tree/TreeRequirement.java | 67 + .../citydoctor2/gui/tree/VegetationNode.java | 61 + .../citydoctor2/gui/tree/VertexNode.java | 53 + .../src/main/resources/citydoctor_logo.ico | Bin 0 -> 4286 bytes .../citydoctor2/gui/AboutDialog.fxml | 35 + .../citydoctor2/gui/CheckDialog.fxml | 129 ++ .../gui/CreateRenderDataDialog.fxml | 22 + .../stuttgart/citydoctor2/gui/FilterPane.fxml | 89 ++ .../citydoctor2/gui/MainToolBar.fxml | 85 ++ .../stuttgart/citydoctor2/gui/MainWindow.fxml | 179 +++ .../citydoctor2/gui/OpenFileDialog.fxml | 124 ++ .../citydoctor2/gui/WriteReportDialog.fxml | 89 ++ .../stuttgart/citydoctor2/gui/icons/Add.png | Bin 0 -> 1346 bytes .../citydoctor2/gui/icons/BMBF_Logo.png | Bin 0 -> 9830 bytes .../gui/icons/Beuth-Logo_basis.png | Bin 0 -> 38623 bytes .../gui/icons/CityDoctor-Logo-rot_klein.jpg | Bin 0 -> 42616 bytes .../gui/icons/CityDoctor-Logo-rot_klein.png | Bin 0 -> 121420 bytes .../citydoctor2/gui/icons/Culling.png | Bin 0 -> 1438 bytes .../citydoctor2/gui/icons/Delete.png | Bin 0 -> 4742 bytes .../citydoctor2/gui/icons/Delete3.png | Bin 0 -> 1226 bytes .../stuttgart/citydoctor2/gui/icons/Globe.png | Bin 0 -> 6728 bytes .../stuttgart/citydoctor2/gui/icons/about.png | Bin 0 -> 1454 bytes .../citydoctor2/gui/icons/autopro32x32.png | Bin 0 -> 1995 bytes .../citydoctor2/gui/icons/check32x32.png | Bin 0 -> 1118 bytes .../citydoctor2/gui/icons/citydoctor_logo.png | Bin 0 -> 1140 bytes .../citydoctor2/gui/icons/error_stat32x32.png | Bin 0 -> 3947 bytes .../citydoctor2/gui/icons/healing.png | Bin 0 -> 5098 bytes .../citydoctor2/gui/icons/hft_logo.png | Bin 0 -> 14528 bytes .../citydoctor2/gui/icons/icon_de.png | Bin 0 -> 195 bytes .../citydoctor2/gui/icons/icon_en.png | Bin 0 -> 1146 bytes .../citydoctor2/gui/icons/lod1_32x32.png | Bin 0 -> 1381 bytes .../citydoctor2/gui/icons/lod2_32x32.png | Bin 0 -> 1816 bytes .../citydoctor2/gui/icons/lod3_32x32.png | Bin 0 -> 2011 bytes .../citydoctor2/gui/icons/lod4_32x32.png | Bin 0 -> 2127 bytes .../citydoctor2/gui/icons/openFolderIcon.png | Bin 0 -> 995 bytes .../stuttgart/citydoctor2/gui/icons/save.png | Bin 0 -> 869 bytes .../stuttgart/citydoctor2/gui/icons/scene.png | Bin 0 -> 1468 bytes .../citydoctor2/gui/icons/validation.png | Bin 0 -> 5916 bytes .../citydoctor2/gui/icons/wireframe32x32.png | Bin 0 -> 2147 bytes .../stuttgart/citydoctor2/gui/statistics.fxml | 35 + .../src/main/resources/log4j2.xml | 23 + ...impleSolid_SrefBS-GE-gml-LR-0001-T0001.gml | 187 +++ .../resources/testConfigWithStreaming.yml | 46 + CityDoctorParent/pom.xml | 1 + 126 files changed, 10089 insertions(+) create mode 100644 CityDoctorParent/CityDoctorGUI/.gitignore create mode 100644 CityDoctorParent/CityDoctorGUI/checkForSolid.xml create mode 100644 CityDoctorParent/CityDoctorGUI/pom.xml create mode 100644 CityDoctorParent/CityDoctorGUI/src/assembly/common/checkForSolid.xml create mode 100644 CityDoctorParent/CityDoctorGUI/src/assembly/lin/assembly.xml create mode 100644 CityDoctorParent/CityDoctorGUI/src/assembly/lin/start.sh create mode 100644 CityDoctorParent/CityDoctorGUI/src/assembly/mac/assembly.xml create mode 100644 CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/assembly.xml create mode 100644 CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/start.bat create mode 100644 CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/start.sh create mode 100644 CityDoctorParent/CityDoctorGUI/src/assembly/win/assembly.xml create mode 100644 CityDoctorParent/CityDoctorGUI/src/assembly/win/start.bat create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/AboutDialog.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckStatus.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorGUIStarter.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ClickDispatcher.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ClickHandler.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ExceptionDialog.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/FilterPane.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/GlobalParameter.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/HighlightController.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/LanguageSelectorCell.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ListErrorVisitor.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/LoadingInfoDialog.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ModelProvider.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/OpenFileDialog.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/PolygonClickDispatcher.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Settings.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TableEditCell.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TreeEditCell.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ValidationView.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/VertexClickDispatcher.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/VertexClickHandler.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/View.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ViewRegistration.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/WriteReportDialog.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/filter/TypeFilterSelection.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/filter/ViewFilter.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/logger/GuiLogger.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/table/ErrorStat.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBoundarySurfacesNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgeConstructiveElementsNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgePartsNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgesNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingPartsNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingsNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllInstallationsNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOpeningsNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTerrainNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTinNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTransportationNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllVegetationNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllWaterNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BoundarySurfaceNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BridgeConstructiveElementNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BridgeNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingPartNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ButtonRenderable.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/CityObjectNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/Displayable.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/EdgeNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorCell.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorItemVisitor.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/GeometryNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/InstallationNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/LandUseNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/LinearRingNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/OpeningNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/PolygonNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ReliefNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/Renderable.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/RenderableTreeCell.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TextNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TinNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TreeRequirement.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/VegetationNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/VertexNode.java create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/citydoctor_logo.ico create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/AboutDialog.fxml create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CheckDialog.fxml create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CreateRenderDataDialog.fxml create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/FilterPane.fxml create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/OpenFileDialog.fxml create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/WriteReportDialog.fxml create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/Add.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/BMBF_Logo.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/Beuth-Logo_basis.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/CityDoctor-Logo-rot_klein.jpg create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/CityDoctor-Logo-rot_klein.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/Culling.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/Delete.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/Delete3.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/Globe.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/about.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/autopro32x32.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/check32x32.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/citydoctor_logo.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/error_stat32x32.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/healing.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/hft_logo.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/icon_de.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/icon_en.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/lod1_32x32.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/lod2_32x32.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/lod3_32x32.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/lod4_32x32.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/openFolderIcon.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/save.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/scene.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/validation.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/wireframe32x32.png create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/statistics.fxml create mode 100644 CityDoctorParent/CityDoctorGUI/src/main/resources/log4j2.xml create mode 100644 CityDoctorParent/CityDoctorGUI/src/test/resources/SimpleSolid_SrefBS-GE-gml-LR-0001-T0001.gml create mode 100644 CityDoctorParent/CityDoctorGUI/src/test/resources/testConfigWithStreaming.yml diff --git a/CityDoctorParent/CityDoctorGUI/.gitignore b/CityDoctorParent/CityDoctorGUI/.gitignore new file mode 100644 index 0000000..b2e3a61 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/.gitignore @@ -0,0 +1,2 @@ +GUISettings.properties +/.gradle/ diff --git a/CityDoctorParent/CityDoctorGUI/checkForSolid.xml b/CityDoctorParent/CityDoctorGUI/checkForSolid.xml new file mode 100644 index 0000000..165be4b --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/checkForSolid.xml @@ -0,0 +1,14 @@ + + + + + + + + ||||SE_ATTRIBUTE_MISSING||any solid + + + ||||SE_ATTRIBUTE_MISSING||any solid + + + diff --git a/CityDoctorParent/CityDoctorGUI/pom.xml b/CityDoctorParent/CityDoctorGUI/pom.xml new file mode 100644 index 0000000..c9a35d4 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/pom.xml @@ -0,0 +1,190 @@ + + + 4.0.0 + + de.hft.stuttgart + CityDoctorParent + 3.14.1 + + + CityDoctorGUI + CityDoctorGUI + Graphical User Interface for CityDoctor + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + + + de.hft.stuttgart + CityDoctorModel + + + de.hft.stuttgart + CityDoctorValidation + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + junit + junit + test + + + org.apache.logging.log4j + log4j-slf4j18-impl + + + + + create-binaries + + jre-${jre-version-short}-full + ${win-jre} + ${win-jre}.jre + + + + + com.googlecode.maven-download-plugin + download-maven-plugin + 1.7.0 + + + downloadWindowsJre + install + + wget + + + https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-windows-amd64-full.zip + false + ${project.build.directory}/jre/jre-win + win-runtime.zip + + + + downloadLinuxJre + install + + wget + + + https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-linux-amd64-full.tar.gz + false + ${project.build.directory}/jre/jre-lin + lin-runtime.tar.gz + + + + downloadMacJre + install + + wget + + + https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-macos-amd64-full.zip + false + ${project.build.directory}/jre/jre-mac + mac-runtime.zip + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + unpack + install + + + + + + + + + run + + + + + + maven-assembly-plugin + + false + + + + create-archive-no-runtime + install + + single + + + ${project.artifactId}-${project.version}-no-runtime + + ${project.basedir}/src/assembly/no_runtime/assembly.xml + + + + + create-archive-win + install + + single + + + ${project.artifactId}-${project.version}-win + + ${project.basedir}/src/assembly/win/assembly.xml + + + + + create-archive-lin + install + + single + + + ${project.artifactId}-${project.version}-lin + + ${project.basedir}/src/assembly/lin/assembly.xml + + + + + create-archive-mac + install + + single + + + ${project.artifactId}-${project.version}-mac + + ${project.basedir}/src/assembly/mac/assembly.xml + + + + + + + + + + \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/assembly/common/checkForSolid.xml b/CityDoctorParent/CityDoctorGUI/src/assembly/common/checkForSolid.xml new file mode 100644 index 0000000..165be4b --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/assembly/common/checkForSolid.xml @@ -0,0 +1,14 @@ + + + + + + + + ||||SE_ATTRIBUTE_MISSING||any solid + + + ||||SE_ATTRIBUTE_MISSING||any solid + + + diff --git a/CityDoctorParent/CityDoctorGUI/src/assembly/lin/assembly.xml b/CityDoctorParent/CityDoctorGUI/src/assembly/lin/assembly.xml new file mode 100644 index 0000000..169bf5e --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/assembly/lin/assembly.xml @@ -0,0 +1,42 @@ + + zip + + zip + + false + + + app + + + + + ${project.basedir}/src/assembly/lin + / + + start.sh + + true + + + ${project.basedir}/src/assembly/common + / + + checkForSolid.xml + + false + + + ${project.build.directory}/jre/jre-lin/runtime/${lin-jre}/ + + /runtime + + **/* + + false + + + \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/assembly/lin/start.sh b/CityDoctorParent/CityDoctorGUI/src/assembly/lin/start.sh new file mode 100644 index 0000000..7066c34 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/assembly/lin/start.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./runtime/bin/java -classpath app/*:plugin/* de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/assembly/mac/assembly.xml b/CityDoctorParent/CityDoctorGUI/src/assembly/mac/assembly.xml new file mode 100644 index 0000000..59ea8a6 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/assembly/mac/assembly.xml @@ -0,0 +1,42 @@ + + zip + + zip + + false + + + app + + + + + ${project.basedir}/src/assembly/lin + / + + start.sh + + true + + + ${project.basedir}/src/assembly/common + / + + checkForSolid.xml + + false + + + ${project.build.directory}/jre/jre-mac/runtime/${mac-jre}/ + + /runtime + + **/* + + false + + + \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/assembly.xml b/CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/assembly.xml new file mode 100644 index 0000000..fd5f0a7 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/assembly.xml @@ -0,0 +1,34 @@ + + zip + + zip + + false + + + app + + + + + ${project.basedir}/src/assembly/no_runtime + / + + start.bat + start.sh + + true + + + ${project.basedir}/src/assembly/common + / + + checkForSolid.xml + + false + + + \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/start.bat b/CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/start.bat new file mode 100644 index 0000000..2da095d --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/start.bat @@ -0,0 +1,2 @@ +java -classpath app/*;plugins/* de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter +pause \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/start.sh b/CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/start.sh new file mode 100644 index 0000000..a8f20ec --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/assembly/no_runtime/start.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -classpath app/*:plugin/* de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/assembly/win/assembly.xml b/CityDoctorParent/CityDoctorGUI/src/assembly/win/assembly.xml new file mode 100644 index 0000000..8a74907 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/assembly/win/assembly.xml @@ -0,0 +1,42 @@ + + zip + + zip + + false + + + app + + + + + ${project.basedir}/src/assembly/win + / + + start.bat + + true + + + ${project.basedir}/src/assembly/common + / + + checkForSolid.xml + + false + + + ${project.build.directory}/jre/jre-win/runtime/${win-jre}/ + + /runtime + + **/* + + false + + + \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/assembly/win/start.bat b/CityDoctorParent/CityDoctorGUI/src/assembly/win/start.bat new file mode 100644 index 0000000..70d8a6a --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/assembly/win/start.bat @@ -0,0 +1,2 @@ +"runtime/bin/java.exe" -classpath app/*;plugins/* de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter +pause \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/AboutDialog.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/AboutDialog.java new file mode 100644 index 0000000..b45b342 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/AboutDialog.java @@ -0,0 +1,101 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.IOException; +import java.io.InputStream; + +import de.hft.stuttgart.citydoctor2.utils.Localization; +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.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.VBox; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.stage.Window; + +public class AboutDialog { + + @FXML + private ImageView logoView; + + @FXML + private ImageView beuthView; + + @FXML + private ImageView minisView; + + @FXML + private ImageView hftView; + + @FXML + private Button closeBtn; + + @FXML + private Label cdLabel; + + private Stage stage; + + public AboutDialog(Window parent) throws IOException { + FXMLLoader loader = new FXMLLoader(AboutDialog.class.getResource("AboutDialog.fxml")); + loader.setController(this); + VBox box = loader.load(); + + + stage = new Stage(StageStyle.UTILITY); + stage.setTitle(Localization.getText("AboutDialog.title")); + stage.setResizable(false); + Scene scene = new Scene(box); + stage.setScene(scene); + stage.initOwner(parent); + stage.initModality(Modality.APPLICATION_MODAL); + } + + public void initialize() { + String developedBy = Localization.getText("AboutDialog.developedBy"); + String contact = Localization.getText("AboutDialog.contact"); + closeBtn.setText(Localization.getText("AboutDialog.closeBtn")); + cdLabel.setText("CityDoctor Version " + Localization.getText(Localization.VERSION) + "\n\n" + + developedBy + ":\n" + + "Hochschule für Technik\n" + + "Beuth Hochschule Berlin\n\n" + + contact + ":\n" + + "Matthias Betz\n" + + "matthias.betz@hft-stuttgart.de"); + + + closeBtn.setOnAction(ae -> hide()); + try { + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/CityDoctor-Logo-rot_klein.png")) { + Image img = new Image(inStream); + logoView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/hft_logo.png")) { + Image img = new Image(inStream); + hftView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/Beuth-Logo_basis.png")) { + Image img = new Image(inStream); + beuthView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/BMBF_Logo.png")) { + Image img = new Image(inStream); + minisView.setImage(img); + } + } catch (IOException e) { + // ignore close exception + } + } + + public void show() { + stage.show(); + } + + public void hide() { + stage.hide(); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java new file mode 100644 index 0000000..9af4319 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckDialog.java @@ -0,0 +1,592 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import de.hft.stuttgart.citydoctor2.check.DefaultParameter; +import de.hft.stuttgart.citydoctor2.check.FilterConfiguration; +import de.hft.stuttgart.citydoctor2.check.GlobalParameters; +import de.hft.stuttgart.citydoctor2.check.Requirement; +import de.hft.stuttgart.citydoctor2.check.RequirementConfiguration; +import de.hft.stuttgart.citydoctor2.check.RequirementType; +import de.hft.stuttgart.citydoctor2.check.Unit; +import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; +import de.hft.stuttgart.citydoctor2.checks.Checks; +import de.hft.stuttgart.citydoctor2.gui.tree.TreeRequirement; +import de.hft.stuttgart.citydoctor2.utils.Localization; +import javafx.application.Platform; +import javafx.event.Event; +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.ProgressBar; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeTableColumn; +import javafx.scene.control.TreeTableView; +import javafx.scene.control.cell.CheckBoxTreeTableCell; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.control.cell.TextFieldTableCell; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.Window; +import javafx.util.StringConverter; +import javafx.util.converter.DefaultStringConverter; + +public class CheckDialog { + + private static final String NUMBER_OF_ROUNDING_PLACES = "numberOfRoundingPlaces"; + + private static final Logger logger = LogManager.getLogger(CheckDialog.class); + + private static final String CHECK_ENABLED = Localization.getText("CheckDialog.checkEnabled"); + private static final String NAME = Localization.getText("CheckDialog.checkName"); + private static final String VALUE = Localization.getText("CheckDialog.parameterValue"); + private static final String UNIT = Localization.getText("CheckDialog.parameterUnit"); + + private Stage stage; + + @FXML + private TreeTableView geometricTable; + + @FXML + private TreeTableView semanticTable; + + @FXML + private TableView globalParametersTable; + + @FXML + private ScrollPane scrollPane; + + @FXML + private Button cancelBtn; + + @FXML + private Button checkBtn; + + @FXML + private ProgressBar progress; + + @FXML + private Tab filterTab; + + @FXML + private TabPane tabPane; + + @FXML + private Button loadBtn; + + @FXML + private ImageView loadView; + + @FXML + private Button saveBtn; + + @FXML + private ImageView saveView; + + @FXML + private Button selectBtn; + + @FXML + private TextField schematronField; + + @FXML + private Tab checksTab; + + @FXML + private Label globalParametersLabel; + + @FXML + private Label availableChecksLabel; + + @FXML + private Label geometricChecksLabel; + + @FXML + private Label semanticChecksLabel; + + @FXML + private Label schematronFileLabel; + + private CityDoctorController controller; + private FilterPane filterPane; + private MainWindow window; + + public CheckDialog(MainWindow window, Window parent, CityDoctorController controller) throws IOException { + this.window = window; + this.controller = controller; + + FXMLLoader loader = new FXMLLoader(CheckDialog.class.getResource("CheckDialog.fxml")); + loader.setController(this); + VBox box = loader.load(); + + stage = new Stage(); + stage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("icons/CityDoctor-Logo-rot_klein.jpg"))); + Scene scene = new Scene(box); + stage.setScene(scene); + stage.initOwner(parent); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setTitle(Localization.getText("CheckDialog.title")); + } + + public void initialize() { + createEnableColumn(geometricTable); + createNameColumn(geometricTable); + createValueColumn(geometricTable); + createUnitColumn(geometricTable); + + createEnableColumn(semanticTable); + createNameColumn(semanticTable); + createValueColumn(semanticTable); + createUnitColumn(semanticTable); + + checksTab.setText(Localization.getText("CheckDialog.checksTab")); + filterTab.setText(Localization.getText("CheckDialog.filterTab")); + globalParametersLabel.setText(Localization.getText("CheckDialog.globalParametersLabel")); + availableChecksLabel.setText(Localization.getText("CheckDialog.availableChecksLabel")); + geometricChecksLabel.setText(Localization.getText("CheckDialog.geometricChecksLabel")); + semanticChecksLabel.setText(Localization.getText("CheckDialog.semanticChecksLabel")); + schematronFileLabel.setText(Localization.getText("CheckDialog.schematronFileLabel")); + selectBtn.setText(Localization.getText("CheckDialog.selectBtn")); + checkBtn.setText(Localization.getText("CheckDialog.checkBtn")); + cancelBtn.setText(Localization.getText("CheckDialog.cancelBtn")); + + List> columns = globalParametersTable.getColumns(); + TableColumn nameCol = new TableColumn<>(NAME); + columns.add(nameCol); + nameCol.setMinWidth(200); + nameCol.setPrefWidth(325); + nameCol.setCellValueFactory(new PropertyValueFactory<>("name")); + + setupValueColumns(columns); + + TableColumn unitCol = new TableColumn<>(UNIT); + columns.add(unitCol); + unitCol.setMinWidth(50); + unitCol.setPrefWidth(75); + unitCol.setMaxWidth(100); + unitCol.setEditable(false); + unitCol.setCellValueFactory(new PropertyValueFactory<>("unit")); + unitCol.setCellFactory(TextFieldTableCell.forTableColumn(new StringConverter() { + + @Override + public String toString(Unit object) { + return object.getRepresentation(); + } + + @Override + public Unit fromString(String string) { + return Unit.valueOf(string); + } + })); + + loadImages(); + + setupSelectButton(); + setupSaveButton(); + setupLoadButton(); + + populateTreeWithChecks(geometricTable, RequirementType.GEOMETRY, true); + populateTreeWithChecks(semanticTable, RequirementType.SEMANTIC, false); + + globalParametersTable.getItems().add(new GlobalParameter(NUMBER_OF_ROUNDING_PLACES, "8", Unit.NONE)); + globalParametersTable.getItems().add(new GlobalParameter("minVertexDistance", "0.0001", Unit.METER)); + + cancelBtn.setOnAction(ea -> stage.close()); + setupCheckButton(); + + tabPane.getSelectionModel().selectedIndexProperty().addListener((v, old, newV) -> { + if (newV.intValue() == 1 && filterPane == null) { + try { + getFilterPane(); + } catch (IOException e) { + window.showExceptionDialog(e); + } + } + }); + + schematronField.setText(ValidationConfiguration.CHECK_FOR_SOLID_XML); + } + + private void setupValueColumns(List> columns) { + TableColumn valueCol = new TableColumn<>(VALUE); + columns.add(valueCol); + valueCol.setMinWidth(150); + valueCol.setPrefWidth(200); + valueCol.setEditable(true); + valueCol.setCellValueFactory(new PropertyValueFactory<>("value")); + valueCol.setCellFactory(column -> new TableEditCell(new DefaultStringConverter()) { + @Override + public void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + if (!empty && item != null) { + GlobalParameter row = (GlobalParameter) getTableRow().getItem(); + if (row == null) { + return; + } + row.setValue(item); + if (NUMBER_OF_ROUNDING_PLACES.equals(row.getName())) { + setEditable(false); + } + } + } + }); + } + + private void setupSelectButton() { + selectBtn.setOnAction(ae -> { + FileChooser chooser = new FileChooser(); + List extensions = new ArrayList<>(); + extensions.add("*.xml"); + String schematronFiles = Localization.getText("CheckDialog.schematronFiles"); + chooser.getExtensionFilters().add(new ExtensionFilter(schematronFiles, extensions)); + File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, "")); + if (dir.exists() && dir.isDirectory()) { + chooser.setInitialDirectory(dir); + } + String schematronChooserTitle = Localization.getText("CheckDialog.schematronChooserTitle"); + chooser.setTitle(schematronChooserTitle); + File file = chooser.showOpenDialog(stage); + if (file != null) { + schematronField.setText(file.getAbsolutePath()); + } + }); + } + + private void setupLoadButton() { + loadBtn.setOnAction(ae -> { + FileChooser fc = new FileChooser(); + String validationConfiguration = Localization.getText("CheckDialog.validationConfiguration"); + fc.getExtensionFilters().add(new ExtensionFilter(validationConfiguration, "*.yml")); + File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, "")); + if (dir.exists() && dir.isDirectory()) { + fc.setInitialDirectory(dir); + } else { + Settings.set(Settings.LAST_OPEN_FOLDER, ""); + } + File f = fc.showOpenDialog(stage); + if (f != null) { + Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent()); + try { + ValidationConfiguration config = ValidationConfiguration.loadValidationConfig(f.getAbsolutePath()); + applyConfig(config); + } catch (IOException e) { + window.showExceptionDialog(e); + } + } + + }); + } + + private void setupSaveButton() { + saveBtn.setOnAction(ae -> { + ValidationConfiguration config = createConfig(); + FileChooser fc = new FileChooser(); + fc.getExtensionFilters() + .add(new ExtensionFilter(Localization.getText("CheckDialog.validationConfiguration"), "*.yml")); + File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, "")); + if (dir.exists() && dir.isDirectory()) { + fc.setInitialDirectory(dir); + } else { + Settings.set(Settings.LAST_OPEN_FOLDER, ""); + } + File f = fc.showSaveDialog(stage); + if (f != null) { + Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent()); + try { + config.saveAs(f); + } catch (IOException e) { + window.showExceptionDialog(e); + } + } + }); + } + + private void applyConfig(ValidationConfiguration config) throws IOException { + FilterConfiguration filter = config.getFilter(); + if (filter != null) { + getFilterPane().applyFilterConfig(filter); + } + Map checks = config.getRequirements(); + for (Entry e : checks.entrySet()) { + for (TreeItem ti : geometricTable.getRoot().getChildren()) { + applyCheckConfig(e, ti); + } + } + globalParametersTable.getItems().clear(); + globalParametersTable.getItems().add(new GlobalParameter(GlobalParameters.NUMBER_OF_ROUNDING_PLACES, + config.getNumberOfRoundingPlacesAsString(), Unit.NONE)); + globalParametersTable.getItems().add(new GlobalParameter(GlobalParameters.MIN_VERTEX_DISTANCE, + config.getMinVertexDistanceAsString(), Unit.METER)); + schematronField.setText(config.getSchematronFilePath()); + } + + private FilterPane getFilterPane() throws IOException { + if (filterPane == null) { + filterPane = new FilterPane(); + Platform.runLater(() -> filterTab.setContent(filterPane.getPane())); + } + return filterPane; + } + + private void applyCheckConfig(Entry e, TreeItem ti) { + Requirement r = ti.getValue().getRequirement(); + RequirementConfiguration cConfig = e.getValue(); + if (r.getId().equals(e.getKey())) { + ti.getValue().getEnabledProperty().set(cConfig.isEnabled()); + for (Entry paramEntry : cConfig.getParameters().entrySet()) { + String name = paramEntry.getKey(); + String value = paramEntry.getValue(); + for (TreeItem configItem : ti.getChildren()) { + TreeRequirement parameterCheck = configItem.getValue(); + if (name.equals(parameterCheck.getNameProperty().getValue())) { + parameterCheck.setValue(value); + } + } + } + } + } + + private boolean collectCheckConfiguration(ValidationConfiguration config) { + collectCheckInformationFromTree(config, geometricTable); + collectCheckInformationFromTree(config, semanticTable); + config.setSchematronFilePathInGlobalParameters(schematronField.getText()); + config.setMinVertexDistanceInGlobalParameters( + Double.parseDouble(globalParametersTable.getItems().get(1).getValue())); + // check if everything is okay with the configuration + config.validateConfiguration(); + return true; + } + + private void collectCheckInformationFromTree(ValidationConfiguration config, TreeTableView table) { + for (TreeItem ti : table.getRoot().getChildren()) { + Requirement r = ti.getValue().getRequirement(); + if (ti.getValue().getEnabledProperty().getValue().booleanValue()) { + RequirementConfiguration cc = config.getRequirements().get(r.getId()); + cc.setEnabled(ti.getValue().isEnabled()); + // collect parameters + collectParameters(cc, ti); + } else { + config.getRequirements().get(r.getId()).setEnabled(false); + } + } + } + + private void loadImages() { + try { + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/openFolderIcon.png")) { + Image img = new Image(inStream); + loadView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/save.png")) { + Image img = new Image(inStream); + saveView.setImage(img); + } + } catch (IOException e) { + // ignore close exception + } + } + + private void setupCheckButton() { + checkBtn.setOnAction(ea -> { + ValidationConfiguration config = createConfig(); + checkBtn.setDisable(true); + cancelBtn.setDisable(true); + stage.setOnCloseRequest(Event::consume); + Thread t = new Thread(() -> { + try { + if (logger.isInfoEnabled()) { + String startingChecks = Localization.getText("CheckDialog.startingChecks"); + logger.info(startingChecks); + } + controller.startChecks(config, progress::setProgress); + if (logger.isInfoEnabled()) { + String checksDone = Localization.getText("CheckDialog.checksDone"); + logger.info(checksDone); + } + } catch (Exception e) { + if (logger.isErrorEnabled()) { + String failedChecks = Localization.getText("CheckDialog.failedChecks"); + logger.error(failedChecks, e); + } + Platform.runLater(() -> { + ExceptionDialog dialog = new ExceptionDialog(); + dialog.show(e); + }); + } finally { + Platform.runLater(() -> { + checkBtn.setDisable(false); + cancelBtn.setDisable(false); + stage.setOnCloseRequest(null); + stage.close(); + }); + } + }); + t.start(); + }); + } + + private ValidationConfiguration createConfig() { + ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); + config.setMinVertexDistanceInGlobalParameters( + Double.parseDouble(globalParametersTable.getItems().get(1).getValue())); + if (filterPane != null) { + config.setFilter(filterPane.getConfiguration()); + } + collectCheckConfiguration(config); + if (schematronField.getText().isEmpty()) { + return config; + } + File f = new File(schematronField.getText()); + if (!f.exists() || f.isDirectory() && logger.isWarnEnabled()) { + String schematronFileNotExisting = Localization.getText("CheckDialog.schematronFileNotExisting"); + logger.warn(schematronFileNotExisting); + } else { + config.setSchematronFilePathInGlobalParameters(schematronField.getText()); + } + return config; + } + + private void collectParameters(RequirementConfiguration cc, TreeItem ti) { + Map parameters = new HashMap<>(); + for (TreeItem paramTi : ti.getChildren()) { + String name = paramTi.getValue().getNameProperty().getValue(); + String value = paramTi.getValue().getValueProperty().getValue(); + parameters.put(name, value); + } + cc.setParameters(parameters); + } + + private void createEnableColumn(TreeTableView view) { + TreeTableColumn enableColumn = new TreeTableColumn<>(CHECK_ENABLED); + view.getColumns().add(enableColumn); + enableColumn.setCellValueFactory(param -> { + TreeRequirement check = param.getValue().getValue(); + if (check != null) { + return check.getEnabledProperty(); + } + return null; + }); + + StringConverter converter = new StringConverter() { + + @Override + public String toString(Boolean object) { + return " "; + } + + @Override + public Boolean fromString(String string) { + return Boolean.parseBoolean(string); + } + }; + + enableColumn.setCellFactory(list -> new CheckBoxTreeTableCell(null, converter) { + + @Override + public void updateItem(Boolean item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null) { + setGraphic(null); + setText(null); + } + } + }); + } + + private void createNameColumn(TreeTableView view) { + TreeTableColumn nameColumn = new TreeTableColumn<>(NAME); + nameColumn.setEditable(false); + view.getColumns().add(nameColumn); + nameColumn.setCellValueFactory(param -> { + if (param.getValue().getValue() != null) { + return param.getValue().getValue().getNameProperty(); + } + return null; + }); + } + + private void createValueColumn(TreeTableView view) { + TreeTableColumn valueColumn = new TreeTableColumn<>(VALUE); + view.getColumns().add(valueColumn); + valueColumn.setCellValueFactory(param -> { + if (param.getValue().getValue() != null) { + return param.getValue().getValue().getValueProperty(); + } + return null; + }); + valueColumn.setCellFactory(col -> new TreeEditCell<>(new DefaultStringConverter())); + valueColumn.setPrefWidth(100); + } + + private void createUnitColumn(TreeTableView view) { + TreeTableColumn unitColumn = new TreeTableColumn<>(UNIT); + unitColumn.setEditable(false); + view.getColumns().add(unitColumn); + unitColumn.setCellValueFactory(param -> { + if (param.getValue().getValue() != null) { + return param.getValue().getValue().getUnitProperty(); + } + return null; + }); + } + + private void populateTreeWithChecks(TreeTableView view, RequirementType type, boolean enabled) { + view.setRoot(new TreeItem<>()); + List requirements = new ArrayList<>(Checks.getAvailableRequirements().values()); + Collections.sort(requirements, (r1, r2) -> r1.getId().compareTo(r2.getId())); + for (Requirement r : requirements) { + if (r.getType() != type) { + continue; + } + TreeRequirement treeCheck = new TreeRequirement(r); + treeCheck.setEnabled(enabled); + TreeItem ti = new TreeItem<>(treeCheck); + view.getRoot().getChildren().add(ti); + for (DefaultParameter dp : r.getDefaultParameter()) { + TreeItem dpTi = new TreeItem<>(new TreeRequirement(dp)); + ti.getChildren().add(dpTi); + } + ti.setExpanded(true); + } + } + + public void show() { + Platform.runLater(() -> { + if (controller.getCurrentConfig() != null) { + for (GlobalParameter parameter : globalParametersTable.getItems()) { + if (parameter.getName().equals(NUMBER_OF_ROUNDING_PLACES)) { + parameter.setValue("" + controller.getCurrentConfig().getNumberOfRoundingPlaces()); + } + } + } + globalParametersTable.refresh(); + progress.setProgress(0d); + stage.showAndWait(); + }); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckStatus.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckStatus.java new file mode 100644 index 0000000..615a77d --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CheckStatus.java @@ -0,0 +1,7 @@ +package de.hft.stuttgart.citydoctor2.gui; + +public enum CheckStatus { + + ERROR, OK, NOT_CHECKED + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java new file mode 100644 index 0000000..fd85e67 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -0,0 +1,1086 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.StringJoiner; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +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 de.hft.stuttgart.citydoctor2.check.CheckError; +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.datastructure.BoundarySurface; +import de.hft.stuttgart.citydoctor2.datastructure.BridgeConstructiveElement; +import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject; +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.datastructure.Installation; +import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; +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.Geometry; +import de.hft.stuttgart.citydoctor2.datastructure.LandObject; +import de.hft.stuttgart.citydoctor2.datastructure.Opening; +import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject; +import de.hft.stuttgart.citydoctor2.datastructure.TinObject; +import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject; +import de.hft.stuttgart.citydoctor2.datastructure.Vegetation; +import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; +import de.hft.stuttgart.citydoctor2.gui.table.ErrorStat; +import de.hft.stuttgart.citydoctor2.gui.tree.AllBoundarySurfacesNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllBridgeConstructiveElementsNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllBridgePartsNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllBridgesNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllInstallationsNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllBuildingPartsNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllBuildingsNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllOpeningsNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllTerrainNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllTinNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllTransportationNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllVegetationNode; +import de.hft.stuttgart.citydoctor2.gui.tree.AllWaterNode; +import de.hft.stuttgart.citydoctor2.gui.tree.BoundarySurfaceNode; +import de.hft.stuttgart.citydoctor2.gui.tree.BridgeConstructiveElementNode; +import de.hft.stuttgart.citydoctor2.gui.tree.BridgeNode; +import de.hft.stuttgart.citydoctor2.gui.tree.InstallationNode; +import de.hft.stuttgart.citydoctor2.gui.tree.BuildingNode; +import de.hft.stuttgart.citydoctor2.gui.tree.BuildingPartNode; +import de.hft.stuttgart.citydoctor2.gui.tree.ButtonRenderable; +import de.hft.stuttgart.citydoctor2.gui.tree.CityObjectNode; +import de.hft.stuttgart.citydoctor2.gui.tree.GeometryNode; +import de.hft.stuttgart.citydoctor2.gui.tree.LandUseNode; +import de.hft.stuttgart.citydoctor2.gui.tree.OpeningNode; +import de.hft.stuttgart.citydoctor2.gui.tree.ReliefNode; +import de.hft.stuttgart.citydoctor2.gui.tree.Renderable; +import de.hft.stuttgart.citydoctor2.gui.tree.TinNode; +import de.hft.stuttgart.citydoctor2.gui.tree.VegetationNode; +import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; +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 de.hft.stuttgart.citydoctor2.utils.Localization; +import javafx.application.Platform; +import javafx.scene.chart.XYChart.Data; +import javafx.scene.chart.XYChart.Series; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Dialog; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; + +public class CityDoctorController { + + private static final int MAX_FEATURES_PER_CHUNK = 200; + + private static final Logger logger = LogManager.getLogger(CityDoctorController.class); + + private MainWindow mainWindow; + + private CityDoctorModel model; + private ParserConfiguration currentConfig; + private String sourceFile; + + private Checker currentChecker; + + private HighlightController highlightController; + private Renderer renderer; + + private AtomicInteger buildingChunkNr = new AtomicInteger(0); + private AtomicInteger vegetationChunkNr = new AtomicInteger(0); + private AtomicInteger transportationChunkNr = new AtomicInteger(0); + private AtomicInteger bridgeChunkNr = new AtomicInteger(0); + private AtomicInteger waterChunkNr = new AtomicInteger(0); + private AtomicInteger landChunkNr = new AtomicInteger(0); + + public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) { + this.mainWindow = mainWindow; + this.highlightController = highlightController; + this.renderer = renderer; + } + + public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation) + throws CityGmlParseException, InvalidGmlFileException { + loadCityGml(path, numberOfRoundingPlaces, l, useValidation, false); + } + + public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation, + boolean lowMemory) throws CityGmlParseException, InvalidGmlFileException { + try { + Platform.runLater(() -> { + mainWindow.getOpenBtn().setDisable(true); + mainWindow.getWriteReportButton().setDisable(true); + mainWindow.getMeshGroup().getChildren().clear(); + + clearTrees(); + mainWindow.resetSearchBar(); + mainWindow.resetFilterComboBox(); + }); + currentChecker = null; + currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory); + GMLValidationHandler handler = null; + List validationIssues = new ArrayList<>(); + if (useValidation) { + handler = new GMLValidationHandler() { + @Override + public void error(SAXParseException exception) { + if (exception.getLineNumber() >= 0) { + String s = "In line " + exception.getLineNumber() + ":"; + validationIssues.add(s); + } + validationIssues.add(exception.getMessage()); + } + + @Override + public void warning(SAXParseException exception) { + error(exception); + } + + @Override + public void fatalError(SAXParseException exception) { + error(exception); + } + }; + } + model = CityGmlParser.parseCityGmlFile(path, currentConfig, l, handler); + if (!validationIssues.isEmpty()) { + StringJoiner sj = new StringJoiner("\n"); + validationIssues.stream().forEach(sj::add); + throw new InvalidGmlFileException(sj.toString()); + } + mainWindow.getClickHandler().setConfig(currentConfig); + sourceFile = path; + renderer.reset(); + Platform.runLater(() -> { + mainWindow.addFileNameToTitle(path); + 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); + buildTrees(); + }); + } finally { + Platform.runLater(() -> mainWindow.getOpenBtn().setDisable(false)); + } + } + + public void buildTrees() { + resetFeatureChunks(); + buildBuildings(model); + buildVegetation(model.getVegetation()); + buildTransportation(model.getTransportation()); + buildBridges(model); + buildWater(model); + buildLand(model); + } + + private void resetFeatureChunks() { + buildingChunkNr.set(0); + vegetationChunkNr.set(0); + transportationChunkNr.set(0); + bridgeChunkNr.set(0); + waterChunkNr.set(0); + landChunkNr.set(0); + } + + private void buildLand(CityDoctorModel model) { + if (model.getLand().isEmpty()) { + return; + } + TreeView landView = mainWindow.getTerrainView(); + TreeItem landRoot = new TreeItem<>(new AllTerrainNode(model.getLand())); + landRoot.setExpanded(true); + landView.setRoot(landRoot); + buildLandFromList(model.getLand(), landView.getRoot()); + addMoreButtonIfNecessary(model.getLand(), landView, landRoot, landChunkNr); + } + + private void buildLandFromList(List list, 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); + Renderable node; + if (land instanceof ReliefObject relief) { + node = new ReliefNode(relief); + } else if (land instanceof TinObject tin) { + node = new TinNode(tin); + } else { + node = new LandUseNode((LandObject) land); + } + TreeItem item = new TreeItem<>(node); + item.setExpanded(true); + root.getChildren().add(item); + createGeometryNodes(land, item); + if (land instanceof ReliefObject relief) { + createTinNodes(relief, item); + } + } + } + + private void createTinNodes(ReliefObject relief, TreeItem item) { + if (relief.getComponents().isEmpty()) { + return; + } + AllTinNode allTinText = new AllTinNode(relief.getComponents()); + TreeItem allBpsTextItem = new TreeItem<>(allTinText); + item.getChildren().add(allBpsTextItem); + + for (TinObject tin : relief.getComponents()) { + TinNode tinNode = new TinNode(tin); + TreeItem tinNodeItem = new TreeItem<>(tinNode); + tinNodeItem.setExpanded(true); + allBpsTextItem.getChildren().add(tinNodeItem); + createGeometryNodes(tin, tinNodeItem); + } + } + + private void buildWater(CityDoctorModel model) { + if (model.getWater().isEmpty()) { + return; + } + TreeView waterView = mainWindow.getWaterView(); + TreeItem waterRoot = new TreeItem<>(new AllWaterNode(model.getWater())); + waterRoot.setExpanded(true); + waterView.setRoot(waterRoot); + buildTreeFromList(model.getWater(), waterView.getRoot(), waterChunkNr); + addMoreButtonIfNecessary(model.getWater(), waterView, waterRoot, waterChunkNr); + } + + private void buildBridges(CityDoctorModel model) { + if (model.getBridges().isEmpty()) { + return; + } + TreeView bridgeView = mainWindow.getBridgeView(); + TreeItem bridgeRoot = new TreeItem<>(new AllBridgesNode(model.getBridges())); + bridgeRoot.setExpanded(true); + bridgeView.setRoot(bridgeRoot); + buildBridgeTreeFromList(model.getBridges(), bridgeView.getRoot()); + addMoreButtonIfNecessary(model.getBridges(), bridgeView, bridgeRoot, bridgeChunkNr); + } + + private void buildTransportation(List trans) { + if (trans.isEmpty()) { + return; + } + TreeView transView = mainWindow.getTransportationView(); + TreeItem transRoot = new TreeItem<>(new AllTransportationNode(model.getTransportation())); + transRoot.setExpanded(true); + transView.setRoot(transRoot); + buildTreeFromList(trans, transView.getRoot(), transportationChunkNr); + addMoreButtonIfNecessary(trans, transView, transRoot, transportationChunkNr); + } + + private void buildVegetation(List veg) { + if (veg.isEmpty()) { + return; + } + TreeView vegetationsView = mainWindow.getVegetationView(); + TreeItem vegRoot = new TreeItem<>(new AllVegetationNode(model.getVegetation())); + vegRoot.setExpanded(true); + vegetationsView.setRoot(vegRoot); + buildTreeFromListWithProducer(veg, vegetationsView.getRoot(), vegetationChunkNr, VegetationNode::new); + addMoreButtonIfNecessary(veg, vegetationsView, vegRoot, vegetationChunkNr); + } + + private void addMoreButtonIfNecessary(List list, TreeView view, + TreeItem root, AtomicInteger chunkCounter) { + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) { + Runnable run = new Runnable() { + 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.incrementAndGet(); + int visibleFeatures = countVisibleNodes(view); + buildTreeFromList(list, root, chunkCounter); + updateTree(root); + if (selectedIndex >= 0) { + view.getSelectionModel().select(selectedIndex); + } + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) { + root.getChildren().add(new TreeItem<>(new ButtonRenderable(this))); + } + view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1); + } + }; + // add button for expansion + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + private void buildBuildings(CityDoctorModel model) { + if (model.getBuildings().isEmpty()) { + return; + } + TreeView buildingsView = mainWindow.getBuildingsView(); + TreeItem root = new TreeItem<>(new AllBuildingsNode(model.getBuildings())); + root.setExpanded(true); + buildBuildingTreeFromList(model.getBuildings(), root); + addMoreButtonToBuildingsIfNecessary(model.getBuildings(), buildingsView, root, buildingChunkNr); + buildingsView.setRoot(root); + } + + private void addMoreButtonToBuildingsIfNecessary(List list, TreeView view, + TreeItem root, AtomicInteger chunkCounter) { + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) { + Runnable run = new Runnable() { + 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); + buildBuildingTreeFromList(list, root); + updateTree(root); + if (selectedIndex >= 0) { + view.getSelectionModel().select(selectedIndex); + } + if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) { + root.getChildren().add(new TreeItem<>(new ButtonRenderable(this))); + } + view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1); + } + }; + // add button for expansion + root.getChildren().add(new TreeItem<>(new ButtonRenderable(run))); + } + } + + 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; + } + + private void buildTreeFromList(List cos, 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); + 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, + AtomicInteger chunkCounter, Function nodeProducer) { + int chunk = chunkCounter.get(); + for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) { + T co = cos.get(i); + Renderable node = nodeProducer.apply(co); + TreeItem nodeItem = new TreeItem<>(node); + nodeItem.setExpanded(true); + root.getChildren().add(nodeItem); + createGeometryNodes(co, nodeItem); + } + } + + private void clearTrees() { + mainWindow.getBuildingsView().setRoot(null); + mainWindow.getVegetationView().setRoot(null); + mainWindow.getTransportationView().setRoot(null); + mainWindow.getBridgeView().setRoot(null); + mainWindow.getWaterView().setRoot(null); + mainWindow.getTerrainView().setRoot(null); + mainWindow.getErrorTree().getRoot().getChildren().clear(); + mainWindow.getGlobalErrorsView().getItems().clear(); + clearGeometryTrees(); + } + + private void clearGeometryTrees() { + highlightController.clearHighlights(); + mainWindow.getPolygonsView().getRoot().getChildren().clear(); + mainWindow.getEdgeView().getRoot().getChildren().clear(); + mainWindow.getVertexView().getRoot().getChildren().clear(); + } + + private void buildBuildingTreeFromList(List list, 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); + BuildingNode node = new BuildingNode(b); + TreeItem item = new TreeItem<>(node); + item.setExpanded(true); + root.getChildren().add(item); + createGeometryNodes(b, item); + createBoundarySurfaceNodes(b.getBoundarySurfaces(), item); + createBuildingInstallationNodes(b, item); + createBuildingPartNodes(b, item); + } + } + + private void buildBridgeTreeFromList(List list, 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); + BridgeNode node = new BridgeNode(bridge); + TreeItem item = new TreeItem<>(node); + item.setExpanded(true); + root.getChildren().add(item); + createGeometryNodes(bridge, item); + createBoundarySurfaceNodes(bridge.getBoundarySurfaces(), item); + createBridgeInstallationNodes(bridge, item); + createConstructiveElementsNodes(bridge, item); + createBridgePartNodes(bridge, item); + } + + } + + private void createBridgeInstallationNodes(BridgeObject bridge, TreeItem root) { + createInstallationNodes(bridge.getBridgeInstallations(), root); + } + + private void createConstructiveElementsNodes(BridgeObject bridge, TreeItem item) { + if (bridge.getConstructiveElements().isEmpty()) { + return; + } + AllBridgeConstructiveElementsNode allBceNode = new AllBridgeConstructiveElementsNode( + bridge.getConstructiveElements()); + TreeItem allBceNodeTextItem = new TreeItem<>(allBceNode); + item.getChildren().add(allBceNodeTextItem); + + for (BridgeConstructiveElement bce : bridge.getConstructiveElements()) { + BridgeConstructiveElementNode bceNode = new BridgeConstructiveElementNode(bce); + TreeItem bpNodeItem = new TreeItem<>(bceNode); + bpNodeItem.setExpanded(true); + allBceNodeTextItem.getChildren().add(bpNodeItem); + createGeometryNodes(bce, bpNodeItem); + createBoundarySurfaceNodes(bce.getBoundarySurfaces(), bpNodeItem); + } + } + + private void createBridgePartNodes(BridgeObject bridge, TreeItem item) { + if (bridge.getParts().isEmpty()) { + return; + } + AllBridgePartsNode allBpsText = new AllBridgePartsNode(bridge.getParts()); + TreeItem allBpsTextItem = new TreeItem<>(allBpsText); + item.getChildren().add(allBpsTextItem); + + for (BridgeObject bp : bridge.getParts()) { + BridgeNode bpNode = new BridgeNode(bp); + TreeItem bpNodeItem = new TreeItem<>(bpNode); + bpNodeItem.setExpanded(true); + allBpsTextItem.getChildren().add(bpNodeItem); + createGeometryNodes(bp, bpNodeItem); + createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem); + } + } + + private void createBuildingInstallationNodes(Building ab, TreeItem root) { + createInstallationNodes(ab.getBuildingInstallations(), root); + } + + private void createInstallationNodes(List installations, TreeItem root) { + if (installations.isEmpty()) { + return; + } + + AllInstallationsNode allBiNode = new AllInstallationsNode(installations); + TreeItem allBpsTextItem = new TreeItem<>(allBiNode); + root.getChildren().add(allBpsTextItem); + for (Installation bi : installations) { + InstallationNode biNode = new InstallationNode(bi); + TreeItem biItem = new TreeItem<>(biNode); + biItem.setExpanded(true); + allBpsTextItem.getChildren().add(biItem); + createGeometryNodes(bi, biItem); + createBoundarySurfaceNodes(bi.getBoundarySurfaces(), biItem); + } + } + + private void createBoundarySurfaceNodes(List bsList, TreeItem root) { + if (bsList.isEmpty()) { + return; + } + AllBoundarySurfacesNode allBsText = new AllBoundarySurfacesNode(bsList); + TreeItem allBpsTextItem = new TreeItem<>(allBsText); + root.getChildren().add(allBpsTextItem); + + allBpsTextItem.getChildren().add(new TreeItem<>()); + + allBpsTextItem.expandedProperty().addListener((obs, old, newV) -> { + if (Boolean.TRUE.equals(newV) && allBpsTextItem.getChildren().size() == 1) { + Platform.runLater(() -> { + allBpsTextItem.getChildren().clear(); + for (BoundarySurface bs : bsList) { + BoundarySurfaceNode bsText = new BoundarySurfaceNode(bs); + TreeItem bsTextItem = new TreeItem<>(bsText); + bsTextItem.setExpanded(true); + allBpsTextItem.getChildren().add(bsTextItem); + createGeometryNodes(bs, bsTextItem); + createOpeningNodes(bs.getOpenings(), bsTextItem); + } + updateTree(allBpsTextItem); + }); + } + }); + } + + private void createOpeningNodes(List openings, TreeItem root) { + if (openings == null || openings.isEmpty()) { + return; + } + AllOpeningsNode allOpeningsNode = new AllOpeningsNode(openings); + TreeItem allOpeningsItem = new TreeItem<>(allOpeningsNode); + for (Opening o : openings) { + OpeningNode openingNode = new OpeningNode(o); + TreeItem openingsItem = new TreeItem<>(openingNode); + allOpeningsItem.getChildren().add(openingsItem); + } + root.getChildren().add(allOpeningsItem); + } + + private void createBuildingPartNodes(Building ab, TreeItem item) { + if (ab.getBuildingParts().isEmpty()) { + return; + } + AllBuildingPartsNode allBpsText = new AllBuildingPartsNode(ab.getBuildingParts()); + TreeItem allBpsTextItem = new TreeItem<>(allBpsText); + item.getChildren().add(allBpsTextItem); + + for (BuildingPart bp : ab.getBuildingParts()) { + BuildingPartNode bpNode = new BuildingPartNode(bp); + TreeItem bpNodeItem = new TreeItem<>(bpNode); + bpNodeItem.setExpanded(true); + allBpsTextItem.getChildren().add(bpNodeItem); + createGeometryNodes(bp, bpNodeItem); + createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem); + } + } + + private void createGeometryNodes(CityObject co, TreeItem item) { + for (Geometry geom : co.getGeometries()) { + GeometryNode geomNode = new GeometryNode(geom); + TreeItem geomItem = new TreeItem<>(geomNode); + item.getChildren().add(geomItem); + } + } + + public void startChecks(ValidationConfiguration config, ProgressListener l) { + if (model == null) { + logger.warn(Localization.getText("CityDoctorController.noDatamodel")); + return; + } + if (sourceFile == null) { + logger.warn(Localization.getText("CityDoctorController.noSourceFile")); + return; + } + try { + Platform.runLater(() -> { + mainWindow.getCheckButton().setDisable(true); + mainWindow.getErrorTree().getRoot().getChildren().clear(); + mainWindow.getGlobalErrorsView().getItems().clear(); + }); + config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces()); + config.setParserConfig(currentConfig); + currentChecker = new Checker(config, model); + currentChecker.runChecks(l); + + Platform.runLater(() -> { + // apply check results to tree views + updateFeatureTrees(); + updateTree(mainWindow.getPolygonsView().getRoot()); + for (CheckError e : model.getGlobalErrors()) { + if (e instanceof SchematronError se) { + mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString()); + } + } + renderer.refresh(); + mainWindow.getWriteReportButton().setDisable(false); + }); + } finally { + Platform.runLater(() -> mainWindow.getCheckButton().setDisable(false)); + } + } + + void updateFeatureTrees() { + updateTree(mainWindow.getBuildingsView().getRoot()); + updateTree(mainWindow.getVegetationView().getRoot()); + updateTree(mainWindow.getBridgeView().getRoot()); + updateTree(mainWindow.getTerrainView().getRoot()); + updateTree(mainWindow.getTransportationView().getRoot()); + updateTree(mainWindow.getWaterView().getRoot()); + renderer.updateErrors(); + } + + private void updateTree(TreeItem root) { + if (root == null) { + return; + } + for (TreeItem item : root.getChildren()) { + updateItem(item); + } + } + + private void updateItem(TreeItem item) { + if (item.getValue() == null) { + return; + } + item.getValue().refreshTextColor(); + for (TreeItem child : item.getChildren()) { + updateItem(child); + } + } + + public void writePdfReport(File pdfFile) { + if (currentChecker == null) { + return; + } + currentChecker.writePdfReport(pdfFile.getAbsolutePath()); + } + + public void writeXmlReport(File xmlFile) { + if (currentChecker == null) { + return; + } + currentChecker.writeXmlReport(xmlFile.getAbsolutePath()); + } + + private Map getErrorStats() { + Map stats = new HashMap<>(); + addErrorStats(stats, model.getBridges()); + addErrorStats(stats, model.getBuildings()); + addErrorStats(stats, model.getLand()); + addErrorStats(stats, model.getTransportation()); + addErrorStats(stats, model.getVegetation()); + addErrorStats(stats, model.getWater()); + return stats; + } + + private void addErrorStats(Map stats, List cos) { + List errors = new ArrayList<>(); + for (CityObject co : cos) { + co.collectContainedErrors(errors); + } + Set filteredErrors = new HashSet<>(errors); + for (CheckError error : filteredErrors) { + ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new); + stat.incrementCount(); + } + } + + public void export(Building b) { + if (model == null) { + return; + } + CityDoctorModel newModel = new CityDoctorModel(model.getParserConfig(), model.getFile()); + newModel.setCityModel(new CityModel()); + newModel.addBuilding(b); + + FileChooser fc = new FileChooser(); + fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml")); + fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off")); + File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, "")); + if (dir.exists() && dir.isDirectory()) { + fc.setInitialDirectory(dir); + } else { + Settings.set(Settings.LAST_OPEN_FOLDER, ""); + } + File f = fc.showSaveDialog(mainWindow.getMainStage()); + if (f != null) { + Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent()); + try { + + newModel.saveAs(f.getAbsolutePath(), true); + } catch (CityDoctorWriteException e) { + logger.error(e); + } + } + } + + public void showWorld() { + if (model != null) { + renderer.render(model); + } + } + + public void storeModel(File f, boolean saveQualityAde) throws CityDoctorWriteException { + if (model == null) { + return; + } + model.saveAs(f.getAbsolutePath(), saveQualityAde); + } + + public void searchFeature(String searchString, FeatureType selectedTab) { + if (model == null || searchString == null || searchString.trim().isEmpty()) { + return; + } + mainWindow.unselectEverything(); + resetFeatureChunks(); + if (selectedTab == FeatureType.BUILDING) { + List foundBuildings = filterFeatures(searchString, model.getBuildings()); + TreeView buildingsView = mainWindow.getBuildingsView(); + TreeItem root = buildingsView.getRoot(); + root.getChildren().clear(); + buildBuildingTreeFromList(foundBuildings, root); + updateTree(root); + addMoreButtonToBuildingsIfNecessary(foundBuildings, buildingsView, root, buildingChunkNr); + } else { + TreeView view; + List cos; + AtomicInteger chunkCounter; + switch (selectedTab) { + case VEGETATION: + view = mainWindow.getVegetationView(); + cos = filterFeatures(searchString, model.getVegetation()); + chunkCounter = vegetationChunkNr; + break; + case BRIDGE: + view = mainWindow.getBridgeView(); + cos = filterFeatures(searchString, model.getBridges()); + chunkCounter = bridgeChunkNr; + break; + case TRANSPORTATION: + view = mainWindow.getTransportationView(); + cos = filterFeatures(searchString, model.getTransportation()); + chunkCounter = transportationChunkNr; + break; + case WATER: + view = mainWindow.getWaterView(); + cos = filterFeatures(searchString, model.getWater()); + chunkCounter = waterChunkNr; + break; + case LAND: + view = mainWindow.getTerrainView(); + cos = filterFeatures(searchString, model.getLand()); + chunkCounter = landChunkNr; + break; + default: + throw new IllegalStateException("Unknown selected feature tab"); + } + TreeItem root = view.getRoot(); + root.getChildren().clear(); + buildTreeFromList(cos, root, chunkCounter); + updateTree(root); + addMoreButtonIfNecessary(cos, view, root, chunkCounter); + } + } + + private List filterFeatures(String searchString, List features) { + List foundFeatures = new ArrayList<>(); + for (T t : features) { + if (t.getGmlId().getGmlString().contains(searchString)) { + foundFeatures.add(t); + } + } + return foundFeatures; + } + + public void resetSearch(FeatureType selectedTab) { + if (model == null) { + return; + } + resetFeatureChunks(); + if (selectedTab == FeatureType.BUILDING) { + // buildings are handled differently, because of surface and building + // installations + TreeItem root = mainWindow.getBuildingsView().getRoot(); + if (root != null) { + root.getChildren().clear(); + buildBuildingTreeFromList(model.getBuildings(), root); + updateTree(root); + addMoreButtonToBuildingsIfNecessary(model.getBuildings(), mainWindow.getBuildingsView(), root, + buildingChunkNr); + } + } else { + TreeView view; + List cos; + AtomicInteger chunkCounter; + switch (selectedTab) { + case VEGETATION: + view = mainWindow.getVegetationView(); + cos = model.getVegetation(); + chunkCounter = vegetationChunkNr; + break; + case BRIDGE: + view = mainWindow.getBridgeView(); + cos = model.getBridges(); + chunkCounter = bridgeChunkNr; + break; + case TRANSPORTATION: + view = mainWindow.getTransportationView(); + cos = model.getTransportation(); + chunkCounter = transportationChunkNr; + break; + case WATER: + view = mainWindow.getWaterView(); + cos = model.getWater(); + chunkCounter = waterChunkNr; + break; + case LAND: + view = mainWindow.getTerrainView(); + cos = model.getLand(); + chunkCounter = landChunkNr; + break; + default: + throw new IllegalStateException("Unknown selected feature tab"); + } + TreeItem root = view.getRoot(); + root.getChildren().clear(); + buildTreeFromList(cos, root, chunkCounter); + updateTree(root); + addMoreButtonIfNecessary(cos, view, root, chunkCounter); + } + } + + public Series createErrorSeries() { + Series series = new Series<>(); + if (model == null) { + return series; + } + Map errorStats = getErrorStats(); + for (ErrorStat es : errorStats.values()) { + series.getData().add(new Data<>(es.getErrorId().toString(), es.getCount())); + } + return series; + } + + public String getFileName() { + return model.getFileName(); + } + + public void fillTreeViewWithErrorBuildings() { + TreeView buildingsView = mainWindow.getBuildingsView(); + TreeItem root = buildingsView.getRoot(); + if (model == null) { + return; + } + if (root == null) { + return; + } + mainWindow.resetSearchBar(); + root.getChildren().clear(); + List errorBuildings = new ArrayList<>(); + for (Building b : model.getBuildings()) { + if (b.containsAnyError()) { + errorBuildings.add(b); + } + } + resetFeatureChunks(); + buildBuildingTreeFromList(errorBuildings, root); + updateTree(root); + addMoreButtonToBuildingsIfNecessary(errorBuildings, buildingsView, root, buildingChunkNr); + } + + public void fillTreeViewWithErrorVegetation() { + if (model == null) { + return; + } + fillTreeViewWithErrorCityObjects(mainWindow.getVegetationView(), model.getVegetation(), vegetationChunkNr); + } + + public void fillTreeViewWithErrorBridges() { + if (model == null) { + return; + } + fillTreeViewWithErrorCityObjects(mainWindow.getBridgeView(), model.getBridges(), bridgeChunkNr); + } + + public void fillTreeViewWithErrorLand() { + if (model == null) { + return; + } + fillTreeViewWithErrorCityObjects(mainWindow.getTerrainView(), model.getLand(), landChunkNr); + } + + public void fillTreeViewWithErrorTransportation() { + if (model == null) { + return; + } + fillTreeViewWithErrorCityObjects(mainWindow.getTransportationView(), model.getTransportation(), + transportationChunkNr); + } + + public void fillTreeViewWithErrorWater() { + 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); + updateTree(root); + addMoreButtonIfNecessary(errorObjects, treeView, root, chunkCounter); + } + + 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.getVegetation()); + 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.getTransportation()); + updateTree(mainWindow.getTransportationView().getRoot()); + break; + case WATER: + buildWater(model); + updateTree(mainWindow.getWaterView().getRoot()); + break; + default: + throw new IllegalStateException(); + } + } + + public CityDoctorModel getModel() { + return model; + } + + public void showView(View v) { + v.fireOnShowEvent(model, currentChecker, mainWindow); + } + + public void askAndSave() { + boolean saveWithQualityAde = true; + if (model.isValidated()) { + ButtonType yesBtn = ButtonType.YES; + ButtonType noBtn = ButtonType.NO; + Dialog dialog = new Dialog<>(); + dialog.setTitle("Save with QualityADE?"); + dialog.setContentText("Save with QualityADE information?"); + dialog.getDialogPane().getButtonTypes().add(yesBtn); + dialog.getDialogPane().getButtonTypes().add(noBtn); + + Optional result = dialog.showAndWait(); + if (result.isPresent() && result.get() == ButtonType.NO) { + saveWithQualityAde = false; + } + } + + FileChooser fc = new FileChooser(); + fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml")); + fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off")); + File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, "")); + if (dir.exists() && dir.isDirectory()) { + fc.setInitialDirectory(dir); + } else { + Settings.set(Settings.LAST_OPEN_FOLDER, ""); + } + File f = fc.showSaveDialog(mainWindow.getMainStage()); + if (f != null) { + Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent()); + try { + storeModel(f, saveWithQualityAde); + } catch (CityDoctorWriteException e) { + mainWindow.showExceptionDialog(e); + } + } + } + + public void delete(TreeItem selectedItem) { + if (model == null) { + return; + } + Renderable render = selectedItem.getValue(); + if (render instanceof BuildingNode node) { + model.getBuildings().remove(node.getBuilding()); + mainWindow.getBuildingsView().getRoot().getChildren().remove(selectedItem); + } + + } + + 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 WATER: + fillTreeViewWithErrorWater(); + break; + default: + throw new IllegalStateException("Unknown selected feature tab: " + mainWindow.getSelectedTab()); + } + } else { + throw new IllegalStateException("Unknown filter index selected: " + newV); + } + } + + public ParserConfiguration getCurrentConfig() { + return currentConfig; + } +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorGUIStarter.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorGUIStarter.java new file mode 100644 index 0000000..e15c9f7 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorGUIStarter.java @@ -0,0 +1,27 @@ +/*- + * Copyright 2022 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 . + */ +package de.hft.stuttgart.citydoctor2.gui; + +public class CityDoctorGUIStarter { + + public static void main(String[] args) { + MainWindow.main(args); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ClickDispatcher.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ClickDispatcher.java new file mode 100644 index 0000000..bff71d1 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ClickDispatcher.java @@ -0,0 +1,9 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import javafx.scene.input.MouseEvent; + +public interface ClickDispatcher { + + public void click(MouseEvent me, ClickHandler handler); + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ClickHandler.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ClickHandler.java new file mode 100644 index 0000000..1211ca9 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ClickHandler.java @@ -0,0 +1,13 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import de.hft.stuttgart.citydoctor2.datastructure.Polygon; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import javafx.scene.input.MouseEvent; + +public interface ClickHandler { + + public void onPolygonClick(Polygon p, MouseEvent me); + + public void onVertexClick(Vertex v, MouseEvent me); + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ExceptionDialog.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ExceptionDialog.java new file mode 100644 index 0000000..56d6b8d --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ExceptionDialog.java @@ -0,0 +1,56 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import de.hft.stuttgart.citydoctor2.utils.Localization; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Label; +import javafx.scene.control.TextArea; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; + +public class ExceptionDialog { + + private Alert alert; + + private TextArea textArea; + + public ExceptionDialog() { + alert = new Alert(AlertType.ERROR); + alert.setTitle("Exception Dialog"); + alert.getDialogPane().setPrefWidth(800); + Label label = new Label(Localization.getText("ExceptionDialog.stacktrace")); + + textArea = new TextArea(); + textArea.setEditable(false); + textArea.setWrapText(true); + + textArea.setMaxWidth(Double.MAX_VALUE); + textArea.setMaxHeight(Double.MAX_VALUE); + GridPane.setVgrow(textArea, Priority.ALWAYS); + GridPane.setHgrow(textArea, Priority.ALWAYS); + + GridPane expContent = new GridPane(); + expContent.setMaxWidth(Double.MAX_VALUE); + expContent.add(label, 0, 0); + expContent.add(textArea, 0, 1); + + // Set expandable Exception into the dialog pane. + alert.getDialogPane().setExpandableContent(expContent); + } + + public void show(Throwable ex) { + // Create expandable Exception. + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + ex.printStackTrace(pw); + String exceptionText = sw.toString(); + textArea.setText(exceptionText); + alert.setContentText(ex.getMessage()); + alert.setHeaderText(ex.getClass().getSimpleName()); + alert.showAndWait(); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/FilterPane.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/FilterPane.java new file mode 100644 index 0000000..a82b30a --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/FilterPane.java @@ -0,0 +1,305 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import de.hft.stuttgart.citydoctor2.check.ExcludeFilterConfiguration; +import de.hft.stuttgart.citydoctor2.check.FilterConfiguration; +import de.hft.stuttgart.citydoctor2.check.IncludeFilterConfiguration; +import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; +import de.hft.stuttgart.citydoctor2.gui.filter.TypeFilterSelection; +import de.hft.stuttgart.citydoctor2.utils.Localization; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; + +public class FilterPane { + + @FXML + private VBox inclFeatureBox; + + @FXML + private ImageView addInclFeatureView; + + @FXML + private VBox inclMatchBox; + + @FXML + private ImageView addInclMatchView; + + @FXML + private Button addInclFeatureBtn; + + @FXML + private Button addInclMatchBtn; + + @FXML + private VBox exclFeatureBox; + + @FXML + private ImageView addExclFeatureView; + + @FXML + private VBox exclMatchBox; + + @FXML + private ImageView addExclMatchView; + + @FXML + private Button addExclFeatureBtn; + + @FXML + private Button addExclMatchBtn; + + private ScrollPane pane; + private Image deleteImg; + + private List> includeTypeBoxes = new ArrayList<>(); + private List includeMatcherFields = new ArrayList<>(); + + private List excludeMatcherFields = new ArrayList<>(); + + private List> excludeTypeBoxes = new ArrayList<>(); + + private List availableFilters; + + public FilterPane() throws IOException { + FXMLLoader fxmlLoader = new FXMLLoader(FilterPane.class.getResource("FilterPane.fxml")); + fxmlLoader.setController(this); + pane = fxmlLoader.load(); + } + + public void initialize() { + loadImages(); + + availableFilters = new ArrayList<>(); + availableFilters.add(new TypeFilterSelection(FeatureType.BUILDING, Localization.getText("FilterPane.buildings"))); + availableFilters.add(new TypeFilterSelection(FeatureType.BRIDGE, Localization.getText("FilterPane.bridges"))); + availableFilters.add(new TypeFilterSelection(FeatureType.LAND, Localization.getText("FilterPane.landUse"))); + availableFilters.add(new TypeFilterSelection(FeatureType.TRANSPORTATION, Localization.getText("FilterPane.transportation"))); + availableFilters.add(new TypeFilterSelection(FeatureType.VEGETATION, Localization.getText("FilterPane.vegetation"))); + availableFilters.add(new TypeFilterSelection(FeatureType.WATER, Localization.getText("FilterPane.water"))); + setupIncludeFeatureButton(); + setupIncludeMatchButton(); + setupExcludeFeatureButton(); + setupExcludeMatchButton(); + } + + private void setupExcludeMatchButton() { + addExclMatchBtn.setOnAction(ae -> addExcludeMatchField()); + } + + private TextField addExcludeMatchField() { + TextField tf = new TextField(); + tf.setPrefWidth(400); + excludeMatcherFields.add(tf); + + HBox box = new HBox(10); + box.getChildren().add(tf); + + ImageView imgView = new ImageView(deleteImg); + imgView.setFitHeight(25); + imgView.setFitWidth(25); + box.getChildren().add(imgView); + imgView.setOnMouseClicked(me2 -> { + excludeMatcherFields.remove(tf); + exclMatchBox.getChildren().remove(box); + }); + + exclMatchBox.getChildren().add(box); + return tf; + } + + private void setupExcludeFeatureButton() { + addExclFeatureBtn.setOnAction(ae -> addExcludeTypeFilterBox()); + } + + private ComboBox addExcludeTypeFilterBox() { + ComboBox cBox = new ComboBox<>(); + cBox.setPrefWidth(400); + cBox.getItems().addAll(availableFilters); + cBox.getSelectionModel().select(0); + excludeTypeBoxes.add(cBox); + + HBox box = new HBox(10); + box.getChildren().add(cBox); + + ImageView imgView = new ImageView(deleteImg); + imgView.setFitHeight(25); + imgView.setFitWidth(25); + box.getChildren().add(imgView); + imgView.setOnMouseClicked(me2 -> { + excludeTypeBoxes.remove(cBox); + exclFeatureBox.getChildren().remove(box); + }); + + exclFeatureBox.getChildren().add(box); + return cBox; + } + + private void setupIncludeMatchButton() { + addInclMatchBtn.setOnAction(ae -> addIncludeMatchField()); + } + + private TextField addIncludeMatchField() { + TextField tf = new TextField(); + tf.setPrefWidth(400); + includeMatcherFields.add(tf); + + HBox box = new HBox(10); + box.getChildren().add(tf); + + ImageView imgView = new ImageView(deleteImg); + imgView.setFitHeight(25); + imgView.setFitWidth(25); + box.getChildren().add(imgView); + imgView.setOnMouseClicked(me2 -> { + includeMatcherFields.remove(tf); + inclMatchBox.getChildren().remove(box); + }); + + inclMatchBox.getChildren().add(box); + return tf; + } + + public void applyFilterConfig(FilterConfiguration filter) { + includeMatcherFields.clear(); + inclMatchBox.getChildren().clear(); + includeTypeBoxes.clear(); + inclFeatureBox.getChildren().clear(); + + excludeMatcherFields.clear(); + exclMatchBox.getChildren().clear(); + excludeTypeBoxes.clear(); + exclFeatureBox.getChildren().clear(); + applyExcludeFilters(filter); + applyIncludeFilters(filter); + } + + private void applyIncludeFilters(FilterConfiguration filter) { + IncludeFilterConfiguration include = filter.getInclude(); + if (include != null) { + for (String s : include.getIds()) { + addIncludeMatchField().setText(s); + } + for (FeatureType type : include.getTypes()) { + ComboBox box = addIncludeTypeFilterBox(); + for (TypeFilterSelection tfs : box.getItems()) { + if (tfs.getType() == type) { + box.getSelectionModel().select(tfs); + } + } + } + } + } + + private void applyExcludeFilters(FilterConfiguration filter) { + ExcludeFilterConfiguration exclude = filter.getExclude(); + if (exclude != null) { + for (String s : exclude.getIds()) { + addExcludeMatchField().setText(s); + } + for (FeatureType type : exclude.getTypes()) { + ComboBox box = addExcludeTypeFilterBox(); + for (TypeFilterSelection tfs : box.getItems()) { + if (tfs.getType() == type) { + box.getSelectionModel().select(tfs); + } + } + } + } + } + + private void setupIncludeFeatureButton() { + addInclFeatureBtn.setOnAction(ae -> addIncludeTypeFilterBox()); + } + + private ComboBox addIncludeTypeFilterBox() { + ComboBox cBox = new ComboBox<>(); + cBox.setPrefWidth(400); + cBox.getItems().addAll(availableFilters); + cBox.getSelectionModel().select(0); + includeTypeBoxes.add(cBox); + + HBox box = new HBox(10); + box.getChildren().add(cBox); + + ImageView imgView = new ImageView(deleteImg); + imgView.setFitHeight(25); + imgView.setFitWidth(25); + box.getChildren().add(imgView); + imgView.setOnMouseClicked(me2 -> { + includeTypeBoxes.remove(cBox); + inclFeatureBox.getChildren().remove(box); + }); + + inclFeatureBox.getChildren().add(box); + return cBox; + } + + public FilterConfiguration getConfiguration() { + IncludeFilterConfiguration includeFilters = new IncludeFilterConfiguration(); + List featureTypes = new ArrayList<>(); + for (ComboBox box : includeTypeBoxes) { + featureTypes.add(box.getSelectionModel().getSelectedItem().getType()); + } + includeFilters.setTypes(featureTypes); + + List ids = new ArrayList<>(); + for (TextField tf : includeMatcherFields) { + ids.add(tf.getText()); + } + includeFilters.setIds(ids); + + ExcludeFilterConfiguration excludeFilters = new ExcludeFilterConfiguration(); + + List excludeFeatureTypes = new ArrayList<>(); + for (ComboBox box : excludeTypeBoxes) { + excludeFeatureTypes.add(box.getSelectionModel().getSelectedItem().getType()); + } + excludeFilters.setTypes(excludeFeatureTypes); + + List excludeIds = new ArrayList<>(); + for (TextField tf : excludeMatcherFields) { + excludeIds.add(tf.getText()); + } + excludeFilters.setIds(excludeIds); + + + FilterConfiguration fConfig = new FilterConfiguration(); + fConfig.setInclude(includeFilters); + fConfig.setExclude(excludeFilters); + return fConfig; + } + + private void loadImages() { + try { + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/Add.png")) { + Image img = new Image(inStream); + addInclFeatureView.setImage(img); + addInclMatchView.setImage(img); + addExclFeatureView.setImage(img); + addExclMatchView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/Delete.png")) { + deleteImg = new Image(inStream); + } + } catch (IOException e) { + // ignore close exception + } + } + + public ScrollPane getPane() { + return pane; + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/GlobalParameter.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/GlobalParameter.java new file mode 100644 index 0000000..ef4309d --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/GlobalParameter.java @@ -0,0 +1,103 @@ +/*- + * 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 . + */ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.Serializable; + +import de.hft.stuttgart.citydoctor2.check.Unit; + +public class GlobalParameter implements Serializable { + + private static final long serialVersionUID = 1423983452743345752L; + + private String name; + private String value; + private Unit unit; + + public GlobalParameter(String name, String value, Unit unit) { + super(); + this.name = name; + this.value = value; + this.unit = unit; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((unit == null) ? 0 : unit.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + GlobalParameter other = (GlobalParameter) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (unit != other.unit) + return false; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + @Override + public String toString() { + return "GlobalParameter [name=" + name + ", value=" + value + ", unit=" + unit + "]"; + } + + public Unit getUnit() { + return unit; + } + + public void setUnit(Unit unit) { + this.unit = unit; + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/HighlightController.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/HighlightController.java new file mode 100644 index 0000000..5d398a0 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/HighlightController.java @@ -0,0 +1,223 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.Edge; +import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; +import de.hft.stuttgart.citydoctor2.datastructure.Polygon; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType; +import de.hft.stuttgart.citydoctor2.math.Triangle3d; +import de.hft.stuttgart.citydoctor2.math.Vector3d; +import javafx.application.Platform; +import javafx.geometry.Point3D; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.paint.Color; +import javafx.scene.paint.PhongMaterial; +import javafx.scene.shape.Cylinder; +import javafx.scene.shape.Sphere; +import javafx.scene.transform.Rotate; +import javafx.scene.transform.Translate; + +public class HighlightController { + + + private double scale; + private Group points; + private Group edges; + + private Group highlights; + + public HighlightController(Group world) { + highlights = new Group(); + points = new Group(); + edges = new Group(); + highlights.getChildren().add(edges); + highlights.getChildren().add(points); + Platform.runLater(() -> world.getChildren().add(highlights)); + } + + public void clearHighlights() { + points.getChildren().clear(); + edges.getChildren().clear(); + } + + public void changeScaling(double translateZ) { + scale = Math.abs(translateZ); + scale = Math.min(150, scale); + scale = Math.max(10, scale); + scale = scale * 0.01; + for (Node n : points.getChildren()) { + n.setScaleX(scale); + n.setScaleY(scale); + n.setScaleZ(scale); + } + double edgeSize = scale / 10; + for (Node n : edges.getChildren()) { + if (n instanceof Cylinder) { + Cylinder cy = (Cylinder) n; + cy.setRadius(edgeSize); + } + } + } + + public void highlight(Polygon p, TriangulatedGeometry currentTriGeom) { + clearHighlights(); + addHighlight(p, currentTriGeom, Color.RED, Color.BLUE); + } + + private void addHighlight(Polygon p, TriangulatedGeometry currentTriGeom, Color extColor, Color intColor) { + if (currentTriGeom == null || p == null) { + return; + } + addHighlight(p.getExteriorRing(), currentTriGeom, extColor); + for (LinearRing intRing : p.getInnerRings()) { + addHighlight(intRing, currentTriGeom, intColor); + } + } + + public void highlight(LinearRing ring, TriangulatedGeometry currentTriGeom) { + clearHighlights(); + if (ring.getType() == LinearRingType.EXTERIOR) { + addHighlight(ring, currentTriGeom, Color.RED); + } else { + addHighlight(ring, currentTriGeom, Color.BLUE); + } + } + + public void highlight(List rings, TriangulatedGeometry currentTriGeom) { + clearHighlights(); + for (LinearRing lr : rings) { + if (lr.getType() == LinearRingType.EXTERIOR) { + addHighlight(lr, currentTriGeom, Color.RED); + } else { + addHighlight(lr, currentTriGeom, Color.BLUE); + } + } + } + + private void addHighlight(LinearRing ring, TriangulatedGeometry currentTriGeom, Color pointColor) { + if (currentTriGeom == null || ring == null) { + return; + } + Vector3d movedBy = currentTriGeom.getMovedBy(); + for (Vertex v : ring.getVertices()) { + highlightPoint(movedBy, v, pointColor); + } + for (int i = 0; i < ring.getVertices().size() - 1; i++) { + Vertex v1 = ring.getVertices().get(i + 0); + Vertex v2 = ring.getVertices().get(i + 1); + highlightEdge(v1, v2, movedBy); + } + } + + public void highlight(Edge e, TriangulatedGeometry currentTriGeom) { + clearHighlights(); + addHighlight(e, currentTriGeom); + } + + private void addHighlight(Edge e, TriangulatedGeometry currentTriGeom) { + if (currentTriGeom == null || e == null) { + return; + } + Vector3d movedBy = currentTriGeom.getMovedBy(); + highlightEdge(e.getFrom(), e.getTo(), movedBy); + highlightPoint(movedBy, e.getFrom(), Color.RED); + highlightPoint(movedBy, e.getTo(), Color.RED); + } + + public void highlight(Vertex v, TriangulatedGeometry currentTriGeom) { + clearHighlights(); + addHighlight(v, currentTriGeom, Color.RED); + } + + public void addHighlight(Vertex v, TriangulatedGeometry currentTriGeom, Color c) { + if (currentTriGeom == null || v == null) { + return; + } + Vector3d movedBy = currentTriGeom.getMovedBy(); + highlightPoint(movedBy, v, c); + } + + private void highlightPoint(Vector3d movedBy, Vertex v, Color color) { + Sphere sp = new Sphere(0.5); + sp.setMaterial(new PhongMaterial(color)); + sp.setTranslateX(v.getX() - movedBy.getX()); + sp.setTranslateY(v.getY() - movedBy.getY()); + sp.setTranslateZ(v.getZ() - movedBy.getZ()); + sp.setScaleX(scale); + sp.setScaleY(scale); + sp.setScaleZ(scale); + + sp.setUserData(new VertexClickDispatcher(v)); + + points.getChildren().add(sp); + } + + private void highlightEdge(Vector3d v1, Vector3d v2, Vector3d movedBy) { + Point3D origin = new Point3D(v1.getX() - movedBy.getX(), v1.getY() - movedBy.getY(), + v1.getZ() - movedBy.getZ()); + Point3D target = new Point3D(v2.getX() - movedBy.getX(), v2.getY() - movedBy.getY(), + v2.getZ() - movedBy.getZ()); + + Point3D yAxis = new Point3D(0, 1, 0); + Point3D diff = target.subtract(origin); + double height = diff.magnitude(); + + Point3D mid = target.midpoint(origin); + Translate moveToMidpoint = new Translate(mid.getX(), mid.getY(), mid.getZ()); + + Point3D axisOfRotation = diff.crossProduct(yAxis); + double angle = Math.acos(diff.normalize().dotProduct(yAxis)); + Rotate rotateAroundCenter = new Rotate(-Math.toDegrees(angle), axisOfRotation); + + Cylinder cy = new Cylinder(scale / 10, height); + cy.setMaterial(new PhongMaterial(Color.ORANGE)); + cy.getTransforms().addAll(moveToMidpoint, rotateAroundCenter); + edges.getChildren().add(cy); + } + + public void highlightEdges(List errorEdges, TriangulatedGeometry currentTriGeom) { + clearHighlights(); + for (Edge e : errorEdges) { + addHighlight(e, currentTriGeom); + } + } + + public void highlightPolygons(List> components, TriangulatedGeometry currentTriGeom) { + clearHighlights(); + for (int i = 0; i < components.size(); i++) { + Color extColor; + Color intColor; + // select some color pairs for exterior and inner rings + switch (i % 3) { + case 1: + extColor = Color.GREEN; + intColor = Color.YELLOW; + break; + case 2: + extColor = Color.BROWN; + intColor = Color.VIOLET; + break; + default: + extColor = Color.RED; + intColor = Color.BLUE; + } + List component = components.get(i); + for (Polygon p : component) { + addHighlight(p, currentTriGeom, extColor, intColor); + } + } + } + + public void addHighlight(Polygon p, TriangulatedGeometry currentTriGeom) { + addHighlight(p, currentTriGeom, Color.RED, Color.BLUE); + } + + public void highlight(Triangle3d t, TriangulatedGeometry currentTriGeom) { + highlightEdge(t.getP1(), t.getP2(), currentTriGeom.getMovedBy()); + highlightEdge(t.getP2(), t.getP3(), currentTriGeom.getMovedBy()); + highlightEdge(t.getP3(), t.getP1(), currentTriGeom.getMovedBy()); + } +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/LanguageSelectorCell.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/LanguageSelectorCell.java new file mode 100644 index 0000000..0d9a928 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/LanguageSelectorCell.java @@ -0,0 +1,49 @@ +/*- + * 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 . + */ +package de.hft.stuttgart.citydoctor2.gui; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import javafx.scene.control.ListCell; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + +public class LanguageSelectorCell extends ListCell { + + private static Map imageCache = new HashMap<>(); + + @Override + protected void updateItem(Locale item, boolean empty) { + super.updateItem(item, empty); + if (item == null || empty) { + setGraphic(null); + } else { + String country = item.getLanguage().toLowerCase(Locale.US); + Image iconRef = imageCache.computeIfAbsent(country, key -> { + String iconPath = "icons/icon_" + country + ".png"; + return new Image(MainWindow.class.getResourceAsStream(iconPath)); + }); + ImageView iconImageView = new ImageView(iconRef); + setGraphic(iconImageView); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ListErrorVisitor.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ListErrorVisitor.java new file mode 100644 index 0000000..65b7cc1 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ListErrorVisitor.java @@ -0,0 +1,263 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.util.ArrayList; +import java.util.List; + +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.ErrorVisitor; +import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError; +import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError; +import de.hft.stuttgart.citydoctor2.check.error.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.PointTouchesEdgeError; +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.RingNotClosedError; +import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError; +import de.hft.stuttgart.citydoctor2.check.error.SchematronError; +import de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError; +import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError; +import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError; +import de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError; +import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError; +import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError; +import de.hft.stuttgart.citydoctor2.datastructure.Edge; +import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import javafx.scene.paint.Color; + +public class ListErrorVisitor implements ErrorVisitor { + + private HighlightController controller; + private TriangulatedGeometry geom; + + public ListErrorVisitor(HighlightController controller) { + this.controller = controller; + } + + public void setGeometry(TriangulatedGeometry geom) { + this.geom = geom; + } + + @Override + public void visit(PolygonHoleOutsideError err) { + List highlightedRings = new ArrayList<>(); + highlightedRings.add(err.getPolygon().getExteriorRing()); + highlightedRings.addAll(err.getHolesOutside()); + controller.highlight(highlightedRings, geom); + } + + @Override + public void visit(NonManifoldEdgeError err) { + controller.highlightEdges(err.getEdges(), geom); + } + + @Override + public void visit(MultipleConnectedComponentsError err) { + controller.highlightPolygons(err.getComponents(), geom); + } + + @Override + public void visit(NestedRingError err) { + List highlightedRings = new ArrayList<>(); + highlightedRings.add(err.getPolygon().getExteriorRing()); + highlightedRings.add(err.getInnerRing()); + controller.highlight(highlightedRings, geom); + } + + @Override + public void visit(NonManifoldVertexError err) { + controller.highlightPolygons(err.getComponents(), geom); + controller.addHighlight(err.getVertex(), geom, Color.BLUEVIOLET); + } + + @Override + public void visit(PolygonWrongOrientationError err) { + controller.highlightEdges(err.getEdges(), geom); + } + + @Override + public void visit(PolygonSameOrientationError err) { + List highlightedRings = new ArrayList<>(); + highlightedRings.add(err.getPolygon().getExteriorRing()); + highlightedRings.add(err.getInnerRing()); + controller.highlight(highlightedRings, geom); + } + + @Override + public void visit(SolidNotClosedError err) { + controller.highlightEdges(err.getErrorEdges(), geom); + } + + @Override + public void visit(DependenciesNotMetError err) { + // don't display + } + + @Override + public void visit(UnknownCheckError err) { + // don't display + } + + @Override + public void visit(RingNotClosedError err) { + controller.highlight(err.getRing(), geom); + } + + @Override + public void visit(ConsecutivePointSameError err) { + controller.highlight(err.getRing(), geom); + controller.addHighlight(err.getVertex1(), geom, Color.BLACK); + controller.addHighlight(err.getVertex2(), geom, Color.BLACK); + } + + @Override + public void visit(AllPolygonsWrongOrientationError err) { + // don't display + } + + @Override + public void visit(PolygonInteriorDisconnectedError err) { + controller.highlight(err.getConnectedRings(), geom); + } + + @Override + public void visit(NullAreaError err) { + // don't display + } + + @Override + public void visit(RingTooFewPointsError err) { + controller.highlight(err.getRing(), geom); + } + + @Override + public void visit(NonPlanarPolygonNormalsDeviation err) { + controller.highlight(err.getPolygon(), geom); + } + + @Override + public void visit(NonPlanarPolygonDistancePlaneError err) { + controller.highlight(err.getPolygon(), geom); + controller.addHighlight(err.getVertex(), geom, Color.BLACK); + } + + @Override + public void visit(PolygonIntersectingRingsError err) { + List rings = new ArrayList<>(); + rings.add(err.getIntersectingRings().getValue0()); + rings.add(err.getIntersectingRings().getValue1()); + controller.highlight(rings, geom); + } + + @Override + public void visit(SolidSelfIntError err) { + // nothing to display for now + } + + @Override + public void visit(TooFewPolygonsError err) { + // don't display + } + + @Override + public void visit(RingDuplicatePointError err) { + controller.highlight(err.getRing(), geom); + controller.addHighlight(err.getVertex1(), geom, Color.BLACK); + controller.addHighlight(err.getVertex2(), geom, Color.BLACK); + } + + @Override + public void visit(RingEdgeIntersectionError err) { + List list = new ArrayList<>(); + list.add(err.getEdge1()); + list.add(err.getEdge2()); + controller.highlightEdges(list, geom); + controller.addHighlight(new Vertex(err.getIntersection()), geom, Color.BLACK); + } + + @Override + public void visit(PointTouchesEdgeError err) { + controller.highlight(err.getEdge(), geom); + controller.addHighlight(err.getVertex(), geom, Color.BLACK); + } + + @Override + public void visit(CheckError err) { + // nothing to display + } + + @Override + public void visit(NotCeilingError err) { + // nothing to display + } + + @Override + public void visit(NotFloorError err) { + // nothing to display + } + + @Override + public void visit(NotWallError err) { + // nothing to display + } + + @Override + public void visit(NotGroundError err) { + // nothing to display + } + + @Override + public void visit(SchematronError err) { + // nothing to display + } + + @Override + public void visit(SurfaceUnfragmentedError err) { + // nothing to display + } + + @Override + public void visit(DegeneratedRingError err) { + controller.highlight(err.getRing(), geom); + } + + @Override + public void visit(AttributeMissingError err) { + // nothing to display + } + + @Override + public void visit(AttributeValueWrongError err) { + // nothing to display + } + + @Override + public void visit(AttributeInvalidError err) { + // nothing to display + } + + @Override + public void visit(PolygonWithoutSurfaceError err) { + controller.highlight(err.getPolygon(), geom); + } +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/LoadingInfoDialog.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/LoadingInfoDialog.java new file mode 100644 index 0000000..fb5064c --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/LoadingInfoDialog.java @@ -0,0 +1,38 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.IOException; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.VBox; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.stage.Window; + +public class LoadingInfoDialog { + + private Stage stage; + + public LoadingInfoDialog(Window parent) throws IOException { + FXMLLoader loader = new FXMLLoader(LoadingInfoDialog.class.getResource("CreateRenderDataDialog.fxml")); + loader.setController(this); + VBox box = loader.load(); + + stage = new Stage(StageStyle.UNDECORATED); + Scene scene = new Scene(box); + scene.setFill(null); + stage.setScene(scene); + stage.initOwner(parent); + stage.initModality(Modality.APPLICATION_MODAL); + } + + public void show() { + stage.show(); + } + + public void hide() { + stage.hide(); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java new file mode 100644 index 0000000..9ad3a38 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java @@ -0,0 +1,349 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import de.hft.stuttgart.citydoctor2.gui.tree.Renderable; +import de.hft.stuttgart.citydoctor2.utils.Localization; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.control.Button; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.Tooltip; +import javafx.scene.control.TreeView; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.stage.Stage; + +public class MainToolBar { + + private static final Logger logger = LogManager.getLogger(MainToolBar.class); + + @FXML + private Button saveBtn; + + @FXML + private ImageView saveView; + + @FXML + private Button openBtn; + + @FXML + private ImageView openImageView; + + @FXML + private Button checkBtn; + + @FXML + private ImageView checkImageView; + + @FXML + private Button showWorldBtn; + + @FXML + private ImageView showWorldImageView; + + @FXML + private Button schematronBtn; + + @FXML + private ImageView schematronImgView; + + @FXML + private ToggleButton lod1Btn; + + @FXML + private ImageView lod1View; + + @FXML + private ToggleButton lod2Btn; + + @FXML + private ImageView lod2View; + + @FXML + private ToggleButton lod3Btn; + + @FXML + private ImageView lod3View; + + @FXML + private ToggleButton lod4Btn; + + @FXML + private ImageView lod4View; + + @FXML + private Button aboutBtn; + + @FXML + private ImageView aboutImgView; + + @FXML + private ToggleButton gridButton; + + @FXML + private ToggleButton cullingButton; + + @FXML + private ImageView cullingImageView; + + @FXML + private ImageView gridImageView; + + @FXML + private Button reportBtn; + + @FXML + private ImageView reportImageView; + + private OpenFileDialog fileDialog; + private CheckDialog checkDialog; + private WriteReportDialog writeDialog; + private AboutDialog aboutDialog; + + private CityDoctorController controller; + private TabPane featurePane; + private Stage stage; + private Renderer renderer; + private MainWindow mainWindow; + + private HBox toolBar; + + public MainToolBar(Stage stage, CityDoctorController controller, TabPane featurePane, Renderer renderer, + MainWindow mainWindow) throws IOException { + this.controller = controller; + this.featurePane = featurePane; + this.renderer = renderer; + this.stage = stage; + this.mainWindow = mainWindow; + + FXMLLoader loader = new FXMLLoader(MainToolBar.class.getResource("MainToolBar.fxml")); + loader.setController(this); + toolBar = loader.load(); + + fileDialog = new OpenFileDialog(stage, controller); + } + + public void initialize() { + openBtn.setOnAction(ae -> fileDialog.show()); + + setupSaveBtn(); + setupCheckButton(); + setupLodButtons(); + setupAboutButton(); + setupReportButton(); + + loadImages(); + + gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected())); + gridButton.setTooltip(new Tooltip(Localization.getText("MainToolBar.wireframe"))); + cullingButton.setOnAction(ae -> renderer.enableCulling(cullingButton.isSelected())); + cullingButton.setTooltip(new Tooltip(Localization.getText("MainToolBar.culling"))); + + showWorldBtn.setOnAction(ar -> { + Tab selectedItem = featurePane.getSelectionModel().getSelectedItem(); + if (selectedItem.getContent() instanceof TreeView) { + @SuppressWarnings("unchecked") + TreeView content = (TreeView) selectedItem.getContent(); + content.getSelectionModel().clearSelection(); + } + controller.showWorld(); + }); + } + + private void loadImages() { + try { + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/openFolderIcon.png")) { + Image img = new Image(inStream); + openImageView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/check32x32.png")) { + Image img = new Image(inStream); + checkImageView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/wireframe32x32.png")) { + Image img = new Image(inStream); + gridImageView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/Culling.png")) { + Image img = new Image(inStream); + cullingImageView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/error_stat32x32.png")) { + Image img = new Image(inStream); + reportImageView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/scene.png")) { + Image img = new Image(inStream); + showWorldImageView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod1_32x32.png")) { + Image img = new Image(inStream); + lod1View.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod2_32x32.png")) { + Image img = new Image(inStream); + lod2View.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod3_32x32.png")) { + Image img = new Image(inStream); + lod3View.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod4_32x32.png")) { + Image img = new Image(inStream); + lod4View.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/about.png")) { + Image img = new Image(inStream); + aboutImgView.setImage(img); + } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/save.png")) { + Image img = new Image(inStream); + saveView.setImage(img); + } + } catch (IOException e) { + // ignore close exception + } + } + + private void setupReportButton() { + reportBtn.setDisable(true); + reportBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.writeReports"))); + reportBtn.setOnAction(ae -> { + if (writeDialog == null) { + try { + writeDialog = new WriteReportDialog(stage, controller, mainWindow); + } catch (IOException e) { + logger.catching(e); + mainWindow.showExceptionDialog(e); + } + } + if (writeDialog != null) { + // writeDialog can be null if creation of said dialog fails + writeDialog.show(); + } + }); + } + + private void setupAboutButton() { + aboutBtn.setOnAction(ae -> { + if (aboutDialog == null) { + try { + aboutDialog = new AboutDialog(stage); + aboutDialog.show(); + } catch (IOException e) { + logger.error("Could not load about dialog.", e); + } + } else { + aboutDialog.show(); + } + }); + } + + private void setupLodButtons() { + lod1Btn.setOnAction(ae -> { + if (lod1Btn.isSelected()) { + renderer.enableLod1(); + } else { + renderer.disableLod1(); + } + }); + lod2Btn.setOnAction(ae -> { + if (lod2Btn.isSelected()) { + renderer.enableLod2(); + } else { + renderer.disableLod2(); + } + }); + lod3Btn.setOnAction(ae -> { + if (lod3Btn.isSelected()) { + renderer.enableLod3(); + } else { + renderer.disableLod3(); + } + }); + lod4Btn.setOnAction(ae -> { + if (lod4Btn.isSelected()) { + renderer.enableLod4(); + } else { + renderer.disableLod4(); + } + }); + } + + private void setupSaveBtn() { + saveBtn.setOnAction(ae -> controller.askAndSave()); + } + + private void setupCheckButton() { + checkBtn.setDisable(true); + checkBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.executeChecks"))); + checkBtn.setOnAction(ae -> { + if (checkDialog == null) { + try { + checkDialog = new CheckDialog(mainWindow, stage, controller); + } catch (IOException e) { + mainWindow.showExceptionDialog(e); + logger.catching(e); + } + } + checkDialog.show(); + }); + } + + public Button getCheckButton() { + return checkBtn; + } + + public ToggleButton getGridButton() { + return gridButton; + } + + public ToggleButton getCullingButton() { + return cullingButton; + } + + public Button getWriteReportButton() { + return reportBtn; + } + + public ToggleButton getLod1Btn() { + return lod1Btn; + } + + public ToggleButton getLod2Btn() { + return lod2Btn; + } + + public ToggleButton getLod3Btn() { + return lod3Btn; + } + + public ToggleButton getLod4Btn() { + return lod4Btn; + } + + public Button getWorldBtn() { + return showWorldBtn; + } + + public Button getSaveBtn() { + return saveBtn; + } + + public HBox getToolBar() { + return toolBar; + } + + public Button getOpenBtn() { + return openBtn; + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java new file mode 100644 index 0000000..4ba6762 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -0,0 +1,980 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Timer; +import java.util.TimerTask; + +import javax.imageio.ImageIO; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import de.hft.stuttgart.citydoctor2.CityDoctorValidation; +import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; +import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox; +import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; +import de.hft.stuttgart.citydoctor2.gui.logger.GuiLogger; +import de.hft.stuttgart.citydoctor2.gui.tree.BuildingNode; +import de.hft.stuttgart.citydoctor2.gui.tree.Renderable; +import de.hft.stuttgart.citydoctor2.gui.tree.RenderableTreeCell; +import de.hft.stuttgart.citydoctor2.parameter.ArgumentParser; +import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; +import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import de.hft.stuttgart.citydoctor2.parser.ProgressListener; +import de.hft.stuttgart.citydoctor2.utils.Localization; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.embed.swing.SwingFXUtils; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.AmbientLight; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.PerspectiveCamera; +import javafx.scene.Scene; +import javafx.scene.SceneAntialiasing; +import javafx.scene.SubScene; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ComboBox; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.MenuItem; +import javafx.scene.control.ProgressBar; +import javafx.scene.control.RadioButton; +import javafx.scene.control.SplitPane; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.control.ToolBar; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.image.WritableImage; +import javafx.scene.input.Dragboard; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.TransferMode; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.paint.Color; +import javafx.scene.transform.Rotate; +import javafx.stage.Stage; + +public class MainWindow extends Application { + + private static final Logger logger = LogManager.getLogger(MainWindow.class); + + private static final double CAMERA_TRANSLATE_Z = -100.0; + private static final double CAMERA_INITIAL_X_ANGLE = 20.0; + private static final double CAMERA_INITIAL_Y_ANGLE = 120.0; + + @FXML + private TreeView buildingsView; + + @FXML + private TreeView vegetationView; + + @FXML + private TreeView transView; + + @FXML + private TreeView bridgeView; + + @FXML + private TreeView waterView; + + @FXML + private TreeView terrainView; + + @FXML + private TreeView polygonView; + + @FXML + private TreeView edgeView; + + @FXML + private TreeView vertexView; + + @FXML + private Pane meshView; + + @FXML + private SplitPane mainContainer; + + @FXML + private TabPane detailsTabPane; + + @FXML + private TabPane featurePane; + + @FXML + private TextField searchField; + + @FXML + private TreeView errorView; + + @FXML + private Button clearBtn; + + @FXML + private Button searchBtn; + + @FXML + private ToggleButton allButton; + + @FXML + private ToggleButton errorButton; + + @FXML + private ComboBox showCityObjectsCombo; + + @FXML + private HBox viewPane; + + @FXML + private ListView globalErrorsView; + + @FXML + private BorderPane mainPane; + + @FXML + private HBox viewButtonBox; + + @FXML + private ComboBox languageSelector; + + @FXML + private Tab buildingsTab; + + @FXML + private Tab vegetationTab; + + @FXML + private Tab transportationTab; + + @FXML + private Tab bridgeTab; + + @FXML + private Tab waterTab; + + @FXML + private Tab terrainTab; + + @FXML + private Tab errorsTab; + + @FXML + private Tab polygonsTab; + + @FXML + private Tab edgesTab; + + @FXML + private Tab verticesTab; + + @FXML + private Tab logTab; + + @FXML + private Tab globalErrorsTab; + + @FXML + private Label viewLabel; + + @FXML + private Label showLabel; + + @FXML + private Label searchLabel; + + @FXML + private Label memoryLabel; + + @FXML + private ProgressBar memoryBar; + + @FXML + private Label memoryConsumptionLabel; + + @FXML + private Label availableLabel; + + private Group meshGroup; + + private Group world; + private PerspectiveCamera camera; + private Rotate cameraXRotation = new Rotate(); + private Rotate cameraZRotation = new Rotate(); + + private double dragX; + private double dragY; + + private double cameraXRot = CAMERA_INITIAL_X_ANGLE; + private double cameraYRot = CAMERA_INITIAL_Y_ANGLE; + private double translateZ = CAMERA_TRANSLATE_Z; + private double[] clickStart = new double[2]; + + @FXML + private TextArea logArea; + + @FXML + private ToolBar viewBar; + + private Stage stage; + private ExceptionDialog exceptionDialog; + + private Renderer renderer; + private CityDoctorController controller; + private HighlightController highlightController; + + private FeatureType selectedTab = FeatureType.BUILDING; + + private MainToolBar mainToolBar; + private SubScene geomScene; + + private static boolean loadFileAtStartup = false; + private static String inputFile; + private static String xmlOutput; + private static String pdfOutput; + private static ValidationConfiguration config; + + private VertexClickHandler clickHandler; + + private ChangeListener filterChangeListener; + + public static void main(String[] args) { + setLocaleFromSettings(); + ArgumentParser argParser = new ArgumentParser(args); + inputFile = CityDoctorValidation.getInputFile(argParser, true); + if (inputFile != null) { + loadFileAtStartup = true; + xmlOutput = CityDoctorValidation.getXmlOutput(argParser); + pdfOutput = CityDoctorValidation.getPdfOutput(argParser); + try { + config = CityDoctorValidation.getValidationConfig(argParser, true); + } catch (FileNotFoundException e) { + Platform.runLater(() -> { + List configFiles = argParser.getValues("config"); + Alert alert = new Alert(AlertType.ERROR); + alert.setContentText(Localization.getText("MainWindow.missingConfig") + configFiles); + alert.showAndWait(); + }); + System.exit(4); + } + } + Application.launch(args); + } + + private static void setLocaleFromSettings() { + String localeString = Settings.get(Settings.LANGUAGE); + if (localeString != null) { + try { + Locale loc = new Locale(localeString); + Locale.setDefault(loc); + } catch (Exception e) { + logger.warn("Could not set language to {}, using system language", localeString); + } + } + } + + @Override + public void start(Stage stage) throws IOException { + this.stage = stage; + stage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("icons/citydoctor_logo.png"))); + FXMLLoader loader = new FXMLLoader(MainWindow.class.getResource("MainWindow.fxml")); + loader.setController(this); + BorderPane bp = loader.load(); + highlightController = new HighlightController(world); + renderer = new Renderer(this, highlightController); + clickHandler = new VertexClickHandler(errorView, renderer, stage); + controller = new CityDoctorController(this, highlightController, renderer); + mainToolBar = new MainToolBar(stage, controller, featurePane, renderer, this); + viewPane.getChildren().add(mainToolBar.getToolBar()); + HBox.setHgrow(mainToolBar.getToolBar(), Priority.ALWAYS); + + ValidationView valView = new ValidationView(this, controller); + ViewRegistration.registerView(valView); + setupViews(valView); + + createLanguageSelector(); + setLabelsInCorrectLanguage(); + + Scene scene = new Scene(bp, 1280, 800); + createDropTarget(scene, valView); + String version = Localization.getText(Localization.VERSION); + stage.setTitle("CityDoctor " + version); + stage.setScene(scene); + stage.show(); + checkForStartupLoading(); + + memoryBar.setOnMouseClicked(me -> System.gc()); + Timer timer = new Timer(true); + // check memory every second + TimerTask task = new TimerTask() { + + Runtime runtime = Runtime.getRuntime(); + + @Override + public void run() { + long totalMemory = runtime.totalMemory(); + long freeMemory = runtime.freeMemory(); + long usedMemory = totalMemory - freeMemory; + double percentage = usedMemory / (double) totalMemory; + if (totalMemory / 1024 / 1024 >= 1024) { + // gb + double totalMemoryGb = totalMemory / (1024d * 1024d * 1024d); + double usedMemoryGb = usedMemory / (1024d * 1024d * 1024d); + String memoryString = String.format("%.1f GB / %.1f GB", usedMemoryGb, totalMemoryGb); + Platform.runLater(() -> { + memoryConsumptionLabel.setText(memoryString); + memoryBar.setProgress(percentage); + }); + } else if (totalMemory / 1024 >= 1024) { + // mb + double totalMemoryMb = totalMemory / (1024d * 1024d); + double usedMemoryMb = usedMemory / (1024d * 1024d); + String memoryString = String.format("%.1f MB / %.1f MB", usedMemoryMb, totalMemoryMb); + Platform.runLater(() -> { + memoryConsumptionLabel.setText(memoryString); + memoryBar.setProgress(percentage); + }); + } + } + }; + timer.schedule(task, 0, 1000); + } + + private void createDropTarget(Scene scene, ValidationView valView) { + setDragOverInteraction(scene, valView); + scene.setOnDragDropped(event -> { + if (ViewRegistration.getCurrentActiveView() != valView) { + return; + } + Dragboard db = event.getDragboard(); + boolean success = false; + if (db.hasFiles() && db.getFiles().size() == 1) { + File f = db.getFiles().get(0); + Thread t = new Thread(() -> { + try { + controller.loadCityGml(f.getAbsolutePath(), 8, (ProgressListener) null, false); + } catch (Exception e) { + if (logger.isErrorEnabled()) { + logger.error(Localization.getText("OpenFileDialog.loadFailed"), e); + } + Platform.runLater(() -> { + ExceptionDialog exDialog = new ExceptionDialog(); + exDialog.show(e); + }); + } + }); + t.start(); + success = true; + } + // let the source know whether the string was successfully transferred and used + event.setDropCompleted(success); + + event.consume(); + }); + } + + private void setDragOverInteraction(Scene scene, ValidationView valView) { + scene.setOnDragOver(event -> { + if (ViewRegistration.getCurrentActiveView() != valView) { + return; + } + if (event.getGestureSource() != scene && event.getDragboard().hasFiles() + && event.getDragboard().getFiles().size() == 1) { + // allow for both copying and moving, whatever user chooses + event.acceptTransferModes(TransferMode.LINK); + } + event.consume(); + }); + } + + private void setLabelsInCorrectLanguage() { + buildingsTab.setText(Localization.getText("MainWindow.buildingsTab")); + vegetationTab.setText(Localization.getText("MainWindow.vegetationTab")); + transportationTab.setText(Localization.getText("MainWindow.transportationTab")); + bridgeTab.setText(Localization.getText("MainWindow.bridgeTab")); + waterTab.setText(Localization.getText("MainWindow.waterTab")); + terrainTab.setText(Localization.getText("MainWindow.terrainTab")); + viewLabel.setText(Localization.getText("MainWindow.viewLabel")); + showLabel.setText(Localization.getText("MainWindow.showLabel")); + searchLabel.setText(Localization.getText("MainWindow.searchLabel")); + searchBtn.setText(Localization.getText("MainWindow.searchBtn")); + clearBtn.setText(Localization.getText("MainWindow.clearBtn")); + errorsTab.setText(Localization.getText("MainWindow.errorsTab")); + polygonsTab.setText(Localization.getText("MainWindow.polygonsTab")); + edgesTab.setText(Localization.getText("MainWindow.edgesTab")); + verticesTab.setText(Localization.getText("MainWindow.verticesTab")); + logTab.setText(Localization.getText("MainWindow.logTab")); + globalErrorsTab.setText(Localization.getText("MainWindow.globalErrorsTab")); + memoryLabel.setText(Localization.getText("MainWindow.memoryLabel")); + availableLabel.setText(String.format("%s %.1f GB", Localization.getText("MainWindow.availableLabel"), + Runtime.getRuntime().maxMemory() / 1024d / 1024d / 1024d)); + } + + private void createLanguageSelector() { + languageSelector.setButtonCell(new LanguageSelectorCell()); + languageSelector.setCellFactory(view -> new LanguageSelectorCell()); + languageSelector.getItems().add(Locale.GERMAN); + languageSelector.getItems().add(Locale.ENGLISH); + if (Locale.getDefault().getLanguage().equals(Locale.GERMAN.getLanguage())) { + languageSelector.getSelectionModel().select(Locale.GERMAN); + } else if (Locale.getDefault().getLanguage().equals(Locale.ENGLISH.getLanguage())) { + languageSelector.getSelectionModel().select(Locale.ENGLISH); + } else { + languageSelector.getSelectionModel().select(Locale.ENGLISH); + Settings.set(Settings.LANGUAGE, Locale.ENGLISH.getLanguage()); + } + languageSelector.getSelectionModel().selectedItemProperty().addListener((obs, oldV, newV) -> { + Alert alert = new Alert(AlertType.CONFIRMATION, Localization.getText("MainWindow.languageChange"), + ButtonType.OK); + alert.showAndWait(); + Settings.set(Settings.LANGUAGE, newV.getLanguage()); + }); + } + + private void checkForStartupLoading() { + if (loadFileAtStartup) { + logger.info(Localization.getText("MainWindow.loadGivenFile")); + Thread t = new Thread(() -> { + try { + // wait a bit for the gui to show + Thread.sleep(500); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + try { + controller.loadCityGml(inputFile, config.getNumberOfRoundingPlaces(), null, + config.isXmlValidation()); + logger.info(Localization.getText("MainWindow.finishedLoading")); + logger.info(Localization.getText("MainWindow.checking")); + controller.startChecks(config, null); + if (xmlOutput != null) { + logger.info(Localization.getText("MainWindow.writeXml")); + controller.writeXmlReport(new File(xmlOutput)); + logger.info(Localization.getText("MainWindow.finishedXml")); + } + if (pdfOutput != null) { + logger.info(Localization.getText("MainWindow.writePdf")); + controller.writePdfReport(new File(pdfOutput)); + logger.info(Localization.getText("MainWindow.finishedPdf")); + } + } catch (CityGmlParseException | InvalidGmlFileException e) { + logger.error(Localization.getText("MainWindow.loadFailed"), e.getMessage()); + } + }); + t.start(); + } + } + + private void setupViews(ValidationView valView) { + ToggleGroup group = new ToggleGroup(); + for (View v : ViewRegistration.getRegisteredViews()) { + + RadioButton radioButton = new RadioButton(); + radioButton.getStyleClass().remove("radio-button"); + radioButton.getStyleClass().add("toggle-button"); + ImageView view = new ImageView(v.getViewLogo()); + view.setFitHeight(32); + view.setFitWidth(32); + radioButton.setGraphic(view); + group.getToggles().add(radioButton); + if (v == valView) { + ViewRegistration.setCurrentActiveView(valView); + group.selectToggle(radioButton); + } + viewButtonBox.getChildren().add(radioButton); + radioButton.selectedProperty().addListener((obs, oldV, newV) -> { + if (Boolean.TRUE.equals(newV)) { + showView(v); + ViewRegistration.setCurrentActiveView(v); + } else { + v.onHide(); + } + }); + } + } + + private void showView(View v) { + viewPane.getChildren().clear(); + controller.showView(v); + Optional toolbar = v.getToolbar(); + if (toolbar.isPresent()) { + viewPane.getChildren().add(toolbar.get()); + HBox.setHgrow(toolbar.get(), Priority.ALWAYS); + } else { + HBox placeHolderToolbar = new HBox(); + placeHolderToolbar.setMaxHeight(Double.MAX_VALUE); + viewPane.getChildren().add(placeHolderToolbar); + HBox.setHgrow(placeHolderToolbar, Priority.ALWAYS); + } + mainPane.setCenter(v.getMainScreen()); + } + + public void initialize() { + GuiLogger.setTextArea(logArea); + + loadFrameConfig(); + + setup3dView(); + setupTrees(); + + setupSearchField(); + setupSearchButtons(); + + setupShowCityComboBox(); + + detailsTabPane.getSelectionModel().selectedIndexProperty() + .addListener((ov, oldI, newI) -> Platform.runLater(() -> { + if (newI.intValue() == 0 && errorView.getSelectionModel().getSelectedItem() != null) { + // the first tab is selected, meaning the error tab + // redisplay the selected error + errorView.getSelectionModel().getSelectedItem().getValue().visit(renderer); + } + })); + } + + private void setupShowCityComboBox() { + showCityObjectsCombo.getItems().addAll(Localization.getText("MainWindow.all"), + Localization.getText("MainWindow.withErrors")); + showCityObjectsCombo.getSelectionModel().selectFirst(); + + showCityObjectsCombo.setCellFactory(param -> new ListCell() { + @Override + public void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + if (item != null) { + setText(item); + if (item.contentEquals(Localization.getText("MainWindow.withErrors"))) { + setTextFill(Color.RED); + } + } else { + setText(null); + } + } + }); + + filterChangeListener = setupFilterSelectionListener(); + showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener); + } + + public void resetFilterComboBox() { + showCityObjectsCombo.getSelectionModel().selectedIndexProperty().removeListener(filterChangeListener); + showCityObjectsCombo.getSelectionModel().selectFirst(); + showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener); + } + + private ChangeListener setupFilterSelectionListener() { + return (obs, oldV, newV) -> controller.errorFilterIndexChanged(newV); + } + + private void setupSearchButtons() { + searchBtn.setOnAction(ae -> controller.searchFeature(searchField.getText(), selectedTab)); + clearBtn.setOnAction(ae -> { + if (searchField.getText().isEmpty()) { + // do not reset search if nothing has bee searched + return; + } + controller.resetSearch(selectedTab); + searchField.setText(""); + }); + } + + private void setupSearchField() { + searchField.textProperty().addListener((obs, oldV, newV) -> { + if (newV.isEmpty()) { + controller.resetSearch(selectedTab); + } + }); + featurePane.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) -> { + if (!searchField.getText().isEmpty()) { + resetSearchBar(); + controller.resetSearch(selectedTab); + } + int index = newV.intValue(); + switch (index) { + case 0: + selectedTab = FeatureType.BUILDING; + break; + case 1: + selectedTab = FeatureType.VEGETATION; + break; + case 2: + selectedTab = FeatureType.TRANSPORTATION; + break; + case 3: + selectedTab = FeatureType.BRIDGE; + break; + case 4: + selectedTab = FeatureType.WATER; + break; + case 5: + selectedTab = FeatureType.LAND; + break; + default: + throw new IllegalStateException("Unknown tab index: " + index); + } + }); + } + + public void resetSearchBar() { + searchField.setText(""); + } + + private void loadFrameConfig() { + stage.setMaximized(Boolean.valueOf(Settings.get(Settings.MAXIMIZED, Boolean.FALSE.toString()))); + stage.maximizedProperty() + .addListener((obs, oldV, newV) -> Settings.set(Settings.MAXIMIZED, Boolean.toString(newV))); + + String widthString = Settings.get(Settings.FRAME_WIDTH); + if (widthString != null) { + stage.setWidth(Double.parseDouble(widthString)); + } + stage.widthProperty().addListener( + (obs, oldV, newV) -> Settings.set(Settings.FRAME_WIDTH, Double.toString(newV.doubleValue()))); + + String heightString = Settings.get(Settings.FRAME_HEIGHT); + if (heightString != null) { + stage.setHeight(Double.parseDouble(heightString)); + } + stage.heightProperty().addListener( + (obs, oldV, newV) -> Settings.set(Settings.FRAME_HEIGHT, Double.toString(newV.doubleValue()))); + } + + public void showExceptionDialog(Throwable e) { + if (exceptionDialog == null) { + exceptionDialog = new ExceptionDialog(); + } + exceptionDialog.show(e); + } + + private void setupTrees() { + setupSelectListener(errorView); + errorView.setRoot(new TreeItem<>()); + errorView.setCellFactory(param -> new RenderableTreeCell()); + + buildingsView.setShowRoot(true); + setupSelectListener(buildingsView); + buildingsView.setCellFactory(param -> new RenderableTreeCell()); + ContextMenu cMenu = new ContextMenu(); + MenuItem mi = new MenuItem(Localization.getText("MainWindow.export")); + mi.setOnAction(ea -> { + Renderable render = buildingsView.getSelectionModel().getSelectedItem().getValue(); + if (render instanceof BuildingNode) { + BuildingNode node = (BuildingNode) render; + controller.export(node.getBuilding()); + } + }); + cMenu.getItems().add(mi); + + MenuItem deleteMi = new MenuItem("Delete"); + deleteMi.setOnAction(ae -> controller.delete(buildingsView.getSelectionModel().getSelectedItem())); + cMenu.getItems().add(deleteMi); + + buildingsView.setContextMenu(cMenu); + + vegetationView.setShowRoot(true); + setupSelectListener(vegetationView); + vegetationView.setCellFactory(param -> new RenderableTreeCell()); + + transView.setShowRoot(true); + setupSelectListener(transView); + transView.setCellFactory(param -> new RenderableTreeCell()); + + bridgeView.setShowRoot(true); + setupSelectListener(bridgeView); + bridgeView.setCellFactory(param -> new RenderableTreeCell()); + + waterView.setShowRoot(true); + setupSelectListener(waterView); + waterView.setCellFactory(param -> new RenderableTreeCell()); + + terrainView.setShowRoot(true); + setupSelectListener(terrainView); + terrainView.setCellFactory(param -> new RenderableTreeCell()); + + setupSelectListener(vertexView); + vertexView.setRoot(new TreeItem<>()); + vertexView.setCellFactory(param -> new RenderableTreeCell()); + + setupSelectListener(polygonView); + polygonView.setRoot(new TreeItem<>()); + polygonView.setCellFactory(param -> new RenderableTreeCell()); + + setupSelectListener(edgeView); + edgeView.setRoot(new TreeItem<>()); + edgeView.setCellFactory(param -> new RenderableTreeCell()); + } + + private void setupSelectListener(TreeView view) { + view.getSelectionModel().selectedItemProperty().addListener((obs, oldI, newI) -> { + if (newI != null) { + newI.getValue().visit(renderer); + } + }); + } + + private void setup3dView() { + Group root = new Group(); + geomScene = new SubScene(root, 500, 300, true, SceneAntialiasing.BALANCED); + geomScene.heightProperty().bind(meshView.heightProperty()); + geomScene.widthProperty().bind(meshView.widthProperty()); + geomScene.setFill(Color.AZURE); + meshView.getChildren().add(geomScene); + + geomScene.addEventFilter(MouseEvent.MOUSE_PRESSED, me -> { + clickStart[0] = me.getScreenX(); + clickStart[1] = me.getScreenY(); + }); + + geomScene.addEventFilter(MouseEvent.MOUSE_RELEASED, me -> { + if (Math.abs(clickStart[0] - me.getScreenX()) > 3 || Math.abs(clickStart[1] - me.getScreenY()) > 3) { + // skip when mouse moved too much + return; + } + Node node = me.getPickResult().getIntersectedNode(); + if (node != null) { + Object o = node.getUserData(); + if (o instanceof ClickDispatcher) { + ClickDispatcher cd = (ClickDispatcher) o; + cd.click(me, clickHandler); + } + } + }); + + world = new Group(); + root.getChildren().add(world); + meshGroup = new Group(); + world.getChildren().add(meshGroup); + + AmbientLight al = new AmbientLight(Color.WHITE); + root.getChildren().add(al); + + buildCamera(); + cameraXRotation.setAxis(Rotate.X_AXIS); + cameraZRotation.setAxis(Rotate.Z_AXIS); + world.getTransforms().add(cameraXRotation); + world.getTransforms().add(cameraZRotation); + root.getChildren().add(camera); + geomScene.setCamera(camera); + + setupMeshViewControls(); + } + + private void setupMeshViewControls() { + meshView.setOnMousePressed(me -> { + if (me.getButton() == MouseButton.PRIMARY) { + dragX = me.getScreenX(); + dragY = me.getScreenY(); + } + }); + + meshView.setOnScroll(se -> { + if (se.getDeltaY() < 0) { + translateZ += translateZ * 0.05; + } else { + translateZ -= translateZ * 0.05; + } + camera.setTranslateZ(translateZ); + highlightController.changeScaling(translateZ); + }); + + meshView.setOnMouseDragged(me -> { + if (me.getButton() == MouseButton.PRIMARY) { + double deltaX = me.getScreenX() - dragX; + double deltaY = me.getScreenY() - dragY; + dragX = me.getScreenX(); + dragY = me.getScreenY(); + + cameraXRot += (deltaX / 3d) % 360; + cameraYRot += (deltaY / 3d) % 360; + + cameraZRotation.setAngle(cameraXRot); + cameraXRotation.setAngle(cameraYRot); + } + }); + } + + private void buildCamera() { + camera = new PerspectiveCamera(true); + camera.setNearClip(0.1); + camera.setFarClip(10000d); + camera.setTranslateZ(translateZ); + cameraZRotation.setAngle(cameraXRot); + cameraXRotation.setAngle(cameraYRot); + } + + public void addFileNameToTitle(String fileName) { + String version = Localization.getText(Localization.VERSION); + stage.setTitle("CityDoctor " + version + " - " + fileName); + } + + public TreeView getBuildingsView() { + return buildingsView; + } + + public TreeView getVegetationView() { + return vegetationView; + } + + public TreeView getTransportationView() { + return transView; + } + + public TreeView getBridgeView() { + return bridgeView; + } + + public TreeView getWaterView() { + return waterView; + } + + public TreeView getTerrainView() { + return terrainView; + } + + public TreeView getPolygonsView() { + return polygonView; + } + + public TreeView getEdgeView() { + return edgeView; + } + + public TreeView getVertexView() { + return vertexView; + } + + public Button getCheckButton() { + return mainToolBar.getCheckButton(); + } + + public ToggleButton getGridButton() { + return mainToolBar.getGridButton(); + } + + public Group getMeshGroup() { + return meshGroup; + } + + public ToggleButton getCullingButton() { + return mainToolBar.getCullingButton(); + } + + public Button getWriteReportButton() { + return mainToolBar.getWriteReportButton(); + } + + public TreeView getErrorTree() { + return errorView; + } + + public Stage getMainStage() { + return stage; + } + + public void unselectEverything() { + buildingsView.getSelectionModel().clearSelection(); + vegetationView.getSelectionModel().clearSelection(); + transView.getSelectionModel().clearSelection(); + waterView.getSelectionModel().clearSelection(); + terrainView.getSelectionModel().clearSelection(); + bridgeView.getSelectionModel().clearSelection(); + polygonView.getSelectionModel().clearSelection(); + edgeView.getSelectionModel().clearSelection(); + vertexView.getSelectionModel().clearSelection(); + errorView.getSelectionModel().clearSelection(); + } + + public ToggleButton getLod1Btn() { + return mainToolBar.getLod1Btn(); + } + + public ToggleButton getLod2Btn() { + return mainToolBar.getLod2Btn(); + } + + public ToggleButton getLod3Btn() { + return mainToolBar.getLod3Btn(); + } + + public ToggleButton getLod4Btn() { + return mainToolBar.getLod4Btn(); + } + + public Button getWorldBtn() { + return mainToolBar.getWorldBtn(); + } + + public Button getSaveBtn() { + return mainToolBar.getSaveBtn(); + } + + public SplitPane getMainContainer() { + return mainContainer; + } + + public ListView getGlobalErrorsView() { + return globalErrorsView; + } + + public void takeViewScreenshot() throws IOException { + WritableImage snapshot = geomScene.snapshot(null, null); + File outputFile = new File("img.png"); + BufferedImage bImage = SwingFXUtils.fromFXImage(snapshot, null); + ImageIO.write(bImage, "png", outputFile); + } + + public void zoomOutForBoundingBox(BoundingBox b) { + double longestSide = b.getDiagonalLength() * 0.4; + double d = longestSide / Math.tan(Math.toRadians(30) / 2); + translateZ = -d; + camera.setTranslateZ(translateZ); + highlightController.changeScaling(-translateZ); + } + + public MainToolBar getMainToolbar() { + return mainToolBar; + } + + public void clearHighlights() { + highlightController.clearHighlights(); + } + + public CityDoctorController getController() { + return controller; + } + + public Button getOpenBtn() { + return mainToolBar.getOpenBtn(); + } + + public VertexClickHandler getClickHandler() { + return clickHandler; + } + + public FeatureType getSelectedTab() { + return selectedTab; + } +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ModelProvider.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ModelProvider.java new file mode 100644 index 0000000..83d7be1 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ModelProvider.java @@ -0,0 +1,17 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; + +public class ModelProvider { + + private CityDoctorController controller; + + public ModelProvider(CityDoctorController controller) { + this.controller = controller; + } + + public CityDoctorModel getModel() { + return controller.getModel(); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/OpenFileDialog.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/OpenFileDialog.java new file mode 100644 index 0000000..ccd3d58 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/OpenFileDialog.java @@ -0,0 +1,204 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.File; +import java.io.IOException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import de.hft.stuttgart.citydoctor2.utils.Localization; +import javafx.application.Platform; +import javafx.event.Event; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressBar; +import javafx.scene.control.TextField; +import javafx.scene.control.TextFormatter; +import javafx.scene.control.TitledPane; +import javafx.scene.image.Image; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.Window; + +public class OpenFileDialog { + + private static final Logger logger = LogManager.getLogger(OpenFileDialog.class); + + private Stage stage; + + @FXML + private Button loadBtn; + + @FXML + private Button cancelBtn; + + @FXML + private Button selectBtn; + + @FXML + private TextField precisionField; + + @FXML + private TextField pathField; + + @FXML + private ProgressBar progress; + + @FXML + private CheckBox useValidationBox; + + @FXML + private Label fileLabel; + + @FXML + private TitledPane settingsPane; + + @FXML + private Label roundingPlacesLabel; + + @FXML + private Label xmlValidationLabel; + + @FXML + private Label lowMemoryLabel; + + @FXML + private CheckBox lowMemoryBox; + + private CityDoctorController controller; + private ExceptionDialog exDialog; + private FileChooser fc; + + public OpenFileDialog(Window parent, CityDoctorController controller) throws IOException { + FXMLLoader loader = new FXMLLoader(OpenFileDialog.class.getResource("OpenFileDialog.fxml")); + loader.setController(this); + VBox box = loader.load(); + this.controller = controller; + 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("Open File"); + stage.getScene().addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent event) -> { + if (event.getCode() == KeyCode.ESCAPE) { + stage.close(); + } + }); + } + + public void initialize() { + cancelBtn.setOnAction(ae -> stage.close()); + setupPrecisionField(); + setupLoadButton(); + setupSelectButton(); + applyLanguageToControls(); + } + + private void applyLanguageToControls() { + fileLabel.setText(Localization.getText("OpenFileDialog.fileLabel")); + selectBtn.setText(Localization.getText("OpenFileDialog.selectBtn")); + loadBtn.setText(Localization.getText("OpenFileDialog.loadBtn")); + settingsPane.setText(Localization.getText("OpenFileDialog.settingsPane")); + roundingPlacesLabel.setText(Localization.getText("OpenFileDialog.roundingPlacesLabel")); + xmlValidationLabel.setText(Localization.getText("OpenFileDialog.xmlValidationLabel")); + cancelBtn.setText(Localization.getText("OpenFileDialog.cancelBtn")); + lowMemoryLabel.setText(Localization.getText("OpenFileDialog.lowMemoryLabel")); + } + + private void setupSelectButton() { + selectBtn.setOnAction(ae -> { + if (fc == null) { + fc = new FileChooser(); + fc.setTitle(Localization.getText("OpenFileDialog.select")); + 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, "")); + if (dir.exists() && dir.isDirectory()) { + fc.setInitialDirectory(dir); + } else { + String userDir = System.getProperty("user.dir"); + Settings.set(Settings.LAST_OPEN_FOLDER, userDir); + fc.setInitialDirectory(new File(userDir)); + } + File f = fc.showOpenDialog(stage); + if (f != null) { + Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent()); + pathField.setText(f.getAbsolutePath()); + } + }); + } + + private void setupLoadButton() { + loadBtn.setOnAction(ae -> { + int numberOfRoundingPlaces = Integer.parseInt(precisionField.getText()); + boolean useValidation = useValidationBox.isSelected(); + boolean lowMemory = lowMemoryBox.isSelected(); + String path = pathField.getText(); + cancelBtn.setDisable(true); + loadBtn.setDisable(true); + pathField.setDisable(true); + selectBtn.setDisable(true); + stage.setOnCloseRequest(Event::consume); + Thread t = new Thread(() -> { + try { + controller.loadCityGml(path, numberOfRoundingPlaces, progress::setProgress, useValidation, + lowMemory); + Platform.runLater(() -> stage.close()); + } catch (Exception e) { + if (logger.isErrorEnabled()) { + logger.error(Localization.getText("OpenFileDialog.loadFailed"), e); + } + Platform.runLater(() -> { + if (exDialog == null) { + exDialog = new ExceptionDialog(); + } + exDialog.show(e); + }); + } finally { + selectBtn.setDisable(false); + pathField.setDisable(false); + cancelBtn.setDisable(false); + loadBtn.setDisable(false); + stage.setOnCloseRequest(null); + } + }); + t.start(); + }); + } + + private void setupPrecisionField() { + TextFormatter formatter = new TextFormatter<>(change -> { + if (!change.isContentChange()) { + return change; + } + + String text = change.getControlNewText(); + try { + Integer.parseInt(text); + return change; + } catch (NumberFormatException e) { + return null; + } + }); + precisionField.setTextFormatter(formatter); + } + + public void show() { + Platform.runLater(() -> { + progress.setProgress(0d); + stage.showAndWait(); + }); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/PolygonClickDispatcher.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/PolygonClickDispatcher.java new file mode 100644 index 0000000..7bbecfd --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/PolygonClickDispatcher.java @@ -0,0 +1,19 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import de.hft.stuttgart.citydoctor2.datastructure.Polygon; +import javafx.scene.input.MouseEvent; + +public class PolygonClickDispatcher implements ClickDispatcher { + + private Polygon p; + + public PolygonClickDispatcher(Polygon p) { + this.p = p; + } + + @Override + public void click(MouseEvent me, ClickHandler handler) { + handler.onPolygonClick(p, me); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java new file mode 100644 index 0000000..91d542d --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java @@ -0,0 +1,780 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.Checkable; +import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; +import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; +import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox; +import de.hft.stuttgart.citydoctor2.datastructure.BridgeConstructiveElement; +import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject; +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.datastructure.Installation; +import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; +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.Edge; +import de.hft.stuttgart.citydoctor2.datastructure.Geometry; +import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; +import de.hft.stuttgart.citydoctor2.datastructure.Lod; +import de.hft.stuttgart.citydoctor2.datastructure.Opening; +import de.hft.stuttgart.citydoctor2.datastructure.Polygon; +import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject; +import de.hft.stuttgart.citydoctor2.datastructure.TinObject; +import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject; +import de.hft.stuttgart.citydoctor2.datastructure.Vegetation; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.datastructure.WaterObject; +import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter; +import de.hft.stuttgart.citydoctor2.gui.tree.EdgeNode; +import de.hft.stuttgart.citydoctor2.gui.tree.ErrorItemVisitor; +import de.hft.stuttgart.citydoctor2.gui.tree.ErrorNode; +import de.hft.stuttgart.citydoctor2.gui.tree.LinearRingNode; +import de.hft.stuttgart.citydoctor2.gui.tree.PolygonNode; +import de.hft.stuttgart.citydoctor2.gui.tree.Renderable; +import de.hft.stuttgart.citydoctor2.gui.tree.VertexNode; +import de.hft.stuttgart.citydoctor2.math.Triangle3d; +import javafx.application.Platform; +import javafx.scene.control.TreeItem; +import javafx.scene.paint.Color; +import javafx.scene.shape.CullFace; +import javafx.scene.shape.DrawMode; + +public class Renderer { + + private static final Logger logger = LogManager.getLogger(Renderer.class); + + private TriangulatedGeometry currentTriGeom; + private Geometry currentGeometry; + + private CullFace currentCulling = CullFace.BACK; + private DrawMode currentDrawMode = DrawMode.FILL; + + private MainWindow mainWindow; + private HighlightController highlightController; + private ListErrorVisitor errVisitor; + + private LoadingInfoDialog loadingDialog; + + private List lodFilters = new ArrayList<>(); + + private Runnable refresher; + private Runnable errorUpdater; + + public Renderer(MainWindow mainWindow, HighlightController highlightController) throws IOException { + this.mainWindow = mainWindow; + this.highlightController = highlightController; + loadingDialog = new LoadingInfoDialog(mainWindow.getMainStage()); + errVisitor = new ListErrorVisitor(highlightController); + setupLodFilters(); + } + + private void setupLodFilters() { + lodFilters.add(new ViewFilter() { + + @Override + public boolean useGeometry(CityObject co, Geometry geom) { + return geom.getLod() == Lod.LOD1; + } + + }); + lodFilters.add(new ViewFilter() { + + @Override + public boolean useGeometry(CityObject co, Geometry geom) { + return geom.getLod() == Lod.LOD2; + } + + }); + lodFilters.add(new ViewFilter() { + + @Override + public boolean useGeometry(CityObject co, Geometry geom) { + return geom.getLod() == Lod.LOD3; + } + + }); + lodFilters.add(new ViewFilter() { + + @Override + protected boolean useGeometry(CityObject co, Geometry geom) { + return geom.getLod() == Lod.LOD0; + } + + }); + lodFilters.add(new ViewFilter() { + + @Override + public boolean useGeometry(CityObject co, Geometry geom) { + return geom.getLod() == Lod.LOD4; + } + }); + } + + public void enableLod1() { + lodFilters.get(0).enable(); + if (refresher != null) { + refresher.run(); + } + } + + public void disableLod1() { + lodFilters.get(0).disable(); + if (refresher != null) { + refresher.run(); + } + } + + public void enableLod2() { + lodFilters.get(1).enable(); + if (refresher != null) { + refresher.run(); + } + } + + public void disableLod2() { + lodFilters.get(1).disable(); + if (refresher != null) { + refresher.run(); + } + } + + public void enableLod3() { + lodFilters.get(2).enable(); + if (refresher != null) { + refresher.run(); + } + } + + public void disableLod3() { + lodFilters.get(2).disable(); + if (refresher != null) { + refresher.run(); + } + } + + public void enableLod4() { + lodFilters.get(3).enable(); + if (refresher != null) { + refresher.run(); + } + } + + public void disableLod4() { + lodFilters.get(3).disable(); + if (refresher != null) { + refresher.run(); + } + } + + public void render(Building building) { + refresher = () -> { + Set setupBuildingPolygons = setupBuildingPolygons(building); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingPolygons)); + render(setupBuildingPolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(building); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private Set setupBuildingPolygons(Building b) { + Set polygons = new HashSet<>(); + addPolygons(b, polygons); + for (BoundarySurface bs : b.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + for (Installation bi : b.getBuildingInstallations()) { + addPolygons(bi, polygons); + for (BoundarySurface bs : bi.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + } + for (BuildingPart bp : b.getBuildingParts()) { + polygons.addAll(setupBuildingPartPolygons(bp)); + } + return polygons; + } + + public void render(BuildingPart bp) { + refresher = () -> { + Set setupBuildingPartPolygons = setupBuildingPartPolygons(bp); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingPartPolygons)); + render(setupBuildingPartPolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(bp); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private Set setupBuildingPartPolygons(BuildingPart bp) { + Set polygons = new HashSet<>(); + addPolygons(bp, polygons); + for (BoundarySurface bs : bp.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + for (Installation bi : bp.getBuildingInstallations()) { + addPolygons(bi, polygons); + for (BoundarySurface bs : bi.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + } + return polygons; + } + + public void render(BridgeObject bridge) { + refresher = () -> { + Set setupBridgePolygons = setupBridgePolygons(bridge); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBridgePolygons)); + render(setupBridgePolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(bridge); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private Set setupBridgePolygons(BridgeObject bridge) { + Set polygons = new HashSet<>(); + addPolygons(bridge, polygons); + for (BoundarySurface bs : bridge.getBoundarySurfaces()) { + addPolygons(bs, polygons); + } + for (BridgeConstructiveElement consElement : bridge.getConstructiveElements()) { + addPolygons(consElement, polygons); + for (BoundarySurface bs : consElement.getBoundarySurfaces()) { + addPolygons(bs, polygons); + } + } + for (Installation inst : bridge.getBridgeInstallations()) { + addPolygons(inst, polygons); + for (BoundarySurface bs : inst.getBoundarySurfaces()) { + addPolygons(bs, polygons); + } + } + return polygons; + } + + public void render(CityObject co) { + refresher = () -> { + Set setupCityObjectPolygons = setupCityObjectPolygons(co); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons)); + render(setupCityObjectPolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(co); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + public void render(ReliefObject relief) { + refresher = () -> { + Set setupCityObjectPolygons = setupReliefPolygons(relief); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons)); + render(setupCityObjectPolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(relief); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private Set setupReliefPolygons(ReliefObject relief) { + Set polygons = new HashSet<>(); + addPolygons(relief, polygons); + for (TinObject tin : relief.getComponents()) { + addPolygons(tin, polygons); + } + return polygons; + } + + public void render(Installation bi) { + refresher = () -> { + Set setupCityObjectPolygons = setupBuildingInstallationPolygons(bi); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons)); + render(setupCityObjectPolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(bi); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private Set setupBuildingInstallationPolygons(Installation bi) { + Set polygons = new HashSet<>(); + addPolygons(bi, polygons); + for (BoundarySurface bs : bi.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + return polygons; + } + + public void render(BoundarySurface bs) { + refresher = () -> { + Set setupBoundarySurfacePolygons = setupBoundarySurfacePolygons(bs); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBoundarySurfacePolygons)); + render(setupBoundarySurfacePolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(bs); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private Set setupBoundarySurfacePolygons(BoundarySurface bs) { + Set polygons = new HashSet<>(); + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + return polygons; + } + + private Set setupCityObjectPolygons(CityObject co) { + Set polygons = new HashSet<>(); + addPolygons(co, polygons); + return polygons; + } + + private void addPolygons(CityObject co, Set polygons) { + for (Geometry geom : co.getGeometries()) { + boolean used = false; + for (ViewFilter filter : lodFilters) { + if (filter.allowedToUse(co, geom)) { + used = true; + break; + } + } + if (used) { + addConcretePolygons(polygons, geom); + } + } + } + + private void displayErrors(Checkable c) { + if (!c.isValidated()) { + return; + } + List errors = new ArrayList<>(); + c.collectContainedErrors(errors); + + // filter out duplicate errors (polygon can be contained in multiple geometries) + Set errorSet = new HashSet<>(errors); + for (CheckError err : errorSet) { + ErrorNode node = new ErrorNode(err); + TreeItem errItem = new TreeItem<>(node); + ErrorItemVisitor visitor = new ErrorItemVisitor(errItem); + err.accept(visitor); + mainWindow.getErrorTree().getRoot().getChildren().add(errItem); + + } + } + + public void render(Geometry geom) { + refresher = () -> { + Platform.runLater(this::clearGeometryTrees); + currentTriGeom = TriangulatedGeometry.of(geom); + if (geom.getEdges() == null && currentGeometry != null) { + // if there are no edges available low memory mode is enabled + // clear the old geometry of all meta information + currentGeometry.clearMetaInformation(); + } + errVisitor.setGeometry(currentTriGeom); + Platform.runLater(() -> { + setupRenderState(); + if (geom.getEdges() == null) { + // create edges and vertices so they can be listed in the gui + geom.prepareForChecking(); + // remember the geometry, so it can be cleared if another is displayed + currentGeometry = geom; + } + addGeometryDataToView(geom); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(geom.getPolygons())); + errorUpdater = () -> displayErrors(geom); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private void addGeometryDataToView(Geometry geom) { + for (Polygon p : geom.getPolygons()) { + addPolygonToView(p); + } + for (Edge e : geom.getEdges()) { + addEdgeToView(e); + } + for (Vertex v : geom.getVertices()) { + addVertexToView(v); + } + } + + private void addVertexToView(Vertex v) { + Renderable cf = new VertexNode(v); + TreeItem ti = new TreeItem<>(cf); + mainWindow.getVertexView().getRoot().getChildren().add(ti); + } + + private void addEdgeToView(Edge e) { + EdgeNode cf = new EdgeNode(e); + TreeItem ti = new TreeItem<>(cf); + mainWindow.getEdgeView().getRoot().getChildren().add(ti); + } + + private void addPolygonToView(Polygon p) { + CheckStatus cs = determineCheckStatus(p); + PolygonNode cf = new PolygonNode(p, cs); + TreeItem ti = new TreeItem<>(cf); + mainWindow.getPolygonsView().getRoot().getChildren().add(ti); + + // add linear rings + CheckStatus csRing = determineCheckStatus(p.getExteriorRing()); + Renderable ccRing = new LinearRingNode(p.getExteriorRing(), csRing); + TreeItem tiRing = new TreeItem<>(ccRing); + ti.getChildren().add(tiRing); + + for (Vertex v : p.getExteriorRing().getVertices()) { + VertexNode vn = new VertexNode(v); + TreeItem tiV = new TreeItem<>(vn); + tiRing.getChildren().add(tiV); + } + + for (LinearRing lr : p.getInnerRings()) { + CheckStatus csInteriorRing = determineCheckStatus(lr); + Renderable ccInteriorRing = new LinearRingNode(lr, csInteriorRing); + TreeItem tiInteriorRing = new TreeItem<>(ccInteriorRing); + ti.getChildren().add(tiInteriorRing); + for (Vertex v : lr.getVertices()) { + VertexNode vn = new VertexNode(v); + TreeItem tiV = new TreeItem<>(vn); + tiRing.getChildren().add(tiV); + } + } + } + + private CheckStatus determineCheckStatus(Checkable c) { + if (!c.isValidated()) { + return CheckStatus.NOT_CHECKED; + } + if (c.containsAnyError()) { + return CheckStatus.ERROR; + } + return CheckStatus.OK; + } + + private void render(Collection polygons) { + Platform.runLater(this::clearGeometryTrees); + currentTriGeom = TriangulatedGeometry.of(polygons); + errVisitor.setGeometry(currentTriGeom); + setupRenderState(); + } + + private void clearGeometryTrees() { + highlightController.clearHighlights(); + mainWindow.getErrorTree().getRoot().getChildren().clear(); + mainWindow.getPolygonsView().getRoot().getChildren().clear(); + mainWindow.getEdgeView().getRoot().getChildren().clear(); + mainWindow.getVertexView().getRoot().getChildren().clear(); + } + + public void renderBuildings(List objects) { + errorUpdater = null; + refresher = () -> { + Platform.runLater(() -> { + loadingDialog.show(); + clearGeometryTrees(); + }); + Thread t = new Thread(() -> { + Set polygons = new HashSet<>(); + for (Building b : objects) { + collectPolygons(polygons, b); + for (BuildingPart bp : b.getBuildingParts()) { + collectPolygons(polygons, bp); + } + } + currentTriGeom = TriangulatedGeometry.of(polygons); + errVisitor.setGeometry(currentTriGeom); + Platform.runLater(() -> { + setupRenderState(); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons)); + loadingDialog.hide(); + }); + }); + t.setUncaughtExceptionHandler((thread, e) -> { + Platform.runLater(() -> loadingDialog.hide()); + logger.catching(e); + }); + t.start(); + }; + refresher.run(); + } + + public void render(CityDoctorModel model) { + errorUpdater = null; + refresher = () -> { + Platform.runLater(() -> { + loadingDialog.show(); + clearGeometryTrees(); + }); + Thread t = new Thread(() -> { + currentTriGeom = TriangulatedGeometry.of(model, lodFilters); + errVisitor.setGeometry(currentTriGeom); + Platform.runLater(() -> { + setupRenderState(); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(model)); + loadingDialog.hide(); + }); + }); + t.setUncaughtExceptionHandler((thread, e) -> { + Platform.runLater(() -> loadingDialog.hide()); + logger.catching(e); + }); + t.start(); + }; + refresher.run(); + } + + public void renderVegetation(List vegetation) { + renderCityObjects(vegetation, Color.LIGHTGREEN); + } + + public void renderTransportation(List transportation) { + renderCityObjects(transportation, Color.YELLOW); + } + + public void renderBridges(List bridges) { + renderCityObjects(bridges, Color.CORAL); + } + + public void renderWater(List water) { + renderCityObjects(water, Color.LIGHTSKYBLUE); + } + + public void renderTerrain(List land) { + renderLandObjects(land, Color.BROWN); + } + + private void renderLandObjects(List cos, Color baseColor) { + errorUpdater = null; + refresher = () -> { + Platform.runLater(() -> { + loadingDialog.show(); + clearGeometryTrees(); + }); + Thread t = new Thread(() -> { + Set polygons = new HashSet<>(); + for (CityObject co : cos) { + addPolygons(co, polygons); + if (co instanceof ReliefObject) { + ReliefObject relief = (ReliefObject) co; + for (TinObject tin : relief.getComponents()) { + addPolygons(tin, polygons); + } + } + } + currentTriGeom = TriangulatedGeometry.of(polygons, baseColor); + errVisitor.setGeometry(currentTriGeom); + Platform.runLater(() -> { + setupRenderState(); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons)); + loadingDialog.hide(); + }); + }); + t.setUncaughtExceptionHandler((thread, e) -> Platform.runLater(() -> loadingDialog.hide())); + t.start(); + }; + refresher.run(); + } + + public void renderCityObjects(List cos, Color baseColor) { + errorUpdater = null; + refresher = () -> { + Platform.runLater(() -> { + loadingDialog.show(); + clearGeometryTrees(); + }); + Thread t = new Thread(() -> { + Set polygons = new HashSet<>(); + for (CityObject co : cos) { + addPolygons(co, polygons); + } + currentTriGeom = TriangulatedGeometry.of(polygons, baseColor); + errVisitor.setGeometry(currentTriGeom); + Platform.runLater(() -> { + setupRenderState(); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons)); + loadingDialog.hide(); + }); + }); + t.setUncaughtExceptionHandler((thread, e) -> Platform.runLater(() -> loadingDialog.hide())); + t.start(); + }; + refresher.run(); + } + + private void collectPolygons(Set polygons, AbstractBuilding ab) { + addPolygons(ab, polygons); + for (Installation bi : ab.getBuildingInstallations()) { + addPolygons(bi, polygons); + for (BoundarySurface bs : bi.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening o : bs.getOpenings()) { + addPolygons(o, polygons); + } + } + } + for (BoundarySurface bs : ab.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening o : bs.getOpenings()) { + addPolygons(o, polygons); + } + } + } + + private void addConcretePolygons(Set polygons, Geometry geom) { + for (Polygon p : geom.getPolygons()) { + polygons.add(p.getOriginal()); + } + } + + private void setupRenderState() { + currentTriGeom.setCullFace(currentCulling); + currentTriGeom.setDrawMode(currentDrawMode); + Platform.runLater(() -> { + mainWindow.getMeshGroup().getChildren().clear(); + mainWindow.getMeshGroup().getChildren().addAll(currentTriGeom.getMeshes()); + }); + mainWindow.getGridButton().setDisable(false); + mainWindow.getCullingButton().setDisable(false); + } + + public void showWireFrame(boolean show) { + if (currentTriGeom != null) { + if (show) { + currentDrawMode = DrawMode.LINE; + } else { + currentDrawMode = DrawMode.FILL; + } + currentTriGeom.setDrawMode(currentDrawMode); + } + } + + public void enableCulling(boolean enable) { + if (currentTriGeom != null) { + if (enable) { + currentCulling = CullFace.BACK; + } else { + currentCulling = CullFace.NONE; + } + currentTriGeom.setCullFace(currentCulling); + } + } + + public void clearCurrentRender() { + // don't render anything + Platform.runLater(() -> { + mainWindow.getMeshGroup().getChildren().clear(); + clearGeometryTrees(); + }); + } + + public void highlight(Polygon p) { + highlightController.highlight(p, currentTriGeom); + } + + public void highlight(LinearRing lr) { + highlightController.highlight(lr, currentTriGeom); + } + + public void highlight(Edge e) { + highlightController.highlight(e, currentTriGeom); + } + + public void highlight(Vertex v) { + highlightController.highlight(v, currentTriGeom); + } + + public void highlight(List highlightedRings) { + highlightController.highlight(highlightedRings, currentTriGeom); + } + + public void highlightEdges(List edges) { + highlightController.highlightEdges(edges, currentTriGeom); + } + + public void highlightPolygons(List> components) { + highlightController.highlightPolygons(components, currentTriGeom); + } + + public void addHighlight(Vertex vertex, Color c) { + highlightController.addHighlight(vertex, currentTriGeom, c); + } + + public void highlight(CheckError err) { + err.accept(errVisitor); + } + + public void refresh() { + if (refresher != null) { + refresher.run(); + } + } + + public void updateErrors() { + if (errorUpdater != null) { + errorUpdater.run(); + } + } + + public void clearHighlights() { + highlightController.clearHighlights(); + } + + public void addHighlight(Polygon p) { + highlightController.addHighlight(p, currentTriGeom); + } + + public void addHighlight(Triangle3d t) { + highlightController.highlight(t, currentTriGeom); + } + + public void reset() { + errorUpdater = null; + refresher = null; + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Settings.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Settings.java new file mode 100644 index 0000000..3b87dc0 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Settings.java @@ -0,0 +1,66 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Properties; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class Settings { + + private static Logger logger = LogManager.getLogger(Settings.class); + + public static final String LAST_OPEN_FOLDER = "lastOpenFolder"; + public static final String MAXIMIZED = "maximized"; + public static final String FRAME_HEIGHT = "frameHeight"; + public static final String FRAME_WIDTH = "frameWidth"; + public static final String FRAME_X = "frameX"; + public static final String FRAME_Y = "frameY"; + public static final String LANGUAGE = "language"; + + private static Properties props; + + static { + props = new Properties(); + + File propFile = new File("GUISettings.properties"); + if (propFile.exists()) { + try (BufferedReader bis = new BufferedReader(new FileReader(propFile))) { + props.load(bis); + } catch (IOException e) { + logger.error("Failed to load settings", e); + } + } + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try (BufferedWriter bw = new BufferedWriter(new FileWriter(propFile))) { + props.store(bw, "GUI configuration"); + } catch (IOException e) { + logger.error("Failed to save settings", e); + } + })); + } + + private Settings() { + + } + + public static String get(String name) { + return props.getProperty(name); + } + + public static void set(String name, String value) { + props.setProperty(name, value); + } + + public static String get(String name, String defaultV) { + return props.getProperty(name, defaultV); + } + + + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TableEditCell.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TableEditCell.java new file mode 100644 index 0000000..13266ab --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TableEditCell.java @@ -0,0 +1,148 @@ +/*- + * 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 . + */ +package de.hft.stuttgart.citydoctor2.gui; + +import javafx.event.Event; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableColumn.CellEditEvent; +import javafx.scene.control.TablePosition; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.util.StringConverter; + +public class TableEditCell extends TableCell { + + // Text field for editing + private final TextField textField = new TextField(); + + // Converter for converting the text in the text field to the user type, and + // vice-versa: + private final StringConverter converter; + + public TableEditCell(StringConverter converter) { + this.converter = converter; + + itemProperty().addListener((obx, oldItem, newItem) -> { + if (newItem == null) { + setText(null); + } else { + setText(converter.toString(newItem)); + } + }); + setGraphic(textField); + setContentDisplay(ContentDisplay.TEXT_ONLY); + + textField.setOnAction(evt -> commitEdit(this.converter.fromString(textField.getText()))); + textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> { + if (Boolean.FALSE.equals(isNowFocused)) { + commitEdit(this.converter.fromString(textField.getText())); + } + }); + textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + if (event.getCode() == KeyCode.ESCAPE) { + textField.setText(converter.toString(getItem())); + cancelEdit(); + event.consume(); + } else if (event.getCode() == KeyCode.RIGHT) { + getTableView().getSelectionModel().selectRightCell(); + event.consume(); + } else if (event.getCode() == KeyCode.LEFT) { + getTableView().getSelectionModel().selectLeftCell(); + event.consume(); + } else if (event.getCode() == KeyCode.UP) { + getTableView().getSelectionModel().selectAboveCell(); + event.consume(); + } else if (event.getCode() == KeyCode.DOWN) { + getTableView().getSelectionModel().selectBelowCell(); + event.consume(); + } + }); + } + + /** + * Convenience converter that does nothing (converts Strings to themselves and + * vice-versa...). + */ + public static final StringConverter IDENTITY_CONVERTER = new StringConverter() { + + @Override + public String toString(String object) { + return object; + } + + @Override + public String fromString(String string) { + return string; + } + + }; + + /** + * Convenience method for creating an EditCell for a String value. + * + * @return + */ + public static TableEditCell createStringEditCell() { + return new TableEditCell<>(IDENTITY_CONVERTER); + } + + // set the text of the text field and display the graphic + @Override + public void startEdit() { + super.startEdit(); + textField.setText(converter.toString(getItem())); + setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + textField.requestFocus(); + } + + // revert to text display + @Override + public void cancelEdit() { + super.cancelEdit(); + setContentDisplay(ContentDisplay.TEXT_ONLY); + } + + // commits the edit. Update property if possible and revert to text display + @Override + public void commitEdit(T item) { + + // This block is necessary to support commit on losing focus, because the + // baked-in mechanism + // sets our editing state to false before we can intercept the loss of focus. + // The default commitEdit(...) method simply bails if we are not editing... + if (!isEditing() && !item.equals(getItem())) { + TableView table = getTableView(); + if (table != null) { + TableColumn column = getTableColumn(); + CellEditEvent event = new CellEditEvent<>(table, + new TablePosition<>(table, getIndex(), column), TableColumn.editCommitEvent(), item); + Event.fireEvent(column, event); + } + } + + super.commitEdit(item); + + setContentDisplay(ContentDisplay.TEXT_ONLY); + } + +} \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TreeEditCell.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TreeEditCell.java new file mode 100644 index 0000000..dcc5263 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TreeEditCell.java @@ -0,0 +1,121 @@ +/*- + * 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 . + */ +package de.hft.stuttgart.citydoctor2.gui; + +import javafx.event.Event; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.TextField; +import javafx.scene.control.TreeTableCell; +import javafx.scene.control.TreeTableColumn; +import javafx.scene.control.TreeTableColumn.CellEditEvent; +import javafx.scene.control.TreeTablePosition; +import javafx.scene.control.TreeTableView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.util.StringConverter; + +public class TreeEditCell extends TreeTableCell { + + // Text field for editing + private final TextField textField = new TextField(); + + // Converter for converting the text in the text field to the user type, and + // vice-versa: + private final StringConverter converter; + + public TreeEditCell(StringConverter converter) { + this.converter = converter; + + itemProperty().addListener((obx, oldItem, newItem) -> { + if (newItem == null) { + setText(null); + } else { + setText(converter.toString(newItem)); + } + }); + setGraphic(textField); + setContentDisplay(ContentDisplay.TEXT_ONLY); + + textField.setOnAction(evt -> commitEdit(this.converter.fromString(textField.getText()))); + textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> { + if (Boolean.FALSE.equals(isNowFocused)) { + commitEdit(this.converter.fromString(textField.getText())); + } + }); + textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + if (event.getCode() == KeyCode.ESCAPE) { + textField.setText(converter.toString(getItem())); + cancelEdit(); + event.consume(); + } else if (event.getCode() == KeyCode.RIGHT) { + getTreeTableView().getSelectionModel().selectRightCell(); + event.consume(); + } else if (event.getCode() == KeyCode.LEFT) { + getTreeTableView().getSelectionModel().selectLeftCell(); + event.consume(); + } else if (event.getCode() == KeyCode.UP) { + getTreeTableView().getSelectionModel().selectAboveCell(); + event.consume(); + } else if (event.getCode() == KeyCode.DOWN) { + getTreeTableView().getSelectionModel().selectBelowCell(); + event.consume(); + } + }); + } + + // set the text of the text field and display the graphic + @Override + public void startEdit() { + super.startEdit(); + textField.setText(converter.toString(getItem())); + setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + textField.requestFocus(); + } + + // revert to text display + @Override + public void cancelEdit() { + super.cancelEdit(); + setContentDisplay(ContentDisplay.TEXT_ONLY); + } + + // commits the edit. Update property if possible and revert to text display + @Override + public void commitEdit(T item) { + + // This block is necessary to support commit on losing focus, because the + // baked-in mechanism + // sets our editing state to false before we can intercept the loss of focus. + // The default commitEdit(...) method simply bails if we are not editing... + if (!isEditing() && !item.equals(getItem())) { + TreeTableView table = getTreeTableView(); + if (table != null) { + TreeTableColumn column = getTableColumn(); + CellEditEvent event = new CellEditEvent<>(table, + new TreeTablePosition<>(table, getIndex(), column), TreeTableColumn.editCommitEvent(), item); + Event.fireEvent(column, event); + } + } + + super.commitEdit(item); + + setContentDisplay(ContentDisplay.TEXT_ONLY); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java new file mode 100644 index 0000000..2732b55 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java @@ -0,0 +1,340 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; +import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.datastructure.Installation; +import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.Geometry; +import de.hft.stuttgart.citydoctor2.datastructure.Opening; +import de.hft.stuttgart.citydoctor2.datastructure.OpeningType; +import de.hft.stuttgart.citydoctor2.datastructure.Polygon; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter; +import de.hft.stuttgart.citydoctor2.math.Triangle3d; +import de.hft.stuttgart.citydoctor2.math.UnitVector3d; +import de.hft.stuttgart.citydoctor2.math.Vector3d; +import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon; +import javafx.scene.paint.Color; +import javafx.scene.paint.PhongMaterial; +import javafx.scene.shape.CullFace; +import javafx.scene.shape.DrawMode; +import javafx.scene.shape.MeshView; +import javafx.scene.shape.TriangleMesh; +import javafx.scene.shape.VertexFormat; + +public class TriangulatedGeometry { + + private static final PhongMaterial GRID_MAT = new PhongMaterial(Color.BLACK); + + // random vector for calculating normal angles, for color determination + private static final UnitVector3d AXIS = new Vector3d(19, 0.8, 1.5).normalize(); + + private Vector3d movedBy; + private List meshes; + private List materials; + + public static TriangulatedGeometry of(Geometry geom) { + return of(geom.getPolygons()); + } + + public static TriangulatedGeometry of(Collection polygons, Color basePolygonColor) { + TriangulatedGeometry triGeom = new TriangulatedGeometry(); + triGeom.materials = new ArrayList<>(); + triGeom.meshes = new ArrayList<>(); + List points = new ArrayList<>(); + for (Polygon p : polygons) { + for (Vertex v : p.getExteriorRing().getVertices()) { + points.add(v); + } + } + triGeom.movedBy = triGeom.findCenter(points); + + addPolygonDataToTriGeom(polygons, basePolygonColor, triGeom); + return triGeom; + } + + private static void addPolygonDataToTriGeom(Collection polygons, Color basePolygonColor, + TriangulatedGeometry triGeom) { + for (Polygon p : polygons) { + TesselatedPolygon tp = p.tesselate(); + TriangleMesh triMesh = new TriangleMesh(VertexFormat.POINT_TEXCOORD); + Map indexMap = new HashMap<>(); + List vertices = new ArrayList<>(); + int index = 0; + for (Triangle3d t : tp.getTriangles()) { + index = triGeom.filterDuplicates(triMesh, indexMap, vertices, index, t.getP1()); + index = triGeom.filterDuplicates(triMesh, indexMap, vertices, index, t.getP2()); + index = triGeom.filterDuplicates(triMesh, indexMap, vertices, index, t.getP3()); + } + + for (Vector3d point : vertices) { + float x = (float) (point.getX() - triGeom.movedBy.getX()); + float y = (float) (point.getY() - triGeom.movedBy.getY()); + float z = (float) (point.getZ() - triGeom.movedBy.getZ()); + triMesh.getPoints().addAll(x, y, z); + } + triMesh.getTexCoords().addAll(0, 0); + + MeshView view = new MeshView(triMesh); + view.setUserData(new PolygonClickDispatcher(p)); + + PhongMaterial mat = triGeom.calculateMaterial(p, basePolygonColor); + triGeom.materials.add(mat); + triGeom.meshes.add(view); + } + } + + public static TriangulatedGeometry of(CityDoctorModel model, List filters) { + List points = new ArrayList<>(); + addPointsFromBuildings(model.getBuildings(), points); + addPointsFromCityObject(model.getBridges(), points); + addPointsFromCityObject(model.getLand(), points); + addPointsFromCityObject(model.getTransportation(), points); + addPointsFromCityObject(model.getVegetation(), points); + addPointsFromCityObject(model.getWater(), points); + + TriangulatedGeometry triGeom = new TriangulatedGeometry(); + triGeom.materials = new ArrayList<>(); + triGeom.meshes = new ArrayList<>(); + triGeom.movedBy = triGeom.findCenter(points); + + addPolygonDataFromBuildings(model.getBuildings(), triGeom, filters); + addPolygonDataFromCityObjects(model.getBridges(), triGeom, Color.CORAL, filters); + addPolygonDataFromCityObjects(model.getLand(), triGeom, Color.BROWN, filters); + addPolygonDataFromCityObjects(model.getTransportation(), triGeom, Color.YELLOW, filters); + addPolygonDataFromCityObjects(model.getVegetation(), triGeom, Color.LIGHTGREEN, filters); + addPolygonDataFromCityObjects(model.getWater(), triGeom, Color.LIGHTSKYBLUE, filters); + + return triGeom; + } + + private static void addPolygonDataFromBuildings(List buildings, TriangulatedGeometry triGeom, List filters) { + for (Building b : buildings) { + addPolygonData(b, triGeom, Color.WHITE, filters); + addPolygonDataFromBoundarySurfaces(b.getBoundarySurfaces(), triGeom, filters); + addPolygonDataFromCityObjects(b.getBuildingInstallations(), triGeom, Color.WHITE, filters); + for (Installation bi : b.getBuildingInstallations()) { + addPolygonDataFromCityObjects(bi.getBoundarySurfaces(), triGeom, Color.WHITE, filters); + } + for (BuildingPart bp : b.getBuildingParts()) { + addPolygonData(bp, triGeom, Color.WHITE, filters); + addPolygonDataFromBoundarySurfaces(bp.getBoundarySurfaces(), triGeom, filters); + addPolygonDataFromCityObjects(bp.getBuildingInstallations(), triGeom, Color.WHITE, filters); + } + } + } + + private static void addPolygonDataFromBoundarySurfaces(List boundarySurfaces, + TriangulatedGeometry triGeom, List filters) { + for (BoundarySurface bs : boundarySurfaces) { + addPolygonData(bs, triGeom, Color.WHITE, filters); + for (Opening o : bs.getOpenings()) { + addPolygonData(o, triGeom, Color.WHITE, filters); + } + } + } + + private static void addPolygonDataFromCityObjects(List cos, + TriangulatedGeometry triGeom, Color color, List filters) { + for (CityObject co : cos) { + addPolygonData(co, triGeom, color, filters); + } + } + + private static void addPolygonData(CityObject co, TriangulatedGeometry triGeom, Color color, List filters) { + for (Geometry geom : co.getGeometries()) { + if (isGeometryFiltered(co, geom, filters)) { + continue; + } + List polygons = new ArrayList<>(); + for (Polygon p : geom.getPolygons()) { + if (p.isLink()) { + continue; + } + polygons.add(p); + } + addPolygonDataToTriGeom(polygons, color, triGeom); + } + } + + private static boolean isGeometryFiltered(CityObject co, Geometry geom, List filters) { + for (ViewFilter filter : filters) { + if (filter.allowedToUse(co, geom)) { + return false; + } + } + return true; + } + + private static void addPointsFromBuildings(List buildings, List points) { + for (Building b : buildings) { + addPoints(b, points); + for (BuildingPart bp : b.getBuildingParts()) { + addPoints(bp, points); + addPointsFromCityObject(bp.getBoundarySurfaces(), points); + addPointsFromCityObject(bp.getBuildingInstallations(), points); + } + addPointsFromCityObject(b.getBoundarySurfaces(), points); + addPointsFromCityObject(b.getBuildingInstallations(), points); + } + } + + private static void addPointsFromCityObject(List cos, List points) { + for (CityObject co : cos) { + addPoints(co, points); + } + } + + private static void addPoints(CityObject co, List points) { + for (Geometry geom : co.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + if (p.isLink()) { + continue; + } + for (Vertex v : p.getExteriorRing().getVertices()) { + points.add(v); + } + } + } + } + + public static TriangulatedGeometry of(Collection polygons) { + return of(polygons, Color.WHITE); + } + + private int filterDuplicates(TriangleMesh triMesh, Map indexMap, List vertices, + int index, Vector3d v) { + Integer vertexIndex = indexMap.get(v); + if (vertexIndex == null) { + indexMap.put(v, index); + vertices.add(v); + vertexIndex = index; + index++; + } + triMesh.getFaces().addAll(vertexIndex, 0); + return index; + } + + private PhongMaterial calculateMaterial(Polygon p, Color baseColor) { + Vector3d normal = p.calculateNormalNormalized(); + + BoundarySurface bs = p.getPartOfSurface(); + if (bs != null) { + if (bs.getType() == BoundarySurfaceType.ROOF) { + baseColor = Color.RED; + } else if (bs.getType() == BoundarySurfaceType.GROUND) { + baseColor = Color.KHAKI; + } + } + + baseColor = determineColorDependingOnParentType(p, baseColor); + + double cos = normal.dot(AXIS); + double acos = Math.acos(cos); + // normalize to range [0.3, 0.9] + acos = acos / Math.PI; + acos = acos * 0.6 + 0.3; + + Color derivedColor = baseColor.deriveColor(0, 1.0, acos, 1.0); + return new PhongMaterial(derivedColor); + } + + private Color determineColorDependingOnParentType(Polygon p, Color baseColor) { + p = p.getOriginal(); + Polygon p1 = p.getLinkedFromPolygon(); + baseColor = changeBaseColorIfPolygonHasOpeningParent(p1, baseColor); + baseColor = changeBaseColorIfPolygonHasOpeningParent(p, baseColor); + return baseColor; + } + + private Color changeBaseColorIfPolygonHasOpeningParent(Polygon p, Color baseColor) { + if (p == null) { + return baseColor; + } + CityObject parent = p.getParent().getParent(); + if (parent instanceof Opening) { + Opening op = (Opening) parent; + if (op.getType() == OpeningType.DOOR) { + baseColor = Color.ORANGE; + } else { + baseColor = Color.TEAL; + } + } + return baseColor; + } + + private Vector3d findCenter(List points) { + double xMin = Double.MAX_VALUE; + double yMin = Double.MAX_VALUE; + double zMin = Double.MAX_VALUE; + + double xMax = Double.NEGATIVE_INFINITY; + double yMax = Double.NEGATIVE_INFINITY; + double zMax = Double.NEGATIVE_INFINITY; + + for (Vector3d point : points) { + if (point.getX() < xMin) { + xMin = point.getX(); + } + if (point.getX() > xMax) { + xMax = point.getX(); + } + if (point.getY() < yMin) { + yMin = point.getY(); + } + if (point.getY() > yMax) { + yMax = point.getY(); + } + if (point.getZ() < zMin) { + zMin = point.getZ(); + } + if (point.getZ() > zMax) { + zMax = point.getZ(); + } + } + + // center + double x = (xMax - xMin) / 2 + xMin; + double y = (yMax - yMin) / 2 + yMin; + double z = (zMax - zMin) / 2 + zMin; + return new Vector3d(x, y, z); + } + + public Vector3d getMovedBy() { + return movedBy; + } + + public void setCullFace(CullFace currentCulling) { + for (MeshView mesh : meshes) { + mesh.setCullFace(currentCulling); + } + } + + public void setDrawMode(DrawMode currentDrawMode) { + if (currentDrawMode == DrawMode.LINE) { + for (MeshView mesh : meshes) { + mesh.setDrawMode(currentDrawMode); + mesh.setMaterial(GRID_MAT); + } + } else if (currentDrawMode == DrawMode.FILL) { + for (int i = 0; i < meshes.size(); i++) { + MeshView mesh = meshes.get(i); + mesh.setDrawMode(currentDrawMode); + mesh.setMaterial(materials.get(i)); + } + } + } + + public List getMeshes() { + return meshes; + } +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ValidationView.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ValidationView.java new file mode 100644 index 0000000..2699d1c --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ValidationView.java @@ -0,0 +1,77 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import de.hft.stuttgart.citydoctor2.check.Checker; +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import javafx.application.Platform; +import javafx.scene.Node; +import javafx.scene.image.Image; +import javafx.scene.layout.HBox; + +public class ValidationView extends View { + + private static final Logger logger = LogManager.getLogger(ValidationView.class); + + private Image viewLogo; + private MainWindow mainWindow; + private CityDoctorController controller; + + public ValidationView(MainWindow mainWindow, CityDoctorController controller) { + this.mainWindow = mainWindow; + this.controller = controller; + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/error_stat32x32.png")) { + viewLogo = new Image(inStream); + } catch (IOException e) { + logger.catching(e); + } + } + + @Override + public Node getMainScreen() { + return mainWindow.getMainContainer(); + } + + @Override + public Optional getToolbar() { + return Optional.of(mainWindow.getMainToolbar().getToolBar()); + } + + @Override + public void onHide() { + Platform.runLater(() -> { + mainWindow.unselectEverything(); + mainWindow.getMeshGroup().getChildren().clear(); + mainWindow.clearHighlights(); + mainWindow.getErrorTree().getRoot().getChildren().clear(); + mainWindow.getPolygonsView().getRoot().getChildren().clear(); + mainWindow.getVertexView().getRoot().getChildren().clear(); + mainWindow.getEdgeView().getRoot().getChildren().clear(); + }); + } + + @Override + public void onShow(CityDoctorModel model, Checker checker) { + if (model == null) { + return; + } + controller.buildTrees(); + controller.updateFeatureTrees(); + } + + @Override + public Image getViewLogo() { + return viewLogo; + } + + @Override + public void initializeView(MainWindow mainWindow) { + // already initialized + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/VertexClickDispatcher.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/VertexClickDispatcher.java new file mode 100644 index 0000000..010e631 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/VertexClickDispatcher.java @@ -0,0 +1,19 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import javafx.scene.input.MouseEvent; + +public class VertexClickDispatcher implements ClickDispatcher { + + private Vertex v; + + public VertexClickDispatcher(Vertex v) { + this.v = v; + } + + @Override + public void click(MouseEvent me, ClickHandler handler) { + handler.onVertexClick(v, me); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/VertexClickHandler.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/VertexClickHandler.java new file mode 100644 index 0000000..1939527 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/VertexClickHandler.java @@ -0,0 +1,71 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import org.locationtech.proj4j.ProjCoordinate; + +import de.hft.stuttgart.citydoctor2.datastructure.Polygon; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.gui.tree.Renderable; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.CustomMenuItem; +import javafx.scene.control.Label; +import javafx.scene.control.MenuItem; +import javafx.scene.control.TreeView; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.stage.Stage; + +public class VertexClickHandler implements ClickHandler { + + private TreeView errorView; + private Renderer renderer; + private Stage stage; + private ParserConfiguration config; + + public VertexClickHandler(TreeView errorView, Renderer renderer, Stage stage) { + this.errorView = errorView; + this.renderer = renderer; + this.stage = stage; + } + + public void setConfig(ParserConfiguration config) { + this.config = config; + } + + @Override + public void onPolygonClick(Polygon p, MouseEvent me) { + if (me.getButton() == MouseButton.PRIMARY) { + errorView.getSelectionModel().clearSelection(); + renderer.highlight(p); + } else if (me.getButton() == MouseButton.SECONDARY) { + MenuItem mi = new MenuItem(p.getGmlId().getGmlString()); + ContextMenu cMenu = new ContextMenu(mi); + cMenu.show(stage, me.getScreenX(), me.getScreenY()); + } + } + + @Override + public void onVertexClick(Vertex v, MouseEvent me) { + if (me.getButton() == MouseButton.SECONDARY) { + MenuItem mi1 = new CustomMenuItem(new Label("Vertex")); + double x = v.getX(); + double y = v.getY(); + if (config.getOriginalTransform() != null) { + ProjCoordinate p1 = new ProjCoordinate(); + ProjCoordinate p2 = new ProjCoordinate(); + p1.x = v.getX(); + p1.y = v.getY(); + config.getOriginalTransform().transform(p1, p2); + x = p2.x; + y = p2.y; + } + + MenuItem mi2 = new MenuItem("x = " + x); + MenuItem mi3 = new MenuItem("y = " + y); + MenuItem mi4 = new MenuItem("z = " + v.getZ()); + ContextMenu cMenu = new ContextMenu(mi1, mi2, mi3, mi4); + cMenu.show(stage, me.getScreenX(), me.getScreenY()); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/View.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/View.java new file mode 100644 index 0000000..71f6ce4 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/View.java @@ -0,0 +1,32 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.util.Optional; + +import de.hft.stuttgart.citydoctor2.check.Checker; +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import javafx.scene.Node; +import javafx.scene.image.Image; +import javafx.scene.layout.HBox; + +public abstract class View { + + private boolean firstTime = true; + + public abstract Optional getToolbar(); + public abstract Node getMainScreen(); + public abstract Image getViewLogo(); + + public abstract void initializeView(MainWindow mainWindow); + + public void fireOnShowEvent(CityDoctorModel model, Checker checker, MainWindow mainWindow) { + if (firstTime) { + firstTime = false; + initializeView(mainWindow); + } + onShow(model, checker); + } + + public abstract void onHide(); + public abstract void onShow(CityDoctorModel model, Checker checker); + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ViewRegistration.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ViewRegistration.java new file mode 100644 index 0000000..9161d18 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/ViewRegistration.java @@ -0,0 +1,31 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class ViewRegistration { + + private static Set registeredViews = new LinkedHashSet<>(); + private static View currentActiveView; + + private ViewRegistration() { + // only static use + } + + public static void registerView(View v) { + registeredViews.add(v); + } + + public static Set getRegisteredViews() { + return registeredViews; + } + + static void setCurrentActiveView(View v) { + currentActiveView = v; + } + + public static View getCurrentActiveView() { + return currentActiveView; + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/WriteReportDialog.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/WriteReportDialog.java new file mode 100644 index 0000000..56f0477 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/WriteReportDialog.java @@ -0,0 +1,262 @@ +package de.hft.stuttgart.citydoctor2.gui; + +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import de.hft.stuttgart.citydoctor2.utils.Localization; +import javafx.application.Platform; +import javafx.embed.swing.SwingFXUtils; +import javafx.event.Event; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.SnapshotParameters; +import javafx.scene.chart.BarChart; +import javafx.scene.chart.CategoryAxis; +import javafx.scene.chart.NumberAxis; +import javafx.scene.chart.XYChart; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.WritableImage; +import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.util.StringConverter; + +public class WriteReportDialog { + + private static Logger logger = LogManager.getLogger(WriteReportDialog.class); + + private Stage stage; + private CityDoctorController controller; + private MainWindow mainWindow; + + @FXML + private CheckBox pdfCheckBox; + + @FXML + private TextField pdfFileField; + + @FXML + private Button selectPdfBtn; + + @FXML + private CheckBox xmlCheckBox; + + @FXML + private TextField xmlFileField; + + @FXML + private Button selectXmlFile; + + @FXML + private Button cancelBtn; + + @FXML + private Button writeBtn; + + @FXML + private BarChart barChart; + + @FXML + private Button saveImageBtn; + + @FXML + private Label errorStatisticsLabel; + + public WriteReportDialog(Stage parent, CityDoctorController controller, MainWindow mainWindow) throws IOException { + FXMLLoader loader = new FXMLLoader(WriteReportDialog.class.getResource("WriteReportDialog.fxml")); + loader.setController(this); + VBox box = loader.load(); + this.controller = controller; + this.mainWindow = mainWindow; + 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("Write Reports"); + stage.setOnHidden(we -> barChart.getData().clear()); + setMaximumWidthOfBarChartColumns(); + } + + private void setMaximumWidthOfBarChartColumns() { + double maxBarWidth = 40; + double minCategoryGap = 10; + + // setting maximum width of columns in bar chart + // from https://stackoverflow.com/questions/27302875/set-bar-chart-column-width-size + stage.getScene().widthProperty().addListener((obs, n, n1) -> { + if (barChart.getData().isEmpty()) + return; + + setCategoryWidthWhenWindowIsBigger(maxBarWidth, n, n1); + if (n != null && (n1.doubleValue() < n.doubleValue()) && barChart.getCategoryGap() > minCategoryGap) { + double barWidth = 0; + CategoryAxis xAxis = (CategoryAxis) barChart.getXAxis(); + do { + double catSpace = xAxis.getCategorySpacing(); + double avilableBarSpace = catSpace - (minCategoryGap + barChart.getBarGap()); + barWidth = Math.min(maxBarWidth, + (avilableBarSpace / barChart.getData().size()) - barChart.getBarGap()); + avilableBarSpace = (barWidth + barChart.getBarGap()) * barChart.getData().size(); + barChart.setCategoryGap(catSpace - avilableBarSpace - barChart.getBarGap()); + } while (barWidth < maxBarWidth && barChart.getCategoryGap() > minCategoryGap); + } + }); + } + + private void setCategoryWidthWhenWindowIsBigger(double maxBarWidth, Number n, Number n1) { + if (n != null && (n1.doubleValue() > n.doubleValue())) { + double barWidth = 0; + CategoryAxis xAxis = (CategoryAxis) barChart.getXAxis(); + do { + double catSpace = xAxis.getCategorySpacing(); + double avilableBarSpace = catSpace - (barChart.getCategoryGap() + barChart.getBarGap()); + barWidth = (avilableBarSpace / barChart.getData().size()) - barChart.getBarGap(); + if (barWidth > maxBarWidth) { + avilableBarSpace = (maxBarWidth + barChart.getBarGap()) * barChart.getData().size(); + barChart.setCategoryGap(catSpace - avilableBarSpace - barChart.getBarGap()); + } + } while (barWidth > maxBarWidth); + } + } + + public void initialize() { + writeBtn.setText(Localization.getText("WriteReportDialog.writeBtn")); + cancelBtn.setText(Localization.getText("WriteReportDialog.cancelBtn")); + errorStatisticsLabel.setText(Localization.getText("WriteReportDialog.errorStatisticsLabel")); + saveImageBtn.setText(Localization.getText("WriteReportDialog.saveImageBtn")); + selectPdfBtn.setText(Localization.getText("WriteReportDialog.selectPdfBtn")); + selectXmlFile.setText(Localization.getText("WriteReportDialog.selectXmlFile")); + barChart.getXAxis().setLabel(Localization.getText("WriteReportDialog.xAxisLabel")); + barChart.getYAxis().setLabel(Localization.getText("WriteReportDialog.yAxisLabel")); + + // formatter to display only whole numbers + NumberAxis yAxis = (NumberAxis) barChart.getYAxis(); + yAxis.setTickLabelFormatter(new StringConverter() { + + @Override + public String toString(Number object) { + if (object.doubleValue() % 1 < 0.00000001) { + return String.valueOf(object.intValue()); + } else { + return ""; + } + } + + @Override + public Number fromString(String string) { + return Integer.parseInt(string); + } + }); + saveImageBtn.setOnAction(ae -> { + FileChooser chooser = new FileChooser(); + chooser.setInitialDirectory(new File(".")); + chooser.getExtensionFilters().add(new ExtensionFilter("PNG-Image", "*.png")); + String fName = controller.getFileName(); + fName = fName.substring(0, fName.lastIndexOf('.')); + chooser.setInitialFileName(fName + "_errors.png"); + File imageFile = chooser.showSaveDialog(stage); + if (imageFile != null) { + WritableImage snapshot = barChart.snapshot(new SnapshotParameters(), null); + try { + ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", imageFile); + } catch (IOException e) { + logger.error("Failed to save image", e); + mainWindow.showExceptionDialog(e); + } + } + }); + + pdfCheckBox.selectedProperty().addListener((obs, oldV, newV) -> { + selectPdfBtn.setDisable(!newV); + pdfFileField.setDisable(!newV); + }); + + xmlCheckBox.selectedProperty().addListener((obs, oldV, newV) -> { + selectXmlFile.setDisable(!newV); + xmlFileField.setDisable(!newV); + }); + + selectPdfBtn.setOnAction(ae -> { + FileChooser chooser = new FileChooser(); + chooser.setTitle("Select PDF File"); + chooser.getExtensionFilters().add(new ExtensionFilter("PDF-Files", ".pdf")); + File pdfFile = chooser.showSaveDialog(stage); + if (pdfFile != null) { + pdfFileField.setText(pdfFile.getAbsolutePath()); + } + }); + + selectXmlFile.setOnAction(ae -> { + FileChooser chooser = new FileChooser(); + chooser.setTitle("Select XML File"); + chooser.getExtensionFilters().add(new ExtensionFilter("XML-Files", ".xml")); + File xmlFile = chooser.showSaveDialog(stage); + if (xmlFile != null) { + xmlFileField.setText(xmlFile.getAbsolutePath()); + } + }); + + initWriteButton(); + + cancelBtn.setOnAction(ae -> stage.close()); + } + + private void initWriteButton() { + writeBtn.setOnAction(ae -> { + stage.setOnCloseRequest(Event::consume); + cancelBtn.setDisable(true); + writeBtn.setDisable(true); + Thread t = new Thread(() -> { + try { + if (pdfCheckBox.isSelected()) { + String file = pdfFileField.getText(); + File pdfFile = new File(file); + File parentFile = pdfFile.getParentFile(); + if (parentFile != null) { + parentFile.mkdirs(); + } + controller.writePdfReport(pdfFile); + } + if (xmlCheckBox.isSelected()) { + String file = xmlFileField.getText(); + File xmlFile = new File(file); + File parentFile = xmlFile.getParentFile(); + if (parentFile != null) { + parentFile.mkdirs(); + } + controller.writeXmlReport(xmlFile); + } + } finally { + Platform.runLater(() -> { + stage.setOnCloseRequest(null); + cancelBtn.setDisable(false); + writeBtn.setDisable(false); + stage.close(); + }); + } + }); + t.start(); + }); + } + + public void show() { + XYChart.Series series = controller.createErrorSeries(); + barChart.getData().clear(); + barChart.getData().add(series); + stage.show(); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/filter/TypeFilterSelection.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/filter/TypeFilterSelection.java new file mode 100644 index 0000000..4d203cf --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/filter/TypeFilterSelection.java @@ -0,0 +1,24 @@ +package de.hft.stuttgart.citydoctor2.gui.filter; + +import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; + +public class TypeFilterSelection { + + private FeatureType type; + private String name; + + public TypeFilterSelection(FeatureType type, String name) { + this.type = type; + this.name = name; + } + + public FeatureType getType() { + return type; + } + + @Override + public String toString() { + return name; + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/filter/ViewFilter.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/filter/ViewFilter.java new file mode 100644 index 0000000..7211f54 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/filter/ViewFilter.java @@ -0,0 +1,35 @@ +package de.hft.stuttgart.citydoctor2.gui.filter; + +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.Geometry; + +public abstract class ViewFilter { + + private boolean enabled = true; + + protected abstract boolean useGeometry(CityObject co, Geometry geom); + + public boolean allowedToUse(CityObject co, Geometry geom) { + if (!enabled) { + return false; + } + return useGeometry(co, geom); + } + + public boolean isEnabled() { + return enabled; + } + + public void enable() { + enabled = true; + } + + public void disable() { + enabled = false; + } + + public void setEnable(boolean enabled) { + this.enabled = enabled; + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/logger/GuiLogger.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/logger/GuiLogger.java new file mode 100644 index 0000000..7b65872 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/logger/GuiLogger.java @@ -0,0 +1,104 @@ +package de.hft.stuttgart.citydoctor2.gui.logger; + +import java.io.Serializable; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.layout.PatternLayout; + +import javafx.application.Platform; +import javafx.scene.control.TextArea; + +@Plugin(name="GuiLogger", category="Core", elementType="appender", printObject=true) +public class GuiLogger extends AbstractAppender { + + private static TextArea area; + + private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); + private final Lock readLock = rwLock.readLock(); + + private static final int CAPACITY = 20000; + + private StringBuilder buffer = new StringBuilder(CAPACITY); + private Thread daemonLoggerThread; + private boolean dirty = false; + + public static void setTextArea(TextArea area) { + GuiLogger.area = area; + } + + @PluginFactory + public static GuiLogger createAppender( + @PluginAttribute("name") String name, + @PluginAttribute("ignoreExceptions") boolean ignoreExceptions, + @PluginElement("Layout") Layout layout, + @PluginElement("Filter") final Filter filter) { + if (name == null) { + LOGGER.error("No name provided for GuiLogger"); + return null; + } + if (layout == null) { + layout = PatternLayout.createDefaultLayout(); + } + GuiLogger guiLogger = new GuiLogger(name, filter, layout, ignoreExceptions, new Property[0]); + guiLogger.daemonLoggerThread = new Thread(() -> { + while (!Thread.interrupted()) { + if (guiLogger.dirty) { + Platform.runLater(() -> { + // set text doesn't scroll + // workaround with clear -> append + area.clear(); + area.appendText(guiLogger.buffer.toString()); + }); + guiLogger.dirty = false; + } + try { + Thread.sleep(200); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + }); + guiLogger.daemonLoggerThread.setDaemon(true); + guiLogger.daemonLoggerThread.start(); + return guiLogger; + } + + protected GuiLogger(String name, Filter filter, Layout layout, boolean ignoreExceptions, + Property[] properties) { + super(name, filter, layout, ignoreExceptions, properties); + } + + @Override + public void append(LogEvent event) { + if (area == null) { + return; + } + readLock.lock(); + try { + String s = new String(getLayout().toByteArray(event)); + int capacityLeft = CAPACITY - buffer.length(); + if (capacityLeft < s.length()) { + int delete = s.length() - capacityLeft; + buffer.delete(0, delete - 1); + } + buffer.append(s); + dirty = true; + } finally { + readLock.unlock(); + } + } + + + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/table/ErrorStat.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/table/ErrorStat.java new file mode 100644 index 0000000..0bca03a --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/table/ErrorStat.java @@ -0,0 +1,65 @@ +package de.hft.stuttgart.citydoctor2.gui.table; + +import de.hft.stuttgart.citydoctor2.check.ErrorId; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleObjectProperty; + +public class ErrorStat { + + private final SimpleObjectProperty errorId = new SimpleObjectProperty<>(); + private final SimpleIntegerProperty count = new SimpleIntegerProperty(); + + public ErrorStat(ErrorId errorId) { + this(errorId, 0); + } + + public ErrorStat(ErrorId errorId, int count) { + this.errorId.set(errorId); + this.count.set(count); + } + + public int getCount() { + return count.get(); + } + + public IntegerProperty getCountProperty() { + return count; + } + + public ObjectProperty getErrorIdProperty() { + return errorId; + } + + public void setCount(int count) { + this.count.set(count); + } + + public void incrementCount() { + count.set(count.get() + 1); + } + + public ErrorId getErrorId() { + return errorId.get(); + } + + public void setErrorId(ErrorId errorId) { + this.errorId.set(errorId); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("ErrorStat [errorId="); + builder.append(errorId); + builder.append(", count="); + builder.append(count); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBoundarySurfacesNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBoundarySurfacesNode.java new file mode 100644 index 0000000..cf02482 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBoundarySurfacesNode.java @@ -0,0 +1,46 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllBoundarySurfacesNode extends Renderable { + + private List boundarySurfaces; + + public AllBoundarySurfacesNode(List boundarySurfaces) { + this.boundarySurfaces = boundarySurfaces; + } + + @Override + public String getText() { + return "Boundary Surfaces"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean isValidated = false; + for (BoundarySurface bs : boundarySurfaces) { + if (bs.isValidated()) { + isValidated = true; + } + if (bs.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + if (isValidated) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgeConstructiveElementsNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgeConstructiveElementsNode.java new file mode 100644 index 0000000..d99a6cc --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgeConstructiveElementsNode.java @@ -0,0 +1,64 @@ +/*- + * Copyright 2022 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.BridgeConstructiveElement; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllBridgeConstructiveElementsNode extends Renderable { + + private List constructiveElements; + + public AllBridgeConstructiveElementsNode(List constructiveElements) { + this.constructiveElements = constructiveElements; + } + + @Override + public String getText() { + return "Constructive Elements"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean isValidated = false; + for (BridgeConstructiveElement bce : constructiveElements) { + if (bce.isValidated()) { + isValidated = true; + } + if (bce.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + if (isValidated) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgePartsNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgePartsNode.java new file mode 100644 index 0000000..23bce24 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgePartsNode.java @@ -0,0 +1,64 @@ +/*- + * Copyright 2022 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllBridgePartsNode extends Renderable { + + private List bridgeParts; + + public AllBridgePartsNode(List bridgeParts) { + this.bridgeParts = bridgeParts; + } + + @Override + public String getText() { + return "Bridge Parts"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean wasChecked = false; + for (BridgeObject bp : bridgeParts) { + if (bp.isValidated()) { + wasChecked = true; + if (bp.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + } + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgesNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgesNode.java new file mode 100644 index 0000000..0e6ea54 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBridgesNode.java @@ -0,0 +1,30 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllBridgesNode extends Renderable { + + private List bridges; + + public AllBridgesNode(List bridges) { + this.bridges = bridges; + } + + @Override + public void refreshTextColor() { + // no color changes + } + + @Override + public String getText() { + return "Bridges"; + } + + @Override + public void visit(Renderer renderer) { + renderer.renderBridges(bridges); + } +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingPartsNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingPartsNode.java new file mode 100644 index 0000000..c64aac1 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingPartsNode.java @@ -0,0 +1,46 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllBuildingPartsNode extends Renderable { + + private List buildingParts; + + public AllBuildingPartsNode(List buildingParts) { + this.buildingParts = buildingParts; + } + + @Override + public String getText() { + return "Building Parts"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean wasChecked = false; + for (BuildingPart bp : buildingParts) { + if (bp.isValidated()) { + wasChecked = true; + if (bp.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + } + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingsNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingsNode.java new file mode 100644 index 0000000..8a156c4 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingsNode.java @@ -0,0 +1,31 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllBuildingsNode extends Renderable { + + private List buildings; + + public AllBuildingsNode(List buildings) { + this.buildings = buildings; + } + + @Override + public void refreshTextColor() { + // no color changes + } + + @Override + public String getText() { + return "Buildings"; + } + + @Override + public void visit(Renderer renderer) { + renderer.renderBuildings(buildings); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllInstallationsNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllInstallationsNode.java new file mode 100644 index 0000000..1256534 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllInstallationsNode.java @@ -0,0 +1,46 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.Installation; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllInstallationsNode extends Renderable { + + private List buildingInstallations; + + public AllInstallationsNode(List bis) { + this.buildingInstallations = bis; + } + + @Override + public String getText() { + return "Building Installations"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean wasChecked = false; + for (Installation bp : buildingInstallations) { + if (bp.isValidated()) { + wasChecked = true; + if (bp.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + } + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } + +} \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOpeningsNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOpeningsNode.java new file mode 100644 index 0000000..f91bd7f --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOpeningsNode.java @@ -0,0 +1,38 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.Opening; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllOpeningsNode extends Renderable { + + private List openings; + + public AllOpeningsNode(List openings) { + this.openings = openings; + } + + @Override + public void refreshTextColor() { + for (Opening bp : openings) { + if (bp.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + setStatus(CheckStatus.OK); + } + + @Override + public String getText() { + return "Openings"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTerrainNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTerrainNode.java new file mode 100644 index 0000000..e4006a4 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTerrainNode.java @@ -0,0 +1,31 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllTerrainNode extends Renderable { + + private List land; + + public AllTerrainNode(List land) { + this.land = land; + } + + @Override + public void refreshTextColor() { + // no use + } + + @Override + public String getText() { + return "Terrain"; + } + + @Override + public void visit(Renderer renderer) { + renderer.renderTerrain(land); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTinNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTinNode.java new file mode 100644 index 0000000..59bfa3a --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTinNode.java @@ -0,0 +1,49 @@ +/*- + * Copyright 2022 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.TinObject; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllTinNode extends Renderable { + + public AllTinNode(List components) { + // TODO Auto-generated constructor stub + } + + @Override + public void refreshTextColor() { + // TODO Auto-generated method stub + + } + + @Override + public String getText() { + return "Components"; + } + + @Override + public void visit(Renderer renderer) { + // TODO Auto-generated method stub + + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTransportationNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTransportationNode.java new file mode 100644 index 0000000..b7a57e2 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTransportationNode.java @@ -0,0 +1,31 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllTransportationNode extends Renderable { + + private List transportation; + + public AllTransportationNode(List transportation) { + this.transportation = transportation; + } + + @Override + public void refreshTextColor() { + // no use + } + + @Override + public String getText() { + return "Transportation"; + } + + @Override + public void visit(Renderer renderer) { + renderer.renderTransportation(transportation); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllVegetationNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllVegetationNode.java new file mode 100644 index 0000000..b2b8a6e --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllVegetationNode.java @@ -0,0 +1,31 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.Vegetation; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllVegetationNode extends Renderable { + + private List vegetation; + + public AllVegetationNode(List vegetation) { + this.vegetation = vegetation; + } + + @Override + public void refreshTextColor() { + // no use + } + + @Override + public String getText() { + return "Vegetation"; + } + + @Override + public void visit(Renderer renderer) { + renderer.renderVegetation(vegetation); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllWaterNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllWaterNode.java new file mode 100644 index 0000000..f4255fe --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllWaterNode.java @@ -0,0 +1,31 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.datastructure.WaterObject; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class AllWaterNode extends Renderable { + + private List water; + + public AllWaterNode(List water) { + this.water = water; + } + + @Override + public void refreshTextColor() { + // no use + } + + @Override + public String getText() { + return "Water"; + } + + @Override + public void visit(Renderer renderer) { + renderer.renderWater(water); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BoundarySurfaceNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BoundarySurfaceNode.java new file mode 100644 index 0000000..7886d62 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BoundarySurfaceNode.java @@ -0,0 +1,35 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class BoundarySurfaceNode extends Renderable { + + private BoundarySurface bs; + + public BoundarySurfaceNode(BoundarySurface bs) { + this.bs = bs; + } + + @Override + public String getText() { + return bs.getGmlId().getGmlString() + " [" + bs.getType() + "]"; + } + + @Override + public void visit(Renderer renderer) { + renderer.render(bs); + } + + @Override + public void refreshTextColor() { + if (!bs.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (bs.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BridgeConstructiveElementNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BridgeConstructiveElementNode.java new file mode 100644 index 0000000..6203a07 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BridgeConstructiveElementNode.java @@ -0,0 +1,54 @@ +/*- + * Copyright 2023 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.BridgeConstructiveElement; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class BridgeConstructiveElementNode extends Renderable { + + private BridgeConstructiveElement bce; + + public BridgeConstructiveElementNode(BridgeConstructiveElement bce) { + this.bce = bce; + } + + @Override + public String getText() { + return bce.getGmlId().getGmlString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.render(bce); + } + + @Override + public void refreshTextColor() { + if (!bce.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (bce.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BridgeNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BridgeNode.java new file mode 100644 index 0000000..79e3a25 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BridgeNode.java @@ -0,0 +1,55 @@ +/*- + * Copyright 2022 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class BridgeNode extends Renderable { + + private BridgeObject bridge; + + public BridgeNode(BridgeObject bridge) { + this.bridge = bridge; + } + + @Override + public String getText() { + return bridge.getGmlId().getGmlString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.render(bridge); + } + + @Override + public void refreshTextColor() { + if (!bridge.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (bridge.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingNode.java new file mode 100644 index 0000000..49a22a6 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingNode.java @@ -0,0 +1,40 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class BuildingNode extends Renderable { + + private Building building; + + public BuildingNode(Building building) { + this.building = building; + } + + @Override + public String getText() { + return building.getGmlId().getGmlString(); + } + + public Building getBuilding() { + return building; + } + + @Override + public void visit(Renderer renderer) { + renderer.render(building); + } + + @Override + public void refreshTextColor() { + if (!building.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (building.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingPartNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingPartNode.java new file mode 100644 index 0000000..0084d1f --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingPartNode.java @@ -0,0 +1,36 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class BuildingPartNode extends Renderable { + + private BuildingPart bp; + + public BuildingPartNode(BuildingPart bp) { + this.bp = bp; + } + + @Override + public String getText() { + return bp.getGmlId().getGmlString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.render(bp); + } + + @Override + public void refreshTextColor() { + if (!bp.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (bp.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ButtonRenderable.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ButtonRenderable.java new file mode 100644 index 0000000..201c28c --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ButtonRenderable.java @@ -0,0 +1,50 @@ +/*- + * 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class ButtonRenderable extends Renderable { + + private Runnable run; + + public ButtonRenderable(Runnable run) { + this.run = run; + } + + public void run() { + run.run(); + } + + @Override + public void refreshTextColor() { + // nothing to do + } + + @Override + public String getText() { + return null; + } + + @Override + public void visit(Renderer renderer) { + // nothing to render + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/CityObjectNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/CityObjectNode.java new file mode 100644 index 0000000..41b9ddd --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/CityObjectNode.java @@ -0,0 +1,36 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class CityObjectNode extends Renderable { + + private CityObject co; + + public CityObjectNode(CityObject co) { + this.co = co; + } + + @Override + public void refreshTextColor() { + if (!co.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (co.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + @Override + public String getText() { + return co.getGmlId().getGmlString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.render(co); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/Displayable.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/Displayable.java new file mode 100644 index 0000000..b626dd0 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/Displayable.java @@ -0,0 +1,9 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.gui.CityDoctorController; + +public interface Displayable { + + public void visitForDisplaying(CityDoctorController controller); + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/EdgeNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/EdgeNode.java new file mode 100644 index 0000000..7f75f84 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/EdgeNode.java @@ -0,0 +1,62 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; + +import de.hft.stuttgart.citydoctor2.datastructure.Edge; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class EdgeNode extends Renderable { + + private static DecimalFormat nf; + + static { + DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(); + dfs.setDecimalSeparator('.'); + nf = new DecimalFormat("#.####", dfs); + } + + private static final String SEPERATOR = ", "; + private Edge e; + private String text; + + public EdgeNode(Edge e) { + this.e = e; + } + + @Override + public void refreshTextColor() { + // not used + } + + @Override + public String getText() { + if (text == null) { + StringBuilder sb = new StringBuilder(); + sb.append("Edge from ["); + Vertex from = e.getFrom(); + sb.append(nf.format(from.getX())); + sb.append(SEPERATOR); + sb.append(nf.format(from.getY())); + sb.append(SEPERATOR); + sb.append(nf.format(from.getZ())); + sb.append("] to ["); + Vertex to = e.getTo(); + sb.append(nf.format(to.getX())); + sb.append(SEPERATOR); + sb.append(nf.format(to.getY())); + sb.append(SEPERATOR); + sb.append(nf.format(to.getZ())); + sb.append("]"); + text = sb.toString(); + } + return text; + } + + @Override + public void visit(Renderer renderer) { + renderer.highlight(e); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorCell.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorCell.java new file mode 100644 index 0000000..c75dd1a --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorCell.java @@ -0,0 +1,20 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.check.CheckError; +import javafx.scene.control.cell.TextFieldTreeCell; + +public class ErrorCell extends TextFieldTreeCell { + + @Override + public void updateItem(CheckError item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setText(null); + setGraphic(null); + } else { + setText(item.getErrorId().toString()); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorItemVisitor.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorItemVisitor.java new file mode 100644 index 0000000..b9153ba --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorItemVisitor.java @@ -0,0 +1,410 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.util.List; + +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.ErrorVisitor; +import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError; +import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError; +import de.hft.stuttgart.citydoctor2.check.error.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.PointTouchesEdgeError; +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.RingNotClosedError; +import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError; +import de.hft.stuttgart.citydoctor2.check.error.SchematronError; +import de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError; +import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError; +import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError; +import de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError; +import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError; +import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError; +import de.hft.stuttgart.citydoctor2.datastructure.Edge; +import de.hft.stuttgart.citydoctor2.datastructure.Polygon; +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import javafx.scene.control.TreeItem; + +public class ErrorItemVisitor implements ErrorVisitor { + + private static final String NAME_OF_ATTRIBUTE = "Name of Attribute: "; + private static final String CHILD_ID = "ChildId: "; + private TreeItem root; + + public ErrorItemVisitor(TreeItem root) { + this.root = root; + } + + @Override + public void visit(PolygonHoleOutsideError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(NonManifoldEdgeError err) { + for (Edge e : err.getEdges()) { + EdgeNode edgeNode = new EdgeNode(e); + TreeItem edgeItem = new TreeItem<>(edgeNode); + root.getChildren().add(edgeItem); + } + } + + @Override + public void visit(MultipleConnectedComponentsError err) { + for (int i = 0; i < err.getComponents().size(); i++) { + TextNode textNode = new TextNode("Component " + i); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + List component = err.getComponents().get(i); + for (Polygon p : component) { + PolygonNode polyNode = new PolygonNode(p, CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + textItem.getChildren().add(polyItem); + } + } + } + + @Override + public void visit(NestedRingError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(NonManifoldVertexError err) { + VertexNode vertexNode = new VertexNode(err.getVertex()); + TreeItem vertexItem = new TreeItem<>(vertexNode); + root.getChildren().add(vertexItem); + for (int i = 0; i < err.getComponents().size(); i++) { + TextNode textNode = new TextNode("Component " + i); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + List component = err.getComponents().get(i); + for (Polygon p : component) { + PolygonNode polyNode = new PolygonNode(p, CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + textItem.getChildren().add(polyItem); + } + } + } + + @Override + public void visit(PolygonWrongOrientationError err) { + for (Edge e : err.getEdges()) { + EdgeNode edgeNode = new EdgeNode(e); + TreeItem edgeItem = new TreeItem<>(edgeNode); + root.getChildren().add(edgeItem); + } + } + + @Override + public void visit(PolygonSameOrientationError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(SolidNotClosedError err) { + for (Edge e : err.getErrorEdges()) { + EdgeNode edgeNode = new EdgeNode(e); + TreeItem edgeItem = new TreeItem<>(edgeNode); + root.getChildren().add(edgeItem); + } + } + + @Override + public void visit(DependenciesNotMetError err) { + // not displayed + } + + @Override + public void visit(UnknownCheckError err) { + // not displayed + + } + + @Override + public void visit(RingNotClosedError err) { + LinearRingNode ringNode = new LinearRingNode(err.getRing(), CheckStatus.NOT_CHECKED); + TreeItem ringItem = new TreeItem<>(ringNode); + root.getChildren().add(ringItem); + } + + @Override + public void visit(ConsecutivePointSameError err) { + LinearRingNode ringNode = new LinearRingNode(err.getRing(), CheckStatus.NOT_CHECKED); + TreeItem ringItem = new TreeItem<>(ringNode); + root.getChildren().add(ringItem); + + VertexNode vertexNode = new VertexNode(err.getVertex1()); + TreeItem vertexItem = new TreeItem<>(vertexNode); + root.getChildren().add(vertexItem); + + vertexNode = new VertexNode(err.getVertex2()); + vertexItem = new TreeItem<>(vertexNode); + root.getChildren().add(vertexItem); + } + + @Override + public void visit(AllPolygonsWrongOrientationError err) { + // not displayed + } + + @Override + public void visit(PolygonInteriorDisconnectedError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(NullAreaError err) { + LinearRingNode ringNode = new LinearRingNode(err.getRing(), CheckStatus.NOT_CHECKED); + TreeItem ringItem = new TreeItem<>(ringNode); + root.getChildren().add(ringItem); + } + + @Override + public void visit(RingTooFewPointsError err) { + LinearRingNode ringNode = new LinearRingNode(err.getRing(), CheckStatus.NOT_CHECKED); + TreeItem ringItem = new TreeItem<>(ringNode); + root.getChildren().add(ringItem); + + if (err.getRing().getGmlId().isGenerated()) { + PolygonNode polyNode = new PolygonNode(err.getRing().getParent(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + ringItem.getChildren().add(polyItem); + } + } + + @Override + public void visit(NonPlanarPolygonNormalsDeviation err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + + TextNode textNode = new TextNode("Deviation: " + err.getDeviation()); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + } + + @Override + public void visit(NonPlanarPolygonDistancePlaneError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + + VertexNode vertexNode = new VertexNode(err.getVertex()); + TreeItem vertexItem = new TreeItem<>(vertexNode); + root.getChildren().add(vertexItem); + + TextNode textNode = new TextNode("Distance: " + err.getDistance() + "m"); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + + } + + @Override + public void visit(PolygonIntersectingRingsError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(SolidSelfIntError err) { + PolygonNode polyNode1 = new PolygonNode(err.getIntersections().get(0).getP1(), CheckStatus.NOT_CHECKED); + TreeItem polyItem1 = new TreeItem<>(polyNode1); + root.getChildren().add(polyItem1); + PolygonNode polyNode2 = new PolygonNode(err.getIntersections().get(0).getP2(), CheckStatus.NOT_CHECKED); + TreeItem polyItem2 = new TreeItem<>(polyNode2); + root.getChildren().add(polyItem2); + } + + @Override + public void visit(TooFewPolygonsError err) { + // not displayed + + } + + @Override + public void visit(RingDuplicatePointError err) { + LinearRingNode ringNode = new LinearRingNode(err.getRing(), CheckStatus.NOT_CHECKED); + TreeItem ringItem = new TreeItem<>(ringNode); + root.getChildren().add(ringItem); + + VertexNode vertexNode = new VertexNode(err.getVertex1()); + TreeItem vertexItem = new TreeItem<>(vertexNode); + root.getChildren().add(vertexItem); + + vertexNode = new VertexNode(err.getVertex2()); + vertexItem = new TreeItem<>(vertexNode); + root.getChildren().add(vertexItem); + + } + + @Override + public void visit(RingEdgeIntersectionError err) { + LinearRingNode ringNode = new LinearRingNode(err.getRing(), CheckStatus.NOT_CHECKED); + TreeItem ringItem = new TreeItem<>(ringNode); + root.getChildren().add(ringItem); + + EdgeNode edgeNode = new EdgeNode(err.getEdge1()); + TreeItem edgeItem = new TreeItem<>(edgeNode); + root.getChildren().add(edgeItem); + + EdgeNode edge2Node = new EdgeNode(err.getEdge2()); + TreeItem edge2Item = new TreeItem<>(edge2Node); + root.getChildren().add(edge2Item); + + VertexNode vertexNode = new VertexNode(new Vertex(err.getIntersection())); + TreeItem vertexItem = new TreeItem<>(vertexNode); + root.getChildren().add(vertexItem); + } + + @Override + public void visit(PointTouchesEdgeError err) { + TextNode textNode = new TextNode("Point touches edge"); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + + VertexNode vertexNode = new VertexNode(err.getVertex()); + TreeItem vertexItem = new TreeItem<>(vertexNode); + root.getChildren().add(vertexItem); + + EdgeNode edgeNode = new EdgeNode(err.getEdge()); + TreeItem edgeItem = new TreeItem<>(edgeNode); + root.getChildren().add(edgeItem); + + LinearRingNode ringNode = new LinearRingNode(err.getRing(), CheckStatus.NOT_CHECKED); + TreeItem ringItem = new TreeItem<>(ringNode); + root.getChildren().add(ringItem); + } + + @Override + public void visit(NotCeilingError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(NotFloorError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(NotWallError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(NotGroundError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(CheckError err) { + // not used + } + + @Override + public void visit(SchematronError err) { + TextNode textNode = new TextNode(err.getErrorIdString()); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + } + + @Override + public void visit(SurfaceUnfragmentedError err) { + TextNode textNode = new TextNode("Deviation: " + err.getAngleDeviation()); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + } + + @Override + public void visit(DegeneratedRingError err) { + TextNode textNode = new TextNode("Type: degenerated ring"); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + + LinearRingNode polyNode = new LinearRingNode(err.getRing(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + + @Override + public void visit(AttributeMissingError err) { + if (!err.getChildId().isEmpty()) { + TextNode textNode = new TextNode(CHILD_ID + err.getChildId()); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + } + TextNode nameNode = new TextNode(NAME_OF_ATTRIBUTE + err.getNameOfAttribute()); + TreeItem nameItem = new TreeItem<>(nameNode); + root.getChildren().add(nameItem); + + } + + @Override + public void visit(AttributeValueWrongError err) { + if (!err.getChildId().isEmpty()) { + TextNode textNode = new TextNode(CHILD_ID + err.getChildId()); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + } + TextNode nameNode = new TextNode(NAME_OF_ATTRIBUTE + err.getNameOfAttribute()); + TreeItem nameItem = new TreeItem<>(nameNode); + root.getChildren().add(nameItem); + } + + @Override + public void visit(AttributeInvalidError err) { + if (!err.getChildId().isEmpty()) { + TextNode textNode = new TextNode(CHILD_ID + err.getChildId()); + TreeItem textItem = new TreeItem<>(textNode); + root.getChildren().add(textItem); + } + TextNode nameNode = new TextNode(NAME_OF_ATTRIBUTE + err.getNameOfAttribute()); + TreeItem nameItem = new TreeItem<>(nameNode); + root.getChildren().add(nameItem); + } + + @Override + public void visit(PolygonWithoutSurfaceError err) { + PolygonNode polyNode = new PolygonNode(err.getPolygon(), CheckStatus.NOT_CHECKED); + TreeItem polyItem = new TreeItem<>(polyNode); + root.getChildren().add(polyItem); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorNode.java new file mode 100644 index 0000000..8ea378a --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ErrorNode.java @@ -0,0 +1,29 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class ErrorNode extends Renderable { + + private CheckError err; + + public ErrorNode(CheckError e) { + this.err = e; + } + + @Override + public void refreshTextColor() { + // not used + } + + @Override + public String getText() { + return err.getErrorId().toString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.highlight(err); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/GeometryNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/GeometryNode.java new file mode 100644 index 0000000..c58d5b7 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/GeometryNode.java @@ -0,0 +1,49 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Geometry; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class GeometryNode extends Renderable { + + private Geometry geom; + private String text; + + public GeometryNode(Geometry geom) { + this.geom = geom; + StringBuilder sb = new StringBuilder(); + sb.append("Geometry ["); + sb.append(geom.getType()); + sb.append(", "); + sb.append(geom.getLod()); + if (geom.getGmlId().isGenerated()) { + sb.append("]"); + } else { + sb.append("] "); + sb.append(geom.getGmlId()); + } + text = sb.toString(); + } + + @Override + public void refreshTextColor() { + if (!geom.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (geom.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + @Override + public String getText() { + return text; + } + + @Override + public void visit(Renderer renderer) { + renderer.render(geom); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/InstallationNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/InstallationNode.java new file mode 100644 index 0000000..cb759c7 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/InstallationNode.java @@ -0,0 +1,35 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Installation; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class InstallationNode extends Renderable { + + private Installation bi; + + public InstallationNode(Installation bi) { + this.bi = bi; + } + + @Override + public String getText() { + return bi.getGmlId().getGmlString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.render(bi); + } + + @Override + public void refreshTextColor() { + if (!bi.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (bi.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } +} \ No newline at end of file diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/LandUseNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/LandUseNode.java new file mode 100644 index 0000000..25e587e --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/LandUseNode.java @@ -0,0 +1,54 @@ +/*- + * Copyright 2022 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.LandObject; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class LandUseNode extends Renderable { + + private LandObject landUse; + + public LandUseNode(LandObject landUse) { + this.landUse = landUse; + } + + @Override + public void refreshTextColor() { + if (!landUse.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (landUse.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + @Override + public String getText() { + return "[LandUse] " + landUse.getGmlId().getGmlString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.render(landUse); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/LinearRingNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/LinearRingNode.java new file mode 100644 index 0000000..6c40634 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/LinearRingNode.java @@ -0,0 +1,41 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class LinearRingNode extends Renderable { + + private LinearRing lr; + private String text; + + public LinearRingNode(LinearRing lr, CheckStatus cs) { + this.lr = lr; + setStatus(cs); + } + + @Override + public void refreshTextColor() { + if (!lr.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (lr.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + @Override + public String getText() { + if (text == null) { + text = "Linear Ring (" + lr.getType().toString() + ") " + lr.getGmlId().getGmlString(); + } + return text; + } + + @Override + public void visit(Renderer renderer) { + renderer.highlight(lr); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/OpeningNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/OpeningNode.java new file mode 100644 index 0000000..9c7100d --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/OpeningNode.java @@ -0,0 +1,36 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Opening; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class OpeningNode extends Renderable { + + private Opening opening; + + public OpeningNode(Opening o) { + opening = o; + } + + @Override + public void refreshTextColor() { + if (!opening.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (opening.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + @Override + public String getText() { + return "Opening [" + opening.getGmlId() + ", " + opening.getType() + "]"; + } + + @Override + public void visit(Renderer renderer) { + renderer.render(opening); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/PolygonNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/PolygonNode.java new file mode 100644 index 0000000..0f7eb3e --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/PolygonNode.java @@ -0,0 +1,41 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Polygon; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class PolygonNode extends Renderable { + + private Polygon p; + private String text; + + public PolygonNode(Polygon p, CheckStatus cs) { + this.p = p; + setStatus(cs); + } + + @Override + public void refreshTextColor() { + if (!p.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (p.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + @Override + public String getText() { + if (text == null) { + text = "Polygon " + p.getGmlId().getGmlString(); + } + return text; + } + + @Override + public void visit(Renderer renderer) { + renderer.highlight(p); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ReliefNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ReliefNode.java new file mode 100644 index 0000000..58520f3 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/ReliefNode.java @@ -0,0 +1,55 @@ +/*- + * Copyright 2022 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class ReliefNode extends Renderable { + + private ReliefObject relief; + + public ReliefNode(ReliefObject relief) { + this.relief = relief; + } + + + @Override + public void refreshTextColor() { + if (!relief.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (relief.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + @Override + public String getText() { + return "[ReliefFeature] " + relief.getGmlId().getGmlString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.render(relief); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/Renderable.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/Renderable.java new file mode 100644 index 0000000..ef0f3cf --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/Renderable.java @@ -0,0 +1,34 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + +public abstract class Renderable { + + private ObjectProperty statusProperty = new SimpleObjectProperty<>(); + + /** + * @return the status + */ + public CheckStatus getStatus() { + return statusProperty.getValue(); + } + + /** + * @param status the status to set + */ + public void setStatus(CheckStatus status) { + statusProperty.set(status); + } + + public ObjectProperty getStatusProperty() { + return statusProperty; + } + + public abstract void refreshTextColor(); + public abstract String getText(); + public abstract void visit(Renderer renderer); + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/RenderableTreeCell.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/RenderableTreeCell.java new file mode 100644 index 0000000..a287216 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/RenderableTreeCell.java @@ -0,0 +1,58 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.scene.control.Button; +import javafx.scene.control.TreeCell; +import javafx.scene.paint.Color; + +public class RenderableTreeCell extends TreeCell { + + private Renderable currentCheckable = null; + private ChangeListener listener; + + @Override + protected void updateItem(Renderable item, boolean empty) { + super.updateItem(item, empty); + if (!empty && item != null) { + if (item instanceof ButtonRenderable) { + Button b = new Button("More ..."); + b.setMaxWidth(Double.MAX_VALUE); + setText(null); + setGraphic(b); + ButtonRenderable renderable = (ButtonRenderable) item; + b.setOnAction(ae -> renderable.run()); + return; + } + updateColor(item.getStatus()); + setText(item.getText()); + setGraphic(null); + if (currentCheckable != item) { + if (listener != null) { + // remove old Listener + item.getStatusProperty().removeListener(listener); + } + // only add listener to the status property once + listener = (obs, oldV, newV) -> Platform.runLater(() -> updateColor(newV)); + item.getStatusProperty().addListener(listener); + currentCheckable = item; + } + } else { + setTextFill(Color.BLACK); + setText(null); + setGraphic(null); + } + } + + private void updateColor(CheckStatus status) { + if (status == CheckStatus.OK) { + setTextFill(Color.GREEN); + } else if (status == CheckStatus.ERROR) { + setTextFill(Color.RED); + } else { + setTextFill(Color.BLACK); + } + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TextNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TextNode.java new file mode 100644 index 0000000..33c4d88 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TextNode.java @@ -0,0 +1,28 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class TextNode extends Renderable { + + private String text; + + public TextNode(String text) { + this.text = text; + } + + @Override + public void refreshTextColor() { + // don't refresh anything + } + + @Override + public String getText() { + return text; + } + + @Override + public void visit(Renderer renderer) { + // not used + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TinNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TinNode.java new file mode 100644 index 0000000..5452596 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TinNode.java @@ -0,0 +1,55 @@ +/*- + * Copyright 2022 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.TinObject; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class TinNode extends Renderable { + + private TinObject tin; + + public TinNode(TinObject tin) { + this.tin = tin; + } + + + @Override + public void refreshTextColor() { + if (!tin.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (tin.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + @Override + public String getText() { + return "[TINRelief] " + tin.getGmlId().getGmlString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.render(tin); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TreeRequirement.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TreeRequirement.java new file mode 100644 index 0000000..8ffd5d4 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TreeRequirement.java @@ -0,0 +1,67 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.check.DefaultParameter; +import de.hft.stuttgart.citydoctor2.check.Requirement; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.value.ObservableValue; + +public class TreeRequirement { + + private BooleanProperty enabled; + private ObservableValue name; + private SimpleStringProperty value; + private ObservableValue unit; + + private Requirement r; + + public TreeRequirement(Requirement r) { + this.r = r; + enabled = new SimpleBooleanProperty(true); + name = new SimpleStringProperty(r.getId()); + unit = new SimpleStringProperty(); + } + + public TreeRequirement(DefaultParameter dp) { + name = new SimpleStringProperty(dp.getName()); + value = new SimpleStringProperty(dp.getValue()); + unit = new SimpleStringProperty(dp.getUnitType().getRepresentation()); + } + + public void setEnabled(boolean enabled) { + this.enabled.set(enabled); + } + + public ObservableValue getUnitProperty() { + return unit; + } + + /** + * @return the enabled + */ + public BooleanProperty getEnabledProperty() { + return enabled; + } + + public ObservableValue getNameProperty() { + return name; + } + + public ObservableValue getValueProperty() { + return value; + } + + public void setValue(String value) { + this.value.setValue(value); + } + + public Requirement getRequirement() { + return r; + } + + public boolean isEnabled() { + return enabled.get(); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/VegetationNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/VegetationNode.java new file mode 100644 index 0000000..33da663 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/VegetationNode.java @@ -0,0 +1,61 @@ +/*- + * Copyright 2022 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 . + */ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Vegetation; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class VegetationNode extends Renderable { + + private Vegetation veg; + private String text; + + public VegetationNode(Vegetation veg) { + this.veg = veg; + StringBuilder sb = new StringBuilder(); + sb.append("["); + sb.append(veg.getVegetationType()); + sb.append("] "); + sb.append(veg.getGmlId().getGmlString()); + text = sb.toString(); + } + + @Override + public void refreshTextColor() { + if (!veg.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (veg.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + @Override + public String getText() { + return text; + } + + @Override + public void visit(Renderer renderer) { + renderer.render(veg); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/VertexNode.java b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/VertexNode.java new file mode 100644 index 0000000..e507e08 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/VertexNode.java @@ -0,0 +1,53 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; + +import de.hft.stuttgart.citydoctor2.datastructure.Vertex; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class VertexNode extends Renderable { + + private static DecimalFormat nf; + + static { + DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(); + dfs.setDecimalSeparator('.'); + nf = new DecimalFormat("#.########", dfs); + } + + private static final String SEPERATOR = ", "; + private Vertex v; + private String text; + + public VertexNode(Vertex v) { + this.v = v; + } + + @Override + public void refreshTextColor() { + // not used + } + + @Override + public String getText() { + if (text == null) { + StringBuilder sb = new StringBuilder(); + sb.append("Vertex ["); + sb.append(nf.format(v.getX())); + sb.append(SEPERATOR); + sb.append(nf.format(v.getY())); + sb.append(SEPERATOR); + sb.append(nf.format(v.getZ())); + sb.append("]"); + text = sb.toString(); + } + return text; + } + + @Override + public void visit(Renderer renderer) { + renderer.highlight(v); + } + +} diff --git a/CityDoctorParent/CityDoctorGUI/src/main/resources/citydoctor_logo.ico b/CityDoctorParent/CityDoctorGUI/src/main/resources/citydoctor_logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..3321b741356a9f40506da39dd8377c83e227d67a GIT binary patch literal 4286 zcmeH~SxA&o6vwamdd%RqvI?#%jujub3bJ4n{j3=2>OOI-<^BUJ-`3m?=Y5x zpR6oK-|TieW0{PxxoDWh^3f=tKUnr=LePR4|L(Uzk_&Z`@e3KH|5i)1FLLDaQuZmJF2%y~K2YR>$oRdE?H7&D8 zqG2j)Pt{AtW@7K%-U$>Z-iLG-Z|xKsK2hvw8jxE-?8S52puaDUhKGqAaQI=#f;KT9 z$|Lb7GqWDqPFiD)@7!UL;{W-+!Ohn##vX{YE^)4wQQLQK?u;L%Qgc^Pa2)iKd{V6s zko|%9ub!)~ah32+D7)D|N9;g z{gZGH+lH^IZ{6P(H=F3 zpFeoGQTp-20l0R-&V33Ob-)yjauuj)gi3O;gWNUIQuyccrpZDbR z?v)!{&f$A#y!@eB5=oDgPxsl;ekf*izT8VbGTj@ba>n+YUHW{vEci zvcjx1g-L9RS*1@g&r{=zd>u1op>H|f8$SshalQkgyJR`V6!BwuXEIrE_>;bTp8N6u>}de?$p95efEy$P b7(0a%CC|gy3_D|KWM;-RqhS=281(!CmHv7& literal 0 HcmV?d00001 diff --git a/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/AboutDialog.fxml b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/AboutDialog.fxml new file mode 100644 index 0000000..ef3c7e4 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/AboutDialog.fxml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CheckDialog.fxml b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CheckDialog.fxml new file mode 100644 index 0000000..19930f9 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CheckDialog.fxml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CreateRenderDataDialog.fxml b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CreateRenderDataDialog.fxml new file mode 100644 index 0000000..49cb7f1 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/CreateRenderDataDialog.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/FilterPane.fxml b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/FilterPane.fxml new file mode 100644 index 0000000..264bab3 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/FilterPane.fxml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml new file mode 100644 index 0000000..17503f5 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml new file mode 100644 index 0000000..e9f2c95 --- /dev/null +++ b/CityDoctorParent/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +