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 {
private final Map<CheckId, CheckResult> checkResults = new HashMap<>();
private boolean isValidated = false;
protected void setValidated(boolean validated) {
public void setValidated(boolean 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;
import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor;
import de.hft.stuttgart.citydoctor2.check.CheckError;
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.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.writer.CityGMLWriterUtils;
......@@ -46,15 +47,15 @@ import java.util.stream.Stream;
public class CityDoctorModel {
private static final String COULD_NOT_FIND_FEATURE = "Could not find feature: ";
private final List<Building> buildings;
private final List<Vegetation> vegetation;
private final List<BridgeObject> bridges;
private final List<CityFurniture> cityfurniture;
private final List<GenericCityObject> genericObjects;
private final List<CityObject> land;
private final List<TransportationObject> roads;
private final List<Tunnel> tunnels;
private final List<WaterObject> water;
private final List<GmlId> buildings;
private final List<GmlId> vegetation;
private final List<GmlId> bridges;
private final List<GmlId> cityfurniture;
private final List<GmlId> genericObjects;
private final List<GmlId> land;
private final List<GmlId> roads;
private final List<GmlId> tunnels;
private final List<GmlId> water;
private CityModel cModel;
private final ParserConfiguration config;
private final String fileName;
......@@ -63,6 +64,7 @@ public class CityDoctorModel {
private boolean isValidated = false;
private ValidationPlan plan;
private CityGMLVersion cityGMLVersion;
private final FeatureCache cache;
public CityDoctorModel(ParserConfiguration config, File file) {
if (config == null) {
......@@ -81,6 +83,7 @@ public class CityDoctorModel {
cityfurniture = new ArrayList<>();
genericObjects = new ArrayList<>();
globalErrors = new ArrayList<>();
cache = new FeatureCache();
}
public boolean isValidated() {
......@@ -118,20 +121,12 @@ public class CityDoctorModel {
}
public Stream<CityObject> createFeatureStream() {
return Stream.of(buildings.stream(), vegetation.stream(), bridges.stream(), land.stream(), roads.stream(),
tunnels.stream(), water.stream(), cityfurniture.stream(), genericObjects.stream()).flatMap(co -> co);
return Stream.of(getBuildings(), getVegetation(), getBridges(), getLand(), getTransportation(),
getTunnels(), getWater(), getCityFurniture(), getGenericCityObjects()).flatMap(co -> co);
}
public void accept(CheckableVisitor visitor) {
buildings.forEach(co -> co.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));
createFeatureStream().forEach(cityObject -> cityObject.accept(visitor));
}
public void saveAs(String file, boolean saveQualityAde) throws CityDoctorWriteException {
......@@ -228,106 +223,153 @@ public class CityDoctorModel {
public Set<CheckError> collectErrors() {
List<CheckError> errors = new ArrayList<>();
collectErrorsFromList(errors, buildings);
collectErrorsFromList(errors, vegetation);
collectErrorsFromList(errors, bridges);
collectErrorsFromList(errors, land);
collectErrorsFromList(errors, roads);
collectErrorsFromList(errors, tunnels);
collectErrorsFromList(errors, water);
collectErrorsFromList(errors, cityfurniture);
collectErrorsFromList(errors, genericObjects);
createFeatureStream().forEach(cityObject -> cityObject.collectErrors(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() {
return fileName;
}
public void addBridge(BridgeObject coBridge) {
bridges.add(coBridge);
}
public void addBuilding(Building coBuilding) {
buildings.add(coBuilding);
buildings.add(coBuilding.getGmlId());
cache.put(coBuilding);
}
public List<Building> getBuildings() {
return buildings;
public Stream<Building> getBuildings() {
return buildings.stream().map(co -> (Building) cache.get(co));
}
public List<BridgeObject> getBridges() {
return bridges;
public int getBuildingCount(){
return buildings.size();
}
public void addBridge(BridgeObject coBridge) {
bridges.add(coBridge.getGmlId());
cache.put(coBridge);
}
public List<CityFurniture> getCityFurniture() {
return cityfurniture;
public Stream<BridgeObject> getBridges() {
return bridges.stream().map(co -> (BridgeObject) cache.get(co));
}
public int getBridgeCount(){
return bridges.size();
}
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() {
return genericObjects;
public int getCityFurnitureCount(){
return cityfurniture.size();
}
public void addGenericCityObject(GenericCityObject coGenericCityObject) {
genericObjects.add(coGenericCityObject);
genericObjects.add(coGenericCityObject.getGmlId());
cache.put(coGenericCityObject);
}
public void setCityModel(CityModel cModel) {
this.cModel = cModel;
public Stream<GenericCityObject> getGenericCityObjects() {
return genericObjects.stream().map(co -> (GenericCityObject) cache.get(co));
}
public CityModel getCityModel() {
return cModel;
public int getGenericCityObjectCount(){
return genericObjects.size();
}
public void addTransportation(TransportationObject to) {
roads.add(to.getGmlId());
cache.put(to);
}
public List<TransportationObject> getTransportation() {
return roads;
public Stream<TransportationObject> getTransportation() {
return roads.stream().map(co -> (TransportationObject) cache.get(co));
}
public List<Tunnel> getTunnels() {
return tunnels;
public int getTransportationCount(){
return roads.size();
}
public List<WaterObject> getWater() {
return water;
public void addTunnel(Tunnel tunnel) {
tunnels.add(tunnel.getGmlId());
cache.put(tunnel);
}
public List<CityObject> getLand() {
return land;
public Stream<Tunnel> getTunnels() {
return tunnels.stream().map(co -> (Tunnel) cache.get(co));
}
public List<Vegetation> getVegetation() {
return vegetation;
public int getTunnelCount(){
return tunnels.size();
}
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) {
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) {
vegetation.add(veg);
vegetation.add(veg.getGmlId());
cache.put(veg);
}
public void addTransportation(TransportationObject to) {
roads.add(to);
public Stream<Vegetation> getVegetation() {
return vegetation.stream().map(co -> (Vegetation) cache.get(co));
}
public void addTunnel(Tunnel tunnel) {
tunnels.add(tunnel);
public int getVegetationCount(){
return vegetation.size();
}
public void setCityModel(CityModel cModel) {
this.cModel = cModel;
}
public CityModel getCityModel() {
return cModel;
}
public FeatureCache getCache(){
return cache;
}
public int getNumberOfFeatures() {
......@@ -362,100 +404,105 @@ public class CityDoctorModel {
}
private void replaceWaterObject(CityObject currentFeature, CityObject nextFeature) {
int index = water.indexOf(currentFeature);
int index = water.indexOf(currentFeature.getGmlId());
if (index == -1) {
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) {
int index = land.indexOf(currentFeature);
int index = land.indexOf(currentFeature.getGmlId());
if (index == -1) {
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) {
int index = vegetation.indexOf(currentFeature);
int index = vegetation.indexOf(currentFeature.getGmlId());
if (index == -1) {
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) {
int index = cityfurniture.indexOf(currentFeature);
int index = cityfurniture.indexOf(currentFeature.getGmlId());
if (index == -1) {
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) {
int index = genericObjects.indexOf(currentFeature);
int index = genericObjects.indexOf(currentFeature.getGmlId());
if (index == -1) {
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) {
int index = roads.indexOf(currentFeature);
int index = roads.indexOf(currentFeature.getGmlId());
if (index == -1) {
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) {
int index = tunnels.indexOf(currentFeature);
int index = tunnels.indexOf(currentFeature.getGmlId());
if (index == -1) {
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) {
int index = bridges.indexOf(currentFeature);
int index = bridges.indexOf(currentFeature.getGmlId());
if (index == -1) {
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) {
int index = buildings.indexOf(currentFeature);
int index = buildings.indexOf(currentFeature.getGmlId());
if (index == -1) {
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) {
if (co instanceof Building b) {
buildings.add(b);
buildings.add(b.getGmlId());
} else if (co instanceof BridgeObject bo) {
bridges.add(bo);
bridges.add(bo.getGmlId());
} else if (co instanceof TransportationObject to) {
roads.add(to);
roads.add(to.getGmlId());
} else if (co instanceof Tunnel tu) {
tunnels.add(tu);
tunnels.add(tu.getGmlId());
} else if (co instanceof Vegetation veg) {
vegetation.add(veg);
vegetation.add(veg.getGmlId());
} else if (co instanceof LandObject lo) {
land.add(lo);
land.add(lo.getGmlId());
} else if (co instanceof WaterObject wo) {
water.add(wo);
water.add(wo.getGmlId());
} else if (co instanceof ReliefObject) {
land.add(co);
land.add(co.getGmlId());
} else if (co instanceof TinObject) {
land.add(co);
land.add(co.getGmlId());
} else if (co instanceof CityFurniture cf) {
cityfurniture.add(cf);
cityfurniture.add(cf.getGmlId());
} else if (co instanceof GenericCityObject gco) {
genericObjects.add(gco);
genericObjects.add(gco.getGmlId());
}
}
......@@ -467,11 +514,4 @@ public class CityDoctorModel {
return cityGMLVersion;
}
public void addRelief(ReliefObject relief) {
addCityObject(relief);
}
public void addTin(TinObject tin) {
addCityObject(tin);
}
}
......@@ -759,22 +759,9 @@ public class CityGmlParser {
}
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) {
for (CityObject co : objects) {
cityObjectConsumer.accept(co);
}
objects.clear();
model.createFeatureStream().forEach(cityObjectConsumer::accept);
}
}
......@@ -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;
......@@ -141,6 +141,7 @@ public class CityGMLWriterUtils {
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()));
}
});
}
}
......@@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
public class CityObjectCacheTest {
public class FeatureCacheTest {
@BeforeClass
public static void setUpBeforeClass() {
......@@ -36,7 +36,7 @@ public class CityObjectCacheTest {
b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0));
assertEquals(0, DatabaseHandler.getFeatureCount());
CityObjectCache cache = new CityObjectCache();
FeatureCache cache = new FeatureCache();
cache.put(b);
assertEquals(1, DatabaseHandler.getFeatureCount());
}
......
......@@ -45,33 +45,8 @@ public class XmlValidationReporter implements Reporter {
@Override
public void writeReport(Checks checks, OutputStream filePath, CityDoctorModel model, ValidationConfiguration config) throws CheckReportWriteException {
StreamReporter streamReporter = new XmlStreamReporter(filePath, model.getFileName(), config);
for (BridgeObject bo : model.getBridges()) {
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);
}
model.createFeatureStream().forEach(streamReporter::report);
for (CheckError err : model.getGlobalErrors()) {
streamReporter.reportGlobalError(err);
}
......
......@@ -13,6 +13,7 @@ import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.*;
import java.util.*;
import java.util.stream.Stream;
public class TriangulatedGeometry {
......@@ -98,8 +99,8 @@ public class TriangulatedGeometry {
private static void addPolygonDataFromCityObjects(List<? extends CityObject> cos,
TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) {
private static void addPolygonDataFromCityObjects(Stream<? extends CityObject> cos,
TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) {
CityObjectCollector collector = new CityObjectCollector();
cos.forEach(cityObject -> cityObject.accept(collector));
collector.getCityObjects().forEach(cityObject -> addPolygonData(cityObject, triGeom, color, filters));
......
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 {
private static final Logger logger = LogManager.getLogger(CityObjectNode.class);
private final CityObject co;
private String text;
public CityObjectNode(CityObject co) {
this.co = co;
this.text = co.getDisplayText();
}
@Override
......@@ -30,7 +32,7 @@ public class CityObjectNode extends Renderable {
@Override
public String getText() {
return co.getDisplayText();
return text;
}
@Override
......
......@@ -689,9 +689,7 @@ public class HealerController {
// triangle area is too small, ignore
return;
}
for (Building b : model.getBuildings()) {
MeshGenerator.replaceGeometryWithMeshedGeometry(b, area);
}
model.getBuildings().forEach(building -> MeshGenerator.replaceGeometryWithMeshedGeometry(building, area));
} catch (NumberFormatException e) {
// no real number was inserted, do nothing
}
......
......@@ -329,7 +329,7 @@ public class GeneticsGUI extends View {
parserConfig);
currentChecker = new Checker(model1);
currentChecker.runChecks();
Building building = model1.getBuildings().get(0);
Building building = model1.getBuildings().toList().getFirst();
currentGeometry = building.getGeometries().get(0);
currentFeature = building;
showCurrentGeometry();
......
......@@ -298,7 +298,7 @@
<!--CityDoctor2 Extension Modules-->
<module>Extensions/CityDoctorGUI</module>
<module>Extensions/CityDoctorAutoPro</module>
<!-- <module>Extensions/CityDoctorAutoPro</module>-->
<module>Extensions/CityDoctorHealer</module>
<module>Extensions/CityDoctorHealerGenetic</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