Commit 5571f382 authored by Luna Riegel's avatar Luna Riegel
Browse files

Merge branch 'dev' into 'master'

CityDoctor Version 3.18.0

See merge request !37
parents 87dba3fa 61859d4f
Pipeline #12247 passed with stage
in 2 minutes and 31 seconds
...@@ -20,9 +20,11 @@ package de.hft.stuttgart.citydoctor2.datastructure; ...@@ -20,9 +20,11 @@ package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator; import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator;
import de.hft.stuttgart.citydoctor2.utils.visitors.MinMaxExtentVisitor;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale;
/** /**
* An axis aligned bounding box represented by its two corners * An axis aligned bounding box represented by its two corners
...@@ -47,6 +49,12 @@ public class BoundingBox { ...@@ -47,6 +49,12 @@ public class BoundingBox {
return BoundingBoxCalculator.calculateBoundingBoxFromPoints(points); return BoundingBoxCalculator.calculateBoundingBoxFromPoints(points);
} }
public static BoundingBox of(CityObject co) {
MinMaxExtentVisitor mima = new MinMaxExtentVisitor();
co.accept(mima);
return BoundingBox.of(mima.getMinMaxExtent());
}
/** /**
* Creates an axis aligned bounding box of the whole model. * Creates an axis aligned bounding box of the whole model.
* *
...@@ -161,4 +169,45 @@ public class BoundingBox { ...@@ -161,4 +169,45 @@ public class BoundingBox {
public double getDiagonalLength() { public double getDiagonalLength() {
return bbox[1].getDistance(bbox[0]); return bbox[1].getDistance(bbox[0]);
} }
/**
* Returns whether this BoundinBox is undefined, meaning that one or all corners are infinite.
* @return True if the BoundingBox is undefined, false otherwise
*/
public boolean isUndefined(){
double x1 = bbox[0].getX();
double y1 = bbox[0].getY();
double x2 = bbox[1].getX();
double y2 = bbox[1].getY();
return (Double.isInfinite(x1) || Double.isInfinite(y1) || Double.isInfinite(x2) || Double.isInfinite(y2));
}
/**
* Returns an axis-aligned 2D polygon projection of this bbox as a WKT String.
* <p/>
* An undefined bbox will return an empty polygon.
* @return The 2D polygon WKT for this bbox
*/
public String to2DWkt() {
if (this.isUndefined()){
return "POLYGON EMPTY";
}
double x1 = bbox[0].getX();
double y1 = bbox[0].getY();
double x2 = bbox[1].getX();
double y2 = bbox[1].getY();
double minX = Math.min(x1, x2);
double minY = Math.min(y1, y2);
double maxX = Math.max(x1, x2);
double maxY = Math.max(y1, y2);
return String.format(Locale.ROOT,"POLYGON((%.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f))",
minX, minY,
maxX, minY,
maxX, maxY,
minX, maxY,
minX, minY
);
}
} }
...@@ -61,15 +61,11 @@ public class BridgeObject extends CityObject { ...@@ -61,15 +61,11 @@ public class BridgeObject extends CityObject {
private BridgeObject parent; private BridgeObject parent;
public BridgeObject(AbstractBridge ab) { public BridgeObject(AbstractBridge ab) {
this.ab = ab; this(BridgeType.BRIDGE, ab, null);
this.type = BridgeType.BRIDGE;
this.parent = null;
} }
public BridgeObject(AbstractBridge ab, BridgeObject parent) { public BridgeObject(AbstractBridge ab, BridgeObject parent) {
this.ab = ab; this(BridgeType.BRIDGE_PART, ab, parent);
this.type = BridgeType.BRIDGE_PART;
this.parent = parent;
} }
private BridgeObject(BridgeType type, AbstractBridge ab, BridgeObject parent) { private BridgeObject(BridgeType type, AbstractBridge ab, BridgeObject parent) {
......
package de.hft.stuttgart.citydoctor2.datastructure; package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; import java.io.Serial;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import java.util.ArrayList;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils; import java.util.List;
import javafx.scene.paint.Color;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty; import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty;
import org.citygml4j.core.model.deprecated.bridge.DeprecatedPropertiesOfBridgeConstructiveElement;
import org.citygml4j.core.util.geometry.GeometryFactory; import org.citygml4j.core.util.geometry.GeometryFactory;
import org.xmlobjects.gml.model.geometry.GeometryProperty;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty;
import org.xmlobjects.gml.model.geometry.complexes.CompositeSurface;
import org.xmlobjects.gml.model.geometry.primitives.Solid; import org.xmlobjects.gml.model.geometry.primitives.Solid;
import org.xmlobjects.gml.model.geometry.primitives.SolidProperty; import org.xmlobjects.gml.model.geometry.primitives.SolidProperty;
import org.xmlobjects.gml.model.geometry.primitives.SurfaceProperty;
import java.io.Serial; import de.hft.stuttgart.citydoctor2.check.CheckableVisitor;
import java.util.ArrayList; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import java.util.List; import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
import javafx.scene.paint.Color;
public class BuildingConstructiveElement extends CityObject { public class BuildingConstructiveElement extends CityObject {
......
...@@ -20,6 +20,8 @@ package de.hft.stuttgart.citydoctor2.datastructure; ...@@ -20,6 +20,8 @@ package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor;
import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckableVisitor;
import de.hft.stuttgart.citydoctor2.database.CityObjectCache;
import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.writer.CityGMLWriterUtils; import de.hft.stuttgart.citydoctor2.writer.CityGMLWriterUtils;
...@@ -45,15 +47,15 @@ import java.util.stream.Stream; ...@@ -45,15 +47,15 @@ import java.util.stream.Stream;
public class CityDoctorModel { public class CityDoctorModel {
private static final String COULD_NOT_FIND_FEATURE = "Could not find feature: "; private static final String COULD_NOT_FIND_FEATURE = "Could not find feature: ";
private final List<Building> buildings; private final List<GmlId> buildings;
private final List<Vegetation> vegetation; private final List<GmlId> vegetation;
private final List<BridgeObject> bridges; private final List<GmlId> bridges;
private final List<CityFurniture> cityfurniture; private final List<GmlId> cityfurniture;
private final List<GenericCityObject> genericObjects; private final List<GmlId> genericObjects;
private final List<CityObject> land; private final List<GmlId> land;
private final List<TransportationObject> roads; private final List<GmlId> roads;
private final List<Tunnel> tunnels; private final List<GmlId> tunnels;
private final List<WaterObject> water; private final List<GmlId> water;
private CityModel cModel; private CityModel cModel;
private final ParserConfiguration config; private final ParserConfiguration config;
private final String fileName; private final String fileName;
...@@ -62,11 +64,15 @@ public class CityDoctorModel { ...@@ -62,11 +64,15 @@ public class CityDoctorModel {
private boolean isValidated = false; private boolean isValidated = false;
private ValidationPlan plan; private ValidationPlan plan;
private CityGMLVersion cityGMLVersion; private CityGMLVersion cityGMLVersion;
private final CityObjectCache cache;
public CityDoctorModel(ParserConfiguration config, File file) { public CityDoctorModel(ParserConfiguration config, File file, CityObjectCache cache) {
if (config == null) { if (config == null) {
throw new IllegalArgumentException("Parser configuration may not be null"); throw new IllegalArgumentException("Parser configuration may not be null");
} }
if( file == null ) {
throw new IllegalArgumentException("File may not be null");
}
this.fileName = file.getName(); this.fileName = file.getName();
this.config = config; this.config = config;
this.file = file; this.file = file;
...@@ -80,6 +86,7 @@ public class CityDoctorModel { ...@@ -80,6 +86,7 @@ public class CityDoctorModel {
cityfurniture = new ArrayList<>(); cityfurniture = new ArrayList<>();
genericObjects = new ArrayList<>(); genericObjects = new ArrayList<>();
globalErrors = new ArrayList<>(); globalErrors = new ArrayList<>();
this.cache = cache;
} }
public boolean isValidated() { public boolean isValidated() {
...@@ -117,8 +124,59 @@ public class CityDoctorModel { ...@@ -117,8 +124,59 @@ public class CityDoctorModel {
} }
public Stream<CityObject> createFeatureStream() { public Stream<CityObject> createFeatureStream() {
return Stream.of(buildings.stream(), vegetation.stream(), bridges.stream(), land.stream(), roads.stream(), return Stream.of(getBuildings(), getVegetation(), getBridges(), getLand(), getTransportation(),
tunnels.stream(), water.stream(), cityfurniture.stream(), genericObjects.stream()).flatMap(co -> co); getTunnels(), getWater(), getCityFurniture(), getOtherCityObjects()).flatMap(co -> co);
}
public List<GmlId> getFeatureIds() {
List<GmlId> featureIds = new ArrayList<>();
featureIds.addAll(buildings);
featureIds.addAll(vegetation);
featureIds.addAll(bridges);
featureIds.addAll(land);
featureIds.addAll(roads);
featureIds.addAll(tunnels);
featureIds.addAll(water);
featureIds.addAll(cityfurniture);
featureIds.addAll(genericObjects);
return featureIds;
}
public boolean containsGmlId(String id) {
if (id == null) {
return false;
}
GmlId gmlId = new GmlId(id);
if (buildings.contains(gmlId)) {
return true;
}
if (vegetation.contains(gmlId)) {
return true;
}
if (bridges.contains(gmlId)) {
return true;
}
if (land.contains(gmlId)) {
return true;
}
if (roads.contains(gmlId)) {
return true;
}
if (tunnels.contains(gmlId)) {
return true;
}
if (water.contains(gmlId)) {
return true;
}
if (cityfurniture.contains(gmlId)) {
return true;
}
return genericObjects.contains(gmlId);
}
public void accept(CheckableVisitor visitor) {
createFeatureStream().forEach(cityObject -> cityObject.accept(visitor));
} }
public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException { public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException {
...@@ -136,6 +194,7 @@ public class CityDoctorModel { ...@@ -136,6 +194,7 @@ public class CityDoctorModel {
} }
} }
private void exportAsGML(String file) throws CityDoctorWriteException { private void exportAsGML(String file) throws CityDoctorWriteException {
CityGMLWriterUtils.writeCityModel(file, this); CityGMLWriterUtils.writeCityModel(file, this);
} }
...@@ -215,106 +274,201 @@ public class CityDoctorModel { ...@@ -215,106 +274,201 @@ public class CityDoctorModel {
public Set<CheckError> collectErrors() { public Set<CheckError> collectErrors() {
List<CheckError> errors = new ArrayList<>(); List<CheckError> errors = new ArrayList<>();
collectErrorsFromList(errors, buildings); createFeatureStream().forEach(cityObject -> cityObject.collectErrors(errors));
collectErrorsFromList(errors, vegetation);
collectErrorsFromList(errors, bridges);
collectErrorsFromList(errors, land);
collectErrorsFromList(errors, roads);
collectErrorsFromList(errors, tunnels);
collectErrorsFromList(errors, water);
collectErrorsFromList(errors, cityfurniture);
collectErrorsFromList(errors, genericObjects);
return new HashSet<>(errors); return new HashSet<>(errors);
} }
private void collectErrorsFromList(List<CheckError> errors, List<? extends CityObject> cos) {
for (CityObject co : cos) {
co.collectContainedErrors(errors);
}
}
public String getFileName() { public String getFileName() {
return fileName; return fileName;
} }
public void addBridge(BridgeObject coBridge) {
bridges.add(coBridge);
}
public void addBuilding(Building coBuilding) { public void addBuilding(Building coBuilding) {
buildings.add(coBuilding); buildings.add(coBuilding.getGmlId());
cache.put(coBuilding);
} }
public List<Building> getBuildings() { public void removeFeature(GmlId id){
buildings.remove(id);
vegetation.remove(id);
bridges.remove(id);
land.remove(id);
roads.remove(id);
tunnels.remove(id);
water.remove(id);
cityfurniture.remove(id);
genericObjects.remove(id);
}
public Stream<Building> getBuildings() {
return buildings.stream().map(co -> (Building) cache.get(co));
}
public List<GmlId> getBuildingIds() {
return buildings; return buildings;
} }
public List<BridgeObject> getBridges() { public int getBuildingCount(){
return buildings.size();
}
public void addBridge(BridgeObject coBridge) {
bridges.add(coBridge.getGmlId());
cache.put(coBridge);
}
public Stream<BridgeObject> getBridges() {
return bridges.stream().map(co -> (BridgeObject) cache.get(co));
}
public List<GmlId> getBridgeIds() {
return bridges; return bridges;
} }
public List<CityFurniture> getCityFurniture() { public int getBridgeCount(){
return cityfurniture; return bridges.size();
} }
public void addCityFurniture(CityFurniture coFurniture) { public void addCityFurniture(CityFurniture coFurniture) {
cityfurniture.add(coFurniture); cityfurniture.add(coFurniture.getGmlId());
cache.put(coFurniture);
}
public Stream<CityFurniture> getCityFurniture() {
return cityfurniture.stream().map(co -> (CityFurniture) cache.get(co));
}
public List<GmlId> getCityFurnitureIds() {
return cityfurniture;
}
public int getCityFurnitureCount(){
return cityfurniture.size();
}
public void addOtherCityObject(CityObject otherCityObject) {
genericObjects.add(otherCityObject.getGmlId());
cache.put(otherCityObject);
} }
public List<GenericCityObject> getGenericCityObjects() { public Stream<CityObject> getOtherCityObjects() {
return genericObjects.stream().map(cache::get);
}
public List<GmlId> getOtherCityObjectsIds() {
return genericObjects; return genericObjects;
} }
public void addGenericCityObject(GenericCityObject coGenericCityObject) { public int getOtherCityObjectsCount(){
genericObjects.add(coGenericCityObject); return genericObjects.size();
} }
public void setCityModel(CityModel cModel) { public void addTransportation(TransportationObject to) {
this.cModel = cModel; roads.add(to.getGmlId());
cache.put(to);
} }
public CityModel getCityModel() { public Stream<TransportationObject> getTransportation() {
return cModel; return roads.stream().map(co -> (TransportationObject) cache.get(co));
} }
public List<TransportationObject> getTransportation() { public List<GmlId> getTransportationIds() {
return roads; return roads;
} }
public List<Tunnel> getTunnels() { public int getTransportationCount(){
return tunnels; return roads.size();
} }
public List<WaterObject> getWater() { public void addTunnel(Tunnel tunnel) {
return water; tunnels.add(tunnel.getGmlId());
cache.put(tunnel);
} }
public List<CityObject> getLand() { public Stream<Tunnel> getTunnels() {
return land; return tunnels.stream().map(co -> (Tunnel) cache.get(co));
} }
public List<Vegetation> getVegetation() { public List<GmlId> getTunnelIds() {
return vegetation; return tunnels;
}
public int getTunnelCount(){
return tunnels.size();
} }
public void addWater(WaterObject wo) { public void addWater(WaterObject wo) {
water.add(wo); water.add(wo.getGmlId());
cache.put(wo);
}
public Stream<WaterObject> getWater() {
return water.stream().map(co -> (WaterObject) cache.get(co));
}
public List<GmlId> getWaterIds() {
return water;
}
public int getWaterCount(){
return water.size();
} }
public void addLand(LandObject lo) { public void addLand(LandObject lo) {
land.add(lo); land.add(lo.getGmlId());
cache.put(lo);
}
public void addRelief(ReliefObject relief) {
land.add(relief.getGmlId());
cache.put(relief);
}
public void addTin(TinObject tin) {
land.add(tin.getGmlId());
cache.put(tin);
}
public Stream<CityObject> getLand() {
return land.stream().map(cache::get);
}
public List<GmlId> getLandIds() {
return land;
}
public int getLandCount(){
return land.size();
} }
public void addVegetation(Vegetation veg) { public void addVegetation(Vegetation veg) {
vegetation.add(veg); vegetation.add(veg.getGmlId());
cache.put(veg);
} }
public void addTransportation(TransportationObject to) { public Stream<Vegetation> getVegetation() {
roads.add(to); return vegetation.stream().map(co -> (Vegetation) cache.get(co));
} }
public void addTunnel(Tunnel tunnel) { public List<GmlId> getVegetationIds() {
tunnels.add(tunnel); return vegetation;
}
public int getVegetationCount(){
return vegetation.size();
}
public void setCityModel(CityModel cModel) {
this.cModel = cModel;
}
public CityModel getCityModel() {
return cModel;
}
public CityObjectCache getCache(){
return cache;
} }
public int getNumberOfFeatures() { public int getNumberOfFeatures() {
...@@ -349,100 +503,109 @@ public class CityDoctorModel { ...@@ -349,100 +503,109 @@ public class CityDoctorModel {
} }
private void replaceWaterObject(CityObject currentFeature, CityObject nextFeature) { private void replaceWaterObject(CityObject currentFeature, CityObject nextFeature) {
int index = water.indexOf(currentFeature); int index = water.indexOf(currentFeature.getGmlId());
if (index == -1) { if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in water objects"); throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in water objects");
} }
water.set(index, (WaterObject) nextFeature); cache.replace(currentFeature.getGmlId(), nextFeature);
water.set(index, nextFeature.getGmlId());
} }
private void replaceLandObject(CityObject currentFeature, CityObject nextFeature) { private void replaceLandObject(CityObject currentFeature, CityObject nextFeature) {
int index = land.indexOf(currentFeature); int index = land.indexOf(currentFeature.getGmlId());
if (index == -1) { if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in land objects"); throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in land objects");
} }
land.set(index, nextFeature); cache.replace(currentFeature.getGmlId(), nextFeature);
land.set(index, nextFeature.getGmlId());
} }
private void replaceVegetation(CityObject currentFeature, CityObject nextFeature) { private void replaceVegetation(CityObject currentFeature, CityObject nextFeature) {
int index = vegetation.indexOf(currentFeature); int index = vegetation.indexOf(currentFeature.getGmlId());
if (index == -1) { if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in vegetation"); throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in vegetation");
} }
vegetation.set(index, (Vegetation) nextFeature); cache.replace(currentFeature.getGmlId(), nextFeature);
vegetation.set(index, nextFeature.getGmlId());
} }
private void replaceCityFurniture(CityObject currentFeature, CityObject nextFeature) { private void replaceCityFurniture(CityObject currentFeature, CityObject nextFeature) {
int index = cityfurniture.indexOf(currentFeature); int index = cityfurniture.indexOf(currentFeature.getGmlId());
if (index == -1) { if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in vegetation"); throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in vegetation");
} }
cityfurniture.set(index, (CityFurniture) nextFeature); cache.replace(currentFeature.getGmlId(), nextFeature);
cityfurniture.set(index, nextFeature.getGmlId());
} }
private void replaceGenericCityObject(CityObject currentFeature, CityObject nextFeature) { private void replaceGenericCityObject(CityObject currentFeature, CityObject nextFeature) {
int index = genericObjects.indexOf(currentFeature); int index = genericObjects.indexOf(currentFeature.getGmlId());
if (index == -1) { if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in generic city objects"); throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in generic city objects");
} }
genericObjects.set(index, (GenericCityObject) nextFeature); cache.replace(currentFeature.getGmlId(), nextFeature);
genericObjects.set(index, nextFeature.getGmlId());
} }
private void replaceTransportationObject(CityObject currentFeature, CityObject nextFeature) { private void replaceTransportationObject(CityObject currentFeature, CityObject nextFeature) {
int index = roads.indexOf(currentFeature); int index = roads.indexOf(currentFeature.getGmlId());
if (index == -1) { if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in transportation objects"); throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in transportation objects");
} }
roads.set(index, (TransportationObject) nextFeature); cache.replace(currentFeature.getGmlId(), nextFeature);
roads.set(index, nextFeature.getGmlId());
} }
private void replaceTunnel(CityObject currentFeature, CityObject nextFeature) { private void replaceTunnel(CityObject currentFeature, CityObject nextFeature) {
int index = tunnels.indexOf(currentFeature); int index = tunnels.indexOf(currentFeature.getGmlId());
if (index == -1) { if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in tunnels"); throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in tunnels");
} }
tunnels.set(index, (Tunnel) nextFeature); cache.replace(currentFeature.getGmlId(), nextFeature);
tunnels.set(index, nextFeature.getGmlId());
} }
private void replaceBridge(CityObject currentFeature, CityObject nextFeature) { private void replaceBridge(CityObject currentFeature, CityObject nextFeature) {
int index = bridges.indexOf(currentFeature); int index = bridges.indexOf(currentFeature.getGmlId());
if (index == -1) { if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in bridges"); throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in bridges");
} }
bridges.set(index, (BridgeObject) nextFeature); cache.replace(currentFeature.getGmlId(), nextFeature);
bridges.set(index, nextFeature.getGmlId());
} }
private void replaceBuilding(CityObject currentFeature, CityObject nextFeature) { private void replaceBuilding(CityObject currentFeature, CityObject nextFeature) {
int index = buildings.indexOf(currentFeature); int index = buildings.indexOf(currentFeature.getGmlId());
if (index == -1) { if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in buildings"); throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in buildings");
} }
buildings.set(index, (Building) nextFeature); cache.replace(currentFeature.getGmlId(), nextFeature);
buildings.set(index, nextFeature.getGmlId());
} }
public void addCityObject(CityObject co) { public void addCityObject(CityObject co) {
if (co instanceof Building b) { if (co instanceof Building b) {
buildings.add(b); buildings.add(b.getGmlId());
} else if (co instanceof BridgeObject bo) { } else if (co instanceof BridgeObject bo) {
bridges.add(bo); bridges.add(bo.getGmlId());
} else if (co instanceof TransportationObject to) { } else if (co instanceof TransportationObject to) {
roads.add(to); roads.add(to.getGmlId());
} else if (co instanceof Tunnel tu) { } else if (co instanceof Tunnel tu) {
tunnels.add(tu); tunnels.add(tu.getGmlId());
} else if (co instanceof Vegetation veg) { } else if (co instanceof Vegetation veg) {
vegetation.add(veg); vegetation.add(veg.getGmlId());
} else if (co instanceof LandObject lo) { } else if (co instanceof LandObject lo) {
land.add(lo); land.add(lo.getGmlId());
} else if (co instanceof WaterObject wo) { } else if (co instanceof WaterObject wo) {
water.add(wo); water.add(wo.getGmlId());
} else if (co instanceof ReliefObject) { } else if (co instanceof ReliefObject) {
land.add(co); land.add(co.getGmlId());
} else if (co instanceof TinObject) { } else if (co instanceof TinObject) {
land.add(co); land.add(co.getGmlId());
} else if (co instanceof CityFurniture cf) { } else if (co instanceof CityFurniture cf) {
cityfurniture.add(cf); cityfurniture.add(cf.getGmlId());
} else if (co instanceof GenericCityObject gco) { } else if (co instanceof GenericCityObject gco) {
genericObjects.add(gco); genericObjects.add(gco.getGmlId());
} }
} }
...@@ -454,11 +617,4 @@ public class CityDoctorModel { ...@@ -454,11 +617,4 @@ public class CityDoctorModel {
return cityGMLVersion; return cityGMLVersion;
} }
public void addRelief(ReliefObject relief) {
addCityObject(relief);
}
public void addTin(TinObject tin) {
addCityObject(tin);
}
} }
...@@ -27,5 +27,5 @@ public enum FeatureType { ...@@ -27,5 +27,5 @@ public enum FeatureType {
BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, INSTALLATION, OPENING, BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, INSTALLATION, OPENING,
BUILDING_PART, BUILDING_SUBDIVISION, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE, BUILDING_PART, BUILDING_SUBDIVISION, BRIDGE_CONSTRUCTION_ELEMENT, BRIDGE_INSTALLATION, ROOM, FURNITURE, CITY_FURNITURE,
GENERIC_CITY_OBJECT, TUNNEL, TUNNEL_PART, TUNNEL_CONSTRUCTION_ELEMENT, TUNNEL_INSTALLATION, OTHER_CITY_OBJECT, TUNNEL, TUNNEL_PART, TUNNEL_CONSTRUCTION_ELEMENT, TUNNEL_INSTALLATION,
} }
...@@ -5,8 +5,16 @@ import org.apache.logging.log4j.Logger; ...@@ -5,8 +5,16 @@ import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.AbstractGenericAttributeProperty; import org.citygml4j.core.model.core.AbstractGenericAttributeProperty;
import org.citygml4j.core.model.generics.*; import org.citygml4j.core.model.generics.*;
import java.io.Serial;
import java.io.Serializable;
public class GenericAttribute { import java.util.List;
public class GenericAttribute implements Serializable {
@Serial
private static final long serialVersionUID = -4101408947632132328L;
private static final Logger logger = LogManager.getLogger(GenericAttribute.class); private static final Logger logger = LogManager.getLogger(GenericAttribute.class);
...@@ -45,6 +53,22 @@ public class GenericAttribute { ...@@ -45,6 +53,22 @@ public class GenericAttribute {
} else if (attributeProperty.getObject() instanceof CodeAttribute ca) { } else if (attributeProperty.getObject() instanceof CodeAttribute ca) {
type = String.format("CodeAttribute (%s)", ca.getValue().getLanguage()); type = String.format("CodeAttribute (%s)", ca.getValue().getLanguage());
value = String.format("'''%s''' %n CodeSpace: %s", ca.getValue().getValue(), ca.getValue().getCodeSpace()); value = String.format("'''%s''' %n CodeSpace: %s", ca.getValue().getValue(), ca.getValue().getCodeSpace());
} else if (attributeProperty.getObject() instanceof GenericAttributeSet gas) {
type = "GenericAttributeSet";
List<AbstractGenericAttributeProperty> attributes = gas.getValue();
StringBuilder sb = new StringBuilder();
sb.append("{");
for (AbstractGenericAttributeProperty attribute : attributes) {
if (attribute.getObject() != null) {
sb.append(attribute.getObject().getName());
sb.append(" = ");
sb.append(attribute.getObject().getValue());
sb.append("; ");
}
}
sb.replace(sb.length() - 1, sb.length(), "}");
value = String.format("'%s'", sb);
} else { } else {
logger.warn("GenericAttribute {} is of unknown type {}", attributeProperty, attributeProperty.getObject()); logger.warn("GenericAttribute {} is of unknown type {}", attributeProperty, attributeProperty.getObject());
value = attributeProperty.getObject().getValue().toString(); value = attributeProperty.getObject().getValue().toString();
......
...@@ -111,7 +111,7 @@ public class GenericCityObject extends CityObject { ...@@ -111,7 +111,7 @@ public class GenericCityObject extends CityObject {
cgmlGos.getDeprecatedProperties().setLod4Geometry(new SolidProperty(solid)); cgmlGos.getDeprecatedProperties().setLod4Geometry(new SolidProperty(solid));
break; break;
default: default:
throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to buildings"); throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to generic city object");
} }
} }
@Override @Override
...@@ -121,7 +121,7 @@ public class GenericCityObject extends CityObject { ...@@ -121,7 +121,7 @@ public class GenericCityObject extends CityObject {
@Override @Override
public FeatureType getFeatureType() { public FeatureType getFeatureType() {
return FeatureType.GENERIC_CITY_OBJECT; return FeatureType.OTHER_CITY_OBJECT;
} }
public List<BoundarySurface> getBoundarySurfaces() { public List<BoundarySurface> getBoundarySurfaces() {
......
...@@ -26,6 +26,8 @@ import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator; ...@@ -26,6 +26,8 @@ import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator;
import de.hft.stuttgart.citydoctor2.utils.SerializablePair; import de.hft.stuttgart.citydoctor2.utils.SerializablePair;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serial; import java.io.Serial;
import java.util.*; import java.util.*;
...@@ -53,9 +55,9 @@ public class Geometry extends GmlElement { ...@@ -53,9 +55,9 @@ public class Geometry extends GmlElement {
private CityObject parent; private CityObject parent;
private final List<Polygon> polygons = new ArrayList<>(2); private final List<Polygon> polygons = new ArrayList<>(2);
private List<Edge> edges; private transient List<Edge> edges;
private Map<SerializablePair<Vertex, Vertex>, Edge> edgeMap; private transient Map<SerializablePair<Vertex, Vertex>, Edge> edgeMap;
private List<Vertex> vertices; private transient List<Vertex> vertices;
private Orientation orientation; private Orientation orientation;
public Geometry(GeometryType type, Lod lod, Orientation orientation) { public Geometry(GeometryType type, Lod lod, Orientation orientation) {
...@@ -344,6 +346,12 @@ public class Geometry extends GmlElement { ...@@ -344,6 +346,12 @@ public class Geometry extends GmlElement {
return false; return false;
} }
@Serial
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
edges = new ArrayList<>();
edgeMap = new HashMap<>();
}
public void clearMetaData() { public void clearMetaData() {
if (vertices != null) { if (vertices != null) {
......
package de.hft.stuttgart.citydoctor2.datastructure; package de.hft.stuttgart.citydoctor2.datastructure;
import java.io.Serial; import java.io.Serial;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import de.hft.stuttgart.citydoctor2.check.CheckableVisitor; import de.hft.stuttgart.citydoctor2.check.CheckableVisitor;
import de.hft.stuttgart.citydoctor2.database.CityObjectCache;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.ImplicitGeometry; import org.citygml4j.core.model.core.ImplicitGeometry;
import de.hft.stuttgart.citydoctor2.math.TransformationMatrix; import de.hft.stuttgart.citydoctor2.math.TransformationMatrix;
...@@ -16,9 +22,15 @@ public class ImplicitGeometryHolder extends Geometry { ...@@ -16,9 +22,15 @@ public class ImplicitGeometryHolder extends Geometry {
@Serial @Serial
private static final long serialVersionUID = -8938931081577196349L; private static final long serialVersionUID = -8938931081577196349L;
private static final Logger logger = LogManager.getLogger(ImplicitGeometryHolder.class);
private ImplicitGeometry cgmlImplicitGeometry = null; public enum PrototypeGeometryType {
LIBRARY_OBJECT, RELATIVE_GEOMETRY
}
private ImplicitGeometry cgmlImplicitGeometry = null;
private static ConcurrentHashMap<ImplicitGeometryHolder, ImplicitGeometry> deferredRelGeomObjects = new ConcurrentHashMap<>();
private final PrototypeGeometryType type; private final PrototypeGeometryType type;
private final Geometry prototypeGeometry; private final Geometry prototypeGeometry;
...@@ -38,7 +50,46 @@ public class ImplicitGeometryHolder extends Geometry { ...@@ -38,7 +50,46 @@ public class ImplicitGeometryHolder extends Geometry {
applyTransformation(); applyTransformation();
} }
public static ImplicitGeometryHolder deferRelativeGeomParsing(ImplicitGeometry ig, Lod lod) {
ImplicitGeometryHolder igh = new ImplicitGeometryHolder(lod);
deferredRelGeomObjects.put(igh, ig);
return igh;
}
public static void resolveDeferredObjects(CityObjectCache cache) {
Set<GmlId> missingGeoms = new HashSet<>();
if(!deferredRelGeomObjects.isEmpty()) {
logger.info("Resolving deferred relative geometries");
}
for (ImplicitGeometryHolder key : deferredRelGeomObjects.keySet()) {
ImplicitGeometry ig = deferredRelGeomObjects.get(key);
String hrefId = ig.getRelativeGeometry().getHref();
hrefId = hrefId.startsWith("#") ? hrefId.substring(1) : hrefId;
GmlId gmlId = new GmlId(hrefId);
RelativeGeometry relGeom = RelativeGeometry.of(gmlId);
if (relGeom != null) {
ImplicitGeometryHolder igh = new ImplicitGeometryHolder(ig, relGeom);
CityObject parent = cache.get(key.getParent().getGmlId());
parent.removeGeometry(igh.getLod(), igh.getType());
parent.addGeometry(igh);
} else {
missingGeoms.add(gmlId);
}
}
if (!missingGeoms.isEmpty()) {
logger.warn("RelativeGeometries of the following gmlIDs could not be resolved: {}", missingGeoms);
}
deferredRelGeomObjects.clear();
if (logger.isDebugEnabled()) {
logger.debug("Resolving HRef geometries finished");
}
}
private ImplicitGeometryHolder(Lod lod) {
super(null, lod, null);
type = null;
prototypeGeometry = new Geometry(null, lod, null);
}
/** /**
* Applies the transformation matrix of the implicit geometry to a copy of the reference geometry and copies the resulting * Applies the transformation matrix of the implicit geometry to a copy of the reference geometry and copies the resulting
* transformed geometry into this object. * transformed geometry into this object.
...@@ -54,6 +105,30 @@ public class ImplicitGeometryHolder extends Geometry { ...@@ -54,6 +105,30 @@ public class ImplicitGeometryHolder extends Geometry {
return type; return type;
} }
/**
* Resets the Object maps of {@link RelativeGeometry} and {@link LibraryObject} to prevent memory and data leaks
*/
public static void resetObjectMaps() {
RelativeGeometry.resetObjectMap();
LibraryObject.resetObjectMap();
}
/**
* Prepares the prototype geometries for checking and sets a lock to prevent clearing of metadata
*/
public static void preparePrototypeGeometriesForChecking(){
RelativeGeometry.preparePrototypeGeometriesForChecking();
LibraryObject.preparePrototypeGeometriesForChecking();
}
/**
* Clears metadata creating for checking of prototype geometries and releases the metadata lock
*/
public static void clearMetaDataOfPrototypeGeometries(){
RelativeGeometry.clearMetaDataOfPrototypeGeometries();
LibraryObject.clearMetaDataOfPrototypeGeometries();
}
@Override @Override
public void accept(CheckableVisitor c) { public void accept(CheckableVisitor c) {
prototypeGeometry.accept(c); prototypeGeometry.accept(c);
...@@ -64,8 +139,5 @@ public class ImplicitGeometryHolder extends Geometry { ...@@ -64,8 +139,5 @@ public class ImplicitGeometryHolder extends Geometry {
return prototypeGeometry.isValidated(); return prototypeGeometry.isValidated();
} }
public enum PrototypeGeometryType {
LIBRARY_OBJECT, RELATIVE_GEOMETRY
}
} }
...@@ -4,7 +4,7 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; ...@@ -4,7 +4,7 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; import de.hft.stuttgart.citydoctor2.utils.Localization;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
...@@ -26,8 +26,8 @@ public class LibraryObject extends Geometry { ...@@ -26,8 +26,8 @@ public class LibraryObject extends Geometry {
private static final Logger logger = LogManager.getLogger(LibraryObject.class); private static final Logger logger = LogManager.getLogger(LibraryObject.class);
@Serial @Serial
private static final long serialVersionUID = -50293435187454911L; private static final long serialVersionUID = -50293435187454911L;
private static boolean metadataLocked = false;
private static Map<String, LibraryObject> libraryObjects = new ConcurrentHashMap<>(); private static final Map<String, LibraryObject> libraryObjects = new ConcurrentHashMap<>();
public static LibraryObject of(Path path, ParserConfiguration config) { public static LibraryObject of(Path path, ParserConfiguration config) {
if (libraryObjects.containsKey(path.toString())) { if (libraryObjects.containsKey(path.toString())) {
...@@ -44,22 +44,6 @@ public class LibraryObject extends Geometry { ...@@ -44,22 +44,6 @@ public class LibraryObject extends Geometry {
return libOb; return libOb;
} }
public static LibraryObject of(CityGmlZipEntry entry, ParserConfiguration config) {
String fileName = entry.getEntrySubPath();
if (libraryObjects.containsKey(fileName)){
return libraryObjects.get(fileName);
}
Geometry protoGeom = parseZipEntry(entry, config);
if (protoGeom == null) {
return null;
}
LibraryObject libOb = new LibraryObject(protoGeom.getType(), protoGeom.getLod(), protoGeom.getOrientation());
protoGeom.getPolygons().forEach(libOb::addPolygon);
libOb.updateEdgesAndVertices();
libraryObjects.put(fileName, libOb);
return libOb;
}
private LibraryObject(GeometryType type, Lod lod, Orientation orientation) { private LibraryObject(GeometryType type, Lod lod, Orientation orientation) {
super(type, lod, orientation); super(type, lod, orientation);
} }
...@@ -69,31 +53,15 @@ public class LibraryObject extends Geometry { ...@@ -69,31 +53,15 @@ public class LibraryObject extends Geometry {
Geometry geo = null; Geometry geo = null;
if (Files.exists(path)) { if (Files.exists(path)) {
try { try {
geo = getProtoGeometry(CityGmlParser.parseCityGmlFileSilently(path.toString(), config)); geo = getProtoGeometry(CityGmlParser.nestedCityGmlFileParse(path.toString(), config));
} catch (CityGmlParseException e) { } catch (CityGmlParseException e) {
logger.error(String.format( logger.error(Localization.getText("LibraryObject.parsingError"), path);
"Encountered an error while parsing library object %s", path));
logger.error(e.getStackTrace()); logger.error(e.getStackTrace());
} catch (InvalidGmlFileException e) { } catch (InvalidGmlFileException e) {
logger.error(e.getStackTrace()); logger.error(e.getStackTrace());
} }
} else { } else {
logger.error(String.format("Implicit geometry references non-existing library object file %s.", path)); logger.error(Localization.getText("LibraryObject.fileMissing"), path);
}
return geo;
}
private static Geometry parseZipEntry(CityGmlZipEntry entry, ParserConfiguration config) {
Geometry geo = null;
try {
entry.loadEntry(config);
geo = getProtoGeometry(entry.getModel());
} catch (InvalidGmlFileException e) {
logger.error(e.getStackTrace());
} catch (CityGmlParseException e) {
logger.error(String.format(
"Encountered an error while parsing library object %s", entry.getEntrySubPath()));
logger.error(e.getStackTrace());
} }
return geo; return geo;
} }
...@@ -111,4 +79,33 @@ public class LibraryObject extends Geometry { ...@@ -111,4 +79,33 @@ public class LibraryObject extends Geometry {
return objects.get(0).getHighestLodGeometry(); return objects.get(0).getHighestLodGeometry();
} }
public static void resetObjectMap() {
libraryObjects.clear();
}
public static void preparePrototypeGeometriesForChecking() {
libraryObjects.values().forEach(LibraryObject::updateEdgesAndVertices);
metadataLocked = true;
}
public static void clearMetaDataOfPrototypeGeometries() {
metadataLocked = false;
libraryObjects.values().forEach(LibraryObject::clearMetaData);
}
@Override
public void updateEdgesAndVertices(){
if (!metadataLocked) {
super.updateEdgesAndVertices();
}
}
@Override
public void clearMetaData(){
if (!metadataLocked) {
super.clearMetaData();
}
}
} }
...@@ -44,17 +44,11 @@ public class Opening extends CityObject { ...@@ -44,17 +44,11 @@ public class Opening extends CityObject {
private BoundarySurface partOf; private BoundarySurface partOf;
private OpeningType type; private OpeningType type;
private SurfaceFeatureType featureType;
private AbstractFillingSurface ao; private AbstractFillingSurface ao;
private Opening(OpeningType type) { public Opening(OpeningType type, BoundarySurface partOf,
this.type = type;
}
public Opening(OpeningType type, SurfaceFeatureType featureType, BoundarySurface partOf,
AbstractFillingSurface ao) { AbstractFillingSurface ao) {
this.featureType = featureType;
this.partOf = partOf; this.partOf = partOf;
this.type = type; this.type = type;
this.ao = ao; this.ao = ao;
...@@ -96,11 +90,6 @@ public class Opening extends CityObject { ...@@ -96,11 +90,6 @@ public class Opening extends CityObject {
} }
} }
public SurfaceFeatureType getSurfaceFeatureType() {
return featureType;
}
public BoundarySurface getPartOf() { public BoundarySurface getPartOf() {
return partOf; return partOf;
} }
......
package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.check.CheckableVisitor;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.construction.OtherConstruction;
import org.citygml4j.core.model.core.AbstractCityObject;
import org.citygml4j.core.model.core.AbstractSpaceBoundaryProperty;
import org.citygml4j.core.util.geometry.GeometryFactory;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty;
import org.xmlobjects.gml.model.geometry.primitives.Solid;
import org.xmlobjects.gml.model.geometry.primitives.SolidProperty;
import java.io.Serial;
import java.util.ArrayList;
import java.util.List;
public class OtherConstructionObject extends CityObject{
@Serial
private static final long serialVersionUID = -3885206364978761701L;
private static final Logger logger = LogManager.getLogger(OtherConstructionObject.class);
private final List<BoundarySurface> boundarySurfaceList = new ArrayList<>();
private OtherConstruction gmlObject;
@Override
public void accept(CheckableVisitor c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (BoundarySurface bs : boundarySurfaceList) {
bs.accept(c);
}
}
public void setGmlObject(OtherConstruction gmlObject) {
this.gmlObject = gmlObject;
}
@Override
public void rebuildGeometries(GeometryFactory factory, ParserConfiguration config) {
for (Geometry geom : getGeometries()) {
if (geom instanceof ImplicitGeometryHolder) {
continue;
}
if (geom.getType() == GeometryType.MULTI_SURFACE) {
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
setMultiSurfaceAccordingToLod(geom, ms);
} else {
Solid solid = CityGmlUtils.createSolid(geom, factory, config);
setSolidAccordingToLod(geom, solid);
}
}
removeEmptyBoundarySurfaces();
}
private void removeEmptyBoundarySurfaces() {
for (BoundarySurface bs : boundarySurfaceList) {
if (bs.getGeometries().isEmpty()) {
for (AbstractSpaceBoundaryProperty bsp : gmlObject.getBoundaries()) {
if (bsp.getObject() != null && bsp.getObject() == bs.getGmlObject()) {
logger.warn("Found empty boundary surface: {}, removing from feature", bs.getGmlId());
gmlObject.getBoundaries().remove(bsp);
break;
}
}
}
}
}
private void setMultiSurfaceAccordingToLod(Geometry geom, MultiSurface ms) {
switch (geom.getLod()) {
case LOD0:
gmlObject.setLod0MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
gmlObject.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
gmlObject.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
default:
throw new IllegalStateException("Cannot add " + geom.getLod() + " multi surface to other construction object");
}
}
private void setSolidAccordingToLod(Geometry geom, Solid solid) {
switch (geom.getLod()) {
case LOD1:
gmlObject.setLod1Solid(new SolidProperty(solid));
break;
case LOD2:
gmlObject.setLod2Solid(new SolidProperty(solid));
break;
case LOD3:
gmlObject.setLod3Solid(new SolidProperty(solid));
break;
default:
throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to other construction object");
}
}
@Override
public OtherConstruction getGmlObject() {
return gmlObject;
}
@Override
public void unsetGmlGeometries() {
gmlObject.setLod0MultiSurface(null);
gmlObject.setLod2MultiSurface(null);
gmlObject.setLod3MultiSurface(null);
gmlObject.setLod1Solid(null);
gmlObject.setLod2Solid(null);
gmlObject.setLod3Solid(null);
}
@Override
public CityObject getTopLevelCityObject() {
return this;
}
@Override
public FeatureType getFeatureType() {
return FeatureType.OTHER_CITY_OBJECT;
}
public void addBoundarySurface(BoundarySurface bs) {
boundarySurfaceList.add(bs);
bs.setParent(this);
}
public List<BoundarySurface> getBoundarySurfaces() {
return boundarySurfaceList;
}
}
...@@ -14,20 +14,53 @@ public class RelativeGeometry extends Geometry { ...@@ -14,20 +14,53 @@ public class RelativeGeometry extends Geometry {
@Serial @Serial
private static final long serialVersionUID = -686112245455298977L; private static final long serialVersionUID = -686112245455298977L;
private static Map<Geometry, RelativeGeometry> relativeGeometries = new ConcurrentHashMap<>(); private static final Map<GmlId, RelativeGeometry> relativeGeometries = new ConcurrentHashMap<>();
private static boolean metadataLocked = false;
public static RelativeGeometry of(Geometry geom) { public static RelativeGeometry of(GmlId gmlId, Geometry geom) {
if (relativeGeometries.containsKey(geom)) { if (relativeGeometries.containsKey(gmlId)) {
return relativeGeometries.get(geom); return relativeGeometries.get(gmlId);
} }
RelativeGeometry relGeo = new RelativeGeometry(geom.getType(), geom.getLod(), geom.getOrientation()); RelativeGeometry relGeo = new RelativeGeometry(geom.getType(), geom.getLod(), geom.getOrientation());
geom.getPolygons().forEach(relGeo::addPolygon); geom.getPolygons().forEach(relGeo::addPolygon);
relGeo.updateEdgesAndVertices(); relGeo.updateEdgesAndVertices();
relativeGeometries.put(geom, relGeo); relativeGeometries.put(gmlId, relGeo);
return relGeo; return relGeo;
} }
public static RelativeGeometry of(GmlId gmlId) {
return relativeGeometries.getOrDefault(gmlId, null);
}
private RelativeGeometry(GeometryType type, Lod lod, Orientation orientation) { private RelativeGeometry(GeometryType type, Lod lod, Orientation orientation) {
super(type, lod, orientation); super(type, lod, orientation);
} }
public static void resetObjectMap() {
relativeGeometries.clear();
}
public static void preparePrototypeGeometriesForChecking() {
relativeGeometries.values().forEach(RelativeGeometry::updateEdgesAndVertices);
metadataLocked = true;
}
public static void clearMetaDataOfPrototypeGeometries() {
metadataLocked = false;
relativeGeometries.values().forEach(RelativeGeometry::clearMetaData);
}
@Override
public void updateEdgesAndVertices(){
if (!metadataLocked) {
super.updateEdgesAndVertices();
}
}
@Override
public void clearMetaData(){
if (!metadataLocked) {
super.clearMetaData();
}
}
} }
...@@ -9,6 +9,8 @@ public class TunnelPart extends AbstractTunnel { ...@@ -9,6 +9,8 @@ public class TunnelPart extends AbstractTunnel {
private Tunnel parent; private Tunnel parent;
private TunnelPart() {
}
public TunnelPart(Tunnel parent) { public TunnelPart(Tunnel parent) {
this.parent = parent; this.parent = parent;
...@@ -34,6 +36,4 @@ public class TunnelPart extends AbstractTunnel { ...@@ -34,6 +36,4 @@ public class TunnelPart extends AbstractTunnel {
return "TunnelPart [id=" + getGmlId() + "]"; return "TunnelPart [id=" + getGmlId() + "]";
} }
private TunnelPart() {
}
} }
...@@ -21,6 +21,8 @@ package de.hft.stuttgart.citydoctor2.datastructure; ...@@ -21,6 +21,8 @@ package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.utils.SerializablePair; import de.hft.stuttgart.citydoctor2.utils.SerializablePair;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serial; import java.io.Serial;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
...@@ -37,7 +39,7 @@ public class Vertex extends Vector3d { ...@@ -37,7 +39,7 @@ public class Vertex extends Vector3d {
@Serial @Serial
private static final long serialVersionUID = -5525361920397934892L; private static final long serialVersionUID = -5525361920397934892L;
private List<SerializablePair<Geometry, HashSet<LinearRing>>> adjacentRings = new ArrayList<>(2); private transient List<SerializablePair<Geometry, HashSet<LinearRing>>> adjacentRings = new ArrayList<>(2);
public Vertex(double x, double y, double z) { public Vertex(double x, double y, double z) {
super(x, y, z); super(x, y, z);
...@@ -91,6 +93,12 @@ public class Vertex extends Vector3d { ...@@ -91,6 +93,12 @@ public class Vertex extends Vector3d {
throw new IllegalStateException("Requested adjacent polygons with Geometry not containing this vertex"); throw new IllegalStateException("Requested adjacent polygons with Geometry not containing this vertex");
} }
@Serial
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
clearAdjacentRings();
}
public Set<Polygon> getAdjacentPolygons(Geometry geom) { public Set<Polygon> getAdjacentPolygons(Geometry geom) {
return getAdjacentPolygonsWithoutNeighbor(geom); return getAdjacentPolygonsWithoutNeighbor(geom);
} }
......
...@@ -24,6 +24,9 @@ import java.util.HashMap; ...@@ -24,6 +24,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import de.hft.stuttgart.citydoctor2.database.FeatureCache;
import de.hft.stuttgart.citydoctor2.database.UnconnectedCache;
import de.hft.stuttgart.citydoctor2.datastructure.OtherConstructionObject;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.CityGMLVersion; import org.citygml4j.core.model.CityGMLVersion;
...@@ -45,6 +48,7 @@ import org.citygml4j.core.model.building.BuildingRoomProperty; ...@@ -45,6 +48,7 @@ import org.citygml4j.core.model.building.BuildingRoomProperty;
import org.citygml4j.core.model.building.BuildingUnit; import org.citygml4j.core.model.building.BuildingUnit;
import org.citygml4j.core.model.building.Storey; import org.citygml4j.core.model.building.Storey;
import org.citygml4j.core.model.construction.AbstractConstruction; import org.citygml4j.core.model.construction.AbstractConstruction;
import org.citygml4j.core.model.construction.OtherConstruction;
import org.citygml4j.core.model.core.AbstractCityObject; import org.citygml4j.core.model.core.AbstractCityObject;
import org.citygml4j.core.model.core.AbstractFeatureWithLifespan; import org.citygml4j.core.model.core.AbstractFeatureWithLifespan;
import org.citygml4j.core.model.core.AbstractGenericAttributeProperty; import org.citygml4j.core.model.core.AbstractGenericAttributeProperty;
...@@ -157,35 +161,32 @@ import de.hft.stuttgart.citydoctor2.datastructure.WaterObject; ...@@ -157,35 +161,32 @@ import de.hft.stuttgart.citydoctor2.datastructure.WaterObject;
import de.hft.stuttgart.citydoctor2.math.graph.KDTree; import de.hft.stuttgart.citydoctor2.math.graph.KDTree;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
public class Citygml3FeatureMapper extends ObjectWalker { public class Citygml3FeatureMapper extends ObjectWalker {
private static final Logger logger = LogManager.getLogger(Citygml3FeatureMapper.class); private static final Logger logger = LogManager.getLogger(Citygml3FeatureMapper.class);
private static FeatureCache featureCache = new FeatureCache();
private final CityDoctorModel model; private final CityDoctorModel model;
private final ParserConfiguration config; private final ParserConfiguration config;
private final Path directory; private final Path directory;
private final CityGmlZipEntry zipEntry;
private final double neighborDistance; private final double neighborDistance;
private Map<String, ConcretePolygon> polygonMap = new HashMap<>(); private Map<String, ConcretePolygon> polygonMap = new HashMap<>();
private Map<String, CompositeCollection> compositeMap = new HashMap<>(); private Map<String, CompositeCollection> compositeMap = new HashMap<>();
private List<ResolvableReference> references = new ArrayList<>(); private List<ResolvableReference> references = new ArrayList<>();
private Map<Vertex, Vertex> vertexMap = new HashMap<>(); private Map<Vertex, Vertex> vertexMap = new HashMap<>();
public Citygml3FeatureMapper(ParserConfiguration config, Path path) { public Citygml3FeatureMapper(ParserConfiguration config, Path path, boolean unconnected) {
this.config = config; this.config = config;
this.directory = path.getParent(); this.directory = path.getParent();
model = new CityDoctorModel(config, path.toFile());
neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces());
zipEntry = null;
}
public Citygml3FeatureMapper(ParserConfiguration config, CityGmlZipEntry entry) { if (unconnected){
this.config = config; model = new CityDoctorModel(config, path.toFile(), new UnconnectedCache());
this.directory = null; } else {
this.zipEntry = entry; featureCache.resetCache();
model = new CityDoctorModel(config, Path.of(entry.getEntrySubPath()).toFile()); ImplicitGeometryHolder.resetObjectMaps();
model = new CityDoctorModel(config, path.toFile(), featureCache);
}
neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces()); neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces());
} }
...@@ -203,6 +204,32 @@ public class Citygml3FeatureMapper extends ObjectWalker { ...@@ -203,6 +204,32 @@ public class Citygml3FeatureMapper extends ObjectWalker {
} }
@Override
public void visit(OtherConstruction gmlOc){
OtherConstructionObject oco = new OtherConstructionObject();
oco.setGmlObject(gmlOc);
mapAbstractOccupiedSpace(gmlOc, oco, Orientation.OUTWARD);
SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
for (AbstractSpaceBoundaryProperty surfaceProp : gmlOc.getBoundaries()) {
if (!surfaceProp.isSetObject()) {
continue;
}
AbstractSpaceBoundary surface = surfaceProp.getObject();
surface.accept(surfaceMapper);
}
for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
oco.addBoundarySurface(bs);
for (Geometry geom : bs.getGeometries()) {
for (Polygon p : geom.getPolygons()) {
p.setPartOfSurface(bs);
}
}
}
finishCityObjectConstruction(oco);
model.addOtherCityObject(oco);
}
@Override @Override
public void visit(org.citygml4j.core.model.cityfurniture.CityFurniture gmlCityFurniture) { public void visit(org.citygml4j.core.model.cityfurniture.CityFurniture gmlCityFurniture) {
CityFurniture cf = new CityFurniture(); CityFurniture cf = new CityFurniture();
...@@ -230,7 +257,7 @@ public class Citygml3FeatureMapper extends ObjectWalker { ...@@ -230,7 +257,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
} }
mapAbstractOccupiedSpace(gos, gco, Orientation.OUTWARD); mapAbstractOccupiedSpace(gos, gco, Orientation.OUTWARD);
finishCityObjectConstruction(gco); finishCityObjectConstruction(gco);
model.addGenericCityObject(gco); model.addOtherCityObject(gco);
} }
@Override @Override
...@@ -1024,30 +1051,39 @@ public class Citygml3FeatureMapper extends ObjectWalker { ...@@ -1024,30 +1051,39 @@ public class Citygml3FeatureMapper extends ObjectWalker {
if (directory != null){ if (directory != null){
Path libraryObjectPath = directory.resolve(ig.getLibraryObject()); Path libraryObjectPath = directory.resolve(ig.getLibraryObject());
libObj = LibraryObject.of(libraryObjectPath, config); libObj = LibraryObject.of(libraryObjectPath, config);
} else {
String libraryObjectPath = ig.getLibraryObject();
Path loPath = zipEntry.resolveLibraryObjectPath(libraryObjectPath);
CityGmlZipEntry libEntry = zipEntry.getArchive().getEntry(loPath.toString());
libObj = LibraryObject.of(libEntry, config);
} }
if (libObj != null) { if (libObj != null) {
igh = new ImplicitGeometryHolder(ig, libObj); igh = new ImplicitGeometryHolder(ig, libObj);
} }
} else if (ig.getRelativeGeometry() != null) { } else if (ig.getRelativeGeometry() != null) {
AbstractGeometry aGeom = ig.getRelativeGeometry().getObject(); AbstractGeometry aGeom = ig.getRelativeGeometry().getObject();
Geometry geom = null;
Lod lod = Lod.values()[lodInt]; Lod lod = Lod.values()[lodInt];
RelativeGeometry relGeo = null; if (aGeom != null) {
if (aGeom instanceof MultiSurface ms) { Geometry geom = null;
geom = parseMultiSurface(ms, lod); if (aGeom instanceof MultiSurface ms) {
} else if (aGeom instanceof Solid s) { geom = parseMultiSurface(ms, lod);
geom = parseSolid(s, lod, orientation); } else if (aGeom instanceof Solid s) {
} else if (aGeom instanceof CompositeSurface cs) { geom = parseSolid(s, lod, orientation);
geom = parseCompositeSurface(cs, lod); } else if (aGeom instanceof CompositeSurface cs) {
} geom = parseCompositeSurface(cs, lod);
if (geom != null) { }
relGeo = RelativeGeometry.of(geom); if (geom != null) {
igh = new ImplicitGeometryHolder(ig, relGeo); geom.setGmlId(new GmlId(aGeom.getId()));
RelativeGeometry relGeo = RelativeGeometry.of(geom.getGmlId(), geom);
igh = new ImplicitGeometryHolder(ig, relGeo);
}
} else if (ig.getRelativeGeometry().getHref() != null) {
String hrefId = ig.getRelativeGeometry().getHref();
hrefId = hrefId.startsWith("#") ? hrefId.substring(1) : hrefId;
GmlId gmlId = new GmlId(hrefId);
RelativeGeometry relGeo = RelativeGeometry.of(gmlId);
if (relGeo == null) {
igh = ImplicitGeometryHolder.deferRelativeGeomParsing(ig, lod);
} else {
igh = new ImplicitGeometryHolder(ig, relGeo);
}
} else {
logger.warn("Relative geometry of GML-ID {} has neither geometry nor an href", ig.getId());
} }
} else { } else {
......
...@@ -54,8 +54,7 @@ public class SurfaceMapper extends ObjectWalker { ...@@ -54,8 +54,7 @@ public class SurfaceMapper extends ObjectWalker {
@Override @Override
public void visit(WallSurface wallSurface) { public void visit(WallSurface wallSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL, BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.WALL, wallSurface);
wallSurface);
handleConstructionSurface(wallSurface, surface); handleConstructionSurface(wallSurface, surface);
surfaces.add(surface); surfaces.add(surface);
} }
...@@ -74,11 +73,11 @@ public class SurfaceMapper extends ObjectWalker { ...@@ -74,11 +73,11 @@ public class SurfaceMapper extends ObjectWalker {
private void readAbstractFillingSurface(AbstractFillingSurface afs, BoundarySurface cdSurface) { private void readAbstractFillingSurface(AbstractFillingSurface afs, BoundarySurface cdSurface) {
Opening o; Opening o;
if (afs instanceof DoorSurface ds) { if (afs instanceof DoorSurface ds) {
o = new Opening(OpeningType.DOOR, SurfaceFeatureType.BUILDING, cdSurface, ds); o = new Opening(OpeningType.DOOR, cdSurface, ds);
} else if (afs instanceof WindowSurface ws) { } else if (afs instanceof WindowSurface ws) {
o = new Opening(OpeningType.WINDOW, SurfaceFeatureType.BUILDING, cdSurface, ws); o = new Opening(OpeningType.WINDOW, cdSurface, ws);
} else { } else {
o = new Opening(OpeningType.UNKNOWN, SurfaceFeatureType.BUILDING, cdSurface, afs); o = new Opening(OpeningType.UNKNOWN, cdSurface, afs);
} }
cdSurface.addOpening(o); cdSurface.addOpening(o);
parseAndAddMultiSurface(afs.getLod0MultiSurface(), Lod.LOD0, o); parseAndAddMultiSurface(afs.getLod0MultiSurface(), Lod.LOD0, o);
...@@ -144,64 +143,56 @@ public class SurfaceMapper extends ObjectWalker { ...@@ -144,64 +143,56 @@ public class SurfaceMapper extends ObjectWalker {
@Override @Override
public void visit(FloorSurface floorSurface) { public void visit(FloorSurface floorSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.FLOOR, BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.FLOOR, floorSurface);
floorSurface);
handleConstructionSurface(floorSurface, surface); handleConstructionSurface(floorSurface, surface);
surfaces.add(surface); surfaces.add(surface);
} }
@Override @Override
public void visit(CeilingSurface ceilingSurface) { public void visit(CeilingSurface ceilingSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.CEILING, BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.CEILING, ceilingSurface);
ceilingSurface);
handleConstructionSurface(ceilingSurface, surface); handleConstructionSurface(ceilingSurface, surface);
surfaces.add(surface); surfaces.add(surface);
} }
@Override @Override
public void visit(GroundSurface groundSurface) { public void visit(GroundSurface groundSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.GROUND, BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.GROUND, groundSurface);
groundSurface);
handleConstructionSurface(groundSurface, surface); handleConstructionSurface(groundSurface, surface);
surfaces.add(surface); surfaces.add(surface);
} }
@Override @Override
public void visit(InteriorWallSurface interiorWallSurface) { public void visit(InteriorWallSurface interiorWallSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.INTERIOR_WALL, BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.INTERIOR_WALL, interiorWallSurface);
interiorWallSurface);
handleConstructionSurface(interiorWallSurface, surface); handleConstructionSurface(interiorWallSurface, surface);
surfaces.add(surface); surfaces.add(surface);
} }
@Override @Override
public void visit(OuterCeilingSurface outerCeilingSurface) { public void visit(OuterCeilingSurface outerCeilingSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.OUTER_CEILING, BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.OUTER_CEILING, outerCeilingSurface);
outerCeilingSurface);
handleConstructionSurface(outerCeilingSurface, surface); handleConstructionSurface(outerCeilingSurface, surface);
surfaces.add(surface); surfaces.add(surface);
} }
@Override @Override
public void visit(OuterFloorSurface outerFloorSurface) { public void visit(OuterFloorSurface outerFloorSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.OUTER_FLOOR, BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.OUTER_FLOOR, outerFloorSurface);
outerFloorSurface);
handleConstructionSurface(outerFloorSurface, surface); handleConstructionSurface(outerFloorSurface, surface);
surfaces.add(surface); surfaces.add(surface);
} }
@Override @Override
public void visit(RoofSurface roofSurface) { public void visit(RoofSurface roofSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.ROOF, BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.ROOF, roofSurface);
roofSurface);
handleConstructionSurface(roofSurface, surface); handleConstructionSurface(roofSurface, surface);
surfaces.add(surface); surfaces.add(surface);
} }
@Override @Override
public void visit(ClosureSurface closureSurface) { public void visit(ClosureSurface closureSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.CLOSURE, BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.CLOSURE, closureSurface);
closureSurface);
handleThematicSurface(closureSurface, surface); handleThematicSurface(closureSurface, surface);
surfaces.add(surface); surfaces.add(surface);
} }
......
...@@ -87,8 +87,13 @@ public class Triangle3d implements Serializable { ...@@ -87,8 +87,13 @@ public class Triangle3d implements Serializable {
Vector3d n2 = v1.cross(v2); Vector3d n2 = v1.cross(v2);
return new Plane(n2, p1); return new Plane(n2, p1);
} }
public boolean doesIntersect(Triangle3d other) { public boolean doesIntersect(Triangle3d other) {
return doesIntersect(other, EPSILON);
}
public boolean doesIntersect(Triangle3d other, double epsilon) {
// plane of other triangle // plane of other triangle
Plane planeT2 = other.getPlane(); Plane planeT2 = other.getPlane();
// check if all points are on one side of the plane // check if all points are on one side of the plane
...@@ -96,13 +101,13 @@ public class Triangle3d implements Serializable { ...@@ -96,13 +101,13 @@ public class Triangle3d implements Serializable {
double distanceP2T2 = planeT2.getSignedDistance(p2); double distanceP2T2 = planeT2.getSignedDistance(p2);
double distanceP3T2 = planeT2.getSignedDistance(p3); double distanceP3T2 = planeT2.getSignedDistance(p3);
if (Math.abs(distanceP1T2) < EPSILON) { if (Math.abs(distanceP1T2) < epsilon) {
distanceP1T2 = 0.0; distanceP1T2 = 0.0;
} }
if (Math.abs(distanceP2T2) < EPSILON) { if (Math.abs(distanceP2T2) < epsilon) {
distanceP2T2 = 0.0; distanceP2T2 = 0.0;
} }
if (Math.abs(distanceP3T2) < EPSILON) { if (Math.abs(distanceP3T2) < epsilon) {
distanceP3T2 = 0.0; distanceP3T2 = 0.0;
} }
...@@ -123,13 +128,13 @@ public class Triangle3d implements Serializable { ...@@ -123,13 +128,13 @@ public class Triangle3d implements Serializable {
double distanceP2T1 = planeT1.getSignedDistance(other.getP2()); double distanceP2T1 = planeT1.getSignedDistance(other.getP2());
double distanceP3T1 = planeT1.getSignedDistance(other.getP3()); double distanceP3T1 = planeT1.getSignedDistance(other.getP3());
if (Math.abs(distanceP1T1) < EPSILON) { if (Math.abs(distanceP1T1) < epsilon) {
distanceP1T1 = 0.0; distanceP1T1 = 0.0;
} }
if (Math.abs(distanceP2T1) < EPSILON) { if (Math.abs(distanceP2T1) < epsilon) {
distanceP2T1 = 0.0; distanceP2T1 = 0.0;
} }
if (Math.abs(distanceP3T1) < EPSILON) { if (Math.abs(distanceP3T1) < epsilon) {
distanceP3T1 = 0.0; distanceP3T1 = 0.0;
} }
...@@ -139,11 +144,15 @@ public class Triangle3d implements Serializable { ...@@ -139,11 +144,15 @@ public class Triangle3d implements Serializable {
return false; return false;
} }
return checkTriangleLineIntersection(other, p1, p2) || checkTriangleLineIntersection(other, p1, p3) boolean intersects = checkTriangleLineIntersection(other, p1, p2) || checkTriangleLineIntersection(other, p1, p3)
|| checkTriangleLineIntersection(other, p2, p3) || checkTriangleLineIntersection(other, p2, p3)
|| checkTriangleLineIntersection(this, other.p1, other.p2) || checkTriangleLineIntersection(this, other.p1, other.p2)
|| checkTriangleLineIntersection(this, other.p1, other.p3) || checkTriangleLineIntersection(this, other.p1, other.p3)
|| checkTriangleLineIntersection(this, other.p2, other.p3); || checkTriangleLineIntersection(this, other.p2, other.p3);
if (intersects) {
System.out.println();
}
return intersects;
} }
private boolean checkTriangleLineIntersection(Triangle3d other, Vector3d a, Vector3d b) { private boolean checkTriangleLineIntersection(Triangle3d other, Vector3d a, Vector3d b) {
...@@ -171,7 +180,11 @@ public class Triangle3d implements Serializable { ...@@ -171,7 +180,11 @@ public class Triangle3d implements Serializable {
private boolean doesIntersectCoplanarTriangle(Triangle3d other) { private boolean doesIntersectCoplanarTriangle(Triangle3d other) {
Triangle2d t1 = projectTo2d(); Triangle2d t1 = projectTo2d();
Triangle2d t2 = other.projectTo2d(); Triangle2d t2 = other.projectTo2d();
return t1.intersects(t2); boolean intersects = t1.intersects(t2);
if (intersects) {
System.out.println();
}
return intersects;
} }
public Triangle2d projectTo2d() { public Triangle2d projectTo2d() {
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
package de.hft.stuttgart.citydoctor2.parser; package de.hft.stuttgart.citydoctor2.parser;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
...@@ -44,6 +43,8 @@ import javax.xml.validation.Schema; ...@@ -44,6 +43,8 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator; import javax.xml.validation.Validator;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
...@@ -78,7 +79,6 @@ import org.xml.sax.InputSource; ...@@ -78,7 +79,6 @@ import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xmlobjects.schema.SchemaHandler; import org.xmlobjects.schema.SchemaHandler;
import org.xmlobjects.schema.SchemaHandlerException; import org.xmlobjects.schema.SchemaHandlerException;
import org.xmlobjects.stream.XMLReader; import org.xmlobjects.stream.XMLReader;
...@@ -90,8 +90,6 @@ import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper; ...@@ -90,8 +90,6 @@ import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler;
import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile;
import de.hft.stuttgart.quality.QualityADEContext; import de.hft.stuttgart.quality.QualityADEContext;
import de.hft.stuttgart.quality.QualityADEModule; import de.hft.stuttgart.quality.QualityADEModule;
...@@ -157,33 +155,111 @@ public class CityGmlParser { ...@@ -157,33 +155,111 @@ public class CityGmlParser {
ADERegistry adeRegistry = ADERegistry.getInstance(); ADERegistry adeRegistry = ADERegistry.getInstance();
adeRegistry.loadADE(new QualityADEContext()); adeRegistry.loadADE(new QualityADEContext());
} catch (CityGMLContextException e) { } catch (CityGMLContextException e) {
logger.fatal("Unable to create citygml4j context", e); logger.fatal(Localization.getText("CityGmlParser.creating4jContextFailed"), e);
throw new IllegalStateException("Unable to create citygml4j context"); throw new IllegalStateException("Unable to create citygml4j context");
} catch (ADEException e) { } catch (ADEException e) {
logger.fatal("Unable to add ADE plugins to citygml4j", e); logger.fatal(Localization.getText("CityGmlParser.creatingAdePluginsFailed"), e);
throw new IllegalStateException("Unable to add ADE plugins to citygml4j"); throw new IllegalStateException("Unable to add ADE plugins to citygml4j");
} }
} }
return context; return context;
} }
public static CityDoctorModel parseCityGmlFileSilently(String file, ParserConfiguration config) /**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* <p/>
* The nested parse uses an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache}, which
* is not connected to the database.
* <p/>
* Nested parsing should only be used for accessing Features in auxiliary, small CityGML files, e.g. parsing of
* {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject LibraryObject} files.
*
* @param filePath String path to the file
* @param config Configuration of the parser
* @return A {@link CityDoctorModel} object with the parsed Features
* @throws CityGmlParseException if parsing of the CityGML-file failed
* @throws InvalidGmlFileException if the GMLValidationHandler found an error
*/
public static CityDoctorModel nestedCityGmlFileParse(String filePath, ParserConfiguration config)
throws CityGmlParseException, InvalidGmlFileException { throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(file, config, null, null, false); return parseCityGmlFile(filePath, config, null, null, false, true);
} }
public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config) /**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* <p/>
* Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references
* to {@link CityObject} are to be avoided, to allow for the memory management of the cache.
*
* @param filePath String path to the file
* @param config Configuration of the parser
* @return A {@link CityDoctorModel} object with the parsed Features
* @throws CityGmlParseException if parsing of the CityGML-file failed
* @throws InvalidGmlFileException if the GMLValidationHandler found an error
*/
public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config)
throws CityGmlParseException, InvalidGmlFileException { throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(file, config, null, null, true); return parseCityGmlFile(filePath, config, null, null);
} }
public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config, ProgressListener l)
/**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* <p/>
* Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references
* to {@link CityObject} are to be avoided, to allow for the memory management of the cache.
*
* @param filePath String path to the file
* @param config Configuration of the parser
* @param l Listener hook for tracking of parsing progress
* @return A {@link CityDoctorModel} object with the parsed Features
* @throws CityGmlParseException if parsing of the CityGML-file failed
* @throws InvalidGmlFileException if the GMLValidationHandler found an error
*/
public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l)
throws CityGmlParseException, InvalidGmlFileException { throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(file, config, l, null, true); return parseCityGmlFile(filePath, config, l, null);
} }
/**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* <p/>
* Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references
* to {@link CityObject} are to be avoided, to allow for the memory management of the cache.
*
* @param filePath String path to the file
* @param config Configuration of the parser
* @param l Listener hook for tracking of parsing progress
* @param handler Validation handler for GML-file validation
* @return A {@link CityDoctorModel} object with the parsed Features
* @throws CityGmlParseException if parsing of the CityGML-file failed
* @throws InvalidGmlFileException if the GMLValidationHandler found an error
*/
public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l, public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l,
GMLValidationHandler handler, boolean verbose) throws CityGmlParseException, InvalidGmlFileException { GMLValidationHandler handler) throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(filePath, config, l, handler, true, false);
}
/**
* Parses a given CityGML file and collects the contained CityObject Features in a {@link CityDoctorModel}.
* <p/>
* Features are held in a non-persistent, embedded Database with an in-memory upstream cache. Thus, strong references
* to {@link CityObject} are to be avoided, to allow for the memory management of the cache.
*
* @param filePath String path to the file
* @param config Configuration of the parser
* @param l Listener hook for tracking of parsing progress
* @param handler Validation handler for GML-file validation
* @param verbose If verbosity is enabled the parser will write info logs to the logger
* @param unconnected If true the parser will use an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache}
* to collect the parsed CityObjects
* @return A {@link CityDoctorModel} object with the parsed Features
* @throws CityGmlParseException if parsing of the CityGML-file failed
* @throws InvalidGmlFileException if the GMLValidationHandler found an error
*/
private static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l,
GMLValidationHandler handler, boolean verbose, boolean unconnected)
throws CityGmlParseException, InvalidGmlFileException {
CityGMLContext context = getContext(); CityGMLContext context = getContext();
Path file = Paths.get(filePath); Path file = Paths.get(filePath);
if (config.getValidate()) { if (config.getValidate()) {
...@@ -194,14 +270,14 @@ public class CityGmlParser { ...@@ -194,14 +270,14 @@ public class CityGmlParser {
} }
try { try {
parseEpsgCodeFromFile(file, config); parseEpsgCodeFromFile(file, config, verbose);
CityGMLInputFactory in = context.createCityGMLInputFactory() CityGMLInputFactory in = context.createCityGMLInputFactory()
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) {
if (l != null) { if (l != null) {
ois.addListener(l::updateProgress); ois.addListener(l::updateProgress);
} }
return readAndKeepFeatures(config, file, in, ois, verbose); return readAndKeepFeatures(config, file, in, ois, verbose, unconnected);
} }
} catch (CityGMLReadException | IOException e) { } catch (CityGMLReadException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e); throw new CityGmlParseException("Failed to read CityGML file", e);
...@@ -209,118 +285,6 @@ public class CityGmlParser { ...@@ -209,118 +285,6 @@ public class CityGmlParser {
} }
public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config)
throws CityGmlParseException, InvalidGmlFileException, IOException {
return parseCityGmlZipEntry(entry, config, null);
}
public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l)
throws CityGmlParseException, InvalidGmlFileException, IOException {
CityGMLContext context = getContext();
if (config.getValidate()) {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
List<String> messages = validateStream(entryFile.getInputStream(), context);
if (!messages.isEmpty()) {
throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0));
}
} catch (Exception e) {
throw new CityGmlParseException(e);
}
}
return decompressAndParseCityGmlEntry(entry, config, l, context);
}
public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, CityGMLContext context)
throws CityGmlParseException {
return decompressAndParseCityGmlEntry(entry, config, null, context);
}
public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, CityGMLContext context)
throws CityGmlParseException {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
BufferedInputStream bis = new BufferedInputStream(entryFile.getInputStream());
readEpsgCodeFromInputStream(bis, config);
CityGMLInputFactory in = context.createCityGMLInputFactory()
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
try (ObservedInputStream ois = new ObservedInputStream(bis, bis.available())) {
if (l != null) {
ois.addListener(l::updateProgress);
}
return readAndKeepFeatures(config, entry, in, ois, false);
}
} catch (CityGMLReadException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e);
} catch (Exception e) {
throw new CityGmlParseException(e);
}
}
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, CityGmlZipEntry entry,
CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException {
return readAndKeepModel(new Citygml3FeatureMapper(config, entry), inputFactory, ois, verbose);
}
private static List<String> validateStream(InputStream vis, CityGMLContext context) throws CityGmlParseException {
GMLValidationHandler handler = new GMLValidationHandler();
try {
BufferedInputStream bis = new BufferedInputStream(vis);
SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler());
readAdditionalSchemaDefinitions(context, bis, schemaHandler);
Source[] schemas = schemaHandler.getSchemas();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Schema schema = schemaFactory.newSchema(schemas);
Validator validator = schema.newValidator();
validator.setErrorHandler(handler);
validator.validate(new StreamSource(bis));
return handler.getMessages();
} catch (SchemaHandlerException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to validate CityGML file", e);
}
}
private static void readAdditionalSchemaDefinitions(CityGMLContext context, BufferedInputStream bis, SchemaHandler schemaHandler)
throws CityGmlParseException {
bis.mark(Integer.MAX_VALUE);
try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects())
.withSchemaHandler(schemaHandler)
.createReader(bis)) {
reader.nextTag();
bis.reset();
} catch (Exception e) {
throw new CityGmlParseException("Failed to read Schema from stream.", e);
}
}
private static void readEpsgCodeFromInputStream(BufferedInputStream bis, ParserConfiguration config) throws CityGmlParseException {
try {
// Mark start position of GML-"file"
bis.mark(Integer.MAX_VALUE);
// Buffer the first 10000 chars of the Stream, EPSG/envelope info should be found within that range
int peekingWidth = Math.min(10000, bis.available());
byte[] buf = new byte[peekingWidth];
bis.read(buf, 0, peekingWidth);
bis.reset();
parseEpsgCodeFromBuffer(buf, config);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e);
}
}
public static void streamCityGml(CityGmlZipEntry entry, ParserConfiguration config, CityGmlConsumer cityObjectConsumer,
String outputFile) throws CityGmlParseException, IOException {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
parseEpsgCodeFromStream(entryFile.getInputStream(), config);
startReadingCityGmlZipEntry(entry, config, null, cityObjectConsumer, outputFile);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML stream", e);
}
}
public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer,
String outputFile) throws CityGmlParseException { String outputFile) throws CityGmlParseException {
...@@ -335,7 +299,7 @@ public class CityGmlParser { ...@@ -335,7 +299,7 @@ public class CityGmlParser {
public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l, public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException { CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException {
parseEpsgCodeFromFile(file, config); parseEpsgCodeFromFile(file, config, true);
startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile); startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile);
} }
...@@ -373,31 +337,11 @@ public class CityGmlParser { ...@@ -373,31 +337,11 @@ public class CityGmlParser {
private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois, private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file); Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file, false);
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile);
} }
private static void startReadingCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l,
CityGmlConsumer cityObjectConsumer, String outputFile) {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry);
ObservedInputStream ois = new ObservedInputStream(entryFile.getInputStream(), entry.getFileSize())) {
if (l != null) {
ois.addListener(l::updateProgress);
}
streamAndDiscardFeatures(entry, config, ois, cityObjectConsumer, outputFile);
} catch (IOException | CityGMLReadException e) {
logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage());
logger.catching(Level.ERROR, e);
}
}
private static void streamAndDiscardFeatures(CityGmlZipEntry entry, ParserConfiguration config, ObservedInputStream ois,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, entry);
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile);
}
private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois, private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
getContext(); getContext();
...@@ -484,8 +428,9 @@ public class CityGmlParser { ...@@ -484,8 +428,9 @@ public class CityGmlParser {
} }
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file, private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file,
CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException { CityGMLInputFactory inputFactory, ObservedInputStream ois,
return readAndKeepModel(new Citygml3FeatureMapper(config, file), inputFactory, ois, verbose); boolean verbose, boolean unconnected) throws CityGMLReadException {
return readAndKeepModel(new Citygml3FeatureMapper(config, file, unconnected), inputFactory, ois, verbose);
} }
private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory, private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory,
...@@ -496,52 +441,75 @@ public class CityGmlParser { ...@@ -496,52 +441,75 @@ public class CityGmlParser {
// object members are replaced by href in model // object members are replaced by href in model
// need to remove the refs and re-add unparsed objects // need to remove the refs and re-add unparsed objects
List<AbstractCityObject> acos = new ArrayList<>(); List<AbstractCityObject> acos = new ArrayList<>();
while (reader.hasNext()) { CityModel cgmlModel = null;
AbstractFeature chunk = reader.next(); CityDoctorModel cdModel = mapper.getModel();
version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); AutoCloseable parsingContext = cdModel.getCache().startParsingMode();
if (chunk instanceof CityModel cModel) { try (parsingContext){
cModel.setCityObjectMembers(null); List<AbstractCityObject> queuedObjects = new ArrayList<>();
mapper.setCityModel(cModel); while (reader.hasNext()) {
mapper.setCityGMLVersion(version); AbstractFeature chunk = reader.next();
} else if (chunk instanceof AbstractCityObject aco) { version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI());
acos.add(aco); if (chunk instanceof CityModel cModel) {
aco.accept(mapper); cModel.setCityObjectMembers(null);
mapper.setCityModel(cModel);
mapper.setCityGMLVersion(version);
cgmlModel = cModel;
if (!queuedObjects.isEmpty()) {
for (AbstractCityObject aco : queuedObjects) {
if (!cdModel.containsGmlId(aco.getId())) {
cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
}
}
}
} else if (chunk instanceof AbstractCityObject aco) {
acos.add(aco); // getid
String id = aco.getId();
aco.accept(mapper);
if (cgmlModel == null) {
// Add object to queue till the CityModel has been parsed
queuedObjects.add(aco);
}else if (!cdModel.containsGmlId(id)){
cgmlModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
}
}
} }
}
if (mapper.getModel().getCityModel() == null) { if (mapper.getModel().getCityModel() == null) {
// file does not contain a city model? // file does not contain a city model?
// create it for now // create it for now
mapper.setCityModel(new CityModel()); logger.warn(Localization.getText("CityGmlParser.missingCityModel"));
} mapper.setCityModel(new CityModel());
CityModel cModel = mapper.getModel().getCityModel(); for (AbstractCityObject aco : queuedObjects) {
if (!cdModel.containsGmlId(aco.getId())) {
// remove those that should have been parsed mapper.getModel().getCityModel().getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
List<AbstractCityObject> parsedCityObjects = mapper.getModel().createFeatureStream() }
.map(CityObject::getGmlObject).toList(); }
acos.removeAll(parsedCityObjects); }
// re-add all not parsed objects
for (AbstractCityObject aco : acos) { ImplicitGeometryHolder.resolveDeferredObjects(mapper.getModel().getCache());
cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); if (logger.isInfoEnabled() && verbose) {
} logger.info(Localization.getText("CityGmlParser.parsedObjects"),
if (logger.isInfoEnabled() && verbose) { mapper.getModel().getNumberOfFeatures());
logger.info(Localization.getText("CityGmlParser.parsedObjects"), }
mapper.getModel().getNumberOfFeatures()); mapper.setCityGMLVersion(version);
return mapper.getModel();
} catch (Exception e) {
throw new CityGMLReadException(e);
} }
mapper.setCityGMLVersion(version);
return mapper.getModel();
} }
} }
private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config) throws CityGmlParseException { private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config, boolean verbose) throws CityGmlParseException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) { try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) {
parseEpsgCodeFromStream(bis, config); parseEpsgCodeFromStream(bis, config, verbose);
} catch (ParserConfigurationException | SAXException | IOException e) { } catch (ParserConfigurationException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e); throw new CityGmlParseException("Failed to read CityGML file", e);
} }
} }
private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config) private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config, boolean verbose)
throws ParserConfigurationException, SAXException { throws ParserConfigurationException, SAXException {
SAXParser parser = FACTORY.newSAXParser(); SAXParser parser = FACTORY.newSAXParser();
CityGmlHandler handler = new CityGmlHandler(); CityGmlHandler handler = new CityGmlHandler();
...@@ -549,46 +517,19 @@ public class CityGmlParser { ...@@ -549,46 +517,19 @@ public class CityGmlParser {
parser.parse(new InputSource(is), handler); parser.parse(new InputSource(is), handler);
} catch (EnvelopeFoundException e) { } catch (EnvelopeFoundException e) {
try { try {
parseCoordinateSystem(config, handler); parseCoordinateSystem(config, handler, verbose);
} catch (Exception e2) { } catch (Exception e2) {
logEpsgParseError(e2); logEpsgParseError(e2);
} }
} catch (Exception e) { } catch (Exception e) {
logEpsgParseError(e); logEpsgParseError(e);
} }
if (handler.getEpsg() == null && logger.isInfoEnabled()) { if (handler.getEpsg() == null && logger.isInfoEnabled() && verbose) {
logger.info(Localization.getText("CityGmlParser.missingEPSGCode")); logger.info(Localization.getText("CityGmlParser.missingEPSGCode"));
} }
} }
private static void parseEpsgCodeFromBuffer(byte[] buffer, ParserConfiguration config)
throws ParserConfigurationException, SAXException {
InputStream is = new ByteArrayInputStream(buffer);
SAXParser parser = FACTORY.newSAXParser();
CityGmlHandler handler = new CityGmlHandler();
try {
parser.parse(new InputSource(is), handler);
} catch (EnvelopeFoundException e) {
try {
parseCoordinateSystem(config, handler);
} catch (Exception e2) {
logEpsgParseError(e2);
}
} catch (SAXParseException spe) {
// suppress XML document structure warning
if (!spe.getMessage().matches("XML document structures must start and end within the same entity.")) {
logEpsgParseError(spe);
}
} catch (Exception e) {
logEpsgParseError(e);
}
if (handler.getEpsg() == null && logger.isInfoEnabled()) {
logger.info(Localization.getText("CityGmlParser.missingEPSGCode"));
}
}
private static void logEpsgParseError(Exception e) { private static void logEpsgParseError(Exception e) {
logger.debug("Exception while parsing for EPSG code", e); logger.debug("Exception while parsing for EPSG code", e);
if (logger.isWarnEnabled()) { if (logger.isWarnEnabled()) {
...@@ -596,7 +537,7 @@ public class CityGmlParser { ...@@ -596,7 +537,7 @@ public class CityGmlParser {
} }
} }
private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler) { private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler, boolean verbose) {
if (handler.getEpsg() == null) { if (handler.getEpsg() == null) {
return; return;
} }
...@@ -604,14 +545,14 @@ public class CityGmlParser { ...@@ -604,14 +545,14 @@ public class CityGmlParser {
if (crs == null) { if (crs == null) {
// could not find a coordinate system for srsName // could not find a coordinate system for srsName
// assuming metric system // assuming metric system
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled() && verbose) {
logger.info(Localization.getText("CityGmlParser.missingEPSGCode")); logger.info(Localization.getText("CityGmlParser.missingEPSGCode"));
} }
return; return;
} }
if (crs.getProjection().getUnits() == Units.METRES) { if (crs.getProjection().getUnits() == Units.METRES) {
// coordinate system is in meters, do not convert // coordinate system is in meters, do not convert
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled() && verbose) {
logger.info(Localization.getText("CityGmlParser.noConversionNeeded")); logger.info(Localization.getText("CityGmlParser.noConversionNeeded"));
} }
return; return;
...@@ -636,11 +577,15 @@ public class CityGmlParser { ...@@ -636,11 +577,15 @@ public class CityGmlParser {
CoordinateReferenceSystem utm; CoordinateReferenceSystem utm;
if (centerLat < 0) { if (centerLat < 0) {
// south // south
logger.info("Converting coordiante system to UTM zone {}S", zone); if (logger.isInfoEnabled() && verbose) {
logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneS"), zone);
}
utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south"); utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south");
} else { } else {
// north // north
logger.info("Converting coordiante system to UTM zone {}N", zone); if (logger.isInfoEnabled() && verbose) {
logger.info(Localization.getText("CityGmlParser.convertCrsToUtmZoneN"), zone);
}
utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone); utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone);
} }
config.setCoordinateSystem(crs, utm); config.setCoordinateSystem(crs, utm);
...@@ -756,22 +701,14 @@ public class CityGmlParser { ...@@ -756,22 +701,14 @@ public class CityGmlParser {
} }
private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) { private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) {
drainCityObjectList(model.getBuildings(), cityObjectConsumer); List<GmlId> id = new ArrayList<>();
drainCityObjectList(model.getBridges(), cityObjectConsumer); model.createFeatureStream().forEach(co -> {
drainCityObjectList(model.getVegetation(), cityObjectConsumer);
drainCityObjectList(model.getLand(), cityObjectConsumer);
drainCityObjectList(model.getTransportation(), cityObjectConsumer);
drainCityObjectList(model.getWater(), cityObjectConsumer);
drainCityObjectList(model.getTunnels(), cityObjectConsumer);
drainCityObjectList(model.getCityFurniture(), cityObjectConsumer);
drainCityObjectList(model.getGenericCityObjects(), cityObjectConsumer);
}
private static void drainCityObjectList(List<? extends CityObject> objects, CityGmlConsumer cityObjectConsumer) {
for (CityObject co : objects) {
cityObjectConsumer.accept(co); cityObjectConsumer.accept(co);
} id.add(co.getGmlId());
objects.clear(); });
id.forEach(model::removeFeature);
} }
} }
...@@ -49,6 +49,15 @@ public class EarcutTesselator { ...@@ -49,6 +49,15 @@ public class EarcutTesselator {
start = addRingToArray(innerRing, vertices, start, axis); start = addRingToArray(innerRing, vertices, start, axis);
} }
// center vertices?
// double[] projectedCenter = new double[2];
// Geometry geometry = p.getParent();
// Vector3d center = geometry.getCenter();
// axis.writeCoordinatesOfVectorInArray(center, projectedCenter, 0);
// for (int i = 0; i < vertices.length; i++) {
// vertices[i] = vertices[i] - projectedCenter[i % 2];
// }
// triangulation // triangulation
List<Integer> indices = Earcut.earcut(vertices, holeStart, 2); List<Integer> indices = Earcut.earcut(vertices, holeStart, 2);
List<Triangle3d> triangles = new ArrayList<>(); List<Triangle3d> triangles = new ArrayList<>();
...@@ -56,14 +65,15 @@ public class EarcutTesselator { ...@@ -56,14 +65,15 @@ public class EarcutTesselator {
throw new IllegalStateException(); throw new IllegalStateException();
} }
for (int i = 0; i < indices.size(); i = i + 3) { for (int i = 0; i < indices.size(); i = i + 3) {
Triangle3d t = new Triangle3d(vertexObjects.get(indices.get(i + 0)), Vertex v1 = vertexObjects.get(indices.get(i + 0));
vertexObjects.get(indices.get(i + 1)), Vertex v2 = vertexObjects.get(indices.get(i + 1));
vertexObjects.get(indices.get(i + 2))); Vertex v3 = vertexObjects.get(indices.get(i + 2));
Triangle3d t = new Triangle3d(v1, v2, v3);
triangles.add(t); triangles.add(t);
} }
return new TesselatedPolygon(triangles, p); return new TesselatedPolygon(triangles, p);
} }
private static void addVerticesToList(LinearRing ring, List<Vertex> vertexObjects) { private static void addVerticesToList(LinearRing ring, List<Vertex> vertexObjects) {
List<Vertex> vertices = ring.getVertices(); List<Vertex> vertices = ring.getVertices();
for (int i = 0; i < vertices.size() - 1; i++) { for (int i = 0; i < vertices.size() - 1; i++) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment