Commit 8b6cdd3b authored by Luna Riegel's avatar Luna Riegel
Browse files

Merge branch 'dev_embedded_geodb' into 'dev'

!Geodatabase implementation

See merge request !36
parents 2fe401b3 e2cce980
Pipeline #12243 passed with stage
in 2 minutes and 14 seconds
......@@ -27,5 +27,5 @@ public enum FeatureType {
BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, INSTALLATION, OPENING,
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,10 +5,16 @@ import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.AbstractGenericAttributeProperty;
import org.citygml4j.core.model.generics.*;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
public class GenericAttribute {
public class GenericAttribute implements Serializable {
@Serial
private static final long serialVersionUID = -4101408947632132328L;
private static final Logger logger = LogManager.getLogger(GenericAttribute.class);
......
......@@ -111,7 +111,7 @@ public class GenericCityObject extends CityObject {
cgmlGos.getDeprecatedProperties().setLod4Geometry(new SolidProperty(solid));
break;
default:
throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to buildings");
throw new IllegalStateException("Cannot add " + geom.getLod() + " solid to generic city object");
}
}
@Override
......@@ -121,7 +121,7 @@ public class GenericCityObject extends CityObject {
@Override
public FeatureType getFeatureType() {
return FeatureType.GENERIC_CITY_OBJECT;
return FeatureType.OTHER_CITY_OBJECT;
}
public List<BoundarySurface> getBoundarySurfaces() {
......
......@@ -26,6 +26,8 @@ import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator;
import de.hft.stuttgart.citydoctor2.utils.SerializablePair;
import javafx.scene.paint.Color;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serial;
import java.util.*;
......@@ -53,9 +55,9 @@ public class Geometry extends GmlElement {
private CityObject parent;
private final List<Polygon> polygons = new ArrayList<>(2);
private List<Edge> edges;
private Map<SerializablePair<Vertex, Vertex>, Edge> edgeMap;
private List<Vertex> vertices;
private transient List<Edge> edges;
private transient Map<SerializablePair<Vertex, Vertex>, Edge> edgeMap;
private transient List<Vertex> vertices;
private Orientation orientation;
public Geometry(GeometryType type, Lod lod, Orientation orientation) {
......@@ -344,6 +346,12 @@ public class Geometry extends GmlElement {
return false;
}
@Serial
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
edges = new ArrayList<>();
edgeMap = new HashMap<>();
}
public void clearMetaData() {
if (vertices != null) {
......
......@@ -6,6 +6,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
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;
......@@ -23,6 +24,10 @@ public class ImplicitGeometryHolder extends Geometry {
private static final long serialVersionUID = -8938931081577196349L;
private static final Logger logger = LogManager.getLogger(ImplicitGeometryHolder.class);
public enum PrototypeGeometryType {
LIBRARY_OBJECT, RELATIVE_GEOMETRY
}
private ImplicitGeometry cgmlImplicitGeometry = null;
private static ConcurrentHashMap<ImplicitGeometryHolder, ImplicitGeometry> deferredRelGeomObjects = new ConcurrentHashMap<>();
......@@ -51,8 +56,11 @@ public class ImplicitGeometryHolder extends Geometry {
return igh;
}
public static void resolveDeferredObjects() {
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();
......@@ -61,7 +69,7 @@ public class ImplicitGeometryHolder extends Geometry {
RelativeGeometry relGeom = RelativeGeometry.of(gmlId);
if (relGeom != null) {
ImplicitGeometryHolder igh = new ImplicitGeometryHolder(ig, relGeom);
CityObject parent = key.getParent();
CityObject parent = cache.get(key.getParent().getGmlId());
parent.removeGeometry(igh.getLod(), igh.getType());
parent.addGeometry(igh);
} else {
......@@ -72,6 +80,9 @@ public class ImplicitGeometryHolder extends Geometry {
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) {
......@@ -94,6 +105,30 @@ public class ImplicitGeometryHolder extends Geometry {
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
public void accept(CheckableVisitor c) {
prototypeGeometry.accept(c);
......@@ -104,8 +139,5 @@ public class ImplicitGeometryHolder extends Geometry {
return prototypeGeometry.isValidated();
}
public enum PrototypeGeometryType {
LIBRARY_OBJECT, RELATIVE_GEOMETRY
}
}
......@@ -4,7 +4,7 @@ import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -26,8 +26,8 @@ public class LibraryObject extends Geometry {
private static final Logger logger = LogManager.getLogger(LibraryObject.class);
@Serial
private static final long serialVersionUID = -50293435187454911L;
private static Map<String, LibraryObject> libraryObjects = new ConcurrentHashMap<>();
private static boolean metadataLocked = false;
private static final Map<String, LibraryObject> libraryObjects = new ConcurrentHashMap<>();
public static LibraryObject of(Path path, ParserConfiguration config) {
if (libraryObjects.containsKey(path.toString())) {
......@@ -44,22 +44,6 @@ public class LibraryObject extends Geometry {
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) {
super(type, lod, orientation);
}
......@@ -69,31 +53,15 @@ public class LibraryObject extends Geometry {
Geometry geo = null;
if (Files.exists(path)) {
try {
geo = getProtoGeometry(CityGmlParser.parseCityGmlFileSilently(path.toString(), config));
geo = getProtoGeometry(CityGmlParser.nestedCityGmlFileParse(path.toString(), config));
} catch (CityGmlParseException e) {
logger.error(String.format(
"Encountered an error while parsing library object %s", path));
logger.error(Localization.getText("LibraryObject.parsingError"), path);
logger.error(e.getStackTrace());
} catch (InvalidGmlFileException e) {
logger.error(e.getStackTrace());
}
} else {
logger.error(String.format("Implicit geometry references non-existing library object file %s.", 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());
logger.error(Localization.getText("LibraryObject.fileMissing"), path);
}
return geo;
}
......@@ -111,4 +79,33 @@ public class LibraryObject extends Geometry {
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,13 +44,11 @@ public class Opening extends CityObject {
private BoundarySurface partOf;
private OpeningType type;
private SurfaceFeatureType featureType;
private AbstractFillingSurface ao;
public Opening(OpeningType type, SurfaceFeatureType featureType, BoundarySurface partOf,
public Opening(OpeningType type, BoundarySurface partOf,
AbstractFillingSurface ao) {
this.featureType = featureType;
this.partOf = partOf;
this.type = type;
this.ao = ao;
......@@ -92,11 +90,6 @@ public class Opening extends CityObject {
}
}
public SurfaceFeatureType getSurfaceFeatureType() {
return featureType;
}
public BoundarySurface getPartOf() {
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;
}
}
......@@ -15,6 +15,7 @@ public class RelativeGeometry extends Geometry {
private static final long serialVersionUID = -686112245455298977L;
private static final Map<GmlId, RelativeGeometry> relativeGeometries = new ConcurrentHashMap<>();
private static boolean metadataLocked = false;
public static RelativeGeometry of(GmlId gmlId, Geometry geom) {
if (relativeGeometries.containsKey(gmlId)) {
......@@ -34,4 +35,32 @@ public class RelativeGeometry extends Geometry {
private RelativeGeometry(GeometryType type, Lod lod, Orientation 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();
}
}
}
......@@ -21,6 +21,8 @@ package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.utils.SerializablePair;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serial;
import java.util.ArrayList;
import java.util.HashSet;
......@@ -37,7 +39,7 @@ public class Vertex extends Vector3d {
@Serial
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) {
super(x, y, z);
......@@ -91,6 +93,12 @@ public class Vertex extends Vector3d {
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) {
return getAdjacentPolygonsWithoutNeighbor(geom);
}
......
......@@ -24,6 +24,9 @@ import java.util.HashMap;
import java.util.List;
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.Logger;
import org.citygml4j.core.model.CityGMLVersion;
......@@ -45,6 +48,7 @@ import org.citygml4j.core.model.building.BuildingRoomProperty;
import org.citygml4j.core.model.building.BuildingUnit;
import org.citygml4j.core.model.building.Storey;
import org.citygml4j.core.model.construction.AbstractConstruction;
import org.citygml4j.core.model.construction.OtherConstruction;
import org.citygml4j.core.model.core.AbstractCityObject;
import org.citygml4j.core.model.core.AbstractFeatureWithLifespan;
import org.citygml4j.core.model.core.AbstractGenericAttributeProperty;
......@@ -157,35 +161,32 @@ import de.hft.stuttgart.citydoctor2.datastructure.WaterObject;
import de.hft.stuttgart.citydoctor2.math.graph.KDTree;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
public class Citygml3FeatureMapper extends ObjectWalker {
private static final Logger logger = LogManager.getLogger(Citygml3FeatureMapper.class);
private static FeatureCache featureCache = new FeatureCache();
private final CityDoctorModel model;
private final ParserConfiguration config;
private final Path directory;
private final CityGmlZipEntry zipEntry;
private final double neighborDistance;
private Map<String, ConcretePolygon> polygonMap = new HashMap<>();
private Map<String, CompositeCollection> compositeMap = new HashMap<>();
private List<ResolvableReference> references = new ArrayList<>();
private Map<Vertex, Vertex> vertexMap = new HashMap<>();
public Citygml3FeatureMapper(ParserConfiguration config, Path path) {
public Citygml3FeatureMapper(ParserConfiguration config, Path path, boolean unconnected) {
this.config = config;
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) {
this.config = config;
this.directory = null;
this.zipEntry = entry;
model = new CityDoctorModel(config, Path.of(entry.getEntrySubPath()).toFile());
if (unconnected){
model = new CityDoctorModel(config, path.toFile(), new UnconnectedCache());
} else {
featureCache.resetCache();
ImplicitGeometryHolder.resetObjectMaps();
model = new CityDoctorModel(config, path.toFile(), featureCache);
}
neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces());
}
......@@ -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
public void visit(org.citygml4j.core.model.cityfurniture.CityFurniture gmlCityFurniture) {
CityFurniture cf = new CityFurniture();
......@@ -230,7 +257,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
mapAbstractOccupiedSpace(gos, gco, Orientation.OUTWARD);
finishCityObjectConstruction(gco);
model.addGenericCityObject(gco);
model.addOtherCityObject(gco);
}
@Override
......@@ -1024,11 +1051,6 @@ public class Citygml3FeatureMapper extends ObjectWalker {
if (directory != null){
Path libraryObjectPath = directory.resolve(ig.getLibraryObject());
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) {
igh = new ImplicitGeometryHolder(ig, libObj);
......
......@@ -54,8 +54,7 @@ public class SurfaceMapper extends ObjectWalker {
@Override
public void visit(WallSurface wallSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.WALL,
wallSurface);
BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.WALL, wallSurface);
handleConstructionSurface(wallSurface, surface);
surfaces.add(surface);
}
......@@ -74,11 +73,11 @@ public class SurfaceMapper extends ObjectWalker {
private void readAbstractFillingSurface(AbstractFillingSurface afs, BoundarySurface cdSurface) {
Opening o;
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) {
o = new Opening(OpeningType.WINDOW, SurfaceFeatureType.BUILDING, cdSurface, ws);
o = new Opening(OpeningType.WINDOW, cdSurface, ws);
} else {
o = new Opening(OpeningType.UNKNOWN, SurfaceFeatureType.BUILDING, cdSurface, afs);
o = new Opening(OpeningType.UNKNOWN, cdSurface, afs);
}
cdSurface.addOpening(o);
parseAndAddMultiSurface(afs.getLod0MultiSurface(), Lod.LOD0, o);
......@@ -144,64 +143,56 @@ public class SurfaceMapper extends ObjectWalker {
@Override
public void visit(FloorSurface floorSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.FLOOR,
floorSurface);
BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.FLOOR, floorSurface);
handleConstructionSurface(floorSurface, surface);
surfaces.add(surface);
}
@Override
public void visit(CeilingSurface ceilingSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.CEILING,
ceilingSurface);
BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.CEILING, ceilingSurface);
handleConstructionSurface(ceilingSurface, surface);
surfaces.add(surface);
}
@Override
public void visit(GroundSurface groundSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.GROUND,
groundSurface);
BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.GROUND, groundSurface);
handleConstructionSurface(groundSurface, surface);
surfaces.add(surface);
}
@Override
public void visit(InteriorWallSurface interiorWallSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.INTERIOR_WALL,
interiorWallSurface);
BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.INTERIOR_WALL, interiorWallSurface);
handleConstructionSurface(interiorWallSurface, surface);
surfaces.add(surface);
}
@Override
public void visit(OuterCeilingSurface outerCeilingSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.OUTER_CEILING,
outerCeilingSurface);
BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.OUTER_CEILING, outerCeilingSurface);
handleConstructionSurface(outerCeilingSurface, surface);
surfaces.add(surface);
}
@Override
public void visit(OuterFloorSurface outerFloorSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.OUTER_FLOOR,
outerFloorSurface);
BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.OUTER_FLOOR, outerFloorSurface);
handleConstructionSurface(outerFloorSurface, surface);
surfaces.add(surface);
}
@Override
public void visit(RoofSurface roofSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.ROOF,
roofSurface);
BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.ROOF, roofSurface);
handleConstructionSurface(roofSurface, surface);
surfaces.add(surface);
}
@Override
public void visit(ClosureSurface closureSurface) {
BoundarySurface surface = new BoundarySurface(SurfaceFeatureType.BUILDING, BoundarySurfaceType.CLOSURE,
closureSurface);
BoundarySurface surface = new BoundarySurface(BoundarySurfaceType.CLOSURE, closureSurface);
handleThematicSurface(closureSurface, surface);
surfaces.add(surface);
}
......
package de.hft.stuttgart.citydoctor2.utils;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ArchivePacker {
private static final Logger logger = LogManager.getLogger(ArchivePacker.class);
private ArchivePacker(){}
public static void packArchive(String targetPath, CityGmlZipArchive archive){
Path outputPath = Path.of(targetPath);
outputPath = outputPath.getParent().resolve(
FilenameUtils.removeExtension(outputPath.getFileName().toString()) + ".zip");
Path tmpDir = null;
try{
tmpDir = Files.createTempDirectory("zipTmp");
tmpDir.toFile().deleteOnExit();
for (CityGmlZipEntry entry : archive.getEntries()) {
if (entry.getErrorType() != null || !entry.isLoaded()) {
continue;
}
CityDoctorModel model = entry.getModel();
Path filePath = tmpDir.resolve(entry.getEntrySubPath());
Files.createDirectories(filePath.getParent());
model.saveAs(filePath.toString(), model.isValidated());
}
zipFolder(tmpDir.toFile(), outputPath.toFile());
}catch (Exception e){
logger.error(e);
} finally {
if (tmpDir != null) {
try {
FileUtils.deleteDirectory(tmpDir.toFile());
} catch (IOException e) {
logger.error(e);
}
}
}
}
public static void zipFolder(File folder, File zipFile) throws IOException {
zipFolder(folder, new FileOutputStream(zipFile));
}
public static void zipFolder(File folder, OutputStream outputStream) throws IOException {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
processFolder(folder, zipOutputStream, folder.getPath().length() + 1);
}
}
private static void processFolder(File folder, ZipOutputStream zipOutputStream, int prefixLength)
throws IOException {
for (File file : folder.listFiles()) {
if (file.isFile()) {
ZipEntry zipEntry = new ZipEntry(file.getPath().substring(prefixLength));
zipOutputStream.putNextEntry(zipEntry);
try (FileInputStream inputStream = new FileInputStream(file)) {
IOUtils.copy(inputStream, zipOutputStream);
}
zipOutputStream.closeEntry();
} else if (file.isDirectory()) {
processFolder(file, zipOutputStream, prefixLength);
}
}
}
public static void packAndDeleteDirectory(String directoryPath) throws IOException {
Path sourcePath = Path.of(directoryPath);
if (!Files.exists(sourcePath)) {
throw new IllegalStateException("Directory " + directoryPath + " does not exist");
}
if (!Files.isDirectory(sourcePath)) {
throw new IllegalStateException("Path " + directoryPath + " is not a directory");
}
Path outputPath = sourcePath.getParent().resolve(sourcePath.getFileName() + ".zip");
zipFolder(sourcePath.toFile(), outputPath.toFile());
FileUtils.deleteDirectory(sourcePath.toFile());
}
}
......@@ -97,7 +97,6 @@ public class BoundingBoxCalculator {
* @return the bounding box of the model
*/
public static BoundingBox calculateBoundingBox(CityDoctorModel model) {
// TODO: Rework to Visitor
MinMaxExtentVisitor mima = new MinMaxExtentVisitor();
model.accept(mima);
......
package de.hft.stuttgart.citydoctor2.utils;
import java.text.ChoiceFormat;
public final class TimeFormatter {
private TimeFormatter(){}
private static final String LESS_THAN_A_SECOND =
Localization.getText("TimeFormatter.lessThanASecond");
private static final ChoiceFormat SECONDS =
new ChoiceFormat(
new double[]{1, 2},
new String[]{Localization.getText("TimeFormatter.oneSecond"),
Localization.getText("TimeFormatter.seconds")}
);
private static final ChoiceFormat MINUTES =
new ChoiceFormat(
new double[]{1, 2},
new String[]{Localization.getText("TimeFormatter.oneMinute"),
Localization.getText("TimeFormatter.minutes")}
);
private static final ChoiceFormat HOURS =
new ChoiceFormat(
new double[] {1, 2},
new String[]{Localization.getText("TimeFormatter.oneHour"),
Localization.getText("TimeFormatter.hours")}
);
/**
* Formats a time measurement in milliseconds as a localized String representation.
*
* <p>
* The String representation will return the time as a rounded down integer of the biggest fitting TimeUnit
* (seconds, minutes or hours), returning "< 1 second" for times under a second.
* <p/>
* @param millis
* @return A localized String representation of the timespan
*/
public static String format(long millis) {
if (millis < 1000) {
return LESS_THAN_A_SECOND;
}else if (millis < 60_000) {
long seconds = millis / 1000;
String pattern = SECONDS.format(seconds);
return pattern.replace("{0}", Long.toString(seconds));
}else if (millis < 3_600_000){
long minutes = millis / 60_000;
String pattern = MINUTES.format(minutes);
return pattern.replace("{0}", Long.toString(minutes));
} else {
long hours = millis / 3_600_000;
String pattern = HOURS.format(hours);
return pattern.replace("{0}", Long.toString(hours));
}
}
}
\ No newline at end of file
......@@ -22,12 +22,12 @@ import java.io.File;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -92,7 +92,7 @@ public class CityGMLWriterUtils {
storeCityObjects(model.getTransportation(), gmlFactory, model, cModel, val);
storeCityObjects(model.getWater(), gmlFactory, model, cModel, val);
storeCityObjects(model.getCityFurniture(), gmlFactory, model, cModel, val);
storeCityObjects(model.getGenericCityObjects(), gmlFactory, model, cModel, val);
storeCityObjects(model.getOtherCityObjects(), gmlFactory, model, cModel, val);
storeCityObjects(model.getTunnels(), gmlFactory, model, cModel, val);
writer.write(cModel);
......@@ -135,12 +135,13 @@ public class CityGMLWriterUtils {
de.hft.stuttgart.quality.model.enums.ErrorId adeId = QualityADEUtils.mapErrorIdToAdeId(e.getKey());
if (adeId == null) {
// error that is not part of the ade standard
logger.warn("Found error {} that cannot be converted to ADE error. It will not be written", e.getKey());
logger.warn(Localization.getText("CityGmlWriterUtils.unknownADEError"), e.getKey());
continue;
}
stats.setName(adeId);
statistics.getErrors().add(new ErrorProperty(stats));
}
// TODO: Quality ADE update to CityGML 3.0
statistics.setNumErrorBuildings(new FeatureStatisticsProperty(countValidatedCityObjects(model.getBuildings())));
statistics.setNumErrorBridgeObjects(new FeatureStatisticsProperty(countValidatedCityObjects(model.getBridges())));
statistics.setNumErrorLandObjects(new FeatureStatisticsProperty(countValidatedCityObjects(model.getLand())));
......@@ -152,29 +153,30 @@ public class CityGMLWriterUtils {
return val;
}
private static FeatureStatistics countValidatedCityObjects(List<? extends CityObject> cos) {
int numChecked = 0;
int numError = 0;
for (CityObject co : cos) {
private static FeatureStatistics countValidatedCityObjects(Stream<? extends CityObject> cos) {
AtomicInteger numChecked = new AtomicInteger();
AtomicInteger numError = new AtomicInteger();
cos.forEach(co -> {
if (co.isValidated()) {
numChecked++;
numChecked.getAndIncrement();
if (co.containsAnyError()) {
numError++;
numError.getAndIncrement();
}
}
}
return new FeatureStatistics(numChecked, numError);
});
return new FeatureStatistics(numChecked.get(), numError.get());
}
private static void storeCityObjects(List<? extends CityObject> cos, GeometryFactory gmlFactory, CityDoctorModel model, CityModel cModel, Validation val) {
for (CityObject co : cos) {
private static void storeCityObjects(Stream<? extends CityObject> cos, GeometryFactory gmlFactory, CityDoctorModel model, CityModel cModel, Validation val) {
cos.forEach(co -> {
if (model.isValidated() && val != null) {
QualityADEUtils.writeQualityAde(co, val);
}
co.reCreateGeometries(gmlFactory, model.getParserConfig());
cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(co.getGmlObject()));
}
});
}
}
package de.hft.stuttgart.citydoctor2.zip;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.ArchivePacker;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
/**
* This class serves as a representation of a ZIP-archive containing CityGml files. The contained files are represented
* by {@link CityGmlZipEntry} objects.
*/
public class CityGmlZipArchive implements Serializable {
private static final Logger logger = LogManager.getLogger(CityGmlZipArchive.class);
@Serial
private static final long serialVersionUID = 2168389511043362615L;
private List<CityGmlZipEntry> entries;
private final Path archivePath;
private final String archiveNameRE;
/**
* Registers a ZIP-file as an archive. All contained XML and GML files will be registered as {@link CityGmlZipEntry} objects.
*
* @param zipFile path of the ZIP-file
* @return A {@link CityGmlZipArchive} object representing the archive, or null if read-access for the ZIP-file was denied
* @throws MalformedZipFileException if the ZIP-file has encoding errors
*/
public static CityGmlZipArchive register(String zipFile) throws MalformedZipFileException {
ArrayList<CityGmlZipEntry> archiveEntries = new ArrayList<>();
CityGmlZipArchive cgmlArchive = new CityGmlZipArchive(Path.of(zipFile));
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
if (ze.isDirectory()) {
continue;
}
if (ze.getName().endsWith(".gml") || ze.getName().endsWith(".xml")) {
// Ignore report files generated by CityDoctor
if (ze.getName().endsWith("_report.xml")) {
continue;
}
archiveEntries.add(CityGmlZipEntry.register(ze, cgmlArchive));
}
}
cgmlArchive.setEntries(archiveEntries);
return cgmlArchive;
} catch (IOException e) {
logger.error(Localization.getText("CityGmlZipArchive.ioError"));
logger.debug(e.getMessage(), e);
return null;
} catch (IllegalArgumentException e) {
logger.error(Localization.getText("CityGmlZipArchive.malformedZipFile"));
throw new MalformedZipFileException(Localization.getText("CityGmlZipArchive.malformedZipFile"));
} catch (Exception e) {
logger.error(Localization.getText("CityGmlZipArchive.generalException"));
logger.debug(e.getMessage(), e);
throw (e);
}
}
/**
* Attempts to load all contained {@link CityGmlZipEntry} objects into memory.
*
* @param config Parser configuration for the entries
*/
public void mountArchive(ParserConfiguration config) {
try (ZipFile zip = new ZipFile(archivePath.toFile())) {
for (CityGmlZipEntry entry : entries) {
entry.loadEntry(config);
}
} catch (IOException e) {
logger.error(e);
}
}
private CityGmlZipArchive(Path archivePath) {
this.archivePath = archivePath;
this.archiveNameRE = archivePath.getFileName().toString().replace(".zip", "") + File.separator;
}
private void setEntries(List<CityGmlZipEntry> entries) {
this.entries = entries;
}
/**
* Saves this archive as a ZIP-file.
* @param path target path for the export
*/
public void exportToZipFile(String path) {
ArchivePacker.packArchive(path, this);
}
/**
* Gets an entry in this archive by its subpath.
*
* @param subpath subpath to the entry from this archive's root directory
* @return the corresponding entry, or null if entry could not be found
*/
public CityGmlZipEntry getEntry(String subpath) {
subpath = stripArchivePath(subpath);
for (CityGmlZipEntry entry : entries) {
String entryName = stripArchivePath(entry.getEntrySubPath());
if (entryName.equals(subpath)) {
return entry;
}
}
return null;
}
/**
* Strips the root directory name of this archive from an entry subpath.
* @param path the relative path to the entry
* @return String of the path without the root directory
*/
private String stripArchivePath(String path) {
Path systemPath = Path.of(path);
return systemPath.toString().replace(archiveNameRE, "");
}
/**
* Gets the path to the ZIP-file associated with this archive.
* @return path of the ZIP-file
*/
public Path getArchivePath() {
return archivePath;
}
/**
* Gets the entry list of this archive.
* @return the entry list
*/
public List<CityGmlZipEntry> getEntries() {
return entries;
}
}
package de.hft.stuttgart.citydoctor2.zip;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParser;
import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.parser.ProgressListener;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
/**
* This class serves as a representation of a CityGml file in a ZIP archive.
* <p/>
* The {@link CityDoctorModel} of the associated file can only be accessed after loading the corresponding entry.
*/
public class CityGmlZipEntry implements Serializable {
private static final Logger logger = LogManager.getLogger(CityGmlZipEntry.class);
@Serial
private static final long serialVersionUID = -5732913269959043262L;
private final Path filePath;
private final String fileName;
private final String displayName;
private transient CityDoctorModel model;
private CityGmlZipArchive parentArchive;
private boolean inMemory;
private long fileSize = -1L;
private static final long MB = 1024 * 1024L;
private ZipEntryErrorType errorType = null;
/**
* Attempts to parse the CityGML model associated with this entry and loads it into memory.
* <p/>
* In the event of a parsing error, the entry will be flagged by assigning the appropriate
* {@link ZipEntryErrorType} to the errorType field.
*
* @param config Parser configuration for this model
*/
public void loadEntry(ParserConfiguration config) {
loadEntry(config, null);
}
/**
* Attempts to parse the CityGML model associated with this entry and loads it into memory.
* <p/>
* In the event of a parsing error, the entry will be flagged by assigning the appropriate
* {@link ZipEntryErrorType} to the errorType field.
*
* @param config Parser configuration for this model
* @param l ProgressListener for tracking parsing progress
*/
public void loadEntry(ParserConfiguration config, ProgressListener l) {
if (inMemory) {
return;
}
if (errorType != null) {
logger.warn("Tried loading erroneous CityGmlZipEntry");
return;
}
try {
this.model = CityGmlParser.parseCityGmlZipEntry(this, config, l);
this.inMemory = true;
} catch (CityGmlParseException | InvalidGmlFileException e) {
logger.error(e);
this.errorType = ZipEntryErrorType.INVALID_CITY_GML_FILE;
} catch (IOException e) {
logger.error(e);
this.errorType = ZipEntryErrorType.IO_ERROR;
}
}
/**
* Registers a {@link ZipEntry ZipEntry} in a ZIP-archive as a {@link CityGmlZipEntry}.
* <p/>
* If the filesize of the associated file exceeds the total available system memory, or read access is denied,
* the entry will be flagged by assigning the appropriate {@link ZipEntryErrorType} to the errorType field.
*
* @param entry The ZipEntry object
* @param parentArchive The parent archive for this entry
* @return A corresponding CityGmlZipEntry object
*/
public static CityGmlZipEntry register(ZipEntry entry, CityGmlZipArchive parentArchive) {
CityGmlZipEntry cgzEntry = new CityGmlZipEntry(entry, parentArchive, false);
try {
if (!cgzEntry.entrySizeWithinMemoryLimits()) {
cgzEntry.errorType = ZipEntryErrorType.EXCESSIVE_FILESIZE;
}
} catch (IOException e) {
logger.error(e);
cgzEntry.errorType = ZipEntryErrorType.IO_ERROR;
}
return cgzEntry;
}
/**
* Checks if the filesize of this entry would exceed the system's available memory.
*
* @return true if within limits, false otherwise
* @throws IOException if read access to the ZIP-archive is denied
*/
private boolean entrySizeWithinMemoryLimits() throws IOException {
long memoryLimit = (long) Math.ceil(((double) Runtime.getRuntime().maxMemory() / MB) * 0.9);
if (fileSize == -1L) {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(this)) {
long filesize = entryFile.getFileSize();
if (filesize != -1) {
this.fileSize = filesize;
} else {
return false;
}
} catch (Exception e) {
throw new IOException(e);
}
}
return memoryLimit > (fileSize / MB);
}
private CityGmlZipEntry(ZipEntry entry, CityGmlZipArchive parentArchive, boolean inMemory) {
this.fileName = entry.getName();
this.filePath = Path.of(entry.getName());
this.displayName = filePath.getFileName().toString();
if (entry.getSize() != -1) {
this.fileSize = entry.getSize();
}
this.model = null;
this.inMemory = inMemory;
this.parentArchive = parentArchive;
}
/**
* Gets the parent archive of this entry.
* @return the archive
*/
public CityGmlZipArchive getArchive() {
return parentArchive;
}
/**
* Gets the sub-path in the archive of the file associated with this entry.
*
* @return the relative sub-path from the archive root directory
*/
public String getEntrySubPath() {
return fileName;
}
/**
* Gets the name of the file associated with this entry.
* @return the filename
*/
public String getDisplayName() {
return displayName;
}
/**
* Gets the {@link ZipEntryErrorType} of this entry.
* @return the error type, or null if this entry is not erroneous
*/
public ZipEntryErrorType getErrorType() {
return errorType;
}
/**
* Gets the {@link CityDoctorModel} of this entry.
* @return The model, or null if the entry has not been loaded or is erroneous
*/
public CityDoctorModel getModel() {
return model;
}
public void setFileSize(long size) {
fileSize = size;
}
/**
* Resolves the reference path of a {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject LibraryObject}
* in the {@link CityDoctorModel} of this entry.
* @param libraryObjectPath The reference path to the LibraryObject's CityGml file
* @return Relative sub-path within the archive to the CityGml file
*/
public Path resolveLibraryObjectPath(String libraryObjectPath) {
return filePath.getParent().resolve(libraryObjectPath);
}
/**
* Checks if the {@link CityDoctorModel} of this entry has been loaded into memory.
*
* @return true if it is loaded, false otherwise
*/
public boolean isLoaded() {
return inMemory;
}
/**
* Returns the estimated uncompressed filesize of this entry.
*
* @return filesize in bytes, -1L if filesize is unknown
*/
public long getFileSize() {
return fileSize;
}
}
package de.hft.stuttgart.citydoctor2.zip;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* This class serves as a resource for read-access to the file that is being represented by
* a CityGmlZipEntry object.
* <p/>
* Does not decompress the file or the parent ZIP-archive.
*/
public class CityGmlZipEntryFile implements AutoCloseable {
private final ZipFile zip;
private final ZipEntry zipEntry;
private final CityGmlZipEntry cgmlZipEntry;
private boolean closed = false;
/**
* Creates a {@link CityGmlZipEntryFile} for a {@link CityGmlZipEntry} object's referenced file.
*
* @param entry the requested entry
* @throws IOException if read access to the ZIP-archive is denied.
*/
public CityGmlZipEntryFile(CityGmlZipEntry entry) throws IOException {
CityGmlZipArchive archive = entry.getArchive();
zip = new ZipFile(archive.getArchivePath().toFile());
zipEntry = zip.getEntry(entry.getEntrySubPath());
this.cgmlZipEntry = entry;
}
/**
* Gets a new InputStream of this file.
* <p/>
* Returned streams are independent of each other, but will all be closed when the {@link CityGmlZipEntryFile} object
* is closed.
* @return an InputStream of this file.
* @throws IOException when this method is called after the file has been closed.
*/
public InputStream getInputStream() throws IOException {
if (closed){
throw new IOException("CityGmlZipEntryFile closed");
}
return zip.getInputStream(zipEntry);
}
/**
* Gets an estimation of this file's filesize.
* @return filesize in bytes, -1L if unknown
* @throws IOException when this method is called after the file has been closed.
*/
public long getFileSize() throws IOException {
if (closed){
throw new IOException("CityGmlZipEntryFile closed");
}
if (cgmlZipEntry.getFileSize() == -1L) {
if (zipEntry.getSize() == -1) {
long bytes = 0;
InputStream is = new BufferedInputStream(this.getInputStream());
for (int i = is.read(); i != -1; i = is.read()) {
bytes++;
}
cgmlZipEntry.setFileSize(bytes);
} else {
cgmlZipEntry.setFileSize(zipEntry.getSize());
}
}
return cgmlZipEntry.getFileSize();
}
@Override
public void close() throws IOException {
zip.close();
closed = true;
}
}
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