From 2c171abea0be52077227d99ef25bacd5b82b25ff Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 19 Sep 2024 13:20:08 +0200 Subject: [PATCH 01/55] Add support for CityGML 3.0 Room and Furniture features --- .../datastructure/AbstractBuilding.java | 46 ++- .../datastructure/AbstractFurniture.java | 110 +++++++ .../datastructure/AbstractRoom.java | 295 ++++++++++++++++++ .../datastructure/BridgeFurniture.java | 24 ++ .../datastructure/BridgeObject.java | 49 +++ .../citydoctor2/datastructure/BridgeRoom.java | 49 +++ .../datastructure/BuildingFurniture.java | 27 ++ .../datastructure/BuildingRoom.java | 49 +++ .../datastructure/FeatureType.java | 2 +- 9 files changed, 649 insertions(+), 2 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeFurniture.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeRoom.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingFurniture.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java index e8e59dd..8c28ccd 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java @@ -55,6 +55,7 @@ public abstract class AbstractBuilding extends CityObject { private final List buildingInstallations = new ArrayList<>(2); private final List boundarySurfaceList = new ArrayList<>(); + private final List buildingRooms = new ArrayList<>(); private org.citygml4j.core.model.building.AbstractBuilding ab; @@ -94,6 +95,9 @@ public abstract class AbstractBuilding extends CityObject { for (Installation bi : buildingInstallations) { bi.unsetGmlGeometries(); } + for (BuildingRoom br : buildingRooms) { + br.unsetGmlGeometries(); + } } @Override @@ -113,6 +117,9 @@ public abstract class AbstractBuilding extends CityObject { for (Installation bi : buildingInstallations) { bi.reCreateGeometries(factory, config); } + for (BuildingRoom br : buildingRooms) { + br.reCreateGeometries(factory, config); + } } private void reCreateBoundarySurface(GeometryFactory factory, ParserConfiguration config, BoundarySurface bs) { @@ -182,6 +189,9 @@ public abstract class AbstractBuilding extends CityObject { for (BoundarySurface bs : boundarySurfaceList) { bs.accept(c); } + for (BuildingRoom br : buildingRooms) { + br.accept(c); + } } @Override @@ -193,6 +203,9 @@ public abstract class AbstractBuilding extends CityObject { for (BoundarySurface bs : boundarySurfaceList) { bs.collectContainedErrors(errors); } + for (BuildingRoom br : buildingRooms) { + br.collectContainedErrors(errors); + } } @Override @@ -204,6 +217,9 @@ public abstract class AbstractBuilding extends CityObject { for (BoundarySurface bs : boundarySurfaceList) { bs.clearAllContainedCheckResults(); } + for (BuildingRoom br : buildingRooms) { + br.clearAllContainedCheckResults(); + } } @Override @@ -222,6 +238,11 @@ public abstract class AbstractBuilding extends CityObject { return true; } } + for (BuildingRoom br : buildingRooms) { + if (br.containsError(checkIdentifier)) { + return true; + } + } return false; } @@ -241,6 +262,11 @@ public abstract class AbstractBuilding extends CityObject { return true; } } + for (BuildingRoom br : buildingRooms) { + if (br.containsAnyError()) { + return true; + } + } return false; } @@ -258,6 +284,10 @@ public abstract class AbstractBuilding extends CityObject { coBi.setParent(this); } + public void addBuildingRoom(BuildingRoom room) { + buildingRooms.add(room); + } + public void setGmlObject(org.citygml4j.core.model.building.AbstractBuilding ab) { this.ab = ab; } @@ -265,7 +295,11 @@ public abstract class AbstractBuilding extends CityObject { public List getBuildingInstallations() { return buildingInstallations; } - + + public List getBuildingRooms() { + return buildingRooms; + } + @Override public void prepareForChecking() { super.prepareForChecking(); @@ -275,6 +309,9 @@ public abstract class AbstractBuilding extends CityObject { for (BoundarySurface bs : boundarySurfaceList) { bs.prepareForChecking(); } + for (BuildingRoom br : buildingRooms) { + br.prepareForChecking(); + } } @Override @@ -286,6 +323,9 @@ public abstract class AbstractBuilding extends CityObject { for (BoundarySurface bs : boundarySurfaceList) { bs.clearMetaInformation(); } + for (BuildingRoom br : buildingRooms) { + br.clearMetaInformation(); + } } @Override @@ -293,6 +333,7 @@ public abstract class AbstractBuilding extends CityObject { super.collectInstances(handler); handler.addInstance(boundarySurfaceList); handler.addInstance(buildingInstallations); + handler.addInstance(buildingRooms); } @Override @@ -305,6 +346,9 @@ public abstract class AbstractBuilding extends CityObject { for (Installation originalBi : originalAb.buildingInstallations) { buildingInstallations.add(handler.getCopyInstance(originalBi)); } + for (BuildingRoom originalBr : originalAb.buildingRooms) { + buildingRooms.add(handler.getCopyInstance(originalBr)); + } ab = originalAb.ab; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java new file mode 100644 index 0000000..cee6aae --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java @@ -0,0 +1,110 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.util.geometry.GeometryFactory; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.primitives.Solid; +import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; + +import java.io.Serial; + +/** + * Represents all types of furniture used inside Buildings. + */ +public abstract class AbstractFurniture extends CityObject{ + + @Serial + private static final long serialVersionUID = -9050689238027190674L; + + private AbstractRoom parent; + + private org.citygml4j.core.model.construction.AbstractFurniture af; + + @Override + public org.citygml4j.core.model.construction.AbstractFurniture getGmlObject(){ + return af; + } + + @Override + public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { + for (Geometry geom : getGeometries()) { + if (geom.getType() == GeometryType.MULTI_SURFACE) { + MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config); + setMultiSurfaceAccordingToLod(geom, ms); + } else { + Solid solid = CityGmlUtils.createSolid(geom, factory, config); + setSolidAccordingToLod(geom, solid); + } + } + } + + public void setParent(AbstractRoom room) { + if (parent == null) { + parent = room; + } else if (parent != room) { + parent.removeFurniture(this); + room.addRoomFurniture(this); + parent = room; + } + } + + + @Override + public void unsetGmlGeometries() { + af.setLod0MultiSurface(null); + af.setLod2MultiSurface(null); + af.setLod3MultiSurface(null); + af.setLod1Solid(null); + af.setLod2Solid(null); + af.setLod3Solid(null); + } + + private void setMultiSurfaceAccordingToLod(Geometry geom, MultiSurface ms) { + switch (geom.getLod()) { + case LOD0: + af.setLod0MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD2: + af.setLod2MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD3: + af.setLod3MultiSurface(new MultiSurfaceProperty(ms)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " multi surface to buildings"); + } + } + + private void setSolidAccordingToLod(Geometry geom, Solid solid) { + switch (geom.getLod()) { + case LOD1: + af.setLod1Solid(new SolidProperty(solid)); + break; + case LOD2: + af.setLod2Solid(new SolidProperty(solid)); + break; + case LOD3: + af.setLod3Solid(new SolidProperty(solid)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to buildings"); + } + } + + + @Override + public FeatureType getFeatureType() { + return FeatureType.FURNITURE; + } + + @Override + public void fillValues(Copyable original, CopyHandler handler){ + super.fillValues(original, handler); + AbstractFurniture originalAf = (AbstractFurniture) original; + af = originalAf.af; + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java new file mode 100644 index 0000000..e0310b4 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java @@ -0,0 +1,295 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.model.core.AbstractCityObject; +import org.citygml4j.core.util.geometry.GeometryFactory; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.primitives.Solid; +import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract base class for rooms inside CityGML 3.0 construction objects. + */ +public abstract class AbstractRoom extends CityObject{ + + @Serial + private static final long serialVersionUID = -1730625513988944329L; + + private final List roomInstallations = new ArrayList<>(2); + private final List abstractFurnitureList = new ArrayList<>(2); + private final List boundarySurfaceList = new ArrayList<>(); + + protected org.citygml4j.core.model.core.AbstractUnoccupiedSpace cgmlRoom; + + @Override + public void accept(Check c) { + super.accept(c); + if (c.canExecute(this)) { + c.check(this); + } + for (Installation roomInstallation : roomInstallations) { + roomInstallation.accept(c); + } + for (AbstractFurniture abstractFurniture : abstractFurnitureList) { + abstractFurniture.accept(c); + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.accept(c); + } + } + + @Override + public void collectContainedErrors(List errors){ + super.collectContainedErrors(errors); + for (Installation roomInstallation : roomInstallations) { + roomInstallation.collectContainedErrors(errors); + } + for (AbstractFurniture abstractFurniture : abstractFurnitureList) { + abstractFurniture.collectContainedErrors(errors); + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.collectContainedErrors(errors); + } + } + + @Override + public void clearAllContainedCheckResults(){ + super.clearAllContainedCheckResults(); + for (Installation roomInstallation : roomInstallations) { + roomInstallation.clearAllContainedCheckResults(); + } + for (AbstractFurniture abstractFurniture : abstractFurnitureList) { + abstractFurniture.clearAllContainedCheckResults(); + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.clearAllContainedCheckResults(); + } + } + + @Override + public boolean containsError(CheckId checkIdentifier){ + boolean hasError = super.containsError(checkIdentifier); + if (hasError){ + return true; + } + for (Installation roomInstallation : roomInstallations) { + if (roomInstallation.containsError(checkIdentifier)){ + return true; + } + } + for (AbstractFurniture abstractFurniture : abstractFurnitureList) { + if (abstractFurniture.containsError(checkIdentifier)){ + return true; + } + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + if (boundarySurface.containsError(checkIdentifier)){ + return true; + } + } + return false; + } + + @Override + public boolean containsAnyError(){ + boolean hasError = super.containsAnyError(); + if (hasError){ + return true; + } + for (Installation roomInstallation : roomInstallations) { + if (roomInstallation.containsAnyError()){ + return true; + } + } + for (AbstractFurniture abstractFurniture : abstractFurnitureList) { + if (abstractFurniture.containsAnyError()){ + return true; + } + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + if (boundarySurface.containsAnyError()){ + return true; + } + } + return false; + } + + @Override + public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { + for (Geometry geom : getGeometries()) { + if (geom.getType() == GeometryType.MULTI_SURFACE) { + MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config); + setMultiSurfaceAccordingToLod(geom, ms); + } else { + Solid solid = CityGmlUtils.createSolid(geom, factory, config); + setSolidAccordingToLod(geom, solid); + } + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.reCreateGeometries(factory, config); + } + for (Installation roomInstallation : roomInstallations) { + roomInstallation.reCreateGeometries(factory, config); + } + for (AbstractFurniture abstractFurniture : abstractFurnitureList) { + abstractFurniture.reCreateGeometries(factory, config); + } + } + + + private void setMultiSurfaceAccordingToLod(Geometry geom, MultiSurface ms) { + switch (geom.getLod()) { + case LOD0: + cgmlRoom.setLod0MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD2: + cgmlRoom.setLod2MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD3: + cgmlRoom.setLod3MultiSurface(new MultiSurfaceProperty(ms)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " multi surface to rooms"); + } + } + + private void setSolidAccordingToLod(Geometry geom, Solid solid) { + switch (geom.getLod()) { + case LOD1: + cgmlRoom.setLod1Solid(new SolidProperty(solid)); + break; + case LOD2: + cgmlRoom.setLod2Solid(new SolidProperty(solid)); + break; + case LOD3: + cgmlRoom.setLod3Solid(new SolidProperty(solid)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to rooms"); + } + } + + + @Override + public void unsetGmlGeometries() { + cgmlRoom.setLod0MultiSurface(null); + cgmlRoom.setLod2MultiSurface(null); + cgmlRoom.setLod3MultiSurface(null); + cgmlRoom.setLod1Solid(null); + cgmlRoom.setLod2Solid(null); + cgmlRoom.setLod3Solid(null); + for (AbstractFurniture abstractFurniture : abstractFurnitureList) { + abstractFurniture.unsetGmlGeometries(); + } + for (Installation roomInstallation : roomInstallations) { + roomInstallation.unsetGmlGeometries(); + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.unsetGmlGeometries(); + } + } + + @Override + public void prepareForChecking(){ + super.prepareForChecking(); + for (AbstractFurniture abstractFurniture : abstractFurnitureList) { + abstractFurniture.prepareForChecking(); + } + for (Installation roomInstallation : roomInstallations) { + roomInstallation.prepareForChecking(); + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.prepareForChecking(); + } + } + + @Override + public void clearMetaInformation() { + super.clearMetaInformation(); + for (AbstractFurniture abstractFurniture : abstractFurnitureList) { + abstractFurniture.clearMetaInformation(); + } + for (Installation roomInstallation : roomInstallations) { + roomInstallation.clearMetaInformation(); + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.clearMetaInformation(); + } + } + + @Override + public AbstractCityObject getGmlObject() { + return cgmlRoom; + } + + public void removeFurniture(AbstractFurniture furniture) { + abstractFurnitureList.remove(furniture); + } + + public List getRoomInstallations() { + return roomInstallations; + } + + public List getRoomFurnitureList() { + return abstractFurnitureList; + } + + public List getBoundarySurfaceList() { + return boundarySurfaceList; + } + + public void addRoomInstallation(Installation roomInstallation) { + roomInstallations.add(roomInstallation); + roomInstallation.setParent(this); + } + + public void addBoundarySurface(BoundarySurface boundarySurface) { + boundarySurfaceList.add(boundarySurface); + boundarySurface.setParent(this); + } + + public void addRoomFurniture(AbstractFurniture abstractFurniture) { + abstractFurnitureList.add(abstractFurniture); + } + + @Override + public FeatureType getFeatureType() { + return FeatureType.ROOM; + } + + @Override + public void collectInstances(CopyHandler handler){ + super.collectInstances(handler); + handler.addInstance(roomInstallations); + handler.addInstance(abstractFurnitureList); + handler.addInstance(boundarySurfaceList); + } + + @Override + public void fillValues(Copyable original, CopyHandler handler){ + super.fillValues(original, handler); + AbstractRoom originalAr = (AbstractRoom) original; + for (BoundarySurface originalBs : originalAr.boundarySurfaceList) { + boundarySurfaceList.add(handler.getCopyInstance(originalBs)); + } + for (Installation originalRi : originalAr.roomInstallations) { + roomInstallations.add(handler.getCopyInstance(originalRi)); + } + for (AbstractFurniture originalAb : originalAr.abstractFurnitureList) { + abstractFurnitureList.add(handler.getCopyInstance(originalAb)); + } + cgmlRoom = originalAr.cgmlRoom; + } + +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeFurniture.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeFurniture.java new file mode 100644 index 0000000..9ee86b8 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeFurniture.java @@ -0,0 +1,24 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.utils.Copyable; + +import java.io.Serial; + +public class BridgeFurniture extends AbstractFurniture { + + @Serial + private static final long serialVersionUID = 2450802405053172352L; + + public BridgeFurniture(BridgeRoom parent) { + setParent(parent); + parent.addRoomFurniture(this); + } + + private BridgeFurniture(){} + + + @Override + public Copyable createCopyInstance(){ + return new BridgeFurniture(); + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObject.java index 9ec8da3..bf6acf6 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeObject.java @@ -52,6 +52,7 @@ public class BridgeObject extends CityObject { private final List elements = new ArrayList<>(2); private final List boundarySurfaces = new ArrayList<>(2); private final List bridgeInstallations = new ArrayList<>(2); + private final List bridgeRooms = new ArrayList<>(2); private AbstractBridge ab; private BridgeType type; public BridgeObject(BridgeType type, AbstractBridge ab) { @@ -72,6 +73,10 @@ public class BridgeObject extends CityObject { return bridgeInstallations; } + public List getBridgeRooms() { + return bridgeRooms; + } + @Override public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { for (Geometry geom : getGeometries()) { @@ -95,6 +100,9 @@ public class BridgeObject extends CityObject { for (BridgeConstructiveElement ele : elements) { ele.reCreateGeometries(factory, config); } + for (BridgeRoom br : bridgeRooms) { + br.reCreateGeometries(factory, config); + } } @@ -140,6 +148,10 @@ public class BridgeObject extends CityObject { coBi.setParent(this); } + public void addBridgeRoom(BridgeRoom room) { + bridgeRooms.add(room); + } + @Override public void clearAllContainedCheckResults() { super.clearAllContainedCheckResults(); @@ -155,6 +167,9 @@ public class BridgeObject extends CityObject { for (BridgeConstructiveElement ele : elements) { ele.clearAllContainedCheckResults(); } + for (BridgeRoom br : bridgeRooms) { + br.clearAllContainedCheckResults(); + } } @Override @@ -172,6 +187,9 @@ public class BridgeObject extends CityObject { for (BridgeConstructiveElement ele : elements) { ele.collectContainedErrors(errors); } + for (BridgeRoom br : bridgeRooms) { + br.collectContainedErrors(errors); + } } @@ -199,6 +217,11 @@ public class BridgeObject extends CityObject { return true; } } + for (BridgeRoom br : bridgeRooms) { + if (br.containsAnyError()) { + return true; + } + } return false; } @@ -236,6 +259,11 @@ public class BridgeObject extends CityObject { return true; } } + for (BridgeRoom br : bridgeRooms) { + if (br.containsError(checkIdentifier)) { + return true; + } + } return false; } @@ -267,6 +295,9 @@ public class BridgeObject extends CityObject { for (BridgeConstructiveElement ele : elements) { ele.accept(c); } + for (BridgeRoom br : bridgeRooms) { + br.accept(c); + } } @@ -324,6 +355,9 @@ public class BridgeObject extends CityObject { for (BridgeConstructiveElement ele : elements) { ele.unsetGmlGeometries(); } + for (BridgeRoom br : bridgeRooms) { + br.unsetGmlGeometries(); + } } @@ -348,6 +382,9 @@ public class BridgeObject extends CityObject { for (Installation bi : bridgeInstallations) { bi.prepareForChecking(); } + for (BridgeObject part : parts) { + part.prepareForChecking(); + } } @Override @@ -369,6 +406,10 @@ public class BridgeObject extends CityObject { ele.clearMetaInformation(); } + for (BridgeRoom br : bridgeRooms) { + br.clearMetaInformation(); + } + } @Override @@ -390,6 +431,10 @@ public class BridgeObject extends CityObject { handler.addInstance(ele); } + for (BridgeRoom br : bridgeRooms) { + handler.addInstance(br); + } + } public void anonymize() { @@ -429,6 +474,10 @@ public class BridgeObject extends CityObject { getConstructiveElements().add(handler.getCopyInstance(ele)); } + for (BridgeRoom br : originalBo.bridgeRooms) { + getBridgeRooms().add(handler.getCopyInstance(br)); + } + } public List getBoundarySurfaces() { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeRoom.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeRoom.java new file mode 100644 index 0000000..fa68be0 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeRoom.java @@ -0,0 +1,49 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; + +import java.io.Serial; + +public class BridgeRoom extends AbstractRoom { + + @Serial + private static final long serialVersionUID = -276088332165299253L; + + private BridgeObject parent; + + private BridgeRoom(){} + + public BridgeRoom(BridgeObject parent) { + this.parent = parent; + parent.addBridgeRoom(this); + } + + + public void setGmlObject(org.citygml4j.core.model.bridge.BridgeRoom cgmlRoom){ + super.cgmlRoom = cgmlRoom; + } + + + public BridgeObject getParent() { + return parent; + } + + @Override + public void fillValues(Copyable original, CopyHandler handler){ + super.fillValues(original, handler); + BridgeRoom oRoom = (BridgeRoom) original; + parent = handler.getCopyInstance(oRoom.getParent()); + } + + @Override + public void collectInstances(CopyHandler handler){ + super.collectInstances(handler); + handler.addInstance(parent); + } + + @Override + public Copyable createCopyInstance() { + return new BridgeRoom(); + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingFurniture.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingFurniture.java new file mode 100644 index 0000000..da4d7ee --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingFurniture.java @@ -0,0 +1,27 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + + +import de.hft.stuttgart.citydoctor2.utils.Copyable; + +import java.io.Serial; + +public class BuildingFurniture extends AbstractFurniture { + + @Serial + private static final long serialVersionUID = -1046265159354525567L; + + public BuildingFurniture(BuildingRoom parent) { + setParent(parent); + parent.addRoomFurniture(this); + } + + private BuildingFurniture(){} + + + @Override + public Copyable createCopyInstance(){ + return new BuildingFurniture(); + } + +} + diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java new file mode 100644 index 0000000..b5b8e5c --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java @@ -0,0 +1,49 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; + +import java.io.Serial; + +public class BuildingRoom extends AbstractRoom{ + + @Serial + private static final long serialVersionUID = -276088332165299253L; + + private AbstractBuilding parent; + + private BuildingRoom(){} + + public BuildingRoom(AbstractBuilding parent) { + this.parent = parent; + parent.addBuildingRoom(this); + } + + + public void setGmlObject(org.citygml4j.core.model.building.BuildingRoom cgmlRoom){ + super.cgmlRoom = cgmlRoom; + } + + + public AbstractBuilding getParent() { + return parent; + } + + @Override + public void fillValues(Copyable original, CopyHandler handler){ + super.fillValues(original, handler); + BuildingRoom oRoom = (BuildingRoom) original; + parent = handler.getCopyInstance(oRoom.getParent()); + } + + @Override + public void collectInstances(CopyHandler handler){ + super.collectInstances(handler); + handler.addInstance(parent); + } + + @Override + public Copyable createCopyInstance() { + return new BuildingRoom(); + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java index c0cff7c..27256ec 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java @@ -27,6 +27,6 @@ package de.hft.stuttgart.citydoctor2.datastructure; public enum FeatureType { BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, INSTALLATION, OPENING, - BUILDING_PART, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION + BUILDING_PART, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE } -- GitLab From fbcd8f92d1c3d61c9d02ac8d35827391bca480af Mon Sep 17 00:00:00 2001 From: Riegel Date: Fri, 20 Sep 2024 13:10:51 +0200 Subject: [PATCH 02/55] Add mapping of Room and Furniture objects --- .../datastructure/AbstractBuilding.java | 13 ++ .../datastructure/AbstractFurniture.java | 29 +++-- .../datastructure/AbstractRoom.java | 54 ++------ ...urniture.java => BridgeRoomFurniture.java} | 13 +- .../datastructure/BuildingRoom.java | 33 ++++- ...niture.java => BuildingRoomFurniture.java} | 13 +- .../citygml3/Citygml3FeatureMapper.java | 122 ++++++++++++++---- 7 files changed, 172 insertions(+), 105 deletions(-) rename CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/{BridgeFurniture.java => BridgeRoomFurniture.java} (52%) rename CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/{BuildingFurniture.java => BuildingRoomFurniture.java} (52%) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java index 8c28ccd..856abfa 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java @@ -57,6 +57,7 @@ public abstract class AbstractBuilding extends CityObject { private final List boundarySurfaceList = new ArrayList<>(); private final List buildingRooms = new ArrayList<>(); + private final List buildingRoomFurnitureList = new ArrayList<>(); private org.citygml4j.core.model.building.AbstractBuilding ab; /** @@ -286,8 +287,16 @@ public abstract class AbstractBuilding extends CityObject { public void addBuildingRoom(BuildingRoom room) { buildingRooms.add(room); + room.setParent(this); } + public void addBuildingRoomFurniture(BuildingRoomFurniture roomFurniture) { + buildingRoomFurnitureList.add(roomFurniture); + roomFurniture.setParent(this); + } + + + public void setGmlObject(org.citygml4j.core.model.building.AbstractBuilding ab) { this.ab = ab; } @@ -300,6 +309,10 @@ public abstract class AbstractBuilding extends CityObject { return buildingRooms; } + public List getBuildingRoomFurnitureList() { + return buildingRoomFurnitureList; + } + @Override public void prepareForChecking() { super.prepareForChecking(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java index cee6aae..1527ef5 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java @@ -11,6 +11,8 @@ import org.xmlobjects.gml.model.geometry.primitives.Solid; import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; import java.io.Serial; +import java.util.ArrayList; +import java.util.List; /** * Represents all types of furniture used inside Buildings. @@ -20,7 +22,9 @@ public abstract class AbstractFurniture extends CityObject{ @Serial private static final long serialVersionUID = -9050689238027190674L; - private AbstractRoom parent; + private final List boundarySurfaceList = new ArrayList<>(); + + private CityObject parent; private org.citygml4j.core.model.construction.AbstractFurniture af; @@ -29,6 +33,15 @@ public abstract class AbstractFurniture extends CityObject{ return af; } + public void addBoundarySurface(BoundarySurface boundarySurface) { + boundarySurfaceList.add(boundarySurface); + boundarySurface.setParent(this); + } + + public List getBoundarySurfaceList() { + return boundarySurfaceList; + } + @Override public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { for (Geometry geom : getGeometries()) { @@ -42,14 +55,12 @@ public abstract class AbstractFurniture extends CityObject{ } } - public void setParent(AbstractRoom room) { - if (parent == null) { - parent = room; - } else if (parent != room) { - parent.removeFurniture(this); - room.addRoomFurniture(this); - parent = room; - } + protected void setGmlObject(org.citygml4j.core.model.construction.AbstractFurniture af){ + this.af = af; + } + + public void setParent(CityObject co) { + parent = co; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java index e0310b4..8e4ea6a 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java @@ -7,6 +7,7 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; import de.hft.stuttgart.citydoctor2.utils.CopyHandler; import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.model.construction.AbstractFurnitureProperty; import org.citygml4j.core.model.core.AbstractCityObject; import org.citygml4j.core.util.geometry.GeometryFactory; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; @@ -27,11 +28,15 @@ public abstract class AbstractRoom extends CityObject{ private static final long serialVersionUID = -1730625513988944329L; private final List roomInstallations = new ArrayList<>(2); - private final List abstractFurnitureList = new ArrayList<>(2); + // Rooms have a Href list of furniture, the actual object is saved in the Building + private final List boundarySurfaceList = new ArrayList<>(); + protected org.citygml4j.core.model.core.AbstractUnoccupiedSpace cgmlRoom; + + @Override public void accept(Check c) { super.accept(c); @@ -41,9 +46,6 @@ public abstract class AbstractRoom extends CityObject{ for (Installation roomInstallation : roomInstallations) { roomInstallation.accept(c); } - for (AbstractFurniture abstractFurniture : abstractFurnitureList) { - abstractFurniture.accept(c); - } for (BoundarySurface boundarySurface : boundarySurfaceList) { boundarySurface.accept(c); } @@ -55,9 +57,6 @@ public abstract class AbstractRoom extends CityObject{ for (Installation roomInstallation : roomInstallations) { roomInstallation.collectContainedErrors(errors); } - for (AbstractFurniture abstractFurniture : abstractFurnitureList) { - abstractFurniture.collectContainedErrors(errors); - } for (BoundarySurface boundarySurface : boundarySurfaceList) { boundarySurface.collectContainedErrors(errors); } @@ -69,9 +68,6 @@ public abstract class AbstractRoom extends CityObject{ for (Installation roomInstallation : roomInstallations) { roomInstallation.clearAllContainedCheckResults(); } - for (AbstractFurniture abstractFurniture : abstractFurnitureList) { - abstractFurniture.clearAllContainedCheckResults(); - } for (BoundarySurface boundarySurface : boundarySurfaceList) { boundarySurface.clearAllContainedCheckResults(); } @@ -88,11 +84,6 @@ public abstract class AbstractRoom extends CityObject{ return true; } } - for (AbstractFurniture abstractFurniture : abstractFurnitureList) { - if (abstractFurniture.containsError(checkIdentifier)){ - return true; - } - } for (BoundarySurface boundarySurface : boundarySurfaceList) { if (boundarySurface.containsError(checkIdentifier)){ return true; @@ -112,11 +103,6 @@ public abstract class AbstractRoom extends CityObject{ return true; } } - for (AbstractFurniture abstractFurniture : abstractFurnitureList) { - if (abstractFurniture.containsAnyError()){ - return true; - } - } for (BoundarySurface boundarySurface : boundarySurfaceList) { if (boundarySurface.containsAnyError()){ return true; @@ -142,9 +128,6 @@ public abstract class AbstractRoom extends CityObject{ for (Installation roomInstallation : roomInstallations) { roomInstallation.reCreateGeometries(factory, config); } - for (AbstractFurniture abstractFurniture : abstractFurnitureList) { - abstractFurniture.reCreateGeometries(factory, config); - } } @@ -189,9 +172,6 @@ public abstract class AbstractRoom extends CityObject{ cgmlRoom.setLod1Solid(null); cgmlRoom.setLod2Solid(null); cgmlRoom.setLod3Solid(null); - for (AbstractFurniture abstractFurniture : abstractFurnitureList) { - abstractFurniture.unsetGmlGeometries(); - } for (Installation roomInstallation : roomInstallations) { roomInstallation.unsetGmlGeometries(); } @@ -203,9 +183,6 @@ public abstract class AbstractRoom extends CityObject{ @Override public void prepareForChecking(){ super.prepareForChecking(); - for (AbstractFurniture abstractFurniture : abstractFurnitureList) { - abstractFurniture.prepareForChecking(); - } for (Installation roomInstallation : roomInstallations) { roomInstallation.prepareForChecking(); } @@ -217,9 +194,6 @@ public abstract class AbstractRoom extends CityObject{ @Override public void clearMetaInformation() { super.clearMetaInformation(); - for (AbstractFurniture abstractFurniture : abstractFurnitureList) { - abstractFurniture.clearMetaInformation(); - } for (Installation roomInstallation : roomInstallations) { roomInstallation.clearMetaInformation(); } @@ -228,22 +202,18 @@ public abstract class AbstractRoom extends CityObject{ } } + + @Override public AbstractCityObject getGmlObject() { return cgmlRoom; } - public void removeFurniture(AbstractFurniture furniture) { - abstractFurnitureList.remove(furniture); - } public List getRoomInstallations() { return roomInstallations; } - public List getRoomFurnitureList() { - return abstractFurnitureList; - } public List getBoundarySurfaceList() { return boundarySurfaceList; @@ -259,9 +229,7 @@ public abstract class AbstractRoom extends CityObject{ boundarySurface.setParent(this); } - public void addRoomFurniture(AbstractFurniture abstractFurniture) { - abstractFurnitureList.add(abstractFurniture); - } + @Override public FeatureType getFeatureType() { @@ -272,7 +240,6 @@ public abstract class AbstractRoom extends CityObject{ public void collectInstances(CopyHandler handler){ super.collectInstances(handler); handler.addInstance(roomInstallations); - handler.addInstance(abstractFurnitureList); handler.addInstance(boundarySurfaceList); } @@ -286,9 +253,6 @@ public abstract class AbstractRoom extends CityObject{ for (Installation originalRi : originalAr.roomInstallations) { roomInstallations.add(handler.getCopyInstance(originalRi)); } - for (AbstractFurniture originalAb : originalAr.abstractFurnitureList) { - abstractFurnitureList.add(handler.getCopyInstance(originalAb)); - } cgmlRoom = originalAr.cgmlRoom; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeFurniture.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeRoomFurniture.java similarity index 52% rename from CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeFurniture.java rename to CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeRoomFurniture.java index 9ee86b8..423b33d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeFurniture.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BridgeRoomFurniture.java @@ -1,24 +1,21 @@ package de.hft.stuttgart.citydoctor2.datastructure; import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.model.bridge.BridgeFurniture; import java.io.Serial; -public class BridgeFurniture extends AbstractFurniture { +public class BridgeRoomFurniture extends AbstractFurniture { @Serial private static final long serialVersionUID = 2450802405053172352L; - public BridgeFurniture(BridgeRoom parent) { - setParent(parent); - parent.addRoomFurniture(this); + public void setGmlObject(BridgeFurniture gmlObject) { + super.setGmlObject(gmlObject); } - private BridgeFurniture(){} - - @Override public Copyable createCopyInstance(){ - return new BridgeFurniture(); + return new BridgeRoomFurniture(); } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java index b5b8e5c..2a7130c 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java @@ -1,29 +1,47 @@ package de.hft.stuttgart.citydoctor2.datastructure; +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.CopyHandler; import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.model.building.BuildingFurnitureProperty; +import org.citygml4j.core.model.construction.AbstractFurnitureProperty; +import org.citygml4j.core.util.geometry.GeometryFactory; import java.io.Serial; +import java.util.ArrayList; +import java.util.List; public class BuildingRoom extends AbstractRoom{ @Serial private static final long serialVersionUID = -276088332165299253L; - + private final List furnitureRefs = new ArrayList<>(2); private AbstractBuilding parent; - private BuildingRoom(){} + public void setGmlObject(org.citygml4j.core.model.building.BuildingRoom cgmlRoom){ + super.cgmlRoom = cgmlRoom; + } + - public BuildingRoom(AbstractBuilding parent) { + public void setParent(AbstractBuilding parent){ this.parent = parent; - parent.addBuildingRoom(this); } - - public void setGmlObject(org.citygml4j.core.model.building.BuildingRoom cgmlRoom){ - super.cgmlRoom = cgmlRoom; + public void addFurnitureRef(BuildingFurnitureProperty furnitureRef) { + furnitureRefs.add(furnitureRef); } + /** + * Returns the list of citygml3 furniture objects registered to this room. + * The reference objects only hold a HRef to the actual furniture object. + * @return + */ + public List getFurnitureRefs() { + return furnitureRefs; + } public AbstractBuilding getParent() { return parent; @@ -32,6 +50,7 @@ public class BuildingRoom extends AbstractRoom{ @Override public void fillValues(Copyable original, CopyHandler handler){ super.fillValues(original, handler); + BuildingRoom oRoom = (BuildingRoom) original; parent = handler.getCopyInstance(oRoom.getParent()); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingFurniture.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoomFurniture.java similarity index 52% rename from CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingFurniture.java rename to CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoomFurniture.java index da4d7ee..01b121b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingFurniture.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoomFurniture.java @@ -2,25 +2,22 @@ package de.hft.stuttgart.citydoctor2.datastructure; import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.model.building.BuildingFurniture; import java.io.Serial; -public class BuildingFurniture extends AbstractFurniture { +public class BuildingRoomFurniture extends AbstractFurniture { @Serial private static final long serialVersionUID = -1046265159354525567L; - public BuildingFurniture(BuildingRoom parent) { - setParent(parent); - parent.addRoomFurniture(this); + public void setGmlObject(BuildingFurniture gmlObject) { + super.setGmlObject(gmlObject); } - private BuildingFurniture(){} - - @Override public Copyable createCopyInstance(){ - return new BuildingFurniture(); + return new BuildingRoomFurniture(); } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index 6b0cb62..1098f83 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -24,6 +24,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import de.hft.stuttgart.citydoctor2.datastructure.*; +import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; +import de.hft.stuttgart.citydoctor2.datastructure.BuildingRoom; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.CityGMLVersion; @@ -34,8 +39,8 @@ import org.citygml4j.core.model.bridge.BridgeInstallation; import org.citygml4j.core.model.bridge.BridgeInstallationProperty; import org.citygml4j.core.model.bridge.BridgePart; import org.citygml4j.core.model.bridge.BridgePartProperty; -import org.citygml4j.core.model.building.BuildingInstallationProperty; -import org.citygml4j.core.model.building.BuildingPartProperty; +import org.citygml4j.core.model.building.*; +import org.citygml4j.core.model.building.BuildingFurniture; import org.citygml4j.core.model.construction.AbstractConstruction; import org.citygml4j.core.model.core.AbstractCityObject; import org.citygml4j.core.model.core.AbstractFeatureWithLifespan; @@ -79,33 +84,9 @@ import org.xmlobjects.gml.model.geometry.primitives.Solid; import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; import org.xmlobjects.gml.model.geometry.primitives.SurfaceProperty; -import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; -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.BridgeObject.BridgeType; -import de.hft.stuttgart.citydoctor2.datastructure.Building; -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.Geometry; -import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; -import de.hft.stuttgart.citydoctor2.datastructure.GmlElement; -import de.hft.stuttgart.citydoctor2.datastructure.GmlId; -import de.hft.stuttgart.citydoctor2.datastructure.Installation; -import de.hft.stuttgart.citydoctor2.datastructure.LandObject; -import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; -import de.hft.stuttgart.citydoctor2.datastructure.LinkedPolygon; -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.TransportationObject; import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject.TransportationType; -import de.hft.stuttgart.citydoctor2.datastructure.Vegetation; import de.hft.stuttgart.citydoctor2.datastructure.Vegetation.VegetationType; -import de.hft.stuttgart.citydoctor2.datastructure.Vertex; -import de.hft.stuttgart.citydoctor2.datastructure.WaterObject; import de.hft.stuttgart.citydoctor2.math.graph.KDTree; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.Localization; @@ -498,8 +479,8 @@ public class Citygml3FeatureMapper extends ObjectWalker { mapAbstractUnoccupiedSpace(ts, to); } - private void mapAbstractUnoccupiedSpace(AbstractUnoccupiedSpace aus, TransportationObject to) { - mapAbstractPhysicalSpace(aus, to); + private void mapAbstractUnoccupiedSpace(AbstractUnoccupiedSpace aus, CityObject co) { + mapAbstractPhysicalSpace(aus, co); } private TransportationObject parseAuxiliaryTrafficSpace(AuxiliaryTrafficSpace ats) { @@ -592,6 +573,15 @@ public class Citygml3FeatureMapper extends ObjectWalker { cdBuilding.addBuildingInstallation(bi); } + for (BuildingRoomProperty brProp : gmlAb.getBuildingRooms()) { + var gmlBr = brProp.getObject(); + if (gmlBr == null) { + continue; + } + BuildingRoom br = mapBuildingRoom(gmlBr); + cdBuilding.addBuildingRoom(br); + } + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); for (AbstractSpaceBoundaryProperty surfaceProp : gmlAb.getBoundaries()) { if (!surfaceProp.isSetObject()) { @@ -620,6 +610,82 @@ public class Citygml3FeatureMapper extends ObjectWalker { } } } + for (BuildingFurnitureProperty bfProp : gmlAb.getBuildingFurniture()){ + var gmlBf = bfProp.getObject(); + if (gmlBf == null) { + continue; + } + BuildingRoomFurniture bf = mapBuildingFurniture(gmlBf); + cdBuilding.addBuildingRoomFurniture(bf); + } + } + + private BuildingRoom mapBuildingRoom(org.citygml4j.core.model.building.BuildingRoom gmlBr) { + + BuildingRoom br = new BuildingRoom(); + br.setGmlObject(gmlBr); + mapAbstractUnoccupiedSpace(gmlBr,br); + + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); + for (AbstractSpaceBoundaryProperty surfaceProp : gmlBr.getBoundaries()) { + if (!surfaceProp.isSetObject()) { + continue; + } + AbstractSpaceBoundary surface = surfaceProp.getObject(); + surface.accept(surfaceMapper); + } + for (BoundarySurface bs : surfaceMapper.getSurfaces()) { + br.addBoundarySurface(bs); + for (Geometry geom : bs.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + p.setPartOfSurface(bs); + } + } + } + for (BuildingInstallationProperty biProp : gmlBr.getBuildingInstallations()) { + var gmlBi = biProp.getObject(); + if (gmlBi == null) { + // ignore empty properties + continue; + } + Installation bi = mapBuildingInstallation(gmlBi); + br.addRoomInstallation(bi); + } + for (BuildingFurnitureProperty bfProp : gmlBr.getBuildingFurniture()){ + var gmlHref = bfProp.getHref(); + if (gmlHref == null) { + continue; + } + br.addFurnitureRef(bfProp); + } + + + return br; + } + + private BuildingRoomFurniture mapBuildingFurniture(BuildingFurniture gmlAF){ + BuildingRoomFurniture bf = new BuildingRoomFurniture(); + bf.setGmlObject(gmlAF); + mapAbstractOccupiedSpace(gmlAF, bf); + bf.unsetGmlGeometries(); + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); + for (AbstractSpaceBoundaryProperty surfaceProp : gmlAF.getBoundaries()) { + if (!surfaceProp.isSetObject()) { + continue; + } + AbstractSpaceBoundary surface = surfaceProp.getObject(); + surface.accept(surfaceMapper); + } + for (BoundarySurface bs : surfaceMapper.getSurfaces()) { + bf.addBoundarySurface(bs); + for (Geometry geom : bs.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + p.setPartOfSurface(bs); + } + } + } + return bf; + } private void updatePartOfSurface(AbstractBuilding cdBuilding, SurfaceMapper surfaceMapper) { -- GitLab From 43f226dbfc7e48730ff2c244421b634dc0e733fc Mon Sep 17 00:00:00 2001 From: Riegel Date: Mon, 23 Sep 2024 16:56:47 +0200 Subject: [PATCH 03/55] Add Rendering support for Furniture and Room objects --- .../datastructure/AbstractFurniture.java | 77 +++++++++++++++ .../citydoctor2/gui/CityDoctorController.java | 93 +++++++++--------- .../stuttgart/citydoctor2/gui/Renderer.java | 94 ++++++++++++++----- .../gui/tree/AllFurnitureNode.java | 45 +++++++++ .../citydoctor2/gui/tree/AllRoomsNode.java | 45 +++++++++ .../citydoctor2/gui/tree/FurnitureNode.java | 36 +++++++ .../citydoctor2/gui/tree/RoomNode.java | 36 +++++++ 7 files changed, 358 insertions(+), 68 deletions(-) create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllFurnitureNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllRoomsNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/FurnitureNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/RoomNode.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java index 1527ef5..139cd63 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractFurniture.java @@ -1,5 +1,8 @@ package de.hft.stuttgart.citydoctor2.datastructure; +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; import de.hft.stuttgart.citydoctor2.utils.CopyHandler; @@ -28,6 +31,63 @@ public abstract class AbstractFurniture extends CityObject{ private org.citygml4j.core.model.construction.AbstractFurniture af; + @Override + public void accept(Check c) { + super.accept(c); + if (c.canExecute(this)) { + c.check(this); + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.accept(c); + } + } + + @Override + public void collectContainedErrors(List errors){ + super.collectContainedErrors(errors); + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.collectContainedErrors(errors); + } + } + + @Override + public void clearAllContainedCheckResults(){ + super.clearAllContainedCheckResults(); + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.clearAllContainedCheckResults(); + } + } + + @Override + public boolean containsError(CheckId checkIdentifier){ + boolean hasError = super.containsError(checkIdentifier); + if (hasError){ + return true; + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + if (boundarySurface.containsError(checkIdentifier)){ + return true; + } + } + return false; + } + + @Override + public boolean containsAnyError(){ + boolean hasError = super.containsAnyError(); + if (hasError){ + return true; + } + for (BoundarySurface boundarySurface : boundarySurfaceList) { + if (boundarySurface.containsAnyError()){ + return true; + } + } + return false; + } + + + @Override public org.citygml4j.core.model.construction.AbstractFurniture getGmlObject(){ return af; @@ -107,6 +167,23 @@ public abstract class AbstractFurniture extends CityObject{ } + @Override + public void prepareForChecking(){ + super.prepareForChecking(); + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.prepareForChecking(); + } + } + + @Override + public void clearMetaInformation() { + super.clearMetaInformation(); + for (BoundarySurface boundarySurface : boundarySurfaceList) { + boundarySurface.clearMetaInformation(); + } + } + + @Override public FeatureType getFeatureType() { return FeatureType.FURNITURE; diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index fd85e67..d64ff1a 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -12,6 +12,8 @@ import java.util.StringJoiner; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import de.hft.stuttgart.citydoctor2.datastructure.*; +import de.hft.stuttgart.citydoctor2.gui.tree.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.core.CityModel; @@ -22,52 +24,8 @@ 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; @@ -441,6 +399,8 @@ public class CityDoctorController { createGeometryNodes(b, item); createBoundarySurfaceNodes(b.getBoundarySurfaces(), item); createBuildingInstallationNodes(b, item); + createBuildingRoomNodes(b, item); + createBuildingFurnitureNodes(b, item); createBuildingPartNodes(b, item); } } @@ -526,6 +486,51 @@ public class CityDoctorController { } } + private void createBuildingRoomNodes(Building ab, TreeItem root) { + createRoomNodes(ab.getBuildingRooms(), root); + } + + private void createRoomNodes(List rooms, TreeItem root) { + if (rooms.isEmpty()) { + return; + } + + AllRoomsNode allRoNode = new AllRoomsNode(rooms); + TreeItem allRoNodeTextItem = new TreeItem<>(allRoNode); + root.getChildren().add(allRoNodeTextItem); + for (AbstractRoom room : rooms) { + RoomNode roomNode = new RoomNode(room); + TreeItem roomNodeTextItem = new TreeItem<>(roomNode); + roomNodeTextItem.setExpanded(true); + allRoNodeTextItem.getChildren().add(roomNodeTextItem); + createGeometryNodes(room, roomNodeTextItem); + createBoundarySurfaceNodes(room.getBoundarySurfaceList(), roomNodeTextItem); + } + + } + + private void createBuildingFurnitureNodes(Building ab, TreeItem root) { + createFurnitureNodes(ab.getBuildingRoomFurnitureList(),root); + } + + private void createFurnitureNodes(List furniture, TreeItem root){ + if(furniture.isEmpty()) { + return; + } + + AllFurnitureNode allFnNode = new AllFurnitureNode(furniture); + TreeItem allFnNodeTextItem = new TreeItem<>(allFnNode); + root.getChildren().add(allFnNodeTextItem); + for (AbstractFurniture fn : furniture) { + FurnitureNode fnNode = new FurnitureNode(fn); + TreeItem fnNodeTextItem = new TreeItem<>(fnNode); + fnNodeTextItem.setExpanded(true); + allFnNodeTextItem.getChildren().add(fnNodeTextItem); + createGeometryNodes(fn, fnNodeTextItem); + createBoundarySurfaceNodes(fn.getBoundarySurfaceList(), fnNodeTextItem); + } + } + private void createBoundarySurfaceNodes(List bsList, TreeItem root) { if (bsList.isEmpty()) { return; diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java index 91d542d..d7c37ef 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java @@ -7,34 +7,12 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import de.hft.stuttgart.citydoctor2.datastructure.*; 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; @@ -208,6 +186,18 @@ public class Renderer { } } } + for (BuildingRoom br : b.getBuildingRooms()) { + addPolygons(br, polygons); + for (BoundarySurface bs : br.getBoundarySurfaceList()){ + addPolygons(bs, polygons); + } + } + for (BuildingRoomFurniture brf : b.getBuildingRoomFurnitureList()){ + addPolygons(brf, polygons); + for (BoundarySurface bs : brf.getBoundarySurfaceList()){ + addPolygons(bs, polygons); + } + } for (BuildingPart bp : b.getBuildingParts()) { polygons.addAll(setupBuildingPartPolygons(bp)); } @@ -247,7 +237,57 @@ public class Renderer { } return polygons; } - + + public void render(AbstractRoom room){ + refresher = () -> { + Set setupRoomPolygons = setupRoomPolygons(room); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupRoomPolygons)); + render(setupRoomPolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(room); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + public Set setupRoomPolygons(AbstractRoom room) { + Set polygons = new HashSet<>(); + addPolygons(room, polygons); + for (BoundarySurface bs : room.getBoundarySurfaceList()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + return polygons; + } + + public void render(AbstractFurniture furniture){ + refresher = () -> { + Set setupFurniturePolygons = setupFurniturePolygons(furniture); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupFurniturePolygons)); + render(setupFurniturePolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(furniture); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + public Set setupFurniturePolygons(AbstractFurniture furniture) { + Set polygons = new HashSet<>(); + addPolygons(furniture, polygons); + for (BoundarySurface bs : furniture.getBoundarySurfaceList()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + return polygons; + } + public void render(BridgeObject bridge) { refresher = () -> { Set setupBridgePolygons = setupBridgePolygons(bridge); @@ -657,6 +697,12 @@ public class Renderer { } } } + for (BuildingRoom br : ab.getBuildingRooms()) { + addPolygons(br, polygons); + for (BoundarySurface bs : br.getBoundarySurfaceList()){ + addPolygons(bs, polygons); + } + } for (BoundarySurface bs : ab.getBoundarySurfaces()) { addPolygons(bs, polygons); for (Opening o : bs.getOpenings()) { diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllFurnitureNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllFurnitureNode.java new file mode 100644 index 0000000..219d486 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllFurnitureNode.java @@ -0,0 +1,45 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.AbstractFurniture; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +import java.util.List; + +public class AllFurnitureNode extends Renderable{ + + private final List furniture; + + public AllFurnitureNode(List furniture) { + this.furniture = furniture; + } + + @Override + public String getText() { + return "Building Furniture"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean wasChecked = false; + for (AbstractFurniture af : furniture) { + if (af.isValidated()) { + wasChecked = true; + if (af.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + } + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllRoomsNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllRoomsNode.java new file mode 100644 index 0000000..1f2f002 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllRoomsNode.java @@ -0,0 +1,45 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.AbstractRoom; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +import java.util.List; + +public class AllRoomsNode extends Renderable{ + + private final List rooms; + + public AllRoomsNode(List rooms) { + this.rooms = rooms; + } + + @Override + public String getText() { + return "Building Rooms"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean wasChecked = false; + for (AbstractRoom ar : rooms) { + if (ar.isValidated()) { + wasChecked = true; + if (ar.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + } + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/FurnitureNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/FurnitureNode.java new file mode 100644 index 0000000..e78251a --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/FurnitureNode.java @@ -0,0 +1,36 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.AbstractFurniture; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class FurnitureNode extends Renderable{ + + private final AbstractFurniture furniture; + + public FurnitureNode(AbstractFurniture furniture) { + this.furniture = furniture; + } + + @Override + public String getText() { + return furniture.getGmlId().getGmlString(); + } + + + @Override + public void visit(Renderer renderer) { + renderer.render(furniture); + } + + @Override + public void refreshTextColor() { + if (!furniture.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (furniture.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/RoomNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/RoomNode.java new file mode 100644 index 0000000..21c283c --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/RoomNode.java @@ -0,0 +1,36 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.AbstractRoom; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class RoomNode extends Renderable{ + + private final AbstractRoom room; + + public RoomNode(AbstractRoom room) { + this.room = room; + } + + @Override + public String getText() { + return room.getGmlId().getGmlString(); + } + + + @Override + public void visit(Renderer renderer) { + renderer.render(room); + } + + @Override + public void refreshTextColor() { + if (!room.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (room.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } +} -- GitLab From 9e65baa7036ea643fb9eeaf568f690124651d7ec Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 1 Oct 2024 14:28:41 +0200 Subject: [PATCH 04/55] Fix Opening treenode text color bug --- .../citydoctor2/gui/tree/AllOpeningsNode.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOpeningsNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOpeningsNode.java index 6a1979d..64f35c9 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOpeningsNode.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOpeningsNode.java @@ -16,13 +16,21 @@ public class AllOpeningsNode extends Renderable { @Override public void refreshTextColor() { - for (Opening bp : openings) { - if (bp.containsAnyError()) { - setStatus(CheckStatus.ERROR); - return; + boolean wasChecked = false; + for (Opening op : openings) { + if (op.isValidated()) { + wasChecked = true; + if (op.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } } } - setStatus(CheckStatus.OK); + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } } @Override -- GitLab From accc496a808b2a80d5711b2a975900b6e52fe49e Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 1 Oct 2024 14:30:12 +0200 Subject: [PATCH 05/55] Add BuildingFurniture checking --- .../datastructure/AbstractBuilding.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java index 856abfa..f797caa 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java @@ -99,6 +99,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoom br : buildingRooms) { br.unsetGmlGeometries(); } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.unsetGmlGeometries(); + } } @Override @@ -121,6 +124,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoom br : buildingRooms) { br.reCreateGeometries(factory, config); } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.reCreateGeometries(factory, config); + } } private void reCreateBoundarySurface(GeometryFactory factory, ParserConfiguration config, BoundarySurface bs) { @@ -193,6 +199,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoom br : buildingRooms) { br.accept(c); } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.accept(c); + } } @Override @@ -207,6 +216,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoom br : buildingRooms) { br.collectContainedErrors(errors); } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.collectContainedErrors(errors); + } } @Override @@ -221,6 +233,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoom br : buildingRooms) { br.clearAllContainedCheckResults(); } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.clearAllContainedCheckResults(); + } } @Override @@ -244,6 +259,11 @@ public abstract class AbstractBuilding extends CityObject { return true; } } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + if (bfr.containsError(checkIdentifier)) { + return true; + } + } return false; } @@ -268,6 +288,11 @@ public abstract class AbstractBuilding extends CityObject { return true; } } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + if (bfr.containsAnyError()) { + return true; + } + } return false; } @@ -325,6 +350,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoom br : buildingRooms) { br.prepareForChecking(); } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.prepareForChecking(); + } } @Override @@ -339,6 +367,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoom br : buildingRooms) { br.clearMetaInformation(); } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.clearMetaInformation(); + } } @Override @@ -347,6 +378,7 @@ public abstract class AbstractBuilding extends CityObject { handler.addInstance(boundarySurfaceList); handler.addInstance(buildingInstallations); handler.addInstance(buildingRooms); + handler.addInstance(buildingRoomFurnitureList); } @Override @@ -362,6 +394,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoom originalBr : originalAb.buildingRooms) { buildingRooms.add(handler.getCopyInstance(originalBr)); } + for (BuildingRoomFurniture originalBFR : originalAb.buildingRoomFurnitureList) { + buildingRoomFurnitureList.add(handler.getCopyInstance(originalBFR)); + } ab = originalAb.ab; } -- GitLab From 5e5e7ad64b4da725de5defb77caa20d3be2977c6 Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 1 Oct 2024 14:30:48 +0200 Subject: [PATCH 06/55] Add support for CityFurniture objects --- .../datastructure/CityDoctorModel.java | 21 ++++ .../datastructure/CityFurniture.java | 114 ++++++++++++++++++ .../datastructure/FeatureType.java | 2 +- .../citygml3/Citygml3FeatureMapper.java | 16 +++ .../CityDoctorLocalization.properties | 1 + .../CityDoctorLocalization_de.properties | 1 + .../citydoctor2/gui/CityDoctorController.java | 41 +++++++ .../stuttgart/citydoctor2/gui/MainWindow.java | 19 +++ .../stuttgart/citydoctor2/gui/Renderer.java | 23 ++++ .../gui/tree/AllCityFurnitureNode.java | 33 +++++ .../gui/tree/CityFurnitureNode.java | 37 ++++++ .../stuttgart/citydoctor2/gui/MainWindow.fxml | 7 +- 12 files changed, 313 insertions(+), 2 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityFurniture.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/CityFurnitureNode.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 12fffe3..1bca2f3 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -57,6 +57,7 @@ public class CityDoctorModel { private final List buildings; private final List vegetation; private final List bridges; + private final List cityfurniture; private final List land; private final List roads; private final List water; @@ -82,6 +83,7 @@ public class CityDoctorModel { land = new ArrayList<>(); roads = new ArrayList<>(); water = new ArrayList<>(); + cityfurniture = new ArrayList<>(); globalErrors = new ArrayList<>(); } @@ -224,6 +226,7 @@ public class CityDoctorModel { collectErrorsFromList(errors, land); collectErrorsFromList(errors, roads); collectErrorsFromList(errors, water); + collectErrorsFromList(errors, cityfurniture); return new HashSet<>(errors); } @@ -253,6 +256,14 @@ public class CityDoctorModel { return bridges; } + public List getCityFurniture() { + return cityfurniture; + } + + public void addCityFurniture(CityFurniture coFurniture) { + cityfurniture.add(coFurniture); + } + public void setCityModel(CityModel cModel) { this.cModel = cModel; } @@ -314,6 +325,8 @@ public class CityDoctorModel { replaceLandObject(currentFeature, nextFeature); } else if (nextFeature instanceof WaterObject) { replaceWaterObject(currentFeature, nextFeature); + } else if (nextFeature instanceof CityFurniture) { + replaceCityFurniture(currentFeature, nextFeature); } } @@ -341,6 +354,14 @@ public class CityDoctorModel { vegetation.set(index, (Vegetation) nextFeature); } + private void replaceCityFurniture(CityObject currentFeature, CityObject nextFeature) { + int index = cityfurniture.indexOf(currentFeature); + if (index == -1) { + throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in vegetation"); + } + cityfurniture.set(index, (CityFurniture) nextFeature); + } + private void replaceTransportationObject(CityObject currentFeature, CityObject nextFeature) { int index = roads.indexOf(currentFeature); if (index == -1) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityFurniture.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityFurniture.java new file mode 100644 index 0000000..27ea27e --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityFurniture.java @@ -0,0 +1,114 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.model.core.AbstractCityObject; +import org.citygml4j.core.util.geometry.GeometryFactory; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.primitives.Solid; +import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; + +import java.io.Serial; + +public class CityFurniture extends CityObject{ + + @Serial + private static final long serialVersionUID = 3325661061543962473L; + + private org.citygml4j.core.model.cityfurniture.CityFurniture cgmlCityFurniture; + + @Override + public void accept(Check c) { + super.accept(c); + if (c.canExecute(this)) { + c.check(this); + } + } + + + @Override + public AbstractCityObject getGmlObject() { + return cgmlCityFurniture; + } + + public void setGmlObject(org.citygml4j.core.model.cityfurniture.CityFurniture gmlCityFurniture) { + cgmlCityFurniture = gmlCityFurniture; + } + + @Override + public void unsetGmlGeometries() { + cgmlCityFurniture.setLod0MultiSurface(null); + cgmlCityFurniture.setLod2MultiSurface(null); + cgmlCityFurniture.setLod3MultiSurface(null); + cgmlCityFurniture.setLod1Solid(null); + cgmlCityFurniture.setLod2Solid(null); + cgmlCityFurniture.setLod3Solid(null); + cgmlCityFurniture.getDeprecatedProperties().setLod4Geometry(null); + } + + @Override + public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { + for (Geometry geom : getGeometries()) { + if (geom.getType() == GeometryType.MULTI_SURFACE) { + MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config); + setMultiSurfaceAccordingToLod(geom, ms); + } else { + Solid solid = CityGmlUtils.createSolid(geom, factory, config); + setSolidAccordingToLod(geom, solid); + } + } + } + + private void setMultiSurfaceAccordingToLod(Geometry geom, MultiSurface ms) { + switch (geom.getLod()) { + case LOD0: + cgmlCityFurniture.setLod0MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD2: + cgmlCityFurniture.setLod2MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD3: + cgmlCityFurniture.setLod3MultiSurface(new MultiSurfaceProperty(ms)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " multi surface to buildings"); + } + } + + private void setSolidAccordingToLod(Geometry geom, Solid solid) { + switch (geom.getLod()) { + case LOD1: + cgmlCityFurniture.setLod1Solid(new SolidProperty(solid)); + break; + case LOD2: + cgmlCityFurniture.setLod2Solid(new SolidProperty(solid)); + break; + case LOD3: + cgmlCityFurniture.setLod3Solid(new SolidProperty(solid)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to buildings"); + } + } + + @Override + public FeatureType getFeatureType() { + return FeatureType.CITY_FURNITURE; + } + + @Override + public Copyable createCopyInstance() { + return new CityFurniture(); + } + + @Override + public void fillValues(Copyable original, CopyHandler handler) { + super.fillValues(original, handler); + CityFurniture originalCF = (CityFurniture) original; + cgmlCityFurniture = originalCF.cgmlCityFurniture; + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java index 27256ec..7e0488a 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java @@ -27,6 +27,6 @@ package de.hft.stuttgart.citydoctor2.datastructure; public enum FeatureType { BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, INSTALLATION, OPENING, - BUILDING_PART, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE + BUILDING_PART, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index 1098f83..d7008d2 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -117,6 +117,22 @@ public class Citygml3FeatureMapper extends ObjectWalker { } + @Override + public void visit(org.citygml4j.core.model.cityfurniture.CityFurniture gmlCityFurniture) { + CityFurniture cf = new CityFurniture(); + cf.setGmlObject(gmlCityFurniture); + mapAbstractOccupiedSpace(gmlCityFurniture, cf); + resolveAndClearReferences(); + cf.unsetGmlGeometries(); + updateEdgesAndVertices(cf); + model.addCityFurniture(cf); + } + + public void visit(org.citygml4j.core.model.generics.GenericOccupiedSpace gos){ + System.out.println(gos.toString()); + } + + @Override public void visit(org.citygml4j.core.model.building.Building gmlBuilding) { Building cdBuilding = new Building(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index bc37e62..afd1fe6 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -49,6 +49,7 @@ MainWindow.transportationTab=Transportation MainWindow.bridgeTab=Bridges MainWindow.waterTab=Water MainWindow.terrainTab=Terrain +MainWindow.cityfurnitureTab=CityFurniture MainWindow.viewLabel=View MainWindow.showLabel=Show: MainWindow.searchLabel=Search: diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index f16c851..3fe2764 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -47,6 +47,7 @@ MainWindow.transportationTab=Verkehrsobjekte MainWindow.bridgeTab=Br\u00fccken MainWindow.waterTab=Gew\u00e4sser MainWindow.terrainTab=Gel\u00e4nde +MainWindow.cityfurnitureTab=Stadtm\u00f6bel MainWindow.viewLabel=Ansicht MainWindow.showLabel=Zeige: MainWindow.searchLabel=Suche: diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index d64ff1a..cc91c53 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -66,6 +66,7 @@ public class CityDoctorController { private AtomicInteger bridgeChunkNr = new AtomicInteger(0); private AtomicInteger waterChunkNr = new AtomicInteger(0); private AtomicInteger landChunkNr = new AtomicInteger(0); + private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0); public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) { this.mainWindow = mainWindow; @@ -149,6 +150,7 @@ public class CityDoctorController { buildBridges(model); buildWater(model); buildLand(model); + buildCityFurniture(model); } private void resetFeatureChunks() { @@ -158,6 +160,7 @@ public class CityDoctorController { bridgeChunkNr.set(0); waterChunkNr.set(0); landChunkNr.set(0); + cityFurnitureChunkNr.set(0); } private void buildLand(CityDoctorModel model) { @@ -212,6 +215,18 @@ public class CityDoctorController { } } + private void buildCityFurniture(CityDoctorModel model){ + if (model.getCityFurniture().isEmpty()){ + return; + } + TreeView cityFurnitureView = mainWindow.getCityFurnitureView(); + TreeItem cityFurnitureRoot = new TreeItem<>(new AllCityFurnitureNode(model.getCityFurniture())); + cityFurnitureRoot.setExpanded(true); + cityFurnitureView.setRoot(cityFurnitureRoot); + buildTreeFromList(model.getCityFurniture(), cityFurnitureView.getRoot(), cityFurnitureChunkNr); + addMoreButtonIfNecessary(model.getCityFurniture(), cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr); + } + private void buildWater(CityDoctorModel model) { if (model.getWater().isEmpty()) { return; @@ -375,6 +390,7 @@ public class CityDoctorController { mainWindow.getBridgeView().setRoot(null); mainWindow.getWaterView().setRoot(null); mainWindow.getTerrainView().setRoot(null); + mainWindow.getCityFurnitureView().setRoot(null); mainWindow.getErrorTree().getRoot().getChildren().clear(); mainWindow.getGlobalErrorsView().getItems().clear(); clearGeometryTrees(); @@ -788,6 +804,11 @@ public class CityDoctorController { cos = filterFeatures(searchString, model.getLand()); chunkCounter = landChunkNr; break; + case CITY_FURNITURE: + view = mainWindow.getCityFurnitureView(); + cos = filterFeatures(searchString, model.getLand()); + chunkCounter = cityFurnitureChunkNr; + break; default: throw new IllegalStateException("Unknown selected feature tab"); } @@ -855,6 +876,11 @@ public class CityDoctorController { cos = model.getLand(); chunkCounter = landChunkNr; break; + case CITY_FURNITURE: + view = mainWindow.getCityFurnitureView(); + cos = model.getCityFurniture(); + chunkCounter = cityFurnitureChunkNr; + break; default: throw new IllegalStateException("Unknown selected feature tab"); } @@ -926,6 +952,14 @@ public class CityDoctorController { fillTreeViewWithErrorCityObjects(mainWindow.getTerrainView(), model.getLand(), landChunkNr); } + public void fillTreeViewWithErrorCityFurniture(){ + if (model == null) { + return; + } + fillTreeViewWithErrorCityObjects(mainWindow.getCityFurnitureView(), + model.getCityFurniture(), cityFurnitureChunkNr); + } + public void fillTreeViewWithErrorTransportation() { if (model == null) { return; @@ -990,6 +1024,10 @@ public class CityDoctorController { buildWater(model); updateTree(mainWindow.getWaterView().getRoot()); break; + case CITY_FURNITURE: + buildCityFurniture(model); + updateTree(mainWindow.getCityFurnitureView().getRoot()); + break; default: throw new IllegalStateException(); } @@ -1077,6 +1115,9 @@ public class CityDoctorController { case WATER: fillTreeViewWithErrorWater(); break; + case CITY_FURNITURE: + fillTreeViewWithErrorCityFurniture(); + break; default: throw new IllegalStateException("Unknown selected feature tab: " + mainWindow.getSelectedTab()); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index 3b9aaf6..125dd0d 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -104,6 +104,9 @@ public class MainWindow extends Application { @FXML private TreeView terrainView; + @FXML + private TreeView cityFurnitureView; + @FXML private TreeView polygonView; @@ -179,6 +182,9 @@ public class MainWindow extends Application { @FXML private Tab terrainTab; + @FXML + private Tab cityFurnitureTab; + @FXML private Tab errorsTab; @@ -417,6 +423,7 @@ public class MainWindow extends Application { bridgeTab.setText(Localization.getText("MainWindow.bridgeTab")); waterTab.setText(Localization.getText("MainWindow.waterTab")); terrainTab.setText(Localization.getText("MainWindow.terrainTab")); + cityFurnitureTab.setText(Localization.getText("MainWindow.cityfurnitureTab")); viewLabel.setText(Localization.getText("MainWindow.viewLabel")); showLabel.setText(Localization.getText("MainWindow.showLabel")); searchLabel.setText(Localization.getText("MainWindow.searchLabel")); @@ -632,6 +639,9 @@ public class MainWindow extends Application { case 5: selectedTab = FeatureType.LAND; break; + case 6: + selectedTab = FeatureType.CITY_FURNITURE; + break; default: throw new IllegalStateException("Unknown tab index: " + index); } @@ -713,6 +723,10 @@ public class MainWindow extends Application { setupSelectListener(terrainView); terrainView.setCellFactory(param -> new RenderableTreeCell()); + cityFurnitureView.setShowRoot(true); + setupSelectListener(cityFurnitureView); + cityFurnitureView.setCellFactory(param -> new RenderableTreeCell()); + setupSelectListener(vertexView); vertexView.setRoot(new TreeItem<>()); vertexView.setCellFactory(param -> new RenderableTreeCell()); @@ -852,6 +866,10 @@ public class MainWindow extends Application { return terrainView; } + public TreeView getCityFurnitureView() { + return cityFurnitureView; + } + public TreeView getPolygonsView() { return polygonView; } @@ -898,6 +916,7 @@ public class MainWindow extends Application { transView.getSelectionModel().clearSelection(); waterView.getSelectionModel().clearSelection(); terrainView.getSelectionModel().clearSelection(); + cityFurnitureView.getSelectionModel().clearSelection(); bridgeView.getSelectionModel().clearSelection(); polygonView.getSelectionModel().clearSelection(); edgeView.getSelectionModel().clearSelection(); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java index d7c37ef..6672e39 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java @@ -382,6 +382,25 @@ public class Renderer { return polygons; } + public void render(CityFurniture cf) { + refresher = () -> { + Set setupCityFurniturePolygons = setupCityFurniturePolygons(cf); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityFurniturePolygons)); + render(setupCityFurniturePolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(cf); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private Set setupCityFurniturePolygons(CityFurniture cf) { + Set polygons = new HashSet<>(); + addPolygons(cf, polygons); + return polygons; + } + public void render(BoundarySurface bs) { refresher = () -> { Set setupBoundarySurfacePolygons = setupBoundarySurfacePolygons(bs); @@ -624,6 +643,10 @@ public class Renderer { renderCityObjects(water, Color.LIGHTSKYBLUE); } + public void renderCityFurniture(List cityFurniture){ + renderCityObjects(cityFurniture, Color.CYAN); + } + public void renderTerrain(List land) { renderLandObjects(land, Color.BROWN); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java new file mode 100644 index 0000000..e6b4c66 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java @@ -0,0 +1,33 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Building; +import de.hft.stuttgart.citydoctor2.datastructure.CityFurniture; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +import java.util.List; + +public class AllCityFurnitureNode extends Renderable{ + + private final List cityFurnitures; + + public AllCityFurnitureNode(List cityFurnitures) { + this.cityFurnitures = cityFurnitures; + } + + @Override + public void refreshTextColor() { + // no color changes + } + + @Override + public String getText() { + return "CityFurniture"; + } + + @Override + public void visit(Renderer renderer) { + renderer.renderCityFurniture(cityFurnitures); + } + +} + diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/CityFurnitureNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/CityFurnitureNode.java new file mode 100644 index 0000000..6571db8 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/CityFurnitureNode.java @@ -0,0 +1,37 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.AbstractRoom; +import de.hft.stuttgart.citydoctor2.datastructure.CityFurniture; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class CityFurnitureNode extends Renderable{ + + private final CityFurniture cityFurniture; + + public CityFurnitureNode(CityFurniture cityFurniture) { + this.cityFurniture = cityFurniture; + } + + @Override + public String getText() { + return cityFurniture.getGmlId().getGmlString(); + } + + + @Override + public void visit(Renderer renderer) { + renderer.render(cityFurniture); + } + + @Override + public void refreshTextColor() { + if (!cityFurniture.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (cityFurniture.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml index e9f2c95..5d741cd 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml @@ -19,7 +19,7 @@ - +
@@ -88,6 +88,11 @@ + + + + + -- GitLab From 89433d80b5efbe7de92a4701f8323f5df2e9f957 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 2 Oct 2024 16:27:32 +0200 Subject: [PATCH 07/55] Add support for GenericCityObject objects --- .../datastructure/CityDoctorModel.java | 25 +++++++++++++++++++ .../datastructure/FeatureType.java | 2 +- .../citygml3/Citygml3FeatureMapper.java | 9 ++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 1bca2f3..1ea1247 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -58,6 +58,7 @@ public class CityDoctorModel { private final List vegetation; private final List bridges; private final List cityfurniture; + private final List genericObjects; private final List land; private final List roads; private final List water; @@ -84,6 +85,7 @@ public class CityDoctorModel { roads = new ArrayList<>(); water = new ArrayList<>(); cityfurniture = new ArrayList<>(); + genericObjects = new ArrayList<>(); globalErrors = new ArrayList<>(); } @@ -227,6 +229,7 @@ public class CityDoctorModel { collectErrorsFromList(errors, roads); collectErrorsFromList(errors, water); collectErrorsFromList(errors, cityfurniture); + collectErrorsFromList(errors, genericObjects); return new HashSet<>(errors); } @@ -264,6 +267,14 @@ public class CityDoctorModel { cityfurniture.add(coFurniture); } + public List getGenericCityObjects(){ + return genericObjects; + } + + public void addGenericCityObject(GenericCityObject coGenericCityObject){ + genericObjects.add(coGenericCityObject); + } + public void setCityModel(CityModel cModel) { this.cModel = cModel; } @@ -327,6 +338,8 @@ public class CityDoctorModel { replaceWaterObject(currentFeature, nextFeature); } else if (nextFeature instanceof CityFurniture) { replaceCityFurniture(currentFeature, nextFeature); + } else if (nextFeature instanceof GenericCityObject) { + replaceGenericCityObject(currentFeature, nextFeature); } } @@ -362,6 +375,14 @@ public class CityDoctorModel { cityfurniture.set(index, (CityFurniture) nextFeature); } + private void replaceGenericCityObject(CityObject currentFeature, CityObject nextFeature) { + int index = genericObjects.indexOf(currentFeature); + if (index == -1) { + throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in generic city objects"); + } + genericObjects.set(index, (GenericCityObject) nextFeature); + } + private void replaceTransportationObject(CityObject currentFeature, CityObject nextFeature) { int index = roads.indexOf(currentFeature); if (index == -1) { @@ -403,6 +424,10 @@ public class CityDoctorModel { land.add(co); } else if (co instanceof TinObject) { land.add(co); + } else if (co instanceof CityFurniture cf) { + cityfurniture.add(cf); + } else if (co instanceof GenericCityObject gco) { + genericObjects.add(gco); } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java index 7e0488a..bce998e 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java @@ -27,6 +27,6 @@ package de.hft.stuttgart.citydoctor2.datastructure; public enum FeatureType { BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, INSTALLATION, OPENING, - BUILDING_PART, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE + BUILDING_PART, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE, GENERIC_CITY_OBJECT } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index d7008d2..4094f5b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -128,8 +128,15 @@ public class Citygml3FeatureMapper extends ObjectWalker { model.addCityFurniture(cf); } + @Override public void visit(org.citygml4j.core.model.generics.GenericOccupiedSpace gos){ - System.out.println(gos.toString()); + GenericCityObject gco = new GenericCityObject(); + gco.setGmlObject(gos); + mapAbstractOccupiedSpace(gos, gco); + resolveAndClearReferences(); + gco.unsetGmlGeometries(); + updateEdgesAndVertices(gco); + model.addGenericCityObject(gco); } -- GitLab From 6b536067cbb4f7c8bb8bce66767aec1d44f25acb Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 2 Oct 2024 16:28:27 +0200 Subject: [PATCH 08/55] Add rendering of GenericCityObject objects --- .../datastructure/GenericCityObject.java | 227 ++++++++++++++++++ .../CityDoctorLocalization.properties | 1 + .../CityDoctorLocalization_de.properties | 1 + .../citydoctor2/gui/CityDoctorController.java | 57 ++++- .../stuttgart/citydoctor2/gui/MainWindow.java | 19 ++ .../stuttgart/citydoctor2/gui/Renderer.java | 25 ++ .../gui/tree/AllCityFurnitureNode.java | 2 +- .../gui/tree/AllOtherObjectsNode.java | 46 ++++ .../gui/tree/GenericCityObjectsNode.java | 36 +++ .../stuttgart/citydoctor2/gui/MainWindow.fxml | 5 + 10 files changed, 412 insertions(+), 7 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericCityObject.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOtherObjectsNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/GenericCityObjectsNode.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericCityObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericCityObject.java new file mode 100644 index 0000000..5bfe8d8 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GenericCityObject.java @@ -0,0 +1,227 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.citygml4j.core.model.core.AbstractCityObject; +import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty; +import org.citygml4j.core.util.geometry.GeometryFactory; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.primitives.Solid; +import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +/** + * + */ +public class GenericCityObject extends CityObject{ + + @Serial + private static final long serialVersionUID = 5886527664552294353L; + + private org.citygml4j.core.model.generics.GenericOccupiedSpace cgmlGos; + + private static final Logger logger = LogManager.getLogger(GenericCityObject.class); + + private final List boundarySurfaceList = new ArrayList<>(); + + @Override + public void accept(Check c) { + super.accept(c); + if (c.canExecute(this)) { + c.check(this); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.accept(c); + } + } + + @Override + public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { + for (Geometry geom : getGeometries()) { + if (geom.getType() == GeometryType.MULTI_SURFACE) { + MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config); + setMultiSurfaceAccordingToLod(geom, ms); + } else { + Solid solid = CityGmlUtils.createSolid(geom, factory, config); + setSolidAccordingToLod(geom, solid); + } + } + for (BoundarySurface bs : boundarySurfaceList) { + reCreateBoundarySurface(factory, config, bs); + } + } + + private void reCreateBoundarySurface(GeometryFactory factory, ParserConfiguration config, BoundarySurface bs) { + if (bs.getGeometries().isEmpty()) { + for (AbstractSpaceBoundaryProperty bsp : cgmlGos.getBoundaries()) { + if (bsp.getObject() != null && bsp.getObject() == bs.getGmlObject()) { + logger.warn("Found empty boundary surface: {}, removing from generic object", bs.getGmlId()); + cgmlGos.getBoundaries().remove(bsp); + break; + } + } + return; + } + bs.reCreateGeometries(factory, config); + } + + private void setMultiSurfaceAccordingToLod(Geometry geom, MultiSurface ms) { + switch (geom.getLod()) { + case LOD0: + cgmlGos.setLod0MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD2: + cgmlGos.setLod2MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD3: + cgmlGos.setLod3MultiSurface(new MultiSurfaceProperty(ms)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " multi surface to buildings"); + } + } + + private void setSolidAccordingToLod(Geometry geom, Solid solid) { + switch (geom.getLod()) { + case LOD1: + cgmlGos.setLod1Solid(new SolidProperty(solid)); + break; + case LOD2: + cgmlGos.setLod2Solid(new SolidProperty(solid)); + break; + case LOD3: + cgmlGos.setLod3Solid(new SolidProperty(solid)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to buildings"); + } + } + + @Override + public void collectContainedErrors(List errors) { + super.collectContainedErrors(errors); + for (BoundarySurface bs : boundarySurfaceList) { + bs.collectContainedErrors(errors); + } + } + + @Override + public void clearAllContainedCheckResults() { + super.clearAllContainedCheckResults(); + for (BoundarySurface bs : boundarySurfaceList) { + bs.clearAllContainedCheckResults(); + } + } + + @Override + public boolean containsError(CheckId checkIdentifier) { + boolean hasError = super.containsError(checkIdentifier); + if (hasError) { + return true; + } + for (BoundarySurface bs : boundarySurfaceList) { + if (bs.containsError(checkIdentifier)) { + return true; + } + } + return false; + } + + @Override + public boolean containsAnyError() { + boolean hasError = super.containsAnyError(); + if (hasError) { + return true; + } + for (BoundarySurface bs : boundarySurfaceList) { + if (bs.containsAnyError()) { + return true; + } + } + return false; + } + + @Override + public void prepareForChecking() { + super.prepareForChecking(); + for (BoundarySurface bs : boundarySurfaceList) { + bs.prepareForChecking(); + } + } + + @Override + public void clearMetaInformation() { + super.clearMetaInformation(); + for (BoundarySurface bs : boundarySurfaceList) { + bs.clearMetaInformation(); + } + } + + @Override + public void collectInstances(CopyHandler handler) { + super.collectInstances(handler); + handler.addInstance(boundarySurfaceList); + } + + @Override + public void fillValues(Copyable original, CopyHandler handler) { + super.fillValues(original, handler); + GenericCityObject originalGos = (GenericCityObject) original; + for (BoundarySurface originalBs : originalGos.boundarySurfaceList) { + boundarySurfaceList.add(handler.getCopyInstance(originalBs)); + } + cgmlGos = originalGos.cgmlGos; + } + + @Override + public AbstractCityObject getGmlObject() { + return null; + } + + @Override + public FeatureType getFeatureType() { + return FeatureType.GENERIC_CITY_OBJECT; + } + + public List getBoundarySurfaces() { + return boundarySurfaceList; + } + + @Override + public void unsetGmlGeometries() { + cgmlGos.setLod1Solid(null); + cgmlGos.setLod2Solid(null); + cgmlGos.setLod3Solid(null); + cgmlGos.setLod2MultiSurface(null); + cgmlGos.setLod3MultiSurface(null); + for (BoundarySurface bs : boundarySurfaceList) { + bs.unsetGmlGeometries(); + } + } + + public void setGmlObject(org.citygml4j.core.model.generics.GenericOccupiedSpace gos){ + this.cgmlGos = gos; + } + + public void addBoundarySurface(BoundarySurface bs) { + boundarySurfaceList.add(bs); + bs.setParent(this); + } + + + @Override + public Copyable createCopyInstance() { + return new GenericCityObject(); + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index afd1fe6..04f69e9 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -50,6 +50,7 @@ MainWindow.bridgeTab=Bridges MainWindow.waterTab=Water MainWindow.terrainTab=Terrain MainWindow.cityfurnitureTab=CityFurniture +MainWindow.otherObjectsTab=Other MainWindow.viewLabel=View MainWindow.showLabel=Show: MainWindow.searchLabel=Search: diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 3fe2764..1b0315d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -48,6 +48,7 @@ MainWindow.bridgeTab=Br\u00fccken MainWindow.waterTab=Gew\u00e4sser MainWindow.terrainTab=Gel\u00e4nde MainWindow.cityfurnitureTab=Stadtm\u00f6bel +MainWindow.otherObjectsTab=Andere MainWindow.viewLabel=Ansicht MainWindow.showLabel=Zeige: MainWindow.searchLabel=Suche: diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index cc91c53..53e4a8f 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -67,6 +67,7 @@ public class CityDoctorController { private AtomicInteger waterChunkNr = new AtomicInteger(0); private AtomicInteger landChunkNr = new AtomicInteger(0); private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0); + private AtomicInteger otherObjectsChunkNr = new AtomicInteger(0); public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) { this.mainWindow = mainWindow; @@ -151,6 +152,7 @@ public class CityDoctorController { buildWater(model); buildLand(model); buildCityFurniture(model); + buildOtherCityObjects(model); } private void resetFeatureChunks() { @@ -161,6 +163,7 @@ public class CityDoctorController { waterChunkNr.set(0); landChunkNr.set(0); cityFurnitureChunkNr.set(0); + otherObjectsChunkNr.set(0); } private void buildLand(CityDoctorModel model) { @@ -227,6 +230,18 @@ public class CityDoctorController { addMoreButtonIfNecessary(model.getCityFurniture(), cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr); } + private void buildOtherCityObjects(CityDoctorModel model){ + if (model.getGenericCityObjects().isEmpty()){ + return; + } + TreeView otherObjectsView = mainWindow.getOtherObjectsView(); + TreeItem otherObjectsRoot = new TreeItem<>(new AllOtherObjectsNode(model.getGenericCityObjects())); + otherObjectsRoot.setExpanded(true); + otherObjectsView.setRoot(otherObjectsRoot); + buildTreeFromList(model.getGenericCityObjects(), otherObjectsView.getRoot(), otherObjectsChunkNr); + addMoreButtonIfNecessary(model.getGenericCityObjects(), otherObjectsView, otherObjectsRoot, otherObjectsChunkNr); + } + private void buildWater(CityDoctorModel model) { if (model.getWater().isEmpty()) { return; @@ -391,6 +406,7 @@ public class CityDoctorController { mainWindow.getWaterView().setRoot(null); mainWindow.getTerrainView().setRoot(null); mainWindow.getCityFurnitureView().setRoot(null); + mainWindow.getOtherObjectsView().setRoot(null); mainWindow.getErrorTree().getRoot().getChildren().clear(); mainWindow.getGlobalErrorsView().getItems().clear(); clearGeometryTrees(); @@ -657,8 +673,10 @@ public class CityDoctorController { updateTree(mainWindow.getVegetationView().getRoot()); updateTree(mainWindow.getBridgeView().getRoot()); updateTree(mainWindow.getTerrainView().getRoot()); + updateTree(mainWindow.getCityFurnitureView().getRoot()); updateTree(mainWindow.getTransportationView().getRoot()); updateTree(mainWindow.getWaterView().getRoot()); + updateTree(mainWindow.getOtherObjectsView().getRoot()); renderer.updateErrors(); } @@ -703,6 +721,8 @@ public class CityDoctorController { addErrorStats(stats, model.getTransportation()); addErrorStats(stats, model.getVegetation()); addErrorStats(stats, model.getWater()); + addErrorStats(stats, model.getCityFurniture()); + addErrorStats(stats, model.getGenericCityObjects()); return stats; } @@ -725,7 +745,7 @@ public class CityDoctorController { 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")); @@ -806,9 +826,14 @@ public class CityDoctorController { break; case CITY_FURNITURE: view = mainWindow.getCityFurnitureView(); - cos = filterFeatures(searchString, model.getLand()); + cos = filterFeatures(searchString, model.getCityFurniture()); chunkCounter = cityFurnitureChunkNr; break; + case GENERIC_CITY_OBJECT: + view = mainWindow.getOtherObjectsView(); + cos = filterFeatures(searchString, model.getGenericCityObjects()); + chunkCounter = otherObjectsChunkNr; + break; default: throw new IllegalStateException("Unknown selected feature tab"); } @@ -877,10 +902,15 @@ public class CityDoctorController { chunkCounter = landChunkNr; break; case CITY_FURNITURE: - view = mainWindow.getCityFurnitureView(); - cos = model.getCityFurniture(); - chunkCounter = cityFurnitureChunkNr; - break; + view = mainWindow.getCityFurnitureView(); + cos = model.getCityFurniture(); + chunkCounter = cityFurnitureChunkNr; + break; + case GENERIC_CITY_OBJECT: + view = mainWindow.getOtherObjectsView(); + cos = model.getGenericCityObjects(); + chunkCounter = otherObjectsChunkNr; + break; default: throw new IllegalStateException("Unknown selected feature tab"); } @@ -960,6 +990,14 @@ public class CityDoctorController { model.getCityFurniture(), cityFurnitureChunkNr); } + public void fillTreeViewWithErrorOtherObjects(){ + if (model == null) { + return; + } + fillTreeViewWithErrorCityObjects(mainWindow.getOtherObjectsView(), + model.getGenericCityObjects(), otherObjectsChunkNr); + } + public void fillTreeViewWithErrorTransportation() { if (model == null) { return; @@ -1028,6 +1066,10 @@ public class CityDoctorController { buildCityFurniture(model); updateTree(mainWindow.getCityFurnitureView().getRoot()); break; + case GENERIC_CITY_OBJECT: + buildOtherCityObjects(model); + updateTree(mainWindow.getOtherObjectsView().getRoot()); + break; default: throw new IllegalStateException(); } @@ -1118,6 +1160,9 @@ public class CityDoctorController { case CITY_FURNITURE: fillTreeViewWithErrorCityFurniture(); break; + case GENERIC_CITY_OBJECT: + fillTreeViewWithErrorOtherObjects(); + break; default: throw new IllegalStateException("Unknown selected feature tab: " + mainWindow.getSelectedTab()); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index 125dd0d..66b03fd 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -107,6 +107,9 @@ public class MainWindow extends Application { @FXML private TreeView cityFurnitureView; + @FXML + TreeView otherObjectsView; + @FXML private TreeView polygonView; @@ -185,6 +188,9 @@ public class MainWindow extends Application { @FXML private Tab cityFurnitureTab; + @FXML + private Tab otherObjectsTab; + @FXML private Tab errorsTab; @@ -424,6 +430,7 @@ public class MainWindow extends Application { waterTab.setText(Localization.getText("MainWindow.waterTab")); terrainTab.setText(Localization.getText("MainWindow.terrainTab")); cityFurnitureTab.setText(Localization.getText("MainWindow.cityfurnitureTab")); + otherObjectsTab.setText(Localization.getText("MainWindow.otherObjectsTab")); viewLabel.setText(Localization.getText("MainWindow.viewLabel")); showLabel.setText(Localization.getText("MainWindow.showLabel")); searchLabel.setText(Localization.getText("MainWindow.searchLabel")); @@ -642,6 +649,9 @@ public class MainWindow extends Application { case 6: selectedTab = FeatureType.CITY_FURNITURE; break; + case 7: + selectedTab = FeatureType.GENERIC_CITY_OBJECT; + break; default: throw new IllegalStateException("Unknown tab index: " + index); } @@ -727,6 +737,10 @@ public class MainWindow extends Application { setupSelectListener(cityFurnitureView); cityFurnitureView.setCellFactory(param -> new RenderableTreeCell()); + otherObjectsView.setShowRoot(true); + setupSelectListener(otherObjectsView); + otherObjectsView.setCellFactory(param -> new RenderableTreeCell()); + setupSelectListener(vertexView); vertexView.setRoot(new TreeItem<>()); vertexView.setCellFactory(param -> new RenderableTreeCell()); @@ -870,6 +884,10 @@ public class MainWindow extends Application { return cityFurnitureView; } + public TreeView getOtherObjectsView(){ + return otherObjectsView; + } + public TreeView getPolygonsView() { return polygonView; } @@ -917,6 +935,7 @@ public class MainWindow extends Application { waterView.getSelectionModel().clearSelection(); terrainView.getSelectionModel().clearSelection(); cityFurnitureView.getSelectionModel().clearSelection(); + otherObjectsView.getSelectionModel().clearSelection(); bridgeView.getSelectionModel().clearSelection(); polygonView.getSelectionModel().clearSelection(); edgeView.getSelectionModel().clearSelection(); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java index 6672e39..c152b4a 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java @@ -401,6 +401,31 @@ public class Renderer { return polygons; } + public void render(GenericCityObject gco){ + refresher = () -> { + Set setupGenericCityObjectPolygons = setupGenericCityObjectPolygons(gco); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupGenericCityObjectPolygons)); + render(setupGenericCityObjectPolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(gco); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private Set setupGenericCityObjectPolygons(GenericCityObject gco) { + Set polygons = new HashSet<>(); + addPolygons(gco, polygons); + for (BoundarySurface bs : gco.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + return polygons; + } + public void render(BoundarySurface bs) { refresher = () -> { Set setupBoundarySurfacePolygons = setupBoundarySurfacePolygons(bs); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java index e6b4c66..3da1516 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java @@ -1,6 +1,6 @@ package de.hft.stuttgart.citydoctor2.gui.tree; -import de.hft.stuttgart.citydoctor2.datastructure.Building; + import de.hft.stuttgart.citydoctor2.datastructure.CityFurniture; import de.hft.stuttgart.citydoctor2.gui.Renderer; diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOtherObjectsNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOtherObjectsNode.java new file mode 100644 index 0000000..cdb90fe --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOtherObjectsNode.java @@ -0,0 +1,46 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.GenericCityObject; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +import java.util.List; + +public class AllOtherObjectsNode extends Renderable{ + + private final List genericObjects; + + public AllOtherObjectsNode(List genericObjects) { + this.genericObjects = genericObjects; + } + + @Override + public void refreshTextColor() { + boolean wasChecked = false; + for (GenericCityObject gco : genericObjects) { + if (gco.isValidated()) { + wasChecked = true; + if (gco.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + } + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } + + @Override + public String getText() { + return "GenericCityObjects"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/GenericCityObjectsNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/GenericCityObjectsNode.java new file mode 100644 index 0000000..c5c4362 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/GenericCityObjectsNode.java @@ -0,0 +1,36 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.GenericCityObject; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class GenericCityObjectsNode extends Renderable{ + + private final GenericCityObject gcobject; + + public GenericCityObjectsNode(GenericCityObject gcobject) { + this.gcobject = gcobject; + } + + @Override + public String getText() { + return gcobject.getGmlId().getGmlString(); + } + + + @Override + public void visit(Renderer renderer) { + renderer.render(gcobject); + } + + @Override + public void refreshTextColor() { + if (!gcobject.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (gcobject.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml index 5d741cd..d25775a 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml @@ -93,6 +93,11 @@ + + + + + -- GitLab From 9e16147718874aecc749111a43950086716f8794 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 2 Oct 2024 16:32:09 +0200 Subject: [PATCH 09/55] Format code --- .../citydoctor2/gui/CityDoctorController.java | 2234 ++++++++--------- .../stuttgart/citydoctor2/gui/MainWindow.java | 1758 ++++++------- 2 files changed, 1996 insertions(+), 1996 deletions(-) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index 53e4a8f..0a0fb47 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -45,1133 +45,1133 @@ 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); - private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0); - private AtomicInteger otherObjectsChunkNr = 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); - buildCityFurniture(model); + 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); + private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0); + private AtomicInteger otherObjectsChunkNr = 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); + buildCityFurniture(model); buildOtherCityObjects(model); - } - - private void resetFeatureChunks() { - buildingChunkNr.set(0); - vegetationChunkNr.set(0); - transportationChunkNr.set(0); - bridgeChunkNr.set(0); - waterChunkNr.set(0); - landChunkNr.set(0); - cityFurnitureChunkNr.set(0); - otherObjectsChunkNr.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 buildCityFurniture(CityDoctorModel model){ - if (model.getCityFurniture().isEmpty()){ - return; - } - TreeView cityFurnitureView = mainWindow.getCityFurnitureView(); - TreeItem cityFurnitureRoot = new TreeItem<>(new AllCityFurnitureNode(model.getCityFurniture())); - cityFurnitureRoot.setExpanded(true); - cityFurnitureView.setRoot(cityFurnitureRoot); - buildTreeFromList(model.getCityFurniture(), cityFurnitureView.getRoot(), cityFurnitureChunkNr); - addMoreButtonIfNecessary(model.getCityFurniture(), cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr); - } - - private void buildOtherCityObjects(CityDoctorModel model){ - if (model.getGenericCityObjects().isEmpty()){ - return; - } - TreeView otherObjectsView = mainWindow.getOtherObjectsView(); - TreeItem otherObjectsRoot = new TreeItem<>(new AllOtherObjectsNode(model.getGenericCityObjects())); - otherObjectsRoot.setExpanded(true); - otherObjectsView.setRoot(otherObjectsRoot); - buildTreeFromList(model.getGenericCityObjects(), otherObjectsView.getRoot(), otherObjectsChunkNr); - addMoreButtonIfNecessary(model.getGenericCityObjects(), otherObjectsView, otherObjectsRoot, otherObjectsChunkNr); - } - - 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.getCityFurnitureView().setRoot(null); + } + + private void resetFeatureChunks() { + buildingChunkNr.set(0); + vegetationChunkNr.set(0); + transportationChunkNr.set(0); + bridgeChunkNr.set(0); + waterChunkNr.set(0); + landChunkNr.set(0); + cityFurnitureChunkNr.set(0); + otherObjectsChunkNr.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 buildCityFurniture(CityDoctorModel model) { + if (model.getCityFurniture().isEmpty()) { + return; + } + TreeView cityFurnitureView = mainWindow.getCityFurnitureView(); + TreeItem cityFurnitureRoot = new TreeItem<>(new AllCityFurnitureNode(model.getCityFurniture())); + cityFurnitureRoot.setExpanded(true); + cityFurnitureView.setRoot(cityFurnitureRoot); + buildTreeFromList(model.getCityFurniture(), cityFurnitureView.getRoot(), cityFurnitureChunkNr); + addMoreButtonIfNecessary(model.getCityFurniture(), cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr); + } + + private void buildOtherCityObjects(CityDoctorModel model) { + if (model.getGenericCityObjects().isEmpty()) { + return; + } + TreeView otherObjectsView = mainWindow.getOtherObjectsView(); + TreeItem otherObjectsRoot = new TreeItem<>(new AllOtherObjectsNode(model.getGenericCityObjects())); + otherObjectsRoot.setExpanded(true); + otherObjectsView.setRoot(otherObjectsRoot); + buildTreeFromList(model.getGenericCityObjects(), otherObjectsView.getRoot(), otherObjectsChunkNr); + addMoreButtonIfNecessary(model.getGenericCityObjects(), otherObjectsView, otherObjectsRoot, otherObjectsChunkNr); + } + + 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.getCityFurnitureView().setRoot(null); mainWindow.getOtherObjectsView().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); - createBuildingRoomNodes(b, item); - createBuildingFurnitureNodes(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 createBuildingRoomNodes(Building ab, TreeItem root) { - createRoomNodes(ab.getBuildingRooms(), root); - } - - private void createRoomNodes(List rooms, TreeItem root) { - if (rooms.isEmpty()) { - return; - } - - AllRoomsNode allRoNode = new AllRoomsNode(rooms); - TreeItem allRoNodeTextItem = new TreeItem<>(allRoNode); - root.getChildren().add(allRoNodeTextItem); - for (AbstractRoom room : rooms) { - RoomNode roomNode = new RoomNode(room); - TreeItem roomNodeTextItem = new TreeItem<>(roomNode); - roomNodeTextItem.setExpanded(true); - allRoNodeTextItem.getChildren().add(roomNodeTextItem); - createGeometryNodes(room, roomNodeTextItem); - createBoundarySurfaceNodes(room.getBoundarySurfaceList(), roomNodeTextItem); - } - - } - - private void createBuildingFurnitureNodes(Building ab, TreeItem root) { - createFurnitureNodes(ab.getBuildingRoomFurnitureList(),root); - } - - private void createFurnitureNodes(List furniture, TreeItem root){ - if(furniture.isEmpty()) { - return; - } - - AllFurnitureNode allFnNode = new AllFurnitureNode(furniture); - TreeItem allFnNodeTextItem = new TreeItem<>(allFnNode); - root.getChildren().add(allFnNodeTextItem); - for (AbstractFurniture fn : furniture) { - FurnitureNode fnNode = new FurnitureNode(fn); - TreeItem fnNodeTextItem = new TreeItem<>(fnNode); - fnNodeTextItem.setExpanded(true); - allFnNodeTextItem.getChildren().add(fnNodeTextItem); - createGeometryNodes(fn, fnNodeTextItem); - createBoundarySurfaceNodes(fn.getBoundarySurfaceList(), fnNodeTextItem); - } - } - - 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.getCityFurnitureView().getRoot()); - updateTree(mainWindow.getTransportationView().getRoot()); - updateTree(mainWindow.getWaterView().getRoot()); + 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); + createBuildingRoomNodes(b, item); + createBuildingFurnitureNodes(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 createBuildingRoomNodes(Building ab, TreeItem root) { + createRoomNodes(ab.getBuildingRooms(), root); + } + + private void createRoomNodes(List rooms, TreeItem root) { + if (rooms.isEmpty()) { + return; + } + + AllRoomsNode allRoNode = new AllRoomsNode(rooms); + TreeItem allRoNodeTextItem = new TreeItem<>(allRoNode); + root.getChildren().add(allRoNodeTextItem); + for (AbstractRoom room : rooms) { + RoomNode roomNode = new RoomNode(room); + TreeItem roomNodeTextItem = new TreeItem<>(roomNode); + roomNodeTextItem.setExpanded(true); + allRoNodeTextItem.getChildren().add(roomNodeTextItem); + createGeometryNodes(room, roomNodeTextItem); + createBoundarySurfaceNodes(room.getBoundarySurfaceList(), roomNodeTextItem); + } + + } + + private void createBuildingFurnitureNodes(Building ab, TreeItem root) { + createFurnitureNodes(ab.getBuildingRoomFurnitureList(), root); + } + + private void createFurnitureNodes(List furniture, TreeItem root) { + if (furniture.isEmpty()) { + return; + } + + AllFurnitureNode allFnNode = new AllFurnitureNode(furniture); + TreeItem allFnNodeTextItem = new TreeItem<>(allFnNode); + root.getChildren().add(allFnNodeTextItem); + for (AbstractFurniture fn : furniture) { + FurnitureNode fnNode = new FurnitureNode(fn); + TreeItem fnNodeTextItem = new TreeItem<>(fnNode); + fnNodeTextItem.setExpanded(true); + allFnNodeTextItem.getChildren().add(fnNodeTextItem); + createGeometryNodes(fn, fnNodeTextItem); + createBoundarySurfaceNodes(fn.getBoundarySurfaceList(), fnNodeTextItem); + } + } + + 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.getCityFurnitureView().getRoot()); + updateTree(mainWindow.getTransportationView().getRoot()); + updateTree(mainWindow.getWaterView().getRoot()); updateTree(mainWindow.getOtherObjectsView().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()); + 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()); addErrorStats(stats, model.getCityFurniture()); addErrorStats(stats, model.getGenericCityObjects()); - 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; - case CITY_FURNITURE: - view = mainWindow.getCityFurnitureView(); - cos = filterFeatures(searchString, model.getCityFurniture()); - chunkCounter = cityFurnitureChunkNr; - break; - case GENERIC_CITY_OBJECT: - view = mainWindow.getOtherObjectsView(); - cos = filterFeatures(searchString, model.getGenericCityObjects()); - chunkCounter = otherObjectsChunkNr; - 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; - case CITY_FURNITURE: - view = mainWindow.getCityFurnitureView(); - cos = model.getCityFurniture(); - chunkCounter = cityFurnitureChunkNr; - break; - case GENERIC_CITY_OBJECT: - view = mainWindow.getOtherObjectsView(); - cos = model.getGenericCityObjects(); - chunkCounter = otherObjectsChunkNr; - 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 fillTreeViewWithErrorCityFurniture(){ - if (model == null) { - return; - } - fillTreeViewWithErrorCityObjects(mainWindow.getCityFurnitureView(), - model.getCityFurniture(), cityFurnitureChunkNr); - } - - public void fillTreeViewWithErrorOtherObjects(){ - if (model == null) { - return; - } - fillTreeViewWithErrorCityObjects(mainWindow.getOtherObjectsView(), - model.getGenericCityObjects(), otherObjectsChunkNr); - } - - 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; - case CITY_FURNITURE: - buildCityFurniture(model); - updateTree(mainWindow.getCityFurnitureView().getRoot()); - break; + 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; + case CITY_FURNITURE: + view = mainWindow.getCityFurnitureView(); + cos = filterFeatures(searchString, model.getCityFurniture()); + chunkCounter = cityFurnitureChunkNr; + break; + case GENERIC_CITY_OBJECT: + view = mainWindow.getOtherObjectsView(); + cos = filterFeatures(searchString, model.getGenericCityObjects()); + chunkCounter = otherObjectsChunkNr; + 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; + case CITY_FURNITURE: + view = mainWindow.getCityFurnitureView(); + cos = model.getCityFurniture(); + chunkCounter = cityFurnitureChunkNr; + break; + case GENERIC_CITY_OBJECT: + view = mainWindow.getOtherObjectsView(); + cos = model.getGenericCityObjects(); + chunkCounter = otherObjectsChunkNr; + 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 fillTreeViewWithErrorCityFurniture() { + if (model == null) { + return; + } + fillTreeViewWithErrorCityObjects(mainWindow.getCityFurnitureView(), + model.getCityFurniture(), cityFurnitureChunkNr); + } + + public void fillTreeViewWithErrorOtherObjects() { + if (model == null) { + return; + } + fillTreeViewWithErrorCityObjects(mainWindow.getOtherObjectsView(), + model.getGenericCityObjects(), otherObjectsChunkNr); + } + + 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; + case CITY_FURNITURE: + buildCityFurniture(model); + updateTree(mainWindow.getCityFurnitureView().getRoot()); + break; case GENERIC_CITY_OBJECT: buildOtherCityObjects(model); updateTree(mainWindow.getOtherObjectsView().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; - case CITY_FURNITURE: - fillTreeViewWithErrorCityFurniture(); - 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; + case CITY_FURNITURE: + fillTreeViewWithErrorCityFurniture(); + break; case GENERIC_CITY_OBJECT: fillTreeViewWithErrorOtherObjects(); 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; - } + 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/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index 66b03fd..cf63a8d 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -80,937 +80,937 @@ import javafx.stage.Stage; public class MainWindow extends Application { - private static final Logger logger = LogManager.getLogger(MainWindow.class); + 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; + 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 buildingsView; - @FXML - private TreeView vegetationView; + @FXML + private TreeView vegetationView; - @FXML - private TreeView transView; + @FXML + private TreeView transView; - @FXML - private TreeView bridgeView; + @FXML + private TreeView bridgeView; - @FXML - private TreeView waterView; + @FXML + private TreeView waterView; - @FXML - private TreeView terrainView; + @FXML + private TreeView terrainView; - @FXML - private TreeView cityFurnitureView; + @FXML + private TreeView cityFurnitureView; - @FXML - TreeView otherObjectsView; + @FXML + TreeView otherObjectsView; - @FXML - private TreeView polygonView; + @FXML + private TreeView polygonView; - @FXML - private TreeView edgeView; + @FXML + private TreeView edgeView; - @FXML - private TreeView vertexView; + @FXML + private TreeView vertexView; - @FXML - private Pane meshView; + @FXML + private Pane meshView; - @FXML - private SplitPane mainContainer; + @FXML + private SplitPane mainContainer; - @FXML - private TabPane detailsTabPane; + @FXML + private TabPane detailsTabPane; - @FXML - private TabPane featurePane; + @FXML + private TabPane featurePane; - @FXML - private TextField searchField; + @FXML + private TextField searchField; - @FXML - private TreeView errorView; + @FXML + private TreeView errorView; - @FXML - private Button clearBtn; + @FXML + private Button clearBtn; - @FXML - private Button searchBtn; + @FXML + private Button searchBtn; - @FXML - private ToggleButton allButton; + @FXML + private ToggleButton allButton; - @FXML - private ToggleButton errorButton; + @FXML + private ToggleButton errorButton; - @FXML - private ComboBox showCityObjectsCombo; + @FXML + private ComboBox showCityObjectsCombo; - @FXML - private HBox viewPane; + @FXML + private HBox viewPane; - @FXML - private ListView globalErrorsView; + @FXML + private ListView globalErrorsView; - @FXML - private BorderPane mainPane; + @FXML + private BorderPane mainPane; - @FXML - private HBox viewButtonBox; + @FXML + private HBox viewButtonBox; - @FXML - private ComboBox languageSelector; + @FXML + private ComboBox languageSelector; - @FXML - private Tab buildingsTab; + @FXML + private Tab buildingsTab; - @FXML - private Tab vegetationTab; + @FXML + private Tab vegetationTab; - @FXML - private Tab transportationTab; + @FXML + private Tab transportationTab; - @FXML - private Tab bridgeTab; + @FXML + private Tab bridgeTab; - @FXML - private Tab waterTab; + @FXML + private Tab waterTab; - @FXML - private Tab terrainTab; + @FXML + private Tab terrainTab; - @FXML - private Tab cityFurnitureTab; + @FXML + private Tab cityFurnitureTab; - @FXML - private Tab otherObjectsTab; + @FXML + private Tab otherObjectsTab; - @FXML - private Tab errorsTab; + @FXML + private Tab errorsTab; - @FXML - private Tab polygonsTab; + @FXML + private Tab polygonsTab; - @FXML - private Tab edgesTab; + @FXML + private Tab edgesTab; - @FXML - private Tab verticesTab; + @FXML + private Tab verticesTab; - @FXML - private Tab logTab; + @FXML + private Tab logTab; - @FXML - private Tab globalErrorsTab; + @FXML + private Tab globalErrorsTab; - @FXML - private Label viewLabel; + @FXML + private Label viewLabel; - @FXML - private Label showLabel; + @FXML + private Label showLabel; - @FXML - private Label searchLabel; + @FXML + private Label searchLabel; - @FXML - private Label memoryLabel; + @FXML + private Label memoryLabel; - @FXML - private ProgressBar memoryBar; + @FXML + private ProgressBar memoryBar; - @FXML - private Label memoryConsumptionLabel; + @FXML + private Label memoryConsumptionLabel; - @FXML - private Label availableLabel; + @FXML + private Label availableLabel; - private Group meshGroup; + private Group meshGroup; - private Group world; - private PerspectiveCamera camera; - private Rotate cameraXRotation = new Rotate(); - private Rotate cameraZRotation = new Rotate(); + private Group world; + private PerspectiveCamera camera; + private Rotate cameraXRotation = new Rotate(); + private Rotate cameraZRotation = new Rotate(); - private double dragX; - private double dragY; + 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]; + 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")); - cityFurnitureTab.setText(Localization.getText("MainWindow.cityfurnitureTab")); - otherObjectsTab.setText(Localization.getText("MainWindow.otherObjectsTab")); - 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; - case 6: - selectedTab = FeatureType.CITY_FURNITURE; - break; - case 7: - selectedTab = FeatureType.GENERIC_CITY_OBJECT; - 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 node) { - 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()); - - cityFurnitureView.setShowRoot(true); - setupSelectListener(cityFurnitureView); - cityFurnitureView.setCellFactory(param -> new RenderableTreeCell()); - - otherObjectsView.setShowRoot(true); - setupSelectListener(otherObjectsView); - otherObjectsView.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 cd) { - 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 getCityFurnitureView() { - return cityFurnitureView; - } - - public TreeView getOtherObjectsView(){ - return otherObjectsView; - } - - 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(); - cityFurnitureView.getSelectionModel().clearSelection(); - otherObjectsView.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; - } + @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")); + cityFurnitureTab.setText(Localization.getText("MainWindow.cityfurnitureTab")); + otherObjectsTab.setText(Localization.getText("MainWindow.otherObjectsTab")); + 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; + case 6: + selectedTab = FeatureType.CITY_FURNITURE; + break; + case 7: + selectedTab = FeatureType.GENERIC_CITY_OBJECT; + 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 node) { + 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()); + + cityFurnitureView.setShowRoot(true); + setupSelectListener(cityFurnitureView); + cityFurnitureView.setCellFactory(param -> new RenderableTreeCell()); + + otherObjectsView.setShowRoot(true); + setupSelectListener(otherObjectsView); + otherObjectsView.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 cd) { + 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 getCityFurnitureView() { + return cityFurnitureView; + } + + public TreeView getOtherObjectsView() { + return otherObjectsView; + } + + 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(); + cityFurnitureView.getSelectionModel().clearSelection(); + otherObjectsView.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; + } } -- GitLab From 01bd12957e51f0f818a22595562818ec3c2e6ac2 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 2 Oct 2024 17:06:35 +0200 Subject: [PATCH 10/55] Add checking of CityFurniture and GenericObjects --- .../stuttgart/citydoctor2/check/Check.java | 26 +++++--------- .../datastructure/CityDoctorModel.java | 5 +-- .../citydoctor2/checks/CheckContainer.java | 34 +++++++++++-------- 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java index f69575c..4ae958f 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java @@ -26,23 +26,7 @@ import java.util.Map; import java.util.Set; import de.hft.stuttgart.citydoctor2.check.error.DependenciesNotMetError; -import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; -import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; -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.CityObject; -import de.hft.stuttgart.citydoctor2.datastructure.Geometry; -import de.hft.stuttgart.citydoctor2.datastructure.LandObject; -import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; -import de.hft.stuttgart.citydoctor2.datastructure.Opening; -import de.hft.stuttgart.citydoctor2.datastructure.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.WaterObject; +import de.hft.stuttgart.citydoctor2.datastructure.*; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; /** @@ -313,6 +297,14 @@ public abstract class Check { } + public void check(CityFurniture cf){ + + } + + public void check(GenericCityObject gco) { + + } + /** * The initialization method of this check. It will be called before any check * method will be executed. Override this if you want to have configurable diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index 1ea1247..ba31e26 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -125,7 +125,7 @@ public class CityDoctorModel { public Stream createFeatureStream() { return Stream.of(buildings.stream(), vegetation.stream(), bridges.stream(), land.stream(), roads.stream(), - water.stream()).flatMap(co -> co); + water.stream(), cityfurniture.stream(),genericObjects.stream()).flatMap(co -> co); } public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException { @@ -316,7 +316,8 @@ public class CityDoctorModel { } public int getNumberOfFeatures() { - return buildings.size() + vegetation.size() + bridges.size() + land.size() + roads.size() + water.size(); + return buildings.size() + vegetation.size() + bridges.size() + land.size() + roads.size() + water.size() + cityfurniture.size() + + genericObjects.size(); } public ParserConfiguration getParserConfig() { diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java index 2b40eb1..8b6f294 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/CheckContainer.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import de.hft.stuttgart.citydoctor2.datastructure.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -34,21 +35,6 @@ import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.RequirementType; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError; -import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; -import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; -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.CityObject; -import de.hft.stuttgart.citydoctor2.datastructure.Geometry; -import de.hft.stuttgart.citydoctor2.datastructure.LandObject; -import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; -import de.hft.stuttgart.citydoctor2.datastructure.Opening; -import de.hft.stuttgart.citydoctor2.datastructure.Polygon; -import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject; -import de.hft.stuttgart.citydoctor2.datastructure.Vegetation; -import de.hft.stuttgart.citydoctor2.datastructure.WaterObject; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.Localization; @@ -244,6 +230,24 @@ public class CheckContainer extends Check { } } + @Override + public void check(CityFurniture cf){ + try { + check.check(cf); + } catch (Exception e) { + handleException(e, cf); + } + } + + @Override + public void check(GenericCityObject gco) { + try { + check.check(gco); + } catch (Exception e) { + handleException(e, gco); + } + } + @Override public RequirementType getType() { return check.getType(); -- GitLab From 8dbd30cea693729a075c5bd8946623a53986a4b2 Mon Sep 17 00:00:00 2001 From: Riegel Date: Fri, 4 Oct 2024 15:29:14 +0200 Subject: [PATCH 11/55] Add support for BuildingSubdivisions --- .../datastructure/AbstractBuilding.java | 45 ++- .../AbstractBuildingSubdivision.java | 366 ++++++++++++++++++ .../datastructure/BuildingRoom.java | 12 +- 3 files changed, 412 insertions(+), 11 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java index f797caa..ca95600 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java @@ -56,7 +56,7 @@ public abstract class AbstractBuilding extends CityObject { private final List buildingInstallations = new ArrayList<>(2); private final List boundarySurfaceList = new ArrayList<>(); private final List buildingRooms = new ArrayList<>(); - + private final List buildingSubdivisions = new ArrayList<>(); private final List buildingRoomFurnitureList = new ArrayList<>(); private org.citygml4j.core.model.building.AbstractBuilding ab; @@ -102,6 +102,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.unsetGmlGeometries(); } + for (AbstractBuildingSubdivision abs : buildingSubdivisions) { + abs.unsetGmlGeometries(); + } } @Override @@ -127,6 +130,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.reCreateGeometries(factory, config); } + for (AbstractBuildingSubdivision abs : buildingSubdivisions) { + abs.reCreateGeometries(factory, config); + } } private void reCreateBoundarySurface(GeometryFactory factory, ParserConfiguration config, BoundarySurface bs) { @@ -202,6 +208,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.accept(c); } + for (AbstractBuildingSubdivision abs : buildingSubdivisions) { + abs.accept(c); + } } @Override @@ -219,6 +228,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.collectContainedErrors(errors); } + for (AbstractBuildingSubdivision abs : buildingSubdivisions) { + abs.collectContainedErrors(errors); + } } @Override @@ -236,6 +248,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.clearAllContainedCheckResults(); } + for (AbstractBuildingSubdivision abs : buildingSubdivisions) { + abs.clearAllContainedCheckResults(); + } } @Override @@ -264,6 +279,11 @@ public abstract class AbstractBuilding extends CityObject { return true; } } + for (AbstractBuildingSubdivision abs : buildingSubdivisions) { + if (abs.containsError(checkIdentifier)) { + return true; + } + } return false; } @@ -293,6 +313,11 @@ public abstract class AbstractBuilding extends CityObject { return true; } } + for (AbstractBuildingSubdivision abs : buildingSubdivisions) { + if (abs.containsAnyError()) { + return true; + } + } return false; } @@ -320,7 +345,9 @@ public abstract class AbstractBuilding extends CityObject { roomFurniture.setParent(this); } - + public void addAbstractBuildingSubdivision(AbstractBuildingSubdivision abs) { + buildingSubdivisions.add(abs); + } public void setGmlObject(org.citygml4j.core.model.building.AbstractBuilding ab) { this.ab = ab; @@ -338,6 +365,10 @@ public abstract class AbstractBuilding extends CityObject { return buildingRoomFurnitureList; } + public List getBuildingSubdivisions() { + return buildingSubdivisions; + } + @Override public void prepareForChecking() { super.prepareForChecking(); @@ -353,6 +384,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.prepareForChecking(); } + for (AbstractBuildingSubdivision abs : buildingSubdivisions) { + abs.prepareForChecking(); + } } @Override @@ -370,6 +404,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.clearMetaInformation(); } + for (AbstractBuildingSubdivision abs : buildingSubdivisions) { + abs.clearMetaInformation(); + } } @Override @@ -379,6 +416,7 @@ public abstract class AbstractBuilding extends CityObject { handler.addInstance(buildingInstallations); handler.addInstance(buildingRooms); handler.addInstance(buildingRoomFurnitureList); + handler.addInstance(buildingSubdivisions); } @Override @@ -397,6 +435,9 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture originalBFR : originalAb.buildingRoomFurnitureList) { buildingRoomFurnitureList.add(handler.getCopyInstance(originalBFR)); } + for (AbstractBuildingSubdivision originalBSub : originalAb.buildingSubdivisions) { + buildingSubdivisions.add(handler.getCopyInstance(originalBSub)); + } ab = originalAb.ab; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java new file mode 100644 index 0000000..fb69ad1 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java @@ -0,0 +1,366 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty; +import org.citygml4j.core.util.geometry.GeometryFactory; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.primitives.Solid; +import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractBuildingSubdivision extends CityObject{ + @Serial + private static final long serialVersionUID = 7033994252340571002L; + + private static final Logger logger = LogManager.getLogger(AbstractBuildingSubdivision.class); + + private final List buildingInstallations = new ArrayList<>(2); + private final List boundarySurfaceList = new ArrayList<>(); + private final List buildingRooms = new ArrayList<>(); + + private final List buildingRoomFurnitureList = new ArrayList<>(); + private org.citygml4j.core.model.building.AbstractBuildingSubdivision abs; + + /** + * Getter for all boundary surfaces contained in this building. + * + * @return the boundary surfaces + */ + public List getBoundarySurfaces() { + return boundarySurfaceList; + } + + @Override + public org.citygml4j.core.model.building.AbstractBuildingSubdivision getGmlObject() { + return abs; + } + + @Override + public FeatureType getFeatureType() { + return FeatureType.BUILDING; + } + + @Override + public void unsetGmlGeometries() { + abs.setLod1Solid(null); + abs.setLod2Solid(null); + abs.setLod3Solid(null); + abs.setLod2MultiSurface(null); + abs.setLod3MultiSurface(null); + + for (BoundarySurface bs : boundarySurfaceList) { + bs.unsetGmlGeometries(); + } + for (Installation bi : buildingInstallations) { + bi.unsetGmlGeometries(); + } + for (BuildingRoom br : buildingRooms) { + br.unsetGmlGeometries(); + } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.unsetGmlGeometries(); + } + } + + @Override + public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { + for (Geometry geom : getGeometries()) { + if (geom.getType() == GeometryType.MULTI_SURFACE) { + MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config); + setMultiSurfaceAccordingToLod(geom, ms); + } else { + Solid solid = CityGmlUtils.createSolid(geom, factory, config); + setSolidAccordingToLod(geom, solid); + } + } + for (BoundarySurface bs : boundarySurfaceList) { + reCreateBoundarySurface(factory, config, bs); + } + for (Installation bi : buildingInstallations) { + bi.reCreateGeometries(factory, config); + } + for (BuildingRoom br : buildingRooms) { + br.reCreateGeometries(factory, config); + } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.reCreateGeometries(factory, config); + } + } + + private void reCreateBoundarySurface(GeometryFactory factory, ParserConfiguration config, BoundarySurface bs) { + if (bs.getGeometries().isEmpty()) { + for (AbstractSpaceBoundaryProperty bsp : abs.getBoundaries()) { + if (bsp.getObject() != null && bsp.getObject() == bs.getGmlObject()) { + logger.warn("Found empty boundary surface: {}, removing from building", bs.getGmlId()); + abs.getBoundaries().remove(bsp); + break; + } + } + return; + } + bs.reCreateGeometries(factory, config); + } + + private void setMultiSurfaceAccordingToLod(Geometry geom, MultiSurface ms) { + switch (geom.getLod()) { + case LOD0: + abs.setLod0MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD2: + abs.setLod2MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD3: + abs.setLod3MultiSurface(new MultiSurfaceProperty(ms)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " multi surface to buildings"); + } + } + + private void setSolidAccordingToLod(Geometry geom, Solid solid) { + switch (geom.getLod()) { + case LOD1: + abs.setLod1Solid(new SolidProperty(solid)); + break; + case LOD2: + abs.setLod2Solid(new SolidProperty(solid)); + break; + case LOD3: + abs.setLod3Solid(new SolidProperty(solid)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to buildings"); + } + } + + @Override + public void accept(Check c) { + super.accept(c); + if (c.canExecute(this)) { + c.check(this); + } + for (Installation bi : buildingInstallations) { + bi.accept(c); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.accept(c); + } + for (BuildingRoom br : buildingRooms) { + br.accept(c); + } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.accept(c); + } + } + + @Override + public void collectContainedErrors(List errors) { + super.collectContainedErrors(errors); + for (Installation bi : buildingInstallations) { + bi.collectContainedErrors(errors); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.collectContainedErrors(errors); + } + for (BuildingRoom br : buildingRooms) { + br.collectContainedErrors(errors); + } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.collectContainedErrors(errors); + } + } + + @Override + public void clearAllContainedCheckResults() { + super.clearAllContainedCheckResults(); + for (Installation bi : buildingInstallations) { + bi.clearAllContainedCheckResults(); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.clearAllContainedCheckResults(); + } + for (BuildingRoom br : buildingRooms) { + br.clearAllContainedCheckResults(); + } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.clearAllContainedCheckResults(); + } + } + + @Override + public boolean containsError(CheckId checkIdentifier) { + boolean hasError = super.containsError(checkIdentifier); + if (hasError) { + return true; + } + for (Installation bi : buildingInstallations) { + if (bi.containsError(checkIdentifier)) { + return true; + } + } + for (BoundarySurface bs : boundarySurfaceList) { + if (bs.containsError(checkIdentifier)) { + return true; + } + } + for (BuildingRoom br : buildingRooms) { + if (br.containsError(checkIdentifier)) { + return true; + } + } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + if (bfr.containsError(checkIdentifier)) { + return true; + } + } + return false; + } + + @Override + public boolean containsAnyError() { + boolean hasError = super.containsAnyError(); + if (hasError) { + return true; + } + for (Installation bi : buildingInstallations) { + if (bi.containsAnyError()) { + return true; + } + } + for (BoundarySurface bs : boundarySurfaceList) { + if (bs.containsAnyError()) { + return true; + } + } + for (BuildingRoom br : buildingRooms) { + if (br.containsAnyError()) { + return true; + } + } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + if (bfr.containsAnyError()) { + return true; + } + } + return false; + } + + void setCityGmlBuilding(org.citygml4j.core.model.building.AbstractBuildingSubdivision abs) { + this.abs = abs; + } + + public void addBoundarySurface(BoundarySurface bs) { + boundarySurfaceList.add(bs); + bs.setParent(this); + } + + public void addBuildingInstallation(Installation coBi) { + buildingInstallations.add(coBi); + coBi.setParent(this); + } + + public void addBuildingRoom(BuildingRoom room) { + buildingRooms.add(room); + room.setParent(this); + } + + public void addBuildingRoomFurniture(BuildingRoomFurniture roomFurniture) { + buildingRoomFurnitureList.add(roomFurniture); + roomFurniture.setParent(this); + } + + + + public void setGmlObject(org.citygml4j.core.model.building.AbstractBuildingSubdivision abs) { + this.abs = abs; + } + + public List getBuildingInstallations() { + return buildingInstallations; + } + + public List getBuildingRooms() { + return buildingRooms; + } + + public List getBuildingRoomFurnitureList() { + return buildingRoomFurnitureList; + } + + @Override + public void prepareForChecking() { + super.prepareForChecking(); + for (Installation bi : buildingInstallations) { + bi.prepareForChecking(); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.prepareForChecking(); + } + for (BuildingRoom br : buildingRooms) { + br.prepareForChecking(); + } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.prepareForChecking(); + } + } + + @Override + public void clearMetaInformation() { + super.clearMetaInformation(); + for (Installation bi : buildingInstallations) { + bi.clearMetaInformation(); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.clearMetaInformation(); + } + for (BuildingRoom br : buildingRooms) { + br.clearMetaInformation(); + } + for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { + bfr.clearMetaInformation(); + } + } + + @Override + public void collectInstances(CopyHandler handler) { + super.collectInstances(handler); + handler.addInstance(boundarySurfaceList); + handler.addInstance(buildingInstallations); + handler.addInstance(buildingRooms); + handler.addInstance(buildingRoomFurnitureList); + } + + @Override + public void fillValues(Copyable original, CopyHandler handler) { + super.fillValues(original, handler); + AbstractBuildingSubdivision originalAbs = (AbstractBuildingSubdivision) original; + for (BoundarySurface originalBs : originalAbs.boundarySurfaceList) { + boundarySurfaceList.add(handler.getCopyInstance(originalBs)); + } + for (Installation originalBi : originalAbs.buildingInstallations) { + buildingInstallations.add(handler.getCopyInstance(originalBi)); + } + for (BuildingRoom originalBr : originalAbs.buildingRooms) { + buildingRooms.add(handler.getCopyInstance(originalBr)); + } + for (BuildingRoomFurniture originalBFR : originalAbs.buildingRoomFurnitureList) { + buildingRoomFurnitureList.add(handler.getCopyInstance(originalBFR)); + } + abs = originalAbs.abs; + } + +} + + diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java index 2a7130c..bcda199 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingRoom.java @@ -1,14 +1,8 @@ package de.hft.stuttgart.citydoctor2.datastructure; -import de.hft.stuttgart.citydoctor2.check.Check; -import de.hft.stuttgart.citydoctor2.check.CheckError; -import de.hft.stuttgart.citydoctor2.check.CheckId; -import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.CopyHandler; import de.hft.stuttgart.citydoctor2.utils.Copyable; import org.citygml4j.core.model.building.BuildingFurnitureProperty; -import org.citygml4j.core.model.construction.AbstractFurnitureProperty; -import org.citygml4j.core.util.geometry.GeometryFactory; import java.io.Serial; import java.util.ArrayList; @@ -19,14 +13,14 @@ public class BuildingRoom extends AbstractRoom{ @Serial private static final long serialVersionUID = -276088332165299253L; private final List furnitureRefs = new ArrayList<>(2); - private AbstractBuilding parent; + private CityObject parent; public void setGmlObject(org.citygml4j.core.model.building.BuildingRoom cgmlRoom){ super.cgmlRoom = cgmlRoom; } - public void setParent(AbstractBuilding parent){ + public void setParent(CityObject parent){ this.parent = parent; } @@ -43,7 +37,7 @@ public class BuildingRoom extends AbstractRoom{ return furnitureRefs; } - public AbstractBuilding getParent() { + public CityObject getParent() { return parent; } -- GitLab From 7d24a81b8cf18de39226ac02cfc358663db8b361 Mon Sep 17 00:00:00 2001 From: Riegel Date: Fri, 4 Oct 2024 16:01:26 +0200 Subject: [PATCH 12/55] Add support for BuildingUnit and Storey --- .../AbstractBuildingSubdivision.java | 2 +- .../datastructure/BuildingUnit.java | 129 ++++++++++++++++++ .../citydoctor2/datastructure/Storey.java | 128 +++++++++++++++++ 3 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingUnit.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Storey.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java index fb69ad1..a715972 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java @@ -31,7 +31,7 @@ public abstract class AbstractBuildingSubdivision extends CityObject{ private final List buildingRooms = new ArrayList<>(); private final List buildingRoomFurnitureList = new ArrayList<>(); - private org.citygml4j.core.model.building.AbstractBuildingSubdivision abs; + protected org.citygml4j.core.model.building.AbstractBuildingSubdivision abs; /** * Getter for all boundary surfaces contained in this building. diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingUnit.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingUnit.java new file mode 100644 index 0000000..4123f53 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/BuildingUnit.java @@ -0,0 +1,129 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.util.geometry.GeometryFactory; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +public class BuildingUnit extends AbstractBuildingSubdivision{ + + @Serial + private static final long serialVersionUID = -2931788311665810322L; + + private final List storeys = new ArrayList<>(); + + @Override + public Copyable createCopyInstance() { + return new BuildingUnit(); + } + + + @Override + public void unsetGmlGeometries() { + super.unsetGmlGeometries(); + for (Storey storey : storeys) { + storey.unsetGmlGeometries(); + } + } + + @Override + public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { + super.reCreateGeometries(factory, config); + for (Storey storey : storeys) { + storey.reCreateGeometries(factory, config); + } + } + + @Override + public void accept(Check c) { + super.accept(c); + for (Storey storey : storeys) { + storey.accept(c); + } + } + + @Override + public void collectContainedErrors(List errors) { + super.collectContainedErrors(errors); + for (Storey storey : storeys) { + storey.collectContainedErrors(errors); + } + } + + @Override + public void clearAllContainedCheckResults() { + super.clearAllContainedCheckResults(); + for (Storey storey : storeys) { + storey.clearAllContainedCheckResults(); + } + } + + @Override + public boolean containsError(CheckId checkIdentifier) { + boolean hasError = super.containsError(checkIdentifier); + if (hasError) { + return true; + } + for (Storey storey : storeys) { + if (storey.containsError(checkIdentifier)) { + return true; + } + } + return false; + } + + @Override + public boolean containsAnyError() { + boolean hasError = super.containsAnyError(); + if (hasError) { + return true; + } + for (Storey storey : storeys) { + if (storey.containsAnyError()) { + return true; + } + } + return false; + } + + @Override + public void prepareForChecking() { + super.prepareForChecking(); + for (Storey storey : storeys) { + storey.prepareForChecking(); + } + } + + @Override + public void clearMetaInformation() { + super.clearMetaInformation(); + for (Storey storey : storeys) { + storey.clearMetaInformation(); + } + } + + @Override + public void collectInstances(CopyHandler handler) { + super.collectInstances(handler); + for (Storey storey : storeys) { + storey.collectInstances(handler); + } + } + + @Override + public void fillValues(Copyable original, CopyHandler handler) { + super.fillValues(original, handler); + BuildingUnit originalBu = (BuildingUnit) original; + for (Storey storey : originalBu.storeys) { + storeys.add(handler.getCopyInstance(storey)); + } + this.abs = originalBu.abs; + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Storey.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Storey.java new file mode 100644 index 0000000..adb216f --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Storey.java @@ -0,0 +1,128 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.util.geometry.GeometryFactory; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +public class Storey extends AbstractBuildingSubdivision{ + + @Serial + private static final long serialVersionUID = 5162814691642668089L; + + private final List buildingUnits = new ArrayList<>(); + + @Override + public Copyable createCopyInstance() { + return new Storey(); + } + + @Override + public void unsetGmlGeometries() { + super.unsetGmlGeometries(); + for (BuildingUnit buildingUnit : buildingUnits) { + buildingUnit.unsetGmlGeometries(); + } + } + + @Override + public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { + super.reCreateGeometries(factory, config); + for (BuildingUnit buildingUnit : buildingUnits) { + buildingUnit.reCreateGeometries(factory, config); + } + } + + @Override + public void accept(Check c) { + super.accept(c); + for (BuildingUnit buildingUnit : buildingUnits) { + buildingUnit.accept(c); + } + } + + @Override + public void collectContainedErrors(List errors) { + super.collectContainedErrors(errors); + for (BuildingUnit buildingUnit : buildingUnits) { + buildingUnit.collectContainedErrors(errors); + } + } + + @Override + public void clearAllContainedCheckResults() { + super.clearAllContainedCheckResults(); + for (BuildingUnit buildingUnit : buildingUnits) { + buildingUnit.clearAllContainedCheckResults(); + } + } + + @Override + public boolean containsError(CheckId checkIdentifier) { + boolean hasError = super.containsError(checkIdentifier); + if (hasError) { + return true; + } + for (BuildingUnit buildingUnit : buildingUnits) { + if (buildingUnit.containsError(checkIdentifier)) { + return true; + } + } + return false; + } + + @Override + public boolean containsAnyError() { + boolean hasError = super.containsAnyError(); + if (hasError) { + return true; + } + for (BuildingUnit buildingUnit : buildingUnits) { + if (buildingUnit.containsAnyError()) { + return true; + } + } + return false; + } + + @Override + public void prepareForChecking() { + super.prepareForChecking(); + for (BuildingUnit buildingUnit : buildingUnits) { + buildingUnit.prepareForChecking(); + } + } + + @Override + public void clearMetaInformation() { + super.clearMetaInformation(); + for (BuildingUnit buildingUnit : buildingUnits) { + buildingUnit.clearMetaInformation(); + } + } + + @Override + public void collectInstances(CopyHandler handler) { + super.collectInstances(handler); + for (BuildingUnit buildingUnit : buildingUnits) { + buildingUnit.collectInstances(handler); + } + } + + @Override + public void fillValues(Copyable original, CopyHandler handler) { + super.fillValues(original, handler); + Storey originalSt = (Storey) original; + for (BuildingUnit buildingUnit : originalSt.buildingUnits) { + buildingUnits.add(handler.getCopyInstance(buildingUnit)); + } + this.abs = originalSt.abs; + } +} -- GitLab From c949352d53b70b488b5a4ccd63e1fedb078ad620 Mon Sep 17 00:00:00 2001 From: Riegel Date: Mon, 7 Oct 2024 11:56:22 +0200 Subject: [PATCH 13/55] Add rendering of Storeys and BuildingUnits --- .../datastructure/AbstractBuilding.java | 96 ++++++++++++++----- .../citygml3/Citygml3FeatureMapper.java | 66 +++++++++++++ .../citydoctor2/gui/CityDoctorController.java | 39 ++++++++ .../stuttgart/citydoctor2/gui/Renderer.java | 50 ++++++++++ .../gui/tree/AllBuildingUnitsNode.java | 46 +++++++++ .../citydoctor2/gui/tree/AllStoreysNode.java | 46 +++++++++ .../gui/tree/BuildingUnitNode.java | 38 ++++++++ .../citydoctor2/gui/tree/StoreyNode.java | 37 +++++++ 8 files changed, 392 insertions(+), 26 deletions(-) create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingUnitsNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllStoreysNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingUnitNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/StoreyNode.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java index ca95600..66a14b0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuilding.java @@ -56,7 +56,8 @@ public abstract class AbstractBuilding extends CityObject { private final List buildingInstallations = new ArrayList<>(2); private final List boundarySurfaceList = new ArrayList<>(); private final List buildingRooms = new ArrayList<>(); - private final List buildingSubdivisions = new ArrayList<>(); + private final List buildingStoreys = new ArrayList<>(); + private final List buildingUnits = new ArrayList<>(); private final List buildingRoomFurnitureList = new ArrayList<>(); private org.citygml4j.core.model.building.AbstractBuilding ab; @@ -102,8 +103,11 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.unsetGmlGeometries(); } - for (AbstractBuildingSubdivision abs : buildingSubdivisions) { - abs.unsetGmlGeometries(); + for (Storey storey : buildingStoreys) { + storey.unsetGmlGeometries(); + } + for (BuildingUnit bu : buildingUnits) { + bu.unsetGmlGeometries(); } } @@ -130,8 +134,11 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.reCreateGeometries(factory, config); } - for (AbstractBuildingSubdivision abs : buildingSubdivisions) { - abs.reCreateGeometries(factory, config); + for (Storey storey : buildingStoreys) { + storey.reCreateGeometries(factory, config); + } + for (BuildingUnit bu : buildingUnits) { + bu.reCreateGeometries(factory, config); } } @@ -208,8 +215,11 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.accept(c); } - for (AbstractBuildingSubdivision abs : buildingSubdivisions) { - abs.accept(c); + for (Storey storey : buildingStoreys) { + storey.accept(c); + } + for (BuildingUnit bu : buildingUnits) { + bu.accept(c); } } @@ -228,8 +238,11 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.collectContainedErrors(errors); } - for (AbstractBuildingSubdivision abs : buildingSubdivisions) { - abs.collectContainedErrors(errors); + for (Storey storey : buildingStoreys) { + storey.collectContainedErrors(errors); + } + for (BuildingUnit bu : buildingUnits) { + bu.collectContainedErrors(errors); } } @@ -248,8 +261,11 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.clearAllContainedCheckResults(); } - for (AbstractBuildingSubdivision abs : buildingSubdivisions) { - abs.clearAllContainedCheckResults(); + for (Storey storey : buildingStoreys) { + storey.clearAllContainedCheckResults(); + } + for (BuildingUnit bu : buildingUnits) { + bu.clearAllContainedCheckResults(); } } @@ -279,8 +295,13 @@ public abstract class AbstractBuilding extends CityObject { return true; } } - for (AbstractBuildingSubdivision abs : buildingSubdivisions) { - if (abs.containsError(checkIdentifier)) { + for (Storey storey : buildingStoreys) { + if (storey.containsError(checkIdentifier)) { + return true; + } + } + for (BuildingUnit bu : buildingUnits) { + if (bu.containsError(checkIdentifier)) { return true; } } @@ -313,8 +334,13 @@ public abstract class AbstractBuilding extends CityObject { return true; } } - for (AbstractBuildingSubdivision abs : buildingSubdivisions) { - if (abs.containsAnyError()) { + for (Storey storey : buildingStoreys) { + if (storey.containsAnyError()) { + return true; + } + } + for (BuildingUnit bu : buildingUnits) { + if (bu.containsAnyError()) { return true; } } @@ -345,8 +371,12 @@ public abstract class AbstractBuilding extends CityObject { roomFurniture.setParent(this); } - public void addAbstractBuildingSubdivision(AbstractBuildingSubdivision abs) { - buildingSubdivisions.add(abs); + public void addStorey(Storey storey) { + buildingStoreys.add(storey); + } + + public void addBuildingUnit(BuildingUnit buildingUnit) { + buildingUnits.add(buildingUnit); } public void setGmlObject(org.citygml4j.core.model.building.AbstractBuilding ab) { @@ -365,8 +395,12 @@ public abstract class AbstractBuilding extends CityObject { return buildingRoomFurnitureList; } - public List getBuildingSubdivisions() { - return buildingSubdivisions; + public List getBuildingStoreys() { + return buildingStoreys; + } + + public List getBuildingUnits() { + return buildingUnits; } @Override @@ -384,8 +418,11 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.prepareForChecking(); } - for (AbstractBuildingSubdivision abs : buildingSubdivisions) { - abs.prepareForChecking(); + for (Storey storey : buildingStoreys) { + storey.prepareForChecking(); + } + for (BuildingUnit bu : buildingUnits) { + bu.prepareForChecking(); } } @@ -404,8 +441,11 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture bfr : buildingRoomFurnitureList) { bfr.clearMetaInformation(); } - for (AbstractBuildingSubdivision abs : buildingSubdivisions) { - abs.clearMetaInformation(); + for (Storey storey : buildingStoreys) { + storey.clearMetaInformation(); + } + for (BuildingUnit bu : buildingUnits) { + bu.clearMetaInformation(); } } @@ -416,7 +456,8 @@ public abstract class AbstractBuilding extends CityObject { handler.addInstance(buildingInstallations); handler.addInstance(buildingRooms); handler.addInstance(buildingRoomFurnitureList); - handler.addInstance(buildingSubdivisions); + handler.addInstance(buildingStoreys); + handler.addInstance(buildingUnits); } @Override @@ -435,8 +476,11 @@ public abstract class AbstractBuilding extends CityObject { for (BuildingRoomFurniture originalBFR : originalAb.buildingRoomFurnitureList) { buildingRoomFurnitureList.add(handler.getCopyInstance(originalBFR)); } - for (AbstractBuildingSubdivision originalBSub : originalAb.buildingSubdivisions) { - buildingSubdivisions.add(handler.getCopyInstance(originalBSub)); + for (Storey originalBStoreys : originalAb.buildingStoreys) { + buildingStoreys.add(handler.getCopyInstance(originalBStoreys)); + } + for (BuildingUnit originalBun : originalAb.buildingUnits) { + buildingUnits.add(handler.getCopyInstance(originalBun)); } ab = originalAb.ab; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index 4094f5b..3b394c1 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -41,6 +41,8 @@ import org.citygml4j.core.model.bridge.BridgePart; import org.citygml4j.core.model.bridge.BridgePartProperty; import org.citygml4j.core.model.building.*; import org.citygml4j.core.model.building.BuildingFurniture; +import org.citygml4j.core.model.building.BuildingUnit; +import org.citygml4j.core.model.building.Storey; import org.citygml4j.core.model.construction.AbstractConstruction; import org.citygml4j.core.model.core.AbstractCityObject; import org.citygml4j.core.model.core.AbstractFeatureWithLifespan; @@ -605,6 +607,22 @@ public class Citygml3FeatureMapper extends ObjectWalker { cdBuilding.addBuildingRoom(br); } + for(AbstractBuildingSubdivisionProperty abs : gmlAb.getBuildingSubdivisions()) { + var gmlABS = abs.getObject(); + if (gmlABS == null) { + continue; + } + if (gmlABS instanceof Storey gmlStorey) { + de.hft.stuttgart.citydoctor2.datastructure.Storey storey = mapBuildingStorey(gmlStorey); + cdBuilding.addStorey(storey); + } + else if (gmlABS instanceof BuildingUnit gmlBU){ + de.hft.stuttgart.citydoctor2.datastructure.BuildingUnit bu = mapBuildingUnit(gmlBU); + cdBuilding.addBuildingUnit(bu); + } + } + + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); for (AbstractSpaceBoundaryProperty surfaceProp : gmlAb.getBoundaries()) { if (!surfaceProp.isSetObject()) { @@ -711,6 +729,54 @@ public class Citygml3FeatureMapper extends ObjectWalker { } + private de.hft.stuttgart.citydoctor2.datastructure.Storey mapBuildingStorey(Storey gmlStorey){ + de.hft.stuttgart.citydoctor2.datastructure.Storey storey = new de.hft.stuttgart.citydoctor2.datastructure.Storey(); + storey.setGmlObject(gmlStorey); + mapAbstractSpace(gmlStorey, storey); + storey.unsetGmlGeometries(); + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); + for (AbstractSpaceBoundaryProperty surfaceProp : gmlStorey.getBoundaries()) { + if (!surfaceProp.isSetObject()) { + continue; + } + AbstractSpaceBoundary surface = surfaceProp.getObject(); + surface.accept(surfaceMapper); + } + for (BoundarySurface bs : surfaceMapper.getSurfaces()) { + storey.addBoundarySurface(bs); + for (Geometry geom : bs.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + p.setPartOfSurface(bs); + } + } + } + return storey; + } + + private de.hft.stuttgart.citydoctor2.datastructure.BuildingUnit mapBuildingUnit(BuildingUnit gmlBU){ + de.hft.stuttgart.citydoctor2.datastructure.BuildingUnit bu = new de.hft.stuttgart.citydoctor2.datastructure.BuildingUnit(); + bu.setGmlObject(gmlBU); + mapAbstractSpace(gmlBU, bu); + bu.unsetGmlGeometries(); + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); + for (AbstractSpaceBoundaryProperty surfaceProp : gmlBU.getBoundaries()) { + if (!surfaceProp.isSetObject()) { + continue; + } + AbstractSpaceBoundary surface = surfaceProp.getObject(); + surface.accept(surfaceMapper); + } + for (BoundarySurface bs : surfaceMapper.getSurfaces()) { + bu.addBoundarySurface(bs); + for (Geometry geom : bs.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + p.setPartOfSurface(bs); + } + } + } + return bu; + } + private void updatePartOfSurface(AbstractBuilding cdBuilding, SurfaceMapper surfaceMapper) { for (BoundarySurface bs : surfaceMapper.getSurfaces()) { cdBuilding.addBoundarySurface(bs); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index 0a0fb47..50e773b 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -433,6 +433,8 @@ public class CityDoctorController { createBuildingInstallationNodes(b, item); createBuildingRoomNodes(b, item); createBuildingFurnitureNodes(b, item); + createBuildingStoreyNodes(b, item); + createBuildingUnitNodes(b, item); createBuildingPartNodes(b, item); } } @@ -541,6 +543,43 @@ public class CityDoctorController { } + private void createBuildingStoreyNodes(Building ab, TreeItem root){ + List storeys = ab.getBuildingStoreys(); + if (storeys.isEmpty()) { + return; + } + AllStoreysNode allSNode = new AllStoreysNode(storeys); + TreeItem allSNodeTextItem = new TreeItem<>(allSNode); + root.getChildren().add(allSNodeTextItem); + for (Storey storey : storeys) { + StoreyNode storeyNode = new StoreyNode(storey); + TreeItem storeyNodeTextItem = new TreeItem<>(storeyNode); + storeyNodeTextItem.setExpanded(true); + allSNodeTextItem.getChildren().add(storeyNodeTextItem); + createGeometryNodes(storey, storeyNodeTextItem); + createBoundarySurfaceNodes(storey.getBoundarySurfaces(), storeyNodeTextItem); + } + } + + + public void createBuildingUnitNodes(Building ab, TreeItem root) { + List buildingUnits = ab.getBuildingUnits(); + if (buildingUnits.isEmpty()) { + return; + } + AllBuildingUnitsNode allSNode = new AllBuildingUnitsNode(buildingUnits); + TreeItem allSNodeTextItem = new TreeItem<>(allSNode); + root.getChildren().add(allSNodeTextItem); + for (BuildingUnit buildingUnit : buildingUnits) { + BuildingUnitNode buildingUnitNode = new BuildingUnitNode(buildingUnit); + TreeItem storeyNodeTextItem = new TreeItem<>(buildingUnitNode); + storeyNodeTextItem.setExpanded(true); + allSNodeTextItem.getChildren().add(storeyNodeTextItem); + createGeometryNodes(buildingUnit, storeyNodeTextItem); + createBoundarySurfaceNodes(buildingUnit.getBoundarySurfaces(), storeyNodeTextItem); + } + } + private void createBuildingFurnitureNodes(Building ab, TreeItem root) { createFurnitureNodes(ab.getBuildingRoomFurnitureList(), root); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java index c152b4a..fe246e5 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java @@ -288,6 +288,56 @@ public class Renderer { return polygons; } + public void render(Storey storey){ + refresher = () -> { + Set setupStoreyPolygons = setupStoreyPolygons(storey); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupStoreyPolygons)); + render(setupStoreyPolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(storey); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + public Set setupStoreyPolygons(Storey storey) { + Set polygons = new HashSet<>(); + addPolygons(storey, polygons); + for (BoundarySurface bs : storey.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + return polygons; + } + + public void render(BuildingUnit buildingUnit){ + refresher = () -> { + Set setupBuildingUnitPolgons = setupBuildingUnitPolygons(buildingUnit); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingUnitPolgons)); + render(setupBuildingUnitPolgons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(buildingUnit); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + public Set setupBuildingUnitPolygons(BuildingUnit buildingUnit) { + Set polygons = new HashSet<>(); + addPolygons(buildingUnit, polygons); + for (BoundarySurface bs : buildingUnit.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + return polygons; + } + public void render(BridgeObject bridge) { refresher = () -> { Set setupBridgePolygons = setupBridgePolygons(bridge); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingUnitsNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingUnitsNode.java new file mode 100644 index 0000000..e91e960 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllBuildingUnitsNode.java @@ -0,0 +1,46 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.BuildingUnit; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +import java.util.List; + +public class AllBuildingUnitsNode extends Renderable{ + + private final List buildingUnits; + + public AllBuildingUnitsNode(List buildingUnits) { + this.buildingUnits = buildingUnits; + } + + @Override + public String getText() { + return "Building Units"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean wasChecked = false; + for (BuildingUnit buildingUnit : buildingUnits) { + if (buildingUnit.isValidated()) { + wasChecked = true; + if (buildingUnit.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + } + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } + +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllStoreysNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllStoreysNode.java new file mode 100644 index 0000000..9a2df07 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllStoreysNode.java @@ -0,0 +1,46 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Storey; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +import java.util.List; + +public class AllStoreysNode extends Renderable{ + + private final List storeys; + + public AllStoreysNode(List storeys) { + this.storeys = storeys; + } + + @Override + public String getText() { + return "Building Storeys"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean wasChecked = false; + for (Storey storey : storeys) { + if (storey.isValidated()) { + wasChecked = true; + if (storey.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + } + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } + +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingUnitNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingUnitNode.java new file mode 100644 index 0000000..805b750 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/BuildingUnitNode.java @@ -0,0 +1,38 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.BuildingUnit; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class BuildingUnitNode extends Renderable{ + + private final BuildingUnit buildingUnit; + + public BuildingUnitNode(BuildingUnit buildingUnit) { + this.buildingUnit = buildingUnit; + } + + @Override + public String getText() { + return buildingUnit.getGmlId().getGmlString(); + } + + + @Override + public void visit(Renderer renderer) { + renderer.render(buildingUnit); + } + + @Override + public void refreshTextColor() { + if (!buildingUnit.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (buildingUnit.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + + +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/StoreyNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/StoreyNode.java new file mode 100644 index 0000000..c0b0fe0 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/StoreyNode.java @@ -0,0 +1,37 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Storey; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class StoreyNode extends Renderable{ + + private final Storey storey; + + public StoreyNode(Storey storey) { + this.storey = storey; + } + + @Override + public String getText() { + return storey.getGmlId().getGmlString(); + } + + + @Override + public void visit(Renderer renderer) { + renderer.render(storey); + } + + @Override + public void refreshTextColor() { + if (!storey.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (storey.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } + +} -- GitLab From fdfd472b7649c66764333c387f466aa39c36fc6e Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 10 Oct 2024 16:46:00 +0200 Subject: [PATCH 14/55] Add data structure support for ImplicitGeometry --- .../datastructure/ImplicitGeometryHolder.java | 77 ++++++++++++ .../datastructure/LibraryObject.java | 80 ++++++++++++ .../datastructure/RelativeGeometry.java | 39 ++++++ .../math/TransformationMatrix.java | 115 ++++++++++++++++++ 4 files changed, 311 insertions(+) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/TransformationMatrix.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java new file mode 100644 index 0000000..cb41b15 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java @@ -0,0 +1,77 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.math.TransformationMatrix; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.citygml4j.core.model.core.ImplicitGeometry; + +import java.io.Serial; + +/** + * Datastructure for representation of implicit geometries + */ +public class ImplicitGeometryHolder extends Geometry{ + + private static final Logger logger = LogManager.getLogger(LibraryObject.class); + @Serial + private static final long serialVersionUID = -8938931081577196349L; + + private ImplicitGeometry cgmlImplicitGeometry = null; + /* + LibraryObject and RelativeGeometry are mutually exclusive + */ + private final LibraryObject libObject; + private final RelativeGeometry relativeGeom; + + public ImplicitGeometryHolder(ImplicitGeometry cgmlImplicitGeometry, LibraryObject libObject) { + super(libObject.getType(), libObject.getLod()); + this.libObject = libObject; + this.relativeGeom = null; + this.cgmlImplicitGeometry = cgmlImplicitGeometry; + applyTransformation(); + } + + public ImplicitGeometryHolder(ImplicitGeometry cgmlImplicitGeometry, RelativeGeometry relativeGeometry) { + super(relativeGeometry.getType(),relativeGeometry.getLod()); + this.libObject = null; + this.relativeGeom = relativeGeometry; + this.cgmlImplicitGeometry = cgmlImplicitGeometry; + applyTransformation(); + } + + + @Override + public void accept(Check c) { + /** + * Checks are only run on the referenced prototypical geometry. + */ + if (relativeGeom != null) { + relativeGeom.accept(c); + } + if (libObject != null) { + libObject.accept(c); + } + } + + /** + * Applies the transformation matrix of the implicit geometry to a copy of the reference geometry and copies the resulting + * transformed geometry into this object. + */ + private void applyTransformation(){ + TransformationMatrix transformMatrix = new TransformationMatrix(cgmlImplicitGeometry); + Geometry refGeo = null; + if (libObject == null){ + refGeo = relativeGeom; + } else if (relativeGeom == null){ + refGeo = libObject; + } + if (refGeo == null){ + throw new NullPointerException("Geometry reference object of ImplicitGeometryHolder is null!"); + } + Geometry transformedGeom = transformMatrix.transformGeometry(refGeo); + transformedGeom.getPolygons().forEach(this::addPolygon); + this.updateEdgesAndVertices(); + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java new file mode 100644 index 0000000..fa178e1 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java @@ -0,0 +1,80 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper; +import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; +import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; +import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.Serial; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +/** + * Reference object for handling of implicit geometries with a library object contained in an external file + */ +public class LibraryObject extends Geometry{ + + private static final Logger logger = LogManager.getLogger(LibraryObject.class); + @Serial + private static final long serialVersionUID = -50293435187454911L; + private final String filepath; + private final ParserConfiguration config; + private static HashMap libraryObjects = new HashMap<>(); + + public static LibraryObject of(Path path, ParserConfiguration config) { + if (libraryObjects.containsKey(path)) { + return libraryObjects.get(path); + } + Geometry protoGeom = parseFile(path, config); + if (protoGeom == null) { + return null; + } + LibraryObject libOb = new LibraryObject(protoGeom.getType(),protoGeom.getLod(),path, config); + protoGeom.getPolygons().forEach(libOb::addPolygon); + libOb.updateEdgesAndVertices(); + libraryObjects.put(path.toString(), libOb); + return libOb; + } + + private LibraryObject(GeometryType type, Lod lod, Path path, ParserConfiguration config) { + super(type,lod); + this.filepath = path.toString(); + this.config = config; + } + + + private static Geometry parseFile(Path path, ParserConfiguration config) { + Geometry geo = null; + if (path.toFile().exists()){ + try { + CityGmlParser.gagLogger(true); + CityDoctorModel model = CityGmlParser.parseCityGmlFile(path.toString(), config); + List objects = model.createFeatureStream().toList(); + if (objects.isEmpty()){ + throw new InvalidGmlFileException("Referenced library-object's gml file does not contain a CityGML object!"); + } else if (objects.size() > 1){ + throw new InvalidGmlFileException("Referenced library-object's gml file contains more than one CityGML object!"); + } + geo = objects.get(0).getHighestLodGeometry(); + } catch (CityGmlParseException e){ + logger.error(String.format( + "Encountered an error while parsing library object %s", path)); + logger.error(e.getStackTrace()); + } catch (InvalidGmlFileException e){ + logger.error(e.getStackTrace()); + } finally { + // Failsafe to remove gag should parsing fail + CityGmlParser.gagLogger(false); + } + } else{ + logger.error(String.format("Implicit geometry references non-existing library object file %s.", path)); + } + return geo; + } + +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java new file mode 100644 index 0000000..f309b6c --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java @@ -0,0 +1,39 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + + +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.Serial; +import java.util.HashMap; + +/** + * Reference object for handling of implicit geometries with a relative geometry + */ +public class RelativeGeometry extends Geometry { + + private static final Logger logger = LogManager.getLogger(RelativeGeometry.class); + @Serial + private static final long serialVersionUID = -686112245455298977L; + + private static HashMap relativeGeometries = new HashMap<>(); + + public static RelativeGeometry of(Geometry geom){ + if (relativeGeometries.containsKey(geom)){ + return relativeGeometries.get(geom); + } + RelativeGeometry relGeo = new RelativeGeometry(geom.getType(),geom.getLod()); + geom.getPolygons().forEach(relGeo::addPolygon); + relGeo.updateEdgesAndVertices(); + relativeGeometries.put(geom,relGeo); + return relGeo; + } + + + private RelativeGeometry(GeometryType type, Lod lod) { + super(type, lod); + } +} + + diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/TransformationMatrix.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/TransformationMatrix.java new file mode 100644 index 0000000..4ffb8bc --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/TransformationMatrix.java @@ -0,0 +1,115 @@ +package de.hft.stuttgart.citydoctor2.math; + +import Jama.Matrix; +import de.hft.stuttgart.citydoctor2.datastructure.*; +import de.hft.stuttgart.citydoctor2.utils.Copy; +import org.citygml4j.core.model.core.ImplicitGeometry; +import org.citygml4j.core.model.core.TransformationMatrix4x4; +import org.xmlobjects.gml.model.geometry.primitives.Point; +import org.xmlobjects.gml.model.geometry.primitives.PointProperty; +import org.xmlobjects.util.copy.CopyBuilder; + +import java.util.*; + +/** + * Adapter class for CityGML-TransformationMatrix4x4 + */ +public class TransformationMatrix { + + private final TransformationMatrix4x4 originalTM4x4; + + private Matrix tMatrix; + /** + * The {@code TransformationMatrix} class is an adapter for applying the {@link TransformationMatrix4x4} of implicitGeometries to geometries + * of CityDoctor's internal model. + * @param transformationMatrix the transformation matrix. + * @param pointProperty the reference point for the translation of the geometry's position. + */ + public TransformationMatrix(TransformationMatrix4x4 transformationMatrix, PointProperty pointProperty) { + originalTM4x4 = transformationMatrix; + setupMatrix(); + incorporateRefPoint(pointProperty); + } + + /** + * The {@code TransformationMatrix} class is an adapter for applying the {@link TransformationMatrix4x4} of implicitGeometries to geometries + * of CityDoctor's internal model. + * @param implicitGeometry the implicitGeometry containing the TransformationMatrix and ReferencePoint. + */ + public TransformationMatrix(ImplicitGeometry implicitGeometry) { + originalTM4x4 = implicitGeometry.getTransformationMatrix(); + setupMatrix(); + incorporateRefPoint(implicitGeometry.getReferencePoint()); + + } + + + private void setupMatrix() { + double[][] matrix = new double[4][4]; + CopyBuilder copyBuilder = new CopyBuilder(); + TransformationMatrix4x4 tmMatrix = (TransformationMatrix4x4) originalTM4x4.deepCopy(copyBuilder); + List tmValues = tmMatrix.toRowMajorList(); + for (int i = 0; i < 4 ; i++){ + for (int j = 0; j < 4 ; j++){ + matrix[i][j] = tmValues.get(i*4+j); + } + } + tMatrix = new Matrix(matrix); + } + + private void incorporateRefPoint(PointProperty pointProperty) { + Point refPoint = pointProperty.getObject(); + List refCoord = refPoint.getPos().toCoordinateList3D(); + double [][] tArray = tMatrix.getArray(); + tArray[0][3] = refCoord.get(0); + tArray[1][3] = refCoord.get(1); + tArray[2][3] = refCoord.get(2); + } + + + /** + * Transforms a vector with the transformation matrix. + * @param referenceGeom reference geometry to transform. + * @return A transformed copy of {@code referenceGeom}. + */ + public Geometry transformGeometry(Geometry referenceGeom) { + + // Create deep copy of reference geom + Geometry geom = Copy.copy(referenceGeom); + Map visitedVertices = new IdentityHashMap<>(); + Set visitedVerticesSet = Collections.newSetFromMap(visitedVertices); + // Transform all vertices + for (Polygon polygon : geom.getPolygons()){ + for (Vertex vertex : polygon.getExteriorRing().getVertices()){ + if (!visitedVerticesSet.contains(vertex)){ + transformVector3D(vertex); + visitedVerticesSet.add(vertex); + } + } + for (LinearRing ring : polygon.getInnerRings()){ + for (Vertex vertex : ring.getVertices()){ + if (!visitedVerticesSet.contains(vertex)){ + transformVector3D(vertex); + visitedVerticesSet.add(vertex); + } + } + } + } + geom.updateEdgesAndVertices(); + return geom; + } + + + public void transformVector3D(Vector3d vector) { + double[] tfVector = new double[]{vector.getX(), vector.getY(), vector.getZ(), 1d}; + Matrix vectorMatrix = new Matrix(tfVector,4); + Matrix res = tMatrix.times(vectorMatrix); + double[] resultVector = res.getRowPackedCopy(); + vector.setX(resultVector[0]); + vector.setY(resultVector[1]); + vector.setZ(resultVector[2]); + } + + + +} -- GitLab From a37867463bbe51e2f5704e1ccac08739c3e88cbb Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 10 Oct 2024 16:55:35 +0200 Subject: [PATCH 15/55] Add mapping of ImplicitGeometry --- .../citygml3/Citygml3FeatureMapper.java | 84 ++++++++++++++----- .../citydoctor2/parser/CityGmlParser.java | 16 +++- 2 files changed, 79 insertions(+), 21 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index 3b394c1..c5c9964 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -1,6 +1,6 @@ /*- * 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 @@ -44,16 +44,7 @@ import org.citygml4j.core.model.building.BuildingFurniture; import org.citygml4j.core.model.building.BuildingUnit; import org.citygml4j.core.model.building.Storey; import org.citygml4j.core.model.construction.AbstractConstruction; -import org.citygml4j.core.model.core.AbstractCityObject; -import org.citygml4j.core.model.core.AbstractFeatureWithLifespan; -import org.citygml4j.core.model.core.AbstractOccupiedSpace; -import org.citygml4j.core.model.core.AbstractPhysicalSpace; -import org.citygml4j.core.model.core.AbstractSpace; -import org.citygml4j.core.model.core.AbstractSpaceBoundary; -import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty; -import org.citygml4j.core.model.core.AbstractThematicSurface; -import org.citygml4j.core.model.core.AbstractUnoccupiedSpace; -import org.citygml4j.core.model.core.CityModel; +import org.citygml4j.core.model.core.*; import org.citygml4j.core.model.deprecated.transportation.TransportationComplex; import org.citygml4j.core.model.landuse.LandUse; import org.citygml4j.core.model.transportation.AbstractTransportationSpace; @@ -103,11 +94,12 @@ public class Citygml3FeatureMapper extends ObjectWalker { private List references = new ArrayList<>(); private Map vertexMap = new HashMap<>(); private final ParserConfiguration config; - + private final Path directory; private final double neighborDistance; public Citygml3FeatureMapper(ParserConfiguration config, Path path) { this.config = config; + this.directory = path.getParent(); model = new CityDoctorModel(config, path.toFile()); neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces()); } @@ -207,13 +199,13 @@ public class Citygml3FeatureMapper extends ObjectWalker { finishCityObjectConstruction(veg); model.addVegetation(veg); } - + @Override public void visit(SolitaryVegetationObject solitaryVegetationObject) { Vegetation veg = new Vegetation(VegetationType.SOLITARY_VEGETATION_OBJECT); veg.setGmlObject(solitaryVegetationObject); mapAbstractVegetationObject(solitaryVegetationObject, veg); - + parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod1Geometry(), Lod.LOD1, veg); parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod2Geometry(), Lod.LOD2, veg); parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod3Geometry(), Lod.LOD3, veg); @@ -222,13 +214,14 @@ public class Citygml3FeatureMapper extends ObjectWalker { finishCityObjectConstruction(veg); model.addVegetation(veg); } - + private void mapAbstractVegetationObject(AbstractVegetationObject avo, Vegetation veg) { mapAbstractOccupiedSpace(avo, veg); } private void mapAbstractOccupiedSpace(AbstractOccupiedSpace aos, CityObject co) { mapAbstractPhysicalSpace(aos, co); + parseImplicitGeometry(aos, co); } private void mapAbstractPhysicalSpace(AbstractPhysicalSpace aps, CityObject co) { @@ -245,6 +238,8 @@ public class Citygml3FeatureMapper extends ObjectWalker { parseAndAddSolid(as.getLod3Solid(), Lod.LOD3, co); } + + @Override public void visit(Bridge bridge) { BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, bridge); @@ -289,7 +284,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { mapConstructiveElement(constructiveElement, cdEle); bo.addConstructiveElement(cdEle); } - + List bridgeInstallations = ab.getBridgeInstallations(); for (BridgeInstallationProperty installationProp : bridgeInstallations) { var gmlBi = installationProp.getObject(); @@ -300,7 +295,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { Installation bi = mapBridgeInstallation(gmlBi); bo.addBridgeInstallation(bi); } - + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); for (AbstractSpaceBoundaryProperty surfaceProp : ab.getBoundaries()) { if (!surfaceProp.isSetObject()) { @@ -310,11 +305,11 @@ public class Citygml3FeatureMapper extends ObjectWalker { surface.accept(surfaceMapper); } updatePartOfSurface(bo, surfaceMapper); - + for (BoundarySurface bs : bo.getBoundarySurfaces()) { updateEdgesAndVertices(bs); } - + bo.unsetGmlGeometries(); } @@ -369,7 +364,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { private void mapConstructiveElement(org.citygml4j.core.model.bridge.BridgeConstructiveElement ele, BridgeConstructiveElement bce) { mapAbstractConstructiveElement(ele, bce); - + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); for (AbstractSpaceBoundaryProperty surfaceProp : ele.getBoundaries()) { if (!surfaceProp.isSetObject()) { @@ -547,6 +542,55 @@ public class Citygml3FeatureMapper extends ObjectWalker { } } + private void parseImplicitGeometry(AbstractOccupiedSpace aos, CityObject co){ + for (int i = 1; i <= 3; i++){ + if (aos.getImplicitRepresentation(i) == null){ + continue; + } + if (aos.getImplicitRepresentation(i).getObject() != null){ + ImplicitGeometry impGeom = aos.getImplicitRepresentation(i).getObject(); + ImplicitGeometryHolder igh = resolveImplicitGeometry(impGeom, i); + if (igh != null){ + co.addGeometry(igh); + } + } else if (aos.getImplicitRepresentation(i).getHref() != null){ + // TODO: HREF handling of implicit geometries + } + + + } + + } + + private ImplicitGeometryHolder resolveImplicitGeometry(ImplicitGeometry ig, int lodInt) { + ImplicitGeometryHolder igh = null; + if (ig.getLibraryObject() != null){ + Path libraryObjectPath = directory.resolve(ig.getLibraryObject()); + LibraryObject libObj = LibraryObject.of(libraryObjectPath, config); + if (libObj != null){ + igh = new ImplicitGeometryHolder(ig, libObj); + } + } else if (ig.getRelativeGeometry() != null){ + AbstractGeometry aGeom = ig.getRelativeGeometry().getObject(); + Geometry geom = null; + Lod lod = Lod.values()[lodInt]; + RelativeGeometry relGeo = null; + if (aGeom instanceof MultiSurface ms){ + geom = parseMultiSurface(ms, lod); + } else if (aGeom instanceof Solid s){ + geom = parseSolid(s, lod); + } + if (geom != null){ + relGeo = RelativeGeometry.of(geom); + igh = new ImplicitGeometryHolder(ig, relGeo); + } + + } else{ + logger.error(String.format("Implicit geometry of GML-ID %s has no referenced geometry.", ig.getId())); + } + return igh; + } + private void resolveAndClearReferences() { for (ResolvableReference ref : references) { String href = ref.href(); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 0a83532..f8d7bcf 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -122,6 +122,9 @@ public class CityGmlParser { private static CityGMLContext context; private static List chunkProperties = new ArrayList<>(); + // Toggle to suppress logger output for parsing of libraryObjects + private static boolean gagged = false; + static { System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); FACTORY = SAXParserFactory.newInstance(); @@ -326,6 +329,14 @@ public class CityGmlParser { return writer; } + /** + * Suppresses logger output of {@link #readAndKeepFeatures} for the next parse. + * Used to prevent logging spam while resolving implicit geometries. + */ + public static void gagLogger(boolean value){ + gagged = value; + } + private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file, CityGMLInputFactory inputFactory, ObservedInputStream ois) throws CityGMLReadException { try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { @@ -362,9 +373,12 @@ public class CityGmlParser { for (AbstractCityObject aco : acos) { cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); } - if (logger.isInfoEnabled()) { + if (logger.isInfoEnabled() && !gagged) { logger.info(Localization.getText("CityGmlParser.parsedObjects"), mapper.getModel().getNumberOfFeatures()); + } else if (gagged){ + // Remove gag + gagged = false; } return mapper.getModel(); } -- GitLab From 7ff5925b5f0b60e8fe5c7ba69d0b2d915afc8c68 Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 10 Oct 2024 16:58:08 +0200 Subject: [PATCH 16/55] Add world rendering for recently added CityObjects --- .../stuttgart/citydoctor2/gui/Renderer.java | 32 ++++++++++++++++++- .../citydoctor2/gui/TriangulatedGeometry.java | 2 ++ .../gui/tree/AllCityFurnitureNode.java | 1 + .../gui/tree/AllOtherObjectsNode.java | 2 ++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java index fe246e5..37e9f9a 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java @@ -719,7 +719,33 @@ public class Renderer { } public void renderCityFurniture(List cityFurniture){ - renderCityObjects(cityFurniture, Color.CYAN); + errorUpdater = null; + refresher = () -> { + Platform.runLater(() -> { + loadingDialog.show(); + clearGeometryTrees(); + }); + Thread t = new Thread(() -> { + Set polygons = new HashSet<>(); + for (CityFurniture cf : cityFurniture) { + collectCityFurniture(polygons, cf); + } + currentTriGeom = TriangulatedGeometry.of(polygons, Color.BLUEVIOLET); + 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 renderTerrain(List land) { @@ -809,6 +835,10 @@ public class Renderer { } } + private void collectCityFurniture(Set polygons, CityFurniture cf) { + addPolygons(cf, polygons); + } + private void addConcretePolygons(Set polygons, Geometry geom) { for (Polygon p : geom.getPolygons()) { polygons.add(p.getOriginal()); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java index b550194..ec6788f 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/TriangulatedGeometry.java @@ -110,6 +110,8 @@ public class TriangulatedGeometry { addPolygonDataFromCityObjects(model.getTransportation(), triGeom, Color.YELLOW, filters); addPolygonDataFromCityObjects(model.getVegetation(), triGeom, Color.LIGHTGREEN, filters); addPolygonDataFromCityObjects(model.getWater(), triGeom, Color.LIGHTSKYBLUE, filters); + addPolygonDataFromCityObjects(model.getCityFurniture(), triGeom, Color.BLUEVIOLET, filters); + addPolygonDataFromCityObjects(model.getGenericCityObjects(), triGeom, Color.DARKGRAY, filters); return triGeom; } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java index 3da1516..936e25f 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllCityFurnitureNode.java @@ -26,6 +26,7 @@ public class AllCityFurnitureNode extends Renderable{ @Override public void visit(Renderer renderer) { + renderer.clearCurrentRender(); renderer.renderCityFurniture(cityFurnitures); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOtherObjectsNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOtherObjectsNode.java index cdb90fe..c45ca90 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOtherObjectsNode.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllOtherObjectsNode.java @@ -3,6 +3,7 @@ package de.hft.stuttgart.citydoctor2.gui.tree; import de.hft.stuttgart.citydoctor2.datastructure.GenericCityObject; import de.hft.stuttgart.citydoctor2.gui.CheckStatus; import de.hft.stuttgart.citydoctor2.gui.Renderer; +import javafx.scene.paint.Color; import java.util.List; @@ -41,6 +42,7 @@ public class AllOtherObjectsNode extends Renderable{ @Override public void visit(Renderer renderer) { renderer.clearCurrentRender(); + renderer.renderCityObjects(genericObjects, Color.GRAY); } } -- GitLab From ab203d0be3671c3104636f8bf21e83a216b3ffbb Mon Sep 17 00:00:00 2001 From: Riegel Date: Thu, 10 Oct 2024 16:58:37 +0200 Subject: [PATCH 17/55] Add tooltip to showWorld button --- .../src/main/resources/CityDoctorLocalization.properties | 1 + .../src/main/resources/CityDoctorLocalization_de.properties | 1 + .../main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 04f69e9..e66b529 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -29,6 +29,7 @@ MainToolBar.wireframe=Show/Hide Wireframe MainToolBar.culling=Enable/Disable Culling MainToolBar.writeReports=Write Reports MainToolBar.executeChecks=Execute Checks +MainToolBar.showWorld=Show entire city model MainWindow.missingConfig=Could not find configuration file. MainWindow.loadGivenFile=Loading given file, please wait MainWindow.finishedLoading=Finished loading diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 1b0315d..55d093e 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -27,6 +27,7 @@ MainToolBar.wireframe=Zeige/Verstecke Gitternetz MainToolBar.culling=Aktiviere/Deaktiviere Entfernen der R\u00fcckseiten MainToolBar.writeReports=Schreibe Reports MainToolBar.executeChecks=F\u00fchre Pr\u00fcfungen aus +MainToolBar.showWorld=Gesamtes Stadtmodell anzeigen MainWindow.missingConfig=Konnte Konfigurationsdatei nicht finden. MainWindow.loadGivenFile=Lade vorhandene Datei, bitte warten MainWindow.finishedLoading=Fertig geladen diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java index 9ad3a38..45ca1f8 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java @@ -139,7 +139,6 @@ public class MainToolBar { setupLodButtons(); setupAboutButton(); setupReportButton(); - loadImages(); gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected())); @@ -156,6 +155,7 @@ public class MainToolBar { } controller.showWorld(); }); + showWorldBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.showWorld"))); } private void loadImages() { -- GitLab From 61c6bd73464f93ab3be9609bfba637d584f5f744 Mon Sep 17 00:00:00 2001 From: Riegel Date: Fri, 11 Oct 2024 14:04:13 +0200 Subject: [PATCH 18/55] Add parsing of composite surfaces for ImplicitGeometry --- .../citygml3/Citygml3FeatureMapper.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index c5c9964..c07a73b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -63,12 +63,14 @@ import org.citygml4j.core.model.vegetation.PlantCover; import org.citygml4j.core.model.vegetation.SolitaryVegetationObject; import org.citygml4j.core.model.waterbody.WaterBody; import org.citygml4j.core.visitor.ObjectWalker; +import org.xmlobjects.gml.adapter.geometry.complexes.CompositeSurfaceAdapter; import org.xmlobjects.gml.model.base.AbstractGML; import org.xmlobjects.gml.model.feature.AbstractFeature; import org.xmlobjects.gml.model.geometry.AbstractGeometry; import org.xmlobjects.gml.model.geometry.GeometryProperty; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.complexes.CompositeSurface; import org.xmlobjects.gml.model.geometry.primitives.AbstractSolid; import org.xmlobjects.gml.model.geometry.primitives.AbstractSurface; import org.xmlobjects.gml.model.geometry.primitives.Shell; @@ -233,9 +235,13 @@ public class Citygml3FeatureMapper extends ObjectWalker { parseAndAddMultiSurface(as.getLod0MultiSurface(), Lod.LOD0, co); parseAndAddMultiSurface(as.getLod2MultiSurface(), Lod.LOD2, co); parseAndAddMultiSurface(as.getLod3MultiSurface(), Lod.LOD3, co); + parseAndAddCompositeSurface(as.getLod0MultiSurface(), Lod.LOD0, co); + parseAndAddCompositeSurface(as.getLod2MultiSurface(), Lod.LOD2, co); + parseAndAddCompositeSurface(as.getLod3MultiSurface(), Lod.LOD3, co); parseAndAddSolid(as.getLod1Solid(), Lod.LOD1, co); parseAndAddSolid(as.getLod2Solid(), Lod.LOD2, co); parseAndAddSolid(as.getLod3Solid(), Lod.LOD3, co); + } @@ -519,9 +525,13 @@ public class Citygml3FeatureMapper extends ObjectWalker { parseAndAddMultiSurface(as.getLod0MultiSurface(), Lod.LOD0, co); parseAndAddMultiSurface(as.getLod2MultiSurface(), Lod.LOD2, co); parseAndAddMultiSurface(as.getLod3MultiSurface(), Lod.LOD3, co); + parseAndAddCompositeSurface(as.getLod0MultiSurface(), Lod.LOD0, co); + parseAndAddCompositeSurface(as.getLod2MultiSurface(), Lod.LOD2, co); + parseAndAddCompositeSurface(as.getLod3MultiSurface(), Lod.LOD3, co); parseAndAddSolid(as.getLod1Solid(), Lod.LOD1, co); parseAndAddSolid(as.getLod2Solid(), Lod.LOD2, co); parseAndAddSolid(as.getLod3Solid(), Lod.LOD3, co); + } private void parseAndAddMultiSurface(MultiSurfaceProperty msp, Lod lod, CityObject co) { @@ -542,6 +552,22 @@ public class Citygml3FeatureMapper extends ObjectWalker { } } + private void parseAndAddCompositeSurface(MultiSurfaceProperty ms, Lod lod, CityObject co){ + if (ms == null || ms.getObject() == null || ms.getObject().getSurfaceMember().isEmpty()) { + return; + } + List surfaces = ms.getObject().getSurfaceMember(); + for (SurfaceProperty surface : surfaces) { + if (surface.getObject() instanceof CompositeSurface cs) { + Geometry geom = parseCompositeSurface(cs, lod); + if (geom != null) { + co.addGeometry(geom); + } + } + } + + } + private void parseImplicitGeometry(AbstractOccupiedSpace aos, CityObject co){ for (int i = 1; i <= 3; i++){ if (aos.getImplicitRepresentation(i) == null){ @@ -579,6 +605,8 @@ public class Citygml3FeatureMapper extends ObjectWalker { geom = parseMultiSurface(ms, lod); } else if (aGeom instanceof Solid s){ geom = parseSolid(s, lod); + } else if (aGeom instanceof CompositeSurface cs){ + geom = parseCompositeSurface(cs, lod); } if (geom != null){ relGeo = RelativeGeometry.of(geom); @@ -948,6 +976,13 @@ public class Citygml3FeatureMapper extends ObjectWalker { } } + public Geometry parseCompositeSurface(CompositeSurface cs, Lod lod) { + Geometry geom = new Geometry(GeometryType.COMPOSITE_SURFACE, lod); + Citygml3GeometryMapper geometryMapper = new Citygml3GeometryMapper(config, vertexMap); + readSurfaceMember(geom, geometryMapper, cs.getSurfaceMembers()); + return geom; + } + private void updateEdgesAndVertices(CityObject co) { // searching for neighboring vertices, replacing them with one single vertex to // avoid later problems with edges and manifold problems -- GitLab From 8858aa4588c1ef36bf8567a791d81abb631ed100 Mon Sep 17 00:00:00 2001 From: Riegel Date: Fri, 18 Oct 2024 13:51:42 +0200 Subject: [PATCH 19/55] Add support for Tunnel --- .../datastructure/AbstractTunnel.java | 418 ++++++++++++++++++ .../datastructure/FeatureType.java | 4 +- .../datastructure/LibraryObject.java | 4 +- .../citydoctor2/datastructure/Tunnel.java | 157 +++++++ .../datastructure/TunnelFurniture.java | 22 + .../datastructure/TunnelHollow.java | 56 +++ .../citydoctor2/datastructure/TunnelPart.java | 55 +++ 7 files changed, 712 insertions(+), 4 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractTunnel.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Tunnel.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelFurniture.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelHollow.java create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractTunnel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractTunnel.java new file mode 100644 index 0000000..02dc38f --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractTunnel.java @@ -0,0 +1,418 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty; +import org.citygml4j.core.util.geometry.GeometryFactory; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.primitives.Solid; +import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractTunnel extends CityObject{ + + @Serial + private static final long serialVersionUID = -2196414503088741206L; + + private static final Logger logger = LogManager.getLogger(AbstractTunnel.class); + + private final List tunnelInstallations = new ArrayList<>(2); + private final List boundarySurfaceList = new ArrayList<>(); + private final List tunnelHollows = new ArrayList<>(); + private final List tunnelParts = new ArrayList<>(); + private final List tunnelFurnitureList = new ArrayList<>(); + private org.citygml4j.core.model.tunnel.AbstractTunnel at; + + /** + * Getter for all boundary surfaces contained in this building. + * + * @return the boundary surfaces + */ + public List getBoundarySurfaces() { + return boundarySurfaceList; + } + + @Override + public org.citygml4j.core.model.tunnel.AbstractTunnel getGmlObject() { + return at; + } + + @Override + public FeatureType getFeatureType() { + return FeatureType.TUNNEL; + } + + @Override + public void unsetGmlGeometries() { + at.setLod1Solid(null); + at.setLod2Solid(null); + at.setLod3Solid(null); + at.setLod2MultiSurface(null); + at.setLod3MultiSurface(null); + at.getDeprecatedProperties().setLod1MultiSurface(null); + at.getDeprecatedProperties().setLod4MultiSurface(null); + at.getDeprecatedProperties().setLod4Solid(null); + + for (BoundarySurface bs : boundarySurfaceList) { + bs.unsetGmlGeometries(); + } + for (Installation bi : tunnelInstallations) { + bi.unsetGmlGeometries(); + } + for (TunnelHollow th : tunnelHollows) { + th.unsetGmlGeometries(); + } + for (TunnelFurniture tfr : tunnelFurnitureList) { + tfr.unsetGmlGeometries(); + } + for (TunnelPart tp : tunnelParts) { + tp.unsetGmlGeometries(); + } + } + + @Override + public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { + for (Geometry geom : getGeometries()) { + if (geom.getType() == GeometryType.MULTI_SURFACE) { + MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config); + setMultiSurfaceAccordingToLod(geom, ms); + } else { + Solid solid = CityGmlUtils.createSolid(geom, factory, config); + setSolidAccordingToLod(geom, solid); + } + } + for (BoundarySurface bs : boundarySurfaceList) { + reCreateBoundarySurface(factory, config, bs); + } + for (Installation bi : tunnelInstallations) { + bi.reCreateGeometries(factory, config); + } + for (TunnelHollow th : tunnelHollows) { + th.reCreateGeometries(factory, config); + } + for (TunnelFurniture tfr : tunnelFurnitureList) { + tfr.reCreateGeometries(factory, config); + } + for (TunnelPart tp : tunnelParts) { + tp.reCreateGeometries(factory, config); + } + } + + private void reCreateBoundarySurface(GeometryFactory factory, ParserConfiguration config, BoundarySurface bs) { + if (bs.getGeometries().isEmpty()) { + for (AbstractSpaceBoundaryProperty bsp : at.getBoundaries()) { + if (bsp.getObject() != null && bsp.getObject() == bs.getGmlObject()) { + logger.warn("Found empty boundary surface: {}, removing from building", bs.getGmlId()); + at.getBoundaries().remove(bsp); + break; + } + } + return; + } + bs.reCreateGeometries(factory, config); + } + + private void setMultiSurfaceAccordingToLod(Geometry geom, MultiSurface ms) { + switch (geom.getLod()) { + case LOD0: + at.setLod0MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD1: + at.getDeprecatedProperties().setLod1MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD2: + at.setLod2MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD3: + at.setLod3MultiSurface(new MultiSurfaceProperty(ms)); + break; + case LOD4: + at.getDeprecatedProperties().setLod4MultiSurface(new MultiSurfaceProperty(ms)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " multi surface to buildings"); + } + } + + private void setSolidAccordingToLod(Geometry geom, Solid solid) { + switch (geom.getLod()) { + case LOD1: + at.setLod1Solid(new SolidProperty(solid)); + break; + case LOD2: + at.setLod2Solid(new SolidProperty(solid)); + break; + case LOD3: + at.setLod3Solid(new SolidProperty(solid)); + break; + case LOD4: + at.getDeprecatedProperties().setLod4Solid(new SolidProperty(solid)); + break; + default: + throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to buildings"); + } + } + + @Override + public void accept(Check c) { + super.accept(c); + if (c.canExecute(this)) { + c.check(this); + } + for (Installation bi : tunnelInstallations) { + bi.accept(c); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.accept(c); + } + for (TunnelHollow th : tunnelHollows) { + th.accept(c); + } + for (TunnelFurniture tfr : tunnelFurnitureList) { + tfr.accept(c); + } + for (TunnelPart tp : tunnelParts) { + tp.accept(c); + } + } + + @Override + public void collectContainedErrors(List errors) { + super.collectContainedErrors(errors); + for (Installation bi : tunnelInstallations) { + bi.collectContainedErrors(errors); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.collectContainedErrors(errors); + } + for (TunnelHollow th : tunnelHollows) { + th.collectContainedErrors(errors); + } + for (TunnelFurniture tfr : tunnelFurnitureList) { + tfr.collectContainedErrors(errors); + } + for (TunnelPart tp : tunnelParts) { + tp.collectContainedErrors(errors); + } + } + + @Override + public void clearAllContainedCheckResults() { + super.clearAllContainedCheckResults(); + for (Installation bi : tunnelInstallations) { + bi.clearAllContainedCheckResults(); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.clearAllContainedCheckResults(); + } + for (TunnelHollow th : tunnelHollows) { + th.clearAllContainedCheckResults(); + } + for (TunnelFurniture tfr : tunnelFurnitureList) { + tfr.clearAllContainedCheckResults(); + } + for (TunnelPart tp : tunnelParts) { + tp.clearAllContainedCheckResults(); + } + } + + @Override + public boolean containsError(CheckId checkIdentifier) { + boolean hasError = super.containsError(checkIdentifier); + if (hasError) { + return true; + } + for (Installation bi : tunnelInstallations) { + if (bi.containsError(checkIdentifier)) { + return true; + } + } + for (BoundarySurface bs : boundarySurfaceList) { + if (bs.containsError(checkIdentifier)) { + return true; + } + } + for (TunnelHollow th : tunnelHollows) { + if (th.containsError(checkIdentifier)) { + return true; + } + } + for (TunnelFurniture tfr : tunnelFurnitureList) { + if (tfr.containsError(checkIdentifier)) { + return true; + } + } + for (TunnelPart tp : tunnelParts) { + if (tp.containsError(checkIdentifier)) { + return true; + } + } + return false; + } + + @Override + public boolean containsAnyError() { + boolean hasError = super.containsAnyError(); + if (hasError) { + return true; + } + for (Installation bi : tunnelInstallations) { + if (bi.containsAnyError()) { + return true; + } + } + for (BoundarySurface bs : boundarySurfaceList) { + if (bs.containsAnyError()) { + return true; + } + } + for (TunnelHollow th : tunnelHollows) { + if (th.containsAnyError()) { + return true; + } + } + for (TunnelFurniture tfr : tunnelFurnitureList) { + if (tfr.containsAnyError()) { + return true; + } + } + for (TunnelPart tp : tunnelParts) { + if (tp.containsAnyError()) { + return true; + } + } + return false; + } + + void setCityGmlBuilding(org.citygml4j.core.model.tunnel.AbstractTunnel at) { + this.at = at; + } + + public void addBoundarySurface(BoundarySurface bs) { + boundarySurfaceList.add(bs); + bs.setParent(this); + } + + public void addTunnelInstallation(Installation coBi) { + tunnelInstallations.add(coBi); + coBi.setParent(this); + } + + public void addTunnelHollow(TunnelHollow hollow) { + tunnelHollows.add(hollow); + hollow.setParent(this); + } + + public void addTunnelFurniture(TunnelFurniture furniture) { + tunnelFurnitureList.add(furniture); + furniture.setParent(this); + } + + + public void addTunnelPart(TunnelPart tunnelPart) { + tunnelParts.add(tunnelPart); + } + + public void setGmlObject(org.citygml4j.core.model.tunnel.AbstractTunnel at) { + this.at = at; + } + + public List getTunnelInstallations() { + return tunnelInstallations; + } + + public List getTunnelHollows() { + return tunnelHollows; + } + + public List getTunnelFurnitureList() { + return tunnelFurnitureList; + } + + public List getTunnelParts() { + return tunnelParts; + } + + @Override + public void prepareForChecking() { + super.prepareForChecking(); + for (Installation bi : tunnelInstallations) { + bi.prepareForChecking(); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.prepareForChecking(); + } + for (TunnelHollow th : tunnelHollows) { + th.prepareForChecking(); + } + for (TunnelFurniture tfr : tunnelFurnitureList) { + tfr.prepareForChecking(); + } + for (TunnelPart tp : tunnelParts) { + tp.prepareForChecking(); + } + } + + @Override + public void clearMetaInformation() { + super.clearMetaInformation(); + for (Installation bi : tunnelInstallations) { + bi.clearMetaInformation(); + } + for (BoundarySurface bs : boundarySurfaceList) { + bs.clearMetaInformation(); + } + for (TunnelHollow th : tunnelHollows) { + th.clearMetaInformation(); + } + for (TunnelFurniture tfr : tunnelFurnitureList) { + tfr.clearMetaInformation(); + } + for (TunnelPart tp : tunnelParts) { + tp.clearMetaInformation(); + } + } + + @Override + public void collectInstances(CopyHandler handler) { + super.collectInstances(handler); + handler.addInstance(boundarySurfaceList); + handler.addInstance(tunnelInstallations); + handler.addInstance(tunnelHollows); + handler.addInstance(tunnelFurnitureList); + handler.addInstance(tunnelParts); + } + + @Override + public void fillValues(Copyable original, CopyHandler handler) { + super.fillValues(original, handler); + AbstractTunnel originalAt = (AbstractTunnel) original; + for (BoundarySurface originalBs : originalAt.boundarySurfaceList) { + boundarySurfaceList.add(handler.getCopyInstance(originalBs)); + } + for (Installation originalTi : originalAt.tunnelInstallations) { + tunnelInstallations.add(handler.getCopyInstance(originalTi)); + } + for (TunnelHollow originalTh : originalAt.tunnelHollows) { + tunnelHollows.add(handler.getCopyInstance(originalTh)); + } + for (TunnelFurniture originalTFR : originalAt.tunnelFurnitureList) { + tunnelFurnitureList.add(handler.getCopyInstance(originalTFR)); + } + for (TunnelPart originalTp : originalAt.tunnelParts) { + tunnelParts.add(handler.getCopyInstance(originalTp)); + } + at = originalAt.at; + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java index bce998e..aaf543e 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java @@ -27,6 +27,6 @@ package de.hft.stuttgart.citydoctor2.datastructure; public enum FeatureType { BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, INSTALLATION, OPENING, - BUILDING_PART, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE, GENERIC_CITY_OBJECT - + BUILDING_PART, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE, GENERIC_CITY_OBJECT, + TUNNEL, TUNNEL_PART } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java index fa178e1..1e5dbe6 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LibraryObject.java @@ -27,8 +27,8 @@ public class LibraryObject extends Geometry{ private static HashMap libraryObjects = new HashMap<>(); public static LibraryObject of(Path path, ParserConfiguration config) { - if (libraryObjects.containsKey(path)) { - return libraryObjects.get(path); + if (libraryObjects.containsKey(path.toString())) { + return libraryObjects.get(path.toString()); } Geometry protoGeom = parseFile(path, config); if (protoGeom == null) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Tunnel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Tunnel.java new file mode 100644 index 0000000..d420096 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Tunnel.java @@ -0,0 +1,157 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty; +import org.citygml4j.core.model.tunnel.TunnelInstallation; +import org.citygml4j.core.model.tunnel.TunnelInstallationProperty; +import org.citygml4j.core.util.geometry.GeometryFactory; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +public class Tunnel extends AbstractTunnel{ + + @Serial + private static final long serialVersionUID = 5316281216192555555L; + + private List tunnelParts = new ArrayList<>(); + + public List getTunnelParts() { + return tunnelParts; + } + + @Override + public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) { + super.reCreateGeometries(factory, config); + for (TunnelPart tp : tunnelParts) { + tp.reCreateGeometries(factory, config); + } + } + + @Override + public void accept(Check c) { + super.accept(c); + if (c.canExecute(this)) { + c.check(this); + } + for (TunnelPart tp : tunnelParts) { + tp.accept(c); + } + } + + @Override + public void clearAllContainedCheckResults() { + super.clearAllContainedCheckResults(); + for (TunnelPart tp : tunnelParts) { + tp.clearAllContainedCheckResults(); + } + } + + @Override + public void collectContainedErrors(List errors) { + super.collectContainedErrors(errors); + for (TunnelPart tp : tunnelParts) { + tp.collectContainedErrors(errors); + } + } + + @Override + public boolean containsAnyError() { + boolean hasError = super.containsAnyError(); + if (hasError) { + return true; + } + for (TunnelPart tp : tunnelParts) { + if (tp.containsAnyError()) { + return true; + } + } + return false; + } + + @Override + public boolean containsError(CheckId checkIdentifier) { + boolean hasError = super.containsError(checkIdentifier); + if (hasError) { + return true; + } + for (TunnelPart tp : tunnelParts) { + if (tp.containsError(checkIdentifier)) { + return true; + } + } + return false; + } + + public void addTunnelPart(TunnelPart tunnelPart) { + tunnelParts.add(tunnelPart); + } + + @Override + public String toString() { + return "Tunnel [id=" + getGmlId() + "]"; + } + + public void anonymize() { + for (Geometry geom : getGeometries()) { + geom.anonymize(); + } + org.citygml4j.core.model.tunnel.Tunnel gmlT = new org.citygml4j.core.model.tunnel.Tunnel(); + gmlT.setId(GmlId.generateId().getGmlString()); + for (Installation ti : getTunnelInstallations()) { + ti.anonymize(); + gmlT.getTunnelInstallations().add(new TunnelInstallationProperty((TunnelInstallation) ti.getGmlObject())); + } + for (BoundarySurface bs : getBoundarySurfaces()) { + bs.anonymize(); + gmlT.addBoundary(new AbstractSpaceBoundaryProperty(bs.getGmlObject())); + } + setCityGmlBuilding(gmlT); + } + + @Override + public void clearMetaInformation() { + super.clearMetaInformation(); + for (TunnelPart tp : tunnelParts) { + tp.clearMetaInformation(); + } + } + + @Override + public void prepareForChecking() { + super.prepareForChecking(); + for (TunnelPart tp : tunnelParts) { + tp.prepareForChecking(); + } + } + + @Override + public void fillValues(Copyable original, CopyHandler handler) { + super.fillValues(original, handler); + Tunnel originalTunnel = (Tunnel) original; + for (TunnelPart originalTp : originalTunnel.tunnelParts) { + tunnelParts.add(handler.getCopyInstance(originalTp)); + } + } + + @Override + public void collectInstances(CopyHandler handler) { + super.collectInstances(handler); + for (TunnelPart tp : tunnelParts) { + handler.addInstance(tp); + } + } + + @Override + public Copyable createCopyInstance() { + return new Tunnel(); + } + + +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelFurniture.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelFurniture.java new file mode 100644 index 0000000..8874bac --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelFurniture.java @@ -0,0 +1,22 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.model.building.BuildingFurniture; + +import java.io.Serial; + +public class TunnelFurniture extends AbstractFurniture { + + @Serial + private static final long serialVersionUID = -4667219019432204174L; + + public void setGmlObject(org.citygml4j.core.model.tunnel.TunnelFurniture gmlObject) { + super.setGmlObject(gmlObject); + } + + @Override + public Copyable createCopyInstance(){ + return new BuildingRoomFurniture(); + } + +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelHollow.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelHollow.java new file mode 100644 index 0000000..b9cb085 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelHollow.java @@ -0,0 +1,56 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; +import org.citygml4j.core.model.tunnel.TunnelFurnitureProperty; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +public class TunnelHollow extends AbstractRoom{ + @Serial + private static final long serialVersionUID = -276088332165299253L; + private final List furnitureRefs = new ArrayList<>(2); + private CityObject parent; + + public void setGmlObject(org.citygml4j.core.model.building.BuildingRoom cgmlRoom){ + super.cgmlRoom = cgmlRoom; + } + + + public void setParent(CityObject parent){ + this.parent = parent; + } + + public void addFurnitureRef(TunnelFurnitureProperty furnitureRef) { + furnitureRefs.add(furnitureRef); + } + + public List getFurnitureRefs() { + return furnitureRefs; + } + + public CityObject getParent() { + return parent; + } + + @Override + public void fillValues(Copyable original, CopyHandler handler){ + super.fillValues(original, handler); + + TunnelHollow oHollow = (TunnelHollow) original; + parent = handler.getCopyInstance(oHollow.getParent()); + } + + @Override + public void collectInstances(CopyHandler handler){ + super.collectInstances(handler); + handler.addInstance(parent); + } + + @Override + public Copyable createCopyInstance() { + return new TunnelHollow(); + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java new file mode 100644 index 0000000..0954492 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java @@ -0,0 +1,55 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; + +import java.io.Serial; + +public class TunnelPart extends AbstractTunnel { + + @Serial + private static final long serialVersionUID = 8200322261958679163L; + + private Tunnel parent; + + private TunnelPart() { + } + + public TunnelPart(Tunnel parent) { + this.parent = parent; + parent.addTunnelPart(this); + } + + public Tunnel getParent() { + return parent; + } + + @Override + public FeatureType getFeatureType() { + return FeatureType.TUNNEL_PART; + } + + @Override + public String toString() { + return "TunnelPart [id=" + getGmlId() + "]"; + } + + @Override + public void fillValues(Copyable original, CopyHandler handler) { + super.fillValues(original, handler); + TunnelPart originalPart = (TunnelPart) original; + parent = handler.getCopyInstance(originalPart.parent); + } + + @Override + public void collectInstances(CopyHandler handler) { + super.collectInstances(handler); + handler.addInstance(parent); + } + + @Override + public Copyable createCopyInstance() { + return new TunnelPart(); + } + +} -- GitLab From 2d9f08ee6f11e3a15e9997a207b3f31d32460547 Mon Sep 17 00:00:00 2001 From: Riegel Date: Fri, 18 Oct 2024 13:54:33 +0200 Subject: [PATCH 20/55] Add CompositePolygon --- .../datastructure/CompositePolygon.java | 278 ++++++++++++++++++ .../citygml3/Citygml3FeatureMapper.java | 8 +- .../mapper/citygml3/SurfaceMapper.java | 54 +++- 3 files changed, 326 insertions(+), 14 deletions(-) create mode 100644 CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CompositePolygon.java diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CompositePolygon.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CompositePolygon.java new file mode 100644 index 0000000..2a867b2 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CompositePolygon.java @@ -0,0 +1,278 @@ +package de.hft.stuttgart.citydoctor2.datastructure; + +import de.hft.stuttgart.citydoctor2.check.Check; +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.CheckId; +import de.hft.stuttgart.citydoctor2.check.Checkable; +import de.hft.stuttgart.citydoctor2.math.Triangle3d; +import de.hft.stuttgart.citydoctor2.math.Vector3d; +import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon; +import de.hft.stuttgart.citydoctor2.utils.CopyHandler; +import de.hft.stuttgart.citydoctor2.utils.Copyable; + +import java.io.Serial; +import java.util.*; + +/** + * + */ +public class CompositePolygon extends ConcretePolygon{ + + @Serial + private static final long serialVersionUID = -1867197873443341287L; + + private Geometry parent; + private BoundarySurface partOfSurface; + private Installation partOfInstallation; + private List compositeMembers = new ArrayList<>(); + private LinearRing exterior = null; + + @Override + public Vector3d calculateNormalNormalized() { + return this.calculateNormal().normalize(); + } + + @Override + public void accept(Check c){ + for (Polygon p : compositeMembers){ + p.accept(c); + } + setValidated(true); + } + + @Override + public Class getCheckClass() { + return Polygon.class; + } + + @Override + public boolean containsAnyError(){ + if (super.containsAnyError()){ + return true; + } + for (Polygon p : compositeMembers){ + if (p.containsAnyError()){ + return true; + } + } + return false; + } + + @Override + public void collectContainedErrors(List errors) { + super.collectContainedErrors(errors); + for (Polygon p : compositeMembers){ + p.collectContainedErrors(errors); + } + } + + @Override + public void clearAllContainedCheckResults() { + super.clearCheckResults(); + for (Polygon p : compositeMembers){ + p.clearAllContainedCheckResults(); + } + } + + public void addCompositeMember(Polygon p){ + compositeMembers.add(p); + } + + public List getCompositeMembers(){ + return compositeMembers; + } + + @Override + public boolean containsError(CheckId checkIdentifier) { + boolean hasError = super.containsError(checkIdentifier); + if (hasError) { + return true; + } + for (Polygon p : compositeMembers){ + if (p.containsError(checkIdentifier)) { + return true; + } + } + return false; + } + + + + @Override + public Vector3d calculateNormal() { + return this.getExteriorRing().calculateNormal(); + } + + @Override + public TesselatedPolygon tesselate() { + List tessPolys = new ArrayList<>(); + for (Polygon p : compositeMembers){ + TesselatedPolygon t = p.tesselate(); + tessPolys.addAll(t.getTriangles()); + } + return new TesselatedPolygon(tessPolys,this ); + } + + @Override + public LinearRing getExteriorRing() { + if( exterior != null ){ + return exterior; + } + Map outerVertices = new HashMap<>(); + for (Polygon p : compositeMembers){ + for (Vertex v: p.getExteriorRing().getVertices()){ + outerVertices.merge(v, 1, Integer::sum); + } + } + List vertices = new ArrayList<>(); + for (Vertex v : outerVertices.keySet()){ + if (outerVertices.get(v) <= 3){ + vertices.add(v); + } + } + LinearRing ext = new LinearRing(LinearRing.LinearRingType.EXTERIOR); + ext.setParent(this); + ext.addAllVertices(vertices); + exterior = ext; + return ext; + } + + @Override + public List getInnerRings() { + List innerRings = new ArrayList<>(); + for (Polygon p : compositeMembers){ + innerRings.addAll(p.getInnerRings()); + } + return innerRings; + } + + @Override + public boolean isPointInsideExteriorRing(Vector3d v) { + for (Polygon p : compositeMembers) { + if (p.isPointInsideExteriorRing(v)) { + return true; + } + } + return false; + } + + @Override + public Geometry getParent() { + return parent; + } + + @Override + public void setParent(Geometry geometry) { + this.parent = geometry; + } + + @Override + public void setExteriorRing(LinearRing extRing) { + // ConcretePolygons' exterior ring is not settable + } + + @Override + public boolean isPolygonConnectedViaPoint(Polygon other) { + for (Polygon p : compositeMembers) { + if (p.isPolygonConnectedViaPoint(other)) { + return true; + } + } + return false; + } + + @Override + public void addInteriorRing(LinearRing inter) { + // ConcretePolygons have no interior ring themselves + } + + @Override + public BoundarySurface getPartOfSurface() { + return partOfSurface; + } + + @Override + public void setPartOfSurface(BoundarySurface bs) { + this.partOfSurface = bs; + } + + @Override + public void removeInnerRing(LinearRing ring) { + for (Polygon p : compositeMembers) { + p.removeInnerRing(ring); + } + } + + @Override + public void setPartOfInstallation(Installation bi) { + this.partOfInstallation = bi; + } + + @Override + public Installation getPartOfInstallation() { + return partOfInstallation; + } + + @Override + public boolean hasPointAsCorner(Vertex v) { + for (Polygon p : compositeMembers){ + if (p.hasPointAsCorner(v)) { + return true; + } + } + return false; + } + + @Override + public void removeRings() { + + } + + @Override + public boolean isLinkedTo() { + return false; + } + + @Override + public boolean isLink() { + return false; + } + + @Override + public LinkedPolygon getLinkedFromPolygon() { + return null; + } + + @Override + void anonymize() { + + } + + + @Override + public double getArea() { + return 0; + } + + @Override + public void prepareForChecking() { + + } + + @Override + public void clearMetaInformation() { + + } + + + + @Override + public void collectInstances(CopyHandler handler) { + + } + + @Override + public Copyable createCopyInstance() { + return null; + } +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index c07a73b..eec47f1 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -553,7 +553,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { } private void parseAndAddCompositeSurface(MultiSurfaceProperty ms, Lod lod, CityObject co){ - if (ms == null || ms.getObject() == null || ms.getObject().getSurfaceMember().isEmpty()) { + if (ms == null || ms.getObject() == null) { return; } List surfaces = ms.getObject().getSurfaceMember(); @@ -633,6 +633,10 @@ public class Citygml3FeatureMapper extends ObjectWalker { } continue; } + // TODO: Insert handling of compPoly dummy objects + if (concPoly instanceof CompositePolygon comp) {} + // + LinkedPolygon lPoly = new LinkedPolygon(concPoly, geom); if (geom.getParent() instanceof BoundarySurface bs) { lPoly.setPartOfSurface(bs); @@ -976,7 +980,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { } } - public Geometry parseCompositeSurface(CompositeSurface cs, Lod lod) { + private Geometry parseCompositeSurface(CompositeSurface cs, Lod lod) { Geometry geom = new Geometry(GeometryType.COMPOSITE_SURFACE, lod); Citygml3GeometryMapper geometryMapper = new Citygml3GeometryMapper(config, vertexMap); readSurfaceMember(geom, geometryMapper, cs.getSurfaceMembers()); diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/SurfaceMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/SurfaceMapper.java index d75534a..43015bb 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/SurfaceMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/SurfaceMapper.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import de.hft.stuttgart.citydoctor2.datastructure.*; import org.citygml4j.core.model.construction.AbstractConstructionSurface; import org.citygml4j.core.model.construction.AbstractFillingSurface; import org.citygml4j.core.model.construction.AbstractFillingSurfaceProperty; @@ -40,20 +41,10 @@ import org.citygml4j.core.model.core.ClosureSurface; import org.citygml4j.core.visitor.ObjectWalker; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.complexes.CompositeSurface; import org.xmlobjects.gml.model.geometry.primitives.AbstractSurface; import org.xmlobjects.gml.model.geometry.primitives.SurfaceProperty; -import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; -import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; -import de.hft.stuttgart.citydoctor2.datastructure.CityObject; -import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; -import de.hft.stuttgart.citydoctor2.datastructure.Geometry; -import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; -import de.hft.stuttgart.citydoctor2.datastructure.Lod; -import de.hft.stuttgart.citydoctor2.datastructure.Opening; -import de.hft.stuttgart.citydoctor2.datastructure.OpeningType; -import de.hft.stuttgart.citydoctor2.datastructure.SurfaceFeatureType; -import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; public class SurfaceMapper extends ObjectWalker { @@ -106,6 +97,11 @@ public class SurfaceMapper extends ObjectWalker { parseAndAddMultiSurface(afs.getLod2MultiSurface(), Lod.LOD2, o); parseAndAddMultiSurface(afs.getLod3MultiSurface(), Lod.LOD3, o); parseAndAddMultiSurface(afs.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, o); + parseAndAddCompositeSurface(afs.getLod0MultiSurface(), Lod.LOD0, o); + parseAndAddCompositeSurface(afs.getLod1MultiSurface(), Lod.LOD1, o); + parseAndAddCompositeSurface(afs.getLod2MultiSurface(), Lod.LOD2, o); + parseAndAddCompositeSurface(afs.getLod3MultiSurface(), Lod.LOD3, o); + parseAndAddCompositeSurface(afs.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, o); o.unsetGmlGeometries(); } @@ -116,6 +112,11 @@ public class SurfaceMapper extends ObjectWalker { parseAndAddMultiSurface(gmlSurface.getLod2MultiSurface(), Lod.LOD2, cdSurface); parseAndAddMultiSurface(gmlSurface.getLod3MultiSurface(), Lod.LOD3, cdSurface); parseAndAddMultiSurface(gmlSurface.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, cdSurface); + parseAndAddCompositeSurface(gmlSurface.getLod0MultiSurface(), Lod.LOD0, cdSurface); + parseAndAddCompositeSurface(gmlSurface.getLod1MultiSurface(), Lod.LOD1, cdSurface); + parseAndAddCompositeSurface(gmlSurface.getLod2MultiSurface(), Lod.LOD2, cdSurface); + parseAndAddCompositeSurface(gmlSurface.getLod3MultiSurface(), Lod.LOD3, cdSurface); + parseAndAddCompositeSurface(gmlSurface.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, cdSurface); } private void parseAndAddMultiSurface(MultiSurfaceProperty msp, Lod lod, CityObject cdSurface) { @@ -132,7 +133,36 @@ public class SurfaceMapper extends ObjectWalker { readSurfaceMember(geom, geometryMapper, ms.getSurfaceMember()); return geom; } - + + private void parseAndAddCompositeSurface(MultiSurfaceProperty ms, Lod lod, CityObject cdSurface){ + if (ms == null || ms.getObject() == null) { + return; + } + List surfaceMember = ms.getObject().getSurfaceMember(); + for (SurfaceProperty surface : surfaceMember) { + if (surface.getObject() instanceof CompositeSurface cs) { + Geometry geom = parseCompositeSurface(cs, lod); + cdSurface.addGeometry(geom); + + } + } + + } + + private Geometry parseCompositeSurface(CompositeSurface cs, Lod lod) { + Geometry geom = new Geometry(GeometryType.COMPOSITE_SURFACE, lod); + Citygml3GeometryMapper geometryMapper = new Citygml3GeometryMapper(config, vertexMap); + readSurfaceMember(geom, geometryMapper, cs.getSurfaceMembers()); + if (cs.getId() != null){ + CompositePolygon compPoly = new CompositePolygon(); + polygonMap.put(cs.getId(), compPoly); + geom.getPolygons().forEach(compPoly::addCompositeMember); + compPoly.setParent(geom); + + } + return geom; + } + private void readSurfaceMember(Geometry geom, Citygml3GeometryMapper geometryMapper, List surfaceMember) { for (SurfaceProperty prop : surfaceMember) { -- GitLab From 81c9bcbd94e2e67650154365537ff4e94a70e0fc Mon Sep 17 00:00:00 2001 From: Riegel Date: Mon, 21 Oct 2024 16:48:03 +0200 Subject: [PATCH 21/55] Add parsing of Tunnel --- .../datastructure/CityDoctorModel.java | 31 ++- .../datastructure/TunnelHollow.java | 4 +- .../citydoctor2/datastructure/TunnelPart.java | 3 +- .../citygml3/Citygml3FeatureMapper.java | 195 +++++++++++++++++- .../CityDoctorLocalization.properties | 1 + .../CityDoctorLocalization_de.properties | 1 + .../citydoctor2/gui/CityDoctorController.java | 31 +++ .../stuttgart/citydoctor2/gui/MainWindow.java | 27 ++- .../gui/tree/AllFurnitureNode.java | 2 +- .../stuttgart/citydoctor2/gui/MainWindow.fxml | 5 + 10 files changed, 286 insertions(+), 14 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java index ba31e26..5a3fe3d 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/CityDoctorModel.java @@ -61,6 +61,7 @@ public class CityDoctorModel { private final List genericObjects; private final List land; private final List roads; + private final List tunnels; private final List water; private CityModel cModel; private final ParserConfiguration config; @@ -83,6 +84,7 @@ public class CityDoctorModel { bridges = new ArrayList<>(); land = new ArrayList<>(); roads = new ArrayList<>(); + tunnels = new ArrayList<>(); water = new ArrayList<>(); cityfurniture = new ArrayList<>(); genericObjects = new ArrayList<>(); @@ -125,7 +127,7 @@ public class CityDoctorModel { public Stream createFeatureStream() { return Stream.of(buildings.stream(), vegetation.stream(), bridges.stream(), land.stream(), roads.stream(), - water.stream(), cityfurniture.stream(),genericObjects.stream()).flatMap(co -> co); + tunnels.stream() ,water.stream(), cityfurniture.stream(),genericObjects.stream()).flatMap(co -> co); } public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException { @@ -227,6 +229,7 @@ public class CityDoctorModel { collectErrorsFromList(errors, bridges); collectErrorsFromList(errors, land); collectErrorsFromList(errors, roads); + collectErrorsFromList(errors, tunnels); collectErrorsFromList(errors, water); collectErrorsFromList(errors, cityfurniture); collectErrorsFromList(errors, genericObjects); @@ -287,6 +290,10 @@ public class CityDoctorModel { return roads; } + public List getTunnels() { + return tunnels; + } + public List getWater() { return water; } @@ -315,9 +322,13 @@ public class CityDoctorModel { roads.add(to); } + public void addTunnel(Tunnel tunnel) { + tunnels.add(tunnel); + } + public int getNumberOfFeatures() { - return buildings.size() + vegetation.size() + bridges.size() + land.size() + roads.size() + water.size() + cityfurniture.size() - + genericObjects.size(); + return buildings.size() + vegetation.size() + bridges.size() + land.size() + tunnels.size() + +roads.size() + water.size() + cityfurniture.size() + genericObjects.size(); } public ParserConfiguration getParserConfig() { @@ -331,7 +342,9 @@ public class CityDoctorModel { replaceBridge(currentFeature, nextFeature); } else if (nextFeature instanceof TransportationObject) { replaceTransportationObject(currentFeature, nextFeature); - } else if (nextFeature instanceof Vegetation) { + }else if (nextFeature instanceof Tunnel) { + replaceTunnel(currentFeature, nextFeature); + }else if (nextFeature instanceof Vegetation) { replaceVegetation(currentFeature, nextFeature); } else if (nextFeature instanceof LandObject) { replaceLandObject(currentFeature, nextFeature); @@ -392,6 +405,14 @@ public class CityDoctorModel { roads.set(index, (TransportationObject) nextFeature); } + private void replaceTunnel(CityObject currentFeature, CityObject nextFeature){ + int index = tunnels.indexOf(currentFeature); + if (index == -1) { + throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in tunnels"); + } + tunnels.set(index, (Tunnel) nextFeature); + } + private void replaceBridge(CityObject currentFeature, CityObject nextFeature) { int index = bridges.indexOf(currentFeature); if (index == -1) { @@ -415,6 +436,8 @@ public class CityDoctorModel { bridges.add(bo); } else if (co instanceof TransportationObject to) { roads.add(to); + } else if (co instanceof Tunnel tu) { + tunnels.add(tu); } else if (co instanceof Vegetation veg) { vegetation.add(veg); } else if (co instanceof LandObject lo) { diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelHollow.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelHollow.java index b9cb085..85e5300 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelHollow.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelHollow.java @@ -14,8 +14,8 @@ public class TunnelHollow extends AbstractRoom{ private final List furnitureRefs = new ArrayList<>(2); private CityObject parent; - public void setGmlObject(org.citygml4j.core.model.building.BuildingRoom cgmlRoom){ - super.cgmlRoom = cgmlRoom; + public void setGmlObject(org.citygml4j.core.model.tunnel.HollowSpace gmlHollowSpace){ + super.cgmlRoom = gmlHollowSpace; } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java index 0954492..7740be6 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelPart.java @@ -12,8 +12,6 @@ public class TunnelPart extends AbstractTunnel { private Tunnel parent; - private TunnelPart() { - } public TunnelPart(Tunnel parent) { this.parent = parent; @@ -52,4 +50,5 @@ public class TunnelPart extends AbstractTunnel { return new TunnelPart(); } + private TunnelPart(){} } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java index eec47f1..bc9eb56 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java @@ -26,9 +26,12 @@ import java.util.Map; import de.hft.stuttgart.citydoctor2.datastructure.*; import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; +import de.hft.stuttgart.citydoctor2.datastructure.AbstractTunnel; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; import de.hft.stuttgart.citydoctor2.datastructure.BuildingRoom; +import de.hft.stuttgart.citydoctor2.datastructure.TunnelFurniture; +import de.hft.stuttgart.citydoctor2.datastructure.TunnelPart; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.core.model.CityGMLVersion; @@ -58,12 +61,13 @@ import org.citygml4j.core.model.transportation.Track; import org.citygml4j.core.model.transportation.TrafficArea; import org.citygml4j.core.model.transportation.TrafficSpace; import org.citygml4j.core.model.transportation.TrafficSpaceProperty; +import org.citygml4j.core.model.tunnel.*; +import org.citygml4j.core.model.tunnel.Tunnel; import org.citygml4j.core.model.vegetation.AbstractVegetationObject; import org.citygml4j.core.model.vegetation.PlantCover; import org.citygml4j.core.model.vegetation.SolitaryVegetationObject; import org.citygml4j.core.model.waterbody.WaterBody; import org.citygml4j.core.visitor.ObjectWalker; -import org.xmlobjects.gml.adapter.geometry.complexes.CompositeSurfaceAdapter; import org.xmlobjects.gml.model.base.AbstractGML; import org.xmlobjects.gml.model.feature.AbstractFeature; import org.xmlobjects.gml.model.geometry.AbstractGeometry; @@ -357,6 +361,195 @@ public class Citygml3FeatureMapper extends ObjectWalker { return bi; } + @Override + public void visit(Tunnel tunnel){ + de.hft.stuttgart.citydoctor2.datastructure.Tunnel tu = new de.hft.stuttgart.citydoctor2.datastructure.Tunnel(); + for (TunnelPartProperty tPartProperty : tunnel.getTunnelParts()) { + if (!tPartProperty.isSetObject()) { + continue; + } + org.citygml4j.core.model.tunnel.TunnelPart gmlTunnelPart = tPartProperty.getObject(); + TunnelPart tPart = new TunnelPart(tu); + readAbstractTunnel(gmlTunnelPart, tPart); + tu.addTunnelPart(tPart); + } + readAbstractTunnel(tunnel, tu); + resolveAndClearReferences(); + updateEdgesAndVertices(tu); + for (TunnelPart part : tu.getTunnelParts()) { + updateEdgesAndVertices(part); + } + model.addTunnel(tu); + + } + + private void readAbstractTunnel(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel, AbstractTunnel cdTunnel) { + mapAbstractOccupiedSpace(gmlTunnel, cdTunnel); + cdTunnel.setGmlObject(gmlTunnel); + + // parse deprecated geometries + parseAndAddMultiSurface(gmlTunnel.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, cdTunnel); + parseAndAddMultiSurface(gmlTunnel.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, cdTunnel); + parseAndAddSolid(gmlTunnel.getDeprecatedProperties().getLod4Solid(), Lod.LOD4, cdTunnel); + + for (TunnelInstallationProperty tiProp : gmlTunnel.getTunnelInstallations()) { + var gmlTi = tiProp.getObject(); + if (gmlTi == null) { + // ignore empty properties + continue; + } + Installation ti = mapTunnelInstallation(gmlTi); + cdTunnel.addTunnelInstallation(ti); + } + + for (HollowSpaceProperty thProp : gmlTunnel.getHollowSpaces()) { + var gmlTh = thProp.getObject(); + if (gmlTh == null) { + continue; + } + TunnelHollow br = mapTunnelHollow(gmlTh); + cdTunnel.addTunnelHollow(br); + } + + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); + for (AbstractSpaceBoundaryProperty surfaceProp : gmlTunnel.getBoundaries()) { + if (!surfaceProp.isSetObject()) { + continue; + } + AbstractSpaceBoundary surface = surfaceProp.getObject(); + surface.accept(surfaceMapper); + } + + cdTunnel.unsetGmlGeometries(); + resolveAndClearReferences(); + updateEdgesAndVertices(cdTunnel); + for (BoundarySurface bs : surfaceMapper.getSurfaces()) { + updateEdgesAndVertices(bs); + for (Opening o : bs.getOpenings()) { + updateEdgesAndVertices(o); + } + } + for (Installation ti : cdTunnel.getTunnelInstallations()) { + updateEdgesAndVertices(ti); + for (BoundarySurface bs : ti.getBoundarySurfaces()) { + updateEdgesAndVertices(bs); + for (Opening o : bs.getOpenings()) { + updateEdgesAndVertices(o); + } + } + } + for (TunnelFurnitureProperty tfProp : gmlTunnel.getTunnelFurniture()){ + var gmlTf = tfProp.getObject(); + if (gmlTf == null) { + continue; + } + TunnelFurniture tf = mapTunnelFurniture(gmlTf); + cdTunnel.addTunnelFurniture(tf); + } + } + + private Installation mapTunnelInstallation(TunnelInstallation gmlTi){ + Installation ti = new Installation(); + ti.setGmlObject(gmlTi); + mapAbstractOccupiedSpace(gmlTi, ti); + GeometryProperty lod2Prop = gmlTi.getDeprecatedProperties().getLod2Geometry(); + parseAndAddAbstractGeometry(lod2Prop, Lod.LOD2, ti); + GeometryProperty lod3Prop = gmlTi.getDeprecatedProperties().getLod3Geometry(); + parseAndAddAbstractGeometry(lod3Prop, Lod.LOD3, ti); + GeometryProperty lod4Prop = gmlTi.getDeprecatedProperties().getLod4Geometry(); + parseAndAddAbstractGeometry(lod4Prop, Lod.LOD4, ti); + ti.unsetGmlGeometries(); + + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); + for (AbstractSpaceBoundaryProperty surfaceProp : gmlTi.getBoundaries()) { + if (!surfaceProp.isSetObject()) { + continue; + } + AbstractSpaceBoundary surface = surfaceProp.getObject(); + surface.accept(surfaceMapper); + } + for (BoundarySurface bs : surfaceMapper.getSurfaces()) { + ti.addBoundarySurface(bs); + for (Geometry geom : bs.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + p.setPartOfSurface(bs); + p.setPartOfInstallation(ti); + } + } + } + for (Geometry geom : ti.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + p.setPartOfInstallation(ti); + } + } + + return ti; + } + + private TunnelHollow mapTunnelHollow(HollowSpace gmlHo){ + TunnelHollow tHollow = new TunnelHollow(); + tHollow.setGmlObject(gmlHo); + mapAbstractUnoccupiedSpace(gmlHo,tHollow); + + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); + for (AbstractSpaceBoundaryProperty surfaceProp : gmlHo.getBoundaries()) { + if (!surfaceProp.isSetObject()) { + continue; + } + AbstractSpaceBoundary surface = surfaceProp.getObject(); + surface.accept(surfaceMapper); + } + for (BoundarySurface bs : surfaceMapper.getSurfaces()) { + tHollow.addBoundarySurface(bs); + for (Geometry geom : bs.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + p.setPartOfSurface(bs); + } + } + } + for (TunnelInstallationProperty tiProp : gmlHo.getTunnelInstallations()) { + var gmlTi = tiProp.getObject(); + if (gmlTi == null) { + // ignore empty properties + continue; + } + Installation bi = mapTunnelInstallation(gmlTi); + tHollow.addRoomInstallation(bi); + } + for (TunnelFurnitureProperty tfProp : gmlHo.getTunnelFurniture()){ + var gmlHref = tfProp.getHref(); + if (gmlHref == null) { + continue; + } + tHollow.addFurnitureRef(tfProp); + } + return tHollow; + } + + private TunnelFurniture mapTunnelFurniture(org.citygml4j.core.model.tunnel.TunnelFurniture gmlTf){ + TunnelFurniture tf = new TunnelFurniture(); + tf.setGmlObject(gmlTf); + mapAbstractOccupiedSpace(gmlTf, tf); + tf.unsetGmlGeometries(); + SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, vertexMap, config); + for (AbstractSpaceBoundaryProperty surfaceProp : gmlTf.getBoundaries()) { + if (!surfaceProp.isSetObject()) { + continue; + } + AbstractSpaceBoundary surface = surfaceProp.getObject(); + surface.accept(surfaceMapper); + } + for (BoundarySurface bs : surfaceMapper.getSurfaces()) { + tf.addBoundarySurface(bs); + for (Geometry geom : bs.getGeometries()) { + for (Polygon p : geom.getPolygons()) { + p.setPartOfSurface(bs); + } + } + } + return tf; + } + private void updatePartOfSurface(BridgeObject bo, SurfaceMapper surfaceMapper) { for (BoundarySurface bs : surfaceMapper.getSurfaces()) { bo.addBoundarySurface(bs); diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index e66b529..25e7a38 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -47,6 +47,7 @@ MainWindow.languageChange=For the change in language to apply, restart CityDocto MainWindow.buildingsTab=Buildings MainWindow.vegetationTab=Vegetation MainWindow.transportationTab=Transportation +MainWindow.tunnelTab=Tunnels MainWindow.bridgeTab=Bridges MainWindow.waterTab=Water MainWindow.terrainTab=Terrain diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 55d093e..5d870f9 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -45,6 +45,7 @@ MainWindow.languageChange=Um die Spracheinstellung zu \u00fcbernehmen muss CityD MainWindow.buildingsTab=Geb\u00e4ude MainWindow.vegetationTab=Vegetation MainWindow.transportationTab=Verkehrsobjekte +MainWindow.tunnelTab=Tunnel MainWindow.bridgeTab=Br\u00fccken MainWindow.waterTab=Gew\u00e4sser MainWindow.terrainTab=Gel\u00e4nde diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index 50e773b..00c42e0 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -63,6 +63,7 @@ public class CityDoctorController { private AtomicInteger buildingChunkNr = new AtomicInteger(0); private AtomicInteger vegetationChunkNr = new AtomicInteger(0); private AtomicInteger transportationChunkNr = new AtomicInteger(0); + private AtomicInteger tunnelChunkNr = new AtomicInteger(0); private AtomicInteger bridgeChunkNr = new AtomicInteger(0); private AtomicInteger waterChunkNr = new AtomicInteger(0); private AtomicInteger landChunkNr = new AtomicInteger(0); @@ -159,6 +160,7 @@ public class CityDoctorController { buildingChunkNr.set(0); vegetationChunkNr.set(0); transportationChunkNr.set(0); + tunnelChunkNr.set(0); bridgeChunkNr.set(0); waterChunkNr.set(0); landChunkNr.set(0); @@ -278,6 +280,18 @@ public class CityDoctorController { addMoreButtonIfNecessary(trans, transView, transRoot, transportationChunkNr); } + private void buildTunnel(List tunnels){ + if (tunnels.isEmpty()) { + return; + } + TreeView transView = mainWindow.getTunnelView(); + TreeItem transRoot = new TreeItem<>(new AllTunnelsNode(model.getTunnels())); + transRoot.setExpanded(true); + transView.setRoot(transRoot); + buildTreeFromList(tunnels, transView.getRoot(), tunnelChunkNr); + addMoreButtonIfNecessary(tunnels, transView, transRoot, tunnelChunkNr); + } + private void buildVegetation(List veg) { if (veg.isEmpty()) { return; @@ -853,6 +867,11 @@ public class CityDoctorController { cos = filterFeatures(searchString, model.getTransportation()); chunkCounter = transportationChunkNr; break; + case TUNNEL: + view = mainWindow.getTunnelView(); + cos = model.getTunnels(); + chunkCounter = tunnelChunkNr; + break; case WATER: view = mainWindow.getWaterView(); cos = filterFeatures(searchString, model.getWater()); @@ -930,6 +949,11 @@ public class CityDoctorController { cos = model.getTransportation(); chunkCounter = transportationChunkNr; break; + case TUNNEL: + view = mainWindow.getTunnelView(); + cos = model.getTunnels(); + chunkCounter = tunnelChunkNr; + break; case WATER: view = mainWindow.getWaterView(); cos = model.getWater(); @@ -1045,6 +1069,13 @@ public class CityDoctorController { transportationChunkNr); } + public void fillTreeViewWithErrorTunnel(){ + if (model == null) { + return; + } + fillTreeViewWithErrorCityObjects(mainWindow.getTunnelView(), model.getTunnels(), tunnelChunkNr); + } + public void fillTreeViewWithErrorWater() { if (model == null) { return; diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index cf63a8d..9d027c7 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -95,6 +95,9 @@ public class MainWindow extends Application { @FXML private TreeView transView; + @FXML + private TreeView tunnelView; + @FXML private TreeView bridgeView; @@ -176,6 +179,9 @@ public class MainWindow extends Application { @FXML private Tab transportationTab; + @FXML + private Tab tunnelTab; + @FXML private Tab bridgeTab; @@ -426,6 +432,7 @@ public class MainWindow extends Application { buildingsTab.setText(Localization.getText("MainWindow.buildingsTab")); vegetationTab.setText(Localization.getText("MainWindow.vegetationTab")); transportationTab.setText(Localization.getText("MainWindow.transportationTab")); + tunnelTab.setText(Localization.getText("MainWindow.tunnelTab")); bridgeTab.setText(Localization.getText("MainWindow.bridgeTab")); waterTab.setText(Localization.getText("MainWindow.waterTab")); terrainTab.setText(Localization.getText("MainWindow.terrainTab")); @@ -638,18 +645,21 @@ public class MainWindow extends Application { selectedTab = FeatureType.TRANSPORTATION; break; case 3: - selectedTab = FeatureType.BRIDGE; + selectedTab = FeatureType.TRANSPORTATION; break; case 4: - selectedTab = FeatureType.WATER; + selectedTab = FeatureType.BRIDGE; break; case 5: - selectedTab = FeatureType.LAND; + selectedTab = FeatureType.WATER; break; case 6: - selectedTab = FeatureType.CITY_FURNITURE; + selectedTab = FeatureType.LAND; break; case 7: + selectedTab = FeatureType.CITY_FURNITURE; + break; + case 8: selectedTab = FeatureType.GENERIC_CITY_OBJECT; break; default: @@ -721,6 +731,10 @@ public class MainWindow extends Application { setupSelectListener(transView); transView.setCellFactory(param -> new RenderableTreeCell()); + tunnelView.setShowRoot(true); + setupSelectListener(tunnelView); + tunnelView.setCellFactory(param -> new RenderableTreeCell()); + bridgeView.setShowRoot(true); setupSelectListener(bridgeView); bridgeView.setCellFactory(param -> new RenderableTreeCell()); @@ -868,6 +882,10 @@ public class MainWindow extends Application { return transView; } + public TreeView getTunnelView() { + return tunnelView; + } + public TreeView getBridgeView() { return bridgeView; } @@ -932,6 +950,7 @@ public class MainWindow extends Application { buildingsView.getSelectionModel().clearSelection(); vegetationView.getSelectionModel().clearSelection(); transView.getSelectionModel().clearSelection(); + tunnelView.getSelectionModel().clearSelection(); waterView.getSelectionModel().clearSelection(); terrainView.getSelectionModel().clearSelection(); cityFurnitureView.getSelectionModel().clearSelection(); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllFurnitureNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllFurnitureNode.java index 219d486..435f1c3 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllFurnitureNode.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllFurnitureNode.java @@ -16,7 +16,7 @@ public class AllFurnitureNode extends Renderable{ @Override public String getText() { - return "Building Furniture"; + return "Furniture"; } @Override diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml index d25775a..93989d0 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainWindow.fxml @@ -73,6 +73,11 @@ + + + + + -- GitLab From c0b61e704202f4cb00d706f00206b459fbf74142 Mon Sep 17 00:00:00 2001 From: Riegel Date: Mon, 21 Oct 2024 16:51:01 +0200 Subject: [PATCH 22/55] Add rendering of Tunnel --- .../stuttgart/citydoctor2/gui/Renderer.java | 115 ++++++++++++++++-- .../gui/tree/AllTunnelPartsNode.java | 45 +++++++ .../citydoctor2/gui/tree/AllTunnelsNode.java | 31 +++++ .../citydoctor2/gui/tree/TunnelNode.java | 39 ++++++ .../citydoctor2/gui/tree/TunnelPartNode.java | 35 ++++++ 5 files changed, 258 insertions(+), 7 deletions(-) create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTunnelPartsNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTunnelsNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TunnelNode.java create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TunnelPartNode.java diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java index 37e9f9a..e822100 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java @@ -251,7 +251,7 @@ public class Renderer { refresher.run(); } - public Set setupRoomPolygons(AbstractRoom room) { + private Set setupRoomPolygons(AbstractRoom room) { Set polygons = new HashSet<>(); addPolygons(room, polygons); for (BoundarySurface bs : room.getBoundarySurfaceList()) { @@ -276,7 +276,7 @@ public class Renderer { refresher.run(); } - public Set setupFurniturePolygons(AbstractFurniture furniture) { + private Set setupFurniturePolygons(AbstractFurniture furniture) { Set polygons = new HashSet<>(); addPolygons(furniture, polygons); for (BoundarySurface bs : furniture.getBoundarySurfaceList()) { @@ -301,7 +301,7 @@ public class Renderer { refresher.run(); } - public Set setupStoreyPolygons(Storey storey) { + private Set setupStoreyPolygons(Storey storey) { Set polygons = new HashSet<>(); addPolygons(storey, polygons); for (BoundarySurface bs : storey.getBoundarySurfaces()) { @@ -326,7 +326,7 @@ public class Renderer { refresher.run(); } - public Set setupBuildingUnitPolygons(BuildingUnit buildingUnit) { + private Set setupBuildingUnitPolygons(BuildingUnit buildingUnit) { Set polygons = new HashSet<>(); addPolygons(buildingUnit, polygons); for (BoundarySurface bs : buildingUnit.getBoundarySurfaces()) { @@ -338,6 +338,73 @@ public class Renderer { return polygons; } + public void render(Tunnel tunnel){ + refresher = () -> { + Set setupTunnelPolygons = setupTunnelPolygons(tunnel); + mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupTunnelPolygons)); + render(setupTunnelPolygons); + Platform.runLater(() -> { + errorUpdater = () -> displayErrors(tunnel); + errorUpdater.run(); + }); + }; + refresher.run(); + } + + private Set setupTunnelPolygons(Tunnel tunnel) { + Set polygons = new HashSet<>(); + addPolygons(tunnel, polygons); + for (BoundarySurface bs : tunnel.getBoundarySurfaces()) { + addPolygons(bs, polygons); + } + for (Installation ti : tunnel.getTunnelInstallations()) { + addPolygons(ti, polygons); + for (BoundarySurface bs : ti.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + } + for (TunnelHollow th : tunnel.getTunnelHollows()) { + addPolygons(th, polygons); + for (BoundarySurface bs : th.getBoundarySurfaceList()){ + addPolygons(bs, polygons); + } + } + for (TunnelFurniture tf : tunnel.getTunnelFurnitureList()){ + addPolygons(tf, polygons); + for (BoundarySurface bs : tf.getBoundarySurfaceList()){ + addPolygons(bs, polygons); + } + } + for (TunnelPart tp : tunnel.getTunnelParts()) { + polygons.addAll(setupTunnelPartPolygons(tp)); + } + return polygons; + } + + private Set setupTunnelPartPolygons(TunnelPart tp) { + Set polygons = new HashSet<>(); + addPolygons(tp, polygons); + for (BoundarySurface bs : tp.getBoundarySurfaces()) { + addPolygons(bs, polygons); + for (Opening op : bs.getOpenings()) { + addPolygons(op, polygons); + } + } + for (Installation bi : tp.getTunnelInstallations()) { + 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); @@ -655,9 +722,9 @@ public class Renderer { Thread t = new Thread(() -> { Set polygons = new HashSet<>(); for (Building b : objects) { - collectPolygons(polygons, b); + collectBuildingPolygons(polygons, b); for (BuildingPart bp : b.getBuildingParts()) { - collectPolygons(polygons, bp); + collectBuildingPolygons(polygons, bp); } } currentTriGeom = TriangulatedGeometry.of(polygons); @@ -810,7 +877,39 @@ public class Renderer { refresher.run(); } - private void collectPolygons(Set polygons, AbstractBuilding ab) { + public void renderTunnels(List objects){ + errorUpdater = null; + refresher = () -> { + Platform.runLater(() -> { + loadingDialog.show(); + clearGeometryTrees(); + }); + Thread t = new Thread(() -> { + Set polygons = new HashSet<>(); + for (Tunnel tunnel : objects) { + collectTunnelPolygons(polygons, tunnel); + for (TunnelPart tp : tunnel.getTunnelParts()) { + collectTunnelPolygons(polygons, tp); + } + } + 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(); + } + + private void collectBuildingPolygons(Set polygons, AbstractBuilding ab) { addPolygons(ab, polygons); for (Installation bi : ab.getBuildingInstallations()) { addPolygons(bi, polygons); @@ -835,6 +934,8 @@ public class Renderer { } } + public void collectTunnelPolygons(Set polygons, AbstractTunnel at) {} + private void collectCityFurniture(Set polygons, CityFurniture cf) { addPolygons(cf, polygons); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTunnelPartsNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTunnelPartsNode.java new file mode 100644 index 0000000..989bafa --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTunnelPartsNode.java @@ -0,0 +1,45 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.TunnelPart; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +import java.util.List; + +public class AllTunnelPartsNode extends Renderable{ + + private final List tunnelParts; + + public AllTunnelPartsNode(List tunnelParts) { + this.tunnelParts = tunnelParts; + } + + @Override + public String getText() { + return "Tunnel Parts"; + } + + @Override + public void visit(Renderer renderer) { + renderer.clearCurrentRender(); + } + + @Override + public void refreshTextColor() { + boolean wasChecked = false; + for (TunnelPart tp : tunnelParts) { + if (tp.isValidated()) { + wasChecked = true; + if (tp.containsAnyError()) { + setStatus(CheckStatus.ERROR); + return; + } + } + } + if (wasChecked) { + setStatus(CheckStatus.OK); + } else { + setStatus(CheckStatus.NOT_CHECKED); + } + } +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTunnelsNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTunnelsNode.java new file mode 100644 index 0000000..477b992 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/AllTunnelsNode.java @@ -0,0 +1,31 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Tunnel; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +import java.util.List; + +public class AllTunnelsNode extends Renderable{ + + private final List tunnels; + + public AllTunnelsNode(List tunnels) { + this.tunnels = tunnels; + } + + @Override + public void refreshTextColor() { + // no color changes + } + + @Override + public String getText() { + return "Tunnels"; + } + + @Override + public void visit(Renderer renderer) { + renderer.renderTunnels(tunnels); + } + +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TunnelNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TunnelNode.java new file mode 100644 index 0000000..6f44be4 --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TunnelNode.java @@ -0,0 +1,39 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.Tunnel; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class TunnelNode extends Renderable{ + + private final Tunnel tunnel; + + public TunnelNode(Tunnel tunnel) { + this.tunnel = tunnel; + } + + @Override + public String getText() { + return tunnel.getGmlId().getGmlString(); + } + + public Tunnel getTunnel() { + return tunnel; + } + + @Override + public void visit(Renderer renderer) { + renderer.render(tunnel); + } + + @Override + public void refreshTextColor() { + if (!tunnel.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (tunnel.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } +} diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TunnelPartNode.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TunnelPartNode.java new file mode 100644 index 0000000..04a9a9b --- /dev/null +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/tree/TunnelPartNode.java @@ -0,0 +1,35 @@ +package de.hft.stuttgart.citydoctor2.gui.tree; + +import de.hft.stuttgart.citydoctor2.datastructure.TunnelPart; +import de.hft.stuttgart.citydoctor2.gui.CheckStatus; +import de.hft.stuttgart.citydoctor2.gui.Renderer; + +public class TunnelPartNode extends Renderable{ + + private final TunnelPart tp; + + public TunnelPartNode(TunnelPart tp) { + this.tp = tp; + } + + @Override + public String getText() { + return tp.getGmlId().getGmlString(); + } + + @Override + public void visit(Renderer renderer) { + renderer.render(tp); + } + + @Override + public void refreshTextColor() { + if (!tp.isValidated()) { + setStatus(CheckStatus.NOT_CHECKED); + } else if (tp.containsAnyError()) { + setStatus(CheckStatus.ERROR); + } else { + setStatus(CheckStatus.OK); + } + } +} -- GitLab From 1ace848c73b419c1f0df220fe61d3288920adf9b Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 22 Oct 2024 10:02:00 +0200 Subject: [PATCH 23/55] Add disabling of empty FeatureTabs to GUI --- .../citydoctor2/gui/CityDoctorController.java | 13 +++++ .../stuttgart/citydoctor2/gui/MainWindow.java | 50 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index 00c42e0..d22f286 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -137,6 +137,7 @@ public class CityDoctorController { mainWindow.getLod4Btn().setDisable(false); mainWindow.getWorldBtn().setDisable(false); mainWindow.getSaveBtn().setDisable(false); + setupFeatureTabs(); buildTrees(); }); } finally { @@ -144,6 +145,18 @@ public class CityDoctorController { } } + private void setupFeatureTabs(){ + mainWindow.setDisableOfFeatureTab(FeatureType.BUILDING, model.getBuildings().isEmpty()); + mainWindow.setDisableOfFeatureTab(FeatureType.VEGETATION, model.getVegetation().isEmpty()); + mainWindow.setDisableOfFeatureTab(FeatureType.TRANSPORTATION, model.getTransportation().isEmpty()); + mainWindow.setDisableOfFeatureTab(FeatureType.TUNNEL, model.getTunnels().isEmpty()); + mainWindow.setDisableOfFeatureTab(FeatureType.BRIDGE, model.getBridges().isEmpty()); + mainWindow.setDisableOfFeatureTab(FeatureType.WATER, model.getWater().isEmpty()); + mainWindow.setDisableOfFeatureTab(FeatureType.LAND, model.getLand().isEmpty()); + mainWindow.setDisableOfFeatureTab(FeatureType.CITY_FURNITURE, model.getCityFurniture().isEmpty()); + mainWindow.setDisableOfFeatureTab(FeatureType.GENERIC_CITY_OBJECT, model.getGenericCityObjects().isEmpty()); + } + public void buildTrees() { resetFeatureChunks(); buildBuildings(model); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index 9d027c7..b63b21f 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -645,7 +645,7 @@ public class MainWindow extends Application { selectedTab = FeatureType.TRANSPORTATION; break; case 3: - selectedTab = FeatureType.TRANSPORTATION; + selectedTab = FeatureType.TUNNEL; break; case 4: selectedTab = FeatureType.BRIDGE; @@ -668,6 +668,54 @@ public class MainWindow extends Application { }); } + public void setDisableOfFeatureTab(FeatureType featureType, boolean value){ + switch (featureType) { + case BUILDING: + buildingsTab.setDisable(value); + break; + case VEGETATION: + vegetationTab.setDisable(value); + break; + case TRANSPORTATION: + transportationTab.setDisable(value); + break; + case TUNNEL: + tunnelTab.setDisable(value); + break; + case BRIDGE: + bridgeTab.setDisable(value); + break; + case WATER: + waterTab.setDisable(value); + break; + case LAND: + terrainTab.setDisable(value); + break; + case CITY_FURNITURE: + cityFurnitureTab.setDisable(value); + break; + case GENERIC_CITY_OBJECT: + otherObjectsTab.setDisable(value); + break; + default: + throw new IllegalArgumentException("Unknown feature type: " + featureType); + + } + } + + public void activateAllTabs(){ + buildingsTab.setDisable(false); + vegetationTab.setDisable(false); + transportationTab.setDisable(false); + tunnelTab.setDisable(false); + bridgeTab.setDisable(false); + waterTab.setDisable(false); + terrainTab.setDisable(false); + cityFurnitureTab.setDisable(false); + otherObjectsTab.setDisable(false); + + } + public void resetSearchBar() { searchField.setText(""); } -- GitLab From d79085638ee480258e6738ad4441c70081f1884a Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 22 Oct 2024 14:26:34 +0200 Subject: [PATCH 24/55] Add camera dragging functionality to MeshView --- .../stuttgart/citydoctor2/gui/MainWindow.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index b63b21f..d536347 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -876,6 +876,10 @@ public class MainWindow extends Application { dragX = me.getScreenX(); dragY = me.getScreenY(); } + if (me.getButton() == MouseButton.SECONDARY) { + dragX = me.getScreenX(); + dragY = me.getScreenY(); + } }); meshView.setOnScroll(se -> { @@ -901,6 +905,18 @@ public class MainWindow extends Application { cameraZRotation.setAngle(cameraXRot); cameraXRotation.setAngle(cameraYRot); } + if (me.getButton() == MouseButton.SECONDARY) { + double translationSpeed = Math.abs(camera.getTranslateZ())/1000; + double deltaX = me.getScreenX() - dragX; + double deltaY = me.getScreenY() - dragY; + dragX = me.getScreenX(); + dragY = me.getScreenY(); + + camera.setTranslateX(camera.getTranslateX() - deltaX * translationSpeed); + camera.setTranslateY(camera.getTranslateY() - deltaY * translationSpeed); + logger.info(camera.getTranslateX()); + logger.info(camera.getTranslateY()); + } }); } @@ -1054,6 +1070,8 @@ public class MainWindow extends Application { double d = longestSide / Math.tan(Math.toRadians(30) / 2); translateZ = -d; camera.setTranslateZ(translateZ); + camera.setTranslateX(0); + camera.setTranslateY(0); highlightController.changeScaling(-translateZ); } -- GitLab From f1d06dce2f2ba03c6e5df4d361b38a3bc7e91e87 Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 22 Oct 2024 15:56:23 +0200 Subject: [PATCH 25/55] Remove forgotten test-log from Debugging --- .../main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index d536347..36dca13 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -914,8 +914,6 @@ public class MainWindow extends Application { camera.setTranslateX(camera.getTranslateX() - deltaX * translationSpeed); camera.setTranslateY(camera.getTranslateY() - deltaY * translationSpeed); - logger.info(camera.getTranslateX()); - logger.info(camera.getTranslateY()); } }); } -- GitLab From 2ed18e2ec77ba95f85576460fd200bb2d38806cc Mon Sep 17 00:00:00 2001 From: Riegel Date: Tue, 22 Oct 2024 15:57:11 +0200 Subject: [PATCH 26/55] Fix oversight in CityGML version number parsing --- .../de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index f8d7bcf..58cdf28 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -341,16 +341,17 @@ public class CityGmlParser { CityGMLInputFactory inputFactory, ObservedInputStream ois) throws CityGMLReadException { try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file); + CityGMLVersion version = null; // model is read in chunked mode // object members are replaced by href in model // need to remove the refs and re-add unparsed objects List acos = new ArrayList<>(); while (reader.hasNext()) { AbstractFeature chunk = reader.next(); + version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); if (chunk instanceof CityModel cModel) { cModel.setCityObjectMembers(null); mapper.setCityModel(cModel); - CityGMLVersion version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); mapper.setCityGMLVersion(version); } else if (chunk instanceof AbstractCityObject aco) { acos.add(aco); @@ -380,6 +381,7 @@ public class CityGmlParser { // Remove gag gagged = false; } + mapper.setCityGMLVersion(version); return mapper.getModel(); } } -- GitLab From 76f34f5197ed9327ed818b6db0761a889b341401 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Oct 2024 13:22:25 +0200 Subject: [PATCH 27/55] Add NorthArrow in MeshView --- .../citydoctor2/gui/CityDoctorController.java | 2 ++ .../stuttgart/citydoctor2/gui/MainWindow.java | 25 +++++++++++++++++- .../stuttgart/citydoctor2/gui/icons/north.png | Bin 0 -> 2078 bytes 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north.png diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index d22f286..f513584 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -137,6 +137,8 @@ public class CityDoctorController { mainWindow.getLod4Btn().setDisable(false); mainWindow.getWorldBtn().setDisable(false); mainWindow.getSaveBtn().setDisable(false); + mainWindow.getNorthArrow().setVisible(true); + mainWindow.alignNorthArrow(); setupFeatureTabs(); buildTrees(); }); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index 36dca13..ddd8ef5 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -12,6 +12,7 @@ import java.util.TimerTask; import javax.imageio.ImageIO; +import javafx.scene.chart.NumberAxis; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -268,6 +269,7 @@ public class MainWindow extends Application { private MainToolBar mainToolBar; private SubScene geomScene; + private ImageView northArrow; private static boolean loadFileAtStartup = false; private static String inputFile; @@ -856,9 +858,15 @@ public class MainWindow extends Application { meshGroup = new Group(); world.getChildren().add(meshGroup); + Image north = new Image(getClass().getResourceAsStream("icons/north.png")); + northArrow = new ImageView(north); + northArrow.setFitHeight(50); + northArrow.setFitWidth(50); + meshView.getChildren().add(northArrow); + northArrow.setVisible(false); + AmbientLight al = new AmbientLight(Color.WHITE); root.getChildren().add(al); - buildCamera(); cameraXRotation.setAxis(Rotate.X_AXIS); cameraZRotation.setAxis(Rotate.Z_AXIS); @@ -904,6 +912,7 @@ public class MainWindow extends Application { cameraZRotation.setAngle(cameraXRot); cameraXRotation.setAngle(cameraYRot); + alignNorthArrow(); } if (me.getButton() == MouseButton.SECONDARY) { double translationSpeed = Math.abs(camera.getTranslateZ())/1000; @@ -927,6 +936,16 @@ public class MainWindow extends Application { cameraXRotation.setAngle(cameraYRot); } + public void alignNorthArrow(){ + double absRotZ = cameraZRotation.getAngle() - 360 * Math.floor(cameraZRotation.getAngle() / 360); + double absRotX = cameraXRotation.getAngle() - 360 * Math.floor(cameraXRotation.getAngle() / 360); + + double northRot = ((80 <= absRotX && absRotX <= 260) ? 360-absRotZ: (absRotZ+180)%360 ); + + northArrow.setRotate(northRot); + } + + public void addFileNameToTitle(String fileName) { String version = Localization.getText(Localization.VERSION); stage.setTitle("CityDoctor " + version + " - " + fileName); @@ -1044,6 +1063,10 @@ public class MainWindow extends Application { return mainToolBar.getWorldBtn(); } + public ImageView getNorthArrow(){ + return northArrow; + } + public Button getSaveBtn() { return mainToolBar.getSaveBtn(); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north.png b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north.png new file mode 100644 index 0000000000000000000000000000000000000000..a957a21c9efa9570d4953c7a49ae0e816b415342 GIT binary patch literal 2078 zcmV+(2;ujMP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T=bJgR%M1pC!C){L367Ecc9hUBU%q6VbJpD4EVnoZ#|Yil*2aeq zAI@^zIjpR#jD7j?#Zj(yjL=@Mm*wT(9YG`2>t5SD;l8-&6`7sBs9?3)!GOx!iHtu9Ex(T6>8u1?c3SYr%$zVwH88) z&IX5*W4Reb6B_86foLJLdU_HIh#X63wSVi@t?a>r2imwg<3c0TK+YT~(+C72M#?$@ zQLa9B?i|$y0bJMF*(o%PjmV+-^5x6nQrkrrS7+SO7}Ky5n-zhBO3_fRuCA`8x=(ZO z-aXrH20wrP4RdX*($v(1I-_LF(U{s5ubV-%JUSHR>N97~P~9g%@9oErAJVJ# zfcqCOUL59FJLUQF=cqGAMjVY^;D7-G*pMMZc*FnKOr+DZ)XJQ{yQuEiI`3EC0>W$eWXKb4N!9 zO+a5?xSrP0LyXtm%GJGk3?z}fS@G`OJ6>O3?|0$dyLb8AxpT>#i8{NrwH0;m;`Qs- z90T!*fR2XM4eY~*4}9asjeg;TTm8$?ih5mfaWU%N#pB11`SoE z`8XkPvph5PfIVZsD@R22!#fkr2! zprC+_88asAvm8Erm}9<&2mx%yj2Ud!tXW~NxpnInudS_Rn6!>g41RjCWXTeY4!WK_ zd&V(phYkwb5a(4M6$~csh!BS-@oCc|Ex^sOVly&|SOYu|s=B(GT!&&c)eBbwy7ueV zt>YCH74$In_tHWUxOmWP- zdGlyK-NlO+S!rph+!G9*B}RLmxN_x6*ZK43>48J&IEfG*^RgNCJBEi3AM%cl4(Z(< zyk0Ne^y2-$jk>xzzIN?eI!1Pz`H2%J!rFkN?fm8tS5l;Wd+gY;X?1ll(AK3(moQYyg!|;y_z^j_YsHEcuItyYvnf-iP<+R;O#FycZCXAMLIS?*^y$-iO-+qu7aXW~ee>qc zlzk5Ty-;CcVal%1q_d8!tSmNd+BCVATJYpsWCv`^mMyHav(x`y9ic2-xR8F#i5_RF zC@3g!iCjD4dm0P|gTY`h7z_r3!C=r6kH?d`0Rn?19*-ycAK01Zr8O*6rT_o{07*qo IM6N<$f^jqw6#xJL literal 0 HcmV?d00001 -- GitLab From aad641669f5d20776e1b1b017ddc2c0179c93ee2 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Oct 2024 14:36:45 +0200 Subject: [PATCH 28/55] Add button for resetting the MeshView camera --- .../CityDoctorLocalization.properties | 1 + .../CityDoctorLocalization_de.properties | 1 + .../citydoctor2/gui/CityDoctorController.java | 11 ++++++++ .../citydoctor2/gui/MainToolBar.java | 26 ++++++++++++++++++ .../stuttgart/citydoctor2/gui/MainWindow.java | 17 ++++++++++++ .../citydoctor2/gui/MainToolBar.fxml | 8 +++++- .../citydoctor2/gui/icons/reset_camera.png | Bin 0 -> 720 bytes 7 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/reset_camera.png diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 25e7a38..9f10f95 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -30,6 +30,7 @@ MainToolBar.culling=Enable/Disable Culling MainToolBar.writeReports=Write Reports MainToolBar.executeChecks=Execute Checks MainToolBar.showWorld=Show entire city model +MainToolBar.resetCamera=Reset view MainWindow.missingConfig=Could not find configuration file. MainWindow.loadGivenFile=Loading given file, please wait MainWindow.finishedLoading=Finished loading diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 5d870f9..89bae3e 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -28,6 +28,7 @@ MainToolBar.culling=Aktiviere/Deaktiviere Entfernen der R\u00fcckseiten MainToolBar.writeReports=Schreibe Reports MainToolBar.executeChecks=F\u00fchre Pr\u00fcfungen aus MainToolBar.showWorld=Gesamtes Stadtmodell anzeigen +MainToolBar.resetCamera=Ansicht zur\u00fccksetzen MainWindow.missingConfig=Konnte Konfigurationsdatei nicht finden. MainWindow.loadGivenFile=Lade vorhandene Datei, bitte warten MainWindow.finishedLoading=Fertig geladen diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index f513584..05ba494 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -59,6 +59,7 @@ public class CityDoctorController { private HighlightController highlightController; private Renderer renderer; + private BoundingBox originBB = null; private AtomicInteger buildingChunkNr = new AtomicInteger(0); private AtomicInteger vegetationChunkNr = new AtomicInteger(0); @@ -137,6 +138,7 @@ public class CityDoctorController { mainWindow.getLod4Btn().setDisable(false); mainWindow.getWorldBtn().setDisable(false); mainWindow.getSaveBtn().setDisable(false); + mainWindow.getResetCameraBtn().setDisable(false); mainWindow.getNorthArrow().setVisible(true); mainWindow.alignNorthArrow(); setupFeatureTabs(); @@ -1259,4 +1261,13 @@ public class CityDoctorController { public ParserConfiguration getCurrentConfig() { return currentConfig; } + + public void setOriginBB(BoundingBox origin){ + this.originBB = origin; + } + + public BoundingBox getOriginBB() { + return originBB; + } + } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java index 45ca1f8..19f2284 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java @@ -49,6 +49,12 @@ public class MainToolBar { @FXML private ImageView showWorldImageView; + @FXML + private Button resetCameraBtn; + + @FXML + private ImageView resetCameraView; + @FXML private Button schematronBtn; @@ -139,6 +145,7 @@ public class MainToolBar { setupLodButtons(); setupAboutButton(); setupReportButton(); + setupResetCameraButton(); loadImages(); gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected())); @@ -208,6 +215,10 @@ public class MainToolBar { Image img = new Image(inStream); saveView.setImage(img); } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/reset_camera.png")) { + Image img = new Image(inStream); + resetCameraView.setImage(img); + } } catch (IOException e) { // ignore close exception } @@ -298,6 +309,13 @@ public class MainToolBar { }); } + private void setupResetCameraButton(){ + resetCameraBtn.setDisable(true); + resetCameraBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.resetCamera"))); + resetCameraBtn.setOnAction(ae -> mainWindow.resetCamera()); + + } + public Button getCheckButton() { return checkBtn; } @@ -310,6 +328,10 @@ public class MainToolBar { return cullingButton; } + public Button getSaveButton() { + return saveBtn; + } + public Button getWriteReportButton() { return reportBtn; } @@ -338,6 +360,10 @@ public class MainToolBar { return saveBtn; } + public Button getResetCameraBtn(){ + return resetCameraBtn; + } + public HBox getToolBar() { return toolBar; } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index ddd8ef5..11d158d 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -945,6 +945,18 @@ public class MainWindow extends Application { northArrow.setRotate(northRot); } + public void resetCamera(){ + cameraXRot = CAMERA_INITIAL_X_ANGLE; + cameraYRot = CAMERA_INITIAL_Y_ANGLE; + cameraZRotation.setAngle(cameraXRot); + cameraXRotation.setAngle(cameraYRot); + alignNorthArrow(); + camera.setTranslateX(0); + camera.setTranslateY(0); + if (controller.getOriginBB() != null) { + zoomOutForBoundingBox(controller.getOriginBB()); + } + } public void addFileNameToTitle(String fileName) { String version = Localization.getText(Localization.VERSION); @@ -1063,6 +1075,10 @@ public class MainWindow extends Application { return mainToolBar.getWorldBtn(); } + public Button getResetCameraBtn(){ + return mainToolBar.getResetCameraBtn(); + } + public ImageView getNorthArrow(){ return northArrow; } @@ -1094,6 +1110,7 @@ public class MainWindow extends Application { camera.setTranslateX(0); camera.setTranslateY(0); highlightController.changeScaling(-translateZ); + controller.setOriginBB(b); } public MainToolBar getMainToolbar() { diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml index 17503f5..1309b50 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml @@ -7,7 +7,7 @@ - + @@ -63,6 +63,12 @@ + + diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/reset_camera.png b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/reset_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..62c9203604f0157fe120beae5bb29ff46636d9a4 GIT binary patch literal 720 zcmV;>0x$iEP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf6951U69E94oEQKA0%b`=K~z{r)t9@E z4N)9`uVp>6-ZVr>i%QTCQ4k73BU%&^(c0CCL@S#82NWV2A`y?qu0$b3r;#YVUqQTm z-`q31XYZbynYnlNuKkiPGiT#_28>~1P^Deh}2!M3^(JvtToO)+|b;uj4vX`T%ENdQUO@a{KnQ-=MH>`zH&Rk zdcbifkP5(KPHu;oZ)hrg(^+s-d9gT_V@{ec6~HIVQ)W43S!H?Ue@GvA4=^N)n~K^#WE1 Date: Wed, 23 Oct 2024 14:37:23 +0200 Subject: [PATCH 29/55] Add upside-down icon for NorthArrow --- .../stuttgart/citydoctor2/gui/MainWindow.java | 21 +++++++++++++++--- .../citydoctor2/gui/icons/north_flip.png | Bin 0 -> 2103 bytes 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north_flip.png diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index 11d158d..bc143b6 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -237,6 +237,9 @@ public class MainWindow extends Application { @FXML private Label availableLabel; + private Image north; + private Image northFlip; + private Group meshGroup; private Group world; @@ -858,7 +861,8 @@ public class MainWindow extends Application { meshGroup = new Group(); world.getChildren().add(meshGroup); - Image north = new Image(getClass().getResourceAsStream("icons/north.png")); + north = new Image(getClass().getResourceAsStream("icons/north.png")); + northFlip = new Image(getClass().getResourceAsStream("icons/north_flip.png")); northArrow = new ImageView(north); northArrow.setFitHeight(50); northArrow.setFitWidth(50); @@ -940,8 +944,19 @@ public class MainWindow extends Application { double absRotZ = cameraZRotation.getAngle() - 360 * Math.floor(cameraZRotation.getAngle() / 360); double absRotX = cameraXRotation.getAngle() - 360 * Math.floor(cameraXRotation.getAngle() / 360); - double northRot = ((80 <= absRotX && absRotX <= 260) ? 360-absRotZ: (absRotZ+180)%360 ); - + double northRot; + if (80 <= absRotX && absRotX <= 260){ + northRot = 360-absRotZ; + if (northArrow.getImage() != north){ + northArrow.setImage(north); + } + } else { + //Camera is below horizon line -> flip north arrow horizontally + northRot = (absRotZ+180)%360; + if (northArrow.getImage() != northFlip){ + northArrow.setImage(northFlip); + } + } northArrow.setRotate(northRot); } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north_flip.png b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north_flip.png new file mode 100644 index 0000000000000000000000000000000000000000..dd35f01e10c864b5838164b0f04a40ac1ca03c4f GIT binary patch literal 2103 zcmV-72*~$|P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!2kdb!2!6DYwZ942h2%CK~#8N?VNi^ zR9PIye^)KFG{a3P%i039>}^B@K}3aZ?4see1l1p@^-tPAZN?gr78vN(E@~~9Wzbee z(F3f|w1|qz-fC}r^p0GcvWHgc+50=5$r8QXhK9k1JsO7Q=4Nv2*fDtf_N}4BHk{B8A3g*^2t0lI zlpQe!h7!7^rG*q17ejDRFocAJKx1R0VO(t}p+#X~@7`+Y=0@wkuwh(n7@;p+xgMa$uWZ!Kph1IZMZ!WZ z;a=#b#wI8zsA5q!=we%Na%^a5U=g2iEi}6MU}<-yVM|9x2OIT(u72|53Ae81T4>C@ zxw&O5>cKWgTfqrZyf7@{53Yr_tu$=0tvYNOIB*~|H8pYRYHo$DuC69IImcOKk29e$ zSMyp|b1U?bBS+ZC5vd$)B``3M%L;@`q0!A!x!DT#Ih=v67Sjid_<>8I4xYW9la6L2OH@RS3>tyY1krH=zxF#T=a0^YHozSdGjXOxY2RBErnd6 z@%wnW3&V}jCr+GTBiFd9I&AUx_vfMl;X-J1b9T1tNzh8~?pOdtV+WcIpv)#n5(Z}zpiWg&|T=GM~||Re3S}}psV%VN~WvO zj~_oKnVJ8xNIpta2O* zP3W>R=WS3eL09X6E<&T5-O0^rfcYKkb{2XKU4-`3&Az_AY~&sT1`J>$@8NZ!>+9=1 zcJsS;@7TzFyj@p&U1%je4QHgMrNigXpCKY5f*nx~y4ss{wZI;|LPJ9X88hZ*7Aa@p z!XF_!I~%4?pH9#F&Ye3XJUra(4juMPCQh8_6%D-VXisu;M#j%jQd06=WMuN>$wG5; zv)eh_&(9B@KY#8OS9{ITc=u%7xFi-SV|VddNJva*9WrD{2s>m4iq-O83-|~4vm^4s z<^1^Z<2Bd8YmPpBx_cA3QqG<&gp|}2!I{X&h!G=%&>uqGT8#Ml`mvF=c*W88?%gA? zvA?oN9*Y*mz}Br>VeHtkiq7%s)vMw>Fa|f#up_R)i8U@RPE#$s;^eF+;%3k$AsQE=s=FT{L$zZlxVH>L(VtX5YR)VEOXp9-r6UyLSn$0_85b zKYjWH)22<+Ob^W+eX^|Y%`mo9RQ#=q$OukwxVHlZlmoi@<;$0vRUn!=8e5W)k?|~Y zPC`NympkuU`}V`^1=-7>Bp( z-=9U&;HM%sHdb|AG;?%$c{v+NQ(pd;OOX*w1qlfWbWh%yGiPAu&YkQTdvM|5>R#fc zNs|OjG3{;b?1&?nrnEp4q0!Ch>Ho0E9;}aRYDytKK3>*4!WtKI=i@EpS^~8%+zg7isR?xx$VeJ7D|v?bte{VqnWtPL7rW^Y)Y}Qv};j z1iqe{`#@9`8mj^;IXTOqprAl;K9Cx{h{D5&J?)z#Gzv8E>C!iFR!CX#dK&Z+V{aX%owpN}6u z66~Y1!+aptw^U?oQ?f&q@|@{ zPeIj_!jym)P%OnrSy>r<Y`LuR;qFVRmst~ddteng3+T#tD28L zeE5KqAZ*yMfli9O|4)aNq@*Os&CQjpi9KcN)Twl0G%+zTF)=YQF)=YQF)=YQ1{RA& h{RV-F9Ttlv@PB;%bmuZ3)` Date: Wed, 23 Oct 2024 16:04:58 +0200 Subject: [PATCH 30/55] Add BUILDING_SUBDIVISION FeatureType --- .../datastructure/AbstractBuildingSubdivision.java | 2 +- .../hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java | 1 - .../hft/stuttgart/citydoctor2/datastructure/FeatureType.java | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java index a715972..5b5240b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractBuildingSubdivision.java @@ -49,7 +49,7 @@ public abstract class AbstractBuildingSubdivision extends CityObject{ @Override public FeatureType getFeatureType() { - return FeatureType.BUILDING; + return FeatureType.BUILDING_SUBDIVISION; } @Override diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java index 8e4ea6a..48cc116 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/AbstractRoom.java @@ -7,7 +7,6 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; import de.hft.stuttgart.citydoctor2.utils.CopyHandler; import de.hft.stuttgart.citydoctor2.utils.Copyable; -import org.citygml4j.core.model.construction.AbstractFurnitureProperty; import org.citygml4j.core.model.core.AbstractCityObject; import org.citygml4j.core.util.geometry.GeometryFactory; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java index aaf543e..4a6bdde 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/FeatureType.java @@ -27,6 +27,6 @@ package de.hft.stuttgart.citydoctor2.datastructure; public enum FeatureType { BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, INSTALLATION, OPENING, - BUILDING_PART, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE, GENERIC_CITY_OBJECT, - TUNNEL, TUNNEL_PART + BUILDING_PART, BUILDING_SUBDIVISION, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE, + GENERIC_CITY_OBJECT, TUNNEL, TUNNEL_PART } -- GitLab From a6ad81b443e7e9dc85ae3051a73d5426ce9404b4 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Oct 2024 16:06:18 +0200 Subject: [PATCH 31/55] Add export according to CityDoctorModel`s CityGML version --- .../hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java index 52d9da5..05f543f 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/writer/CityGMLWriterUtils.java @@ -68,7 +68,7 @@ public class CityGMLWriterUtils { public static void writeCityModel(String file, CityDoctorModel model) throws CityDoctorWriteException { CityGMLContext gmlContext = CityGmlParser.getContext(); CityModel cModel = model.getCityModel(); - CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(CityGMLVersion.v2_0); + CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(model.getCityGMLVersion()); try (CityGMLWriter writer = factory.createCityGMLWriter(new File(file))) { writer.withIndent(" "); writer.withDefaultPrefixes(); @@ -92,6 +92,9 @@ public class CityGMLWriterUtils { storeCityObjects(model.getLand(), gmlFactory, model, cModel, val); storeCityObjects(model.getTransportation(), gmlFactory, model, cModel, val); storeCityObjects(model.getWater(), gmlFactory, model, cModel, val); + storeCityObjects(model.getCityFurniture(), gmlFactory, model, cModel, val); + storeCityObjects(model.getGenericCityObjects(), gmlFactory, model, cModel, val); + storeCityObjects(model.getTunnels(), gmlFactory, model, cModel, val); writer.write(cModel); cModel.getCityObjectMembers().clear(); -- GitLab From bcff13337f465faa28b040c0061d1f092750c766 Mon Sep 17 00:00:00 2001 From: Riegel Date: Wed, 23 Oct 2024 16:42:51 +0200 Subject: [PATCH 32/55] Replace NorthArrow and ResetCamera icons --- .../stuttgart/citydoctor2/gui/MainWindow.java | 6 ++++-- .../stuttgart/citydoctor2/gui/icons/north.png | Bin 2078 -> 12334 bytes .../citydoctor2/gui/icons/north_flip.png | Bin 2103 -> 10532 bytes .../citydoctor2/gui/icons/reset_camera.png | Bin 720 -> 857 bytes 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index bc143b6..d91e912 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -861,11 +861,13 @@ public class MainWindow extends Application { meshGroup = new Group(); world.getChildren().add(meshGroup); - north = new Image(getClass().getResourceAsStream("icons/north.png")); - northFlip = new Image(getClass().getResourceAsStream("icons/north_flip.png")); + north = new Image(getClass().getResourceAsStream("icons/north.png"), 50, 50, true, true); + northFlip = new Image(getClass().getResourceAsStream("icons/north_flip.png"), 50, 50, true, true); northArrow = new ImageView(north); northArrow.setFitHeight(50); northArrow.setFitWidth(50); + northArrow.setX(10); + northArrow.setY(10); meshView.getChildren().add(northArrow); northArrow.setVisible(false); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north.png b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north.png index a957a21c9efa9570d4953c7a49ae0e816b415342..60930d58445fb8712b6828ae09b64565dd634571 100644 GIT binary patch literal 12334 zcmbt)d0dQd_y0BR(uR^Eg%AyqR7y2jA}N(<)1tJ{rbVloNhpy%#GsTGEtd9OyHrS8 zwbw*aX|vP}O*8lR-0^vS&-3@s>*dAV%el^ZpZD3W>%L}aa7d6}k{=-?sC)Q;F+v>h zUk=2_1OLqWcg({-1aIR*`%u>VHRJGu%Sl^b8=>6jr3qfPBNjDVXL-tMH!Q;3 zcHAw%@Q!x zvYCGStUWGlQi2F#pLu6Js_vIV z6uzVB?SAq(qR7@AgvsU+)ze7gsQMJ#IxG-gJ0Uv8f$lE_vA2kg><73~qX!7+z!}vw z)-Uq%erTtP5|HmfxZ=2XEiI$k7*VW@5L)(?!SpaqT#9TDfz->&%F2ToxY(Px*wnLc zYcHY62o4lsk}#Tm`1a>oT={YVSS2@EZZHj(au3v9-!;SVFajw<8{xgzZmzDUK8*Xg z=PM9wS8$^i4taU`swcSEW3c2lL(#e#C4H?l+_FvlC~@PDz2tn3&0HMapm4!qz+pe& zfZ&ck=#!;-eyDl?)}>HW5VCMoi?6f{O+|?#u+qx6d6W0S)Wkachon$=*A_tA0} z0w`R;Z>7R5D%`sF{Q2{XhFcDzmVIF9^{9s7)naD+$O5c$E)A)|47gt)Qlla0hhxw4 z@2}9Yxj5g9$MzT_$z_9EDP4{MVA6u&MC*SGua8+v4B4cAP4%Lan(7;~Xz$)Jj_ zorA+jJJr-(@j?G{aH+^TknB2rib9Te=)v-{!Bo~t9FH>q<+u1XLgCW+F?jsI zRbWT$lBSL-*MO1Pe6l{S+A;O;qZ?GV z0-yg}eKYP8=mg=^wun<1&gNx+)2C0KlautZ_=zb;u4N~#6}4hQ&~wJiF9gunTed0N}_ zf?B-#U4VlCaC|yBQG3ZTzefR&H7=a{ee~K(A+|QA+x6R17FPt4eD^M1!fvLTZg2iM zAqKjJLKO7YXb{KrsHO&5OjGKf!*(C-gOH-&|OYgJJaBhOLGK5maxnMI9iX$$v3Dk@x^0ur8 zf|iwrbF<=zjS0Eg1f0gmFK&pIzlNj-HlAO6MX1P4 zi#YZ;SWw0ee!TxYE%1EKjUCDzp3pt!x z_H7wF3D8y>^w(ahJhzAP8kXz6roQf5K9w^9(hFtaTaJv3F$ng}XXPXwr|+g5hDTJz zLA_{8AsL7*^rfcu(&CHvc|oQ1xN_E;)#8PUm+-9DUi{&d-!lMF8^H+zD~3*MlVm}k z$q(0Lb=mhu5CytkeMhfkE#XcS!|m8`=_hs&1uGKoRvO6Em7D?WwV)5bV=pkGd!0* z&HVGLp!n zN&CauiCq)%CpG)do-wD3NBW&B!JwRTookt4V8*$#oaFQ*to{N)jte2acVC`s>9;$H z>1ll)UDL2z4$40tg<0(@-R~eh#*}~}g1D*+ryDh2i%U#9aUjZ7Nf3I&t|$Cgk#$!~)$fc& z9jT8yU9jezB^o9-snFpJFruIhY;BCMFV z5em5(2jWPT_;p(E-X{I_{ZxQRxIYFd?`?|aRQ%7o635A==omZ4;v4!Llc6L*6 zciYh8vw^RHtvS45*4d15yR&f>P=t zdTWgkOaGXBo&ZwflR}B+omXo=)=>}bC%13p z#}^QRY^r_I)HK>mGX6fM44Gi3nYnp@c5Xq`+WEt`&sg1ZK#A+d5NgU*UK`D%9!i(v z_ggQ6JMv+T6%!MzYwPhQ?k5j-iH!qwZ0*YF7cN&73oU=7@I}+r~X$7VnM>ht7~}-vGBZwohU?shO?yRT7A@OR!Ce zb<6H-+1}2is^0+z!QhR_oQT+E0Ua&TLy*-Zz7=RnTn4@A-s#U(_SE8=tSKJ|?#%YI zzet)bS3}i5axempWM|c8u|~(bOo>3jwWenzkQs-gOtsf@==91ve{{CF3~4Uxkq|NC z;77JgZp(Tes}K8T0j1_;JPW&Fch<7pZGU!MdnAz)CA1RIf_E{yqWec@yNl;Fjz&ar z5=9gn-1(atPcH(?k4Se)=v8O_a&u^z6heBYqF28fiaJaTbQ?^C z{^lDr=ieFtEwx6^l@-d_ct`8MkAVE!aBk*uT5muk*Zx9`c7mv}eFA6xxg zsW_xzn<6E(XV0D;B66d|R;uR|#<`Jp-7cdfKpUs59Wblf!QA&qO_UsT$CUsD3Gb`V zmAYUadHvUuuD2y%n)+zK8u0WXqE zM>6h5#+%|4H383NL^x)IZ<|}M-ySY%(aNTMVeaO!gNe(;en-3k&8bv8AVKSSOC%jr zuBe|mVGHg-+{iGhu+`ZobfmC-l4eEkdfHQ!()DZ6PyB$&bqJxV4_-FxGM#6RSl{1i z$_zSy995u}g2nW~gwgbEZt)+_`^Uo=VXG~Qsy~%aABrVtCAGYuW9F*9gr#%nwrA&D{X>glwk$7;ERE*E*EU~&tSu(4^x;My*FO~O~MeBLxva>x3pGo z^6wJ6&srTc1%T17aiH!;+OcVewlR{sCI-^0CozFP9?pSzuA2RPBp{?>#QJ%5wwA+| z8-tt)B7C?o5el)ItI>b&v^=0Z2`y5NANVqk$Virp9Of^;^u{Vma5pas8Uzkx z=k{NJb4D>w)TuS-yBf>`)oX$9K^;@RLDaw`iD`Ry$*&cUH^MtyuQ+YUt`BAhov$(} z98;Lp8T0x#lt1qbXR)f@c`03ve7xx6Ndm0S8+&$bQO9*9eQD!FmKHBfzvPnqT zxX;(Q&MMr+BqmuX(4)Ui08o4gDE`ij(n(oL%#L@&nz!s=K8hKPTZ&`<1^D+4=TX>5 zW&g99NwV-?|K0~Pci^d`kQ#Jr^!Hi4o$3GjR|w#`SUxQu`$jVf)*-8A5xi{=5#Bcy zVjhM4OPkm;rirX7;lS)uyc7qhBe1-;_Y@|-jbW!8Cq}i4p0%zx8T#)X7?l%Kt(|$8 z5nws@uaIVn2w+vV+NW$loygd?tD9HM8B-w5JSel_1Y@>?w1HAf%=%2gl#;iVsV%)q zrou^l(KArIyL%u5rBH)fC+4P)+tO`9KYaMGfyyBVZP29aHkd#_iQr(?7QI}V3ctPU zM7p_Q=oT@j#siWzx4!%Ln?g)<1IUK&0hIg^Yte(Ar$XuEE3<1Cr+r+|9lWNvm5Rr_ zIdf*q)?~)F3;3vm&#>3ri~I6BmNuD49|v(^i*Y<80B|)5LZ)w~GIVx*;1zYox@7c^ zGpwO(KD@$#{PzIL4%7|j0~HkT)m33<;yjhuC}jEdierW_a4-AT3`3L;l=@~I_U6nd z_YD*J38TXDaHG{#xy%ZO5?@3wQ|zGUViaS0xE-IObb6ttm|8Oq>Jv{(NuQ_;X=>qf5HP*b9z)Y@>uw zIPPFdX9^8?-HRdWO%CMt1!AhlOCd5@HH*ZoBFqlLCXXVrxc>K6ub0B6m^ZJDi4O*u zU0d*VlM%_~LfJq9@U;H-eNdQZ+(+$NDrv$HC1yg3F7NVEP#V5S0-}UHKKr$ZKqa&s z0fn9wLd-lEao*y_c@H<>BnXm+y3q~!to`C)c%1Y1;7UqbA|kC8+!S-IOzOGHpXjX$ zg}Hi)9}u}9hgx)84jP@Z1;R7Q2BAIV{ZCZYALAOs#+n)IUc)O0&9d@x4M3FUIDFZ^uMmfkaOR8 zDFil1f(`hss1fUTj`=T*l^zyFbwR12bb`h%VCj20U1!(ZLqKU>S>4-VRE}0~k!4Be zw43D<=~yGp3|K>Ez`big`jP`G;NV8Rb`XTu@jP^Ug8vq#(@e&+L*P`gas*^aw zEvF9}i8esO;62*+P9NZt0bWGJm(j6vK+mI_Hs~#rbKBJ=2PR|~A(?Xn?!`_|#AEkp zrh%dymBmukD*Wh`>p!yBx<=V9Uw+})n9sS-lMw|vwu1NG;Vtg6#!!vcBJWZt`w=U- zQSx1fp3{02hude@0mYVmuMi|?g7A}&jstdEzLt{o9kE?89qFp^ni16N-a=np&vwj}Q(P`Y8} zzl*WpLro-bK@9GMCX;4YDt^#t4b&0ZzTQrEW6-h& zd@KOyn3_-5ol%r9kEFI1`9{Htd~3PUrLMoFbqFu5mhTwKyd9w&C~38HeY#FFLm=?jLk%FnX@@HpzM2eOxpN<5c4s(kMpSxf%1=F zTKl-vVi~N3%bp2E8t#HO@aNCq4Qf-hSM2#=BBjC$Rz@e5!UkbU#_GE%D8?x{*4!1)N?0EA( z1vT0`&n;#MYm3$4e=;Pz=JWiaZ)s(vVfRG*Y^>&E5>uK< zb$C!5cY?q?NT8&x7Crbeq3FxsI@(aFpMD&CS`Qp+$2+B;+y3p88uk@5StmmHNJeoW zeW)DdvOk@Y^wRdCpfGTq5`=W@yZ;K$>U{-M*3Y2wM8Qg%e!d9)>r|6#_C(3+Q7iOE zda)`Xcr1&S4SL;eRh~4kbpV|YfMQ-#*rWVEeK5btzP@jF0hGjBPy-I1ja{1uy+y}b zqeNEp+l_cvoM;3$4rDn5GEaUDcRVM&wS|3X1lr=VsNH}Ia6zdRrr5#h$7vG1P)W%h zc7PIIIV7QS=+cbx-$y zJe%U%TeRreC}74WPiQI}{x(R4@@{>L3AY}6ZVz1@UPX22P({i9!$GTgeSv>ztME9z z2XUH*$7$_qx8cR%BnqlRni+H@_bh}C==S|`z`z>9b)_;$tf|Mji0vt<@+Y1mdYYOH`W znTHGEI(flRF3M9th5wFPQc8Cv@N^JTS>T@@SQt4aB8mqLQ2#G#Q*sNA!D)vgDo@65 z^yFJwX(GbJzj>oc*5x z7C2Y~)qDnzbZbLYeXlTC?&P14h5U8Mups&r9Uj4lha>VjwED2V19C(eiHG2p5$w6U;jWWLGO)nO2t7 zv%2Y_W+oW2vqV440d>IH&;<)PnLy{?XCLXkpE1soAXQT<0>()FVjtU{pME^!AKHpK)V6S9wv7nxxusNX1Y-_^9$V(Ju zgq6+BoS+8nd~j5U%3;)6=~IZS4rgmy|E;HOxHFe+qcKH$p`MTdy)vc09Xr)MOPaCetG+>f+3GIQ@!e%^1|j1+4KqXWIzp zBEbf^lNam?T%BzeD%ZnWyVOv(upb>urPe;uPH!)jhhv;*os{BUMvGUkcr@N+Ye33L z$jr)m!Ff|9_Txp}q^2pX$+yZnd^o>n^8MasVUOU|-N|lYZXBd39$%HpHs3S)Dez29 zC?~IGk{SKQj#Y6fT@x%b*mj#-A_YfM7Dl4H+p?`<)0*WAJn2=zw`q@#rXTMu>;B-Z zq-GO3#YJ+1%4Bn#7=NW>rwO`vIM?mey^&p!so6`yjFbyJ*mvPb<6V>xqQ8WM&-1sX zcFU>g#8TlrI(KfHt!A$Z4L{oV2Ic$J;timOh;7~bq=ibypc$f>mb*Y1&4UMOwkwi}0?Izdn-*driPM3=3 z_dL&0(&>W-z0~5_eX#97n^(>u_a3rIfwc7n8uik}oqmPcnqL|Fo=;(+jtkNz<$Ao6 zE=85xCbf)8ei?W7;Q2juZAJF_8|q!K;$uIU33ffScecd61=*UzPq(peQElghu z&XFF;)%zlE#tc?&HxAF3?DhBMd4FvqdU!qPTl3DwiWc6HVE){_4B9{y=FZZOzM+#s zcBOHiyZeWoXyvC1bdL>Jdm8DwA=6)o4&)7CMhr-$R;>h5${Co#*3H8Hq;80`q0i>u zrE&Ripe70tsRA3};)OTdizhTy_ZMnRkoV=z&e-p`DCibW##Qt^z>CVDS-{FitdDMr zxs{sa37D9)HqF}<0=xkQQr4-}8iX9!#-5bZUG8y=<*j5NaB5uvy-NMfOqP;9Hsh=m zcs*Fy`7*xqB8t9zdCw*v$t|cbYv&t(oQg9oMM0j*+9pun7GBgm14k7UV;__ggE#+A zM*O$3wb26(KpDLQJ?E1TA5QoUebbTFu`zZIWRjV( zRn7RK0_^NB+-{uei};703F)nR57az!Q=*%Er&8=7+b{c#lUQR2 z!}e;VxStIN!{SlT%E9ffB~IFx|0Oo^_LLS$<&9Chs^OIQgg2upWw@X3r-Ks>9@xp(qa{Ab3Jx;)zM`$?bbtm-^{ z%gEM!-2cqTYp*Y_M;*4 zM0wV9KQANy2qvXtSi6ltAZ>x&0^{7tA*05eODRi|e)gWjnmI%4TT} zO5`yXQ@t)tv=2$9ML{|cE?#9ZP~>VfO3;i8dv zJFSv4E=nBipzjh%IAJ}{+!?Df$+B>46-*2*FT$p3yBML)-t=?v;cYbeJFsD>ncjVw zvowf5nQ3)|d`xEp9AKi5w~&)h*X4yB+-47_sKb$(f^Z(FnQAuAysT(aeo?R06bJ6g zUFN_MvWJeh&I<-}ABwo&)l(&ClmAbX@MbYpI7wk@T#|q^OsLx=UBXLOhokNrIylP8 z0o%~zjUZ|1Gg%s}fVwu?hC4|Q=1fK7lqYC!9BUk^yvJ#^&aYrZ&IwoyC!ytq0!_=c z63x<^f_f)vu>v5;y2~Hee)_}~8d-Po+aouo#(Cl7EiC4$#?`%}15H```m>P9>xoI4 zj#YlNv*V-A(uj{p>Y(_hHf?d_oG+^zVwp5KWZ0PFzF$h()#$mFlQ~_Z&ajvDvzPd8 zwm8@ZLe}w@^jbAfB>4``s0!@H3CU?jbIJ-PiopT<^^-sq|dtdO#3o z&h?f)%;;2D)~D2)Ooqw_-x|0XyYuPx*M!T}S#=8Pa=@wK^9;$1Py5}O+kLc5tmi$h zD9-xzXLr||-wzTVpjpqq*s&lm6(fCut3@$OYJz#P&#NuA-fC+}~#>Lk{4HS}*Q?}p&&rUPjsZgg=}_cEvVdj(OyOe>$9#{?ameLrtM z_BpJo^pxMVQGTK(><&gi+pC#nR+QhRsMLYYdg18(xp24q)rpMTqx_SeFz5=)aqvA+lbzsQ6 zBAq;(JUD!G=KF{xt=?c((kgMtU0~KJj)p|mmaVKRci&OmyEyh~f z)S!1=+f-=oZWH-nlVzmD{%eNQ?iW+lRl}H!;#JLJaEvQ-`=-BBa;CXO4^T^{d^r|s_f#Ke`PVIj1iR?Es$Dpw(tCv|;t!ax052V3dWA#<#*wpAYpv5N{ZeL>wP` zu^QTy?8ut-rkrC&dkga-lkDFVi}i$4NW|gJswl~x&f18Q80nQCi=-H*2=0#y;oBHz zwX%?6*Uj#AIYHGwYoebPuuD14#)ceTMyf6CbddM(YNG{4O5}%hoX%fu+h;aT7ysF^ zvkt!F2;o*bCEthf3a$Bdc|EUOM^n7)SW#iy)Q%rDilj3o0z0-(*JxGkTpMlUU~}jg zdB$dJMnf~MNi$8Mv_SZR%ak{Fn^L^NrkHHG3j^>OPkU@nm5`3sXEw_$FSL)RaGPd& zxsQ6(`4(;@`BaRXl-jD2`zG#6Tx z+~a&UFM*c$1*?9?r0ik3R^sX81eP}z!j&x8ES9)bbB`Do`50ol%D$LaSnq0(s)LKOT-dS9$_~2;& zHOll&3c2XFAi_^ttFp{2QdNS_V<82nTAIajo~`L7bL#NQxXyG5d>WwgZWv~>DBfSZ zxtC9^Fm(-97nlzwpTv)9Wwqm3*_f{^b~v!o4znCBJ!J;Y0Q zvSk{Mvp*zi6GY_Pxh}sw`{l(_+7L%Z5&|CQh08NM| zBwp!XQBMtQ`nH&lAIxz!=FX1tcUNer@}Nqoz5d@AqDTDRso`Ipxg6}A4;|^-t5QvW z)5a=%pQQVGMvlXbV`h}#qZZE;1N~ziY?+SZA(q3oENQdb+Ej;W&3*@U$JG6I+2Ud# zys5B9ux01RW-&d+@Ye8~OCJ7ON;J>3LYBkBVbXlz6Fem`T;#-ZpDJsgv05xVa?J|L zMsM|xo>6U57jD(Cn4yWKF%j?xuMD9IJkgpCWAAlUH>359MaLS|*6NWE*n_WZv z44{VTVoen=V*1T(RGB4w`th9Z=J#YMzqf0})CBM#NK-9s?^Enc)mBysPeb@&AL9D$ zS^I3%ea2dbu83Rv-}CKfcNW{cHCXOpd delta 2062 zcmV+p2=Vu>V4e^miBL{Q4GJ0x0000DNk~Le000150000`2nGNE0GTslWRW2#f5HF& z4#EKyC`y0;00*;4L_t(|UhSM)Y)nBI$7iWa*&;+Dw55VaiHK+MAQ3N!5D5?B8c97O zArcWOaeFB311$}aRtS~!1w{mLr`4h*y|~|PR!Lp9#{9p|Z1-|@_w3EiY3G+r=3Ms7 z?*7mJeCL}vv&#$ygTY`h7z_r3f5BjI7H{6X;d0SY^pmwC`1+}td;I0wfF-PYE|hYugla@;wrtgMWE`SQh4u6B&jUayzs<>fJ7mycCc zR6uyS#~C<6Xt+5iCx`X#-=B4LbwQ@92u0D0@ z6xF(+mC!9MEi5nJIyM6ctp=4sQLf&zXAf&=Xwc5p+6evX)himI3(cEDi6k`8+11(z zEy9Ln-W-Z@t`%zE_U+r*)2C0hanbNq51OV%i&Vn zMHg3R+|d}*uoIgVfrCoXP_C}7uBN(AbMM|g+inIwfBxi4mo5!+ZLHGN)Py>tWX#c+ z+7+*xL9{$N6y@qOXU85yjj@OQu}?-yMy(oJed2h_{%3mk26W_X$@cZ%)+B0LRXm zGl!fh!aW$eWXKb4N!9O+a5?xSrP0LyXtm%GJGk3?z}fS@G`O zJ6>O3?|0$dyLb8AxpT>#i8{NrwH0;m;`Qs-90T!*fR2XM4eY~*4}9asjeg;TTm8$? zih5mfaWU%N#pB11`SzjUy{T zESHBSf5q|R$HN4TRNQVi#}r|)>`dOieM`c{0)zQDA#k%iG$mZPaDk5;Ig-Q_`LlUxAt!0?Bj!q1Ida-255{wSIf1W*i#xZJ#4hq^3=T#mR3?}Y~5Qiu6 zY11Puz|FB@Gct-;13VC_y1JTNhhjC=3s(ZV_UqQI;}sPZ^f310$B+Kk;e9r5-t2E9 zG;S+bA3AghwX=Z8(PA?)1iPbX5HdG6m&BModp2by-Ft#5Dk|bvu3U-OW_fw^e0$WW ze^DGKRxGE5w2XD&GPMdBxU5Adgf8=`Q>WUxOmWP-dGlyK-NlO+S!rph+!G9*B}RLm zxN_x6*ZK43>48J&IEfG*^RgNCJBEi3AM%cl4(Z(xzzIN?eI!1Pz z`H2%J!rFkN?fm8tS5l;Wd+gY;X?1lzP$-!v&~O-p#?&#a%eJ`4LV`x%cI?=} zMQE}8K4|aAkt5U=ib??|PMjEZd?is*7Ng->%%M}U8FS=>i)nrGIXS$FofQh*ad@@>Lpwyji&!F*;Rq-VkzDuU;M2 zR!K<-Iq>MwqqJ|=u3cP+me6}5ZZBK5jM_t)G-(ozfzwW%zzt5t=J)U4)5x3Ll{Arr z1~PW@YuB#PNL->q1N`z9H&n4mf92S*V+psFHZZOWH{)4cs^__d#^^wB6^$M}n$Mg$ zlh2<&KWZLr2fT+!BT48HBSvK8*eZ_x42oP%8aYhM zMb4g*_EG}ATy1S_T0Rg$0>13@>C<^lO^sz29H@AG^XAQzeGdG+P+?(VWXi74q_d8! ztSmNd+BCVATJYpsWCv`^mMyHav(x`y9ic2-xR8F#i5_RFC@3g!iCjD4dm0P|gTY`h s7z_r3!C=r6kH?d`0Rn?19*-ycAK01Zr8O*6rT_o{07*qoM6N<$g1*NSPXGV_ diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north_flip.png b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/north_flip.png index dd35f01e10c864b5838164b0f04a40ac1ca03c4f..cdab6d118236a16272db8a0f4142552a71c03d54 100644 GIT binary patch literal 10532 zcma)ic{r3|7xz6wq9RK|6e>%ym243s+EkWSTTB$PWUEk^QA$JMCP`Vdj+yz+Gy2~5{r>x0S6B1g+d22Sf9H43xt}M>%3_z`l2uC(LV|mCn;k$1 z4?ppUpAY`~8}wxk{=)?v*tH!!X^AtxonS_$)O;}Ki^2%Cr;VP`i}g`d6MJZaqh*pI`7pfue!Eezx&*8UB9e^brL^c z%tc%6r*C6ybv;F0E=s@o+3PI4-cR99>=XB}?Bsak*GmK>w}049pTEcBnejB4-{Qob zTYl^IDOR5KIeONz%WZD3Jw5s5x9MHY`U9bj13XoaN?oTL@?M<%71B6SF|duo<5j)5 z&N$!eJ7uQK^jU1{w!f{*4tLb6S%tTIO_kRDc>7I+J}7{!Zd6rO)qNiPt5jzykMzWF zNTtXw@8u(}m#ks_o-@2CyUDq5=Eqa(MaW`5A}>)(s>^R{Zx6N z4h)>Gsl^2A;Ls}Hfrd1njna9jr;Qi6zX>toyujpM-+{;0^{U;VHt)i@Z?BQMBOwrIcHzHb0Z2;hVEh;RkkxWa~E`()G@{z zZ^f!0Uu>p!N`0p)zurxRVz(L^&Q}bcnLEh1CyXkh02lUoN4jlv>~QoHe$@25+Iegk zQ^s)@BEJPACElr zJ{)^md#$=RAJae%(eJ1x)p?eemm42hvjnHyF`rC_12D*FGHET;Amxk1ohQ6h6RGH>HIJu z$t|glq4A?HcB3)#eOg-D^4pQy(H09to-+((&(Av5)ksg*Zp;ta1$q+tV5!EU($nAO z*l-HTGCrMNIv3nvDMA(Wd@h0`m1XqB8o)7iz&Vhw0CXQ6bqr=41<6YO&;1x~#)koi zO>pGD#KrK&x8ro5%>UNFbdF2H1n)-V7u&W0PPAoxI{$nvrIo-Is3v&yMePSZglI$| zZLN_$8aAby2#2!;EH$_7g0JtW)-1Lw_5^0@SP?`Ov`o^Pz>Gd}h%IR*xi415nCU#*ZhRQf zW3>r~I#w*@W%Y$r_&sj^C0+M6f{=X&ko~L2)nr5+6wOYAr8=hSFQ#|4x6cycTnU%K z&%Pe=ld^@)z^cpO|yDSc&h`cUUe#tdB^xTcR)((V*%L0q2ZUM`-?1SGD zo;>qIA>7|xKL;lU8aZ}Ge5(`?@>wx0FvAwbJ2q@;lEa~(cz&F0Lk-~)FX~bDE24JS z)(~8<-<#w|y9ZpToA78<6Z|e#6p4$qLv(FV=1O^5vwhvOMA-__KeJ90rus+bN4os$or4;6xSJjN5lt6Du&N z-bSz|`HV}U&Adtb%coJK+QvE7(@JZ$RE~5}`|rM!OnNt`nA3kjufJ8Wx#PWBL9k!X z+*JAU#@~bEjZH~C-};9tv#zuXLfriS|D&J#)4$Tatu>^+HRM%&if%Uvq600_vhw}# z@NkW~!}HM_aisY^jsAleL%6q9@^H><4IKeK>%MC#J2>^{Yb0g%gIk!BBzX#4hMKTu zMz$XSCE;4s9=8P7qzL>kwwU8T%C^!EGQMS*){$pl$_^^A3q7QbI*L;4sUm(qXi=9) zSAUu4&v1O_KZGoCk641XtZ!h~*)o+Z?5B*)58-PYL&{Z9vFReTO4RLFiqTBuO!CJg zSIIXlakmVT?5Y02zU=CNfd;igMCX z@KG|SkIk1BsQ$bdR6@T+T!}d52 zRnQmkmgq@+`Nk83>=cAYgq6CIm+?;m$*v9l!*LW^e1G18smQba7<9gCs^v^4 zb`M7DaLp9GtnAAZL39?zio#f8-S4q-5WEdWnkBY@U>ALv)R}K!jWuA6j4Pt$FwYe7 z7qe5Lg*Jvas$NBRTT7!~a)_`fqu3~UpL)MQRQh2oJT!<0LFtXWJb!BTYj`$qND_i-VW!C4t`ne4!HkV}! zqND?l;)s_3k9VbI#h`l!~2pp`tL6U?GQ;5gqM59lp9#Y6YS zY$ewaV{bDzZLKP}tm+=vO4#jfp6tA7#m>V>k`H7M_lfm`t&Rg`-K^P^de@@?Z52Uz zaIPF)^wah>M`NHMc+3v4Ov0nTUNH)O-smR

4IEoP37wmR@ZD91VtAC%%T*S}k*Z zXN4;aNji(DN-kwn9OV{Xa@Zq8u=F4AR$k7QcLaz03u#*V9sYrBWFv6?OBGWjjmZ0l zyDo($;O@zS7~*dR=R)lT-yPCuP971KC%!bUyMSYXjxN^Y>5T)mT^qWVf{=8CR$se0 z4a}k%d0%4WvJN_9iOAPC1k-Ezx$~ow_B*%?;E14nfD-dxgY$nH&iOC( zGD&^k_2TT1m^#NGc|4uv7u?vz9s}mQ9I~WMQyjHrq|OiYkhYJdKf=?`37~=&LB6Ll2NeY4k(Z53F63`w z`u#Dv0_ZCs>`L`353cQ`sTuebB}C_|ZYL$?2DCl!*K}l5HGFU_+6Jqu;*gGV1KZwd zZW=iKAeJ{K$ei0Qjdb961Xr0pI$!BXlaZH;O#8@3t`|i_(+sn1TsJ)h`L=9bWLnhp z5f}|H5qv++VaVwGd3|}QY?S@fj{>o=E)O314}Mf2@1kN|6FLXM(&@u-H!yJk?10R* z`L26$I)sXv4pblo+aNeHNt+_?H|>&^>v940mf=u?a>H(W+7!nmLl7zgBj8dX6CI!+ zt=jjb{Z33$Vm3Zec=Ujf!SVeI6Wl+Dea=|o3prq*q)(VB6vmsBG9sB!{ z@9MOpG_nA@9E=wIyMW}Dn!9MM%aaGI0DaBPnSDZ!ZuRJU_% zlGve%=`FlXeB^_|s3#&ZHO>`;L?WalocW{O>zsC^A;Hp{{I~=8LMoX`z=$Az@lTii z5Uwl`TKpiPP-ttW*;*Ei&|Xtzz*WPtJKy^C=@GrUOeAsh%`{;E+2 z8Y_k4?gMotdp~?C;ncXDB1#&;G607a>z52f+v=?UbiG8q8&q0e9FK!$t z&|)9)HN+#s?TLxtfnh@W;h=q6D<63mtS9nxo}>_&O0e9^;{+nOO^Jz?ji2b64lu_o z@#erpng6&HW^P)t4l+zdXt_vcKh!cdLi(4DT6q84d@nKN99Z?ZDC*(&8?{kgY1tpI z1xfSy%9qJi7B*sN4W!EJ-n??*`hlD9VKT#i)4E)J;U6D_6r^IdeI1C0_v zJRy~3KNnbZ-cF1ljjlss$)mj5Cmmvk4KE71q%76wTJ%`|#HI5dJPyr#WL;Q95cZ=V z>18GBiGS05KpJ&HEw5{}xhvg^r{Ydk!%WKS6hH=r3-$Jkm}PZgW-H7zf^b_c^K?;Y zF|OAqM-b&JAwsm4Su5Lzk{Nznk{9jPpKUDRtiGy35Ws0LxB!BAa>>dqY1w!v;|(dW zzI2rv6mVvzf^<^;48#llEmowz)IpsJh;Rd=m(YEOS zF)=UN*$`2{-XTsbb1+H*g}!Q?gZ0G~dcfO~OwWH=ux?B;qPuV4EZ=46iFfDG^nf9m zyoNGxdcy)J=D=-E{*QRv&qsp3ho#AdqDV4RmE+VP&(nmtmsArUF&E_Q84L`1Dmdya zWYAYcw}ELEi6Ii#s!L=x%V9Pwq6*R_7h1W|?f-2xPaU+K3w#c-^_vKiyaim{!NFbo zuW5-fAfY-U|NAu+`m0y3CiH*DH1fH?o>y7-rq#PL^-s_N6+|a#@+O!c3+la>=V|%; ziGH*gCn<&_f3)h&i-dE7*s)XIynJHTA-Hrcs8#FM4`KN2c0^o}QEZTAAzLRej=AQz zRM`VTx(z?tqS5fvf$0SJ!J@_%4a~u4+pQPb z8EM6YBf{s5M1z$2DgWJ$F zOFaooLLSPHIZ%jsRO59Wq$Y;kZ;PHb_-A1)vpQH{MdsIPXFs*bEmYu%kSoPF_gjdN zka5NU80<$pALgWu@j56^6uDcZ7TV60ZoAEQ1)_wzg#|m{59!kX@B}t9U|*-l(L*>P zA))lf<8C1j-ESbm!Hh%LHUf5E`e)x3&{hHCWvpe@4CJYKBFH`ZfHxBkFGU_T#VCJl z7zjAoA@cLSpp}t*oF`9{D30uE7IkiGn$d03RTw8Tiq7tlCf|mo&ja}Iop5ic?b0Ruv%ogA{- z#^8)W{6OC5F7~9*J_eaeYC0k`0}W_L##k-z@D~e2^{{2rQcZVe$7$nwwXy^We|S;O z!xej<;T$0XhD>FrAwo$8Z#m@oAY$mgpfCkwcqk&PJmpswiV4^BPrR9P=Xv7>2YXil z9oo=Pp~|Ym%$p#FvJYU}FwT$qFMg@6cyxF(?wT$*=l{j;+7eNeZIS4}W#RAISX{{$ zB*cOu2^cp-wEivLGAa&I*CVUd3P-OypCalR;mA2=kIouVHkSH70CHex%|6#bB_b%> zJkgF@4n*hn4G<(m{$a%Dy;mtb{Rg2RdYms7uTrrzS#- z`Ob^@G%y77*R&jz?#aWf>9eW zccf_jFHvBV>&#Si6#f(MjH;Fay~*tuJcE(2a6@jEHg2i}C#ixXn}Cl^hn~l$H9ZKN_W68B3a?|l>Z(uRF6;_W-8*6uUhZWbE zKPYPV(ugeJq999<9uKy{@f2ag}oTrQ2_njDUf)tV2h^9den4HITYA}VaazND6D<) znpQrl1|C&j{WQSM;F`*T0F{|^1#dLB7B!XW_4)lVHpLd+iKqeGc(=^r5UyotvR>xg z>$~Xh4ro?1+H~kYY&<0}Cx@CIDW6>ca}O4R0nEq5(9gtiW7AeHn9l$s95hc`mZpu9 zT#6$%oS5Z!2pum_75pH{kIpD!g$e(qW+1LYA9tQ61sI|1ci`GW4$-#j=S@UV$|x?w z$U>MeAKS>T^d>&)Mm$Qp`soK0yLU__E5KV%%9a83ALT{Q)EnGYS$n-z1@{eMPh1%P zG83mpkdyqesL~{kBj;%qM%nEU?}5KrnR9QAOeJ8big_|1#W>PxH4Fj-=W!9d{VZ^C z+g;?9hUiB&oIGH#m*+Fq872CITZLQYf%L)=cbv~imuBiDD5dDuRn4(|Tq5?nbr>XU> z7xW`s|JnAd95mK)igUoxig9hg*0BelJ~-UMOI5%l>01oWWBrI9HmmL&;YVJFuyk1X z?q|%lmZx|MPQi%GLwkl6kn7Uz{FxW&D4HiOAlDW{?#syt9h3sfY_iTs+{4{US)ONO=#S zgeCLZ-9`+f^;D?Z^ZSK&-p|Sm&l8~Uy_^+zil zxv85kq;6RmfAvl?K1@LQISBz2f`El8M$Yec@8DN{EO7IRp-J+=tyDsGN z^tzP_KbK0V&lh;mkA>z62s1NuP?kLMbvD0VVSR)r?85J6@;|^U=?mh>Vo@5?+~i~Q zuD{~kthJ!z1$!(FX;-3Hg@1nlLf4zeE>QUXw_Pi-WMJw&oKWhQvd-7*7)i5@6u2~h z{mre%ok9J3i$R!4XR%m&bx(KB^anIydAe%U*e;*1{{;O>65lgLR0H-^HNWO{Kqfn5Rg=Fx z{SzG&f3Oyu^2hP*64(5nB_Q$*#j2p%hy1x~ri_ z#L2rv;02#p^iNZLvUY$|_?>0X|KX$DFdETs^zJQ9js3$A-YVhtRBfN=Z%BtKSE?w( zJqG$r$rw8&fhk}{33ndf z6*%%@Zua?_n%L-iHi5TDuxauxiaWK?bztiQKJAi4{~XlmhcX`dh&nysI_RlG`jV{e z^9fZs&71cWU}#8!fZk)0%lj^+VlvN%#e~;+Ljk*8GM0rx{r{OkA58#^&^jxEC#Z(3 z6Mr6WYbm(d6DzH2WZT7=J~Qi>)Z}NxeydS2`Z|B$;Al=-=j+G4`{WxQWE9`E{CIUd zkoDK2WUlPzj$Qg&DPf1_zuiA-m}WuKog11xQ?h2tj=A!1w7Y$Oftbi#ZTI9K(+e6u zla`=-F*gq5!>F|X?sXYo;8wGXB~2TXrp-~c`hwIOgk4xOdY97oDLN%{a?Baam}=I7 zSBw7GTr+N{h&&*2wL3E+VPpfX&ydpbB3myTKe`i-@&hJ+HIohqtvJH*G)tZRwsBf^ z;J1d*O>3jpm2K%WtEZ->UKS0paw`@eHJW^68M;1F4vpGc^L4=+s#kg!YO}K2?l;WE ziE_T)&cVq$R>kMw;*8ld5@wq3k1N%_+_r)A*=fyoerKpV9U(iU#ha>}{Zz!h|0aC9 zHqpR;b5CGW_o*9}-rSq<=afC-f@($_DuTx9pavPwvl;K9op8uUS z{wQb1Zo9lAvVQ_@5@nfR>ojQd<&B0DACd;*Zydz+93ERlgI9W{4Apx@QG2`^9)flm zs|tcK0yZ)#4`a>IKJm%%qY9OL7U`(aBQyO`(hHdxV+Wym%e3NF(t)v}k$S;*a-*3H zg56}uja2E;Z%ihW@^!KkXynWbMFMY#HyZwk?`baLjPL00;M96O?j$*M;N9Hzqqzro zvL|Pwm{ZSJ)oIzIscN1}Q}=VjJo8*&VB^3+dx`vm;$$kG{GKE}$js#^=isv7@-EaG zCs{Vri&^zT38k{+w6Ys(m$jf?Wjb7OM3u=LYY2w>Ve*ZGY}sf2JtJN>yKSpST7Is7 zHPZ0|znxo|kyp~&pb~Dis$smVse#3#oUK8L8q}Rr!n{qxbD-&nu{$fIrqLp_n@c9Kl^1 zL8quo4?O4}dpWey(&=NzM&cOLfsu3(t;NI#8S^y9d}G@NSM?tU|E_PGd;DjTa_CY3 zk5Q#vUf6JM0MDGwitLcZovPh9-M8cIeUpdKPL(7!Yfk3VrIyCIxKBjw9ssPKCzp*? z2@eD{9pOE7wUkZ!e8?ZCijrupw|44(zTOV1&pWnRlKI)T13yNKLmwwk(0liI;$sq! z=JqG75zE`ml;r-vBh!n+^?a{~-XzYC9DUq8ov8V-{h0o`=h+Jb6**^pavAKt=t~dw zTny}vtgNgY*&-FM&X3k5w6ZzG*^4VST$lJu^t4Uy*!q+Om>|SrEe{YiSgu= z_vLcV^vZp4*6w+3BB#W2u5wF(Hg4y;ljLK)3EJFh@_Vs|8B&G~kS&4U-=dxNo)c0&7TM_=yKU>x*n^LyE;U&hxe6!bH65nPy1 z=Z!ROja<=~Y+t=xjVuBezTLM>b#|AL%I&0qYX&30?X8F_A9-;Z{ zkwJsw)2VFbM0tVXQBLm~Z7wKC#%`K;R zVj`E&`IR}8*dq3e;v<4TFX_K*RXvT`ODdw*Gs>v#ZN3gzXAOnpJ`aNUWtPl{^fw`NiS0K~2{TPqVS zV-KWH8jJ=g78>7b{Pc5VBU~XnVe!J5i(=gZmu<$6)T8E^O+ofM=9ry4}@r91ekz9&;Oyw%#e(u;4Uehd% z#ca#`BabNor(})cgMip#YsRj+1 zSVR23261S(HxEn8OYhYtNK;pDZ?#iDzEE%evc}OIa0@xvHSN+@KPeU88!a zJ`290Xd)j;V#n?YW6(L@$2-!W#%O3?ZFE;xn%^kJ`?8ofytI>5>U~vA(3`B|j@v@5 z9;f*mtGSn8O7Mx3YpW&p?ddmuT3&v^h>~*`x=xj-3Fr4+qs?67T+VU$#!IfnkdRcB z!?5L)-(sG6&+-l*m-6XVzvTD&o}iU6j_T|;<)mZm#I(I!6!w<-%7dOD;t?@(#__X4ic4YjJvM*!h@Z9o-lbGUXzBHB_GR#G=024K7&azRV3RB5#f#avJVkghHWqH<7 zPi^uGsBgYh^3aOH^ir6QaTgI*7;UO$6$DQ&#(Cp(_Mn~*iOl0n=k8A{i3z-9^FPDG zVXCrIwYJ7*+^#m0?}bSC`M2P*zhL=L8GLSaVYsu*)~NZB9#3JI@!xeVLXxjT7h$z8 zyp>)vbpFeGbF%n_8iTGe0|^bs`KL1H6dMddfTXj+tEZM>*}oGk83)sXXBkPP>14O` z*>RZzQflPrv&YVV9*X=P6l9*&iETF|f}w_Uy1Qd$OH) G@&5qFVJo=+ delta 2087 zcmV+?2-x?eQnwHxiBL{Q4GJ0x0000DNk~Le000150000`2nGNE0GTslWRW2#f589% z4#5Gqk!$S$00+!TL_t(|UhSNFNK{!I$A4EXwKT&`Da+adwd`#~1VKcFZS11qwglB5 zsr66VKW)Yukro)})-Gx-m}SsbM$rST(6oq(%HC>keDsc7o3e*i>e>4{p2->Kj&twK zy>rin`vIBryQAZs@BPm2{Lb$le+3g06B82?6B82?6B82?hUVsG za_rbKc>DIPp~N_*)#lOD4`D?JV+lqJ3HxPaB#3;Tx}SkFI~Dsf3|HqO~+kb zUG(wWw{Ote+DgZ~V;G?i9XiBDKqo^hK|w*Z5@8{?7(!@tbAEm$i+UW5psS5gfp9Oh z#bP0)rKK$D=Iht5Y}C!5L4#;T!a^?LUg)OACMYPVVo^8fVq0)>Y-ng;5ub1^G`jg< zX?LYzOGifs8})#$e)8lAf48pYT4>C@xw&O5>cKWgTfqrZyf7@{53Yr_tu$=0tvYNO zIB*~|H8pYRYHo$DuC69IImcOKk29e$SMyp|b1U?bBS+ZC5vd$)B``3M%L;@`q0!A! zx!DT#Ih=v67Sjid_<>8I4RcY8F zSLlF%09^EN;c9M#zIpQ|*|^bhxh;iUq4E28xeLRM&?iouU?bPKsyb}(_xIq*c`@9te+e_&sPhq{^zp^qPzUTQl-s;Sf$*aOiU-GzSe;DM}4!xlN^oh_KF zuV25eYx>Y#=%YuEvXOk03XPzv_1sFPtI&@hKPH)(|FTFvN>c|S2!gJ4wXQ-d&COQi zmUq4B*RP*$t3};}uBoXZ>(-U9$b0D3uhvcIvNGpwP%S}Mf9ruRLZh4A$<1nj`5o(a z7J3a`g!a_UzP`R}+3ys^SgKN*vNgnT~~WuXeB)jXQZd4!{^VR zAtEAz9Z?Rt+M9K?z#hFqLqh`@Gv;R&DQDrrA0azC8>Ua6PS5+!ojW8vJlyRL9rjEn zPMqi!4ZP}Te@}9AM#j%jQd06=WMuN>$wG5;v)eh_&(9B@KY#8OS9{ITc=u%7xFi-S zV|VddNJva*9WrD{2s>m4iq-O83-|~4vm^4s<^1^Z<2Bd8YmPpBx_cA3QqG<&gp|}2 z!I{X&h!G=%&>uqGT8#Ml`mvF=c*W88?%gA?vA?oNe;$h##lY6BTVd?jv5L;|>eZ{_ zJTL|~(Xb<~!HG34E>2S|yyECnr%thvJl3!O1&WG_6pM_^m@z}Z&5?M&P%cWo*IhJs zG;XCGHR>l8xn|$KKVbRt;d4;g#O zVp#`s=FD+8-z!(HkjlzRx)B{6jDL2qWXTd3IdUZZ-q){R6a4$cn3x!N^X3ix8E4>4 z-w6{YXrhUxj+RqtWM(EpVPTFbiEbq8ustspGDH(ry@2sR&`x8b98xmIU7k+ ze_sBVOOX*w1qlfWbWh%yGiPAu&YkQTdvM|5>R#fcNs|OjG3{;b?1&?nrnEp4q0!Ch z>Ho0E9;}aRYDytKK3>*4!WtKI=i(mHmMvRo zymIAA+6||LirQCe+Vi6=KCkvsWp&lo|f2gP^ zp{lA1u3fuEs})bd*TZV0z1==sKE6`d+4 zh!u<|sO0O_)zuNPrY7RTh9o8?l5^+Isq#Br< zY`LuR;qFVRmst~ddteng3+T#tD28LeE5KqAZ*yMfli9O|4)aN zq@*Os&CQjpi9KcN)Twl0G%+zTF)=YQF)=YQF)=YQ1{RA&{RV-F9Ttlv@PB;%bmuZ3 R)`pH?ny*JR9HvtmrJNkQ5=WQImji?%a!CY z@J>oGxaCb5$jDVn>Yh{Fftf;?n3zyP3gvMgMJQzAdK5f_KaC^NhsCHo(D@2R zJ#ilIVIo`5-hcd%4m8L_ypQ%;@)pK26*ZjnXJvGwL)EeqqXFE+SdO6n_!cMYK!Yd| zk74xY@&;CLlVJ?5463Q4_&BIGL?)e1tGiEN7b*{g60w5Yy=(9|Ds$)HT{L8~*|ik< zT~853L$$M5Ebdbym&-Mm%jI2}OlD6$pSKCugp+U{4u3?=5e7Ga{kQ>@IJ3}1-QM0l zmZy0{RVUYCCjWWK%{!^LQg1613L3_-xUayz^M`1=Obo()LA53=++0JqHE5b%OvDXf zXjt}yrNt&tE%e(}TxMDxxB>hK%fE&jfEE!kDl9EYbAf(ej!#U>12+IW?53mEd5d}` z+WsA{aeph?S5?;p<_2&*EVZ)D2&#sscrmD&8I26y<9Cj9)N{)&?c$FUjmi zIT|$tn{h90L<2*yiJ_&HuIkCi{_aw#RM`zNXH1~zA)rse5i^RkSs91ha68sSi;ueT z9rk7_juKtmmgqnnddlcx)LUkwz1}$;sE5sHBY!@u0}Z0-w;OdGC8<}BY7wzPXp@<^ zzjIiMc0(fD&4?|7SnsD@G^dA4? cK^H3Z6L+53r+WO46aWAK07*qoM6N<$f&saI-2eap delta 660 zcmV;F0&D%*2G9kNNPi0x000XU0RWnu7ytkQWl2OqR9HvVm%EP*Q5=A;Wj(XrG(<^@ zO3)Bd5DG#gS`-q|+SQ3fE1LZW6e1cT5s${ML?J|{ktn@iLA-t6+%vmp@1C2Pxp(%i z{gN*;XXc)Be)l(LW`5I{A{K>hA$?(6Cxdvpx!YTo<5m2N7k@lZhPpDa4WHm{{1Gh0 ztgvmxf3p{tp#R7~U*VO|&Y6p+@I2nf6L<|@<7M=`bG*Up_yr4b^2g{zAA(=e30gC@ z9$%ur-QxjxihnTgtDe7$^RbYlXou~%7*_<%Mv8TK8|`xzpW`K5fc6`}{pk5!Sjb5< zY9}v?X+DQeqJQqkti_*rAZtaW7NG-3f7q76K11lbG5CcTlaG~;Jw_@3kGXg{;U;du zds%B7Gc*S$&6f(m)&2;7Wv%Kw!yC=rF}|Z?zROw#7;P%BZkdhkZu^r2ae2BhsJHdLuaVL-pz++Bs zhnR0@Dt*&ga8!A*IF@5hnlBZ=C(Ki3Ib~U8dFAAmTb5nc{+3~uV=0!Rx2JurtTv+k zEmjJ#aDTjmMr?3+-(_5;xtDDlF+~sLB{sBN8dCF?WYz_JV0000 Date: Thu, 24 Oct 2024 14:59:00 +0200 Subject: [PATCH 33/55] Add button to hide roofs in Object view --- .../CityDoctorLocalization.properties | 1 + .../CityDoctorLocalization_de.properties | 1 + .../citydoctor2/gui/CityDoctorController.java | 1 + .../citydoctor2/gui/MainToolBar.java | 28 ++++++++++++ .../stuttgart/citydoctor2/gui/MainWindow.java | 4 ++ .../stuttgart/citydoctor2/gui/Renderer.java | 41 +++++++++++++++++- .../citydoctor2/gui/MainToolBar.fxml | 5 +++ .../citydoctor2/gui/icons/hide_roof.png | Bin 0 -> 879 bytes 8 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/icons/hide_roof.png diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties index 9f10f95..410abe0 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization.properties @@ -31,6 +31,7 @@ MainToolBar.writeReports=Write Reports MainToolBar.executeChecks=Execute Checks MainToolBar.showWorld=Show entire city model MainToolBar.resetCamera=Reset view +MainToolBar.hideRoof=Show/Hide roofs in object view MainWindow.missingConfig=Could not find configuration file. MainWindow.loadGivenFile=Loading given file, please wait MainWindow.finishedLoading=Finished loading diff --git a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties index 89bae3e..7e1ce1b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties +++ b/CityDoctorParent/CityDoctorModel/src/main/resources/CityDoctorLocalization_de.properties @@ -29,6 +29,7 @@ MainToolBar.writeReports=Schreibe Reports MainToolBar.executeChecks=F\u00fchre Pr\u00fcfungen aus MainToolBar.showWorld=Gesamtes Stadtmodell anzeigen MainToolBar.resetCamera=Ansicht zur\u00fccksetzen +MainToolBar.hideRoof=Zeige/Verstecke D\u00e4cher in Objektansicht MainWindow.missingConfig=Konnte Konfigurationsdatei nicht finden. MainWindow.loadGivenFile=Lade vorhandene Datei, bitte warten MainWindow.finishedLoading=Fertig geladen diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java index 05ba494..db25ebb 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/CityDoctorController.java @@ -139,6 +139,7 @@ public class CityDoctorController { mainWindow.getWorldBtn().setDisable(false); mainWindow.getSaveBtn().setDisable(false); mainWindow.getResetCameraBtn().setDisable(false); + mainWindow.getHideRoofBtn().setDisable(false); mainWindow.getNorthArrow().setVisible(true); mainWindow.alignNorthArrow(); setupFeatureTabs(); diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java index 19f2284..8651d88 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainToolBar.java @@ -49,6 +49,12 @@ public class MainToolBar { @FXML private ImageView showWorldImageView; + @FXML + private ToggleButton hideRoofBtn; + + @FXML + private ImageView hideRoofView; + @FXML private Button resetCameraBtn; @@ -146,6 +152,7 @@ public class MainToolBar { setupAboutButton(); setupReportButton(); setupResetCameraButton(); + setupHideRoofButton(); loadImages(); gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected())); @@ -219,6 +226,10 @@ public class MainToolBar { Image img = new Image(inStream); resetCameraView.setImage(img); } + try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/hide_roof.png")) { + Image img = new Image(inStream); + hideRoofView.setImage(img); + } } catch (IOException e) { // ignore close exception } @@ -316,6 +327,19 @@ public class MainToolBar { } + private void setupHideRoofButton() { + hideRoofBtn.setDisable(true); + hideRoofBtn.setSelected(false); + hideRoofBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.hideRoof"))); + hideRoofBtn.setOnAction(ae -> { + if (hideRoofBtn.isSelected()) { + renderer.hideRoofs(); + } else { + renderer.showRoofs(); + }}); + + } + public Button getCheckButton() { return checkBtn; } @@ -352,6 +376,10 @@ public class MainToolBar { return lod4Btn; } + public ToggleButton getHideRoofBtn(){ + return hideRoofBtn; + } + public Button getWorldBtn() { return showWorldBtn; } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java index d91e912..18018ff 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/MainWindow.java @@ -1096,6 +1096,10 @@ public class MainWindow extends Application { return mainToolBar.getResetCameraBtn(); } + public ToggleButton getHideRoofBtn(){ + return mainToolBar.getHideRoofBtn(); + } + public ImageView getNorthArrow(){ return northArrow; } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java index e822100..752d7bd 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/java/de/hft/stuttgart/citydoctor2/gui/Renderer.java @@ -45,7 +45,7 @@ public class Renderer { private LoadingInfoDialog loadingDialog; private List lodFilters = new ArrayList<>(); - + private ViewFilter roofFilter; private Runnable refresher; private Runnable errorUpdater; @@ -97,6 +97,25 @@ public class Renderer { return geom.getLod() == Lod.LOD4; } }); + roofFilter = new ViewFilter() { + @Override + public boolean allowedToUse(CityObject co, Geometry geom){ + if (!this.isEnabled()){ + return true; + } + return this.useGeometry(co, geom); + } + + @Override + public boolean useGeometry(CityObject co, Geometry geom) { + if (co instanceof BoundarySurface bs){ + return bs.getType() != BoundarySurfaceType.ROOF; + } + return true; + } + + }; + roofFilter.setEnable(false); } public void enableLod1() { @@ -155,6 +174,20 @@ public class Renderer { } } + public void hideRoofs(){ + roofFilter.setEnable(true); + if (refresher != null) { + refresher.run(); + } + } + + public void showRoofs(){ + roofFilter.setEnable(false); + if (refresher != null) { + refresher.run(); + } + } + public void render(Building building) { refresher = () -> { Set setupBuildingPolygons = setupBuildingPolygons(building); @@ -581,7 +614,9 @@ public class Renderer { } } if (used) { - addConcretePolygons(polygons, geom); + if (roofFilter.allowedToUse(co, geom)) { + addConcretePolygons(polygons, geom); + } } } } @@ -942,6 +977,8 @@ public class Renderer { private void addConcretePolygons(Set polygons, Geometry geom) { for (Polygon p : geom.getPolygons()) { + if (p.getOriginal().getPartOfSurface() != null && + !roofFilter.allowedToUse(p.getOriginal().getPartOfSurface(), p.getParent())) continue; polygons.add(p.getOriginal()); } } diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml index 1309b50..2c02a21 100644 --- a/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml +++ b/CityDoctorParent/Extensions/CityDoctorGUI/src/main/resources/de/hft/stuttgart/citydoctor2/gui/MainToolBar.fxml @@ -36,6 +36,11 @@ + + + + +