diff --git a/CityDoctorParent/CityDoctorCheckResult/pom.xml b/CityDoctorParent/CityDoctorCheckResult/pom.xml
index 1e0b0ee67a676dc5dbe3716e74459070ffd43a7e..26828461ded0e90b9011bb4f23ee6977c98b3e45 100644
--- a/CityDoctorParent/CityDoctorCheckResult/pom.xml
+++ b/CityDoctorParent/CityDoctorCheckResult/pom.xml
@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<parent>
 		<groupId>de.hft.stuttgart</groupId>
diff --git a/CityDoctorParent/CityDoctorEdge/pom.xml b/CityDoctorParent/CityDoctorEdge/pom.xml
index a0f20756efe753ad2d45f0bc9514e4abae59c9ea..b4b4dcdda9845ddc99e94e72991656da79ba66da 100644
--- a/CityDoctorParent/CityDoctorEdge/pom.xml
+++ b/CityDoctorParent/CityDoctorEdge/pom.xml
@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<parent>
 		<groupId>de.hft.stuttgart</groupId>
diff --git a/CityDoctorParent/CityDoctorEdge/src/main/java/de/hft/stuttgart/citydoctor2/edge/Vector2d.java b/CityDoctorParent/CityDoctorEdge/src/main/java/de/hft/stuttgart/citydoctor2/edge/Vector2d.java
index 4ff8e6ccc5d434f7ca2690f501463f8e0561e0ed..c6bed4925715cd3e1928ea466ca5875b97e2375d 100644
--- a/CityDoctorParent/CityDoctorEdge/src/main/java/de/hft/stuttgart/citydoctor2/edge/Vector2d.java
+++ b/CityDoctorParent/CityDoctorEdge/src/main/java/de/hft/stuttgart/citydoctor2/edge/Vector2d.java
@@ -29,7 +29,6 @@ public class Vector2d {
 		this.y = y;
 	}
 
-	@SuppressWarnings("SuspiciousNameCombination")
     public Vector2d getPerpendicularVector() {
 		return new Vector2d(y, -x);
 	}
diff --git a/CityDoctorParent/CityDoctorModel/pom.xml b/CityDoctorParent/CityDoctorModel/pom.xml
index 1b8c32a18c38449bdc40a7c8237b1d058031382f..ae6596624a1ea2d200a04a17e1621f461e0e6c79 100644
--- a/CityDoctorParent/CityDoctorModel/pom.xml
+++ b/CityDoctorParent/CityDoctorModel/pom.xml
@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<parent>
 		<groupId>de.hft.stuttgart</groupId>
@@ -92,7 +94,8 @@
 					<prefix>git</prefix>
 					<verbose>false</verbose>
 					<generateGitPropertiesFile>true</generateGitPropertiesFile>
