/*- * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart * * This file is part of CityDoctor2. * * CityDoctor2 is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CityDoctor2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with CityDoctor2. If not, see . */ package de.hft.stuttgart.citydoctor2.mapper; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.citygml4j.model.citygml.bridge.AbstractBridge; import org.citygml4j.model.citygml.bridge.Bridge; import org.citygml4j.model.citygml.bridge.BridgePart; import org.citygml4j.model.citygml.building.BuildingInstallationProperty; import org.citygml4j.model.citygml.building.BuildingPartProperty; import org.citygml4j.model.citygml.building.OpeningProperty; import org.citygml4j.model.citygml.core.CityModel; import org.citygml4j.model.citygml.landuse.LandUse; import org.citygml4j.model.citygml.relief.AbstractReliefComponent; import org.citygml4j.model.citygml.relief.ReliefComponentProperty; import org.citygml4j.model.citygml.relief.ReliefFeature; import org.citygml4j.model.citygml.relief.TINRelief; import org.citygml4j.model.citygml.relief.TinProperty; import org.citygml4j.model.citygml.transportation.AuxiliaryTrafficArea; import org.citygml4j.model.citygml.transportation.AuxiliaryTrafficAreaProperty; import org.citygml4j.model.citygml.transportation.Railway; import org.citygml4j.model.citygml.transportation.Road; import org.citygml4j.model.citygml.transportation.Square; import org.citygml4j.model.citygml.transportation.Track; import org.citygml4j.model.citygml.transportation.TrafficArea; import org.citygml4j.model.citygml.transportation.TrafficAreaProperty; import org.citygml4j.model.citygml.transportation.TransportationComplex; import org.citygml4j.model.citygml.vegetation.PlantCover; import org.citygml4j.model.citygml.vegetation.SolitaryVegetationObject; import org.citygml4j.model.citygml.waterbody.WaterBody; import org.citygml4j.model.gml.geometry.AbstractGeometry; import org.citygml4j.model.gml.geometry.GeometryProperty; import org.citygml4j.model.gml.geometry.complexes.CompositeSurface; import org.citygml4j.model.gml.geometry.primitives.AbstractSolid; import org.citygml4j.model.gml.geometry.primitives.TriangulatedSurface; import org.citygml4j.util.walker.FeatureWalker; import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType; import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject; import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject.BridgeType; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.BuildingInstallation; import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.datastructure.LandObject; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.LinkedPolygon; import de.hft.stuttgart.citydoctor2.datastructure.Lod; import de.hft.stuttgart.citydoctor2.datastructure.Opening; import de.hft.stuttgart.citydoctor2.datastructure.OpeningType; import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject; import de.hft.stuttgart.citydoctor2.datastructure.SurfaceFeatureType; import de.hft.stuttgart.citydoctor2.datastructure.TinObject; import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject; import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject.TransportationType; import de.hft.stuttgart.citydoctor2.datastructure.Vegetation; import de.hft.stuttgart.citydoctor2.datastructure.Vegetation.VegetationType; import de.hft.stuttgart.citydoctor2.datastructure.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.WaterObject; import de.hft.stuttgart.citydoctor2.math.graph.KDTree; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.utils.Pair; /** * This walker maps all cityGML4j features to the respective CityDoctor data * structure. * * @author Matthias Betz * */ public class FeatureMapper extends FeatureWalker { private static final Logger logger = LogManager.getLogger(FeatureMapper.class); private CityDoctorModel model; private ParserConfiguration config; private double neighborDistance; public FeatureMapper(ParserConfiguration config, File file) { this.config = config; neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces()); model = new CityDoctorModel(config, file); } @Override public void visit(WaterBody waterBody) { WaterObject wo = new WaterObject(); model.addWater(wo); wo.setGmlObject(waterBody); if (waterBody.isSetId()) { wo.setGmlId(new GmlId(waterBody.getId())); } mapWaterBodyGeometries(waterBody, wo); } private void mapWaterBodyGeometries(WaterBody waterBody, WaterObject wo) { Map polygons = new HashMap<>(); Map vertexMap = new HashMap<>(); List> linkedPolygons = new ArrayList<>(); GeometryMapper.mapLod0MultiSurface(waterBody.getLod0MultiSurface(), wo, config, polygons, linkedPolygons, vertexMap); waterBody.unsetLod0MultiSurface(); GeometryMapper.mapLod1MultiSurface(waterBody.getLod1MultiSurface(), wo, config, polygons, linkedPolygons, vertexMap); waterBody.unsetLod1MultiSurface(); GeometryMapper.mapLod1Solid(waterBody.getLod1Solid(), wo, config, polygons, linkedPolygons, vertexMap); waterBody.unsetLod1Solid(); GeometryMapper.mapLod2Solid(waterBody.getLod2Solid(), wo, config, polygons, linkedPolygons, vertexMap); waterBody.unsetLod2Solid(); GeometryMapper.mapLod3Solid(waterBody.getLod3Solid(), wo, config, polygons, linkedPolygons, vertexMap); waterBody.unsetLod3Solid(); GeometryMapper.mapLod4Solid(waterBody.getLod4Solid(), wo, config, polygons, linkedPolygons, vertexMap); waterBody.unsetLod4Solid(); createLinkedPolygons(polygons, linkedPolygons); updateEdgesAndVertices(wo); } private void createLinkedPolygons(Map polygons, List> linkedPolygons) { for (Pair link : linkedPolygons) { ConcretePolygon concPoly = polygons.get(link.getValue0()); if (concPoly == null) { if (logger.isWarnEnabled()) { logger.warn(Localization.getText("FeatureMapper.polygonUnreferenced"), link.getValue0()); } continue; } Geometry geom = link.getValue1(); LinkedPolygon lPoly = new LinkedPolygon(concPoly, geom); if (geom.getParent() instanceof BoundarySurface) { BoundarySurface bs = (BoundarySurface) geom.getParent(); lPoly.setPartOfSurface(bs); if (bs.getParent() instanceof BuildingInstallation) { lPoly.setPartOfInstallation((BuildingInstallation) bs.getParent()); } } geom.addPolygon(lPoly); } } 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 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); } } } @Override public void visit(PlantCover plantCover) { Vegetation veg = new Vegetation(VegetationType.PLANT_COVER); model.addVegetation(veg); mapPlantCover(plantCover, veg); } private void mapPlantCover(PlantCover plantCover, Vegetation veg) { veg.setGmlObject(plantCover); if (plantCover.isSetId()) { veg.setGmlId(new GmlId(plantCover.getId())); } mapPlantCoverGeometries(plantCover, veg); } private void mapPlantCoverGeometries(PlantCover plantCover, Vegetation veg) { Map polygons = new HashMap<>(); List> linkedPolygons = new ArrayList<>(); Map vertexMap = new HashMap<>(); GeometryMapper.mapLod1MultiSurface(plantCover.getLod1MultiSurface(), veg, config, polygons, linkedPolygons, vertexMap); plantCover.unsetLod1MultiSurface(); GeometryMapper.mapLod2MultiSurface(plantCover.getLod2MultiSurface(), veg, config, polygons, linkedPolygons, vertexMap); plantCover.unsetLod2MultiSurface(); GeometryMapper.mapLod3MultiSurface(plantCover.getLod3MultiSurface(), veg, config, polygons, linkedPolygons, vertexMap); plantCover.unsetLod3MultiSurface(); GeometryMapper.mapLod4MultiSurface(plantCover.getLod4MultiSurface(), veg, config, polygons, linkedPolygons, vertexMap); plantCover.unsetLod4MultiSurface(); createLinkedPolygons(polygons, linkedPolygons); updateEdgesAndVertices(veg); } @Override public void visit(SolitaryVegetationObject solitaryVegetationObject) { Vegetation veg = new Vegetation(VegetationType.SOLITARY_VEGETATION_OBJECT); model.addVegetation(veg); mapSolitaryVegObject(solitaryVegetationObject, veg); } private void mapSolitaryVegObject(SolitaryVegetationObject svo, Vegetation veg) { veg.setGmlObject(svo); if (svo.isSetId()) { veg.setGmlId(new GmlId(svo.getId())); } mapSolitaryVegGeometries(svo, veg); } private void mapSolitaryVegGeometries(SolitaryVegetationObject svo, Vegetation veg) { Map polygons = new HashMap<>(); List> linkedPolygons = new ArrayList<>(); Map vertexMap = new HashMap<>(); GeometryMapper.mapLod1Geometry(svo.getLod1Geometry(), veg, config, polygons, linkedPolygons, vertexMap); svo.unsetLod1Geometry(); GeometryMapper.mapLod2Geometry(svo.getLod2Geometry(), veg, config, polygons, linkedPolygons, vertexMap); svo.unsetLod2Geometry(); GeometryMapper.mapLod3Geometry(svo.getLod3Geometry(), veg, config, polygons, linkedPolygons, vertexMap); svo.unsetLod3Geometry(); GeometryMapper.mapLod4Geometry(svo.getLod4Geometry(), veg, config, polygons, linkedPolygons, vertexMap); svo.unsetLod4Geometry(); createLinkedPolygons(polygons, linkedPolygons); updateEdgesAndVertices(veg); } @Override public void visit(LandUse landUse) { LandObject lo = new LandObject(landUse); model.addLand(lo); mapLandUse(landUse, lo); } private void mapLandUse(LandUse landUse, LandObject lo) { lo.setGmlObject(landUse); if (landUse.isSetId()) { lo.setGmlId(new GmlId(landUse.getId())); } mapLandUseGeometries(landUse, lo); } private void mapLandUseGeometries(LandUse landUse, LandObject lo) { Map polygons = new HashMap<>(); List> linkedPolygons = new ArrayList<>(); Map vertexMap = new HashMap<>(); GeometryMapper.mapLod0MultiSurface(landUse.getLod0MultiSurface(), lo, config, polygons, linkedPolygons, vertexMap); landUse.unsetLod0MultiSurface(); GeometryMapper.mapLod1MultiSurface(landUse.getLod1MultiSurface(), lo, config, polygons, linkedPolygons, vertexMap); landUse.unsetLod1MultiSurface(); GeometryMapper.mapLod2MultiSurface(landUse.getLod2MultiSurface(), lo, config, polygons, linkedPolygons, vertexMap); landUse.unsetLod2MultiSurface(); GeometryMapper.mapLod3MultiSurface(landUse.getLod3MultiSurface(), lo, config, polygons, linkedPolygons, vertexMap); landUse.unsetLod3MultiSurface(); GeometryMapper.mapLod4MultiSurface(landUse.getLod4MultiSurface(), lo, config, polygons, linkedPolygons, vertexMap); landUse.unsetLod4MultiSurface(); createLinkedPolygons(polygons, linkedPolygons); updateEdgesAndVertices(lo); } @Override public void visit(Road road) { TransportationObject trans = new TransportationObject(TransportationType.ROAD); mapTransportationComplex(road, trans); } @Override public void visit(Railway railway) { TransportationObject trans = new TransportationObject(TransportationType.RAILWAY); mapTransportationComplex(railway, trans); } @Override public void visit(Square square) { TransportationObject trans = new TransportationObject(TransportationType.SQUARE); mapTransportationComplex(square, trans); } @Override public void visit(Track track) { TransportationObject trans = new TransportationObject(TransportationType.TRACK); mapTransportationComplex(track, trans); } @Override public void visit(AuxiliaryTrafficArea auxiliaryTrafficArea) { TransportationObject trans = new TransportationObject(TransportationType.AUXILLIARY_TRAFFIC_AREA); mapAuxTrafficArea(auxiliaryTrafficArea, trans); } @Override public void visit(TransportationComplex tc) { TransportationObject trans = new TransportationObject(TransportationType.TRANSPORTATION_COMPLEX); model.addTransportation(trans); mapTransportationComplex(tc, trans); if (tc.isSetAuxiliaryTrafficArea()) { for (AuxiliaryTrafficAreaProperty atap : tc.getAuxiliaryTrafficArea()) { if (atap.isSetAuxiliaryTrafficArea()) { TransportationObject subTrans = new TransportationObject( TransportationType.AUXILLIARY_TRAFFIC_AREA); trans.addChild(subTrans); mapAuxTrafficArea(atap.getAuxiliaryTrafficArea(), subTrans); } } } if (tc.isSetTrafficArea()) { for (TrafficAreaProperty tap : tc.getTrafficArea()) { if (tap.isSetTrafficArea()) { TransportationObject subTrans = new TransportationObject(TransportationType.TRAFFIC_AREA); trans.addChild(subTrans); mapTrafficArea(tap.getTrafficArea(), subTrans); } } } } @Override public void visit(TrafficArea trafficArea) { TransportationObject trans = new TransportationObject(TransportationType.TRAFFIC_AREA); model.addTransportation(trans); mapTrafficArea(trafficArea, trans); } private void mapTrafficArea(TrafficArea ta, TransportationObject to) { to.setGmlObject(ta); if (ta.isSetId()) { to.setGmlId(new GmlId(ta.getId())); } mapTrafficAreaGeometries(ta, to); } private void mapTrafficAreaGeometries(TrafficArea ta, TransportationObject to) { Map polygons = new HashMap<>(); List> linkedPolygons = new ArrayList<>(); Map vertexMap = new HashMap<>(); GeometryMapper.mapLod2MultiSurface(ta.getLod2MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); ta.unsetLod2MultiSurface(); GeometryMapper.mapLod3MultiSurface(ta.getLod3MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); ta.unsetLod3MultiSurface(); GeometryMapper.mapLod4MultiSurface(ta.getLod4MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); ta.unsetLod4MultiSurface(); createLinkedPolygons(polygons, linkedPolygons); updateEdgesAndVertices(to); } private void mapAuxTrafficArea(AuxiliaryTrafficArea ata, TransportationObject to) { to.setGmlObject(ata); if (ata.isSetId()) { to.setGmlId(new GmlId(ata.getId())); } mapAuxTrafficAreaGeometries(ata, to); } private void mapAuxTrafficAreaGeometries(AuxiliaryTrafficArea ata, TransportationObject to) { Map polygons = new HashMap<>(); List> linkedPolygons = new ArrayList<>(); Map vertexMap = new HashMap<>(); GeometryMapper.mapLod2MultiSurface(ata.getLod2MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); ata.unsetLod2MultiSurface(); GeometryMapper.mapLod3MultiSurface(ata.getLod3MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); ata.unsetLod3MultiSurface(); GeometryMapper.mapLod4MultiSurface(ata.getLod4MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); ata.unsetLod4MultiSurface(); createLinkedPolygons(polygons, linkedPolygons); updateEdgesAndVertices(to); } private void mapTransportationComplex(TransportationComplex tc, TransportationObject to) { model.addTransportation(to); to.setGmlObject(tc); if (tc.isSetId()) { to.setGmlId(new GmlId(tc.getId())); } mapTransportationComplexGeometries(tc, to); } private void mapTransportationComplexGeometries(TransportationComplex tc, TransportationObject to) { Map polygons = new HashMap<>(); List> linkedPolygons = new ArrayList<>(); Map vertexMap = new HashMap<>(); GeometryMapper.mapLod1MultiSurface(tc.getLod1MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); tc.unsetLod1MultiSurface(); GeometryMapper.mapLod2MultiSurface(tc.getLod2MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); tc.unsetLod2MultiSurface(); GeometryMapper.mapLod3MultiSurface(tc.getLod3MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); tc.unsetLod3MultiSurface(); GeometryMapper.mapLod4MultiSurface(tc.getLod4MultiSurface(), to, config, polygons, linkedPolygons, vertexMap); tc.unsetLod4MultiSurface(); createLinkedPolygons(polygons, linkedPolygons); updateEdgesAndVertices(to); } @Override public void visit(org.citygml4j.model.citygml.building.Building building) { Building coBuilding = new Building(); model.addBuilding(coBuilding); mapAbstractBuilding(building, coBuilding); if (building.isSetConsistsOfBuildingPart()) { for (BuildingPartProperty bpp : building.getConsistsOfBuildingPart()) { if (bpp.isSetBuildingPart()) { BuildingPart coBuildingPart = new BuildingPart(coBuilding); mapAbstractBuilding(bpp.getBuildingPart(), coBuildingPart); } } } } private void mapAbstractBuilding(org.citygml4j.model.citygml.building.AbstractBuilding ab, AbstractBuilding coBuilding) { coBuilding.setGmlObject(ab); if (ab.isSetId()) { coBuilding.setGmlId(new GmlId(ab.getId())); } Map polygons = new HashMap<>(); List> linkedPolygons = new ArrayList<>(); Map vertexMap = new HashMap<>(); mapBuildingMultiSurfaceGeometries(ab, coBuilding, polygons, linkedPolygons, vertexMap); mapBuildingSolidGeometries(ab, coBuilding, polygons, linkedPolygons, vertexMap); // handle boundary surfaces if (ab.isSetBoundedBySurface()) { for (org.citygml4j.model.citygml.building.BoundarySurfaceProperty bsp : ab.getBoundedBySurface()) { mapBuildingSurface(bsp, coBuilding, polygons, linkedPolygons, vertexMap); } } // handle outer building installations if (ab.isSetOuterBuildingInstallation()) { for (BuildingInstallationProperty bip : ab.getOuterBuildingInstallation()) { mapBuildingInstallation(bip, coBuilding, polygons, linkedPolygons, vertexMap); } } createLinkedPolygons(polygons, linkedPolygons); updateEdgesAndVertices(coBuilding); } private void mapBuildingInstallation(BuildingInstallationProperty bip, AbstractBuilding coBuilding, Map polygons, List> linkedPolygons, Map vertexMap) { if (!bip.isSetBuildingInstallation()) { return; } BuildingInstallation coBi = new BuildingInstallation(); coBuilding.addBuildingInstallation(coBi); org.citygml4j.model.citygml.building.BuildingInstallation gmlBi = bip.getBuildingInstallation(); coBi.setGmlObject(gmlBi); if (gmlBi.isSetLod2Geometry()) { GeometryProperty abstractGeometry = gmlBi.getLod2Geometry(); GeometryType type = extractGeometryType(abstractGeometry); Geometry geom = new Geometry(type, Lod.LOD2); GeometryMapper mapper = new GeometryMapper(geom, config, polygons, linkedPolygons, vertexMap); GeometryMapper.mapBuildingInstallationIntoGeometry(abstractGeometry, mapper, coBi); coBi.addGeometry(geom); gmlBi.unsetLod2Geometry(); } if (gmlBi.isSetLod3Geometry()) { GeometryProperty abstractGeometry = gmlBi.getLod3Geometry(); GeometryType type = extractGeometryType(abstractGeometry); Geometry geom = new Geometry(type, Lod.LOD3); GeometryMapper mapper = new GeometryMapper(geom, config, polygons, linkedPolygons, vertexMap); GeometryMapper.mapBuildingInstallationIntoGeometry(abstractGeometry, mapper, coBi); coBi.addGeometry(geom); gmlBi.unsetLod3Geometry(); } if (gmlBi.isSetLod4Geometry()) { GeometryProperty abstractGeometry = gmlBi.getLod4Geometry(); GeometryType type = extractGeometryType(abstractGeometry); Geometry geom = new Geometry(type, Lod.LOD4); GeometryMapper mapper = new GeometryMapper(geom, config, polygons, linkedPolygons, vertexMap); GeometryMapper.mapBuildingInstallationIntoGeometry(abstractGeometry, mapper, coBi); coBi.addGeometry(geom); gmlBi.unsetLod4Geometry(); } // handle boundary surfaces if (gmlBi.isSetBoundedBySurface()) { for (org.citygml4j.model.citygml.building.BoundarySurfaceProperty bsp : gmlBi.getBoundedBySurface()) { mapBuildingInstallationSurface(bsp, coBi, polygons, linkedPolygons, vertexMap); } } updateEdgesAndVertices(coBi); } private GeometryType extractGeometryType(GeometryProperty agp) { AbstractGeometry abstractGeometry = agp.getObject(); if (abstractGeometry instanceof CompositeSurface) { return GeometryType.COMPOSITE_SURFACE; } else if (abstractGeometry instanceof AbstractSolid) { return GeometryType.SOLID; } return GeometryType.MULTI_SURFACE; } private void mapBuildingSolidGeometries(org.citygml4j.model.citygml.building.AbstractBuilding ab, AbstractBuilding coBuilding, Map polygons, List> linkedPolygons, Map vertexMap) { GeometryMapper.mapLod1Solid(ab.getLod1Solid(), coBuilding, config, polygons, linkedPolygons, vertexMap); ab.unsetLod1Solid(); GeometryMapper.mapLod2Solid(ab.getLod2Solid(), coBuilding, config, polygons, linkedPolygons, vertexMap); ab.unsetLod2Solid(); GeometryMapper.mapLod3Solid(ab.getLod3Solid(), coBuilding, config, polygons, linkedPolygons, vertexMap); ab.unsetLod3Solid(); GeometryMapper.mapLod4Solid(ab.getLod4Solid(), coBuilding, config, polygons, linkedPolygons, vertexMap); ab.unsetLod4Solid(); } private void mapBuildingMultiSurfaceGeometries(org.citygml4j.model.citygml.building.AbstractBuilding ab, AbstractBuilding coBuilding, Map polygons, List> linkedPolygons, Map vertexMap) { GeometryMapper.mapLod1MultiSurface(ab.getLod1MultiSurface(), coBuilding, config, polygons, linkedPolygons, vertexMap); ab.unsetLod1MultiSurface(); GeometryMapper.mapLod2MultiSurface(ab.getLod2MultiSurface(), coBuilding, config, polygons, linkedPolygons, vertexMap); ab.unsetLod2MultiSurface(); GeometryMapper.mapLod3MultiSurface(ab.getLod3MultiSurface(), coBuilding, config, polygons, linkedPolygons, vertexMap); ab.unsetLod3MultiSurface(); GeometryMapper.mapLod4MultiSurface(ab.getLod4MultiSurface(), coBuilding, config, polygons, linkedPolygons, vertexMap); ab.unsetLod4MultiSurface(); } private void mapBuildingSurface(org.citygml4j.model.citygml.building.BoundarySurfaceProperty bsp, AbstractBuilding coBuilding, Map polygons, List> linkedPolygons, Map vertexMap) { if (!bsp.isSetBoundarySurface()) { return; } org.citygml4j.model.citygml.building.AbstractBoundarySurface abs = bsp.getBoundarySurface(); BoundarySurfaceType surfaceType = mapSurfaceType(abs); BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, surfaceType, abs); if (abs.isSetId()) { bs.setGmlId(new GmlId(abs.getId())); } coBuilding.addBoundarySurface(bs); createBoundarySurfaceGeometries(abs, bs, null, polygons, linkedPolygons, vertexMap); mapBuildingOpenings(abs.getOpening(), bs, polygons, linkedPolygons, vertexMap); updateEdgesAndVertices(bs); } private void mapBuildingOpenings(List openings, BoundarySurface bs, Map polygons, List> linkedPolygons, Map vertexMap) { if (openings == null || openings.isEmpty()) { return; } for (org.citygml4j.model.citygml.building.OpeningProperty op : openings) { if (!op.isSetOpening()) { continue; } org.citygml4j.model.citygml.building.AbstractOpening ap = op.getOpening(); OpeningType type = mapOpeningType(ap); Opening opening = new Opening(type, SurfaceFeatureType.BUILDING, bs, ap); bs.addOpening(opening); createOpeningGeometries(ap, opening, polygons, linkedPolygons, vertexMap); updateEdgesAndVertices(opening); } } private void createOpeningGeometries(org.citygml4j.model.citygml.building.AbstractOpening ap, Opening opening, Map polygons, List> linkedPolygons, Map vertexMap) { if (ap.isSetLod3MultiSurface()) { GeometryMapper.mapLod3MultiSurface(ap.getLod3MultiSurface(), opening, config, polygons, linkedPolygons, vertexMap); ap.unsetLod3MultiSurface(); } if (ap.isSetLod4MultiSurface()) { GeometryMapper.mapLod4MultiSurface(ap.getLod3MultiSurface(), opening, config, polygons, linkedPolygons, vertexMap); ap.unsetLod4MultiSurface(); } } private OpeningType mapOpeningType(org.citygml4j.model.citygml.building.AbstractOpening ap) { if (ap instanceof org.citygml4j.model.citygml.building.Door) { return OpeningType.DOOR; } if (ap instanceof org.citygml4j.model.citygml.building.Window) { return OpeningType.WINDOW; } return OpeningType.UNKNOWN; } private void mapBuildingInstallationSurface(org.citygml4j.model.citygml.building.BoundarySurfaceProperty bsp, BuildingInstallation coBi, Map polygons, List> linkedPolygons, Map vertexMap) { if (!bsp.isSetBoundarySurface()) { return; } org.citygml4j.model.citygml.building.AbstractBoundarySurface abs = bsp.getBoundarySurface(); BoundarySurfaceType surfaceType = mapSurfaceType(abs); BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BUILDING, surfaceType, abs); if (abs.isSetId()) { bs.setGmlId(new GmlId(abs.getId())); } coBi.addBoundarySurface(bs); createBoundarySurfaceGeometries(abs, bs, coBi, polygons, linkedPolygons, vertexMap); mapBuildingOpenings(abs.getOpening(), bs, polygons, linkedPolygons, vertexMap); updateEdgesAndVertices(bs); } private void createBoundarySurfaceGeometries(org.citygml4j.model.citygml.building.AbstractBoundarySurface abs, BoundarySurface bs, BuildingInstallation coBi, Map polygons, List> linkedPolygons, Map vertexMap) { if (abs.isSetLod2MultiSurface()) { Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD2); GeometryMapper mapper = new GeometryMapper(geom, config, polygons, linkedPolygons, vertexMap); GeometryMapper.mapBoundarySurfaceIntoGeometry(abs.getLod2MultiSurface(), mapper, bs, coBi); bs.addGeometry(geom); abs.unsetLod2MultiSurface(); } if (abs.isSetLod3MultiSurface()) { Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD3); GeometryMapper mapper = new GeometryMapper(geom, config, polygons, linkedPolygons, vertexMap); GeometryMapper.mapBoundarySurfaceIntoGeometry(abs.getLod3MultiSurface(), mapper, bs, coBi); bs.addGeometry(geom); abs.unsetLod3MultiSurface(); } if (abs.isSetLod4MultiSurface()) { Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD4); GeometryMapper mapper = new GeometryMapper(geom, config, polygons, linkedPolygons, vertexMap); GeometryMapper.mapBoundarySurfaceIntoGeometry(abs.getLod4MultiSurface(), mapper, bs, coBi); bs.addGeometry(geom); abs.unsetLod4MultiSurface(); } } private void createBoundarySurfaceGeometries(org.citygml4j.model.citygml.bridge.AbstractBoundarySurface abs, BoundarySurface bs, Map polygons, List> linkedPolygons, Map vertexMap) { if (abs.isSetLod2MultiSurface()) { Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD2); GeometryMapper mapper = new GeometryMapper(geom, config, polygons, linkedPolygons, vertexMap); GeometryMapper.mapBoundarySurfaceIntoGeometry(abs.getLod2MultiSurface(), mapper, bs, null); bs.addGeometry(geom); abs.unsetLod2MultiSurface(); } if (abs.isSetLod3MultiSurface()) { Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD3); GeometryMapper mapper = new GeometryMapper(geom, config, polygons, linkedPolygons, vertexMap); GeometryMapper.mapBoundarySurfaceIntoGeometry(abs.getLod3MultiSurface(), mapper, bs, null); bs.addGeometry(geom); abs.unsetLod3MultiSurface(); } if (abs.isSetLod4MultiSurface()) { Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, Lod.LOD4); GeometryMapper mapper = new GeometryMapper(geom, config, polygons, linkedPolygons, vertexMap); GeometryMapper.mapBoundarySurfaceIntoGeometry(abs.getLod4MultiSurface(), mapper, bs, null); bs.addGeometry(geom); abs.unsetLod4MultiSurface(); } } private BoundarySurfaceType mapSurfaceType(org.citygml4j.model.citygml.building.AbstractBoundarySurface abs) { switch (abs.getCityGMLClass()) { case BUILDING_WALL_SURFACE: return BoundarySurfaceType.WALL; case BUILDING_ROOF_SURFACE: return BoundarySurfaceType.ROOF; case BUILDING_GROUND_SURFACE: return BoundarySurfaceType.GROUND; case BUILDING_CLOSURE_SURFACE: return BoundarySurfaceType.CLOSURE; case BUILDING_FLOOR_SURFACE: return BoundarySurfaceType.OUTER_FLOOR; case OUTER_BUILDING_FLOOR_SURFACE: return BoundarySurfaceType.OUTER_FLOOR; case BUILDING_CEILING_SURFACE: return BoundarySurfaceType.OUTER_CEILING; case OUTER_BUILDING_CEILING_SURFACE: return BoundarySurfaceType.OUTER_CEILING; default: return BoundarySurfaceType.UNDEFINED; } } @Override public void visit(Bridge bridge) { BridgeObject coBridge = new BridgeObject(BridgeType.BRIDGE, bridge); mapBridge(bridge, coBridge); } @Override public void visit(BridgePart bridgePart) { BridgeObject coBridge = new BridgeObject(BridgeType.BRIDGE_PART, bridgePart); mapBridge(bridgePart, coBridge); } private void mapBridge(AbstractBridge aBridge, BridgeObject coBridge) { model.addBridge(coBridge); coBridge.setGmlObject(aBridge); if (aBridge.isSetId()) { coBridge.setGmlId(new GmlId(aBridge.getId())); } Map polygons = new HashMap<>(); List> linkedPolygons = new ArrayList<>(); Map vertexMap = new HashMap<>(); // map geometries mapBridgeMultiSurfaceGeometries(aBridge, coBridge, polygons, linkedPolygons, vertexMap); mapBridgeSolidGeometries(aBridge, coBridge, polygons, linkedPolygons, vertexMap); // handle boundary surfaces if (aBridge.isSetBoundedBySurface()) { for (org.citygml4j.model.citygml.bridge.BoundarySurfaceProperty bsp : aBridge.getBoundedBySurface()) { mapBridgeSurface(bsp, coBridge, polygons, linkedPolygons, vertexMap); } } createLinkedPolygons(polygons, linkedPolygons); updateEdgesAndVertices(coBridge); } private void mapBridgeMultiSurfaceGeometries(AbstractBridge aBridge, BridgeObject coBridge, Map polygons, List> linkedPolygons, Map vertexMap) { GeometryMapper.mapLod1MultiSurface(aBridge.getLod1MultiSurface(), coBridge, config, polygons, linkedPolygons, vertexMap); aBridge.unsetLod1MultiSurface(); GeometryMapper.mapLod2MultiSurface(aBridge.getLod2MultiSurface(), coBridge, config, polygons, linkedPolygons, vertexMap); aBridge.unsetLod2MultiSurface(); GeometryMapper.mapLod3MultiSurface(aBridge.getLod3MultiSurface(), coBridge, config, polygons, linkedPolygons, vertexMap); aBridge.unsetLod3MultiSurface(); GeometryMapper.mapLod4MultiSurface(aBridge.getLod4MultiSurface(), coBridge, config, polygons, linkedPolygons, vertexMap); aBridge.unsetLod4MultiSurface(); } private void mapBridgeSolidGeometries(AbstractBridge aBridge, BridgeObject coBridge, Map polygons, List> linkedPolygons, Map vertexMap) { GeometryMapper.mapLod1Solid(aBridge.getLod1Solid(), coBridge, config, polygons, linkedPolygons, vertexMap); aBridge.unsetLod1Solid(); GeometryMapper.mapLod2Solid(aBridge.getLod2Solid(), coBridge, config, polygons, linkedPolygons, vertexMap); aBridge.unsetLod2Solid(); GeometryMapper.mapLod3Solid(aBridge.getLod3Solid(), coBridge, config, polygons, linkedPolygons, vertexMap); aBridge.unsetLod3Solid(); GeometryMapper.mapLod4Solid(aBridge.getLod4Solid(), coBridge, config, polygons, linkedPolygons, vertexMap); aBridge.unsetLod4Solid(); } private void mapBridgeSurface(org.citygml4j.model.citygml.bridge.BoundarySurfaceProperty bsp, BridgeObject coBridge, Map polygons, List> linkedPolygons, Map vertexMap) { if (!bsp.isSetBoundarySurface()) { return; } org.citygml4j.model.citygml.bridge.AbstractBoundarySurface abs = bsp.getBoundarySurface(); BoundarySurfaceType surfaceType = mapSurfaceType(abs); BoundarySurface bs = new BoundarySurface(SurfaceFeatureType.BRIDGE, surfaceType, abs); bs.setGmlObject(abs); coBridge.addBoundarySurface(bs); mapBridgeOpenings(abs.getOpening(), bs, polygons, linkedPolygons, vertexMap); createBoundarySurfaceGeometries(abs, bs, polygons, linkedPolygons, vertexMap); updateEdgesAndVertices(bs); } private void mapBridgeOpenings(List openings, BoundarySurface bs, Map polygons, List> linkedPolygons, Map vertexMap) { if (openings == null || openings.isEmpty()) { return; } for (org.citygml4j.model.citygml.bridge.OpeningProperty op : openings) { if (!op.isSetOpening()) { continue; } org.citygml4j.model.citygml.bridge.AbstractOpening ap = op.getOpening(); OpeningType type = mapOpeningType(ap); Opening opening = new Opening(type, SurfaceFeatureType.BRIDGE, bs, ap); bs.addOpening(opening); createOpeningGeometries(ap, opening, polygons, linkedPolygons, vertexMap); updateEdgesAndVertices(opening); } } private void createOpeningGeometries(org.citygml4j.model.citygml.bridge.AbstractOpening ap, Opening opening, Map polygons, List> linkedPolygons, Map vertexMap) { if (ap.isSetLod3MultiSurface()) { GeometryMapper.mapLod3MultiSurface(ap.getLod3MultiSurface(), opening, config, polygons, linkedPolygons, vertexMap); ap.unsetLod3MultiSurface(); } if (ap.isSetLod4MultiSurface()) { GeometryMapper.mapLod4MultiSurface(ap.getLod3MultiSurface(), opening, config, polygons, linkedPolygons, vertexMap); ap.unsetLod4MultiSurface(); } } private OpeningType mapOpeningType(org.citygml4j.model.citygml.bridge.AbstractOpening ap) { if (ap instanceof org.citygml4j.model.citygml.bridge.Door) { return OpeningType.DOOR; } if (ap instanceof org.citygml4j.model.citygml.bridge.Window) { return OpeningType.WINDOW; } return OpeningType.UNKNOWN; } private BoundarySurfaceType mapSurfaceType(org.citygml4j.model.citygml.bridge.AbstractBoundarySurface abs) { switch (abs.getCityGMLClass()) { case BRIDGE_CEILING_SURFACE: return BoundarySurfaceType.CEILING; case BRIDGE_CLOSURE_SURFACE: return BoundarySurfaceType.CLOSURE; case BRIDGE_FLOOR_SURFACE: return BoundarySurfaceType.FLOOR; case BRIDGE_GROUND_SURFACE: return BoundarySurfaceType.GROUND; case BRIDGE_ROOF_SURFACE: return BoundarySurfaceType.ROOF; case BRIDGE_WALL_SURFACE: return BoundarySurfaceType.WALL; case INTERIOR_BRIDGE_WALL_SURFACE: return BoundarySurfaceType.INTERIOR_WALL; case OUTER_BRIDGE_CEILING_SURFACE: return BoundarySurfaceType.OUTER_CEILING; case OUTER_BUILDING_FLOOR_SURFACE: return BoundarySurfaceType.OUTER_FLOOR; default: return BoundarySurfaceType.UNDEFINED; } } @Override public void visit(TINRelief relief) { model.addTin(mapTinRelief(relief)); } private TinObject mapTinRelief(TINRelief relief) { TinObject tin = new TinObject(relief); if (relief.isSetId()) { tin.setGmlId(new GmlId(relief.getId())); } TinProperty tinProp = relief.getTin(); if (tinProp == null) { return tin; } TriangulatedSurface triangSurface = tinProp.getObject(); Lod lod = Lod.LOD1; switch (relief.getLod()) { case 0: lod = Lod.LOD0; break; case 1: break; case 2: lod = Lod.LOD2; break; case 3: lod = Lod.LOD3; break; case 4: lod = Lod.LOD4; break; default: logger.warn("Lod for relief {} was not specified, assuming lod 1", relief.getId()); } Geometry geom = new Geometry(GeometryType.MULTI_SURFACE, lod); GeometryMapper.mapTriangulatedSurface(geom, triangSurface, config, new HashMap<>(), new ArrayList<>(), new HashMap<>()); tin.addGeometry(geom); return tin; } @Override public void visit(ReliefFeature reliefFeature) { ReliefObject relief = new ReliefObject(reliefFeature); model.addRelief(relief); if (reliefFeature.isSetId()) { relief.setGmlId(new GmlId(reliefFeature.getId())); } List reliefprops = reliefFeature.getReliefComponent(); for (ReliefComponentProperty prop : reliefprops) { AbstractReliefComponent object = prop.getObject(); if (object == null) { continue; } if (object instanceof TINRelief) { TINRelief tinRelief = (TINRelief) object; TinObject tinObject = mapTinRelief(tinRelief); relief.getComponents().add(tinObject); } } } public CityDoctorModel getModel() { return model; } public void setCityModel(CityModel cModel) { model.setCityModel(cModel); } }