Commit b2d40f78 authored by Riegel's avatar Riegel
Browse files

Merge branch 'dev_citygml3' into 'dev'

CityGML 3.0. Support

See merge request !10
parents ec3bffa6 45b8f722
Pipeline #10321 passed with stage
in 1 minute and 35 seconds
......@@ -122,6 +122,9 @@ public class CityGmlParser {
private static CityGMLContext context;
private static List<QName> chunkProperties = new ArrayList<>();
// Toggle to suppress logger output for parsing of libraryObjects
private static boolean gagged = false;
static {
System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
FACTORY = SAXParserFactory.newInstance();
......@@ -326,20 +329,29 @@ public class CityGmlParser {
return writer;
}
/**
* Suppresses logger output of {@link #readAndKeepFeatures} for the next parse.
* Used to prevent logging spam while resolving implicit geometries.
*/
public static void gagLogger(boolean value){
gagged = value;
}
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file,
CityGMLInputFactory inputFactory, ObservedInputStream ois) throws CityGMLReadException {
try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) {
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file);
CityGMLVersion version = null;
// model is read in chunked mode
// object members are replaced by href in model
// need to remove the refs and re-add unparsed objects
List<AbstractCityObject> acos = new ArrayList<>();
while (reader.hasNext()) {
AbstractFeature chunk = reader.next();
version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI());
if (chunk instanceof CityModel cModel) {
cModel.setCityObjectMembers(null);
mapper.setCityModel(cModel);
CityGMLVersion version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI());
mapper.setCityGMLVersion(version);
} else if (chunk instanceof AbstractCityObject aco) {
acos.add(aco);
......@@ -362,10 +374,14 @@ public class CityGmlParser {
for (AbstractCityObject aco : acos) {
cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
}
if (logger.isInfoEnabled()) {
if (logger.isInfoEnabled() && !gagged) {
logger.info(Localization.getText("CityGmlParser.parsedObjects"),
mapper.getModel().getNumberOfFeatures());
} else if (gagged){
// Remove gag
gagged = false;
}
mapper.setCityGMLVersion(version);
return mapper.getModel();
}
}
......
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
......@@ -18,181 +18,198 @@
*/
package de.hft.stuttgart.citydoctor2.utils;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.*;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import org.citygml4j.core.util.geometry.GeometryFactory;
import org.locationtech.proj4j.BasicCoordinateTransform;
import org.locationtech.proj4j.ProjCoordinate;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface;
import org.xmlobjects.gml.model.geometry.complexes.CompositeSurface;
import org.xmlobjects.gml.model.geometry.primitives.AbstractRingProperty;
import org.xmlobjects.gml.model.geometry.primitives.Shell;
import org.xmlobjects.gml.model.geometry.primitives.ShellProperty;
import org.xmlobjects.gml.model.geometry.primitives.Solid;
import org.xmlobjects.gml.model.geometry.primitives.SurfaceProperty;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import org.xmlobjects.gml.model.geometry.primitives.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Utility class dedicated to create CityGML4j datastructures.
*
* @author Matthias Betz
*
* @author Matthias Betz
*/
public final class CityGmlUtils {
private CityGmlUtils() {
// util class
}
public static org.xmlobjects.gml.model.geometry.primitives.Polygon createGmlPolygon(GeometryFactory factory,
Polygon cdPoly, ParserConfiguration config) {
if (cdPoly.getExteriorRing() == null) {
return null;
}
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = new org.xmlobjects.gml.model.geometry.primitives.Polygon();
// exterior ring
LinearRing extLr = cdPoly.getExteriorRing();
if (extLr.getVertices().size() < 3) {
// this ring does not have enough points in it
// this leads to errors when exporting therefore ignore it
return null;
}
org.xmlobjects.gml.model.geometry.primitives.LinearRing gmlLr = createGmlRing(factory, config, extLr);
gmlPoly.setExterior(new AbstractRingProperty(gmlLr));
// interior rings
for (LinearRing lr : cdPoly.getInnerRings()) {
gmlLr = createGmlRing(factory, config, lr);
if (lr.getVertices().size() < 3) {
// this ring does not have enough points in it
// this leads to errors when exporting therefore ignore it
return null;
}
gmlPoly.getInterior().add(new AbstractRingProperty(gmlLr));
}
gmlPoly.setId(cdPoly.getGmlId().getGmlString());
return gmlPoly;
}
public static org.xmlobjects.gml.model.geometry.primitives.LinearRing createGmlRing(GeometryFactory factory,
ParserConfiguration config, LinearRing lr) {
ProjCoordinate p1 = new ProjCoordinate();
ProjCoordinate p2 = new ProjCoordinate();
List<Double> ringValues = new ArrayList<>();
BasicCoordinateTransform trans = config.getOriginalTransform();
for (Vertex v : lr.getVertices()) {
double x = v.getX();
double y = v.getY();
double z = v.getZ();
if (trans != null) {
p1.x = x;
p1.y = y;
trans.transform(p1, p2);
x = p2.x;
y = p2.y;
z = z * config.getFromMeters();
}
ringValues.add(x);
ringValues.add(y);
ringValues.add(z);
}
org.xmlobjects.gml.model.geometry.primitives.LinearRing gmlLr = factory.createLinearRing(ringValues, 3);
gmlLr.setId(lr.getGmlId().getGmlString());
return gmlLr;
}
public static Solid createSolid(Geometry geom, GeometryFactory factory, ParserConfiguration config) {
if (geom.getType() != GeometryType.SOLID) {
throw new IllegalArgumentException("Only solids are allowed");
}
CompositeSurface comp = new CompositeSurface();
List<SurfaceProperty> surfaceMember = comp.getSurfaceMembers();
for (Polygon cdPoly : geom.getPolygons()) {
if (!cdPoly.isLink()) {
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
if (gmlPoly != null) {
surfaceMember.add(new SurfaceProperty(gmlPoly));
}
} else {
// add reference to polygon
surfaceMember.add(new SurfaceProperty("#" + cdPoly.getGmlId().getGmlString()));
}
}
if (surfaceMember.isEmpty()) {
return null;
}
Solid solid = new Solid();
Shell shell = new Shell(surfaceMember);
solid.setExterior(new ShellProperty(shell));
solid.setId(geom.getGmlId().getGmlString());
return solid;
}
public static MultiSurface createMultiSurface(Geometry geom, GeometryFactory factory,
ParserConfiguration config) {
if (geom.getType() != GeometryType.MULTI_SURFACE) {
throw new IllegalArgumentException("This can only handle MultiSurfaces");
}
List<SurfaceProperty> surfaces = new ArrayList<>();
for (Polygon cdPoly : geom.getPolygons()) {
if (!cdPoly.isLink()) {
// is not part of a boundary surface
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
if (gmlPoly != null) {
surfaces.add(new SurfaceProperty(gmlPoly));
}
} else {
// add reference to polygon
surfaces.add(new SurfaceProperty("#" + cdPoly.getGmlId().getGmlString()));
}
}
if (surfaces.isEmpty()) {
return null;
}
MultiSurface ms = new MultiSurface(surfaces);
ms.setId(geom.getGmlId().getGmlString());
return ms;
}
public static MultiSurface createMultiSurface(List<Polygon> polygons, GeometryFactory factory,
ParserConfiguration config) {
List<SurfaceProperty> surfaces = new ArrayList<>();
for (Polygon cdPoly : polygons) {
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
if (gmlPoly != null) {
surfaces.add(new SurfaceProperty(gmlPoly));
}
}
if (surfaces.isEmpty()) {
return null;
}
return new MultiSurface(surfaces);
}
public static CompositeSurface createCompositeSurface(Geometry geom, GeometryFactory factory,
ParserConfiguration config) {
List<SurfaceProperty> surfaces = new ArrayList<>();
for (Polygon cdPoly : geom.getPolygons()) {
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
if (gmlPoly != null) {
surfaces.add(new SurfaceProperty(gmlPoly));
}
}
if (surfaces.isEmpty()) {
return null;
}
return new CompositeSurface(surfaces);
}
private CityGmlUtils() {
// util class
}
public static org.xmlobjects.gml.model.geometry.primitives.Polygon createGmlPolygon(GeometryFactory factory,
Polygon cdPoly, ParserConfiguration config) {
if (cdPoly.getExteriorRing() == null) {
return null;
}
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = new org.xmlobjects.gml.model.geometry.primitives.Polygon();
// exterior ring
LinearRing extLr = cdPoly.getExteriorRing();
if (extLr.getVertices().size() < 3) {
// this ring does not have enough points in it
// this leads to errors when exporting therefore ignore it
return null;
}
org.xmlobjects.gml.model.geometry.primitives.LinearRing gmlLr = createGmlRing(factory, config, extLr);
gmlPoly.setExterior(new AbstractRingProperty(gmlLr));
// interior rings
for (LinearRing lr : cdPoly.getInnerRings()) {
gmlLr = createGmlRing(factory, config, lr);
if (lr.getVertices().size() < 3) {
// this ring does not have enough points in it
// this leads to errors when exporting therefore ignore it
return null;
}
gmlPoly.getInterior().add(new AbstractRingProperty(gmlLr));
}
gmlPoly.setId(cdPoly.getGmlId().getGmlString());
return gmlPoly;
}
public static CompositeSurface createGmlComposite(GeometryFactory factory, CompositeCollection comp,
ParserConfiguration config) {
List<SurfaceProperty> surfaces = new ArrayList<>();
for (ConcretePolygon cd : comp.getNonRecursiveCompositeMembers()) {
surfaces.add(new SurfaceProperty(createGmlPolygon(factory, cd, config)));
}
for (CompositeCollection cc : comp.getChildComposites()) {
surfaces.add(new SurfaceProperty(createGmlComposite(factory, cc, config)));
}
if (surfaces.isEmpty()) {
return null;
}
return new CompositeSurface(surfaces);
}
public static org.xmlobjects.gml.model.geometry.primitives.LinearRing createGmlRing(GeometryFactory factory,
ParserConfiguration config, LinearRing lr) {
ProjCoordinate p1 = new ProjCoordinate();
ProjCoordinate p2 = new ProjCoordinate();
List<Double> ringValues = new ArrayList<>();
BasicCoordinateTransform trans = config.getOriginalTransform();
for (Vertex v : lr.getVertices()) {
double x = v.getX();
double y = v.getY();
double z = v.getZ();
if (trans != null) {
p1.x = x;
p1.y = y;
trans.transform(p1, p2);
x = p2.x;
y = p2.y;
z = z * config.getFromMeters();
}
ringValues.add(x);
ringValues.add(y);
ringValues.add(z);
}
org.xmlobjects.gml.model.geometry.primitives.LinearRing gmlLr = factory.createLinearRing(ringValues, 3);
gmlLr.setId(lr.getGmlId().getGmlString());
return gmlLr;
}
public static Solid createSolid(Geometry geom, GeometryFactory factory, ParserConfiguration config) {
if (geom.getType() != GeometryType.SOLID) {
throw new IllegalArgumentException("Only solids are allowed");
}
CompositeSurface comp = new CompositeSurface();
List<SurfaceProperty> surfaceMember = comp.getSurfaceMembers();
for (Polygon cdPoly : geom.getPolygons()) {
if (!cdPoly.isLink()) {
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
if (gmlPoly != null) {
surfaceMember.add(new SurfaceProperty(gmlPoly));
}
} else {
// add reference to polygon
surfaceMember.add(new SurfaceProperty("#" + cdPoly.getGmlId().getGmlString()));
}
}
if (surfaceMember.isEmpty()) {
return null;
}
Solid solid = new Solid();
Shell shell = new Shell(surfaceMember);
solid.setExterior(new ShellProperty(shell));
solid.setId(geom.getGmlId().getGmlString());
return solid;
}
public static MultiSurface createMultiSurface(Geometry geom, GeometryFactory factory,
ParserConfiguration config) {
if (geom.getType() != GeometryType.MULTI_SURFACE && geom.getType() != GeometryType.COMPOSITE_SURFACE) {
throw new IllegalArgumentException("This can only handle MultiSurfaces");
}
List<SurfaceProperty> surfaces = new ArrayList<>();
Set<CompositeCollection> compositeCollections = new HashSet<>();
for (Polygon cdPoly : geom.getPolygons()) {
if (cdPoly instanceof ConcretePolygon conc && conc.isCompositeMember()) {
compositeCollections.add(conc.getPartOfComposite());
} else if (!cdPoly.isLink()) {
// is not part of a boundary surface
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
if (gmlPoly != null) {
surfaces.add(new SurfaceProperty(gmlPoly));
}
} else {
// add reference to polygon
surfaces.add(new SurfaceProperty("#" + cdPoly.getGmlId().getGmlString()));
}
}
for (CompositeCollection cdPoly : compositeCollections) {
surfaces.add(new SurfaceProperty(createGmlComposite(factory, cdPoly, config)));
}
if (surfaces.isEmpty()) {
return null;
}
MultiSurface ms = new MultiSurface(surfaces);
ms.setId(geom.getGmlId().getGmlString());
return ms;
}
public static MultiSurface createMultiSurface(List<Polygon> polygons, GeometryFactory factory,
ParserConfiguration config) {
List<SurfaceProperty> surfaces = new ArrayList<>();
for (Polygon cdPoly : polygons) {
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
if (gmlPoly != null) {
surfaces.add(new SurfaceProperty(gmlPoly));
}
}
if (surfaces.isEmpty()) {
return null;
}
return new MultiSurface(surfaces);
}
public static CompositeSurface createCompositeSurface(Geometry geom, GeometryFactory factory,
ParserConfiguration config) {
List<SurfaceProperty> surfaces = new ArrayList<>();
for (Polygon cdPoly : geom.getPolygons()) {
org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly = createGmlPolygon(factory, cdPoly, config);
if (gmlPoly != null) {
surfaces.add(new SurfaceProperty(gmlPoly));
}
}
if (surfaces.isEmpty()) {
return null;
}
return new CompositeSurface(surfaces);
}
}
......@@ -68,7 +68,7 @@ public class CityGMLWriterUtils {
public static void writeCityModel(String file, CityDoctorModel model) throws CityDoctorWriteException {
CityGMLContext gmlContext = CityGmlParser.getContext();
CityModel cModel = model.getCityModel();
CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(CityGMLVersion.v2_0);
CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(model.getCityGMLVersion());
try (CityGMLWriter writer = factory.createCityGMLWriter(new File(file))) {
writer.withIndent(" ");
writer.withDefaultPrefixes();
......@@ -92,6 +92,9 @@ public class CityGMLWriterUtils {
storeCityObjects(model.getLand(), gmlFactory, model, cModel, val);
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.getTunnels(), gmlFactory, model, cModel, val);
writer.write(cModel);
cModel.getCityObjectMembers().clear();
......@@ -169,6 +172,7 @@ public class CityGMLWriterUtils {
if (model.isValidated() && val != null) {
QualityADEUtils.writeQualityAde(co, val);
}
co.reCreateGeometries(gmlFactory, model.getParserConfig());
cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(co.getGmlObject()));
}
......
......@@ -29,6 +29,9 @@ MainToolBar.wireframe=Show/Hide Wireframe
MainToolBar.culling=Enable/Disable Culling
MainToolBar.writeReports=Write Reports
MainToolBar.executeChecks=Execute Checks
MainToolBar.showWorld=Show entire city model
MainToolBar.resetCamera=Reset view
MainToolBar.hideRoof=Show/Hide roof BoundarySurfaces
MainWindow.missingConfig=Could not find configuration file.
MainWindow.loadGivenFile=Loading given file, please wait
MainWindow.finishedLoading=Finished loading
......@@ -46,9 +49,12 @@ MainWindow.languageChange=For the change in language to apply, restart CityDocto
MainWindow.buildingsTab=Buildings
MainWindow.vegetationTab=Vegetation
MainWindow.transportationTab=Transportation
MainWindow.tunnelTab=Tunnels
MainWindow.bridgeTab=Bridges
MainWindow.waterTab=Water
MainWindow.terrainTab=Terrain
MainWindow.cityfurnitureTab=CityFurniture
MainWindow.otherObjectsTab=Other
MainWindow.viewLabel=View
MainWindow.showLabel=Show:
MainWindow.searchLabel=Search:
......@@ -58,6 +64,7 @@ MainWindow.errorsTab=Errors
MainWindow.polygonsTab=Polygons
MainWindow.edgesTab=Edges
MainWindow.verticesTab=Vertices
MainWindow.attributeTab=Attributes
MainWindow.logTab=Log
MainWindow.globalErrorsTab=Global Errors
OpenFileDialog.fileLabel=File:
......
......@@ -27,6 +27,9 @@ MainToolBar.wireframe=Zeige/Verstecke Gitternetz
MainToolBar.culling=Aktiviere/Deaktiviere Entfernen der R\u00fcckseiten
MainToolBar.writeReports=Schreibe Reports
MainToolBar.executeChecks=F\u00fchre Pr\u00fcfungen aus
MainToolBar.showWorld=Gesamtes Stadtmodell anzeigen
MainToolBar.resetCamera=Ansicht zur\u00fccksetzen
MainToolBar.hideRoof=Zeige/Verstecke Dach BoundarySurfaces
MainWindow.missingConfig=Konnte Konfigurationsdatei nicht finden.
MainWindow.loadGivenFile=Lade vorhandene Datei, bitte warten
MainWindow.finishedLoading=Fertig geladen
......@@ -44,9 +47,12 @@ MainWindow.languageChange=Um die Spracheinstellung zu \u00fcbernehmen muss CityD
MainWindow.buildingsTab=Geb\u00e4ude
MainWindow.vegetationTab=Vegetation
MainWindow.transportationTab=Verkehrsobjekte
MainWindow.tunnelTab=Tunnel
MainWindow.bridgeTab=Br\u00fccken
MainWindow.waterTab=Gew\u00e4sser
MainWindow.terrainTab=Gel\u00e4nde
MainWindow.cityfurnitureTab=Stadtm\u00f6bel
MainWindow.otherObjectsTab=Andere
MainWindow.viewLabel=Ansicht
MainWindow.showLabel=Zeige:
MainWindow.searchLabel=Suche:
......@@ -56,6 +62,7 @@ MainWindow.errorsTab=Fehler
MainWindow.polygonsTab=Polygone
MainWindow.edgesTab=Kanten
MainWindow.verticesTab=Punkte
MainWindow.attributeTab=Attribute
MainWindow.logTab=Log
MainWindow.globalErrorsTab=Globale Fehler
OpenFileDialog.fileLabel=Datei:
......
......@@ -28,6 +28,7 @@ import java.util.List;
import java.util.UUID;
import org.citygml4j.core.ade.ADEException;
import org.citygml4j.core.model.CityGMLVersion;
import org.citygml4j.core.model.core.AbstractFeatureProperty;
import org.citygml4j.core.model.core.CityModel;
import org.citygml4j.xml.writer.CityGMLWriteException;
......@@ -131,6 +132,7 @@ public class CityDoctorModelTest {
File f = Mockito.mock(File.class);
ParserConfiguration config = new ParserConfiguration(8, false);
CityDoctorModel model = new CityDoctorModel(config, f);
model.setParsedCityGMLVersion(CityGMLVersion.v2_0);
CityModel cModel = new CityModel();
model.setCityModel(cModel);
Building b = new Building();
......@@ -156,6 +158,7 @@ public class CityDoctorModelTest {
File f = Mockito.mock(File.class);
ParserConfiguration config = new ParserConfiguration(8, false);
CityDoctorModel model = new CityDoctorModel(config, f);
model.setParsedCityGMLVersion(CityGMLVersion.v2_0);
CityModel cModel = new CityModel();
ValidationPlan plan = new ValidationPlan();
plan.setId("CD_PLAN_" + UUID.randomUUID().toString());
......
package de.hft.stuttgart.citydoctor2.datastructure;
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 org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
public class CompositePolygonTest {
@Test
public void testCompositePolygonParsing() throws CityGmlParseException, InvalidGmlFileException {
ParserConfiguration config = new ParserConfiguration(8, false);
CityDoctorModel m = CityGmlParser.parseCityGmlFile("src/test/resources/FZK_haus.gml", config);
Building b = m.getBuildings().get(0);
assertNotNull(b);
assertFalse(b.getBoundarySurfaces().isEmpty());
for (BoundarySurface bs : b.getBoundarySurfaces()) {
assertNotNull(bs);
assertFalse(bs.getGeometries().isEmpty());
for (Geometry geom : bs.getGeometries()) {
assertNotNull(geom);
assertFalse(geom.getPolygons().isEmpty());
}
}
}
}
package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.check.AbstractCheck;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import org.citygml4j.core.model.core.ImplicitGeometry;
import org.citygml4j.core.model.core.TransformationMatrix4x4;
import org.junit.Before;
import org.junit.Test;
import org.xmlobjects.gml.model.geometry.DirectPosition;
import org.xmlobjects.gml.model.geometry.primitives.Point;
import org.xmlobjects.gml.model.geometry.primitives.PointProperty;
import org.xmlobjects.gml.util.jama.Matrix;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.*;
public class ImplicitGeometryTest {
private Path path;
private ParserConfiguration config;
private String pathString = "src/test/resources/ImplicitTestGeom.gml";
private TransformationMatrix4x4 origin;
@Before
public void setUp() {
path = Paths.get(pathString);
config = new ParserConfiguration(8, false);
Matrix og = new Matrix(4, 4);
og.set(0, 0, 1);
og.set(1, 1, 1);
og.set(2, 2, 1);
origin = new TransformationMatrix4x4(og);
}
@Test
public void testLibraryObjectParsing() {
LibraryObject lo = LibraryObject.of(path, config);
assertNotNull(lo);
LibraryObject lo2 = LibraryObject.of(path, config);
assertSame(lo, lo2);
}
@Test
public void testObjectTransform() {
LibraryObject lo = LibraryObject.of(path, config);
assertNotNull(lo);
ImplicitGeometry geom = new ImplicitGeometry();
geom.setTransformationMatrix(origin);
geom.setReferencePoint(new PointProperty(new Point(new DirectPosition(0, 0))));
ImplicitGeometryHolder igh = new ImplicitGeometryHolder(geom, lo);
assertNotNull(igh);
geom.setReferencePoint(new PointProperty(new Point(new DirectPosition(5, 8))));
ImplicitGeometryHolder igh2 = new ImplicitGeometryHolder(geom, lo);
assertNotNull(igh2);
assertNotEquals(igh, igh2);
BoundingBox bb1 = BoundingBox.of(igh.getPolygons());
BoundingBox bb2 = BoundingBox.of(igh2.getPolygons());
assertNotEquals(bb1, bb2);
}
@Test
public void testAccept() {
LibraryObject lib = LibraryObject.of(path, config);
assertNotNull(lib);
ImplicitGeometry geom = new ImplicitGeometry();
geom.setTransformationMatrix(origin);
geom.setReferencePoint(new PointProperty(new Point(new DirectPosition(0, 0))));
int polyCount = lib.getPolygons().size();
ImplicitGeometryHolder igh = new ImplicitGeometryHolder(geom, lib);
AtomicInteger loCounter = new AtomicInteger(0);
AbstractCheck c = new AbstractCheck() {
@Override
public void check(Polygon lo) {
loCounter.incrementAndGet();
}
};
igh.accept(c);
assertEquals("ImplicitGeometryHolder should only check polygons of prototype geometry",
polyCount, loCounter.intValue());
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="utf-8"?>
<core:CityModel xmlns:core='http://www.opengis.net/citygml/2.0' xmlns:xlink='http://www.w3.org/1999/xlink' xmlns:gml='http://www.opengis.net/gml' xmlns:xAL='urn:oasis:names:tc:ciq:xsdschema:xAL:2.0' xmlns:app='http://www.opengis.net/citygml/appearance/2.0' xmlns:brid='http://www.opengis.net/citygml/bridge/2.0' xmlns:bldg='http://www.opengis.net/citygml/building/2.0' xmlns:frn='http://www.opengis.net/citygml/cityfurniture/2.0' xmlns:grp='http://www.opengis.net/citygml/cityobjectgroup/2.0' xmlns:gen='http://www.opengis.net/citygml/generics/2.0' xmlns:luse='http://www.opengis.net/citygml/landuse/2.0' xmlns:dem='http://www.opengis.net/citygml/relief/2.0' xmlns:tex='http://www.opengis.net/citygml/texturedsurface/2.0' xmlns:tran='http://www.opengis.net/citygml/transportation/2.0' xmlns:tun='http://www.opengis.net/citygml/tunnel/2.0' xmlns:veg='http://www.opengis.net/citygml/vegetation/2.0' xmlns:wtr='http://www.opengis.net/citygml/waterbody/2.0' xmlns:smil20='http://www.w3.org/2001/SMIL20/' xmlns:smil='http://www.w3.org/2001/SMIL20/Language' xmlns:xml='http://www.w3.org/XML/1998/namespace' xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://www.opengis.net/citygml/profiles/base/2.0 http://schemas.opengis.net/citygml/profiles/base/2.0/CityGML.xsd' gml:id='0'>
<core:cityObjectMember>
<frn:CityFurniture gml:id='ImplicitGeometryLod2'>
<gml:name>TestGeometry</gml:name>
<frn:lod2ImplicitRepresentation>
<core:ImplicitGeometry gml:id='ImplicitGeometry'>
<core:transformationMatrix>
1.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0
0.0 0.0 1.0 0.0
0.0 0.0 0.0 1.0
</core:transformationMatrix>
<core:relativeGMLGeometry>
<gml:CompositeSurface gml:id='1' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:surfaceMember>
<gml:Polygon gml:id='2' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='3' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 4.034 -2.193 58.441 4.034 -2.193 58.158 4.034 -1.750 58.441 4.034 -2.193 58.441</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='4' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='5' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 4.034 -0.039 100.000 4.034 1.864 58.441 4.034 -1.750 58.441 4.034 -0.039 59.536 4.034 -0.039 100.000</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='6' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='7' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 6.111 -37.093 35.821 6.110 -0.039 59.536 4.034 -0.039 59.536 4.034 -1.750 58.441 4.034 -2.193 58.158 4.035 -37.093 35.821 6.111 -37.093 35.821</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='8' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='9' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 6.110 -0.039 100.000 6.110 1.973 56.052 4.034 1.973 56.052 4.034 1.864 58.441 4.034 -0.039 100.000 6.110 -0.039 100.000</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='10' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='11' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 4.035 -37.093 35.821 4.034 -2.164 55.986 4.034 -2.050 56.052 6.110 -2.050 56.052 6.111 -37.093 35.821 4.035 -37.093 35.821</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='12' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='13' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 6.111 -37.093 35.821 6.110 -2.050 56.052 6.110 37.015 35.821 6.110 1.973 56.052 6.110 -0.039 100.000 6.110 -0.039 59.536 6.111 -37.093 35.821</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='14' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='15' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 4.035 -37.093 35.821 4.034 -2.193 58.158 4.034 -2.193 55.986 4.034 -2.164 55.986 4.035 -37.093 35.821</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='16' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='17' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 4.034 37.015 35.821 4.034 -1.923 55.986 4.034 2.087 55.986 4.034 37.015 35.821</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='18' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='19' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 1.696 -0.979 55.986 -0.000 -1.958 55.986 -0.000 -1.958 0.000 1.696 -0.979 0.000 1.696 -0.979 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='20' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='21' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 4.034 -2.164 55.986 4.034 -1.923 55.986 4.034 -2.050 56.052 4.034 -2.164 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='22' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='23' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 6.110 37.015 35.821 6.110 -2.050 56.052 4.034 -2.050 56.052 4.034 -1.923 55.986 4.034 37.015 35.821 6.110 37.015 35.821</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='24' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='25' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 4.034 37.015 35.821 4.034 2.087 55.986 4.034 1.973 56.052 6.110 1.973 56.052 6.110 37.015 35.821 4.034 37.015 35.821</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='26' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='27' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -0.000 1.958 0.000 1.696 0.979 0.000 1.696 -0.979 0.000 -0.000 -1.958 0.000 -1.696 -0.979 0.000 -1.696 0.979 0.000 -0.000 1.958 0.000</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='28' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='29' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -1.696 0.979 55.986 -0.000 1.958 55.986 -0.000 1.958 0.000 -1.696 0.979 0.000 -1.696 0.979 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='30' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='31' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -3.336 -2.193 58.441 4.034 -2.193 58.441 4.034 -1.750 58.441 4.034 1.864 58.441 4.034 2.116 58.441 -3.336 2.116 58.441 -3.336 -2.193 58.441</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='32' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='33' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -3.336 -2.193 55.986 4.034 -2.193 55.986 4.034 -2.193 58.158 4.034 -2.193 58.441 -3.336 -2.193 58.441 -3.336 -2.193 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='34' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='35' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 4.034 -0.039 100.000 4.034 -0.039 59.536 6.110 -0.039 59.536 6.110 -0.039 100.000 4.034 -0.039 100.000</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='36' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='37' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -0.000 1.958 55.986 1.696 0.979 55.986 1.696 0.979 0.000 -0.000 1.958 0.000 -0.000 1.958 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='38' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='39' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 4.034 2.116 58.441 4.034 1.864 58.441 4.034 1.973 56.052 4.034 2.087 55.986 4.034 2.116 55.986 4.034 2.116 58.441</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='40' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='41' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> 1.696 0.979 55.986 1.696 -0.979 55.986 1.696 -0.979 0.000 1.696 0.979 0.000 1.696 0.979 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='42' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='43' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -0.000 -1.958 55.986 -1.696 -0.979 55.986 -1.696 -0.979 0.000 -0.000 -1.958 0.000 -0.000 -1.958 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='44' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='45' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -3.336 2.116 55.986 4.034 2.116 55.986 4.034 2.087 55.986 4.034 -1.923 55.986 4.034 -2.164 55.986 4.034 -2.193 55.986 -3.336 -2.193 55.986 -3.336 2.116 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
<gml:interior>
<gml:LinearRing gml:id='46' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -0.000 1.958 55.986 -1.696 0.979 55.986 -1.696 -0.979 55.986 -0.000 -1.958 55.986 1.696 -0.979 55.986 1.696 0.979 55.986 -0.000 1.958 55.986</gml:posList>
</gml:LinearRing>
</gml:interior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='47' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='48' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -1.696 -0.979 55.986 -1.696 0.979 55.986 -1.696 0.979 0.000 -1.696 -0.979 0.000 -1.696 -0.979 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='49' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='50' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -3.336 2.116 55.986 -3.336 -2.193 55.986 -3.336 -2.193 58.441 -3.336 2.116 58.441 -3.336 2.116 55.986</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id='51' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:exterior>
<gml:LinearRing gml:id='52' srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'>
<gml:posList srsName='urn:ogc:def:crs:EPSG::-1' srsDimension='3'> -3.336 2.116 58.441 4.034 2.116 58.441 4.034 2.116 55.986 -3.336 2.116 55.986 -3.336 2.116 58.441</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:CompositeSurface>
</core:relativeGMLGeometry>
<core:referencePoint>
<gml:Point><gml:pos srsDimension="3">0.0 0.0 0.0</gml:pos>
</gml:Point></core:referencePoint>
</core:ImplicitGeometry>
</frn:lod2ImplicitRepresentation>
</frn:CityFurniture>
</core:cityObjectMember>
</core:CityModel>
......@@ -22,6 +22,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.datastructure.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -34,21 +35,6 @@ import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.Installation;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LandObject;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject;
import de.hft.stuttgart.citydoctor2.datastructure.Vegetation;
import de.hft.stuttgart.citydoctor2.datastructure.WaterObject;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.Localization;
......@@ -244,6 +230,24 @@ public class CheckContainer extends Check {
}
}
@Override
public void check(CityFurniture cf){
try {
check.check(cf);
} catch (Exception e) {
handleException(e, cf);
}
}
@Override
public void check(GenericCityObject gco) {
try {
check.check(gco);
} catch (Exception e) {
handleException(e, gco);
}
}
@Override
public RequirementType getType() {
return check.getType();
......
package de.hft.stuttgart.citydoctor2.gui;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.CityModel;
import org.xml.sax.SAXParseException;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.ErrorId;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeConstructiveElement;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.Installation;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.FeatureType;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LandObject;
import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject;
import de.hft.stuttgart.citydoctor2.datastructure.TinObject;
import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject;
import de.hft.stuttgart.citydoctor2.datastructure.Vegetation;
import de.hft.stuttgart.citydoctor2.datastructure.*;
import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException;
import de.hft.stuttgart.citydoctor2.gui.table.ErrorStat;
import de.hft.stuttgart.citydoctor2.gui.tree.AllBoundarySurfacesNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllBridgeConstructiveElementsNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllBridgePartsNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllBridgesNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllInstallationsNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllBuildingPartsNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllBuildingsNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllOpeningsNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllTerrainNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllTinNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllTransportationNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllVegetationNode;
import de.hft.stuttgart.citydoctor2.gui.tree.AllWaterNode;
import de.hft.stuttgart.citydoctor2.gui.tree.BoundarySurfaceNode;
import de.hft.stuttgart.citydoctor2.gui.tree.BridgeConstructiveElementNode;
import de.hft.stuttgart.citydoctor2.gui.tree.BridgeNode;
import de.hft.stuttgart.citydoctor2.gui.tree.InstallationNode;
import de.hft.stuttgart.citydoctor2.gui.tree.BuildingNode;
import de.hft.stuttgart.citydoctor2.gui.tree.BuildingPartNode;
import de.hft.stuttgart.citydoctor2.gui.tree.ButtonRenderable;
import de.hft.stuttgart.citydoctor2.gui.tree.CityObjectNode;
import de.hft.stuttgart.citydoctor2.gui.tree.GeometryNode;
import de.hft.stuttgart.citydoctor2.gui.tree.LandUseNode;
import de.hft.stuttgart.citydoctor2.gui.tree.OpeningNode;
import de.hft.stuttgart.citydoctor2.gui.tree.ReliefNode;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import de.hft.stuttgart.citydoctor2.gui.tree.TinNode;
import de.hft.stuttgart.citydoctor2.gui.tree.VegetationNode;
import de.hft.stuttgart.citydoctor2.gui.tree.*;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler;
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 de.hft.stuttgart.citydoctor2.parser.*;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.application.Platform;
import javafx.scene.chart.XYChart.Data;
......@@ -84,1003 +21,1244 @@ import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.CityModel;
import org.xml.sax.SAXParseException;
import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
public class CityDoctorController {
private static final int MAX_FEATURES_PER_CHUNK = 200;
private static final Logger logger = LogManager.getLogger(CityDoctorController.class);
private MainWindow mainWindow;
private CityDoctorModel model;
private ParserConfiguration currentConfig;
private String sourceFile;
private Checker currentChecker;
private HighlightController highlightController;
private Renderer renderer;
private AtomicInteger buildingChunkNr = new AtomicInteger(0);
private AtomicInteger vegetationChunkNr = new AtomicInteger(0);
private AtomicInteger transportationChunkNr = new AtomicInteger(0);
private AtomicInteger bridgeChunkNr = new AtomicInteger(0);
private AtomicInteger waterChunkNr = new AtomicInteger(0);
private AtomicInteger landChunkNr = new AtomicInteger(0);
public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) {
this.mainWindow = mainWindow;
this.highlightController = highlightController;
this.renderer = renderer;
}
public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation)
throws CityGmlParseException, InvalidGmlFileException {
loadCityGml(path, numberOfRoundingPlaces, l, useValidation, false);
}
public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation,
boolean lowMemory) throws CityGmlParseException, InvalidGmlFileException {
try {
Platform.runLater(() -> {
mainWindow.getOpenBtn().setDisable(true);
mainWindow.getWriteReportButton().setDisable(true);
mainWindow.getMeshGroup().getChildren().clear();
clearTrees();
mainWindow.resetSearchBar();
mainWindow.resetFilterComboBox();
});
currentChecker = null;
currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory);
GMLValidationHandler handler = null;
List<String> validationIssues = new ArrayList<>();
if (useValidation) {
handler = new GMLValidationHandler() {
@Override
public void error(SAXParseException exception) {
if (exception.getLineNumber() >= 0) {
String s = "In line " + exception.getLineNumber() + ":";
validationIssues.add(s);
}
validationIssues.add(exception.getMessage());
}
@Override
public void warning(SAXParseException exception) {
error(exception);
}
@Override
public void fatalError(SAXParseException exception) {
error(exception);
}
};
}
model = CityGmlParser.parseCityGmlFile(path, currentConfig, l, handler);
if (!validationIssues.isEmpty()) {
StringJoiner sj = new StringJoiner("\n");
validationIssues.stream().forEach(sj::add);
throw new InvalidGmlFileException(sj.toString());
}
mainWindow.getClickHandler().setConfig(currentConfig);
sourceFile = path;
renderer.reset();
Platform.runLater(() -> {
mainWindow.addFileNameToTitle(path);
mainWindow.getCheckButton().setDisable(false);
mainWindow.getLod1Btn().setDisable(false);
mainWindow.getLod2Btn().setDisable(false);
mainWindow.getLod3Btn().setDisable(false);
mainWindow.getLod4Btn().setDisable(false);
mainWindow.getWorldBtn().setDisable(false);
mainWindow.getSaveBtn().setDisable(false);
buildTrees();
});
} finally {
Platform.runLater(() -> mainWindow.getOpenBtn().setDisable(false));
}
}
public void buildTrees() {
resetFeatureChunks();
buildBuildings(model);
buildVegetation(model.getVegetation());
buildTransportation(model.getTransportation());
buildBridges(model);
buildWater(model);
buildLand(model);
}
private void resetFeatureChunks() {
buildingChunkNr.set(0);
vegetationChunkNr.set(0);
transportationChunkNr.set(0);
bridgeChunkNr.set(0);
waterChunkNr.set(0);
landChunkNr.set(0);
}
private void buildLand(CityDoctorModel model) {
if (model.getLand().isEmpty()) {
return;
}
TreeView<Renderable> landView = mainWindow.getTerrainView();
TreeItem<Renderable> landRoot = new TreeItem<>(new AllTerrainNode(model.getLand()));
landRoot.setExpanded(true);
landView.setRoot(landRoot);
buildLandFromList(model.getLand(), landView.getRoot());
addMoreButtonIfNecessary(model.getLand(), landView, landRoot, landChunkNr);
}
private void buildLandFromList(List<CityObject> list, TreeItem<Renderable> root) {
int landChunk = landChunkNr.get();
for (int i = landChunk * MAX_FEATURES_PER_CHUNK; i < (landChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
CityObject land = list.get(i);
Renderable node;
if (land instanceof ReliefObject relief) {
node = new ReliefNode(relief);
} else if (land instanceof TinObject tin) {
node = new TinNode(tin);
} else {
node = new LandUseNode((LandObject) land);
}
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(land, item);
if (land instanceof ReliefObject relief) {
createTinNodes(relief, item);
}
}
}
private void createTinNodes(ReliefObject relief, TreeItem<Renderable> item) {
if (relief.getComponents().isEmpty()) {
return;
}
AllTinNode allTinText = new AllTinNode(relief.getComponents());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allTinText);
item.getChildren().add(allBpsTextItem);
for (TinObject tin : relief.getComponents()) {
TinNode tinNode = new TinNode(tin);
TreeItem<Renderable> tinNodeItem = new TreeItem<>(tinNode);
tinNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(tinNodeItem);
createGeometryNodes(tin, tinNodeItem);
}
}
private void buildWater(CityDoctorModel model) {
if (model.getWater().isEmpty()) {
return;
}
TreeView<Renderable> waterView = mainWindow.getWaterView();
TreeItem<Renderable> waterRoot = new TreeItem<>(new AllWaterNode(model.getWater()));
waterRoot.setExpanded(true);
waterView.setRoot(waterRoot);
buildTreeFromList(model.getWater(), waterView.getRoot(), waterChunkNr);
addMoreButtonIfNecessary(model.getWater(), waterView, waterRoot, waterChunkNr);
}
private void buildBridges(CityDoctorModel model) {
if (model.getBridges().isEmpty()) {
return;
}
TreeView<Renderable> bridgeView = mainWindow.getBridgeView();
TreeItem<Renderable> bridgeRoot = new TreeItem<>(new AllBridgesNode(model.getBridges()));
bridgeRoot.setExpanded(true);
bridgeView.setRoot(bridgeRoot);
buildBridgeTreeFromList(model.getBridges(), bridgeView.getRoot());
addMoreButtonIfNecessary(model.getBridges(), bridgeView, bridgeRoot, bridgeChunkNr);
}
private void buildTransportation(List<TransportationObject> trans) {
if (trans.isEmpty()) {
return;
}
TreeView<Renderable> transView = mainWindow.getTransportationView();
TreeItem<Renderable> transRoot = new TreeItem<>(new AllTransportationNode(model.getTransportation()));
transRoot.setExpanded(true);
transView.setRoot(transRoot);
buildTreeFromList(trans, transView.getRoot(), transportationChunkNr);
addMoreButtonIfNecessary(trans, transView, transRoot, transportationChunkNr);
}
private void buildVegetation(List<Vegetation> veg) {
if (veg.isEmpty()) {
return;
}
TreeView<Renderable> vegetationsView = mainWindow.getVegetationView();
TreeItem<Renderable> vegRoot = new TreeItem<>(new AllVegetationNode(model.getVegetation()));
vegRoot.setExpanded(true);
vegetationsView.setRoot(vegRoot);
buildTreeFromListWithProducer(veg, vegetationsView.getRoot(), vegetationChunkNr, VegetationNode::new);
addMoreButtonIfNecessary(veg, vegetationsView, vegRoot, vegetationChunkNr);
}
private void addMoreButtonIfNecessary(List<? extends CityObject> list, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter) {
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
Runnable run = new Runnable() {
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.incrementAndGet();
int visibleFeatures = countVisibleNodes(view);
buildTreeFromList(list, root, chunkCounter);
updateTree(root);
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
};
// add button for expansion
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void buildBuildings(CityDoctorModel model) {
if (model.getBuildings().isEmpty()) {
return;
}
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = new TreeItem<>(new AllBuildingsNode(model.getBuildings()));
root.setExpanded(true);
buildBuildingTreeFromList(model.getBuildings(), root);
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), buildingsView, root, buildingChunkNr);
buildingsView.setRoot(root);
}
private void addMoreButtonToBuildingsIfNecessary(List<Building> list, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter) {
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
Runnable run = new Runnable() {
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);
buildBuildingTreeFromList(list, root);
updateTree(root);
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
};
// add button for expansion
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
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;
}
private void buildTreeFromList(List<? extends CityObject> cos, TreeItem<Renderable> root,
AtomicInteger chunkCounter) {
int chunk = chunkCounter.get();
for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) {
CityObject co = cos.get(i);
CityObjectNode node = new CityObjectNode(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
}
}
private <T extends CityObject> void buildTreeFromListWithProducer(List<T> cos, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Function<T, Renderable> nodeProducer) {
int chunk = chunkCounter.get();
for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) {
T co = cos.get(i);
Renderable node = nodeProducer.apply(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
}
}
private void clearTrees() {
mainWindow.getBuildingsView().setRoot(null);
mainWindow.getVegetationView().setRoot(null);
mainWindow.getTransportationView().setRoot(null);
mainWindow.getBridgeView().setRoot(null);
mainWindow.getWaterView().setRoot(null);
mainWindow.getTerrainView().setRoot(null);
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear();
clearGeometryTrees();
}
private void clearGeometryTrees() {
highlightController.clearHighlights();
mainWindow.getPolygonsView().getRoot().getChildren().clear();
mainWindow.getEdgeView().getRoot().getChildren().clear();
mainWindow.getVertexView().getRoot().getChildren().clear();
}
private void buildBuildingTreeFromList(List<Building> list, TreeItem<Renderable> root) {
int buildingChunk = buildingChunkNr.get();
for (int i = buildingChunk * MAX_FEATURES_PER_CHUNK; i < (buildingChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
Building b = list.get(i);
BuildingNode node = new BuildingNode(b);
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(b, item);
createBoundarySurfaceNodes(b.getBoundarySurfaces(), item);
createBuildingInstallationNodes(b, item);
createBuildingPartNodes(b, item);
}
}
private void buildBridgeTreeFromList(List<BridgeObject> list, TreeItem<Renderable> root) {
int bridgeChunk = bridgeChunkNr.get();
for (int i = bridgeChunk * MAX_FEATURES_PER_CHUNK; i < (bridgeChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
BridgeObject bridge = list.get(i);
BridgeNode node = new BridgeNode(bridge);
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(bridge, item);
createBoundarySurfaceNodes(bridge.getBoundarySurfaces(), item);
createBridgeInstallationNodes(bridge, item);
createConstructiveElementsNodes(bridge, item);
createBridgePartNodes(bridge, item);
}
}
private void createBridgeInstallationNodes(BridgeObject bridge, TreeItem<Renderable> root) {
createInstallationNodes(bridge.getBridgeInstallations(), root);
}
private void createConstructiveElementsNodes(BridgeObject bridge, TreeItem<Renderable> item) {
if (bridge.getConstructiveElements().isEmpty()) {
return;
}
AllBridgeConstructiveElementsNode allBceNode = new AllBridgeConstructiveElementsNode(
bridge.getConstructiveElements());
TreeItem<Renderable> allBceNodeTextItem = new TreeItem<>(allBceNode);
item.getChildren().add(allBceNodeTextItem);
for (BridgeConstructiveElement bce : bridge.getConstructiveElements()) {
BridgeConstructiveElementNode bceNode = new BridgeConstructiveElementNode(bce);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bceNode);
bpNodeItem.setExpanded(true);
allBceNodeTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bce, bpNodeItem);
createBoundarySurfaceNodes(bce.getBoundarySurfaces(), bpNodeItem);
}
}
private void createBridgePartNodes(BridgeObject bridge, TreeItem<Renderable> item) {
if (bridge.getParts().isEmpty()) {
return;
}
AllBridgePartsNode allBpsText = new AllBridgePartsNode(bridge.getParts());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText);
item.getChildren().add(allBpsTextItem);
for (BridgeObject bp : bridge.getParts()) {
BridgeNode bpNode = new BridgeNode(bp);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem);
}
}
private void createBuildingInstallationNodes(Building ab, TreeItem<Renderable> root) {
createInstallationNodes(ab.getBuildingInstallations(), root);
}
private void createInstallationNodes(List<Installation> installations, TreeItem<Renderable> root) {
if (installations.isEmpty()) {
return;
}
AllInstallationsNode allBiNode = new AllInstallationsNode(installations);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBiNode);
root.getChildren().add(allBpsTextItem);
for (Installation bi : installations) {
InstallationNode biNode = new InstallationNode(bi);
TreeItem<Renderable> biItem = new TreeItem<>(biNode);
biItem.setExpanded(true);
allBpsTextItem.getChildren().add(biItem);
createGeometryNodes(bi, biItem);
createBoundarySurfaceNodes(bi.getBoundarySurfaces(), biItem);
}
}
private void createBoundarySurfaceNodes(List<BoundarySurface> bsList, TreeItem<Renderable> root) {
if (bsList.isEmpty()) {
return;
}
AllBoundarySurfacesNode allBsText = new AllBoundarySurfacesNode(bsList);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBsText);
root.getChildren().add(allBpsTextItem);
allBpsTextItem.getChildren().add(new TreeItem<>());
allBpsTextItem.expandedProperty().addListener((obs, old, newV) -> {
if (Boolean.TRUE.equals(newV) && allBpsTextItem.getChildren().size() == 1) {
Platform.runLater(() -> {
allBpsTextItem.getChildren().clear();
for (BoundarySurface bs : bsList) {
BoundarySurfaceNode bsText = new BoundarySurfaceNode(bs);
TreeItem<Renderable> bsTextItem = new TreeItem<>(bsText);
bsTextItem.setExpanded(true);
allBpsTextItem.getChildren().add(bsTextItem);
createGeometryNodes(bs, bsTextItem);
createOpeningNodes(bs.getOpenings(), bsTextItem);
}
updateTree(allBpsTextItem);
});
}
});
}
private void createOpeningNodes(List<Opening> openings, TreeItem<Renderable> root) {
if (openings == null || openings.isEmpty()) {
return;
}
AllOpeningsNode allOpeningsNode = new AllOpeningsNode(openings);
TreeItem<Renderable> allOpeningsItem = new TreeItem<>(allOpeningsNode);
for (Opening o : openings) {
OpeningNode openingNode = new OpeningNode(o);
TreeItem<Renderable> openingsItem = new TreeItem<>(openingNode);
allOpeningsItem.getChildren().add(openingsItem);
}
root.getChildren().add(allOpeningsItem);
}
private void createBuildingPartNodes(Building ab, TreeItem<Renderable> item) {
if (ab.getBuildingParts().isEmpty()) {
return;
}
AllBuildingPartsNode allBpsText = new AllBuildingPartsNode(ab.getBuildingParts());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText);
item.getChildren().add(allBpsTextItem);
for (BuildingPart bp : ab.getBuildingParts()) {
BuildingPartNode bpNode = new BuildingPartNode(bp);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem);
}
}
private void createGeometryNodes(CityObject co, TreeItem<Renderable> item) {
for (Geometry geom : co.getGeometries()) {
GeometryNode geomNode = new GeometryNode(geom);
TreeItem<Renderable> geomItem = new TreeItem<>(geomNode);
item.getChildren().add(geomItem);
}
}
public void startChecks(ValidationConfiguration config, ProgressListener l) {
if (model == null) {
logger.warn(Localization.getText("CityDoctorController.noDatamodel"));
return;
}
if (sourceFile == null) {
logger.warn(Localization.getText("CityDoctorController.noSourceFile"));
return;
}
try {
Platform.runLater(() -> {
mainWindow.getCheckButton().setDisable(true);
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear();
});
config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces());
config.setParserConfig(currentConfig);
currentChecker = new Checker(config, model);
currentChecker.runChecks(l);
Platform.runLater(() -> {
// apply check results to tree views
updateFeatureTrees();
updateTree(mainWindow.getPolygonsView().getRoot());
for (CheckError e : model.getGlobalErrors()) {
if (e instanceof SchematronError se) {
mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString());
}
}
renderer.refresh();
mainWindow.getWriteReportButton().setDisable(false);
});
} finally {
Platform.runLater(() -> mainWindow.getCheckButton().setDisable(false));
}
}
void updateFeatureTrees() {
updateTree(mainWindow.getBuildingsView().getRoot());
updateTree(mainWindow.getVegetationView().getRoot());
updateTree(mainWindow.getBridgeView().getRoot());
updateTree(mainWindow.getTerrainView().getRoot());
updateTree(mainWindow.getTransportationView().getRoot());
updateTree(mainWindow.getWaterView().getRoot());
renderer.updateErrors();
}
private void updateTree(TreeItem<Renderable> root) {
if (root == null) {
return;
}
for (TreeItem<Renderable> item : root.getChildren()) {
updateItem(item);
}
}
private void updateItem(TreeItem<Renderable> item) {
if (item.getValue() == null) {
return;
}
item.getValue().refreshTextColor();
for (TreeItem<Renderable> child : item.getChildren()) {
updateItem(child);
}
}
public void writePdfReport(File pdfFile) {
if (currentChecker == null) {
return;
}
currentChecker.writePdfReport(pdfFile.getAbsolutePath());
}
public void writeXmlReport(File xmlFile) {
if (currentChecker == null) {
return;
}
currentChecker.writeXmlReport(xmlFile.getAbsolutePath());
}
private Map<ErrorId, ErrorStat> getErrorStats() {
Map<ErrorId, ErrorStat> stats = new HashMap<>();
addErrorStats(stats, model.getBridges());
addErrorStats(stats, model.getBuildings());
addErrorStats(stats, model.getLand());
addErrorStats(stats, model.getTransportation());
addErrorStats(stats, model.getVegetation());
addErrorStats(stats, model.getWater());
return stats;
}
private void addErrorStats(Map<ErrorId, ErrorStat> stats, List<? extends CityObject> cos) {
List<CheckError> errors = new ArrayList<>();
for (CityObject co : cos) {
co.collectContainedErrors(errors);
}
Set<CheckError> filteredErrors = new HashSet<>(errors);
for (CheckError error : filteredErrors) {
ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new);
stat.incrementCount();
}
}
public void export(Building b) {
if (model == null) {
return;
}
CityDoctorModel newModel = new CityDoctorModel(model.getParserConfig(), model.getFile());
newModel.setCityModel(new CityModel());
newModel.addBuilding(b);
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml"));
fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off"));
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
if (dir.exists() && dir.isDirectory()) {
fc.setInitialDirectory(dir);
} else {
Settings.set(Settings.LAST_OPEN_FOLDER, "");
}
File f = fc.showSaveDialog(mainWindow.getMainStage());
if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try {
newModel.saveAs(f.getAbsolutePath(), true);
} catch (CityDoctorWriteException e) {
logger.error(e);
}
}
}
public void showWorld() {
if (model != null) {
renderer.render(model);
}
}
public void storeModel(File f, boolean saveQualityAde) throws CityDoctorWriteException {
if (model == null) {
return;
}
model.saveAs(f.getAbsolutePath(), saveQualityAde);
}
public void searchFeature(String searchString, FeatureType selectedTab) {
if (model == null || searchString == null || searchString.trim().isEmpty()) {
return;
}
mainWindow.unselectEverything();
resetFeatureChunks();
if (selectedTab == FeatureType.BUILDING) {
List<Building> foundBuildings = filterFeatures(searchString, model.getBuildings());
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot();
root.getChildren().clear();
buildBuildingTreeFromList(foundBuildings, root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(foundBuildings, buildingsView, root, buildingChunkNr);
} else {
TreeView<Renderable> view;
List<? extends CityObject> cos;
AtomicInteger chunkCounter;
switch (selectedTab) {
case VEGETATION:
view = mainWindow.getVegetationView();
cos = filterFeatures(searchString, model.getVegetation());
chunkCounter = vegetationChunkNr;
break;
case BRIDGE:
view = mainWindow.getBridgeView();
cos = filterFeatures(searchString, model.getBridges());
chunkCounter = bridgeChunkNr;
break;
case TRANSPORTATION:
view = mainWindow.getTransportationView();
cos = filterFeatures(searchString, model.getTransportation());
chunkCounter = transportationChunkNr;
break;
case WATER:
view = mainWindow.getWaterView();
cos = filterFeatures(searchString, model.getWater());
chunkCounter = waterChunkNr;
break;
case LAND:
view = mainWindow.getTerrainView();
cos = filterFeatures(searchString, model.getLand());
chunkCounter = landChunkNr;
break;
default:
throw new IllegalStateException("Unknown selected feature tab");
}
TreeItem<Renderable> root = view.getRoot();
root.getChildren().clear();
buildTreeFromList(cos, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(cos, view, root, chunkCounter);
}
}
private <T extends CityObject> List<T> filterFeatures(String searchString, List<T> features) {
List<T> foundFeatures = new ArrayList<>();
for (T t : features) {
if (t.getGmlId().getGmlString().contains(searchString)) {
foundFeatures.add(t);
}
}
return foundFeatures;
}
public void resetSearch(FeatureType selectedTab) {
if (model == null) {
return;
}
resetFeatureChunks();
if (selectedTab == FeatureType.BUILDING) {
// buildings are handled differently, because of surface and building
// installations
TreeItem<Renderable> root = mainWindow.getBuildingsView().getRoot();
if (root != null) {
root.getChildren().clear();
buildBuildingTreeFromList(model.getBuildings(), root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), mainWindow.getBuildingsView(), root,
buildingChunkNr);
}
} else {
TreeView<Renderable> view;
List<? extends CityObject> cos;
AtomicInteger chunkCounter;
switch (selectedTab) {
case VEGETATION:
view = mainWindow.getVegetationView();
cos = model.getVegetation();
chunkCounter = vegetationChunkNr;
break;
case BRIDGE:
view = mainWindow.getBridgeView();
cos = model.getBridges();
chunkCounter = bridgeChunkNr;
break;
case TRANSPORTATION:
view = mainWindow.getTransportationView();
cos = model.getTransportation();
chunkCounter = transportationChunkNr;
break;
case WATER:
view = mainWindow.getWaterView();
cos = model.getWater();
chunkCounter = waterChunkNr;
break;
case LAND:
view = mainWindow.getTerrainView();
cos = model.getLand();
chunkCounter = landChunkNr;
break;
default:
throw new IllegalStateException("Unknown selected feature tab");
}
TreeItem<Renderable> root = view.getRoot();
root.getChildren().clear();
buildTreeFromList(cos, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(cos, view, root, chunkCounter);
}
}
public Series<String, Number> createErrorSeries() {
Series<String, Number> series = new Series<>();
if (model == null) {
return series;
}
Map<ErrorId, ErrorStat> errorStats = getErrorStats();
for (ErrorStat es : errorStats.values()) {
series.getData().add(new Data<>(es.getErrorId().toString(), es.getCount()));
}
return series;
}
public String getFileName() {
return model.getFileName();
}
public void fillTreeViewWithErrorBuildings() {
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot();
if (model == null) {
return;
}
if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
List<Building> errorBuildings = new ArrayList<>();
for (Building b : model.getBuildings()) {
if (b.containsAnyError()) {
errorBuildings.add(b);
}
}
resetFeatureChunks();
buildBuildingTreeFromList(errorBuildings, root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(errorBuildings, buildingsView, root, buildingChunkNr);
}
public void fillTreeViewWithErrorVegetation() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getVegetationView(), model.getVegetation(), vegetationChunkNr);
}
public void fillTreeViewWithErrorBridges() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getBridgeView(), model.getBridges(), bridgeChunkNr);
}
public void fillTreeViewWithErrorLand() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getTerrainView(), model.getLand(), landChunkNr);
}
public void fillTreeViewWithErrorTransportation() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getTransportationView(), model.getTransportation(),
transportationChunkNr);
}
public void fillTreeViewWithErrorWater() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getWaterView(), model.getWater(), waterChunkNr);
}
private void fillTreeViewWithErrorCityObjects(TreeView<Renderable> treeView, List<? extends CityObject> objects,
AtomicInteger chunkCounter) {
TreeItem<Renderable> root = treeView.getRoot();
if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
List<CityObject> errorObjects = new ArrayList<>();
for (CityObject co : objects) {
if (co.containsAnyError()) {
errorObjects.add(co);
}
}
buildTreeFromList(errorObjects, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(errorObjects, treeView, root, chunkCounter);
}
public void fillTreeViewFromFeatures(FeatureType selectedTab) {
if (model == null) {
return;
}
mainWindow.resetSearchBar();
switch (selectedTab) {
case BUILDING:
buildBuildings(model);
updateTree(mainWindow.getBuildingsView().getRoot());
break;
case VEGETATION:
buildVegetation(model.getVegetation());
updateTree(mainWindow.getVegetationView().getRoot());
break;
case BRIDGE:
buildBridges(model);
updateTree(mainWindow.getBridgeView().getRoot());
break;
case LAND:
buildLand(model);
updateTree(mainWindow.getTerrainView().getRoot());
break;
case TRANSPORTATION:
buildTransportation(model.getTransportation());
updateTree(mainWindow.getTransportationView().getRoot());
break;
case WATER:
buildWater(model);
updateTree(mainWindow.getWaterView().getRoot());
break;
default:
throw new IllegalStateException();
}
}
public CityDoctorModel getModel() {
return model;
}
public void showView(View v) {
v.fireOnShowEvent(model, currentChecker, mainWindow);
}
public void askAndSave() {
boolean saveWithQualityAde = true;
if (model.isValidated()) {
ButtonType yesBtn = ButtonType.YES;
ButtonType noBtn = ButtonType.NO;
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setTitle("Save with QualityADE?");
dialog.setContentText("Save with QualityADE information?");
dialog.getDialogPane().getButtonTypes().add(yesBtn);
dialog.getDialogPane().getButtonTypes().add(noBtn);
Optional<ButtonType> result = dialog.showAndWait();
if (result.isPresent() && result.get() == ButtonType.NO) {
saveWithQualityAde = false;
}
}
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml"));
fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off"));
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
if (dir.exists() && dir.isDirectory()) {
fc.setInitialDirectory(dir);
} else {
Settings.set(Settings.LAST_OPEN_FOLDER, "");
}
File f = fc.showSaveDialog(mainWindow.getMainStage());
if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try {
storeModel(f, saveWithQualityAde);
} catch (CityDoctorWriteException e) {
mainWindow.showExceptionDialog(e);
}
}
}
public void delete(TreeItem<Renderable> selectedItem) {
if (model == null) {
return;
}
Renderable render = selectedItem.getValue();
if (render instanceof BuildingNode node) {
model.getBuildings().remove(node.getBuilding());
mainWindow.getBuildingsView().getRoot().getChildren().remove(selectedItem);
}
}
public void errorFilterIndexChanged(Number newV) {
mainWindow.getMeshGroup().getChildren().clear();
mainWindow.unselectEverything();
if (newV.intValue() == 0) {
fillTreeViewFromFeatures(mainWindow.getSelectedTab());
} else if (newV.intValue() == 1) {
switch (mainWindow.getSelectedTab()) {
case BUILDING:
fillTreeViewWithErrorBuildings();
break;
case VEGETATION:
fillTreeViewWithErrorVegetation();
break;
case BRIDGE:
fillTreeViewWithErrorBridges();
break;
case LAND:
fillTreeViewWithErrorLand();
break;
case TRANSPORTATION:
fillTreeViewWithErrorTransportation();
break;
case WATER:
fillTreeViewWithErrorWater();
break;
default:
throw new IllegalStateException("Unknown selected feature tab: " + mainWindow.getSelectedTab());
}
} else {
throw new IllegalStateException("Unknown filter index selected: " + newV);
}
}
public ParserConfiguration getCurrentConfig() {
return currentConfig;
}
private static final int MAX_FEATURES_PER_CHUNK = 200;
private static final Logger logger = LogManager.getLogger(CityDoctorController.class);
private MainWindow mainWindow;
private CityDoctorModel model;
private ParserConfiguration currentConfig;
private String sourceFile;
private Checker currentChecker;
private HighlightController highlightController;
private Renderer renderer;
private BoundingBox originBB = null;
private AtomicInteger buildingChunkNr = new AtomicInteger(0);
private AtomicInteger vegetationChunkNr = new AtomicInteger(0);
private AtomicInteger transportationChunkNr = new AtomicInteger(0);
private AtomicInteger tunnelChunkNr = new AtomicInteger(0);
private AtomicInteger bridgeChunkNr = new AtomicInteger(0);
private AtomicInteger waterChunkNr = new AtomicInteger(0);
private AtomicInteger landChunkNr = new AtomicInteger(0);
private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0);
private AtomicInteger otherObjectsChunkNr = new AtomicInteger(0);
public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) {
this.mainWindow = mainWindow;
this.highlightController = highlightController;
this.renderer = renderer;
}
public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation)
throws CityGmlParseException, InvalidGmlFileException {
loadCityGml(path, numberOfRoundingPlaces, l, useValidation, false);
}
public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation,
boolean lowMemory) throws CityGmlParseException, InvalidGmlFileException {
try {
Platform.runLater(() -> {
mainWindow.getOpenBtn().setDisable(true);
mainWindow.getWriteReportButton().setDisable(true);
mainWindow.getMeshGroup().getChildren().clear();
clearTrees();
mainWindow.resetSearchBar();
mainWindow.resetFilterComboBox();
});
currentChecker = null;
currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory);
GMLValidationHandler handler = null;
List<String> validationIssues = new ArrayList<>();
if (useValidation) {
handler = new GMLValidationHandler() {
@Override
public void error(SAXParseException exception) {
if (exception.getLineNumber() >= 0) {
String s = "In line " + exception.getLineNumber() + ":";
validationIssues.add(s);
}
validationIssues.add(exception.getMessage());
}
@Override
public void warning(SAXParseException exception) {
error(exception);
}
@Override
public void fatalError(SAXParseException exception) {
error(exception);
}
};
}
model = CityGmlParser.parseCityGmlFile(path, currentConfig, l, handler);
if (!validationIssues.isEmpty()) {
StringJoiner sj = new StringJoiner("\n");
validationIssues.stream().forEach(sj::add);
throw new InvalidGmlFileException(sj.toString());
}
mainWindow.getClickHandler().setConfig(currentConfig);
sourceFile = path;
renderer.reset();
Platform.runLater(() -> {
mainWindow.addFileNameToTitle(path);
mainWindow.getCheckButton().setDisable(false);
mainWindow.getLod1Btn().setDisable(false);
mainWindow.getLod2Btn().setDisable(false);
mainWindow.getLod3Btn().setDisable(false);
mainWindow.getLod4Btn().setDisable(false);
mainWindow.getWorldBtn().setDisable(false);
mainWindow.getSaveBtn().setDisable(false);
mainWindow.getResetCameraBtn().setDisable(false);
mainWindow.getHideRoofBtn().setDisable(false);
mainWindow.getNorthArrow().setVisible(true);
mainWindow.alignNorthArrow();
setupFeatureTabs();
buildTrees();
});
} finally {
Platform.runLater(() -> mainWindow.getOpenBtn().setDisable(false));
}
}
private void setupFeatureTabs() {
mainWindow.setDisableOfFeatureTab(FeatureType.BUILDING, model.getBuildings().isEmpty());
mainWindow.setDisableOfFeatureTab(FeatureType.VEGETATION, model.getVegetation().isEmpty());
mainWindow.setDisableOfFeatureTab(FeatureType.TRANSPORTATION, model.getTransportation().isEmpty());
mainWindow.setDisableOfFeatureTab(FeatureType.TUNNEL, model.getTunnels().isEmpty());
mainWindow.setDisableOfFeatureTab(FeatureType.BRIDGE, model.getBridges().isEmpty());
mainWindow.setDisableOfFeatureTab(FeatureType.WATER, model.getWater().isEmpty());
mainWindow.setDisableOfFeatureTab(FeatureType.LAND, model.getLand().isEmpty());
mainWindow.setDisableOfFeatureTab(FeatureType.CITY_FURNITURE, model.getCityFurniture().isEmpty());
mainWindow.setDisableOfFeatureTab(FeatureType.GENERIC_CITY_OBJECT, model.getGenericCityObjects().isEmpty());
}
public void buildTrees() {
resetFeatureChunks();
buildBuildings(model);
buildVegetation(model.getVegetation());
buildTransportation(model.getTransportation());
buildBridges(model);
buildWater(model);
buildLand(model);
buildCityFurniture(model);
buildOtherCityObjects(model);
}
private void resetFeatureChunks() {
buildingChunkNr.set(0);
vegetationChunkNr.set(0);
transportationChunkNr.set(0);
tunnelChunkNr.set(0);
bridgeChunkNr.set(0);
waterChunkNr.set(0);
landChunkNr.set(0);
cityFurnitureChunkNr.set(0);
otherObjectsChunkNr.set(0);
}
private void buildLand(CityDoctorModel model) {
if (model.getLand().isEmpty()) {
return;
}
TreeView<Renderable> landView = mainWindow.getTerrainView();
TreeItem<Renderable> landRoot = new TreeItem<>(new AllTerrainNode(model.getLand()));
landRoot.setExpanded(true);
landView.setRoot(landRoot);
buildLandFromList(model.getLand(), landView.getRoot());
addMoreButtonIfNecessary(model.getLand(), landView, landRoot, landChunkNr);
}
private void buildLandFromList(List<CityObject> list, TreeItem<Renderable> root) {
int landChunk = landChunkNr.get();
for (int i = landChunk * MAX_FEATURES_PER_CHUNK; i < (landChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
CityObject land = list.get(i);
Renderable node;
if (land instanceof ReliefObject relief) {
node = new ReliefNode(relief);
} else if (land instanceof TinObject tin) {
node = new TinNode(tin);
} else {
node = new LandUseNode((LandObject) land);
}
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(land, item);
if (land instanceof ReliefObject relief) {
createTinNodes(relief, item);
}
}
}
private void createTinNodes(ReliefObject relief, TreeItem<Renderable> item) {
if (relief.getComponents().isEmpty()) {
return;
}
AllTinNode allTinText = new AllTinNode(relief.getComponents());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allTinText);
item.getChildren().add(allBpsTextItem);
for (TinObject tin : relief.getComponents()) {
TinNode tinNode = new TinNode(tin);
TreeItem<Renderable> tinNodeItem = new TreeItem<>(tinNode);
tinNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(tinNodeItem);
createGeometryNodes(tin, tinNodeItem);
}
}
private void buildCityFurniture(CityDoctorModel model) {
if (model.getCityFurniture().isEmpty()) {
return;
}
TreeView<Renderable> cityFurnitureView = mainWindow.getCityFurnitureView();
TreeItem<Renderable> cityFurnitureRoot = new TreeItem<>(new AllCityFurnitureNode(model.getCityFurniture()));
cityFurnitureRoot.setExpanded(true);
cityFurnitureView.setRoot(cityFurnitureRoot);
buildTreeFromList(model.getCityFurniture(), cityFurnitureView.getRoot(), cityFurnitureChunkNr);
addMoreButtonIfNecessary(model.getCityFurniture(), cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr);
}
private void buildOtherCityObjects(CityDoctorModel model) {
if (model.getGenericCityObjects().isEmpty()) {
return;
}
TreeView<Renderable> otherObjectsView = mainWindow.getOtherObjectsView();
TreeItem<Renderable> otherObjectsRoot = new TreeItem<>(new AllOtherObjectsNode(model.getGenericCityObjects()));
otherObjectsRoot.setExpanded(true);
otherObjectsView.setRoot(otherObjectsRoot);
buildTreeFromList(model.getGenericCityObjects(), otherObjectsView.getRoot(), otherObjectsChunkNr);
addMoreButtonIfNecessary(model.getGenericCityObjects(), otherObjectsView, otherObjectsRoot, otherObjectsChunkNr);
}
private void buildWater(CityDoctorModel model) {
if (model.getWater().isEmpty()) {
return;
}
TreeView<Renderable> waterView = mainWindow.getWaterView();
TreeItem<Renderable> waterRoot = new TreeItem<>(new AllWaterNode(model.getWater()));
waterRoot.setExpanded(true);
waterView.setRoot(waterRoot);
buildTreeFromList(model.getWater(), waterView.getRoot(), waterChunkNr);
addMoreButtonIfNecessary(model.getWater(), waterView, waterRoot, waterChunkNr);
}
private void buildBridges(CityDoctorModel model) {
if (model.getBridges().isEmpty()) {
return;
}
TreeView<Renderable> bridgeView = mainWindow.getBridgeView();
TreeItem<Renderable> bridgeRoot = new TreeItem<>(new AllBridgesNode(model.getBridges()));
bridgeRoot.setExpanded(true);
bridgeView.setRoot(bridgeRoot);
buildBridgeTreeFromList(model.getBridges(), bridgeView.getRoot());
addMoreButtonIfNecessary(model.getBridges(), bridgeView, bridgeRoot, bridgeChunkNr);
}
private void buildTransportation(List<TransportationObject> trans) {
if (trans.isEmpty()) {
return;
}
TreeView<Renderable> transView = mainWindow.getTransportationView();
TreeItem<Renderable> transRoot = new TreeItem<>(new AllTransportationNode(model.getTransportation()));
transRoot.setExpanded(true);
transView.setRoot(transRoot);
buildTreeFromList(trans, transView.getRoot(), transportationChunkNr);
addMoreButtonIfNecessary(trans, transView, transRoot, transportationChunkNr);
}
private void buildTunnel(List<Tunnel> tunnels) {
if (tunnels.isEmpty()) {
return;
}
TreeView<Renderable> transView = mainWindow.getTunnelView();
TreeItem<Renderable> transRoot = new TreeItem<>(new AllTunnelsNode(model.getTunnels()));
transRoot.setExpanded(true);
transView.setRoot(transRoot);
buildTreeFromList(tunnels, transView.getRoot(), tunnelChunkNr);
addMoreButtonIfNecessary(tunnels, transView, transRoot, tunnelChunkNr);
}
private void buildVegetation(List<Vegetation> veg) {
if (veg.isEmpty()) {
return;
}
TreeView<Renderable> vegetationsView = mainWindow.getVegetationView();
TreeItem<Renderable> vegRoot = new TreeItem<>(new AllVegetationNode(model.getVegetation()));
vegRoot.setExpanded(true);
vegetationsView.setRoot(vegRoot);
buildTreeFromListWithProducer(veg, vegetationsView.getRoot(), vegetationChunkNr, VegetationNode::new);
addMoreButtonIfNecessary(veg, vegetationsView, vegRoot, vegetationChunkNr);
}
private void addMoreButtonIfNecessary(List<? extends CityObject> list, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter) {
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
Runnable run = new Runnable() {
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.incrementAndGet();
int visibleFeatures = countVisibleNodes(view);
buildTreeFromList(list, root, chunkCounter);
updateTree(root);
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
};
// add button for expansion
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void buildBuildings(CityDoctorModel model) {
if (model.getBuildings().isEmpty()) {
return;
}
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = new TreeItem<>(new AllBuildingsNode(model.getBuildings()));
root.setExpanded(true);
buildBuildingTreeFromList(model.getBuildings(), root);
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), buildingsView, root, buildingChunkNr);
buildingsView.setRoot(root);
}
private void addMoreButtonToBuildingsIfNecessary(List<Building> list, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter) {
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
Runnable run = new Runnable() {
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);
buildBuildingTreeFromList(list, root);
updateTree(root);
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
};
// add button for expansion
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
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;
}
private void buildTreeFromList(List<? extends CityObject> cos, TreeItem<Renderable> root,
AtomicInteger chunkCounter) {
int chunk = chunkCounter.get();
for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) {
CityObject co = cos.get(i);
CityObjectNode node = new CityObjectNode(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
}
}
private <T extends CityObject> void buildTreeFromListWithProducer(List<T> cos, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Function<T, Renderable> nodeProducer) {
int chunk = chunkCounter.get();
for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) {
T co = cos.get(i);
Renderable node = nodeProducer.apply(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
}
}
private void clearTrees() {
mainWindow.getBuildingsView().setRoot(null);
mainWindow.getVegetationView().setRoot(null);
mainWindow.getTransportationView().setRoot(null);
mainWindow.getBridgeView().setRoot(null);
mainWindow.getWaterView().setRoot(null);
mainWindow.getTerrainView().setRoot(null);
mainWindow.getCityFurnitureView().setRoot(null);
mainWindow.getOtherObjectsView().setRoot(null);
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getAttributeView().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear();
clearGeometryTrees();
}
private void clearGeometryTrees() {
highlightController.clearHighlights();
mainWindow.getPolygonsView().getRoot().getChildren().clear();
mainWindow.getEdgeView().getRoot().getChildren().clear();
mainWindow.getVertexView().getRoot().getChildren().clear();
}
private void buildBuildingTreeFromList(List<Building> list, TreeItem<Renderable> root) {
int buildingChunk = buildingChunkNr.get();
for (int i = buildingChunk * MAX_FEATURES_PER_CHUNK; i < (buildingChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
Building b = list.get(i);
BuildingNode node = new BuildingNode(b);
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(b, item);
createBoundarySurfaceNodes(b.getBoundarySurfaces(), item);
createBuildingInstallationNodes(b, item);
createBuildingRoomNodes(b, item);
createBuildingFurnitureNodes(b, item);
createBuildingStoreyNodes(b, item);
createBuildingUnitNodes(b, item);
createBuildingPartNodes(b, item);
}
}
private void buildBridgeTreeFromList(List<BridgeObject> list, TreeItem<Renderable> root) {
int bridgeChunk = bridgeChunkNr.get();
for (int i = bridgeChunk * MAX_FEATURES_PER_CHUNK; i < (bridgeChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
BridgeObject bridge = list.get(i);
BridgeNode node = new BridgeNode(bridge);
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(bridge, item);
createBoundarySurfaceNodes(bridge.getBoundarySurfaces(), item);
createBridgeInstallationNodes(bridge, item);
createConstructiveElementsNodes(bridge, item);
createBridgePartNodes(bridge, item);
}
}
private void createBridgeInstallationNodes(BridgeObject bridge, TreeItem<Renderable> root) {
createInstallationNodes(bridge.getBridgeInstallations(), root);
}
private void createConstructiveElementsNodes(BridgeObject bridge, TreeItem<Renderable> item) {
if (bridge.getConstructiveElements().isEmpty()) {
return;
}
AllBridgeConstructiveElementsNode allBceNode = new AllBridgeConstructiveElementsNode(
bridge.getConstructiveElements());
TreeItem<Renderable> allBceNodeTextItem = new TreeItem<>(allBceNode);
item.getChildren().add(allBceNodeTextItem);
for (BridgeConstructiveElement bce : bridge.getConstructiveElements()) {
BridgeConstructiveElementNode bceNode = new BridgeConstructiveElementNode(bce);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bceNode);
bpNodeItem.setExpanded(true);
allBceNodeTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bce, bpNodeItem);
createBoundarySurfaceNodes(bce.getBoundarySurfaces(), bpNodeItem);
}
}
private void createBridgePartNodes(BridgeObject bridge, TreeItem<Renderable> item) {
if (bridge.getParts().isEmpty()) {
return;
}
AllBridgePartsNode allBpsText = new AllBridgePartsNode(bridge.getParts());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText);
item.getChildren().add(allBpsTextItem);
for (BridgeObject bp : bridge.getParts()) {
BridgeNode bpNode = new BridgeNode(bp);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem);
}
}
private void createBuildingInstallationNodes(Building ab, TreeItem<Renderable> root) {
createInstallationNodes(ab.getBuildingInstallations(), root);
}
private void createInstallationNodes(List<Installation> installations, TreeItem<Renderable> root) {
if (installations.isEmpty()) {
return;
}
AllInstallationsNode allBiNode = new AllInstallationsNode(installations);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBiNode);
root.getChildren().add(allBpsTextItem);
for (Installation bi : installations) {
InstallationNode biNode = new InstallationNode(bi);
TreeItem<Renderable> biItem = new TreeItem<>(biNode);
biItem.setExpanded(true);
allBpsTextItem.getChildren().add(biItem);
createGeometryNodes(bi, biItem);
createBoundarySurfaceNodes(bi.getBoundarySurfaces(), biItem);
}
}
private void createBuildingRoomNodes(Building ab, TreeItem<Renderable> root) {
createRoomNodes(ab.getBuildingRooms(), root);
}
private void createRoomNodes(List<? extends AbstractRoom> rooms, TreeItem<Renderable> root) {
if (rooms.isEmpty()) {
return;
}
AllRoomsNode allRoNode = new AllRoomsNode(rooms);
TreeItem<Renderable> allRoNodeTextItem = new TreeItem<>(allRoNode);
root.getChildren().add(allRoNodeTextItem);
for (AbstractRoom room : rooms) {
RoomNode roomNode = new RoomNode(room);
TreeItem<Renderable> roomNodeTextItem = new TreeItem<>(roomNode);
roomNodeTextItem.setExpanded(true);
allRoNodeTextItem.getChildren().add(roomNodeTextItem);
createGeometryNodes(room, roomNodeTextItem);
createBoundarySurfaceNodes(room.getBoundarySurfaces(), roomNodeTextItem);
}
}
private void createBuildingStoreyNodes(Building ab, TreeItem<Renderable> root) {
List<Storey> storeys = ab.getBuildingStoreys();
if (storeys.isEmpty()) {
return;
}
AllStoreysNode allSNode = new AllStoreysNode(storeys);
TreeItem<Renderable> allSNodeTextItem = new TreeItem<>(allSNode);
root.getChildren().add(allSNodeTextItem);
for (Storey storey : storeys) {
StoreyNode storeyNode = new StoreyNode(storey);
TreeItem<Renderable> storeyNodeTextItem = new TreeItem<>(storeyNode);
storeyNodeTextItem.setExpanded(true);
allSNodeTextItem.getChildren().add(storeyNodeTextItem);
createGeometryNodes(storey, storeyNodeTextItem);
createBoundarySurfaceNodes(storey.getBoundarySurfaces(), storeyNodeTextItem);
}
}
public void createBuildingUnitNodes(Building ab, TreeItem<Renderable> root) {
List<BuildingUnit> buildingUnits = ab.getBuildingUnits();
if (buildingUnits.isEmpty()) {
return;
}
AllBuildingUnitsNode allSNode = new AllBuildingUnitsNode(buildingUnits);
TreeItem<Renderable> allSNodeTextItem = new TreeItem<>(allSNode);
root.getChildren().add(allSNodeTextItem);
for (BuildingUnit buildingUnit : buildingUnits) {
BuildingUnitNode buildingUnitNode = new BuildingUnitNode(buildingUnit);
TreeItem<Renderable> storeyNodeTextItem = new TreeItem<>(buildingUnitNode);
storeyNodeTextItem.setExpanded(true);
allSNodeTextItem.getChildren().add(storeyNodeTextItem);
createGeometryNodes(buildingUnit, storeyNodeTextItem);
createBoundarySurfaceNodes(buildingUnit.getBoundarySurfaces(), storeyNodeTextItem);
}
}
private void createBuildingFurnitureNodes(Building ab, TreeItem<Renderable> root) {
createFurnitureNodes(ab.getBuildingRoomFurnitureList(), root);
}
private void createFurnitureNodes(List<? extends AbstractFurniture> furniture, TreeItem<Renderable> root) {
if (furniture.isEmpty()) {
return;
}
AllFurnitureNode allFnNode = new AllFurnitureNode(furniture);
TreeItem<Renderable> allFnNodeTextItem = new TreeItem<>(allFnNode);
root.getChildren().add(allFnNodeTextItem);
for (AbstractFurniture fn : furniture) {
FurnitureNode fnNode = new FurnitureNode(fn);
TreeItem<Renderable> fnNodeTextItem = new TreeItem<>(fnNode);
fnNodeTextItem.setExpanded(true);
allFnNodeTextItem.getChildren().add(fnNodeTextItem);
createGeometryNodes(fn, fnNodeTextItem);
createBoundarySurfaceNodes(fn.getBoundarySurfaceList(), fnNodeTextItem);
}
}
private void createBoundarySurfaceNodes(List<BoundarySurface> bsList, TreeItem<Renderable> root) {
if (bsList.isEmpty()) {
return;
}
AllBoundarySurfacesNode allBsText = new AllBoundarySurfacesNode(bsList);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBsText);
root.getChildren().add(allBpsTextItem);
allBpsTextItem.getChildren().add(new TreeItem<>());
allBpsTextItem.expandedProperty().addListener((obs, old, newV) -> {
if (Boolean.TRUE.equals(newV) && allBpsTextItem.getChildren().size() == 1) {
Platform.runLater(() -> {
allBpsTextItem.getChildren().clear();
for (BoundarySurface bs : bsList) {
BoundarySurfaceNode bsText = new BoundarySurfaceNode(bs);
TreeItem<Renderable> bsTextItem = new TreeItem<>(bsText);
bsTextItem.setExpanded(true);
allBpsTextItem.getChildren().add(bsTextItem);
createGeometryNodes(bs, bsTextItem);
createOpeningNodes(bs.getOpenings(), bsTextItem);
}
updateTree(allBpsTextItem);
});
}
});
}
private void createOpeningNodes(List<Opening> openings, TreeItem<Renderable> root) {
if (openings == null || openings.isEmpty()) {
return;
}
AllOpeningsNode allOpeningsNode = new AllOpeningsNode(openings);
TreeItem<Renderable> allOpeningsItem = new TreeItem<>(allOpeningsNode);
for (Opening o : openings) {
OpeningNode openingNode = new OpeningNode(o);
TreeItem<Renderable> openingsItem = new TreeItem<>(openingNode);
allOpeningsItem.getChildren().add(openingsItem);
}
root.getChildren().add(allOpeningsItem);
}
private void createBuildingPartNodes(Building ab, TreeItem<Renderable> item) {
if (ab.getBuildingParts().isEmpty()) {
return;
}
AllBuildingPartsNode allBpsText = new AllBuildingPartsNode(ab.getBuildingParts());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText);
item.getChildren().add(allBpsTextItem);
for (BuildingPart bp : ab.getBuildingParts()) {
BuildingPartNode bpNode = new BuildingPartNode(bp);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem);
}
}
private void createGeometryNodes(CityObject co, TreeItem<Renderable> item) {
for (Geometry geom : co.getGeometries()) {
GeometryNode geomNode = new GeometryNode(geom);
TreeItem<Renderable> geomItem = new TreeItem<>(geomNode);
item.getChildren().add(geomItem);
}
}
public void startChecks(ValidationConfiguration config, ProgressListener l) {
if (model == null) {
logger.warn(Localization.getText("CityDoctorController.noDatamodel"));
return;
}
if (sourceFile == null) {
logger.warn(Localization.getText("CityDoctorController.noSourceFile"));
return;
}
try {
Platform.runLater(() -> {
mainWindow.getCheckButton().setDisable(true);
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear();
});
config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces());
config.setParserConfig(currentConfig);
currentChecker = new Checker(config, model);
currentChecker.runChecks(l);
Platform.runLater(() -> {
// apply check results to tree views
updateFeatureTrees();
updateTree(mainWindow.getPolygonsView().getRoot());
for (CheckError e : model.getGlobalErrors()) {
if (e instanceof SchematronError se) {
mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString());
}
}
renderer.refresh();
mainWindow.getWriteReportButton().setDisable(false);
});
} finally {
Platform.runLater(() -> mainWindow.getCheckButton().setDisable(false));
}
}
void updateFeatureTrees() {
updateTree(mainWindow.getBuildingsView().getRoot());
updateTree(mainWindow.getVegetationView().getRoot());
updateTree(mainWindow.getBridgeView().getRoot());
updateTree(mainWindow.getTerrainView().getRoot());
updateTree(mainWindow.getCityFurnitureView().getRoot());
updateTree(mainWindow.getTransportationView().getRoot());
updateTree(mainWindow.getWaterView().getRoot());
updateTree(mainWindow.getOtherObjectsView().getRoot());
renderer.updateErrors();
}
private void updateTree(TreeItem<Renderable> root) {
if (root == null) {
return;
}
for (TreeItem<Renderable> item : root.getChildren()) {
updateItem(item);
}
}
private void updateItem(TreeItem<Renderable> item) {
if (item.getValue() == null) {
return;
}
item.getValue().refreshTextColor();
for (TreeItem<Renderable> child : item.getChildren()) {
updateItem(child);
}
}
public void writePdfReport(File pdfFile) {
if (currentChecker == null) {
return;
}
currentChecker.writePdfReport(pdfFile.getAbsolutePath());
}
public void writeXmlReport(File xmlFile) {
if (currentChecker == null) {
return;
}
currentChecker.writeXmlReport(xmlFile.getAbsolutePath());
}
private Map<ErrorId, ErrorStat> getErrorStats() {
Map<ErrorId, ErrorStat> stats = new HashMap<>();
addErrorStats(stats, model.getBridges());
addErrorStats(stats, model.getBuildings());
addErrorStats(stats, model.getLand());
addErrorStats(stats, model.getTransportation());
addErrorStats(stats, model.getVegetation());
addErrorStats(stats, model.getWater());
addErrorStats(stats, model.getCityFurniture());
addErrorStats(stats, model.getGenericCityObjects());
return stats;
}
private void addErrorStats(Map<ErrorId, ErrorStat> stats, List<? extends CityObject> cos) {
List<CheckError> errors = new ArrayList<>();
for (CityObject co : cos) {
co.collectContainedErrors(errors);
}
Set<CheckError> filteredErrors = new HashSet<>(errors);
for (CheckError error : filteredErrors) {
ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new);
stat.incrementCount();
}
}
public void export(Building b) {
if (model == null) {
return;
}
CityDoctorModel newModel = new CityDoctorModel(model.getParserConfig(), model.getFile());
newModel.setCityModel(new CityModel());
newModel.addBuilding(b);
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml"));
fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off"));
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
if (dir.exists() && dir.isDirectory()) {
fc.setInitialDirectory(dir);
} else {
Settings.set(Settings.LAST_OPEN_FOLDER, "");
}
File f = fc.showSaveDialog(mainWindow.getMainStage());
if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try {
newModel.saveAs(f.getAbsolutePath(), true);
} catch (CityDoctorWriteException e) {
logger.error(e);
}
}
}
public void showWorld() {
if (model != null) {
renderer.render(model);
}
}
public void storeModel(File f, boolean saveQualityAde) throws CityDoctorWriteException {
if (model == null) {
return;
}
model.saveAs(f.getAbsolutePath(), saveQualityAde);
}
public void searchFeature(String searchString, FeatureType selectedTab) {
if (model == null || searchString == null || searchString.trim().isEmpty()) {
return;
}
mainWindow.unselectEverything();
resetFeatureChunks();
if (selectedTab == FeatureType.BUILDING) {
List<Building> foundBuildings = filterFeatures(searchString, model.getBuildings());
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot();
root.getChildren().clear();
buildBuildingTreeFromList(foundBuildings, root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(foundBuildings, buildingsView, root, buildingChunkNr);
} else {
TreeView<Renderable> view;
List<? extends CityObject> cos;
AtomicInteger chunkCounter;
switch (selectedTab) {
case VEGETATION:
view = mainWindow.getVegetationView();
cos = filterFeatures(searchString, model.getVegetation());
chunkCounter = vegetationChunkNr;
break;
case BRIDGE:
view = mainWindow.getBridgeView();
cos = filterFeatures(searchString, model.getBridges());
chunkCounter = bridgeChunkNr;
break;
case TRANSPORTATION:
view = mainWindow.getTransportationView();
cos = filterFeatures(searchString, model.getTransportation());
chunkCounter = transportationChunkNr;
break;
case TUNNEL:
view = mainWindow.getTunnelView();
cos = model.getTunnels();
chunkCounter = tunnelChunkNr;
break;
case WATER:
view = mainWindow.getWaterView();
cos = filterFeatures(searchString, model.getWater());
chunkCounter = waterChunkNr;
break;
case LAND:
view = mainWindow.getTerrainView();
cos = filterFeatures(searchString, model.getLand());
chunkCounter = landChunkNr;
break;
case CITY_FURNITURE:
view = mainWindow.getCityFurnitureView();
cos = filterFeatures(searchString, model.getCityFurniture());
chunkCounter = cityFurnitureChunkNr;
break;
case GENERIC_CITY_OBJECT:
view = mainWindow.getOtherObjectsView();
cos = filterFeatures(searchString, model.getGenericCityObjects());
chunkCounter = otherObjectsChunkNr;
break;
default:
throw new IllegalStateException("Unknown selected feature tab");
}
TreeItem<Renderable> root = view.getRoot();
root.getChildren().clear();
buildTreeFromList(cos, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(cos, view, root, chunkCounter);
}
}
private <T extends CityObject> List<T> filterFeatures(String searchString, List<T> features) {
List<T> foundFeatures = new ArrayList<>();
for (T t : features) {
if (t.getGmlId().getGmlString().contains(searchString)) {
foundFeatures.add(t);
}
}
return foundFeatures;
}
public void resetSearch(FeatureType selectedTab) {
if (model == null) {
return;
}
resetFeatureChunks();
if (selectedTab == FeatureType.BUILDING) {
// buildings are handled differently, because of surface and building
// installations
TreeItem<Renderable> root = mainWindow.getBuildingsView().getRoot();
if (root != null) {
root.getChildren().clear();
buildBuildingTreeFromList(model.getBuildings(), root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), mainWindow.getBuildingsView(), root,
buildingChunkNr);
}
} else {
TreeView<Renderable> view;
List<? extends CityObject> cos;
AtomicInteger chunkCounter;
switch (selectedTab) {
case VEGETATION:
view = mainWindow.getVegetationView();
cos = model.getVegetation();
chunkCounter = vegetationChunkNr;
break;
case BRIDGE:
view = mainWindow.getBridgeView();
cos = model.getBridges();
chunkCounter = bridgeChunkNr;
break;
case TRANSPORTATION:
view = mainWindow.getTransportationView();
cos = model.getTransportation();
chunkCounter = transportationChunkNr;
break;
case TUNNEL:
view = mainWindow.getTunnelView();
cos = model.getTunnels();
chunkCounter = tunnelChunkNr;
break;
case WATER:
view = mainWindow.getWaterView();
cos = model.getWater();
chunkCounter = waterChunkNr;
break;
case LAND:
view = mainWindow.getTerrainView();
cos = model.getLand();
chunkCounter = landChunkNr;
break;
case CITY_FURNITURE:
view = mainWindow.getCityFurnitureView();
cos = model.getCityFurniture();
chunkCounter = cityFurnitureChunkNr;
break;
case GENERIC_CITY_OBJECT:
view = mainWindow.getOtherObjectsView();
cos = model.getGenericCityObjects();
chunkCounter = otherObjectsChunkNr;
break;
default:
throw new IllegalStateException("Unknown selected feature tab");
}
TreeItem<Renderable> root = view.getRoot();
root.getChildren().clear();
buildTreeFromList(cos, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(cos, view, root, chunkCounter);
}
}
public Series<String, Number> createErrorSeries() {
Series<String, Number> series = new Series<>();
if (model == null) {
return series;
}
Map<ErrorId, ErrorStat> errorStats = getErrorStats();
for (ErrorStat es : errorStats.values()) {
series.getData().add(new Data<>(es.getErrorId().toString(), es.getCount()));
}
return series;
}
public String getFileName() {
return model.getFileName();
}
public void fillTreeViewWithErrorBuildings() {
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot();
if (model == null) {
return;
}
if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
List<Building> errorBuildings = new ArrayList<>();
for (Building b : model.getBuildings()) {
if (b.containsAnyError()) {
errorBuildings.add(b);
}
}
resetFeatureChunks();
buildBuildingTreeFromList(errorBuildings, root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(errorBuildings, buildingsView, root, buildingChunkNr);
}
public void fillTreeViewWithErrorVegetation() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getVegetationView(), model.getVegetation(), vegetationChunkNr);
}
public void fillTreeViewWithErrorBridges() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getBridgeView(), model.getBridges(), bridgeChunkNr);
}
public void fillTreeViewWithErrorLand() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getTerrainView(), model.getLand(), landChunkNr);
}
public void fillTreeViewWithErrorCityFurniture() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getCityFurnitureView(),
model.getCityFurniture(), cityFurnitureChunkNr);
}
public void fillTreeViewWithErrorOtherObjects() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getOtherObjectsView(),
model.getGenericCityObjects(), otherObjectsChunkNr);
}
public void fillTreeViewWithErrorTransportation() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getTransportationView(), model.getTransportation(),
transportationChunkNr);
}
public void fillTreeViewWithErrorTunnel() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getTunnelView(), model.getTunnels(), tunnelChunkNr);
}
public void fillTreeViewWithErrorWater() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getWaterView(), model.getWater(), waterChunkNr);
}
private void fillTreeViewWithErrorCityObjects(TreeView<Renderable> treeView, List<? extends CityObject> objects,
AtomicInteger chunkCounter) {
TreeItem<Renderable> root = treeView.getRoot();
if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
List<CityObject> errorObjects = new ArrayList<>();
for (CityObject co : objects) {
if (co.containsAnyError()) {
errorObjects.add(co);
}
}
buildTreeFromList(errorObjects, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(errorObjects, treeView, root, chunkCounter);
}
public void fillTreeViewFromFeatures(FeatureType selectedTab) {
if (model == null) {
return;
}
mainWindow.resetSearchBar();
switch (selectedTab) {
case BUILDING:
buildBuildings(model);
updateTree(mainWindow.getBuildingsView().getRoot());
break;
case VEGETATION:
buildVegetation(model.getVegetation());
updateTree(mainWindow.getVegetationView().getRoot());
break;
case BRIDGE:
buildBridges(model);
updateTree(mainWindow.getBridgeView().getRoot());
break;
case LAND:
buildLand(model);
updateTree(mainWindow.getTerrainView().getRoot());
break;
case TRANSPORTATION:
buildTransportation(model.getTransportation());
updateTree(mainWindow.getTransportationView().getRoot());
break;
case WATER:
buildWater(model);
updateTree(mainWindow.getWaterView().getRoot());
break;
case CITY_FURNITURE:
buildCityFurniture(model);
updateTree(mainWindow.getCityFurnitureView().getRoot());
break;
case GENERIC_CITY_OBJECT:
buildOtherCityObjects(model);
updateTree(mainWindow.getOtherObjectsView().getRoot());
break;
default:
throw new IllegalStateException();
}
}
public CityDoctorModel getModel() {
return model;
}
public void showView(View v) {
v.fireOnShowEvent(model, currentChecker, mainWindow);
}
public void askAndSave() {
boolean saveWithQualityAde = true;
if (model.isValidated()) {
ButtonType yesBtn = ButtonType.YES;
ButtonType noBtn = ButtonType.NO;
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setTitle("Save with QualityADE?");
dialog.setContentText("Save with QualityADE information?");
dialog.getDialogPane().getButtonTypes().add(yesBtn);
dialog.getDialogPane().getButtonTypes().add(noBtn);
Optional<ButtonType> result = dialog.showAndWait();
if (result.isPresent() && result.get() == ButtonType.NO) {
saveWithQualityAde = false;
}
}
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml"));
fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off"));
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
if (dir.exists() && dir.isDirectory()) {
fc.setInitialDirectory(dir);
} else {
Settings.set(Settings.LAST_OPEN_FOLDER, "");
}
File f = fc.showSaveDialog(mainWindow.getMainStage());
if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try {
storeModel(f, saveWithQualityAde);
logger.info("Saving successful");
} catch (CityDoctorWriteException e) {
mainWindow.showExceptionDialog(e);
}
}
}
public void delete(TreeItem<Renderable> selectedItem) {
if (model == null) {
return;
}
Renderable render = selectedItem.getValue();
if (render instanceof BuildingNode node) {
model.getBuildings().remove(node.getBuilding());
mainWindow.getBuildingsView().getRoot().getChildren().remove(selectedItem);
}
}
public void errorFilterIndexChanged(Number newV) {
mainWindow.getMeshGroup().getChildren().clear();
mainWindow.unselectEverything();
if (newV.intValue() == 0) {
fillTreeViewFromFeatures(mainWindow.getSelectedTab());
} else if (newV.intValue() == 1) {
switch (mainWindow.getSelectedTab()) {
case BUILDING:
fillTreeViewWithErrorBuildings();
break;
case VEGETATION:
fillTreeViewWithErrorVegetation();
break;
case BRIDGE:
fillTreeViewWithErrorBridges();
break;
case LAND:
fillTreeViewWithErrorLand();
break;
case TRANSPORTATION:
fillTreeViewWithErrorTransportation();
break;
case WATER:
fillTreeViewWithErrorWater();
break;
case CITY_FURNITURE:
fillTreeViewWithErrorCityFurniture();
break;
case GENERIC_CITY_OBJECT:
fillTreeViewWithErrorOtherObjects();
break;
default:
throw new IllegalStateException("Unknown selected feature tab: " + mainWindow.getSelectedTab());
}
} else {
throw new IllegalStateException("Unknown filter index selected: " + newV);
}
}
public ParserConfiguration getCurrentConfig() {
return currentConfig;
}
public void setOriginBB(BoundingBox origin) {
this.originBB = origin;
}
public BoundingBox getOriginBB() {
return originBB;
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.io.IOException;
import java.io.InputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.Tooltip;
import javafx.scene.control.TreeView;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
public class MainToolBar {
private static final Logger logger = LogManager.getLogger(MainToolBar.class);
private static final Logger logger = LogManager.getLogger(MainToolBar.class);
@FXML
private Button saveBtn;
@FXML
private ImageView saveView;
@FXML
private Button openBtn;
@FXML
private ImageView openImageView;
@FXML
private Button saveBtn;
@FXML
private Button checkBtn;
@FXML
private ImageView saveView;
@FXML
private ImageView checkImageView;
@FXML
private Button openBtn;
@FXML
private Button showWorldBtn;
@FXML
private ImageView openImageView;
@FXML
private ImageView showWorldImageView;
@FXML
private Button checkBtn;
@FXML
private ToggleButton hideRoofBtn;
@FXML
private ImageView checkImageView;
@FXML
private ImageView hideRoofView;
@FXML
private Button showWorldBtn;
@FXML
private Button resetCameraBtn;
@FXML
private ImageView showWorldImageView;
@FXML
private ImageView resetCameraView;
@FXML
private Button schematronBtn;
@FXML
private Button schematronBtn;
@FXML
private ImageView schematronImgView;
@FXML
private ImageView schematronImgView;
@FXML
private ToggleButton lod1Btn;
@FXML
private ToggleButton lod1Btn;
@FXML
private ImageView lod1View;
@FXML
private ImageView lod1View;
@FXML
private ToggleButton lod2Btn;
@FXML
private ToggleButton lod2Btn;
@FXML
private ImageView lod2View;
@FXML
private ImageView lod2View;
@FXML
private ToggleButton lod3Btn;
@FXML
private ToggleButton lod3Btn;
@FXML
private ImageView lod3View;
@FXML
private ImageView lod3View;
@FXML
private ToggleButton lod4Btn;
@FXML
private ToggleButton lod4Btn;
@FXML
private ImageView lod4View;
@FXML
private ImageView lod4View;
@FXML
private Button aboutBtn;
@FXML
private ImageView aboutImgView;
@FXML
private ToggleButton gridButton;
@FXML
private ToggleButton cullingButton;
@FXML
private ImageView cullingImageView;
@FXML
private ImageView gridImageView;
@FXML
private Button reportBtn;
@FXML
private ImageView reportImageView;
private OpenFileDialog fileDialog;
private CheckDialog checkDialog;
private WriteReportDialog writeDialog;
private AboutDialog aboutDialog;
private CityDoctorController controller;
private TabPane featurePane;
private Stage stage;
private Renderer renderer;
private MainWindow mainWindow;
private HBox toolBar;
public MainToolBar(Stage stage, CityDoctorController controller, TabPane featurePane, Renderer renderer,
MainWindow mainWindow) throws IOException {
this.controller = controller;
this.featurePane = featurePane;
this.renderer = renderer;
this.stage = stage;
this.mainWindow = mainWindow;
FXMLLoader loader = new FXMLLoader(MainToolBar.class.getResource("MainToolBar.fxml"));
loader.setController(this);
toolBar = loader.load();
fileDialog = new OpenFileDialog(stage, controller);
}
public void initialize() {
openBtn.setOnAction(ae -> fileDialog.show());
setupSaveBtn();
setupCheckButton();
setupLodButtons();
setupAboutButton();
setupReportButton();
loadImages();
gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected()));
gridButton.setTooltip(new Tooltip(Localization.getText("MainToolBar.wireframe")));
cullingButton.setOnAction(ae -> renderer.enableCulling(cullingButton.isSelected()));
cullingButton.setTooltip(new Tooltip(Localization.getText("MainToolBar.culling")));
showWorldBtn.setOnAction(ar -> {
Tab selectedItem = featurePane.getSelectionModel().getSelectedItem();
if (selectedItem.getContent() instanceof TreeView) {
@SuppressWarnings("unchecked")
TreeView<Renderable> content = (TreeView<Renderable>) selectedItem.getContent();
content.getSelectionModel().clearSelection();
}
controller.showWorld();
});
}
private void loadImages() {
try {
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/openFolderIcon.png")) {
Image img = new Image(inStream);
openImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/check32x32.png")) {
Image img = new Image(inStream);
checkImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/wireframe32x32.png")) {
Image img = new Image(inStream);
gridImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/Culling.png")) {
Image img = new Image(inStream);
cullingImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/error_stat32x32.png")) {
Image img = new Image(inStream);
reportImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/scene.png")) {
Image img = new Image(inStream);
showWorldImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod1_32x32.png")) {
Image img = new Image(inStream);
lod1View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod2_32x32.png")) {
Image img = new Image(inStream);
lod2View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod3_32x32.png")) {
Image img = new Image(inStream);
lod3View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod4_32x32.png")) {
Image img = new Image(inStream);
lod4View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/about.png")) {
Image img = new Image(inStream);
aboutImgView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/save.png")) {
Image img = new Image(inStream);
saveView.setImage(img);
}
} catch (IOException e) {
// ignore close exception
}
}
private void setupReportButton() {
reportBtn.setDisable(true);
reportBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.writeReports")));
reportBtn.setOnAction(ae -> {
if (writeDialog == null) {
try {
writeDialog = new WriteReportDialog(stage, controller, mainWindow);
} catch (IOException e) {
logger.catching(e);
mainWindow.showExceptionDialog(e);
}
}
if (writeDialog != null) {
// writeDialog can be null if creation of said dialog fails
writeDialog.show();
}
});
}
private void setupAboutButton() {
aboutBtn.setOnAction(ae -> {
if (aboutDialog == null) {
try {
aboutDialog = new AboutDialog(stage);
aboutDialog.show();
} catch (IOException e) {
logger.error("Could not load about dialog.", e);
}
} else {
aboutDialog.show();
}
});
}
private void setupLodButtons() {
lod1Btn.setOnAction(ae -> {
if (lod1Btn.isSelected()) {
renderer.enableLod1();
} else {
renderer.disableLod1();
}
});
lod2Btn.setOnAction(ae -> {
if (lod2Btn.isSelected()) {
renderer.enableLod2();
} else {
renderer.disableLod2();
}
});
lod3Btn.setOnAction(ae -> {
if (lod3Btn.isSelected()) {
renderer.enableLod3();
} else {
renderer.disableLod3();
}
});
lod4Btn.setOnAction(ae -> {
if (lod4Btn.isSelected()) {
renderer.enableLod4();
} else {
renderer.disableLod4();
}
});
}
private void setupSaveBtn() {
saveBtn.setOnAction(ae -> controller.askAndSave());
}
private void setupCheckButton() {
checkBtn.setDisable(true);
checkBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.executeChecks")));
checkBtn.setOnAction(ae -> {
if (checkDialog == null) {
try {
checkDialog = new CheckDialog(mainWindow, stage, controller);
} catch (IOException e) {
mainWindow.showExceptionDialog(e);
logger.catching(e);
}
}
checkDialog.show();
});
}
public Button getCheckButton() {
return checkBtn;
}
public ToggleButton getGridButton() {
return gridButton;
}
public ToggleButton getCullingButton() {
return cullingButton;
}
public Button getWriteReportButton() {
return reportBtn;
}
public ToggleButton getLod1Btn() {
return lod1Btn;
}
public ToggleButton getLod2Btn() {
return lod2Btn;
}
public ToggleButton getLod3Btn() {
return lod3Btn;
}
public ToggleButton getLod4Btn() {
return lod4Btn;
}
public Button getWorldBtn() {
return showWorldBtn;
}
public Button getSaveBtn() {
return saveBtn;
}
public HBox getToolBar() {
return toolBar;
}
public Button getOpenBtn() {
return openBtn;
}
@FXML
private Button aboutBtn;
@FXML
private ImageView aboutImgView;
@FXML
private ToggleButton gridButton;
@FXML
private ToggleButton cullingButton;
@FXML
private ImageView cullingImageView;
@FXML
private ImageView gridImageView;
@FXML
private Button reportBtn;
@FXML
private ImageView reportImageView;
private OpenFileDialog fileDialog;
private CheckDialog checkDialog;
private WriteReportDialog writeDialog;
private AboutDialog aboutDialog;
private CityDoctorController controller;
private TabPane featurePane;
private Stage stage;
private Renderer renderer;
private MainWindow mainWindow;
private HBox toolBar;
public MainToolBar(Stage stage, CityDoctorController controller, TabPane featurePane, Renderer renderer,
MainWindow mainWindow) throws IOException {
this.controller = controller;
this.featurePane = featurePane;
this.renderer = renderer;
this.stage = stage;
this.mainWindow = mainWindow;
FXMLLoader loader = new FXMLLoader(MainToolBar.class.getResource("MainToolBar.fxml"));
loader.setController(this);
toolBar = loader.load();
fileDialog = new OpenFileDialog(stage, controller);
}
public void initialize() {
openBtn.setOnAction(ae -> fileDialog.show());
setupSaveBtn();
setupCheckButton();
setupLodButtons();
setupAboutButton();
setupReportButton();
setupResetCameraButton();
setupHideRoofButton();
loadImages();
gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected()));
gridButton.setTooltip(new Tooltip(Localization.getText("MainToolBar.wireframe")));
cullingButton.setOnAction(ae -> renderer.enableCulling(cullingButton.isSelected()));
cullingButton.setTooltip(new Tooltip(Localization.getText("MainToolBar.culling")));
showWorldBtn.setOnAction(ar -> {
Tab selectedItem = featurePane.getSelectionModel().getSelectedItem();
if (selectedItem.getContent() instanceof TreeView) {
@SuppressWarnings("unchecked")
TreeView<Renderable> content = (TreeView<Renderable>) selectedItem.getContent();
content.getSelectionModel().clearSelection();
}
controller.showWorld();
});
showWorldBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.showWorld")));
}
private void loadImages() {
try {
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/openFolderIcon.png")) {
Image img = new Image(inStream);
openImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/check32x32.png")) {
Image img = new Image(inStream);
checkImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/wireframe32x32.png")) {
Image img = new Image(inStream);
gridImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/Culling.png")) {
Image img = new Image(inStream);
cullingImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/error_stat32x32.png")) {
Image img = new Image(inStream);
reportImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/scene.png")) {
Image img = new Image(inStream);
showWorldImageView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod1_32x32.png")) {
Image img = new Image(inStream);
lod1View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod2_32x32.png")) {
Image img = new Image(inStream);
lod2View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod3_32x32.png")) {
Image img = new Image(inStream);
lod3View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/lod4_32x32.png")) {
Image img = new Image(inStream);
lod4View.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/about.png")) {
Image img = new Image(inStream);
aboutImgView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/save.png")) {
Image img = new Image(inStream);
saveView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/reset_camera.png")) {
Image img = new Image(inStream);
resetCameraView.setImage(img);
}
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/hide_roof.png")) {
Image img = new Image(inStream);
hideRoofView.setImage(img);
}
} catch (IOException e) {
// ignore close exception
}
}
private void setupReportButton() {
reportBtn.setDisable(true);
reportBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.writeReports")));
reportBtn.setOnAction(ae -> {
if (writeDialog == null) {
try {
writeDialog = new WriteReportDialog(stage, controller, mainWindow);
} catch (IOException e) {
logger.catching(e);
mainWindow.showExceptionDialog(e);
}
}
if (writeDialog != null) {
// writeDialog can be null if creation of said dialog fails
writeDialog.show();
}
});
}
private void setupAboutButton() {
aboutBtn.setOnAction(ae -> {
if (aboutDialog == null) {
try {
aboutDialog = new AboutDialog(stage);
aboutDialog.show();
} catch (IOException e) {
logger.error("Could not load about dialog.", e);
}
} else {
aboutDialog.show();
}
});
}
private void setupLodButtons() {
lod1Btn.setOnAction(ae -> {
if (lod1Btn.isSelected()) {
renderer.enableLod1();
} else {
renderer.disableLod1();
}
});
lod2Btn.setOnAction(ae -> {
if (lod2Btn.isSelected()) {
renderer.enableLod2();
} else {
renderer.disableLod2();
}
});
lod3Btn.setOnAction(ae -> {
if (lod3Btn.isSelected()) {
renderer.enableLod3();
} else {
renderer.disableLod3();
}
});
lod4Btn.setOnAction(ae -> {
if (lod4Btn.isSelected()) {
renderer.enableLod4();
} else {
renderer.disableLod4();
}
});
}
private void setupSaveBtn() {
saveBtn.setOnAction(ae -> controller.askAndSave());
}
private void setupCheckButton() {
checkBtn.setDisable(true);
checkBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.executeChecks")));
checkBtn.setOnAction(ae -> {
if (checkDialog == null) {
try {
checkDialog = new CheckDialog(mainWindow, stage, controller);
} catch (IOException e) {
mainWindow.showExceptionDialog(e);
logger.catching(e);
}
}
checkDialog.show();
});
}
private void setupResetCameraButton() {
resetCameraBtn.setDisable(true);
resetCameraBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.resetCamera")));
resetCameraBtn.setOnAction(ae -> mainWindow.resetCamera());
}
private void setupHideRoofButton() {
hideRoofBtn.setDisable(true);
hideRoofBtn.setSelected(false);
hideRoofBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.hideRoof")));
hideRoofBtn.setOnAction(ae -> {
if (hideRoofBtn.isSelected()) {
renderer.hideRoofs();
} else {
renderer.showRoofs();
}
});
}
public Button getCheckButton() {
return checkBtn;
}
public ToggleButton getGridButton() {
return gridButton;
}
public ToggleButton getCullingButton() {
return cullingButton;
}
public Button getSaveButton() {
return saveBtn;
}
public Button getWriteReportButton() {
return reportBtn;
}
public ToggleButton getLod1Btn() {
return lod1Btn;
}
public ToggleButton getLod2Btn() {
return lod2Btn;
}
public ToggleButton getLod3Btn() {
return lod3Btn;
}
public ToggleButton getLod4Btn() {
return lod4Btn;
}
public ToggleButton getHideRoofBtn() {
return hideRoofBtn;
}
public Button getWorldBtn() {
return showWorldBtn;
}
public Button getSaveBtn() {
return saveBtn;
}
public Button getResetCameraBtn() {
return resetCameraBtn;
}
public HBox getToolBar() {
return toolBar;
}
public Button getOpenBtn() {
return openBtn;
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Timer;
import java.util.TimerTask;
import javax.imageio.ImageIO;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.CityDoctorValidation;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox;
......@@ -34,35 +19,9 @@ import javafx.beans.value.ChangeListener;
import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.AmbientLight;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.control.Alert;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.RadioButton;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.ToolBar;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
......@@ -77,902 +36,1109 @@ import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
public class MainWindow extends Application {
private static final Logger logger = LogManager.getLogger(MainWindow.class);
private static final Logger logger = LogManager.getLogger(MainWindow.class);
private static final double CAMERA_TRANSLATE_Z = -100.0;
private static final double CAMERA_INITIAL_X_ANGLE = 20.0;
private static final double CAMERA_INITIAL_Y_ANGLE = 120.0;
@FXML
private TreeView<Renderable> buildingsView;
@FXML
private TreeView<Renderable> vegetationView;
@FXML
private TreeView<Renderable> transView;
@FXML
private TreeView<Renderable> tunnelView;
@FXML
private TreeView<Renderable> bridgeView;
@FXML
private TreeView<Renderable> waterView;
@FXML
private TreeView<Renderable> terrainView;
private static final double CAMERA_TRANSLATE_Z = -100.0;
private static final double CAMERA_INITIAL_X_ANGLE = 20.0;
private static final double CAMERA_INITIAL_Y_ANGLE = 120.0;
@FXML
private TreeView<Renderable> cityFurnitureView;
@FXML
private TreeView<Renderable> buildingsView;
@FXML
private TreeView<Renderable> attributeView;
@FXML
private TreeView<Renderable> vegetationView;
@FXML
TreeView<Renderable> otherObjectsView;
@FXML
private TreeView<Renderable> transView;
@FXML
private TreeView<Renderable> polygonView;
@FXML
private TreeView<Renderable> bridgeView;
@FXML
private TreeView<Renderable> edgeView;
@FXML
private TreeView<Renderable> waterView;
@FXML
private TreeView<Renderable> vertexView;
@FXML
private TreeView<Renderable> terrainView;
@FXML
private Pane meshView;
@FXML
private TreeView<Renderable> polygonView;
@FXML
private SplitPane mainContainer;
@FXML
private TreeView<Renderable> edgeView;
@FXML
private TabPane detailsTabPane;
@FXML
private TreeView<Renderable> vertexView;
@FXML
private TabPane featurePane;
@FXML
private Pane meshView;
@FXML
private TextField searchField;
@FXML
private SplitPane mainContainer;
@FXML
private TreeView<Renderable> errorView;
@FXML
private TabPane detailsTabPane;
@FXML
private Button clearBtn;
@FXML
private TabPane featurePane;
@FXML
private Button searchBtn;
@FXML
private TextField searchField;
@FXML
private ToggleButton allButton;
@FXML
private TreeView<Renderable> errorView;
@FXML
private ToggleButton errorButton;
@FXML
private Button clearBtn;
@FXML
private ComboBox<String> showCityObjectsCombo;
@FXML
private Button searchBtn;
@FXML
private HBox viewPane;
@FXML
private ToggleButton allButton;
@FXML
private ListView<String> globalErrorsView;
@FXML
private ToggleButton errorButton;
@FXML
private BorderPane mainPane;
@FXML
private ComboBox<String> showCityObjectsCombo;
@FXML
private HBox viewButtonBox;
@FXML
private HBox viewPane;
@FXML
private ComboBox<Locale> languageSelector;
@FXML
private ListView<String> globalErrorsView;
@FXML
private Tab buildingsTab;
@FXML
private BorderPane mainPane;
@FXML
private Tab vegetationTab;
@FXML
private HBox viewButtonBox;
@FXML
private Tab transportationTab;
@FXML
private ComboBox<Locale> languageSelector;
@FXML
private Tab tunnelTab;
@FXML
private Tab buildingsTab;
@FXML
private Tab bridgeTab;
@FXML
private Tab vegetationTab;
@FXML
private Tab waterTab;
@FXML
private Tab transportationTab;
@FXML
private Tab terrainTab;
@FXML
private Tab bridgeTab;
@FXML
private Tab cityFurnitureTab;
@FXML
private Tab waterTab;
@FXML
private Tab otherObjectsTab;
@FXML
private Tab terrainTab;
@FXML
private Tab errorsTab;
@FXML
private Tab errorsTab;
@FXML
private Tab polygonsTab;
@FXML
private Tab polygonsTab;
@FXML
private Tab edgesTab;
@FXML
private Tab edgesTab;
@FXML
private Tab verticesTab;
@FXML
private Tab verticesTab;
@FXML
private Tab attributeTab;
@FXML
private Tab logTab;
@FXML
private Tab logTab;
@FXML
private Tab globalErrorsTab;
@FXML
private Tab globalErrorsTab;
@FXML
private Label viewLabel;
@FXML
private Label viewLabel;
@FXML
private Label showLabel;
@FXML
private Label showLabel;
@FXML
private Label searchLabel;
@FXML
private Label searchLabel;
@FXML
private Label memoryLabel;
@FXML
private Label memoryLabel;
@FXML
private ProgressBar memoryBar;
@FXML
private ProgressBar memoryBar;
@FXML
private Label memoryConsumptionLabel;
@FXML
private Label memoryConsumptionLabel;
@FXML
private Label availableLabel;
@FXML
private Label availableLabel;
private Group meshGroup;
private Image north;
private Image northFlip;
private Group world;
private PerspectiveCamera camera;
private Rotate cameraXRotation = new Rotate();
private Rotate cameraZRotation = new Rotate();
private Group meshGroup;
private double dragX;
private double dragY;
private Group world;
private PerspectiveCamera camera;
private Rotate cameraXRotation = new Rotate();
private Rotate cameraZRotation = new Rotate();
private double cameraXRot = CAMERA_INITIAL_X_ANGLE;
private double cameraYRot = CAMERA_INITIAL_Y_ANGLE;
private double translateZ = CAMERA_TRANSLATE_Z;
private double[] clickStart = new double[2];
private double dragX;
private double dragY;
@FXML
private TextArea logArea;
@FXML
private ToolBar viewBar;
private Stage stage;
private ExceptionDialog exceptionDialog;
private Renderer renderer;
private CityDoctorController controller;
private HighlightController highlightController;
private FeatureType selectedTab = FeatureType.BUILDING;
private MainToolBar mainToolBar;
private SubScene geomScene;
private static boolean loadFileAtStartup = false;
private static String inputFile;
private static String xmlOutput;
private static String pdfOutput;
private static ValidationConfiguration config;
private VertexClickHandler clickHandler;
private ChangeListener<Number> filterChangeListener;
public static void main(String[] args) {
setLocaleFromSettings();
ArgumentParser argParser = new ArgumentParser(args);
inputFile = CityDoctorValidation.getInputFile(argParser, true);
if (inputFile != null) {
loadFileAtStartup = true;
xmlOutput = CityDoctorValidation.getXmlOutput(argParser);
pdfOutput = CityDoctorValidation.getPdfOutput(argParser);
try {
config = CityDoctorValidation.getValidationConfig(argParser, true);
} catch (FileNotFoundException e) {
Platform.runLater(() -> {
List<String> configFiles = argParser.getValues("config");
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText(Localization.getText("MainWindow.missingConfig") + configFiles);
alert.showAndWait();
});
System.exit(4);
}
}
Application.launch(args);
}
private static void setLocaleFromSettings() {
String localeString = Settings.get(Settings.LANGUAGE);
if (localeString != null) {
try {
Locale loc = new Locale(localeString);
Locale.setDefault(loc);
} catch (Exception e) {
logger.warn("Could not set language to {}, using system language", localeString);
}
}
}
@Override
public void start(Stage stage) throws IOException {
this.stage = stage;
stage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("icons/citydoctor_logo.png")));
FXMLLoader loader = new FXMLLoader(MainWindow.class.getResource("MainWindow.fxml"));
loader.setController(this);
BorderPane bp = loader.load();
highlightController = new HighlightController(world);
renderer = new Renderer(this, highlightController);
clickHandler = new VertexClickHandler(errorView, renderer, stage);
controller = new CityDoctorController(this, highlightController, renderer);
mainToolBar = new MainToolBar(stage, controller, featurePane, renderer, this);
viewPane.getChildren().add(mainToolBar.getToolBar());
HBox.setHgrow(mainToolBar.getToolBar(), Priority.ALWAYS);
ValidationView valView = new ValidationView(this, controller);
ViewRegistration.registerView(valView);
setupViews(valView);
createLanguageSelector();
setLabelsInCorrectLanguage();
Scene scene = new Scene(bp, 1280, 800);
createDropTarget(scene, valView);
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version);
stage.setScene(scene);
stage.show();
checkForStartupLoading();
memoryBar.setOnMouseClicked(me -> System.gc());
Timer timer = new Timer(true);
// check memory every second
TimerTask task = new TimerTask() {
Runtime runtime = Runtime.getRuntime();
@Override
public void run() {
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
double percentage = usedMemory / (double) totalMemory;
if (totalMemory / 1024 / 1024 >= 1024) {
// gb
double totalMemoryGb = totalMemory / (1024d * 1024d * 1024d);
double usedMemoryGb = usedMemory / (1024d * 1024d * 1024d);
String memoryString = String.format("%.1f GB / %.1f GB", usedMemoryGb, totalMemoryGb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
} else if (totalMemory / 1024 >= 1024) {
// mb
double totalMemoryMb = totalMemory / (1024d * 1024d);
double usedMemoryMb = usedMemory / (1024d * 1024d);
String memoryString = String.format("%.1f MB / %.1f MB", usedMemoryMb, totalMemoryMb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
}
}
};
timer.schedule(task, 0, 1000);
}
private void createDropTarget(Scene scene, ValidationView valView) {
setDragOverInteraction(scene, valView);
scene.setOnDragDropped(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasFiles() && db.getFiles().size() == 1) {
File f = db.getFiles().get(0);
Thread t = new Thread(() -> {
try {
controller.loadCityGml(f.getAbsolutePath(), 8, (ProgressListener) null, false);
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(Localization.getText("OpenFileDialog.loadFailed"), e);
}
Platform.runLater(() -> {
ExceptionDialog exDialog = new ExceptionDialog();
exDialog.show(e);
});
}
});
t.start();
success = true;
}
// let the source know whether the string was successfully transferred and used
event.setDropCompleted(success);
event.consume();
});
}
private void setDragOverInteraction(Scene scene, ValidationView valView) {
scene.setOnDragOver(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
if (event.getGestureSource() != scene && event.getDragboard().hasFiles()
&& event.getDragboard().getFiles().size() == 1) {
// allow for both copying and moving, whatever user chooses
event.acceptTransferModes(TransferMode.LINK);
}
event.consume();
});
}
private void setLabelsInCorrectLanguage() {
buildingsTab.setText(Localization.getText("MainWindow.buildingsTab"));
vegetationTab.setText(Localization.getText("MainWindow.vegetationTab"));
transportationTab.setText(Localization.getText("MainWindow.transportationTab"));
bridgeTab.setText(Localization.getText("MainWindow.bridgeTab"));
waterTab.setText(Localization.getText("MainWindow.waterTab"));
terrainTab.setText(Localization.getText("MainWindow.terrainTab"));
viewLabel.setText(Localization.getText("MainWindow.viewLabel"));
showLabel.setText(Localization.getText("MainWindow.showLabel"));
searchLabel.setText(Localization.getText("MainWindow.searchLabel"));
searchBtn.setText(Localization.getText("MainWindow.searchBtn"));
clearBtn.setText(Localization.getText("MainWindow.clearBtn"));
errorsTab.setText(Localization.getText("MainWindow.errorsTab"));
polygonsTab.setText(Localization.getText("MainWindow.polygonsTab"));
edgesTab.setText(Localization.getText("MainWindow.edgesTab"));
verticesTab.setText(Localization.getText("MainWindow.verticesTab"));
logTab.setText(Localization.getText("MainWindow.logTab"));
globalErrorsTab.setText(Localization.getText("MainWindow.globalErrorsTab"));
memoryLabel.setText(Localization.getText("MainWindow.memoryLabel"));
availableLabel.setText(String.format("%s %.1f GB", Localization.getText("MainWindow.availableLabel"),
Runtime.getRuntime().maxMemory() / 1024d / 1024d / 1024d));
}
private void createLanguageSelector() {
languageSelector.setButtonCell(new LanguageSelectorCell());
languageSelector.setCellFactory(view -> new LanguageSelectorCell());
languageSelector.getItems().add(Locale.GERMAN);
languageSelector.getItems().add(Locale.ENGLISH);
if (Locale.getDefault().getLanguage().equals(Locale.GERMAN.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.GERMAN);
} else if (Locale.getDefault().getLanguage().equals(Locale.ENGLISH.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
} else {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
Settings.set(Settings.LANGUAGE, Locale.ENGLISH.getLanguage());
}
languageSelector.getSelectionModel().selectedItemProperty().addListener((obs, oldV, newV) -> {
Alert alert = new Alert(AlertType.CONFIRMATION, Localization.getText("MainWindow.languageChange"),
ButtonType.OK);
alert.showAndWait();
Settings.set(Settings.LANGUAGE, newV.getLanguage());
});
}
private void checkForStartupLoading() {
if (loadFileAtStartup) {
logger.info(Localization.getText("MainWindow.loadGivenFile"));
Thread t = new Thread(() -> {
try {
// wait a bit for the gui to show
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
try {
controller.loadCityGml(inputFile, config.getNumberOfRoundingPlaces(), null,
config.isXmlValidation());
logger.info(Localization.getText("MainWindow.finishedLoading"));
logger.info(Localization.getText("MainWindow.checking"));
controller.startChecks(config, null);
if (xmlOutput != null) {
logger.info(Localization.getText("MainWindow.writeXml"));
controller.writeXmlReport(new File(xmlOutput));
logger.info(Localization.getText("MainWindow.finishedXml"));
}
if (pdfOutput != null) {
logger.info(Localization.getText("MainWindow.writePdf"));
controller.writePdfReport(new File(pdfOutput));
logger.info(Localization.getText("MainWindow.finishedPdf"));
}
} catch (CityGmlParseException | InvalidGmlFileException e) {
logger.error(Localization.getText("MainWindow.loadFailed"), e.getMessage());
}
});
t.start();
}
}
private void setupViews(ValidationView valView) {
ToggleGroup group = new ToggleGroup();
for (View v : ViewRegistration.getRegisteredViews()) {
RadioButton radioButton = new RadioButton();
radioButton.getStyleClass().remove("radio-button");
radioButton.getStyleClass().add("toggle-button");
ImageView view = new ImageView(v.getViewLogo());
view.setFitHeight(32);
view.setFitWidth(32);
radioButton.setGraphic(view);
group.getToggles().add(radioButton);
if (v == valView) {
ViewRegistration.setCurrentActiveView(valView);
group.selectToggle(radioButton);
}
viewButtonBox.getChildren().add(radioButton);
radioButton.selectedProperty().addListener((obs, oldV, newV) -> {
if (Boolean.TRUE.equals(newV)) {
showView(v);
ViewRegistration.setCurrentActiveView(v);
} else {
v.onHide();
}
});
}
}
private void showView(View v) {
viewPane.getChildren().clear();
controller.showView(v);
Optional<HBox> toolbar = v.getToolbar();
if (toolbar.isPresent()) {
viewPane.getChildren().add(toolbar.get());
HBox.setHgrow(toolbar.get(), Priority.ALWAYS);
} else {
HBox placeHolderToolbar = new HBox();
placeHolderToolbar.setMaxHeight(Double.MAX_VALUE);
viewPane.getChildren().add(placeHolderToolbar);
HBox.setHgrow(placeHolderToolbar, Priority.ALWAYS);
}
mainPane.setCenter(v.getMainScreen());
}
public void initialize() {
GuiLogger.setTextArea(logArea);
loadFrameConfig();
setup3dView();
setupTrees();
setupSearchField();
setupSearchButtons();
setupShowCityComboBox();
detailsTabPane.getSelectionModel().selectedIndexProperty()
.addListener((ov, oldI, newI) -> Platform.runLater(() -> {
if (newI.intValue() == 0 && errorView.getSelectionModel().getSelectedItem() != null) {
// the first tab is selected, meaning the error tab
// redisplay the selected error
errorView.getSelectionModel().getSelectedItem().getValue().visit(renderer);
}
}));
}
private void setupShowCityComboBox() {
showCityObjectsCombo.getItems().addAll(Localization.getText("MainWindow.all"),
Localization.getText("MainWindow.withErrors"));
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.setCellFactory(param -> new ListCell<>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
if (item.contentEquals(Localization.getText("MainWindow.withErrors"))) {
setTextFill(Color.RED);
}
} else {
setText(null);
}
}
});
filterChangeListener = setupFilterSelectionListener();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
public void resetFilterComboBox() {
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().removeListener(filterChangeListener);
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
private ChangeListener<Number> setupFilterSelectionListener() {
return (obs, oldV, newV) -> controller.errorFilterIndexChanged(newV);
}
private void setupSearchButtons() {
searchBtn.setOnAction(ae -> controller.searchFeature(searchField.getText(), selectedTab));
clearBtn.setOnAction(ae -> {
if (searchField.getText().isEmpty()) {
// do not reset search if nothing has bee searched
return;
}
controller.resetSearch(selectedTab);
searchField.setText("");
});
}
private void setupSearchField() {
searchField.textProperty().addListener((obs, oldV, newV) -> {
if (newV.isEmpty()) {
controller.resetSearch(selectedTab);
}
});
featurePane.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) -> {
if (!searchField.getText().isEmpty()) {
resetSearchBar();
controller.resetSearch(selectedTab);
}
int index = newV.intValue();
switch (index) {
case 0:
selectedTab = FeatureType.BUILDING;
break;
case 1:
selectedTab = FeatureType.VEGETATION;
break;
case 2:
selectedTab = FeatureType.TRANSPORTATION;
break;
case 3:
selectedTab = FeatureType.BRIDGE;
break;
case 4:
selectedTab = FeatureType.WATER;
break;
case 5:
selectedTab = FeatureType.LAND;
break;
default:
throw new IllegalStateException("Unknown tab index: " + index);
}
});
}
public void resetSearchBar() {
searchField.setText("");
}
private void loadFrameConfig() {
stage.setMaximized(Boolean.valueOf(Settings.get(Settings.MAXIMIZED, Boolean.FALSE.toString())));
stage.maximizedProperty()
.addListener((obs, oldV, newV) -> Settings.set(Settings.MAXIMIZED, Boolean.toString(newV)));
String widthString = Settings.get(Settings.FRAME_WIDTH);
if (widthString != null) {
stage.setWidth(Double.parseDouble(widthString));
}
stage.widthProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_WIDTH, Double.toString(newV.doubleValue())));
String heightString = Settings.get(Settings.FRAME_HEIGHT);
if (heightString != null) {
stage.setHeight(Double.parseDouble(heightString));
}
stage.heightProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_HEIGHT, Double.toString(newV.doubleValue())));
}
public void showExceptionDialog(Throwable e) {
if (exceptionDialog == null) {
exceptionDialog = new ExceptionDialog();
}
exceptionDialog.show(e);
}
private void setupTrees() {
setupSelectListener(errorView);
errorView.setRoot(new TreeItem<>());
errorView.setCellFactory(param -> new RenderableTreeCell());
buildingsView.setShowRoot(true);
setupSelectListener(buildingsView);
buildingsView.setCellFactory(param -> new RenderableTreeCell());
ContextMenu cMenu = new ContextMenu();
MenuItem mi = new MenuItem(Localization.getText("MainWindow.export"));
mi.setOnAction(ea -> {
Renderable render = buildingsView.getSelectionModel().getSelectedItem().getValue();
if (render instanceof BuildingNode node) {
controller.export(node.getBuilding());
}
});
cMenu.getItems().add(mi);
MenuItem deleteMi = new MenuItem("Delete");
deleteMi.setOnAction(ae -> controller.delete(buildingsView.getSelectionModel().getSelectedItem()));
cMenu.getItems().add(deleteMi);
buildingsView.setContextMenu(cMenu);
vegetationView.setShowRoot(true);
setupSelectListener(vegetationView);
vegetationView.setCellFactory(param -> new RenderableTreeCell());
transView.setShowRoot(true);
setupSelectListener(transView);
transView.setCellFactory(param -> new RenderableTreeCell());
bridgeView.setShowRoot(true);
setupSelectListener(bridgeView);
bridgeView.setCellFactory(param -> new RenderableTreeCell());
waterView.setShowRoot(true);
setupSelectListener(waterView);
waterView.setCellFactory(param -> new RenderableTreeCell());
terrainView.setShowRoot(true);
setupSelectListener(terrainView);
terrainView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(vertexView);
vertexView.setRoot(new TreeItem<>());
vertexView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(polygonView);
polygonView.setRoot(new TreeItem<>());
polygonView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(edgeView);
edgeView.setRoot(new TreeItem<>());
edgeView.setCellFactory(param -> new RenderableTreeCell());
}
private void setupSelectListener(TreeView<Renderable> view) {
view.getSelectionModel().selectedItemProperty().addListener((obs, oldI, newI) -> {
if (newI != null) {
newI.getValue().visit(renderer);
}
});
}
private void setup3dView() {
Group root = new Group();
geomScene = new SubScene(root, 500, 300, true, SceneAntialiasing.BALANCED);
geomScene.heightProperty().bind(meshView.heightProperty());
geomScene.widthProperty().bind(meshView.widthProperty());
geomScene.setFill(Color.AZURE);
meshView.getChildren().add(geomScene);
geomScene.addEventFilter(MouseEvent.MOUSE_PRESSED, me -> {
clickStart[0] = me.getScreenX();
clickStart[1] = me.getScreenY();
});
geomScene.addEventFilter(MouseEvent.MOUSE_RELEASED, me -> {
if (Math.abs(clickStart[0] - me.getScreenX()) > 3 || Math.abs(clickStart[1] - me.getScreenY()) > 3) {
// skip when mouse moved too much
return;
}
Node node = me.getPickResult().getIntersectedNode();
if (node != null) {
Object o = node.getUserData();
if (o instanceof ClickDispatcher cd) {
cd.click(me, clickHandler);
}
}
});
world = new Group();
root.getChildren().add(world);
meshGroup = new Group();
world.getChildren().add(meshGroup);
AmbientLight al = new AmbientLight(Color.WHITE);
root.getChildren().add(al);
buildCamera();
cameraXRotation.setAxis(Rotate.X_AXIS);
cameraZRotation.setAxis(Rotate.Z_AXIS);
world.getTransforms().add(cameraXRotation);
world.getTransforms().add(cameraZRotation);
root.getChildren().add(camera);
geomScene.setCamera(camera);
setupMeshViewControls();
}
private void setupMeshViewControls() {
meshView.setOnMousePressed(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
dragX = me.getScreenX();
dragY = me.getScreenY();
}
});
meshView.setOnScroll(se -> {
if (se.getDeltaY() < 0) {
translateZ += translateZ * 0.05;
} else {
translateZ -= translateZ * 0.05;
}
camera.setTranslateZ(translateZ);
highlightController.changeScaling(translateZ);
});
meshView.setOnMouseDragged(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
double deltaX = me.getScreenX() - dragX;
double deltaY = me.getScreenY() - dragY;
dragX = me.getScreenX();
dragY = me.getScreenY();
cameraXRot += (deltaX / 3d) % 360;
cameraYRot += (deltaY / 3d) % 360;
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
}
});
}
private void buildCamera() {
camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);
camera.setFarClip(10000d);
camera.setTranslateZ(translateZ);
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
}
public void addFileNameToTitle(String fileName) {
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version + " - " + fileName);
}
public TreeView<Renderable> getBuildingsView() {
return buildingsView;
}
public TreeView<Renderable> getVegetationView() {
return vegetationView;
}
public TreeView<Renderable> getTransportationView() {
return transView;
}
public TreeView<Renderable> getBridgeView() {
return bridgeView;
}
public TreeView<Renderable> getWaterView() {
return waterView;
}
public TreeView<Renderable> getTerrainView() {
return terrainView;
}
public TreeView<Renderable> getPolygonsView() {
return polygonView;
}
public TreeView<Renderable> getEdgeView() {
return edgeView;
}
public TreeView<Renderable> getVertexView() {
return vertexView;
}
public Button getCheckButton() {
return mainToolBar.getCheckButton();
}
public ToggleButton getGridButton() {
return mainToolBar.getGridButton();
}
public Group getMeshGroup() {
return meshGroup;
}
public ToggleButton getCullingButton() {
return mainToolBar.getCullingButton();
}
public Button getWriteReportButton() {
return mainToolBar.getWriteReportButton();
}
public TreeView<Renderable> getErrorTree() {
return errorView;
}
public Stage getMainStage() {
return stage;
}
public void unselectEverything() {
buildingsView.getSelectionModel().clearSelection();
vegetationView.getSelectionModel().clearSelection();
transView.getSelectionModel().clearSelection();
waterView.getSelectionModel().clearSelection();
terrainView.getSelectionModel().clearSelection();
bridgeView.getSelectionModel().clearSelection();
polygonView.getSelectionModel().clearSelection();
edgeView.getSelectionModel().clearSelection();
vertexView.getSelectionModel().clearSelection();
errorView.getSelectionModel().clearSelection();
}
public ToggleButton getLod1Btn() {
return mainToolBar.getLod1Btn();
}
public ToggleButton getLod2Btn() {
return mainToolBar.getLod2Btn();
}
public ToggleButton getLod3Btn() {
return mainToolBar.getLod3Btn();
}
public ToggleButton getLod4Btn() {
return mainToolBar.getLod4Btn();
}
public Button getWorldBtn() {
return mainToolBar.getWorldBtn();
}
public Button getSaveBtn() {
return mainToolBar.getSaveBtn();
}
public SplitPane getMainContainer() {
return mainContainer;
}
public ListView<String> getGlobalErrorsView() {
return globalErrorsView;
}
public void takeViewScreenshot() throws IOException {
WritableImage snapshot = geomScene.snapshot(null, null);
File outputFile = new File("img.png");
BufferedImage bImage = SwingFXUtils.fromFXImage(snapshot, null);
ImageIO.write(bImage, "png", outputFile);
}
public void zoomOutForBoundingBox(BoundingBox b) {
double longestSide = b.getDiagonalLength() * 0.4;
double d = longestSide / Math.tan(Math.toRadians(30) / 2);
translateZ = -d;
camera.setTranslateZ(translateZ);
highlightController.changeScaling(-translateZ);
}
public MainToolBar getMainToolbar() {
return mainToolBar;
}
public void clearHighlights() {
highlightController.clearHighlights();
}
public CityDoctorController getController() {
return controller;
}
public Button getOpenBtn() {
return mainToolBar.getOpenBtn();
}
public VertexClickHandler getClickHandler() {
return clickHandler;
}
public FeatureType getSelectedTab() {
return selectedTab;
}
private double cameraXRot = CAMERA_INITIAL_X_ANGLE;
private double cameraYRot = CAMERA_INITIAL_Y_ANGLE;
private double translateZ = CAMERA_TRANSLATE_Z;
private double[] clickStart = new double[2];
@FXML
private TextArea logArea;
@FXML
private ToolBar viewBar;
private Stage stage;
private ExceptionDialog exceptionDialog;
private Renderer renderer;
private CityDoctorController controller;
private HighlightController highlightController;
private FeatureType selectedTab = FeatureType.BUILDING;
private MainToolBar mainToolBar;
private SubScene geomScene;
private ImageView northArrow;
private static boolean loadFileAtStartup = false;
private static String inputFile;
private static String xmlOutput;
private static String pdfOutput;
private static ValidationConfiguration config;
private VertexClickHandler clickHandler;
private ChangeListener<Number> filterChangeListener;
public static void main(String[] args) {
setLocaleFromSettings();
ArgumentParser argParser = new ArgumentParser(args);
inputFile = CityDoctorValidation.getInputFile(argParser, true);
if (inputFile != null) {
loadFileAtStartup = true;
xmlOutput = CityDoctorValidation.getXmlOutput(argParser);
pdfOutput = CityDoctorValidation.getPdfOutput(argParser);
try {
config = CityDoctorValidation.getValidationConfig(argParser, true);
} catch (FileNotFoundException e) {
Platform.runLater(() -> {
List<String> configFiles = argParser.getValues("config");
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText(Localization.getText("MainWindow.missingConfig") + configFiles);
alert.showAndWait();
});
System.exit(4);
}
}
Application.launch(args);
}
private static void setLocaleFromSettings() {
String localeString = Settings.get(Settings.LANGUAGE);
if (localeString != null) {
try {
Locale loc = new Locale(localeString);
Locale.setDefault(loc);
} catch (Exception e) {
logger.warn("Could not set language to {}, using system language", localeString);
}
}
}
@Override
public void start(Stage stage) throws IOException {
this.stage = stage;
stage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("icons/citydoctor_logo.png")));
FXMLLoader loader = new FXMLLoader(MainWindow.class.getResource("MainWindow.fxml"));
loader.setController(this);
BorderPane bp = loader.load();
highlightController = new HighlightController(world);
renderer = new Renderer(this, highlightController);
clickHandler = new VertexClickHandler(errorView, renderer, stage);
controller = new CityDoctorController(this, highlightController, renderer);
mainToolBar = new MainToolBar(stage, controller, featurePane, renderer, this);
viewPane.getChildren().add(mainToolBar.getToolBar());
HBox.setHgrow(mainToolBar.getToolBar(), Priority.ALWAYS);
ValidationView valView = new ValidationView(this, controller);
ViewRegistration.registerView(valView);
setupViews(valView);
createLanguageSelector();
setLabelsInCorrectLanguage();
Scene scene = new Scene(bp, 1280, 800);
createDropTarget(scene, valView);
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version);
stage.setScene(scene);
stage.show();
checkForStartupLoading();
memoryBar.setOnMouseClicked(me -> System.gc());
Timer timer = new Timer(true);
// check memory every second
TimerTask task = new TimerTask() {
Runtime runtime = Runtime.getRuntime();
@Override
public void run() {
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
double percentage = usedMemory / (double) totalMemory;
if (totalMemory / 1024 / 1024 >= 1024) {
// gb
double totalMemoryGb = totalMemory / (1024d * 1024d * 1024d);
double usedMemoryGb = usedMemory / (1024d * 1024d * 1024d);
String memoryString = String.format("%.1f GB / %.1f GB", usedMemoryGb, totalMemoryGb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
} else if (totalMemory / 1024 >= 1024) {
// mb
double totalMemoryMb = totalMemory / (1024d * 1024d);
double usedMemoryMb = usedMemory / (1024d * 1024d);
String memoryString = String.format("%.1f MB / %.1f MB", usedMemoryMb, totalMemoryMb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
}
}
};
timer.schedule(task, 0, 1000);
}
private void createDropTarget(Scene scene, ValidationView valView) {
setDragOverInteraction(scene, valView);
scene.setOnDragDropped(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasFiles() && db.getFiles().size() == 1) {
File f = db.getFiles().get(0);
Thread t = new Thread(() -> {
try {
controller.loadCityGml(f.getAbsolutePath(), 8, (ProgressListener) null, false);
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(Localization.getText("OpenFileDialog.loadFailed"), e);
}
Platform.runLater(() -> {
ExceptionDialog exDialog = new ExceptionDialog();
exDialog.show(e);
});
}
});
t.start();
success = true;
}
// let the source know whether the string was successfully transferred and used
event.setDropCompleted(success);
event.consume();
});
}
private void setDragOverInteraction(Scene scene, ValidationView valView) {
scene.setOnDragOver(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
if (event.getGestureSource() != scene && event.getDragboard().hasFiles()
&& event.getDragboard().getFiles().size() == 1) {
// allow for both copying and moving, whatever user chooses
event.acceptTransferModes(TransferMode.LINK);
}
event.consume();
});
}
private void setLabelsInCorrectLanguage() {
buildingsTab.setText(Localization.getText("MainWindow.buildingsTab"));
vegetationTab.setText(Localization.getText("MainWindow.vegetationTab"));
transportationTab.setText(Localization.getText("MainWindow.transportationTab"));
tunnelTab.setText(Localization.getText("MainWindow.tunnelTab"));
bridgeTab.setText(Localization.getText("MainWindow.bridgeTab"));
waterTab.setText(Localization.getText("MainWindow.waterTab"));
terrainTab.setText(Localization.getText("MainWindow.terrainTab"));
cityFurnitureTab.setText(Localization.getText("MainWindow.cityfurnitureTab"));
otherObjectsTab.setText(Localization.getText("MainWindow.otherObjectsTab"));
viewLabel.setText(Localization.getText("MainWindow.viewLabel"));
showLabel.setText(Localization.getText("MainWindow.showLabel"));
searchLabel.setText(Localization.getText("MainWindow.searchLabel"));
searchBtn.setText(Localization.getText("MainWindow.searchBtn"));
clearBtn.setText(Localization.getText("MainWindow.clearBtn"));
errorsTab.setText(Localization.getText("MainWindow.errorsTab"));
polygonsTab.setText(Localization.getText("MainWindow.polygonsTab"));
edgesTab.setText(Localization.getText("MainWindow.edgesTab"));
verticesTab.setText(Localization.getText("MainWindow.verticesTab"));
attributeTab.setText(Localization.getText("MainWindow.attributeTab"));
logTab.setText(Localization.getText("MainWindow.logTab"));
globalErrorsTab.setText(Localization.getText("MainWindow.globalErrorsTab"));
memoryLabel.setText(Localization.getText("MainWindow.memoryLabel"));
availableLabel.setText(String.format("%s %.1f GB", Localization.getText("MainWindow.availableLabel"),
Runtime.getRuntime().maxMemory() / 1024d / 1024d / 1024d));
}
private void createLanguageSelector() {
languageSelector.setButtonCell(new LanguageSelectorCell());
languageSelector.setCellFactory(view -> new LanguageSelectorCell());
languageSelector.getItems().add(Locale.GERMAN);
languageSelector.getItems().add(Locale.ENGLISH);
if (Locale.getDefault().getLanguage().equals(Locale.GERMAN.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.GERMAN);
} else if (Locale.getDefault().getLanguage().equals(Locale.ENGLISH.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
} else {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
Settings.set(Settings.LANGUAGE, Locale.ENGLISH.getLanguage());
}
languageSelector.getSelectionModel().selectedItemProperty().addListener((obs, oldV, newV) -> {
Alert alert = new Alert(AlertType.CONFIRMATION, Localization.getText("MainWindow.languageChange"),
ButtonType.OK);
alert.showAndWait();
Settings.set(Settings.LANGUAGE, newV.getLanguage());
});
}
private void checkForStartupLoading() {
if (loadFileAtStartup) {
logger.info(Localization.getText("MainWindow.loadGivenFile"));
Thread t = new Thread(() -> {
try {
// wait a bit for the gui to show
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
try {
controller.loadCityGml(inputFile, config.getNumberOfRoundingPlaces(), null,
config.isXmlValidation());
logger.info(Localization.getText("MainWindow.finishedLoading"));
logger.info(Localization.getText("MainWindow.checking"));
controller.startChecks(config, null);
if (xmlOutput != null) {
logger.info(Localization.getText("MainWindow.writeXml"));
controller.writeXmlReport(new File(xmlOutput));
logger.info(Localization.getText("MainWindow.finishedXml"));
}
if (pdfOutput != null) {
logger.info(Localization.getText("MainWindow.writePdf"));
controller.writePdfReport(new File(pdfOutput));
logger.info(Localization.getText("MainWindow.finishedPdf"));
}
} catch (CityGmlParseException | InvalidGmlFileException e) {
logger.error(Localization.getText("MainWindow.loadFailed"), e.getMessage());
}
});
t.start();
}
}
private void setupViews(ValidationView valView) {
ToggleGroup group = new ToggleGroup();
for (View v : ViewRegistration.getRegisteredViews()) {
RadioButton radioButton = new RadioButton();
radioButton.getStyleClass().remove("radio-button");
radioButton.getStyleClass().add("toggle-button");
ImageView view = new ImageView(v.getViewLogo());
view.setFitHeight(32);
view.setFitWidth(32);
radioButton.setGraphic(view);
group.getToggles().add(radioButton);
if (v == valView) {
ViewRegistration.setCurrentActiveView(valView);
group.selectToggle(radioButton);
}
viewButtonBox.getChildren().add(radioButton);
radioButton.selectedProperty().addListener((obs, oldV, newV) -> {
if (Boolean.TRUE.equals(newV)) {
showView(v);
ViewRegistration.setCurrentActiveView(v);
} else {
v.onHide();
}
});
}
}
private void showView(View v) {
viewPane.getChildren().clear();
controller.showView(v);
Optional<HBox> toolbar = v.getToolbar();
if (toolbar.isPresent()) {
viewPane.getChildren().add(toolbar.get());
HBox.setHgrow(toolbar.get(), Priority.ALWAYS);
} else {
HBox placeHolderToolbar = new HBox();
placeHolderToolbar.setMaxHeight(Double.MAX_VALUE);
viewPane.getChildren().add(placeHolderToolbar);
HBox.setHgrow(placeHolderToolbar, Priority.ALWAYS);
}
mainPane.setCenter(v.getMainScreen());
}
public void initialize() {
GuiLogger.setTextArea(logArea);
loadFrameConfig();
setup3dView();
setupTrees();
setupSearchField();
setupSearchButtons();
setupShowCityComboBox();
detailsTabPane.getSelectionModel().selectedIndexProperty()
.addListener((ov, oldI, newI) -> Platform.runLater(() -> {
if (newI.intValue() == 0 && errorView.getSelectionModel().getSelectedItem() != null) {
// the first tab is selected, meaning the error tab
// redisplay the selected error
errorView.getSelectionModel().getSelectedItem().getValue().visit(renderer);
}
}));
}
private void setupShowCityComboBox() {
showCityObjectsCombo.getItems().addAll(Localization.getText("MainWindow.all"),
Localization.getText("MainWindow.withErrors"));
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.setCellFactory(param -> new ListCell<>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
if (item.contentEquals(Localization.getText("MainWindow.withErrors"))) {
setTextFill(Color.RED);
}
} else {
setText(null);
}
}
});
filterChangeListener = setupFilterSelectionListener();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
public void resetFilterComboBox() {
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().removeListener(filterChangeListener);
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
private ChangeListener<Number> setupFilterSelectionListener() {
return (obs, oldV, newV) -> controller.errorFilterIndexChanged(newV);
}
private void setupSearchButtons() {
searchBtn.setOnAction(ae -> controller.searchFeature(searchField.getText(), selectedTab));
clearBtn.setOnAction(ae -> {
if (searchField.getText().isEmpty()) {
// do not reset search if nothing has bee searched
return;
}
controller.resetSearch(selectedTab);
searchField.setText("");
});
}
private void setupSearchField() {
searchField.textProperty().addListener((obs, oldV, newV) -> {
if (newV.isEmpty()) {
controller.resetSearch(selectedTab);
}
});
featurePane.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) -> {
if (!searchField.getText().isEmpty()) {
resetSearchBar();
controller.resetSearch(selectedTab);
}
int index = newV.intValue();
switch (index) {
case 0:
selectedTab = FeatureType.BUILDING;
break;
case 1:
selectedTab = FeatureType.VEGETATION;
break;
case 2:
selectedTab = FeatureType.TRANSPORTATION;
break;
case 3:
selectedTab = FeatureType.TUNNEL;
break;
case 4:
selectedTab = FeatureType.BRIDGE;
break;
case 5:
selectedTab = FeatureType.WATER;
break;
case 6:
selectedTab = FeatureType.LAND;
break;
case 7:
selectedTab = FeatureType.CITY_FURNITURE;
break;
case 8:
selectedTab = FeatureType.GENERIC_CITY_OBJECT;
break;
default:
throw new IllegalStateException("Unknown tab index: " + index);
}
});
}
public void setDisableOfFeatureTab(FeatureType featureType, boolean value) {
switch (featureType) {
case BUILDING:
buildingsTab.setDisable(value);
break;
case VEGETATION:
vegetationTab.setDisable(value);
break;
case TRANSPORTATION:
transportationTab.setDisable(value);
break;
case TUNNEL:
tunnelTab.setDisable(value);
break;
case BRIDGE:
bridgeTab.setDisable(value);
break;
case WATER:
waterTab.setDisable(value);
break;
case LAND:
terrainTab.setDisable(value);
break;
case CITY_FURNITURE:
cityFurnitureTab.setDisable(value);
break;
case GENERIC_CITY_OBJECT:
otherObjectsTab.setDisable(value);
break;
default:
throw new IllegalArgumentException("Unknown feature type: " + featureType);
}
}
public void activateAllTabs() {
buildingsTab.setDisable(false);
vegetationTab.setDisable(false);
transportationTab.setDisable(false);
tunnelTab.setDisable(false);
bridgeTab.setDisable(false);
waterTab.setDisable(false);
terrainTab.setDisable(false);
cityFurnitureTab.setDisable(false);
otherObjectsTab.setDisable(false);
}
public void resetSearchBar() {
searchField.setText("");
}
private void loadFrameConfig() {
stage.setMaximized(Boolean.valueOf(Settings.get(Settings.MAXIMIZED, Boolean.FALSE.toString())));
stage.maximizedProperty()
.addListener((obs, oldV, newV) -> Settings.set(Settings.MAXIMIZED, Boolean.toString(newV)));
String widthString = Settings.get(Settings.FRAME_WIDTH);
if (widthString != null) {
stage.setWidth(Double.parseDouble(widthString));
}
stage.widthProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_WIDTH, Double.toString(newV.doubleValue())));
String heightString = Settings.get(Settings.FRAME_HEIGHT);
if (heightString != null) {
stage.setHeight(Double.parseDouble(heightString));
}
stage.heightProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_HEIGHT, Double.toString(newV.doubleValue())));
}
public void showExceptionDialog(Throwable e) {
if (exceptionDialog == null) {
exceptionDialog = new ExceptionDialog();
}
exceptionDialog.show(e);
}
private void setupTrees() {
setupSelectListener(errorView);
errorView.setRoot(new TreeItem<>());
errorView.setCellFactory(param -> new RenderableTreeCell());
buildingsView.setShowRoot(true);
setupSelectListener(buildingsView);
buildingsView.setCellFactory(param -> new RenderableTreeCell());
ContextMenu cMenu = new ContextMenu();
MenuItem mi = new MenuItem(Localization.getText("MainWindow.export"));
mi.setOnAction(ea -> {
Renderable render = buildingsView.getSelectionModel().getSelectedItem().getValue();
if (render instanceof BuildingNode node) {
controller.export(node.getBuilding());
}
});
cMenu.getItems().add(mi);
MenuItem deleteMi = new MenuItem("Delete");
deleteMi.setOnAction(ae -> controller.delete(buildingsView.getSelectionModel().getSelectedItem()));
cMenu.getItems().add(deleteMi);
buildingsView.setContextMenu(cMenu);
vegetationView.setShowRoot(true);
setupSelectListener(vegetationView);
vegetationView.setCellFactory(param -> new RenderableTreeCell());
vegetationView.setContextMenu(cMenu);
transView.setShowRoot(true);
setupSelectListener(transView);
transView.setCellFactory(param -> new RenderableTreeCell());
tunnelView.setShowRoot(true);
setupSelectListener(tunnelView);
tunnelView.setCellFactory(param -> new RenderableTreeCell());
bridgeView.setShowRoot(true);
setupSelectListener(bridgeView);
bridgeView.setCellFactory(param -> new RenderableTreeCell());
waterView.setShowRoot(true);
setupSelectListener(waterView);
waterView.setCellFactory(param -> new RenderableTreeCell());
terrainView.setShowRoot(true);
setupSelectListener(terrainView);
terrainView.setCellFactory(param -> new RenderableTreeCell());
cityFurnitureView.setShowRoot(true);
setupSelectListener(cityFurnitureView);
cityFurnitureView.setCellFactory(param -> new RenderableTreeCell());
otherObjectsView.setShowRoot(true);
setupSelectListener(otherObjectsView);
otherObjectsView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(vertexView);
vertexView.setRoot(new TreeItem<>());
vertexView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(polygonView);
polygonView.setRoot(new TreeItem<>());
polygonView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(edgeView);
edgeView.setRoot(new TreeItem<>());
edgeView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(attributeView);
attributeView.setRoot(new TreeItem<>());
attributeView.setCellFactory(param -> new RenderableTreeCell());
}
private void setupSelectListener(TreeView<Renderable> view) {
view.getSelectionModel().selectedItemProperty().addListener((obs, oldI, newI) -> {
if (newI != null) {
newI.getValue().visit(renderer);
}
});
}
private void setup3dView() {
Group root = new Group();
geomScene = new SubScene(root, 500, 300, true, SceneAntialiasing.BALANCED);
geomScene.heightProperty().bind(meshView.heightProperty());
geomScene.widthProperty().bind(meshView.widthProperty());
geomScene.setFill(Color.AZURE);
meshView.getChildren().add(geomScene);
geomScene.addEventFilter(MouseEvent.MOUSE_PRESSED, me -> {
clickStart[0] = me.getScreenX();
clickStart[1] = me.getScreenY();
});
geomScene.addEventFilter(MouseEvent.MOUSE_RELEASED, me -> {
if (Math.abs(clickStart[0] - me.getScreenX()) > 3 || Math.abs(clickStart[1] - me.getScreenY()) > 3) {
// skip when mouse moved too much
return;
}
Node node = me.getPickResult().getIntersectedNode();
if (node != null) {
Object o = node.getUserData();
if (o instanceof ClickDispatcher cd) {
cd.click(me, clickHandler);
}
}
});
world = new Group();
root.getChildren().add(world);
meshGroup = new Group();
world.getChildren().add(meshGroup);
north = new Image(getClass().getResourceAsStream("icons/north.png"), 50, 50, true, true);
northFlip = new Image(getClass().getResourceAsStream("icons/north_flip.png"), 50, 50, true, true);
northArrow = new ImageView(north);
northArrow.setFitHeight(50);
northArrow.setFitWidth(50);
northArrow.setX(10);
northArrow.setY(10);
meshView.getChildren().add(northArrow);
northArrow.setVisible(false);
AmbientLight al = new AmbientLight(Color.WHITE);
root.getChildren().add(al);
buildCamera();
cameraXRotation.setAxis(Rotate.X_AXIS);
cameraZRotation.setAxis(Rotate.Z_AXIS);
world.getTransforms().add(cameraXRotation);
world.getTransforms().add(cameraZRotation);
root.getChildren().add(camera);
geomScene.setCamera(camera);
setupMeshViewControls();
}
private void setupMeshViewControls() {
meshView.setOnMousePressed(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
dragX = me.getScreenX();
dragY = me.getScreenY();
}
if (me.getButton() == MouseButton.SECONDARY) {
dragX = me.getScreenX();
dragY = me.getScreenY();
}
});
meshView.setOnScroll(se -> {
if (se.getDeltaY() < 0) {
translateZ += translateZ * 0.05;
} else {
translateZ -= translateZ * 0.05;
}
camera.setTranslateZ(translateZ);
highlightController.changeScaling(translateZ);
});
meshView.setOnMouseDragged(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
double deltaX = me.getScreenX() - dragX;
double deltaY = me.getScreenY() - dragY;
dragX = me.getScreenX();
dragY = me.getScreenY();
cameraXRot += (deltaX / 3d) % 360;
cameraYRot += (deltaY / 3d) % 360;
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
alignNorthArrow();
}
if (me.getButton() == MouseButton.SECONDARY) {
double translationSpeed = Math.abs(camera.getTranslateZ()) / 1000;
double deltaX = me.getScreenX() - dragX;
double deltaY = me.getScreenY() - dragY;
dragX = me.getScreenX();
dragY = me.getScreenY();
camera.setTranslateX(camera.getTranslateX() - deltaX * translationSpeed);
camera.setTranslateY(camera.getTranslateY() - deltaY * translationSpeed);
}
});
}
private void buildCamera() {
camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);
camera.setFarClip(10000d);
camera.setTranslateZ(translateZ);
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
}
public void alignNorthArrow() {
double absRotZ = cameraZRotation.getAngle() - 360 * Math.floor(cameraZRotation.getAngle() / 360);
double absRotX = cameraXRotation.getAngle() - 360 * Math.floor(cameraXRotation.getAngle() / 360);
double northRot;
if (80 <= absRotX && absRotX <= 260) {
northRot = 360 - absRotZ;
if (northArrow.getImage() != north) {
northArrow.setImage(north);
}
} else {
//Camera is below horizon line -> flip north arrow horizontally
northRot = (absRotZ + 180) % 360;
if (northArrow.getImage() != northFlip) {
northArrow.setImage(northFlip);
}
}
northArrow.setRotate(northRot);
}
public void resetCamera() {
cameraXRot = CAMERA_INITIAL_X_ANGLE;
cameraYRot = CAMERA_INITIAL_Y_ANGLE;
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
alignNorthArrow();
camera.setTranslateX(0);
camera.setTranslateY(0);
if (controller.getOriginBB() != null) {
zoomOutForBoundingBox(controller.getOriginBB());
}
}
public void addFileNameToTitle(String fileName) {
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version + " - " + fileName);
}
public TreeView<Renderable> getBuildingsView() {
return buildingsView;
}
public TreeView<Renderable> getVegetationView() {
return vegetationView;
}
public TreeView<Renderable> getTransportationView() {
return transView;
}
public TreeView<Renderable> getTunnelView() {
return tunnelView;
}
public TreeView<Renderable> getBridgeView() {
return bridgeView;
}
public TreeView<Renderable> getWaterView() {
return waterView;
}
public TreeView<Renderable> getTerrainView() {
return terrainView;
}
public TreeView<Renderable> getCityFurnitureView() {
return cityFurnitureView;
}
public TreeView<Renderable> getAttributeView() {
return attributeView;
}
public TreeView<Renderable> getOtherObjectsView() {
return otherObjectsView;
}
public TreeView<Renderable> getPolygonsView() {
return polygonView;
}
public TreeView<Renderable> getEdgeView() {
return edgeView;
}
public TreeView<Renderable> getVertexView() {
return vertexView;
}
public Button getCheckButton() {
return mainToolBar.getCheckButton();
}
public ToggleButton getGridButton() {
return mainToolBar.getGridButton();
}
public Group getMeshGroup() {
return meshGroup;
}
public ToggleButton getCullingButton() {
return mainToolBar.getCullingButton();
}
public Button getWriteReportButton() {
return mainToolBar.getWriteReportButton();
}
public TreeView<Renderable> getErrorTree() {
return errorView;
}
public Stage getMainStage() {
return stage;
}
public void unselectEverything() {
buildingsView.getSelectionModel().clearSelection();
vegetationView.getSelectionModel().clearSelection();
transView.getSelectionModel().clearSelection();
tunnelView.getSelectionModel().clearSelection();
waterView.getSelectionModel().clearSelection();
terrainView.getSelectionModel().clearSelection();
cityFurnitureView.getSelectionModel().clearSelection();
otherObjectsView.getSelectionModel().clearSelection();
bridgeView.getSelectionModel().clearSelection();
polygonView.getSelectionModel().clearSelection();
edgeView.getSelectionModel().clearSelection();
vertexView.getSelectionModel().clearSelection();
attributeView.getSelectionModel().clearSelection();
errorView.getSelectionModel().clearSelection();
}
public ToggleButton getLod1Btn() {
return mainToolBar.getLod1Btn();
}
public ToggleButton getLod2Btn() {
return mainToolBar.getLod2Btn();
}
public ToggleButton getLod3Btn() {
return mainToolBar.getLod3Btn();
}
public ToggleButton getLod4Btn() {
return mainToolBar.getLod4Btn();
}
public Button getWorldBtn() {
return mainToolBar.getWorldBtn();
}
public Button getResetCameraBtn() {
return mainToolBar.getResetCameraBtn();
}
public ToggleButton getHideRoofBtn() {
return mainToolBar.getHideRoofBtn();
}
public ImageView getNorthArrow() {
return northArrow;
}
public Button getSaveBtn() {
return mainToolBar.getSaveBtn();
}
public SplitPane getMainContainer() {
return mainContainer;
}
public ListView<String> getGlobalErrorsView() {
return globalErrorsView;
}
public void takeViewScreenshot() throws IOException {
WritableImage snapshot = geomScene.snapshot(null, null);
File outputFile = new File("img.png");
BufferedImage bImage = SwingFXUtils.fromFXImage(snapshot, null);
ImageIO.write(bImage, "png", outputFile);
}
public void zoomOutForBoundingBox(BoundingBox b) {
double longestSide = b.getDiagonalLength() * 0.4;
double d = longestSide / Math.tan(Math.toRadians(30) / 2);
translateZ = -d;
camera.setTranslateZ(translateZ);
camera.setTranslateX(0);
camera.setTranslateY(0);
highlightController.changeScaling(-translateZ);
controller.setOriginBB(b);
}
public MainToolBar getMainToolbar() {
return mainToolBar;
}
public void clearHighlights() {
highlightController.clearHighlights();
}
public CityDoctorController getController() {
return controller;
}
public Button getOpenBtn() {
return mainToolBar.getOpenBtn();
}
public VertexClickHandler getClickHandler() {
return clickHandler;
}
public FeatureType getSelectedTab() {
return selectedTab;
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundingBox;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeConstructiveElement;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.Installation;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.ReliefObject;
import de.hft.stuttgart.citydoctor2.datastructure.TinObject;
import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject;
import de.hft.stuttgart.citydoctor2.datastructure.Vegetation;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.datastructure.WaterObject;
import de.hft.stuttgart.citydoctor2.datastructure.*;
import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter;
import de.hft.stuttgart.citydoctor2.gui.tree.EdgeNode;
import de.hft.stuttgart.citydoctor2.gui.tree.ErrorItemVisitor;
import de.hft.stuttgart.citydoctor2.gui.tree.ErrorNode;
import de.hft.stuttgart.citydoctor2.gui.tree.LinearRingNode;
import de.hft.stuttgart.citydoctor2.gui.tree.PolygonNode;
import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import de.hft.stuttgart.citydoctor2.gui.tree.VertexNode;
import de.hft.stuttgart.citydoctor2.gui.tree.*;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import javafx.application.Platform;
import javafx.scene.control.TreeItem;
import javafx.scene.paint.Color;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.*;
public class Renderer {
private static final Logger logger = LogManager.getLogger(Renderer.class);
private TriangulatedGeometry currentTriGeom;
private Geometry currentGeometry;
private CullFace currentCulling = CullFace.BACK;
private DrawMode currentDrawMode = DrawMode.FILL;
private MainWindow mainWindow;
private HighlightController highlightController;
private ListErrorVisitor errVisitor;
private LoadingInfoDialog loadingDialog;
private List<ViewFilter> lodFilters = new ArrayList<>();
private Runnable refresher;
private Runnable errorUpdater;
public Renderer(MainWindow mainWindow, HighlightController highlightController) throws IOException {
this.mainWindow = mainWindow;
this.highlightController = highlightController;
loadingDialog = new LoadingInfoDialog(mainWindow.getMainStage());
errVisitor = new ListErrorVisitor(highlightController);
setupLodFilters();
}
private void setupLodFilters() {
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD1;
}
});
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD2;
}
});
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD3;
}
});
lodFilters.add(new ViewFilter() {
@Override
protected boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD0;
}
});
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD4;
}
});
}
public void enableLod1() {
lodFilters.get(0).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod1() {
lodFilters.get(0).disable();
if (refresher != null) {
refresher.run();
}
}
public void enableLod2() {
lodFilters.get(1).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod2() {
lodFilters.get(1).disable();
if (refresher != null) {
refresher.run();
}
}
public void enableLod3() {
lodFilters.get(2).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod3() {
lodFilters.get(2).disable();
if (refresher != null) {
refresher.run();
}
}
public void enableLod4() {
lodFilters.get(3).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod4() {
lodFilters.get(3).disable();
if (refresher != null) {
refresher.run();
}
}
public void render(Building building) {
refresher = () -> {
Set<ConcretePolygon> setupBuildingPolygons = setupBuildingPolygons(building);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingPolygons));
render(setupBuildingPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(building);
errorUpdater.run();
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingPolygons(Building b) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(b, polygons);
for (BoundarySurface bs : b.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
for (Installation bi : b.getBuildingInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
for (BuildingPart bp : b.getBuildingParts()) {
polygons.addAll(setupBuildingPartPolygons(bp));
}
return polygons;
}
public void render(BuildingPart bp) {
refresher = () -> {
Set<ConcretePolygon> setupBuildingPartPolygons = setupBuildingPartPolygons(bp);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingPartPolygons));
render(setupBuildingPartPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bp);
errorUpdater.run();
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingPartPolygons(BuildingPart bp) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bp, polygons);
for (BoundarySurface bs : bp.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
for (Installation bi : bp.getBuildingInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
return polygons;
}
public void render(BridgeObject bridge) {
refresher = () -> {
Set<ConcretePolygon> setupBridgePolygons = setupBridgePolygons(bridge);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBridgePolygons));
render(setupBridgePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bridge);
errorUpdater.run();
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBridgePolygons(BridgeObject bridge) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bridge, polygons);
for (BoundarySurface bs : bridge.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
for (BridgeConstructiveElement consElement : bridge.getConstructiveElements()) {
addPolygons(consElement, polygons);
for (BoundarySurface bs : consElement.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (Installation inst : bridge.getBridgeInstallations()) {
addPolygons(inst, polygons);
for (BoundarySurface bs : inst.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
return polygons;
}
public void render(CityObject co) {
refresher = () -> {
Set<ConcretePolygon> setupCityObjectPolygons = setupCityObjectPolygons(co);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(co);
errorUpdater.run();
});
};
refresher.run();
}
public void render(ReliefObject relief) {
refresher = () -> {
Set<ConcretePolygon> setupCityObjectPolygons = setupReliefPolygons(relief);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(relief);
errorUpdater.run();
});
};
refresher.run();
}
private Set<ConcretePolygon> setupReliefPolygons(ReliefObject relief) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(relief, polygons);
for (TinObject tin : relief.getComponents()) {
addPolygons(tin, polygons);
}
return polygons;
}
public void render(Installation bi) {
refresher = () -> {
Set<ConcretePolygon> setupCityObjectPolygons = setupBuildingInstallationPolygons(bi);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bi);
errorUpdater.run();
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingInstallationPolygons(Installation bi) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(BoundarySurface bs) {
refresher = () -> {
Set<ConcretePolygon> setupBoundarySurfacePolygons = setupBoundarySurfacePolygons(bs);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBoundarySurfacePolygons));
render(setupBoundarySurfacePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bs);
errorUpdater.run();
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBoundarySurfacePolygons(BoundarySurface bs) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
return polygons;
}
private Set<ConcretePolygon> setupCityObjectPolygons(CityObject co) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(co, polygons);
return polygons;
}
private void addPolygons(CityObject co, Set<ConcretePolygon> polygons) {
for (Geometry geom : co.getGeometries()) {
boolean used = false;
for (ViewFilter filter : lodFilters) {
if (filter.allowedToUse(co, geom)) {
used = true;
break;
}
}
if (used) {
addConcretePolygons(polygons, geom);
}
}
}
private void displayErrors(Checkable c) {
if (!c.isValidated()) {
return;
}
List<CheckError> errors = new ArrayList<>();
c.collectContainedErrors(errors);
// filter out duplicate errors (polygon can be contained in multiple geometries)
Set<CheckError> errorSet = new HashSet<>(errors);
for (CheckError err : errorSet) {
ErrorNode node = new ErrorNode(err);
TreeItem<Renderable> errItem = new TreeItem<>(node);
ErrorItemVisitor visitor = new ErrorItemVisitor(errItem);
err.accept(visitor);
mainWindow.getErrorTree().getRoot().getChildren().add(errItem);
}
}
public void render(Geometry geom) {
refresher = () -> {
Platform.runLater(this::clearGeometryTrees);
currentTriGeom = TriangulatedGeometry.of(geom);
if (geom.getEdges() == null && currentGeometry != null) {
// if there are no edges available low memory mode is enabled
// clear the old geometry of all meta information
currentGeometry.clearMetaInformation();
}
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
if (geom.getEdges() == null) {
// create edges and vertices so they can be listed in the gui
geom.prepareForChecking();
// remember the geometry, so it can be cleared if another is displayed
currentGeometry = geom;
}
addGeometryDataToView(geom);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(geom.getPolygons()));
errorUpdater = () -> displayErrors(geom);
errorUpdater.run();
});
};
refresher.run();
}
private void addGeometryDataToView(Geometry geom) {
for (Polygon p : geom.getPolygons()) {
addPolygonToView(p);
}
for (Edge e : geom.getEdges()) {
addEdgeToView(e);
}
for (Vertex v : geom.getVertices()) {
addVertexToView(v);
}
}
private void addVertexToView(Vertex v) {
Renderable cf = new VertexNode(v);
TreeItem<Renderable> ti = new TreeItem<>(cf);
mainWindow.getVertexView().getRoot().getChildren().add(ti);
}
private void addEdgeToView(Edge e) {
EdgeNode cf = new EdgeNode(e);
TreeItem<Renderable> ti = new TreeItem<>(cf);
mainWindow.getEdgeView().getRoot().getChildren().add(ti);
}
private void addPolygonToView(Polygon p) {
CheckStatus cs = determineCheckStatus(p);
PolygonNode cf = new PolygonNode(p, cs);
TreeItem<Renderable> ti = new TreeItem<>(cf);
mainWindow.getPolygonsView().getRoot().getChildren().add(ti);
// add linear rings
CheckStatus csRing = determineCheckStatus(p.getExteriorRing());
Renderable ccRing = new LinearRingNode(p.getExteriorRing(), csRing);
TreeItem<Renderable> tiRing = new TreeItem<>(ccRing);
ti.getChildren().add(tiRing);
for (Vertex v : p.getExteriorRing().getVertices()) {
VertexNode vn = new VertexNode(v);
TreeItem<Renderable> tiV = new TreeItem<>(vn);
tiRing.getChildren().add(tiV);
}
for (LinearRing lr : p.getInnerRings()) {
CheckStatus csInteriorRing = determineCheckStatus(lr);
Renderable ccInteriorRing = new LinearRingNode(lr, csInteriorRing);
TreeItem<Renderable> tiInteriorRing = new TreeItem<>(ccInteriorRing);
ti.getChildren().add(tiInteriorRing);
for (Vertex v : lr.getVertices()) {
VertexNode vn = new VertexNode(v);
TreeItem<Renderable> tiV = new TreeItem<>(vn);
tiRing.getChildren().add(tiV);
}
}
}
private CheckStatus determineCheckStatus(Checkable c) {
if (!c.isValidated()) {
return CheckStatus.NOT_CHECKED;
}
if (c.containsAnyError()) {
return CheckStatus.ERROR;
}
return CheckStatus.OK;
}
private void render(Collection<? extends Polygon> polygons) {
Platform.runLater(this::clearGeometryTrees);
currentTriGeom = TriangulatedGeometry.of(polygons);
errVisitor.setGeometry(currentTriGeom);
setupRenderState();
}
private void clearGeometryTrees() {
highlightController.clearHighlights();
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getPolygonsView().getRoot().getChildren().clear();
mainWindow.getEdgeView().getRoot().getChildren().clear();
mainWindow.getVertexView().getRoot().getChildren().clear();
}
public void renderBuildings(List<Building> objects) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (Building b : objects) {
collectPolygons(polygons, b);
for (BuildingPart bp : b.getBuildingParts()) {
collectPolygons(polygons, bp);
}
}
currentTriGeom = TriangulatedGeometry.of(polygons);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
}
public void render(CityDoctorModel model) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
});
Thread t = new Thread(() -> {
currentTriGeom = TriangulatedGeometry.of(model, lodFilters);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(model));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
}
public void renderVegetation(List<Vegetation> vegetation) {
renderCityObjects(vegetation, Color.LIGHTGREEN);
}
public void renderTransportation(List<TransportationObject> transportation) {
renderCityObjects(transportation, Color.YELLOW);
}
public void renderBridges(List<BridgeObject> bridges) {
renderCityObjects(bridges, Color.CORAL);
}
public void renderWater(List<WaterObject> water) {
renderCityObjects(water, Color.LIGHTSKYBLUE);
}
public void renderTerrain(List<CityObject> land) {
renderLandObjects(land, Color.BROWN);
}
private void renderLandObjects(List<CityObject> cos, Color baseColor) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (CityObject co : cos) {
addPolygons(co, polygons);
if (co instanceof ReliefObject) {
ReliefObject relief = (ReliefObject) co;
for (TinObject tin : relief.getComponents()) {
addPolygons(tin, polygons);
}
}
}
currentTriGeom = TriangulatedGeometry.of(polygons, baseColor);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> Platform.runLater(() -> loadingDialog.hide()));
t.start();
};
refresher.run();
}
public void renderCityObjects(List<? extends CityObject> cos, Color baseColor) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (CityObject co : cos) {
addPolygons(co, polygons);
}
currentTriGeom = TriangulatedGeometry.of(polygons, baseColor);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> Platform.runLater(() -> loadingDialog.hide()));
t.start();
};
refresher.run();
}
private void collectPolygons(Set<ConcretePolygon> polygons, AbstractBuilding ab) {
addPolygons(ab, polygons);
for (Installation bi : ab.getBuildingInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
for (BoundarySurface bs : ab.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
private void addConcretePolygons(Set<ConcretePolygon> polygons, Geometry geom) {
for (Polygon p : geom.getPolygons()) {
polygons.add(p.getOriginal());
}
}
private void setupRenderState() {
currentTriGeom.setCullFace(currentCulling);
currentTriGeom.setDrawMode(currentDrawMode);
Platform.runLater(() -> {
mainWindow.getMeshGroup().getChildren().clear();
mainWindow.getMeshGroup().getChildren().addAll(currentTriGeom.getMeshes());
});
mainWindow.getGridButton().setDisable(false);
mainWindow.getCullingButton().setDisable(false);
}
public void showWireFrame(boolean show) {
if (currentTriGeom != null) {
if (show) {
currentDrawMode = DrawMode.LINE;
} else {
currentDrawMode = DrawMode.FILL;
}
currentTriGeom.setDrawMode(currentDrawMode);
}
}
public void enableCulling(boolean enable) {
if (currentTriGeom != null) {
if (enable) {
currentCulling = CullFace.BACK;
} else {
currentCulling = CullFace.NONE;
}
currentTriGeom.setCullFace(currentCulling);
}
}
public void clearCurrentRender() {
// don't render anything
Platform.runLater(() -> {
mainWindow.getMeshGroup().getChildren().clear();
clearGeometryTrees();
});
}
public void highlight(Polygon p) {
highlightController.highlight(p, currentTriGeom);
}
public void highlight(LinearRing lr) {
highlightController.highlight(lr, currentTriGeom);
}
public void highlight(Edge e) {
highlightController.highlight(e, currentTriGeom);
}
public void highlight(Vertex v) {
highlightController.highlight(v, currentTriGeom);
}
public void highlight(List<LinearRing> highlightedRings) {
highlightController.highlight(highlightedRings, currentTriGeom);
}
public void highlightEdges(List<Edge> edges) {
highlightController.highlightEdges(edges, currentTriGeom);
}
public void highlightPolygons(List<List<Polygon>> components) {
highlightController.highlightPolygons(components, currentTriGeom);
}
public void addHighlight(Vertex vertex, Color c) {
highlightController.addHighlight(vertex, currentTriGeom, c);
}
public void highlight(CheckError err) {
err.accept(errVisitor);
}
public void refresh() {
if (refresher != null) {
refresher.run();
}
}
public void updateErrors() {
if (errorUpdater != null) {
errorUpdater.run();
}
}
public void clearHighlights() {
highlightController.clearHighlights();
}
public void addHighlight(Polygon p) {
highlightController.addHighlight(p, currentTriGeom);
}
public void addHighlight(Triangle3d t) {
highlightController.highlight(t, currentTriGeom);
}
public void reset() {
errorUpdater = null;
refresher = null;
}
private static final Logger logger = LogManager.getLogger(Renderer.class);
private TriangulatedGeometry currentTriGeom;
private Geometry currentGeometry;
private CullFace currentCulling = CullFace.NONE;
private DrawMode currentDrawMode = DrawMode.FILL;
private MainWindow mainWindow;
private HighlightController highlightController;
private ListErrorVisitor errVisitor;
private LoadingInfoDialog loadingDialog;
private List<ViewFilter> lodFilters = new ArrayList<>();
private ViewFilter roofFilter;
private Runnable refresher;
private Runnable errorUpdater;
public Renderer(MainWindow mainWindow, HighlightController highlightController) throws IOException {
this.mainWindow = mainWindow;
this.highlightController = highlightController;
loadingDialog = new LoadingInfoDialog(mainWindow.getMainStage());
errVisitor = new ListErrorVisitor(highlightController);
setupLodFilters();
}
private void setupLodFilters() {
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD1;
}
});
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD2;
}
});
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD3;
}
});
lodFilters.add(new ViewFilter() {
@Override
protected boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD0;
}
});
lodFilters.add(new ViewFilter() {
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
return geom.getLod() == Lod.LOD4;
}
});
roofFilter = new ViewFilter() {
@Override
public boolean allowedToUse(CityObject co, Geometry geom) {
if (!this.isEnabled()) {
return true;
}
return this.useGeometry(co, geom);
}
@Override
public boolean useGeometry(CityObject co, Geometry geom) {
if (co instanceof BoundarySurface bs) {
return bs.getType() != BoundarySurfaceType.ROOF;
}
return true;
}
};
roofFilter.setEnable(false);
}
public void enableLod1() {
lodFilters.get(0).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod1() {
lodFilters.get(0).disable();
if (refresher != null) {
refresher.run();
}
}
public void enableLod2() {
lodFilters.get(1).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod2() {
lodFilters.get(1).disable();
if (refresher != null) {
refresher.run();
}
}
public void enableLod3() {
lodFilters.get(2).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod3() {
lodFilters.get(2).disable();
if (refresher != null) {
refresher.run();
}
}
public void enableLod4() {
lodFilters.get(3).enable();
if (refresher != null) {
refresher.run();
}
}
public void disableLod4() {
lodFilters.get(3).disable();
if (refresher != null) {
refresher.run();
}
}
public void hideRoofs() {
roofFilter.setEnable(true);
if (refresher != null) {
refresher.run();
}
}
public void showRoofs() {
roofFilter.setEnable(false);
if (refresher != null) {
refresher.run();
}
}
public void render(Building building) {
refresher = () -> {
Set<ConcretePolygon> setupBuildingPolygons = setupBuildingPolygons(building);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingPolygons));
render(setupBuildingPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(building);
errorUpdater.run();
addGenericAttributesToView(building);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingPolygons(Building b) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(b, polygons);
for (BoundarySurface bs : b.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
for (Installation bi : b.getBuildingInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
for (BuildingRoom br : b.getBuildingRooms()) {
addPolygons(br, polygons);
for (BoundarySurface bs : br.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BuildingRoomFurniture brf : b.getBuildingRoomFurnitureList()) {
addPolygons(brf, polygons);
for (BoundarySurface bs : brf.getBoundarySurfaceList()) {
addPolygons(bs, polygons);
}
}
for (BuildingPart bp : b.getBuildingParts()) {
polygons.addAll(setupBuildingPartPolygons(bp));
}
return polygons;
}
public void render(BuildingPart bp) {
refresher = () -> {
Set<ConcretePolygon> setupBuildingPartPolygons = setupBuildingPartPolygons(bp);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingPartPolygons));
render(setupBuildingPartPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bp);
errorUpdater.run();
addGenericAttributesToView(bp);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingPartPolygons(BuildingPart bp) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bp, polygons);
for (BoundarySurface bs : bp.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
for (Installation bi : bp.getBuildingInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
return polygons;
}
public void render(AbstractRoom room) {
refresher = () -> {
Set<ConcretePolygon> setupRoomPolygons = setupRoomPolygons(room);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupRoomPolygons));
render(setupRoomPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(room);
errorUpdater.run();
addGenericAttributesToView(room);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupRoomPolygons(AbstractRoom room) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(room, polygons);
for (BoundarySurface bs : room.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(AbstractFurniture furniture) {
refresher = () -> {
Set<ConcretePolygon> setupFurniturePolygons = setupFurniturePolygons(furniture);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupFurniturePolygons));
render(setupFurniturePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(furniture);
errorUpdater.run();
addGenericAttributesToView(furniture);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupFurniturePolygons(AbstractFurniture furniture) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(furniture, polygons);
for (BoundarySurface bs : furniture.getBoundarySurfaceList()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(Storey storey) {
refresher = () -> {
Set<ConcretePolygon> setupStoreyPolygons = setupStoreyPolygons(storey);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupStoreyPolygons));
render(setupStoreyPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(storey);
errorUpdater.run();
addGenericAttributesToView(storey);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupStoreyPolygons(Storey storey) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(storey, polygons);
for (BoundarySurface bs : storey.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(BuildingUnit buildingUnit) {
refresher = () -> {
Set<ConcretePolygon> setupBuildingUnitPolgons = setupBuildingUnitPolygons(buildingUnit);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBuildingUnitPolgons));
render(setupBuildingUnitPolgons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(buildingUnit);
errorUpdater.run();
addGenericAttributesToView(buildingUnit);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingUnitPolygons(BuildingUnit buildingUnit) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(buildingUnit, polygons);
for (BoundarySurface bs : buildingUnit.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(Tunnel tunnel) {
refresher = () -> {
Set<ConcretePolygon> setupTunnelPolygons = setupTunnelPolygons(tunnel);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupTunnelPolygons));
render(setupTunnelPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(tunnel);
errorUpdater.run();
addGenericAttributesToView(tunnel);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupTunnelPolygons(Tunnel tunnel) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(tunnel, polygons);
for (BoundarySurface bs : tunnel.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
for (Installation ti : tunnel.getTunnelInstallations()) {
addPolygons(ti, polygons);
for (BoundarySurface bs : ti.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
for (TunnelHollow th : tunnel.getTunnelHollows()) {
addPolygons(th, polygons);
for (BoundarySurface bs : th.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (TunnelFurniture tf : tunnel.getTunnelFurnitureList()) {
addPolygons(tf, polygons);
for (BoundarySurface bs : tf.getBoundarySurfaceList()) {
addPolygons(bs, polygons);
}
}
for (TunnelPart tp : tunnel.getTunnelParts()) {
polygons.addAll(setupTunnelPartPolygons(tp));
}
return polygons;
}
private Set<ConcretePolygon> setupTunnelPartPolygons(TunnelPart tp) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(tp, polygons);
for (BoundarySurface bs : tp.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
for (Installation bi : tp.getTunnelInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
}
return polygons;
}
public void render(BridgeObject bridge) {
refresher = () -> {
Set<ConcretePolygon> setupBridgePolygons = setupBridgePolygons(bridge);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBridgePolygons));
render(setupBridgePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bridge);
errorUpdater.run();
addGenericAttributesToView(bridge);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBridgePolygons(BridgeObject bridge) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bridge, polygons);
for (BoundarySurface bs : bridge.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
for (BridgeConstructiveElement consElement : bridge.getConstructiveElements()) {
addPolygons(consElement, polygons);
for (BoundarySurface bs : consElement.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (Installation inst : bridge.getBridgeInstallations()) {
addPolygons(inst, polygons);
for (BoundarySurface bs : inst.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
return polygons;
}
public void render(CityObject co) {
refresher = () -> {
Set<ConcretePolygon> setupCityObjectPolygons = setupCityObjectPolygons(co);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(co);
errorUpdater.run();
addGenericAttributesToView(co);
});
};
refresher.run();
}
public void render(ReliefObject relief) {
refresher = () -> {
Set<ConcretePolygon> setupCityObjectPolygons = setupReliefPolygons(relief);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(relief);
errorUpdater.run();
addGenericAttributesToView(relief);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupReliefPolygons(ReliefObject relief) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(relief, polygons);
for (TinObject tin : relief.getComponents()) {
addPolygons(tin, polygons);
}
return polygons;
}
public void render(Installation bi) {
refresher = () -> {
Set<ConcretePolygon> setupCityObjectPolygons = setupBuildingInstallationPolygons(bi);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityObjectPolygons));
render(setupCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bi);
errorUpdater.run();
addGenericAttributesToView(bi);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBuildingInstallationPolygons(Installation bi) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(CityFurniture cf) {
refresher = () -> {
Set<ConcretePolygon> setupCityFurniturePolygons = setupCityFurniturePolygons(cf);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupCityFurniturePolygons));
render(setupCityFurniturePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(cf);
errorUpdater.run();
addGenericAttributesToView(cf);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupCityFurniturePolygons(CityFurniture cf) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(cf, polygons);
return polygons;
}
public void render(GenericCityObject gco) {
refresher = () -> {
Set<ConcretePolygon> setupGenericCityObjectPolygons = setupGenericCityObjectPolygons(gco);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupGenericCityObjectPolygons));
render(setupGenericCityObjectPolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(gco);
errorUpdater.run();
addGenericAttributesToView(gco);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupGenericCityObjectPolygons(GenericCityObject gco) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(gco, polygons);
for (BoundarySurface bs : gco.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
}
return polygons;
}
public void render(BoundarySurface bs) {
refresher = () -> {
Set<ConcretePolygon> setupBoundarySurfacePolygons = setupBoundarySurfacePolygons(bs);
mainWindow.zoomOutForBoundingBox(BoundingBox.of(setupBoundarySurfacePolygons));
render(setupBoundarySurfacePolygons);
Platform.runLater(() -> {
errorUpdater = () -> displayErrors(bs);
errorUpdater.run();
addGenericAttributesToView(bs);
});
};
refresher.run();
}
private Set<ConcretePolygon> setupBoundarySurfacePolygons(BoundarySurface bs) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(bs, polygons);
for (Opening op : bs.getOpenings()) {
addPolygons(op, polygons);
}
return polygons;
}
private Set<ConcretePolygon> setupCityObjectPolygons(CityObject co) {
Set<ConcretePolygon> polygons = new HashSet<>();
addPolygons(co, polygons);
return polygons;
}
private void addPolygons(CityObject co, Set<ConcretePolygon> polygons) {
for (Geometry geom : co.getGeometries()) {
boolean used = false;
for (ViewFilter filter : lodFilters) {
if (filter.allowedToUse(co, geom)) {
used = true;
break;
}
}
if (used) {
if (roofFilter.allowedToUse(co, geom)) {
addConcretePolygons(polygons, geom);
}
}
}
}
private void displayErrors(Checkable c) {
if (!c.isValidated()) {
return;
}
List<CheckError> errors = new ArrayList<>();
c.collectContainedErrors(errors);
// filter out duplicate errors (polygon can be contained in multiple geometries)
Set<CheckError> errorSet = new HashSet<>(errors);
for (CheckError err : errorSet) {
ErrorNode node = new ErrorNode(err);
TreeItem<Renderable> errItem = new TreeItem<>(node);
ErrorItemVisitor visitor = new ErrorItemVisitor(errItem);
err.accept(visitor);
mainWindow.getErrorTree().getRoot().getChildren().add(errItem);
}
}
public void render(Geometry geom) {
refresher = () -> {
Platform.runLater(this::clearGeometryTrees);
currentTriGeom = TriangulatedGeometry.of(geom);
if (geom.getEdges() == null && currentGeometry != null) {
// if there are no edges available low memory mode is enabled
// clear the old geometry of all meta information
currentGeometry.clearMetaInformation();
}
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
if (geom.getEdges() == null) {
// create edges and vertices so they can be listed in the gui
geom.prepareForChecking();
// remember the geometry, so it can be cleared if another is displayed
currentGeometry = geom;
}
addGeometryDataToView(geom);
addGenericAttributesToView(geom.getParent());
mainWindow.zoomOutForBoundingBox(BoundingBox.of(geom.getPolygons()));
errorUpdater = () -> displayErrors(geom);
errorUpdater.run();
});
};
refresher.run();
}
private void addGeometryDataToView(Geometry geom) {
for (Polygon p : geom.getPolygons()) {
addPolygonToView(p);
}
for (Edge e : geom.getEdges()) {
addEdgeToView(e);
}
for (Vertex v : geom.getVertices()) {
addVertexToView(v);
}
}
private void addGenericAttributesToView(CityObject co) {
clearAttributes();
for (GenericAttribute gen : co.getGenericAttributes()) {
addGenericAttributeToView(gen);
}
}
private void addVertexToView(Vertex v) {
Renderable cf = new VertexNode(v);
TreeItem<Renderable> ti = new TreeItem<>(cf);
mainWindow.getVertexView().getRoot().getChildren().add(ti);
}
private void addEdgeToView(Edge e) {
EdgeNode cf = new EdgeNode(e);
TreeItem<Renderable> ti = new TreeItem<>(cf);
mainWindow.getEdgeView().getRoot().getChildren().add(ti);
}
private void addPolygonToView(Polygon p) {
CheckStatus cs = determineCheckStatus(p);
PolygonNode cf = new PolygonNode(p, cs);
TreeItem<Renderable> ti = new TreeItem<>(cf);
mainWindow.getPolygonsView().getRoot().getChildren().add(ti);
// add linear rings
CheckStatus csRing = determineCheckStatus(p.getExteriorRing());
Renderable ccRing = new LinearRingNode(p.getExteriorRing(), csRing);
TreeItem<Renderable> tiRing = new TreeItem<>(ccRing);
ti.getChildren().add(tiRing);
for (Vertex v : p.getExteriorRing().getVertices()) {
VertexNode vn = new VertexNode(v);
TreeItem<Renderable> tiV = new TreeItem<>(vn);
tiRing.getChildren().add(tiV);
}
for (LinearRing lr : p.getInnerRings()) {
CheckStatus csInteriorRing = determineCheckStatus(lr);
Renderable ccInteriorRing = new LinearRingNode(lr, csInteriorRing);
TreeItem<Renderable> tiInteriorRing = new TreeItem<>(ccInteriorRing);
ti.getChildren().add(tiInteriorRing);
for (Vertex v : lr.getVertices()) {
VertexNode vn = new VertexNode(v);
TreeItem<Renderable> tiV = new TreeItem<>(vn);
tiRing.getChildren().add(tiV);
}
}
}
private void addGenericAttributeToView(GenericAttribute gen) {
GenericAttributeNode genNode = new GenericAttributeNode(gen);
TreeItem<Renderable> ti = new TreeItem<>(genNode);
mainWindow.getAttributeView().getRoot().getChildren().add(ti);
}
private CheckStatus determineCheckStatus(Checkable c) {
if (!c.isValidated()) {
return CheckStatus.NOT_CHECKED;
}
if (c.containsAnyError()) {
return CheckStatus.ERROR;
}
return CheckStatus.OK;
}
private void render(Collection<? extends Polygon> polygons) {
Platform.runLater(this::clearGeometryTrees);
currentTriGeom = TriangulatedGeometry.of(polygons);
errVisitor.setGeometry(currentTriGeom);
setupRenderState();
}
private void clearGeometryTrees() {
highlightController.clearHighlights();
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getPolygonsView().getRoot().getChildren().clear();
mainWindow.getEdgeView().getRoot().getChildren().clear();
mainWindow.getVertexView().getRoot().getChildren().clear();
}
public void renderBuildings(List<Building> objects) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (Building b : objects) {
collectBuildingPolygons(polygons, b);
for (BuildingPart bp : b.getBuildingParts()) {
collectBuildingPolygons(polygons, bp);
}
}
currentTriGeom = TriangulatedGeometry.of(polygons);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
}
public void render(CityDoctorModel model) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
currentTriGeom = TriangulatedGeometry.of(model, lodFilters);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(model));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
}
public void renderVegetation(List<Vegetation> vegetation) {
renderCityObjects(vegetation, Color.LIGHTGREEN);
}
public void renderTransportation(List<TransportationObject> transportation) {
renderCityObjects(transportation, Color.YELLOW);
}
public void renderBridges(List<BridgeObject> bridges) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (BridgeObject b : bridges) {
collectBridgePolygons(polygons, b);
for (BridgeObject bp : b.getParts()) {
collectBridgePolygons(polygons, bp);
}
}
currentTriGeom = TriangulatedGeometry.of(polygons, Color.CORAL);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
}
public void renderWater(List<WaterObject> water) {
renderCityObjects(water, Color.LIGHTSKYBLUE);
}
public void renderCityFurniture(List<CityFurniture> cityFurniture) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (CityFurniture cf : cityFurniture) {
collectCityFurniture(polygons, cf);
}
currentTriGeom = TriangulatedGeometry.of(polygons, Color.BLUEVIOLET);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
}
public void renderTerrain(List<CityObject> land) {
renderLandObjects(land, Color.BROWN);
}
private void renderLandObjects(List<CityObject> cos, Color baseColor) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (CityObject co : cos) {
addPolygons(co, polygons);
if (co instanceof ReliefObject) {
ReliefObject relief = (ReliefObject) co;
for (TinObject tin : relief.getComponents()) {
addPolygons(tin, polygons);
}
}
}
currentTriGeom = TriangulatedGeometry.of(polygons, baseColor);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> Platform.runLater(() -> loadingDialog.hide()));
t.start();
};
refresher.run();
}
public void renderCityObjects(List<? extends CityObject> cos, Color baseColor) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (CityObject co : cos) {
addPolygons(co, polygons);
}
currentTriGeom = TriangulatedGeometry.of(polygons, baseColor);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> Platform.runLater(() -> loadingDialog.hide()));
t.start();
};
refresher.run();
}
public void renderTunnels(List<Tunnel> objects) {
errorUpdater = null;
refresher = () -> {
Platform.runLater(() -> {
loadingDialog.show();
clearGeometryTrees();
clearAttributes();
});
Thread t = new Thread(() -> {
Set<ConcretePolygon> polygons = new HashSet<>();
for (Tunnel tunnel : objects) {
collectTunnelPolygons(polygons, tunnel);
for (TunnelPart tp : tunnel.getTunnelParts()) {
collectTunnelPolygons(polygons, tp);
}
}
currentTriGeom = TriangulatedGeometry.of(polygons);
errVisitor.setGeometry(currentTriGeom);
Platform.runLater(() -> {
setupRenderState();
mainWindow.zoomOutForBoundingBox(BoundingBox.of(polygons));
loadingDialog.hide();
});
});
t.setUncaughtExceptionHandler((thread, e) -> {
Platform.runLater(() -> loadingDialog.hide());
logger.catching(e);
});
t.start();
};
refresher.run();
}
private void collectBuildingPolygons(Set<ConcretePolygon> polygons, AbstractBuilding ab) {
addPolygons(ab, polygons);
for (Installation bi : ab.getBuildingInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
for (BuildingRoom br : ab.getBuildingRooms()) {
addPolygons(br, polygons);
for (BoundarySurface bs : br.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BoundarySurface bs : ab.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
private void collectBridgePolygons(Set<ConcretePolygon> polygons, BridgeObject br) {
addPolygons(br, polygons);
for (Installation bi : br.getBridgeInstallations()) {
addPolygons(bi, polygons);
for (BoundarySurface bs : bi.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
for (BridgeRoom r : br.getBridgeRooms()) {
addPolygons(r, polygons);
for (BoundarySurface bs : r.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BridgeConstructiveElement bce : br.getConstructiveElements()) {
addPolygons(bce, polygons);
for (BoundarySurface bs : bce.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BoundarySurface bs : br.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
public void collectTunnelPolygons(Set<ConcretePolygon> polygons, AbstractTunnel at) {
addPolygons(at, polygons);
for (Installation ti : at.getTunnelInstallations()) {
addPolygons(ti, polygons);
for (BoundarySurface bs : ti.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
for (TunnelHollow th : at.getTunnelHollows()) {
addPolygons(th, polygons);
for (BoundarySurface bs : th.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (TunnelConstructiveElement tce : at.getTunnelConstructiveElements()) {
addPolygons(tce, polygons);
for (BoundarySurface bs : tce.getBoundarySurfaces()) {
addPolygons(bs, polygons);
}
}
for (BoundarySurface bs : at.getBoundarySurfaces()) {
addPolygons(bs, polygons);
for (Opening o : bs.getOpenings()) {
addPolygons(o, polygons);
}
}
}
private void collectCityFurniture(Set<ConcretePolygon> polygons, CityFurniture cf) {
addPolygons(cf, polygons);
}
private void addConcretePolygons(Set<ConcretePolygon> polygons, Geometry geom) {
for (Polygon p : geom.getPolygons()) {
if (p.getOriginal().getPartOfSurface() != null &&
!roofFilter.allowedToUse(p.getOriginal().getPartOfSurface(), p.getParent())) continue;
polygons.add(p.getOriginal());
}
}
private void setupRenderState() {
currentTriGeom.setCullFace(currentCulling);
currentTriGeom.setDrawMode(currentDrawMode);
Platform.runLater(() -> {
mainWindow.getMeshGroup().getChildren().clear();
mainWindow.getMeshGroup().getChildren().addAll(currentTriGeom.getMeshes());
});
mainWindow.getGridButton().setDisable(false);
mainWindow.getCullingButton().setDisable(false);
}
public void showWireFrame(boolean show) {
if (currentTriGeom != null) {
if (show) {
currentDrawMode = DrawMode.LINE;
} else {
currentDrawMode = DrawMode.FILL;
}
currentTriGeom.setDrawMode(currentDrawMode);
}
}
public void enableCulling(boolean enable) {
if (currentTriGeom != null) {
if (enable) {
currentCulling = CullFace.BACK;
} else {
currentCulling = CullFace.NONE;
}
currentTriGeom.setCullFace(currentCulling);
}
}
public void clearCurrentRender() {
// don't render anything
Platform.runLater(() -> {
mainWindow.getMeshGroup().getChildren().clear();
clearGeometryTrees();
clearAttributes();
});
}
private void clearAttributes() {
mainWindow.getAttributeView().getRoot().getChildren().clear();
}
public void highlight(Polygon p) {
highlightController.highlight(p, currentTriGeom);
}
public void highlight(LinearRing lr) {
highlightController.highlight(lr, currentTriGeom);
}
public void highlight(Edge e) {
highlightController.highlight(e, currentTriGeom);
}
public void highlight(Vertex v) {
highlightController.highlight(v, currentTriGeom);
}
public void highlight(List<LinearRing> highlightedRings) {
highlightController.highlight(highlightedRings, currentTriGeom);
}
public void highlightEdges(List<Edge> edges) {
highlightController.highlightEdges(edges, currentTriGeom);
}
public void highlightPolygons(List<List<Polygon>> components) {
highlightController.highlightPolygons(components, currentTriGeom);
}
public void addHighlight(Vertex vertex, Color c) {
highlightController.addHighlight(vertex, currentTriGeom, c);
}
public void highlight(CheckError err) {
err.accept(errVisitor);
}
public void refresh() {
if (refresher != null) {
refresher.run();
}
}
public void updateErrors() {
if (errorUpdater != null) {
errorUpdater.run();
}
}
public void clearHighlights() {
highlightController.clearHighlights();
}
public void addHighlight(Polygon p) {
highlightController.addHighlight(p, currentTriGeom);
}
public void addHighlight(Triangle3d t) {
highlightController.highlight(t, currentTriGeom);
}
public void reset() {
errorUpdater = null;
refresher = null;
}
}
package de.hft.stuttgart.citydoctor2.gui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.Installation;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.datastructure.OpeningType;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.*;
import de.hft.stuttgart.citydoctor2.gui.filter.ViewFilter;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import de.hft.stuttgart.citydoctor2.math.UnitVector3d;
......@@ -24,11 +9,9 @@ import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.shape.VertexFormat;
import javafx.scene.shape.*;
import java.util.*;
public class TriangulatedGeometry {
......@@ -105,11 +88,13 @@ public class TriangulatedGeometry {
triGeom.movedBy = triGeom.findCenter(points);
addPolygonDataFromBuildings(model.getBuildings(), triGeom, filters);
addPolygonDataFromCityObjects(model.getBridges(), triGeom, Color.CORAL, filters);
addPolygonDataFromBridges(model.getBridges(), triGeom, filters);
addPolygonDataFromCityObjects(model.getLand(), triGeom, Color.BROWN, filters);
addPolygonDataFromCityObjects(model.getTransportation(), triGeom, Color.YELLOW, filters);
addPolygonDataFromCityObjects(model.getVegetation(), triGeom, Color.LIGHTGREEN, filters);
addPolygonDataFromCityObjects(model.getWater(), triGeom, Color.LIGHTSKYBLUE, filters);
addPolygonDataFromCityObjects(model.getCityFurniture(), triGeom, Color.BLUEVIOLET, filters);
addPolygonDataFromCityObjects(model.getGenericCityObjects(), triGeom, Color.DARKGRAY, filters);
return triGeom;
}
......@@ -130,6 +115,23 @@ public class TriangulatedGeometry {
}
}
private static void addPolygonDataFromBridges(List<BridgeObject> bridges, TriangulatedGeometry triGeom, List<ViewFilter> filters) {
for (BridgeObject b : bridges) {
addPolygonData(b, triGeom, Color.CORAL, filters);
addPolygonDataFromBoundarySurfaces(b.getBoundarySurfaces(), triGeom, filters);
addPolygonDataFromCityObjects(b.getBridgeInstallations(), triGeom, Color.CORAL, filters);
for (Installation bi : b.getBridgeInstallations()) {
addPolygonData(bi, triGeom, Color.CORAL, filters);
addPolygonDataFromCityObjects(bi.getBoundarySurfaces(), triGeom, Color.CORAL, filters);
}
for (BridgeConstructiveElement bce : b.getConstructiveElements()) {
addPolygonData(bce, triGeom, Color.CORAL, filters);
addPolygonDataFromCityObjects(bce.getBoundarySurfaces(), triGeom, Color.CORAL, filters);
}
addPolygonDataFromBridges(b.getParts(), triGeom, filters);
}
}
private static void addPolygonDataFromBoundarySurfaces(List<BoundarySurface> boundarySurfaces,
TriangulatedGeometry triGeom, List<ViewFilter> filters) {
for (BoundarySurface bs : boundarySurfaces) {
......
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingUnit;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
import java.util.List;
public class AllBuildingUnitsNode extends Renderable{
private final List<BuildingUnit> buildingUnits;
public AllBuildingUnitsNode(List<BuildingUnit> buildingUnits) {
this.buildingUnits = buildingUnits;
}
@Override
public String getText() {
return "Building Units";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
@Override
public void refreshTextColor() {
boolean wasChecked = false;
for (BuildingUnit buildingUnit : buildingUnits) {
if (buildingUnit.isValidated()) {
wasChecked = true;
if (buildingUnit.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.CityFurniture;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
import java.util.List;
public class AllCityFurnitureNode extends Renderable{
private final List<CityFurniture> cityFurnitures;
public AllCityFurnitureNode(List<CityFurniture> cityFurnitures) {
this.cityFurnitures = cityFurnitures;
}
@Override
public void refreshTextColor() {
// no color changes
}
@Override
public String getText() {
return "CityFurniture";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
renderer.renderCityFurniture(cityFurnitures);
}
}
package de.hft.stuttgart.citydoctor2.gui.tree;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractFurniture;
import de.hft.stuttgart.citydoctor2.gui.CheckStatus;
import de.hft.stuttgart.citydoctor2.gui.Renderer;
import java.util.List;
public class AllFurnitureNode extends Renderable{
private final List<? extends AbstractFurniture> furniture;
public AllFurnitureNode(List<? extends AbstractFurniture> furniture) {
this.furniture = furniture;
}
@Override
public String getText() {
return "Furniture";
}
@Override
public void visit(Renderer renderer) {
renderer.clearCurrentRender();
}
@Override
public void refreshTextColor() {
boolean wasChecked = false;
for (AbstractFurniture af : furniture) {
if (af.isValidated()) {
wasChecked = true;
if (af.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
}
......@@ -16,13 +16,21 @@ public class AllOpeningsNode extends Renderable {
@Override
public void refreshTextColor() {
for (Opening bp : openings) {
if (bp.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
boolean wasChecked = false;
for (Opening op : openings) {
if (op.isValidated()) {
wasChecked = true;
if (op.containsAnyError()) {
setStatus(CheckStatus.ERROR);
return;
}
}
}
setStatus(CheckStatus.OK);
if (wasChecked) {
setStatus(CheckStatus.OK);
} else {
setStatus(CheckStatus.NOT_CHECKED);
}
}
@Override
......
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