Commit 28c42d2d authored by Luna Riegel's avatar Luna Riegel
Browse files

!Feature: Implement GeoDB and FeatureCache

BREAKING CHANGE: Implements the saving and loading of CityObjects to a
GeoDB with in-memory caching. This allows reading of bigger CityGml
files and serves as basis for the implementation of Topological Checks.

CityDoctorModel will now return Streams instead of Lists for the
CityObject-getters (like getBuildings()). This requires a rework of all
methods calling them.
parent 286d394c
Pipeline #11965 failed with stage
in 1 minute and 3 seconds
...@@ -52,7 +52,7 @@ public abstract class Checkable implements Serializable { ...@@ -52,7 +52,7 @@ public abstract class Checkable implements Serializable {
private final Map<CheckId, CheckResult> checkResults = new HashMap<>(); private final Map<CheckId, CheckResult> checkResults = new HashMap<>();
private boolean isValidated = false; private boolean isValidated = false;
protected void setValidated(boolean validated) { public void setValidated(boolean validated) {
isValidated = validated; isValidated = validated;
} }
......
package de.hft.stuttgart.citydoctor2.database;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.utils.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
/**
* Cache for CityObjects
*/
public class CityObjectCache {
private final LoadingCache<GmlId, CityObject> cache;
public CityObjectCache() {
cache = Caffeine.newBuilder().maximumSize(1000).build(DatabaseHandler::unmarshallCityObject);
}
public void put(CityObject cityObject) {
DatabaseHandler.marshallCityObject(cityObject);
cache.put(cityObject.getGmlId(), cityObject);
}
public CityObject get(GmlId id) {
return cache.get(id);
}
public List<CityObject> get(List<GmlId> ids) {
List<CityObject> features = new ArrayList<>();
for (GmlId id : ids) {
features.add(cache.get(id));
}
return features;
}
}
package de.hft.stuttgart.citydoctor2.database;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.RemovalCause;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.utils.visitors.CityObjectCollector;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* Cache for CityObjects
*/
public class FeatureCache{
private final LoadingCache<GmlId, CityObject> cache;
private final Cache<GmlId, CityObject> subCache;
public FeatureCache() {
cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> {
if (value != null && cause.wasEvicted()) {
DatabaseHandler.marshallCityObject(value);
}
}).build(DatabaseHandler::unmarshallCityObject);
subCache = Caffeine.newBuilder().maximumSize(250).build();
DatabaseHandler.setupDatabase();
}
public void put(CityObject cityObject) {
DatabaseHandler.marshallCityObject(cityObject);
cache.put(cityObject.getGmlId(), cityObject);
}
public CityObject get(GmlId id) {
return cache.get(id);
}
public void replace(GmlId id, CityObject cityObject) {
cache.invalidate(id);
put(cityObject);
}
public Stream<CityObject> getStream(List<GmlId> gmlIds) {
return gmlIds.stream().map(this::get);
}
/**
* Gets a non-top-level CityObject directly. Only use this in situations where directly accessing the object is required
* and access via the top-level CityObject is impossible or highly inefficient, e.g. rendering the associated object
* of a CityObjectNode.
* @param coa the adapter object of the subpart
* @return the associated CityObject
*/
public CityObject getSubPart(SubPartAdapter coa) {
CityObject co = subCache.getIfPresent(coa.getFeatureId());
if (co == null) {
return buildAndGetSubPart(coa);
}
else return co;
}
/**
*
* @param coa
* @return
*/
private CityObject buildAndGetSubPart(SubPartAdapter coa) {
CityObject parent = cache.get(coa.getTopLevelId());
if (parent == null) {
throw new IllegalStateException("CityObjectAdapter has non-existent parent - GmlId: " + coa.getTopLevelId());
}
CityObjectCollector collector = new CityObjectCollector();
parent.accept(collector);
collector.getCityObjects().forEach(cityObject -> subCache.put(cityObject.getGmlId(), cityObject));
CityObject co = subCache.getIfPresent(coa.getFeatureId());
if (co == null) {
throw new IllegalStateException("CityObjectAdapter has non-existent feature - GmlId: " + coa.getFeatureId() +
", parent GmlId: " + coa.getTopLevelId());
}
return co;
}
public List<CityObject> get(List<GmlId> ids) {
List<CityObject> features = new ArrayList<>();
for (GmlId id : ids) {
features.add(cache.get(id));
}
return features;
}
}
package de.hft.stuttgart.citydoctor2.database;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
/**
* Adapter class for Lists. All function calls on objects of this
* class get passed to their respective CityObjects.
*/
public class SubPartAdapter {
// TODO: Remove CO extension and change model to stream and map objects to their respective classes
private final GmlId featureId;
private final GmlId topLevelId;
private FeatureCache cache;
public SubPartAdapter(CityObject co, FeatureCache cache) {
this.featureId = co.getGmlId();
this.topLevelId = co.getTopLevelCityObject().getGmlId();
this.cache = cache;
}
public boolean isValidated(){
return cache.get(featureId).isValidated();
}
public GmlId getFeatureId() {
return featureId;
}
public GmlId getTopLevelId() {
return topLevelId;
}
}
package de.hft.stuttgart.citydoctor2.database;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
public class TopLevelCityObjectAdapter {
private final GmlId featureId;
private FeatureCache cache;
public TopLevelCityObjectAdapter(CityObject co, FeatureCache cache) {
this.featureId = co.getGmlId();
this.cache = cache;
}
public boolean isValidated(){
return cache.get(featureId).isValidated();
}
}
...@@ -21,6 +21,7 @@ package de.hft.stuttgart.citydoctor2.datastructure; ...@@ -21,6 +21,7 @@ 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.check.CheckableVisitor;
import de.hft.stuttgart.citydoctor2.database.FeatureCache;
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;
...@@ -46,15 +47,15 @@ import java.util.stream.Stream; ...@@ -46,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;
...@@ -63,6 +64,7 @@ public class CityDoctorModel { ...@@ -63,6 +64,7 @@ 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 FeatureCache cache;
public CityDoctorModel(ParserConfiguration config, File file) { public CityDoctorModel(ParserConfiguration config, File file) {
if (config == null) { if (config == null) {
...@@ -81,6 +83,7 @@ public class CityDoctorModel { ...@@ -81,6 +83,7 @@ public class CityDoctorModel {
cityfurniture = new ArrayList<>(); cityfurniture = new ArrayList<>();
genericObjects = new ArrayList<>(); genericObjects = new ArrayList<>();
globalErrors = new ArrayList<>(); globalErrors = new ArrayList<>();
cache = new FeatureCache();
} }
public boolean isValidated() { public boolean isValidated() {
...@@ -118,20 +121,12 @@ public class CityDoctorModel { ...@@ -118,20 +121,12 @@ 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(), getGenericCityObjects()).flatMap(co -> co);
} }
public void accept(CheckableVisitor visitor) { public void accept(CheckableVisitor visitor) {
buildings.forEach(co -> co.accept(visitor)); createFeatureStream().forEach(cityObject -> cityObject.accept(visitor));
vegetation.forEach(co -> co.accept(visitor));
bridges.forEach(co -> co.accept(visitor));
land.forEach(co -> co.accept(visitor));
roads.forEach(co -> co.accept(visitor));
tunnels.forEach(co -> co.accept(visitor));
water.forEach(co -> co.accept(visitor));
cityfurniture.forEach(co -> co.accept(visitor));
genericObjects.forEach(co -> co.accept(visitor));
} }
public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException { public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException {
...@@ -228,106 +223,153 @@ public class CityDoctorModel { ...@@ -228,106 +223,153 @@ 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 Stream<Building> getBuildings() {
return buildings; return buildings.stream().map(co -> (Building) cache.get(co));
} }
public List<BridgeObject> getBridges() { public int getBuildingCount(){
return bridges; return buildings.size();
}
public void addBridge(BridgeObject coBridge) {
bridges.add(coBridge.getGmlId());
cache.put(coBridge);
} }
public List<CityFurniture> getCityFurniture() { public Stream<BridgeObject> getBridges() {
return cityfurniture; return bridges.stream().map(co -> (BridgeObject) cache.get(co));
}
public int getBridgeCount(){
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<GenericCityObject> getGenericCityObjects() { public int getCityFurnitureCount(){
return genericObjects; return cityfurniture.size();
} }
public void addGenericCityObject(GenericCityObject coGenericCityObject) { public void addGenericCityObject(GenericCityObject coGenericCityObject) {
genericObjects.add(coGenericCityObject); genericObjects.add(coGenericCityObject.getGmlId());
cache.put(coGenericCityObject);
} }
public void setCityModel(CityModel cModel) { public Stream<GenericCityObject> getGenericCityObjects() {
this.cModel = cModel; return genericObjects.stream().map(co -> (GenericCityObject) cache.get(co));
} }
public CityModel getCityModel() { public int getGenericCityObjectCount(){
return cModel; return genericObjects.size();
}
public void addTransportation(TransportationObject to) {
roads.add(to.getGmlId());
cache.put(to);
} }
public List<TransportationObject> getTransportation() { public Stream<TransportationObject> getTransportation() {
return roads; return roads.stream().map(co -> (TransportationObject) cache.get(co));
} }
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 int getTunnelCount(){
return vegetation; 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 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 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 int getVegetationCount(){
tunnels.add(tunnel); return vegetation.size();
}
public void setCityModel(CityModel cModel) {
this.cModel = cModel;
}
public CityModel getCityModel() {
return cModel;
}
public FeatureCache getCache(){
return cache;
} }
public int getNumberOfFeatures() { public int getNumberOfFeatures() {
...@@ -362,100 +404,105 @@ public class CityDoctorModel { ...@@ -362,100 +404,105 @@ 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); 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); 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); 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); 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());
} }
} }
...@@ -467,11 +514,4 @@ public class CityDoctorModel { ...@@ -467,11 +514,4 @@ public class CityDoctorModel {
return cityGMLVersion; return cityGMLVersion;
} }
public void addRelief(ReliefObject relief) {
addCityObject(relief);
}
public void addTin(TinObject tin) {
addCityObject(tin);
}
} }
...@@ -759,22 +759,9 @@ public class CityGmlParser { ...@@ -759,22 +759,9 @@ public class CityGmlParser {
} }
private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) { private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) {
drainCityObjectList(model.getBuildings(), cityObjectConsumer);
drainCityObjectList(model.getBridges(), cityObjectConsumer);
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) { model.createFeatureStream().forEach(cityObjectConsumer::accept);
for (CityObject co : objects) {
cityObjectConsumer.accept(co);
}
objects.clear();
} }
} }
...@@ -22,12 +22,12 @@ import java.io.File; ...@@ -22,12 +22,12 @@ import java.io.File;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
...@@ -141,6 +141,7 @@ public class CityGMLWriterUtils { ...@@ -141,6 +141,7 @@ public class CityGMLWriterUtils {
stats.setName(adeId); stats.setName(adeId);
statistics.getErrors().add(new ErrorProperty(stats)); statistics.getErrors().add(new ErrorProperty(stats));
} }
// TODO: Quality ADE update to CityGML 3.0
statistics.setNumErrorBuildings(new FeatureStatisticsProperty(countValidatedCityObjects(model.getBuildings()))); statistics.setNumErrorBuildings(new FeatureStatisticsProperty(countValidatedCityObjects(model.getBuildings())));
statistics.setNumErrorBridgeObjects(new FeatureStatisticsProperty(countValidatedCityObjects(model.getBridges()))); statistics.setNumErrorBridgeObjects(new FeatureStatisticsProperty(countValidatedCityObjects(model.getBridges())));
statistics.setNumErrorLandObjects(new FeatureStatisticsProperty(countValidatedCityObjects(model.getLand()))); statistics.setNumErrorLandObjects(new FeatureStatisticsProperty(countValidatedCityObjects(model.getLand())));
...@@ -152,29 +153,30 @@ public class CityGMLWriterUtils { ...@@ -152,29 +153,30 @@ public class CityGMLWriterUtils {
return val; return val;
} }
private static FeatureStatistics countValidatedCityObjects(List<? extends CityObject> cos) { private static FeatureStatistics countValidatedCityObjects(Stream<? extends CityObject> cos) {
int numChecked = 0; AtomicInteger numChecked = new AtomicInteger();
int numError = 0; AtomicInteger numError = new AtomicInteger();
for (CityObject co : cos) { cos.forEach(co -> {
if (co.isValidated()) { if (co.isValidated()) {
numChecked++; numChecked.getAndIncrement();
if (co.containsAnyError()) { 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) { private static void storeCityObjects(Stream<? extends CityObject> cos, GeometryFactory gmlFactory, CityDoctorModel model, CityModel cModel, Validation val) {
for (CityObject co : cos) { cos.forEach(co -> {
if (model.isValidated() && val != null) { if (model.isValidated() && val != null) {
QualityADEUtils.writeQualityAde(co, val); QualityADEUtils.writeQualityAde(co, val);
} }
co.reCreateGeometries(gmlFactory, model.getParserConfig()); co.reCreateGeometries(gmlFactory, model.getParserConfig());
cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(co.getGmlObject())); cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(co.getGmlObject()));
} });
} }
} }
...@@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals; ...@@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
public class CityObjectCacheTest { public class FeatureCacheTest {
@BeforeClass @BeforeClass
public static void setUpBeforeClass() { public static void setUpBeforeClass() {
...@@ -36,7 +36,7 @@ public class CityObjectCacheTest { ...@@ -36,7 +36,7 @@ public class CityObjectCacheTest {
b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0));
assertEquals(0, DatabaseHandler.getFeatureCount()); assertEquals(0, DatabaseHandler.getFeatureCount());
CityObjectCache cache = new CityObjectCache(); FeatureCache cache = new FeatureCache();
cache.put(b); cache.put(b);
assertEquals(1, DatabaseHandler.getFeatureCount()); assertEquals(1, DatabaseHandler.getFeatureCount());
} }
......
...@@ -45,33 +45,8 @@ public class XmlValidationReporter implements Reporter { ...@@ -45,33 +45,8 @@ public class XmlValidationReporter implements Reporter {
@Override @Override
public void writeReport(Checks checks, OutputStream filePath, CityDoctorModel model, ValidationConfiguration config) throws CheckReportWriteException { public void writeReport(Checks checks, OutputStream filePath, CityDoctorModel model, ValidationConfiguration config) throws CheckReportWriteException {
StreamReporter streamReporter = new XmlStreamReporter(filePath, model.getFileName(), config); StreamReporter streamReporter = new XmlStreamReporter(filePath, model.getFileName(), config);
for (BridgeObject bo : model.getBridges()) { model.createFeatureStream().forEach(streamReporter::report);
streamReporter.report(bo);
}
for (Building b : model.getBuildings()) {
streamReporter.report(b);
}
for (TransportationObject to : model.getTransportation()) {
streamReporter.report(to);
}
for (CityObject lo : model.getLand()) {
streamReporter.report(lo);
}
for (Vegetation v : model.getVegetation()) {
streamReporter.report(v);
}
for (WaterObject wo : model.getWater()) {
streamReporter.report(wo);
}
for (Tunnel tunnel : model.getTunnels()) {
streamReporter.report(tunnel);
}
for (CityFurniture cf : model.getCityFurniture()) {
streamReporter.report(cf);
}
for (GenericCityObject gco : model.getGenericCityObjects()) {
streamReporter.report(gco);
}
for (CheckError err : model.getGlobalErrors()) { for (CheckError err : model.getGlobalErrors()) {
streamReporter.reportGlobalError(err); streamReporter.reportGlobalError(err);
} }
......
...@@ -13,6 +13,7 @@ import javafx.scene.paint.PhongMaterial; ...@@ -13,6 +13,7 @@ import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.*; import javafx.scene.shape.*;
import java.util.*; import java.util.*;
import java.util.stream.Stream;
public class TriangulatedGeometry { public class TriangulatedGeometry {
...@@ -98,7 +99,7 @@ public class TriangulatedGeometry { ...@@ -98,7 +99,7 @@ public class TriangulatedGeometry {
private static void addPolygonDataFromCityObjects(List<? extends CityObject> cos, private static void addPolygonDataFromCityObjects(Stream<? extends CityObject> cos,
TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) { TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) {
CityObjectCollector collector = new CityObjectCollector(); CityObjectCollector collector = new CityObjectCollector();
cos.forEach(cityObject -> cityObject.accept(collector)); cos.forEach(cityObject -> cityObject.accept(collector));
......
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.gui.CityDoctorController;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class AddMoreButtonRunnable implements Runnable{
private int featureCount;
private final TreeView<Renderable> view;
private final TreeItem<Renderable> root;
private final AtomicInteger chunkCounter;
private static final int MAX_FEATURES_PER_CHUNK = CityDoctorController.getMaxFeaturesPerChunk();
protected AddMoreButtonRunnable(int featureCount, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter){
this.featureCount = featureCount;
this.view = view;
this.root = root;
this.chunkCounter = chunkCounter;
}
@Override
public void run() {
// 29 is the height of one item
int numberOfDisplayedItems = (int) (view.getHeight() / 29);
int selectedIndex = view.getSelectionModel().getSelectedIndex();
// remove button node
root.getChildren().remove(root.getChildren().size() - 1);
chunkCounter.getAndIncrement();
int visibleFeatures = countVisibleNodes(view);
buildNodeTreeCall();
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
/**
* Function hook for the buildTree call of the view
*/
public abstract void buildNodeTreeCall();
private static int countVisibleNodes(TreeView<Renderable> view) {
return countVisibleNodes(view.getRoot()) + 1;
}
private static int countVisibleNodes(TreeItem<Renderable> root) {
int visibleFeatures = root.getChildren().size();
for (TreeItem<Renderable> child : root.getChildren()) {
if (!child.getChildren().isEmpty() && child.isExpanded()) {
visibleFeatures += countVisibleNodes(child);
}
}
return visibleFeatures;
}
}
...@@ -12,9 +12,11 @@ public class CityObjectNode extends Renderable { ...@@ -12,9 +12,11 @@ public class CityObjectNode extends Renderable {
private static final Logger logger = LogManager.getLogger(CityObjectNode.class); private static final Logger logger = LogManager.getLogger(CityObjectNode.class);
private final CityObject co; private final CityObject co;
private String text;
public CityObjectNode(CityObject co) { public CityObjectNode(CityObject co) {
this.co = co; this.co = co;
this.text = co.getDisplayText();
} }
@Override @Override
...@@ -30,7 +32,7 @@ public class CityObjectNode extends Renderable { ...@@ -30,7 +32,7 @@ public class CityObjectNode extends Renderable {
@Override @Override
public String getText() { public String getText() {
return co.getDisplayText(); return text;
} }
@Override @Override
......
...@@ -689,9 +689,7 @@ public class HealerController { ...@@ -689,9 +689,7 @@ public class HealerController {
// triangle area is too small, ignore // triangle area is too small, ignore
return; return;
} }
for (Building b : model.getBuildings()) { model.getBuildings().forEach(building -> MeshGenerator.replaceGeometryWithMeshedGeometry(building, area));
MeshGenerator.replaceGeometryWithMeshedGeometry(b, area);
}
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// no real number was inserted, do nothing // no real number was inserted, do nothing
} }
......
...@@ -329,7 +329,7 @@ public class GeneticsGUI extends View { ...@@ -329,7 +329,7 @@ public class GeneticsGUI extends View {
parserConfig); parserConfig);
currentChecker = new Checker(model1); currentChecker = new Checker(model1);
currentChecker.runChecks(); currentChecker.runChecks();
Building building = model1.getBuildings().get(0); Building building = model1.getBuildings().toList().getFirst();
currentGeometry = building.getGeometries().get(0); currentGeometry = building.getGeometries().get(0);
currentFeature = building; currentFeature = building;
showCurrentGeometry(); showCurrentGeometry();
......
...@@ -298,7 +298,7 @@ ...@@ -298,7 +298,7 @@
<!--CityDoctor2 Extension Modules--> <!--CityDoctor2 Extension Modules-->
<module>Extensions/CityDoctorGUI</module> <module>Extensions/CityDoctorGUI</module>
<module>Extensions/CityDoctorAutoPro</module> <!-- <module>Extensions/CityDoctorAutoPro</module>-->
<module>Extensions/CityDoctorHealer</module> <module>Extensions/CityDoctorHealer</module>
<module>Extensions/CityDoctorHealerGenetic</module> <module>Extensions/CityDoctorHealerGenetic</module>
<module>Extensions/CityDoctorHealerGUI</module> <module>Extensions/CityDoctorHealerGUI</module>
......
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