-					<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
+					<generateGitPropertiesFilename>
+						${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
 					<gitDescribe>
 						<skip>false</skip>
 						<always>false</always>
diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/HealingMethod.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/HealingMethod.java
index ca4b61389f12353e1f5f6128761d8cc590a91d7a..3879e29e1e4180057d990d4e0b665642a73f1362 100644
--- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/HealingMethod.java
+++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/HealingMethod.java
@@ -60,10 +60,8 @@ import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
  * @author Matthias Betz
  *
  */
-@SuppressWarnings("unused")
 public interface HealingMethod {
 	
-	
 	public HealingID getID();
 
 	default boolean visit(CheckError e, ModificationListener l) {
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 53d8ec553ba812147e1ff5151847cabc4d0b81ea..c39481b6e7819c9a8435b71456dd89405df33b33 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
@@ -124,7 +124,10 @@ public abstract class AbstractFurniture extends CityObject {
     public void setParent(CityObject co) {
         parent = co;
     }
-
+    
+    public CityObject getParent() {
+		return parent;
+	}
 
     @Override
     public void unsetGmlGeometries() {
diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java
index f219de024883002a449e9adcd70ab921cdf000f4..cf7380109110816c7a43f639c547dc271b1861fb 100644
--- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java
+++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/ImplicitGeometryHolder.java
@@ -1,15 +1,14 @@
 package de.hft.stuttgart.citydoctor2.datastructure;
 
+import java.io.Serial;
+import java.util.List;
+
+import org.citygml4j.core.model.core.ImplicitGeometry;
+
 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.math.TransformationMatrix;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.citygml4j.core.model.core.ImplicitGeometry;
-
-import java.io.Serial;
-import java.util.List;
 
 /**
  * Datastructure for representation and resolving of implicit geometries
@@ -18,7 +17,6 @@ import java.util.List;
  */
 public class ImplicitGeometryHolder extends Geometry {
 
-    private static final Logger logger = LogManager.getLogger(ImplicitGeometryHolder.class);
     @Serial
     private static final long serialVersionUID = -8938931081577196349L;
 
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 f1e2024c079b2e83ac3ae8ad766cdff0fbce1893..850989f8d102910ecc547253c61191d46ac76bc2 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
@@ -14,68 +14,66 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * Reference object for handling of implicit geometries with a library object contained in an external file
+ * Reference object for handling of implicit geometries with a library object
+ * contained in an external file
  *
  * @author Riegel
  */
 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 Map<String, LibraryObject> libraryObjects = new ConcurrentHashMap<>();
+	private static final Logger logger = LogManager.getLogger(LibraryObject.class);
+	@Serial
+	private static final long serialVersionUID = -50293435187454911L;
 
-    public static LibraryObject of(Path path, ParserConfiguration config) {
-        if (libraryObjects.containsKey(path.toString())) {
-            return libraryObjects.get(path.toString());
-        }
-        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 static Map<String, LibraryObject> libraryObjects = new ConcurrentHashMap<>();
 
-    private LibraryObject(GeometryType type, Lod lod, Path path, ParserConfiguration config) {
-        super(type, lod);
-        this.filepath = path.toString();
-        this.config = config;
-    }
+	public static LibraryObject of(Path path, ParserConfiguration config) {
+		if (libraryObjects.containsKey(path.toString())) {
+			return libraryObjects.get(path.toString());
+		}
+		Geometry protoGeom = parseFile(path, config);
+		if (protoGeom == null) {
+			return null;
+		}
+		LibraryObject libOb = new LibraryObject(protoGeom.getType(), protoGeom.getLod());
+		protoGeom.getPolygons().forEach(libOb::addPolygon);
+		libOb.updateEdgesAndVertices();
+		libraryObjects.put(path.toString(), libOb);
+		return libOb;
+	}
 
+	private LibraryObject(GeometryType type, Lod lod) {
+		super(type, lod);
+	}
 
-    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<CityObject> 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;
-    }
+	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<CityObject> 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("Encountered an error while parsing library object {}", 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("Implicit geometry references non-existing library object file {}.", 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
index bbacc03a7f8266db710cc83f154baf8b0112cc63..8abe39a197cf6027b7864991d43b125be8d2fae0 100644
--- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java
+++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/RelativeGeometry.java
@@ -1,9 +1,5 @@
 package de.hft.stuttgart.citydoctor2.datastructure;
 
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
 import java.io.Serial;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -15,27 +11,23 @@ import java.util.concurrent.ConcurrentHashMap;
  */
 public class RelativeGeometry extends Geometry {
 
-    private static final Logger logger = LogManager.getLogger(RelativeGeometry.class);
-    @Serial
-    private static final long serialVersionUID = -686112245455298977L;
-
-    private static Map<Geometry, RelativeGeometry> relativeGeometries = new ConcurrentHashMap<>();
-
-    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);
-    }
+	@Serial
+	private static final long serialVersionUID = -686112245455298977L;
+
+	private static Map<Geometry, RelativeGeometry> relativeGeometries = new ConcurrentHashMap<>();
+
+	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/datastructure/TunnelConstructiveElement.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelConstructiveElement.java
index 4e8dad0c10ec368a54747d2b417c6fb7e70954bc..45ba893248ed42ff1e09aee4bb65d3beacdb1f04 100644
--- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelConstructiveElement.java
+++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/TunnelConstructiveElement.java
@@ -60,6 +60,8 @@ public class TunnelConstructiveElement extends CityObject {
                     MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
                     setMultiSurfaceAccordingToLod(geom, ms);
                     break;
+                case COMPOSITE_SURFACE:
+                	throw new IllegalStateException("Tunnel constructive element cannot have a composite surface geometry");
             }
         }
         for (BoundarySurface bs : boundarySurfaceList) {
diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java
index 881ca6b1f91526f4f0ac7140324b5b149508bda4..09ce064577ac0a068374b9d502d6fc0917387760 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
@@ -72,1187 +72,1213 @@ import java.util.Map;
 
 public class Citygml3FeatureMapper extends ObjectWalker {
 
-    private static final Logger logger = LogManager.getLogger(Citygml3FeatureMapper.class);
-
-    private final CityDoctorModel model;
-    private final ParserConfiguration config;
-    private final Path directory;
-    private final double neighborDistance;
-    private Map<String, ConcretePolygon> polygonMap = new HashMap<>();
-    private Map<String, CompositeCollection> compositeMap = new HashMap<>();
-    private List<ResolvableReference> references = new ArrayList<>();
-    private Map<Vertex, Vertex> vertexMap = new HashMap<>();
-
-    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());
-    }
-
-    public static void parseId(AbstractGML gml, GmlElement gmlElement) {
-        String id = gml.getId();
-        if (id != null) {
-            gmlElement.setGmlId(new GmlId(id));
-        }
-    }
-
-    @Override
-    public void visit(AbstractSpace space) {
-        // if we are here, an AbstractSpace thing was read that is not handled in the
-        // other methods
-
-    }
-
-    @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);
-    }
-
-    @Override
-    public void visit(org.citygml4j.core.model.generics.GenericOccupiedSpace gos) {
-        GenericCityObject gco = new GenericCityObject();
-        gco.setGmlObject(gos);
-        GeometryProperty<?> agLod1 = gos.getDeprecatedProperties().getLod1Geometry();
-        GeometryProperty<?> agLod4 = gos.getDeprecatedProperties().getLod4Geometry();
-        if (agLod1 != null && agLod1.getObject() != null &&
-                agLod1.getObject() instanceof MultiSurface ms) {
-            gco.addGeometry(parseMultiSurface(ms, Lod.LOD1));
-        }
-        if (agLod4 != null && agLod4.getObject() != null) {
-            if (agLod4.getObject() instanceof MultiSurface ms) {
-                gco.addGeometry(parseMultiSurface(ms, Lod.LOD4));
-            } else if (agLod4.getObject() instanceof AbstractSolid solid) {
-                gco.addGeometry(parseSolid(solid, Lod.LOD4));
-            }
-        }
-        mapAbstractOccupiedSpace(gos, gco);
-        resolveAndClearReferences();
-        gco.unsetGmlGeometries();
-        updateEdgesAndVertices(gco);
-        model.addGenericCityObject(gco);
-    }
-
-    @Override
-    public void visit(org.citygml4j.core.model.building.Building gmlBuilding) {
-        Building cdBuilding = new Building();
-        readAbstractBuilding(gmlBuilding, cdBuilding);
-        for (BuildingPartProperty bpProp : gmlBuilding.getBuildingParts()) {
-            if (!bpProp.isSetObject()) {
-                continue;
-            }
-            BuildingPart part = new BuildingPart(cdBuilding);
-            readAbstractBuilding(bpProp.getObject(), part);
-        }
-        model.addBuilding(cdBuilding);
-    }
-
-    @Override
-    public void visit(WaterBody waterBody) {
-        WaterObject wo = new WaterObject();
-        wo.setGmlObject(waterBody);
-        mapAbstractOccupiedSpace(waterBody, wo);
-        parseAndAddMultiSurface(waterBody.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, wo);
-        parseAndAddSolid(waterBody.getDeprecatedProperties().getLod4Solid(), Lod.LOD4, wo);
-
-        SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
-        for (AbstractSpaceBoundaryProperty surfaceProp : waterBody.getBoundaries()) {
-            if (!surfaceProp.isSetObject()) {
-                continue;
-            }
-            AbstractSpaceBoundary surface = surfaceProp.getObject();
-            surface.accept(surfaceMapper);
-        }
-        for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
-            wo.addBoundarySurface(bs);
-        }
-
-        resolveAndClearReferences();
-        wo.unsetGmlGeometries();
-        updateEdgesAndVertices(wo);
-        for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
-            updateEdgesAndVertices(bs);
-        }
-        model.addWater(wo);
-    }
-
-    @Override
-    public void visit(LandUse landUse) {
-        LandObject lo = new LandObject(landUse);
-
-        mapAbstractThematicSurface(landUse, lo);
-
-        finishCityObjectConstruction(lo);
-        model.addLand(lo);
-    }
-
-    @Override
-    public void visit(PlantCover plantCover) {
-        Vegetation veg = new Vegetation(VegetationType.PLANT_COVER);
-        veg.setGmlObject(plantCover);
-        mapAbstractVegetationObject(plantCover, veg);
-
-        parseAndAddMultiSurface(plantCover.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, veg);
-        parseAndAddMultiSurface(plantCover.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, veg);
-
-        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);
-        parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod4Geometry(), Lod.LOD4, veg);
-
-        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) {
-        mapAbstractSpace(aps, co);
-    }
-
-    private void mapAbstractSpace(AbstractSpace as, CityObject co) {
-        mapAbstractFeature(as, co);
-        parseAndAddMultiSurface(as.getLod0MultiSurface(), Lod.LOD0, co);
-        parseAndAddMultiSurface(as.getLod2MultiSurface(), Lod.LOD2, co);
-        parseAndAddMultiSurface(as.getLod3MultiSurface(), Lod.LOD3, co);
-        parseAndAddSolid(as.getLod1Solid(), Lod.LOD1, co);
-        parseAndAddSolid(as.getLod2Solid(), Lod.LOD2, co);
-        parseAndAddSolid(as.getLod3Solid(), Lod.LOD3, co);
-        parseAndAddGenericAttributes(as, co);
-    }
-
-    private void parseAndAddGenericAttributes(AbstractSpace as, CityObject co) {
-        for (AbstractGenericAttributeProperty aga : as.getGenericAttributes()) {
-            co.addGenericAttribute(new GenericAttribute(aga));
-        }
-    }
-
-    @Override
-    public void visit(Bridge bridge) {
-        BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, bridge);
-
-
-        // parse deprecated geometries
-        parseAndAddMultiSurface(bridge.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, bo);
-        parseAndAddMultiSurface(bridge.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, bo);
-        parseAndAddSolid(bridge.getDeprecatedProperties().getLod4Solid(), Lod.LOD4, bo);
-
-        mapAbstractBridge(bridge, bo);
-        for (BridgePartProperty bPartProperty : bridge.getBridgeParts()) {
-            if (!bPartProperty.isSetObject()) {
-                continue;
-            }
-            BridgePart gmlBridgePart = bPartProperty.getObject();
-            BridgeObject bPart = new BridgeObject(BridgeType.BRIDGE_PART, gmlBridgePart);
-            mapAbstractBridge(gmlBridgePart, bPart);
-            bo.addBridgePart(bPart);
-        }
-        resolveAndClearReferences();
-        updateEdgesAndVertices(bo);
-        for (BridgeObject part : bo.getParts()) {
-            updateEdgesAndVertices(part);
-        }
-        for (BridgeConstructiveElement ele : bo.getConstructiveElements()) {
-            updateEdgesAndVertices(ele);
-            for (BoundarySurface bs : ele.getBoundarySurfaces()) {
-                updateEdgesAndVertices(bs);
-            }
-        }
-        for (Installation bi : bo.getBridgeInstallations()) {
-            updateEdgesAndVertices(bi);
-            for (BoundarySurface bs : bi.getBoundarySurfaces()) {
-                updateEdgesAndVertices(bs);
-            }
-        }
-        model.addBridge(bo);
-    }
-
-    private void mapAbstractBridge(AbstractBridge ab, BridgeObject bo) {
-        mapAbstractConstruction(ab, bo);
-        for (BridgeConstructiveElementProperty eleProp : ab.getBridgeConstructiveElements()) {
-            if (!eleProp.isSetObject()) {
-                continue;
-            }
-            org.citygml4j.core.model.bridge.BridgeConstructiveElement constructiveElement = eleProp.getObject();
-            BridgeConstructiveElement cdEle = new BridgeConstructiveElement(constructiveElement);
-            mapConstructiveElement(constructiveElement, cdEle);
-            bo.addConstructiveElement(cdEle);
-        }
-
-        List<BridgeInstallationProperty> bridgeInstallations = ab.getBridgeInstallations();
-        for (BridgeInstallationProperty installationProp : bridgeInstallations) {
-            var gmlBi = installationProp.getObject();
-            if (gmlBi == null) {
-                // ignore empty properties
-                continue;
-            }
-            Installation bi = mapBridgeInstallation(gmlBi);
-            bo.addBridgeInstallation(bi);
-        }
-
-        SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
-        for (AbstractSpaceBoundaryProperty surfaceProp : ab.getBoundaries()) {
-            if (!surfaceProp.isSetObject()) {
-                continue;
-            }
-            AbstractSpaceBoundary surface = surfaceProp.getObject();
-            surface.accept(surfaceMapper);
-        }
-        updatePartOfSurface(bo, surfaceMapper);
-
-        for (BoundarySurface bs : bo.getBoundarySurfaces()) {
-            updateEdgesAndVertices(bs);
-        }
-
-        bo.unsetGmlGeometries();
-    }
-
-    private Installation mapBridgeInstallation(BridgeInstallation gmlBi) {
-        Installation bi = new Installation();
-        bi.setGmlObject(gmlBi);
-        mapAbstractOccupiedSpace(gmlBi, bi);
-        GeometryProperty<?> lod2Prop = gmlBi.getDeprecatedProperties().getLod2Geometry();
-        parseAndAddAbstractGeometry(lod2Prop, Lod.LOD2, bi);
-        GeometryProperty<?> lod3Prop = gmlBi.getDeprecatedProperties().getLod3Geometry();
-        parseAndAddAbstractGeometry(lod3Prop, Lod.LOD3, bi);
-        GeometryProperty<?> lod4Prop = gmlBi.getDeprecatedProperties().getLod4Geometry();
-        parseAndAddAbstractGeometry(lod4Prop, Lod.LOD4, bi);
-        bi.unsetGmlGeometries();
-
-        SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
-        for (AbstractSpaceBoundaryProperty surfaceProp : gmlBi.getBoundaries()) {
-            if (!surfaceProp.isSetObject()) {
-                continue;
-            }
-            AbstractSpaceBoundary surface = surfaceProp.getObject();
-            surface.accept(surfaceMapper);
-        }
-        for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
-            bi.addBoundarySurface(bs);
-            for (Geometry geom : bs.getGeometries()) {
-                for (Polygon p : geom.getPolygons()) {
-                    p.setPartOfSurface(bs);
-                    p.setPartOfInstallation(bi);
-                }
-            }
-        }
-        for (Geometry geom : bi.getGeometries()) {
-            for (Polygon p : geom.getPolygons()) {
-                p.setPartOfInstallation(bi);
-            }
-        }
-
-        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);
-        }
-        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);
-        }
-
-        for (TunnelConstructiveElementProperty teProp : gmlTunnel.getTunnelConstructiveElements()) {
-            var gmlTc = teProp.getObject();
-            if (gmlTc == null) {
-                continue;
-            }
-            TunnelConstructiveElement tc = mapTunnelConstructiveElement(gmlTc);
-            cdTunnel.addTunnelConstructiveElement(tc);
-        }
-
-        SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
-        for (AbstractSpaceBoundaryProperty surfaceProp : gmlTunnel.getBoundaries()) {
-            if (!surfaceProp.isSetObject()) {
-                continue;
-            }
-            AbstractSpaceBoundary surface = surfaceProp.getObject();
-            surface.accept(surfaceMapper);
-        }
-        for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
-            updateEdgesAndVertices(bs);
-            for (Opening o : bs.getOpenings()) {
-                updateEdgesAndVertices(o);
-            }
-        }
-
-
-        cdTunnel.unsetGmlGeometries();
-        resolveAndClearReferences();
-        updateEdgesAndVertices(cdTunnel);
-    }
-
-    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, compositeMap, 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, compositeMap, 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, compositeMap, 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 TunnelConstructiveElement mapTunnelConstructiveElement(org.citygml4j.core.model.tunnel.TunnelConstructiveElement gmlTe) {
-        TunnelConstructiveElement te = new TunnelConstructiveElement(gmlTe);
-        mapAbstractOccupiedSpace(gmlTe, te);
-        te.unsetGmlGeometries();
-        SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
-        for (AbstractSpaceBoundaryProperty surfaceProp : gmlTe.getBoundaries()) {
-            if (!surfaceProp.isSetObject()) {
-                continue;
-            }
-            AbstractSpaceBoundary surface = surfaceProp.getObject();
-            surface.accept(surfaceMapper);
-        }
-        for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
-            te.addBoundarySurface(bs);
-            for (Geometry geom : bs.getGeometries()) {
-                for (Polygon p : geom.getPolygons()) {
-                    p.setPartOfSurface(bs);
-                }
-            }
-        }
-        return te;
-    }
-
-    private void updatePartOfSurface(BridgeObject bo, SurfaceMapper surfaceMapper) {
-        for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
-            bo.addBoundarySurface(bs);
-            for (Geometry geom : bs.getGeometries()) {
-                for (Polygon p : geom.getPolygons()) {
-                    p.setPartOfSurface(bs);
-                }
-            }
-        }
-    }
-
-    private void mapConstructiveElement(org.citygml4j.core.model.bridge.BridgeConstructiveElement ele, BridgeConstructiveElement bce) {
-        mapAbstractConstructiveElement(ele, bce);
-
-        SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
-        for (AbstractSpaceBoundaryProperty surfaceProp : ele.getBoundaries()) {
-            if (!surfaceProp.isSetObject()) {
-                continue;
-            }
-            AbstractSpaceBoundary surface = surfaceProp.getObject();
-            surface.accept(surfaceMapper);
-        }
-        for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
-            bce.addBoundarySurface(bs);
-        }
-    }
-
-    private void mapAbstractConstructiveElement(org.citygml4j.core.model.bridge.BridgeConstructiveElement ele, BridgeConstructiveElement bce) {
-        mapAbstractOccupiedSpace(ele, bce);
-    }
-
-    private void mapAbstractConstruction(AbstractConstruction ac, BridgeObject bo) {
-        mapAbstractOccupiedSpace(ac, bo);
-    }
-
-    @Override
-    public void visit(AuxiliaryTrafficArea ata) {
-        TransportationObject to = new TransportationObject(TransportationType.AUXILLIARY_TRAFFIC_AREA);
-        mapAbstractThematicSurface(ata, to);
-        finishTransportationMapping(to);
-    }
-
-    @Override
-    public void visit(TrafficArea ta) {
-        TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
-        mapAbstractThematicSurface(ta, to);
-        finishTransportationMapping(to);
-    }
-
-    @Override
-    public void visit(Road road) {
-        TransportationObject to = new TransportationObject(TransportationType.ROAD);
-        mapAbstractTransportationSpace(road, to);
-        finishTransportationMapping(to);
-    }
-
-    private void mapAbstractThematicSurface(AbstractThematicSurface ats, CityObject co) {
-        mapAbstractSpaceBoundary(ats, co);
-        parseAndAddMultiSurface(ats.getLod0MultiSurface(), Lod.LOD0, co);
-        parseAndAddMultiSurface(ats.getLod1MultiSurface(), Lod.LOD1, co);
-        parseAndAddMultiSurface(ats.getLod2MultiSurface(), Lod.LOD2, co);
-        parseAndAddMultiSurface(ats.getLod3MultiSurface(), Lod.LOD3, co);
-        parseAndAddMultiSurface(ats.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, co);
-    }
-
-    private void mapAbstractSpaceBoundary(AbstractSpaceBoundary asb, CityObject co) {
-        mapAbstractCityObject(asb, co);
-    }
-
-    private void mapAbstractCityObject(AbstractCityObject aco, CityObject to) {
-        mapAbstractFeatureWithLifespan(aco, to);
-    }
-
-    private void mapAbstractFeatureWithLifespan(AbstractFeatureWithLifespan afwl, CityObject to) {
-        mapAbstractFeature(afwl, to);
-    }
-
-    private void mapAbstractFeature(AbstractFeature af, CityObject to) {
-        mapAbstractGML(af, to);
-    }
-
-    private void mapAbstractGML(AbstractGML ag, CityObject to) {
-        parseId(ag, to);
-    }
-
-    @Override
-    public void visit(Railway railway) {
-        TransportationObject to = new TransportationObject(TransportationType.RAILWAY);
-        mapAbstractTransportationSpace(railway, to);
-        finishTransportationMapping(to);
-    }
-
-    @Override
-    public void visit(Square square) {
-        TransportationObject to = new TransportationObject(TransportationType.SQUARE);
-        mapAbstractTransportationSpace(square, to);
-        finishTransportationMapping(to);
-    }
-
-    @Override
-    public void visit(Track track) {
-        TransportationObject to = new TransportationObject(TransportationType.TRACK);
-        mapAbstractTransportationSpace(track, to);
-        finishTransportationMapping(to);
-    }
-
-    @Override
-    public void visit(TransportationComplex transportationComplex) {
-        TransportationObject to = new TransportationObject(TransportationType.TRANSPORTATION_COMPLEX);
-        mapAbstractTransportationSpace(transportationComplex, to);
-        finishTransportationMapping(to);
-    }
-
-    private void finishTransportationMapping(TransportationObject to) {
-        finishCityObjectConstruction(to);
-        model.addTransportation(to);
-    }
-
-    private void finishCityObjectConstruction(CityObject co) {
-        co.unsetGmlGeometries();
-        resolveAndClearReferences();
-        updateEdgesAndVertices(co);
-    }
-
-    private void mapAbstractTransportationSpace(AbstractTransportationSpace ats, TransportationObject to) {
-        to.setGmlObject(ats);
-        parseAbstractTransportationSpaceGeometries(ats, to);
-        for (TrafficSpaceProperty tsp : ats.getTrafficSpaces()) {
-            if (tsp.isSetObject()) {
-                TransportationObject trafficSpace = new TransportationObject(TransportationType.TRAFFIC_SPACE);
-                mapTrafficSpace(tsp.getObject(), trafficSpace);
-                finishTransportationMapping(trafficSpace);
-                to.getChildren().add(trafficSpace);
-            }
-        }
-        for (AuxiliaryTrafficSpaceProperty auxTrafficProp : ats.getAuxiliaryTrafficSpaces()) {
-            if (auxTrafficProp.isSetObject()) {
-                TransportationObject trafficSpace = parseAuxiliaryTrafficSpace(auxTrafficProp.getObject());
-                finishTransportationMapping(trafficSpace);
-                to.addChild(trafficSpace);
-            }
-        }
-    }
-
-    private void mapTrafficSpace(TrafficSpace ts, TransportationObject to) {
-        mapAbstractUnoccupiedSpace(ts, to);
-    }
-
-    private void mapAbstractUnoccupiedSpace(AbstractUnoccupiedSpace aus, CityObject co) {
-        mapAbstractPhysicalSpace(aus, co);
-    }
-
-    private TransportationObject parseAuxiliaryTrafficSpace(AuxiliaryTrafficSpace ats) {
-        TransportationObject to = new TransportationObject(TransportationType.AUXILLIARY_TRAFFIC_SPACE);
-        parseAbstractSpaceGeometries(ats, to);
-        finishCityObjectConstruction(to);
-        return to;
-    }
-
-    private void parseAbstractTransportationSpaceGeometries(AbstractTransportationSpace space, CityObject co) {
-        parseAndAddMultiSurface(space.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, co);
-        parseAndAddMultiSurface(space.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, co);
-    }
-
-    private void parseAbstractSpaceGeometries(AbstractSpace as, CityObject co) {
-        parseAndAddMultiSurface(as.getLod0MultiSurface(), Lod.LOD0, co);
-        parseAndAddMultiSurface(as.getLod2MultiSurface(), Lod.LOD2, co);
-        parseAndAddMultiSurface(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) {
-        if (msp == null || msp.getObject() == null) {
-            return;
-        }
-        Geometry geom = parseMultiSurface(msp.getObject(), lod);
-        co.addGeometry(geom);
-    }
-
-    private void parseAndAddSolid(SolidProperty sp, Lod lod, CityObject co) {
-        if (sp == null || sp.getObject() == null) {
-            return;
-        }
-        Geometry geom = parseSolid(sp.getObject(), lod);
-        if (geom != null) {
-            co.addGeometry(geom);
-        }
-    }
-
-    private void parseAndAddCompositeSurface(MultiSurfaceProperty ms, Lod lod, CityObject co) {
-        if (ms == null || ms.getObject() == null) {
-            return;
-        }
-        List<SurfaceProperty> 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) {
-                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) {
-                // Hrefs of ImplicitGeometries point to its RelativeGeometry.
-                // Can be ignored for CD since the Href is only used for repeating the RelativeGeometry at different LODs
-            }
-
-
-        }
-
-    }
-
-    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);
-            } else if (aGeom instanceof CompositeSurface cs) {
-                geom = parseCompositeSurface(cs, 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();
-            if (href.startsWith("#")) {
-                href = href.substring(1);
-            }
-            Geometry geom = ref.geometry();
-            CompositeCollection comp = compositeMap.get(href);
-            if (comp != null) {
-                comp.getCompositeMembers().forEach(geom::addPolygon);
-            } else {
-                ConcretePolygon concPoly = polygonMap.get(href);
-                if (concPoly == null) {
-                    if (logger.isWarnEnabled()) {
-                        logger.warn(Localization.getText("FeatureMapper.polygonUnreferenced"), href);
-                    }
-                } else {
-                    LinkedPolygon lPoly = new LinkedPolygon(concPoly, geom);
-                    if (geom.getParent() instanceof BoundarySurface bs) {
-                        lPoly.setPartOfSurface(bs);
-                        if (bs.getParent() instanceof Installation bi) {
-                            lPoly.setPartOfInstallation(bi);
-                        }
-                    }
-                    geom.addPolygon(lPoly);
-                }
-            }
-        }
-
-        // clear storage for polygons and vertices
-        // probably faster than .clear() ?
-        references = new ArrayList<>();
-        vertexMap = new HashMap<>();
-        polygonMap = new HashMap<>();
-    }
-
-    private void readAbstractBuilding(org.citygml4j.core.model.building.AbstractBuilding gmlAb,
-                                      AbstractBuilding cdBuilding) {
-        mapAbstractOccupiedSpace(gmlAb, cdBuilding);
-        cdBuilding.setGmlObject(gmlAb);
-
-        // parse deprecated geometries
-        parseAndAddMultiSurface(gmlAb.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, cdBuilding);
-        parseAndAddMultiSurface(gmlAb.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, cdBuilding);
-        parseAndAddSolid(gmlAb.getDeprecatedProperties().getLod4Solid(), Lod.LOD4, cdBuilding);
-
-        for (BuildingInstallationProperty biProp : gmlAb.getBuildingInstallations()) {
-            var gmlBi = biProp.getObject();
-            if (gmlBi == null) {
-                // ignore empty properties
-                continue;
-            }
-            Installation bi = mapBuildingInstallation(gmlBi);
-            cdBuilding.addBuildingInstallation(bi);
-        }
-
-        for (BuildingRoomProperty brProp : gmlAb.getBuildingRooms()) {
-            var gmlBr = brProp.getObject();
-            if (gmlBr == null) {
-                continue;
-            }
-            BuildingRoom br = mapBuildingRoom(gmlBr);
-            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, compositeMap, vertexMap, config);
-        for (AbstractSpaceBoundaryProperty surfaceProp : gmlAb.getBoundaries()) {
-            if (!surfaceProp.isSetObject()) {
-                continue;
-            }
-            AbstractSpaceBoundary surface = surfaceProp.getObject();
-            surface.accept(surfaceMapper);
-        }
-        updatePartOfSurface(cdBuilding, surfaceMapper);
-
-        cdBuilding.unsetGmlGeometries();
-        resolveAndClearReferences();
-        updateEdgesAndVertices(cdBuilding);
-        for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
-            updateEdgesAndVertices(bs);
-            for (Opening o : bs.getOpenings()) {
-                updateEdgesAndVertices(o);
-            }
-        }
-        for (Installation bi : cdBuilding.getBuildingInstallations()) {
-            updateEdgesAndVertices(bi);
-            for (BoundarySurface bs : bi.getBoundarySurfaces()) {
-                updateEdgesAndVertices(bs);
-                for (Opening o : bs.getOpenings()) {
-                    updateEdgesAndVertices(o);
-                }
-            }
-        }
-        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, compositeMap, 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, compositeMap, 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 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, compositeMap, 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, compositeMap, 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);
-            for (Geometry geom : bs.getGeometries()) {
-                for (Polygon p : geom.getPolygons()) {
-                    p.setPartOfSurface(bs);
-                }
-            }
-        }
-    }
-
-    private Installation mapBuildingInstallation(org.citygml4j.core.model.building.BuildingInstallation gmlBi) {
-        Installation bi = new Installation();
-        bi.setGmlObject(gmlBi);
-        mapAbstractOccupiedSpace(gmlBi, bi);
-        GeometryProperty<?> lod2Prop = gmlBi.getDeprecatedProperties().getLod2Geometry();
-        parseAndAddAbstractGeometry(lod2Prop, Lod.LOD2, bi);
-        GeometryProperty<?> lod3Prop = gmlBi.getDeprecatedProperties().getLod3Geometry();
-        parseAndAddAbstractGeometry(lod3Prop, Lod.LOD3, bi);
-        GeometryProperty<?> lod4Prop = gmlBi.getDeprecatedProperties().getLod4Geometry();
-        parseAndAddAbstractGeometry(lod4Prop, Lod.LOD4, bi);
-        bi.unsetGmlGeometries();
-
-        SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
-        for (AbstractSpaceBoundaryProperty surfaceProp : gmlBi.getBoundaries()) {
-            if (!surfaceProp.isSetObject()) {
-                continue;
-            }
-            AbstractSpaceBoundary surface = surfaceProp.getObject();
-            surface.accept(surfaceMapper);
-        }
-        for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
-            bi.addBoundarySurface(bs);
-            for (Geometry geom : bs.getGeometries()) {
-                for (Polygon p : geom.getPolygons()) {
-                    p.setPartOfSurface(bs);
-                    p.setPartOfInstallation(bi);
-                }
-            }
-        }
-        for (Geometry geom : bi.getGeometries()) {
-            for (Polygon p : geom.getPolygons()) {
-                p.setPartOfInstallation(bi);
-            }
-        }
-
-        return bi;
-    }
-
-    private void parseAndAddAbstractGeometry(GeometryProperty<?> geomProp, Lod lod, CityObject co) {
-        if (geomProp == null || geomProp.getObject() == null) {
-            return;
-        }
-        AbstractGeometry abstractGeometry = geomProp.getObject();
-        if (abstractGeometry instanceof MultiSurface ms) {
-            Geometry geom = parseMultiSurface(ms, lod);
-            co.addGeometry(geom);
-        } else if (abstractGeometry instanceof Solid solid) {
-            Geometry geom = parseSolid(solid, lod);
-            if (geom != null) {
-                co.addGeometry(geom);
-            }
-        } else {
-            logger.warn("Cannot handle geometry type {}, ignoring", abstractGeometry.getClass().getSimpleName());
-        }
-    }
-
-    private Geometry parseSolid(AbstractSolid abstractSolid, Lod lod) {
-        if (abstractSolid instanceof Solid s) {
-            return handleSolidGeometry(s, lod);
-        } else {
-            logger.warn("Cannot handle solid class {}, can only process pure solids",
-                    abstractSolid.getClass().getSimpleName());
-            return null;
-        }
-    }
-
-    private Geometry handleSolidGeometry(Solid solid, Lod lod) {
-        ShellProperty exteriorProperty = solid.getExterior();
-        if (exteriorProperty == null || exteriorProperty.getObject() == null) {
-            logger.warn("Found solid {} without exterior hull, ignoring", solid.getId());
-            return null;
-        }
-        Geometry geom = new Geometry(GeometryType.SOLID, lod);
-        Shell exterior = solid.getExterior().getObject();
-        Citygml3GeometryMapper geometryMapper = new Citygml3GeometryMapper(config, vertexMap);
-        readSurfaceMember(geom, geometryMapper, exterior.getSurfaceMembers());
-        return geom;
-    }
-
-    private Geometry parseMultiSurface(MultiSurface ms, Lod lod) {
-        Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, lod);
-        Citygml3GeometryMapper geometryMapper = new Citygml3GeometryMapper(config, vertexMap);
-        readSurfaceMember(geom, geometryMapper, ms.getSurfaceMember());
-        return geom;
-    }
-
-    private void readSurfaceMember(Geometry geom, Citygml3GeometryMapper geometryMapper,
-                                   List<SurfaceProperty> surfaceMember) {
-        for (SurfaceProperty prop : surfaceMember) {
-            if (prop.getHref() != null) {
-                references.add(new ResolvableReference(prop.getHref(), geom));
-                continue;
-            }
-            if (prop.getObject() != null) {
-                AbstractSurface as = prop.getObject();
-                as.accept(geometryMapper);
-            }
-        }
-
-        List<ConcretePolygon> polygons = geometryMapper.getPolygons();
-
-        for (ConcretePolygon concretePoly : polygons) {
-            geom.addPolygon(concretePoly);
-            if (concretePoly.hasExistingGmlId()) {
-                polygonMap.put(concretePoly.getGmlId().getGmlString(), concretePoly);
-            }
-        }
-    }
-
-    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());
-        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
-        for (Geometry geom : co.getGeometries()) {
-            KDTree tree = new KDTree();
-            for (Polygon poly : geom.getPolygons()) {
-                LinearRing lr = poly.getExteriorRing();
-                updateRing(tree, lr);
-                for (LinearRing innerRing : poly.getInnerRings()) {
-                    updateRing(tree, innerRing);
-                }
-            }
-            if (!config.useLowMemoryConsumption()) {
-                // no low memory consumption mode meaning create all meta information in
-                // geometry
-                geom.updateEdgesAndVertices();
-            }
-        }
-    }
-
-    private void updateRing(KDTree tree, LinearRing lr) {
-        for (int i = 0; i < lr.getVertices().size(); i++) {
-            Vertex v = lr.getVertices().get(i);
-            List<Vertex> nodesInRange = tree.getNodesInRange(v, neighborDistance);
-            if (nodesInRange.isEmpty()) {
-                tree.add(v);
-            } else {
-                // replace other vertex with any neighboring one
-                Vertex original = nodesInRange.get(0);
-                lr.setVertex(i, original);
-            }
-        }
-    }
-
-    public void setCityModel(CityModel cModel) {
-        model.setCityModel(cModel);
-    }
-
-    public CityDoctorModel getModel() {
-        return model;
-    }
-
-    public void setCityGMLVersion(CityGMLVersion cityGMLVersion) {
-        model.setParsedCityGMLVersion(cityGMLVersion);
-    }
+	private static final Logger logger = LogManager.getLogger(Citygml3FeatureMapper.class);
+
+	private final CityDoctorModel model;
+	private final ParserConfiguration config;
+	private final Path directory;
+	private final double neighborDistance;
+	private Map<String, ConcretePolygon> polygonMap = new HashMap<>();
+	private Map<String, CompositeCollection> compositeMap = new HashMap<>();
+	private List<ResolvableReference> references = new ArrayList<>();
+	private Map<Vertex, Vertex> vertexMap = new HashMap<>();
+
+	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());
+	}
+
+	public static void parseId(AbstractGML gml, GmlElement gmlElement) {
+		String id = gml.getId();
+		if (id != null) {
+			gmlElement.setGmlId(new GmlId(id));
+		}
+	}
+
+	@Override
+	public void visit(AbstractSpace space) {
+		// if we are here, an AbstractSpace thing was read that is not handled in the
+		// other methods
+
+	}
+
+	@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);
+	}
+
+	@Override
+	public void visit(org.citygml4j.core.model.generics.GenericOccupiedSpace gos) {
+		GenericCityObject gco = new GenericCityObject();
+		gco.setGmlObject(gos);
+		GeometryProperty<?> agLod1 = gos.getDeprecatedProperties().getLod1Geometry();
+		GeometryProperty<?> agLod4 = gos.getDeprecatedProperties().getLod4Geometry();
+		if (agLod1 != null && agLod1.getObject() != null && agLod1.getObject() instanceof MultiSurface ms) {
+			gco.addGeometry(parseMultiSurface(ms, Lod.LOD1));
+		}
+		if (agLod4 != null && agLod4.getObject() != null) {
+			if (agLod4.getObject() instanceof MultiSurface ms) {
+				gco.addGeometry(parseMultiSurface(ms, Lod.LOD4));
+			} else if (agLod4.getObject() instanceof AbstractSolid solid) {
+				gco.addGeometry(parseSolid(solid, Lod.LOD4));
+			}
+		}
+		mapAbstractOccupiedSpace(gos, gco);
+		resolveAndClearReferences();
+		gco.unsetGmlGeometries();
+		updateEdgesAndVertices(gco);
+		model.addGenericCityObject(gco);
+	}
+
+	@Override
+	public void visit(org.citygml4j.core.model.building.Building gmlBuilding) {
+		Building cdBuilding = new Building();
+		readAbstractBuilding(gmlBuilding, cdBuilding);
+		for (BuildingPartProperty bpProp : gmlBuilding.getBuildingParts()) {
+			if (!bpProp.isSetObject()) {
+				continue;
+			}
+			BuildingPart part = new BuildingPart(cdBuilding);
+			readAbstractBuilding(bpProp.getObject(), part);
+		}
+		model.addBuilding(cdBuilding);
+	}
+
+	@Override
+	public void visit(WaterBody waterBody) {
+		WaterObject wo = new WaterObject();
+		wo.setGmlObject(waterBody);
+		mapAbstractOccupiedSpace(waterBody, wo);
+		parseAndAddMultiSurface(waterBody.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, wo);
+		parseAndAddSolid(waterBody.getDeprecatedProperties().getLod4Solid(), Lod.LOD4, wo);
+
+		SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
+		for (AbstractSpaceBoundaryProperty surfaceProp : waterBody.getBoundaries()) {
+			if (!surfaceProp.isSetObject()) {
+				continue;
+			}
+			AbstractSpaceBoundary surface = surfaceProp.getObject();
+			surface.accept(surfaceMapper);
+		}
+		for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
+			wo.addBoundarySurface(bs);
+		}
+
+		resolveAndClearReferences();
+		wo.unsetGmlGeometries();
+		updateEdgesAndVertices(wo);
+		for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
+			updateEdgesAndVertices(bs);
+		}
+		model.addWater(wo);
+	}
+
+	@Override
+	public void visit(LandUse landUse) {
+		LandObject lo = new LandObject(landUse);
+
+		mapAbstractThematicSurface(landUse, lo);
+
+		finishCityObjectConstruction(lo);
+		model.addLand(lo);
+	}
+
+	@Override
+	public void visit(PlantCover plantCover) {
+		Vegetation veg = new Vegetation(VegetationType.PLANT_COVER);
+		veg.setGmlObject(plantCover);
+		mapAbstractVegetationObject(plantCover, veg);
+
+		parseAndAddMultiSurface(plantCover.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, veg);
+		parseAndAddMultiSurface(plantCover.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, veg);
+
+		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);
+		parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod4Geometry(), Lod.LOD4,
+				veg);
+
+		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) {
+		mapAbstractSpace(aps, co);
+	}
+
+	private void mapAbstractSpace(AbstractSpace as, CityObject co) {
+		mapAbstractFeature(as, co);
+		parseAndAddMultiSurface(as.getLod0MultiSurface(), Lod.LOD0, co);
+		parseAndAddMultiSurface(as.getLod2MultiSurface(), Lod.LOD2, co);
+		parseAndAddMultiSurface(as.getLod3MultiSurface(), Lod.LOD3, co);
+		parseAndAddSolid(as.getLod1Solid(), Lod.LOD1, co);
+		parseAndAddSolid(as.getLod2Solid(), Lod.LOD2, co);
+		parseAndAddSolid(as.getLod3Solid(), Lod.LOD3, co);
+		parseAndAddGenericAttributes(as, co);
+	}
+
+	private void parseAndAddGenericAttributes(AbstractSpace as, CityObject co) {
+		for (AbstractGenericAttributeProperty aga : as.getGenericAttributes()) {
+			co.addGenericAttribute(new GenericAttribute(aga));
+		}
+	}
+
+	@Override
+	public void visit(Bridge bridge) {
+		BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, bridge);
+
+		// parse deprecated geometries
+		parseAndAddMultiSurface(bridge.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, bo);
+		parseAndAddMultiSurface(bridge.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, bo);
+		parseAndAddSolid(bridge.getDeprecatedProperties().getLod4Solid(), Lod.LOD4, bo);
+
+		mapAbstractBridge(bridge, bo);
+		for (BridgePartProperty bPartProperty : bridge.getBridgeParts()) {
+			if (!bPartProperty.isSetObject()) {
+				continue;
+			}
+			BridgePart gmlBridgePart = bPartProperty.getObject();
+			BridgeObject bPart = new BridgeObject(BridgeType.BRIDGE_PART, gmlBridgePart);
+			mapAbstractBridge(gmlBridgePart, bPart);
+			bo.addBridgePart(bPart);
+		}
+		resolveAndClearReferences();
+		updateEdgesAndVertices(bo);
+		for (BridgeObject part : bo.getParts()) {
+			updateEdgesAndVertices(part);
+		}
+		for (BridgeConstructiveElement ele : bo.getConstructiveElements()) {
+			updateEdgesAndVertices(ele);
+			for (BoundarySurface bs : ele.getBoundarySurfaces()) {
+				updateEdgesAndVertices(bs);
+			}
+		}
+		for (Installation bi : bo.getBridgeInstallations()) {
+			updateEdgesAndVertices(bi);
+			for (BoundarySurface bs : bi.getBoundarySurfaces()) {
+				updateEdgesAndVertices(bs);
+			}
+		}
+		model.addBridge(bo);
+	}
+
+	private void mapAbstractBridge(AbstractBridge ab, BridgeObject bo) {
+		mapAbstractConstruction(ab, bo);
+		for (BridgeConstructiveElementProperty eleProp : ab.getBridgeConstructiveElements()) {
+			if (!eleProp.isSetObject()) {
+				continue;
+			}
+			org.citygml4j.core.model.bridge.BridgeConstructiveElement constructiveElement = eleProp.getObject();
+			BridgeConstructiveElement cdEle = new BridgeConstructiveElement(constructiveElement);
+			mapConstructiveElement(constructiveElement, cdEle);
+			bo.addConstructiveElement(cdEle);
+		}
+
+		List<BridgeInstallationProperty> bridgeInstallations = ab.getBridgeInstallations();
+		for (BridgeInstallationProperty installationProp : bridgeInstallations) {
+			var gmlBi = installationProp.getObject();
+			if (gmlBi == null) {
+				// ignore empty properties
+				continue;
+			}
+			Installation bi = mapBridgeInstallation(gmlBi);
+			bo.addBridgeInstallation(bi);
+		}
+
+		SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
+		for (AbstractSpaceBoundaryProperty surfaceProp : ab.getBoundaries()) {
+			if (!surfaceProp.isSetObject()) {
+				continue;
+			}
+			AbstractSpaceBoundary surface = surfaceProp.getObject();
+			surface.accept(surfaceMapper);
+		}
+		updatePartOfSurface(bo, surfaceMapper);
+
+		for (BoundarySurface bs : bo.getBoundarySurfaces()) {
+			updateEdgesAndVertices(bs);
+		}
+
+		bo.unsetGmlGeometries();
+	}
+
+	private Installation mapBridgeInstallation(BridgeInstallation gmlBi) {
+		Installation bi = new Installation();
+		bi.setGmlObject(gmlBi);
+		mapAbstractOccupiedSpace(gmlBi, bi);
+		GeometryProperty<?> lod2Prop = gmlBi.getDeprecatedProperties().getLod2Geometry();
+		parseAndAddAbstractGeometry(lod2Prop, Lod.LOD2, bi);
+		GeometryProperty<?> lod3Prop = gmlBi.getDeprecatedProperties().getLod3Geometry();
+		parseAndAddAbstractGeometry(lod3Prop, Lod.LOD3, bi);
+		GeometryProperty<?> lod4Prop = gmlBi.getDeprecatedProperties().getLod4Geometry();
+		parseAndAddAbstractGeometry(lod4Prop, Lod.LOD4, bi);
+		bi.unsetGmlGeometries();
+
+		SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
+		for (AbstractSpaceBoundaryProperty surfaceProp : gmlBi.getBoundaries()) {
+			if (!surfaceProp.isSetObject()) {
+				continue;
+			}
+			AbstractSpaceBoundary surface = surfaceProp.getObject();
+			surface.accept(surfaceMapper);
+		}
+		for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
+			bi.addBoundarySurface(bs);
+			for (Geometry geom : bs.getGeometries()) {
+				for (Polygon p : geom.getPolygons()) {
+					p.setPartOfSurface(bs);
+					p.setPartOfInstallation(bi);
+				}
+			}
+		}
+		for (Geometry geom : bi.getGeometries()) {
+			for (Polygon p : geom.getPolygons()) {
+				p.setPartOfInstallation(bi);
+			}
+		}
+
+		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);
+
+		parseTunnelInstallations(gmlTunnel, cdTunnel);
+		parseHollowSpaces(gmlTunnel, cdTunnel);
+		parseTunnelFurniture(gmlTunnel, cdTunnel);
+		parseTunnelConstructiveElements(gmlTunnel, cdTunnel);
+		parseTunnelBoundarySurfaces(gmlTunnel);
+		for (Installation ti : cdTunnel.getTunnelInstallations()) {
+			updateEdgesAndVertices(ti);
+			for (BoundarySurface bs : ti.getBoundarySurfaces()) {
+				updateEdgesAndVertices(bs);
+				for (Opening o : bs.getOpenings()) {
+					updateEdgesAndVertices(o);
+				}
+			}
+		}
+		
+		
+		cdTunnel.unsetGmlGeometries();
+		resolveAndClearReferences();
+		updateEdgesAndVertices(cdTunnel);
+	}
+
+	private void parseTunnelBoundarySurfaces(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel) {
+		SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
+		for (AbstractSpaceBoundaryProperty surfaceProp : gmlTunnel.getBoundaries()) {
+			if (!surfaceProp.isSetObject()) {
+				continue;
+			}
+			AbstractSpaceBoundary surface = surfaceProp.getObject();
+			surface.accept(surfaceMapper);
+		}
+		for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
+			updateEdgesAndVertices(bs);
+			for (Opening o : bs.getOpenings()) {
+				updateEdgesAndVertices(o);
+			}
+		}
+	}
+
+	private void parseTunnelConstructiveElements(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel, AbstractTunnel cdTunnel) {
+		for (TunnelConstructiveElementProperty teProp : gmlTunnel.getTunnelConstructiveElements()) {
+			var gmlTc = teProp.getObject();
+			if (gmlTc == null) {
+				continue;
+			}
+			TunnelConstructiveElement tc = mapTunnelConstructiveElement(gmlTc);
+			cdTunnel.addTunnelConstructiveElement(tc);
+		}
+	}
+
+	private void parseTunnelFurniture(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel, AbstractTunnel cdTunnel) {
+		for (TunnelFurnitureProperty tfProp : gmlTunnel.getTunnelFurniture()) {
+			var gmlTf = tfProp.getObject();
+			if (gmlTf == null) {
+				continue;
+			}
+			TunnelFurniture tf = mapTunnelFurniture(gmlTf);
+			cdTunnel.addTunnelFurniture(tf);
+		}
+	}
+
+	private void parseHollowSpaces(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel, AbstractTunnel cdTunnel) {
+		for (HollowSpaceProperty thProp : gmlTunnel.getHollowSpaces()) {
+			var gmlTh = thProp.getObject();
+			if (gmlTh == null) {
+				continue;
+			}
+			TunnelHollow br = mapTunnelHollow(gmlTh);
+			cdTunnel.addTunnelHollow(br);
+		}
+	}
+
+	private void parseTunnelInstallations(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel, AbstractTunnel cdTunnel) {
+		for (TunnelInstallationProperty tiProp : gmlTunnel.getTunnelInstallations()) {
+			var gmlTi = tiProp.getObject();
+			if (gmlTi == null) {
+				// ignore empty properties
+				continue;
+			}
+			Installation ti = mapTunnelInstallation(gmlTi);
+			cdTunnel.addTunnelInstallation(ti);
+		}
+	}
+
+	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, compositeMap, 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, compositeMap, 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, compositeMap, 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 TunnelConstructiveElement mapTunnelConstructiveElement(
+			org.citygml4j.core.model.tunnel.TunnelConstructiveElement gmlTe) {
+		TunnelConstructiveElement te = new TunnelConstructiveElement(gmlTe);
+		mapAbstractOccupiedSpace(gmlTe, te);
+		te.unsetGmlGeometries();
+		SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
+		for (AbstractSpaceBoundaryProperty surfaceProp : gmlTe.getBoundaries()) {
+			if (!surfaceProp.isSetObject()) {
+				continue;
+			}
+			AbstractSpaceBoundary surface = surfaceProp.getObject();
+			surface.accept(surfaceMapper);
+		}
+		for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
+			te.addBoundarySurface(bs);
+			for (Geometry geom : bs.getGeometries()) {
+				for (Polygon p : geom.getPolygons()) {
+					p.setPartOfSurface(bs);
+				}
+			}
+		}
+		return te;
+	}
+
+	private void updatePartOfSurface(BridgeObject bo, SurfaceMapper surfaceMapper) {
+		for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
+			bo.addBoundarySurface(bs);
+			for (Geometry geom : bs.getGeometries()) {
+				for (Polygon p : geom.getPolygons()) {
+					p.setPartOfSurface(bs);
+				}
+			}
+		}
+	}
+
+	private void mapConstructiveElement(org.citygml4j.core.model.bridge.BridgeConstructiveElement ele,
+			BridgeConstructiveElement bce) {
+		mapAbstractConstructiveElement(ele, bce);
+
+		SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
+		for (AbstractSpaceBoundaryProperty surfaceProp : ele.getBoundaries()) {
+			if (!surfaceProp.isSetObject()) {
+				continue;
+			}
+			AbstractSpaceBoundary surface = surfaceProp.getObject();
+			surface.accept(surfaceMapper);
+		}
+		for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
+			bce.addBoundarySurface(bs);
+		}
+	}
+
+	private void mapAbstractConstructiveElement(org.citygml4j.core.model.bridge.BridgeConstructiveElement ele,
+			BridgeConstructiveElement bce) {
+		mapAbstractOccupiedSpace(ele, bce);
+	}
+
+	private void mapAbstractConstruction(AbstractConstruction ac, BridgeObject bo) {
+		mapAbstractOccupiedSpace(ac, bo);
+	}
+
+	@Override
+	public void visit(AuxiliaryTrafficArea ata) {
+		TransportationObject to = new TransportationObject(TransportationType.AUXILLIARY_TRAFFIC_AREA);
+		mapAbstractThematicSurface(ata, to);
+		finishTransportationMapping(to);
+	}
+
+	@Override
+	public void visit(TrafficArea ta) {
+		TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
+		mapAbstractThematicSurface(ta, to);
+		finishTransportationMapping(to);
+	}
+
+	@Override
+	public void visit(Road road) {
+		TransportationObject to = new TransportationObject(TransportationType.ROAD);
+		mapAbstractTransportationSpace(road, to);
+		finishTransportationMapping(to);
+	}
+
+	private void mapAbstractThematicSurface(AbstractThematicSurface ats, CityObject co) {
+		mapAbstractSpaceBoundary(ats, co);
+		parseAndAddMultiSurface(ats.getLod0MultiSurface(), Lod.LOD0, co);
+		parseAndAddMultiSurface(ats.getLod1MultiSurface(), Lod.LOD1, co);
+		parseAndAddMultiSurface(ats.getLod2MultiSurface(), Lod.LOD2, co);
+		parseAndAddMultiSurface(ats.getLod3MultiSurface(), Lod.LOD3, co);
+		parseAndAddMultiSurface(ats.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, co);
+	}
+
+	private void mapAbstractSpaceBoundary(AbstractSpaceBoundary asb, CityObject co) {
+		mapAbstractCityObject(asb, co);
+	}
+
+	private void mapAbstractCityObject(AbstractCityObject aco, CityObject to) {
+		mapAbstractFeatureWithLifespan(aco, to);
+	}
+
+	private void mapAbstractFeatureWithLifespan(AbstractFeatureWithLifespan afwl, CityObject to) {
+		mapAbstractFeature(afwl, to);
+	}
+
+	private void mapAbstractFeature(AbstractFeature af, CityObject to) {
+		mapAbstractGML(af, to);
+	}
+
+	private void mapAbstractGML(AbstractGML ag, CityObject to) {
+		parseId(ag, to);
+	}
+
+	@Override
+	public void visit(Railway railway) {
+		TransportationObject to = new TransportationObject(TransportationType.RAILWAY);
+		mapAbstractTransportationSpace(railway, to);
+		finishTransportationMapping(to);
+	}
+
+	@Override
+	public void visit(Square square) {
+		TransportationObject to = new TransportationObject(TransportationType.SQUARE);
+		mapAbstractTransportationSpace(square, to);
+		finishTransportationMapping(to);
+	}
+
+	@Override
+	public void visit(Track track) {
+		TransportationObject to = new TransportationObject(TransportationType.TRACK);
+		mapAbstractTransportationSpace(track, to);
+		finishTransportationMapping(to);
+	}
+
+	@Override
+	public void visit(TransportationComplex transportationComplex) {
+		TransportationObject to = new TransportationObject(TransportationType.TRANSPORTATION_COMPLEX);
+		mapAbstractTransportationSpace(transportationComplex, to);
+		finishTransportationMapping(to);
+	}
+
+	private void finishTransportationMapping(TransportationObject to) {
+		finishCityObjectConstruction(to);
+		model.addTransportation(to);
+	}
+
+	private void finishCityObjectConstruction(CityObject co) {
+		co.unsetGmlGeometries();
+		resolveAndClearReferences();
+		updateEdgesAndVertices(co);
+	}
+
+	private void mapAbstractTransportationSpace(AbstractTransportationSpace ats, TransportationObject to) {
+		to.setGmlObject(ats);
+		parseAbstractTransportationSpaceGeometries(ats, to);
+		for (TrafficSpaceProperty tsp : ats.getTrafficSpaces()) {
+			if (tsp.isSetObject()) {
+				TransportationObject trafficSpace = new TransportationObject(TransportationType.TRAFFIC_SPACE);
+				mapTrafficSpace(tsp.getObject(), trafficSpace);
+				finishTransportationMapping(trafficSpace);
+				to.getChildren().add(trafficSpace);
+			}
+		}
+		for (AuxiliaryTrafficSpaceProperty auxTrafficProp : ats.getAuxiliaryTrafficSpaces()) {
+			if (auxTrafficProp.isSetObject()) {
+				TransportationObject trafficSpace = parseAuxiliaryTrafficSpace(auxTrafficProp.getObject());
+				finishTransportationMapping(trafficSpace);
+				to.addChild(trafficSpace);
+			}
+		}
+	}
+
+	private void mapTrafficSpace(TrafficSpace ts, TransportationObject to) {
+		mapAbstractUnoccupiedSpace(ts, to);
+	}
+
+	private void mapAbstractUnoccupiedSpace(AbstractUnoccupiedSpace aus, CityObject co) {
+		mapAbstractPhysicalSpace(aus, co);
+	}
+
+	private TransportationObject parseAuxiliaryTrafficSpace(AuxiliaryTrafficSpace ats) {
+		TransportationObject to = new TransportationObject(TransportationType.AUXILLIARY_TRAFFIC_SPACE);
+		parseAbstractSpaceGeometries(ats, to);
+		finishCityObjectConstruction(to);
+		return to;
+	}
+
+	private void parseAbstractTransportationSpaceGeometries(AbstractTransportationSpace space, CityObject co) {
+		parseAndAddMultiSurface(space.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, co);
+		parseAndAddMultiSurface(space.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, co);
+	}
+
+	private void parseAbstractSpaceGeometries(AbstractSpace as, CityObject co) {
+		parseAndAddMultiSurface(as.getLod0MultiSurface(), Lod.LOD0, co);
+		parseAndAddMultiSurface(as.getLod2MultiSurface(), Lod.LOD2, co);
+		parseAndAddMultiSurface(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) {
+		if (msp == null || msp.getObject() == null) {
+			return;
+		}
+		Geometry geom = parseMultiSurface(msp.getObject(), lod);
+		co.addGeometry(geom);
+	}
+
+	private void parseAndAddSolid(SolidProperty sp, Lod lod, CityObject co) {
+		if (sp == null || sp.getObject() == null) {
+			return;
+		}
+		Geometry geom = parseSolid(sp.getObject(), 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) {
+				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) {
+				// Hrefs of ImplicitGeometries point to its RelativeGeometry.
+				// Can be ignored for CD since the Href is only used for repeating the
+				// RelativeGeometry at different LODs
+			}
+
+		}
+
+	}
+
+	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);
+			} else if (aGeom instanceof CompositeSurface cs) {
+				geom = parseCompositeSurface(cs, lod);
+			}
+			if (geom != null) {
+				relGeo = RelativeGeometry.of(geom);
+				igh = new ImplicitGeometryHolder(ig, relGeo);
+			}
+
+		} else {
+			logger.error("Implicit geometry of GML-ID {} has no referenced geometry.", ig.getId());
+		}
+		return igh;
+	}
+
+	private void resolveAndClearReferences() {
+		for (ResolvableReference ref : references) {
+			String href = ref.href();
+			if (href.startsWith("#")) {
+				href = href.substring(1);
+			}
+			Geometry geom = ref.geometry();
+			CompositeCollection comp = compositeMap.get(href);
+			if (comp != null) {
+				// composite collection, add each containing polygon to the geometry
+				comp.getCompositeMembers().forEach(geom::addPolygon);
+			} else {
+				handlePolygonReference(href, geom);
+			}
+		}
+
+		// clear storage for polygons and vertices
+		references = new ArrayList<>();
+		vertexMap = new HashMap<>();
+		polygonMap = new HashMap<>();
+	}
+
+	private void handlePolygonReference(String href, Geometry geom) {
+		ConcretePolygon concPoly = polygonMap.get(href);
+		if (concPoly == null) {
+			if (logger.isWarnEnabled()) {
+				logger.warn(Localization.getText("FeatureMapper.polygonUnreferenced"), href);
+			}
+		} else {
+			LinkedPolygon lPoly = new LinkedPolygon(concPoly, geom);
+			if (geom.getParent() instanceof BoundarySurface bs) {
+				lPoly.setPartOfSurface(bs);
+				if (bs.getParent() instanceof Installation bi) {
+					lPoly.setPartOfInstallation(bi);
+				}
+			}
+			geom.addPolygon(lPoly);
+		}
+	}
+
+	private void readAbstractBuilding(org.citygml4j.core.model.building.AbstractBuilding gmlAb,
+			AbstractBuilding cdBuilding) {
+		mapAbstractOccupiedSpace(gmlAb, cdBuilding);
+		cdBuilding.setGmlObject(gmlAb);
+
+		// parse deprecated geometries
+		parseAndAddMultiSurface(gmlAb.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, cdBuilding);
+		parseAndAddMultiSurface(gmlAb.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, cdBuilding);
+		parseAndAddSolid(gmlAb.getDeprecatedProperties().getLod4Solid(), Lod.LOD4, cdBuilding);
+
+		parseBuildingInstallations(gmlAb, cdBuilding);
+		parseBuildingRooms(gmlAb, cdBuilding);
+		parseBuildingSubdivisions(gmlAb, cdBuilding);
+		parseBoundarySurfaces(gmlAb, cdBuilding);
+		parseBuildingFurniture(gmlAb, cdBuilding);
+	}
+
+	private void parseBuildingFurniture(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
+		for (BuildingFurnitureProperty bfProp : gmlAb.getBuildingFurniture()) {
+			var gmlBf = bfProp.getObject();
+			if (gmlBf == null) {
+				continue;
+			}
+			BuildingRoomFurniture bf = mapBuildingFurniture(gmlBf);
+			cdBuilding.addBuildingRoomFurniture(bf);
+		}
+	}
+
+	private void parseBoundarySurfaces(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
+		SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
+		for (AbstractSpaceBoundaryProperty surfaceProp : gmlAb.getBoundaries()) {
+			if (!surfaceProp.isSetObject()) {
+				continue;
+			}
+			AbstractSpaceBoundary surface = surfaceProp.getObject();
+			surface.accept(surfaceMapper);
+		}
+		updatePartOfSurface(cdBuilding, surfaceMapper);
+
+		cdBuilding.unsetGmlGeometries();
+		resolveAndClearReferences();
+		updateEdgesAndVertices(cdBuilding);
+		for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
+			updateEdgesAndVertices(bs);
+			for (Opening o : bs.getOpenings()) {
+				updateEdgesAndVertices(o);
+			}
+		}
+	}
+
+	private void parseBuildingSubdivisions(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
+		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);
+			}
+		}
+	}
+
+	private void parseBuildingRooms(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
+		for (BuildingRoomProperty brProp : gmlAb.getBuildingRooms()) {
+			var gmlBr = brProp.getObject();
+			if (gmlBr == null) {
+				continue;
+			}
+			BuildingRoom br = mapBuildingRoom(gmlBr);
+			cdBuilding.addBuildingRoom(br);
+		}
+	}
+
+	private void parseBuildingInstallations(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
+		for (BuildingInstallationProperty biProp : gmlAb.getBuildingInstallations()) {
+			var gmlBi = biProp.getObject();
+			if (gmlBi == null) {
+				// ignore empty properties
+				continue;
+			}
+			Installation bi = mapBuildingInstallation(gmlBi);
+			cdBuilding.addBuildingInstallation(bi);
+		}
+		for (Installation bi : cdBuilding.getBuildingInstallations()) {
+			updateEdgesAndVertices(bi);
+			for (BoundarySurface bs : bi.getBoundarySurfaces()) {
+				updateEdgesAndVertices(bs);
+				for (Opening o : bs.getOpenings()) {
+					updateEdgesAndVertices(o);
+				}
+			}
+		}
+	}
+
+	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, compositeMap, 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, compositeMap, 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 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, compositeMap, 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, compositeMap, 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);
+			for (Geometry geom : bs.getGeometries()) {
+				for (Polygon p : geom.getPolygons()) {
+					p.setPartOfSurface(bs);
+				}
+			}
+		}
+	}
+
+	private Installation mapBuildingInstallation(org.citygml4j.core.model.building.BuildingInstallation gmlBi) {
+		Installation bi = new Installation();
+		bi.setGmlObject(gmlBi);
+		mapAbstractOccupiedSpace(gmlBi, bi);
+		GeometryProperty<?> lod2Prop = gmlBi.getDeprecatedProperties().getLod2Geometry();
+		parseAndAddAbstractGeometry(lod2Prop, Lod.LOD2, bi);
+		GeometryProperty<?> lod3Prop = gmlBi.getDeprecatedProperties().getLod3Geometry();
+		parseAndAddAbstractGeometry(lod3Prop, Lod.LOD3, bi);
+		GeometryProperty<?> lod4Prop = gmlBi.getDeprecatedProperties().getLod4Geometry();
+		parseAndAddAbstractGeometry(lod4Prop, Lod.LOD4, bi);
+		bi.unsetGmlGeometries();
+
+		SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
+		for (AbstractSpaceBoundaryProperty surfaceProp : gmlBi.getBoundaries()) {
+			if (!surfaceProp.isSetObject()) {
+				continue;
+			}
+			AbstractSpaceBoundary surface = surfaceProp.getObject();
+			surface.accept(surfaceMapper);
+		}
+		for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
+			bi.addBoundarySurface(bs);
+			for (Geometry geom : bs.getGeometries()) {
+				for (Polygon p : geom.getPolygons()) {
+					p.setPartOfSurface(bs);
+					p.setPartOfInstallation(bi);
+				}
+			}
+		}
+		for (Geometry geom : bi.getGeometries()) {
+			for (Polygon p : geom.getPolygons()) {
+				p.setPartOfInstallation(bi);
+			}
+		}
+
+		return bi;
+	}
+
+	private void parseAndAddAbstractGeometry(GeometryProperty<?> geomProp, Lod lod, CityObject co) {
+		if (geomProp == null || geomProp.getObject() == null) {
+			return;
+		}
+		AbstractGeometry abstractGeometry = geomProp.getObject();
+		if (abstractGeometry instanceof MultiSurface ms) {
+			Geometry geom = parseMultiSurface(ms, lod);
+			co.addGeometry(geom);
+		} else if (abstractGeometry instanceof Solid solid) {
+			Geometry geom = parseSolid(solid, lod);
+			if (geom != null) {
+				co.addGeometry(geom);
+			}
+		} else {
+			logger.warn("Cannot handle geometry type {}, ignoring", abstractGeometry.getClass().getSimpleName());
+		}
+	}
+
+	private Geometry parseSolid(AbstractSolid abstractSolid, Lod lod) {
+		if (abstractSolid instanceof Solid s) {
+			return handleSolidGeometry(s, lod);
+		} else {
+			logger.warn("Cannot handle solid class {}, can only process pure solids",
+					abstractSolid.getClass().getSimpleName());
+			return null;
+		}
+	}
+
+	private Geometry handleSolidGeometry(Solid solid, Lod lod) {
+		ShellProperty exteriorProperty = solid.getExterior();
+		if (exteriorProperty == null || exteriorProperty.getObject() == null) {
+			logger.warn("Found solid {} without exterior hull, ignoring", solid.getId());
+			return null;
+		}
+		Geometry geom = new Geometry(GeometryType.SOLID, lod);
+		Shell exterior = solid.getExterior().getObject();
+		Citygml3GeometryMapper geometryMapper = new Citygml3GeometryMapper(config, vertexMap);
+		readSurfaceMember(geom, geometryMapper, exterior.getSurfaceMembers());
+		return geom;
+	}
+
+	private Geometry parseMultiSurface(MultiSurface ms, Lod lod) {
+		Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, lod);
+		Citygml3GeometryMapper geometryMapper = new Citygml3GeometryMapper(config, vertexMap);
+		readSurfaceMember(geom, geometryMapper, ms.getSurfaceMember());
+		return geom;
+	}
+
+	private void readSurfaceMember(Geometry geom, Citygml3GeometryMapper geometryMapper,
+			List<SurfaceProperty> surfaceMember) {
+		for (SurfaceProperty prop : surfaceMember) {
+			if (prop.getHref() != null) {
+				references.add(new ResolvableReference(prop.getHref(), geom));
+				continue;
+			}
+			if (prop.getObject() != null) {
+				AbstractSurface as = prop.getObject();
+				as.accept(geometryMapper);
+			}
+		}
+
+		List<ConcretePolygon> polygons = geometryMapper.getPolygons();
+
+		for (ConcretePolygon concretePoly : polygons) {
+			geom.addPolygon(concretePoly);
+			if (concretePoly.hasExistingGmlId()) {
+				polygonMap.put(concretePoly.getGmlId().getGmlString(), concretePoly);
+			}
+		}
+	}
+
+	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());
+		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
+		for (Geometry geom : co.getGeometries()) {
+			KDTree tree = new KDTree();
+			for (Polygon poly : geom.getPolygons()) {
+				LinearRing lr = poly.getExteriorRing();
+				updateRing(tree, lr);
+				for (LinearRing innerRing : poly.getInnerRings()) {
+					updateRing(tree, innerRing);
+				}
+			}
+			if (!config.useLowMemoryConsumption()) {
+				// no low memory consumption mode meaning create all meta information in
+				// geometry
+				geom.updateEdgesAndVertices();
+			}
+		}
+	}
+
+	private void updateRing(KDTree tree, LinearRing lr) {
+		for (int i = 0; i < lr.getVertices().size(); i++) {
+			Vertex v = lr.getVertices().get(i);
+			List<Vertex> nodesInRange = tree.getNodesInRange(v, neighborDistance);
+			if (nodesInRange.isEmpty()) {
+				tree.add(v);
+			} else {
+				// replace other vertex with any neighboring one
+				Vertex original = nodesInRange.get(0);
+				lr.setVertex(i, original);
+			}
+		}
+	}
+
+	public void setCityModel(CityModel cModel) {
+		model.setCityModel(cModel);
+	}
+
+	public CityDoctorModel getModel() {
+		return model;
+	}
+
+	public void setCityGMLVersion(CityGMLVersion cityGMLVersion) {
+		model.setParsedCityGMLVersion(cityGMLVersion);
+	}
 }
diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3GeometryMapper.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3GeometryMapper.java
index d65b32cefa624e1baca7e9238f3371792dbcb6bb..10fc35331dc3364ecf21a484721b2cb583895bde 100644
--- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3GeometryMapper.java
+++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3GeometryMapper.java
@@ -59,7 +59,7 @@ public class Citygml3GeometryMapper extends GeometryWalker {
     public void visit(Polygon polygon) {
         parsePolygon(polygon.getId(), polygon.getExterior(), polygon.getInterior());
         if (polygon.getExterior() == null) {
-            logger.warn(String.format("No exterior: %s", polygon.getId()));
+            logger.warn("No exterior: {}", polygon.getId());
         }
     }
 
diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/ProjectionAxis.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/ProjectionAxis.java
index 15e0494f12c59a571871185d988f3f7b9c12b61e..afbac56ac0e731c025ce61fc0f5b55d6e1321aa7 100644
--- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/ProjectionAxis.java
+++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/math/ProjectionAxis.java
@@ -34,7 +34,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
  *
  */
 public class ProjectionAxis {
-	
+
 	private static final String DIVISOR_IS_0 = "Divisor is 0";
 
 	private final int[] axis;
@@ -87,20 +87,36 @@ public class ProjectionAxis {
 		this.axis = axis;
 	}
 
+	/**
+	 * This will write the two coordinates determined in this projection axis into a
+	 * given array at the startIndex location. The array has to have at least a
+	 * length of startIndex + 1.
+	 * 
+	 * @param v          the vector from which the coordinates are taken.
+	 * @param array      the array that is written to.
+	 * @param startIndex the start location in the array.
+	 */
+	public void writeCoordinatesOfVectorInArray(Vector3d v, double[] array, int startIndex) {
+		array[startIndex] = v.getCoordinate(axis[0]);
+		array[startIndex + 1] = v.getCoordinate(axis[1]);
+	}
+
 	public Vector2d project(Vector3d v) {
 		return new Vector2d(v.getCoordinate(axis[0]), v.getCoordinate(axis[1]));
 	}
 
 	/**
 	 * calculates the missing coordinate for 3d vector from the plane and this axis.
+	 * 
 	 * @return the projected 3d point.
 	 */
 	public Vector3d projectToPlane(Plane plane, Vector2d v) {
 		return projectToPlane(plane, v.getX(), v.getY());
 	}
-	
+
 	/**
 	 * calculates the missing coordinate for 3d vector from the plane and this axis.
+	 * 
 	 * @return the projected 3d point.
 	 */
 	public Vector3d projectToPlane(Plane plane, double vectorX, double vectorY) {
diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/EarcutTesselator.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/EarcutTesselator.java
index c160e081fb1a8e8c0f350cdb249c8a2a15814df2..acfc90f753a0b01a18f94ff71b0664389d4e5962 100644
--- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/EarcutTesselator.java
+++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/tesselation/EarcutTesselator.java
@@ -6,10 +6,8 @@ import java.util.List;
 import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
 import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
 import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
-import de.hft.stuttgart.citydoctor2.math.Polygon2d;
-import de.hft.stuttgart.citydoctor2.math.Ring2d;
+import de.hft.stuttgart.citydoctor2.math.ProjectionAxis;
 import de.hft.stuttgart.citydoctor2.math.Triangle3d;
-import de.hft.stuttgart.citydoctor2.math.Vector2d;
 import earcut4j.Earcut;
 
 /**
@@ -35,8 +33,8 @@ public class EarcutTesselator {
 			}
 		}
 		// collect vertices
-		// project polygon to 2D space as library is buggy for more than 2 dimensions
-		Polygon2d projectedPolygon = Polygon2d.withProjection(p);
+		// find most dominant projection axis for 2d projection
+		ProjectionAxis axis = ProjectionAxis.of(p);
 		
 		double[] vertices = new double[nrOfVertices * 2];
 		List<Vertex> vertexObjects = new ArrayList<>();
@@ -45,9 +43,10 @@ public class EarcutTesselator {
 			addVerticesToList(innerRing, vertexObjects);
 		}
 		
-		int start = addRingToArray(projectedPolygon.getExterior(), vertices, 0);
-		for (Ring2d innerRing : projectedPolygon.getInteriorRings()) {
-			start = addRingToArray(innerRing, vertices, start);
+		// write the vector data according to the projection axis into the array
+		int start = addRingToArray(p.getExteriorRing(), vertices, 0, axis);
+		for (LinearRing innerRing : p.getInnerRings()) {
+			start = addRingToArray(innerRing, vertices, start, axis);
 		}
 		
 		// triangulation
@@ -73,12 +72,12 @@ public class EarcutTesselator {
 		}
 	}
 
-	private static int addRingToArray(Ring2d ring, double[] vertices, int start) {
-		List<Vector2d> ringVertices = ring.getVertices();
+	private static int addRingToArray(LinearRing ring, double[] vertices, int start, ProjectionAxis axis) {
+		List<Vertex> ringVertices = ring.getVertices();
 		for (int i = 0; i < ringVertices.size() - 1; i++) {
-			Vector2d v = ringVertices.get(i);
-			vertices[start++] = v.getX();
-			vertices[start++] = v.getY();
+			Vertex v = ringVertices.get(i);
+			axis.writeCoordinatesOfVectorInArray(v, vertices, start);
+			start = start + 2;
 		}
 		return start;
 	}
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 00c75d547d02c7e812e1282eff3ef345a774a0bd..4069621e5d79b0386bb5f9d4885ef4243b1d482a 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
@@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.citygml4j.core.model.CityGMLVersion;
 import org.citygml4j.core.model.core.AbstractCityObjectProperty;
 import org.citygml4j.core.model.core.AbstractFeatureProperty;
 import org.citygml4j.core.model.core.CityModel;
diff --git a/CityDoctorParent/CityDoctorValidation/pom.xml b/CityDoctorParent/CityDoctorValidation/pom.xml
index ed95f448d5302eb71d74935545b7163fb2319c19..cc2370edd236c90e3e0ef516e3099e811bd68821 100644
--- a/CityDoctorParent/CityDoctorValidation/pom.xml
+++ b/CityDoctorParent/CityDoctorValidation/pom.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0">
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0">
 	<modelVersion>4.0.0</modelVersion>
 	<parent>
 		<groupId>de.hft.stuttgart</groupId>
@@ -65,11 +65,11 @@
 			<groupId>net.sf.saxon</groupId>
 			<artifactId>Saxon-HE</artifactId>
 		</dependency>
-        <dependency>
-            <groupId>org.yaml</groupId>
-            <artifactId>snakeyaml</artifactId>
-        </dependency>
-    </dependencies>
+		<dependency>
+			<groupId>org.yaml</groupId>
+			<artifactId>snakeyaml</artifactId>
+		</dependency>
+	</dependencies>
 	<build>
 		<resources>
 			<resource>
diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java
index 2e33ed82d319293fbc67ffcd3472e71b41311d33..0d5e778e7fc4895c30bb9e599c7a09fd6bc315ac 100644
--- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java
+++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java
@@ -423,6 +423,7 @@ public class Checker {
 		}
 	}
 
+	@SuppressWarnings("resource")
 	public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config,
 			InputStream in) {
 		if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) {
diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java
index f4d88c2f544ade9917c1fc7abfd2649a4bf292f0..78189eb9d156cec92beec11ffed6d5d9552f967b 100644
--- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java
+++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java
@@ -65,23 +65,19 @@ public class SvrlContentHandler implements ContentHandler {
 	public void setDocumentLocator(Locator locator) {
 		// not needed
 	}
-	@SuppressWarnings("RedundantThrows")
 	@Override
 	public void startDocument() throws SAXException {
 		// not needed
 	}
 
-	@SuppressWarnings("RedundantThrows")
     @Override
 	public void endDocument() throws SAXException {
 		// not needed
 	}
-	@SuppressWarnings("RedundantThrows")
 	@Override
 	public void startPrefixMapping(String prefix, String uri) throws SAXException {
 		// not needed
 	}
-	@SuppressWarnings("RedundantThrows")
 	@Override
 	public void endPrefixMapping(String prefix) throws SAXException {
 		// not needed
@@ -133,17 +129,14 @@ public class SvrlContentHandler implements ContentHandler {
 			buffer.append(ch, start, length);
 		}
 	}
-	@SuppressWarnings("RedundantThrows")
 	@Override
 	public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
 		// not needed
 	}
-	@SuppressWarnings("RedundantThrows")
 	@Override
 	public void processingInstruction(String target, String data) throws SAXException {
 		// not needed
 	}
-	@SuppressWarnings("RedundantThrows")
 	@Override
 	public void skippedEntity(String name) throws SAXException {
 		// not needed
diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java
index 01162cd05bbf9aa5b8fafec6b81721ad89b3f5bd..0eefd3498e4f95f4b290be5e3c193252e5eee90e 100644
--- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java
+++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheck.java
@@ -76,7 +76,6 @@ public class SolidSelfIntCheck extends Check {
 		}
 		CheckResult cr;
 		List<PolygonIntersection> intersections = SelfIntersectionUtil.calculateSolidSelfIntersection(g);
-//		List<PolygonIntersection> intersections = SelfIntersectionUtil.doesSolidSelfIntersect2(g);
 		if (intersections.isEmpty()) {
 			cr = new CheckResult(this, ResultStatus.OK, null);
 		} else {
diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckFalsePositiveBigMeshTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckFalsePositiveBigMeshTest.java
index d83aa851815932aa5e1a8ccbaf2329f2548eb8c5..d79fed452bec668bfba03968c8bb20f24ddfde8a 100644
--- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckFalsePositiveBigMeshTest.java
+++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckFalsePositiveBigMeshTest.java
@@ -36,7 +36,6 @@ public class SolidSelfIntCheckFalsePositiveBigMeshTest {
 		Checker c = new Checker(config, m);
 		c.runChecks();
 		Building building = m.getBuildings().get(0);
-		System.out.println(building.containsAnyError());
 		/*
 		 *  The examples have no actual self-intersections, but can contain other actual model defects.
 		 *  If an error is detected, it is thus required to check if the
diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java
index 1aacfc900ce5858f1f0c9c4b7906aca741c2d097..847de23fb231a38b6160cf06f45a707edbc3a475 100644
--- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java
+++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/SolidSelfIntCheckTest.java
@@ -86,7 +86,6 @@ public class SolidSelfIntCheckTest {
 		Checker c = new Checker(config, m);
 		c.runChecks();
 		Building building = m.getBuildings().get(0);
-		System.out.println(building.containsAnyError());
 		/*
 		 *  The examples have no actual self-intersections, but can contain other actual model defects.
 		 *  If an error is detected, it is thus required to check if the
diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/util/GeometryTestUtils.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/util/GeometryTestUtils.java
index 9a8e56ba8c110f6c8334a8d0c3a127924850d329..0c4db51a209d6003b4d4923dbbd20d7a26ca1ac7 100644
--- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/util/GeometryTestUtils.java
+++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/util/GeometryTestUtils.java
@@ -21,7 +21,6 @@ package de.hft.stuttgart.citydoctor2.checks.util;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.StringJoiner;
 
 import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
 import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldVertexSystemTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldVertexSystemTest.java
index e6adcf3808f9b7de1e14f609afccb702ef4b18b9..127745410d707602a3238b97d3f377516ed9bb0e 100644
--- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldVertexSystemTest.java
+++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/systemtest/NonManifoldVertexSystemTest.java
@@ -45,7 +45,6 @@ public class NonManifoldVertexSystemTest {
 		CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml");
 		Geometry g = c.getBuildings().get(0).getGeometries().get(0);
 		CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NON_MANIFOLD_VERTEX);
-		System.out.println(cr);
 		assertEquals(ResultStatus.OK, cr.getResultStatus());
 	}
 	
diff --git a/CityDoctorParent/Extensions/CityDoctorAutoPro/pom.xml b/CityDoctorParent/Extensions/CityDoctorAutoPro/pom.xml
index 0961ad7b8239efb00392f675c80464911b3292c0..f2045edde1a531b854b1deae451297d35e6bd60f 100644
--- a/CityDoctorParent/Extensions/CityDoctorAutoPro/pom.xml
+++ b/CityDoctorParent/Extensions/CityDoctorAutoPro/pom.xml
@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<parent>
 		<groupId>de.hft.stuttgart</groupId>
diff --git a/CityDoctorParent/Extensions/CityDoctorGUI/pom.xml b/CityDoctorParent/Extensions/CityDoctorGUI/pom.xml
index 2d70d35792a929a7e2e7f9e897431b5f14b70fc1..150b533eaa61840c2b4fd88a569985917359dfcb 100644
--- a/CityDoctorParent/Extensions/CityDoctorGUI/pom.xml
+++ b/CityDoctorParent/Extensions/CityDoctorGUI/pom.xml
@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<parent>
 		<groupId>de.hft.stuttgart</groupId>
@@ -45,6 +47,18 @@
 			<groupId>org.apache.logging.log4j</groupId>
 			<artifactId>log4j-slf4j18-impl</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>org.openjfx</groupId>
+			<artifactId>javafx-controls</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.openjfx</groupId>
+			<artifactId>javafx-fxml</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.openjfx</groupId>
+			<artifactId>javafx-swing</artifactId>
+		</dependency>
 	</dependencies>
 	<profiles>
 		<profile>
@@ -68,9 +82,11 @@
 									<goal>wget</goal>
 								</goals>
 								<configuration>
-									<uri>https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-windows-amd64-full.zip</uri>
+									<uri>
+										https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-windows-amd64-full.zip</uri>
 									<unpack>false</unpack>
-									<outputDirectory>${project.build.directory}/jre/jre-win</outputDirectory>
+									<outputDirectory>
+										${project.build.directory}/jre/jre-win</outputDirectory>
 									<outputFileName>win-runtime.zip</outputFileName>
 								</configuration>
 							</execution>
@@ -81,9 +97,11 @@
 									<goal>wget</goal>
 								</goals>
 								<configuration>
-									<uri>https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-linux-amd64-full.tar.gz</uri>
+									<uri>
+										https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-linux-amd64-full.tar.gz</uri>
 									<unpack>false</unpack>
-									<outputDirectory>${project.build.directory}/jre/jre-lin</outputDirectory>
+									<outputDirectory>
+										${project.build.directory}/jre/jre-lin</outputDirectory>
 									<outputFileName>lin-runtime.tar.gz</outputFileName>
 								</configuration>
 							</execution>
@@ -94,9 +112,11 @@
 									<goal>wget</goal>
 								</goals>
 								<configuration>
-									<uri>https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-macos-amd64-full.zip</uri>
+									<uri>
+										https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-macos-amd64-full.zip</uri>
 									<unpack>false</unpack>
-									<outputDirectory>${project.build.directory}/jre/jre-mac</outputDirectory>
+									<outputDirectory>
+										${project.build.directory}/jre/jre-mac</outputDirectory>
 									<outputFileName>mac-runtime.zip</outputFileName>
 								</configuration>
 							</execution>
@@ -112,9 +132,16 @@
 								<phase>install</phase>
 								<configuration>
 									<target name="unpack">
-										<untar src="${project.build.directory}/jre/jre-lin/lin-runtime.tar.gz" dest="${project.build.directory}/jre/jre-lin/runtime" compression="gzip"></untar>
-										<unzip src="${project.build.directory}/jre/jre-win/win-runtime.zip" dest="${project.build.directory}/jre/jre-win/runtime"></unzip>
-										<unzip src="${project.build.directory}/jre/jre-mac/mac-runtime.zip" dest="${project.build.directory}/jre/jre-mac/runtime"></unzip>
+										<untar
+											src="${project.build.directory}/jre/jre-lin/lin-runtime.tar.gz"
+											dest="${project.build.directory}/jre/jre-lin/runtime"
+											compression="gzip"></untar>
+										<unzip
+											src="${project.build.directory}/jre/jre-win/win-runtime.zip"
+											dest="${project.build.directory}/jre/jre-win/runtime"></unzip>
+										<unzip
+											src="${project.build.directory}/jre/jre-mac/mac-runtime.zip"
+											dest="${project.build.directory}/jre/jre-mac/runtime"></unzip>
 									</target>
 								</configuration>
 								<goals>
@@ -136,9 +163,11 @@
 									<goal>single</goal>
 								</goals>
 								<configuration>
-									<finalName>${project.artifactId}-${project.version}-no-runtime</finalName>
+									<finalName>
+										${project.artifactId}-${project.version}-no-runtime</finalName>
 									<descriptors>
-										<descriptor>${project.basedir}/src/assembly/no_runtime/assembly.xml</descriptor>
+										<descriptor>
+											${project.basedir}/src/assembly/no_runtime/assembly.xml</descriptor>
 									</descriptors>
 								</configuration>
 							</execution>
@@ -149,9 +178,11 @@
 									<goal>single</goal>
 								</goals>
 								<configuration>
-									<finalName>${project.artifactId}-${project.version}-win</finalName>
+									<finalName>
+										${project.artifactId}-${project.version}-win</finalName>
 									<descriptors>
-										<descriptor>${project.basedir}/src/assembly/win/assembly.xml</descriptor>
+										<descriptor>
+											${project.basedir}/src/assembly/win/assembly.xml</descriptor>
 									</descriptors>
 								</configuration>
 							</execution>
@@ -162,9 +193,11 @@
 									<goal>single</goal>
 								</goals>
 								<configuration>
-									<finalName>${project.artifactId}-${project.version}-lin</finalName>
+									<finalName>
+										${project.artifactId}-${project.version}-lin</finalName>
 									<descriptors>
-										<descriptor>${project.basedir}/src/assembly/lin/assembly.xml</descriptor>
+										<descriptor>
+											${project.basedir}/src/assembly/lin/assembly.xml</descriptor>
 									</descriptors>
 								</configuration>
 							</execution>
@@ -175,9 +208,11 @@
 									<goal>single</goal>
 								</goals>
 								<configuration>
-									<finalName>${project.artifactId}-${project.version}-mac</finalName>
+									<finalName>
+										${project.artifactId}-${project.version}-mac</finalName>
 									<descriptors>
-										<descriptor>${project.basedir}/src/assembly/mac/assembly.xml</descriptor>
+										<descriptor>
+											${project.basedir}/src/assembly/mac/assembly.xml</descriptor>
 									</descriptors>
 								</configuration>
 							</execution>
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 0f7be6b2786e07565c4eb4ad55602923562ac68e..9b159e725e07516f13e6315b6adb6b32d5c425dc 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
@@ -160,6 +160,7 @@ public class CityDoctorController {
         buildLand(model);
         buildCityFurniture(model);
         buildOtherCityObjects(model);
+        buildTunnel(model.getTunnels());
     }
 
     private void resetFeatureChunks() {
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
index 6571db89fe6aa37a85a5359961ea51fc7007410e..28641a6638ee16922f25016953ea54e4e8d57cd3 100644
--- 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
@@ -1,6 +1,5 @@
 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;
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 ece119e4a02d62c0516e3c6546df696b9b842ae6..43517fea045cf4a6ee7b8a692946b226f9483d91 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
@@ -4,7 +4,7 @@
 <?import javafx.scene.control.*?>
 <?import javafx.scene.image.*?>
 <?import javafx.scene.layout.*?>
-<HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" spacing="5.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1">
+<HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" spacing="5.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
    <children>
       <HBox spacing="5.0" HBox.hgrow="NEVER">
          <children>
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 e2db85a5325e68b7397ecd53fe9719f55ff1f3d0..c6ed28ace694382fe21cf564089a245acd6a9ce8 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
@@ -3,7 +3,7 @@
 <?import javafx.geometry.*?>
 <?import javafx.scene.control.*?>
 <?import javafx.scene.layout.*?>
-<BorderPane fx:id="mainPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="400.0" minWidth="400.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1">
+<BorderPane fx:id="mainPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="400.0" minWidth="400.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
    <center>
       <SplitPane fx:id="mainContainer" dividerPositions="0.47069431920649235" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minWidth="300.0" prefHeight="600.0" prefWidth="1100.0">
         <items>
diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/pom.xml b/CityDoctorParent/Extensions/CityDoctorHealer/pom.xml
index 69468d1e55408ce4be180a4aed5b0727c43c8e4a..809c4f014afc0237fc40de0aeb992057a79f14a8 100644
--- a/CityDoctorParent/Extensions/CityDoctorHealer/pom.xml
+++ b/CityDoctorParent/Extensions/CityDoctorHealer/pom.xml
@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<parent>
 		<groupId>de.hft.stuttgart</groupId>
@@ -8,7 +10,7 @@
 		<relativePath>../../pom.xml</relativePath>
 	</parent>
 	<artifactId>CityDoctorHealer</artifactId>
-	
+
 	<dependencies>
 		<dependency>
 			<groupId>de.hft.stuttgart</groupId>
@@ -37,9 +39,12 @@
 				<extensions>true</extensions>
 				<configuration>
 					<javahClassNames>
-						<javahClassName>de.hft.stuttgart.citydoctor2.connect.edge.CppFeature</javahClassName>
-						<javahClassName>de.hft.stuttgart.citydoctor2.CppInitializer</javahClassName>
-						<javahClassName>de.hft.stuttgart.citydoctor2.connect.edge.CppHealResult</javahClassName>
+						<javahClassName>
+							de.hft.stuttgart.citydoctor2.connect.edge.CppFeature</javahClassName>
+						<javahClassName>
+							de.hft.stuttgart.citydoctor2.CppInitializer</javahClassName>
+						<javahClassName>
+							de.hft.stuttgart.citydoctor2.connect.edge.CppHealResult</javahClassName>
 					</javahClassNames>
 				</configuration>
 			</plugin>
diff --git a/CityDoctorParent/Extensions/CityDoctorHealer/src/main/java/de/hft/stuttgart/citydoctor2/healing/HealMainBuilding.java b/CityDoctorParent/Extensions/CityDoctorHealer/src/main/java/de/hft/stuttgart/citydoctor2/healing/HealMainBuilding.java
index 31ab9ff214eacfcd206d5aa0f17c31b9a14a17a9..c0249fe66c2cc4efb60a6c78bf7fe4001ad1c7ca 100644
--- a/CityDoctorParent/Extensions/CityDoctorHealer/src/main/java/de/hft/stuttgart/citydoctor2/healing/HealMainBuilding.java
+++ b/CityDoctorParent/Extensions/CityDoctorHealer/src/main/java/de/hft/stuttgart/citydoctor2/healing/HealMainBuilding.java
@@ -36,7 +36,6 @@ public class HealMainBuilding implements HealingMethod {
 		}
 		logger.debug("Executing Repair for AttributeMissingError with message Main Building");
 		Building b = (Building) err.getFeature();
-		System.out.println("Building: " + b.getGmlId());
 		BuildingPart largestPart = null;
 		double largestArea = 0;
 		for (BuildingPart part : b.getBuildingParts()) {
@@ -58,7 +57,6 @@ public class HealMainBuilding implements HealingMethod {
 			// no suitable part found
 			return false;
 		}
-		System.out.println("Part: " + largestPart.getGmlId() + " has area: " + largestArea);
 		b.getBuildingParts().remove(largestPart);
 		var gmlBuilding = (org.citygml4j.core.model.building.Building) b.getGmlObject();
 		BuildingPartProperty buildingPartProp = findBuildingPartProp(largestPart, gmlBuilding);
diff --git a/CityDoctorParent/Extensions/CityDoctorHealerGUI/pom.xml b/CityDoctorParent/Extensions/CityDoctorHealerGUI/pom.xml
index 1b51334e2657a60a617f9d426b802163ee20d365..4b461ef2dd96987a25017216f1e54c3e5a59dc85 100644
--- a/CityDoctorParent/Extensions/CityDoctorHealerGUI/pom.xml
+++ b/CityDoctorParent/Extensions/CityDoctorHealerGUI/pom.xml
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0">
+<project xmlns="http://maven.apache.org/POM/4.0.0" 
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
+             http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<parent>
 		<groupId>de.hft.stuttgart</groupId>
@@ -23,6 +26,14 @@
 			<groupId>de.hft.stuttgart</groupId>
 			<artifactId>CityDoctorAutoPro</artifactId>
 		</dependency>
+					<dependency>
+				<groupId>org.openjfx</groupId>
+				<artifactId>javafx-controls</artifactId>
+			</dependency>
+			<dependency>
+				<groupId>org.openjfx</groupId>
+				<artifactId>javafx-fxml</artifactId>
+			</dependency>
 	</dependencies>
 	<build>
 		<plugins>
diff --git a/CityDoctorParent/pom.xml b/CityDoctorParent/pom.xml
index be26e9b0be37f21c88c6ee3e769cb69fcecf9188..e927a41af825f3a84dccaea5b74a7ea101e7ac63 100644
--- a/CityDoctorParent/pom.xml
+++ b/CityDoctorParent/pom.xml
@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>de.hft.stuttgart</groupId>
 	<artifactId>CityDoctorParent</artifactId>
@@ -37,7 +39,8 @@
 					<configuration>
 						<testFailureIgnore>false</testFailureIgnore>
 						<excludes>
-							<exclude>**/SolidSelfIntCheckFalsePositiveBigMeshTest.java</exclude>
+							<exclude>
+								**/SolidSelfIntCheckFalsePositiveBigMeshTest.java</exclude>
 						</excludes>
 					</configuration>
 				</plugin>
@@ -246,21 +249,6 @@
 			<version>4.6.1</version>
 			<scope>test</scope>
 		</dependency>
-		<dependency>
-			<groupId>org.openjfx</groupId>
-			<artifactId>javafx-controls</artifactId>
-			<version>${jfx-version}</version>
-		</dependency>
-		<dependency>
-			<groupId>org.openjfx</groupId>
-			<artifactId>javafx-fxml</artifactId>
-			<version>${jfx-version}</version>
-		</dependency>
-		<dependency>
-			<groupId>org.openjfx</groupId>
-			<artifactId>javafx-swing</artifactId>
-			<version>${jfx-version}</version>
-		</dependency>
 	</dependencies>
 	<modules>
 		<!--CityDoctor2 Core Modules-->