Commit 297334ab authored by Matthias Betz's avatar Matthias Betz
Browse files

Add design spec for OpenGL 3D view



Replaces the JavaFX SubScene/MeshView rendering with an OpenGL viewport
(openglfx-jogl, GL 3.3 core), single global VBO, GPU color-picking, and
baked fake shading. Windows/Linux targets; JavaFX 3D path removed.

Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
parents 5c2847ef bd40b396
...@@ -71,7 +71,7 @@ public class OuterEdgeCheckTest { ...@@ -71,7 +71,7 @@ public class OuterEdgeCheckTest {
expectedErrorEdges.add(new Vertex(1.0, 0.0, 3.5)); expectedErrorEdges.add(new Vertex(1.0, 0.0, 3.5));
expectedErrorEdges.add(new Vertex(2.0, 0.0, 3.0)); expectedErrorEdges.add(new Vertex(2.0, 0.0, 3.0));
Geometry geom = m.getBuildings().get(0).getGeometries().get(0); Geometry geom = m.getBuildings().toList().get(0).getGeometries().get(0);
CheckResult cr = geom.getCheckResult(CheckId.C_GE_S_NOT_CLOSED); CheckResult cr = geom.getCheckResult(CheckId.C_GE_S_NOT_CLOSED);
Assert.assertEquals(ResultStatus.ERROR, cr.getResultStatus()); Assert.assertEquals(ResultStatus.ERROR, cr.getResultStatus());
} }
......
...@@ -31,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckResult; ...@@ -31,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.ErrorId; import de.hft.stuttgart.citydoctor2.check.ErrorId;
import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.checks.geometry.PlanarCheck;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon; import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException;
...@@ -106,7 +107,7 @@ public class PlanarTest { ...@@ -106,7 +107,7 @@ public class PlanarTest {
@Test @Test
public void testPlanarPolygon3() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testPlanarPolygon3() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml");
for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) {
CheckResult cr = p.getCheckResult(CheckId.C_GE_P_NON_PLANAR); CheckResult cr = p.getCheckResult(CheckId.C_GE_P_NON_PLANAR);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
} }
...@@ -142,7 +143,7 @@ public class PlanarTest { ...@@ -142,7 +143,7 @@ public class PlanarTest {
public void testPlanarPolygon6() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testPlanarPolygon6() throws CityGmlParseException, IOException, InvalidGmlFileException {
Map<String, Map<String, String>> paramMap = new HashMap<>(); Map<String, Map<String, String>> paramMap = new HashMap<>();
Map<String, String> parameter = new HashMap<>(); Map<String, String> parameter = new HashMap<>();
parameter.put("type", "both"); parameter.put(PlanarCheck.TYPE, PlanarCheck.BOTH);
parameter.put("distanceTolerance", "0.5"); parameter.put("distanceTolerance", "0.5");
paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter); paramMap.put(RequirementId.R_GE_P_NON_PLANAR.toString(), parameter);
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0002-T0002.gml", paramMap); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0002-T0002.gml", paramMap);
......
...@@ -43,7 +43,7 @@ public class PolygonIntersectingRingsTest { ...@@ -43,7 +43,7 @@ public class PolygonIntersectingRingsTest {
@Test @Test
public void testSelfIntPolygon1() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testSelfIntPolygon1() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml");
for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) {
CheckResult cr = p.getCheckResult(CheckId.C_GE_P_INTERSECTING_RINGS); CheckResult cr = p.getCheckResult(CheckId.C_GE_P_INTERSECTING_RINGS);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
} }
...@@ -52,7 +52,7 @@ public class PolygonIntersectingRingsTest { ...@@ -52,7 +52,7 @@ public class PolygonIntersectingRingsTest {
@Test @Test
public void testSelfIntPolygon2() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testSelfIntPolygon2() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-PO-0001-T0002.gml");
for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) {
CheckResult cr = p.getCheckResult(CheckId.C_GE_P_INTERSECTING_RINGS); CheckResult cr = p.getCheckResult(CheckId.C_GE_P_INTERSECTING_RINGS);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
} }
......
...@@ -91,7 +91,7 @@ public class RingClosedCheckTest extends TestCleanUp { ...@@ -91,7 +91,7 @@ public class RingClosedCheckTest extends TestCleanUp {
@Test @Test
public void testClosedRing3() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testClosedRing3() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml");
for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) {
LinearRing r = p.getExteriorRing(); LinearRing r = p.getExteriorRing();
CheckResult cr = r.getCheckResult(CheckId.C_GE_R_NOT_CLOSED); CheckResult cr = r.getCheckResult(CheckId.C_GE_R_NOT_CLOSED);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
...@@ -111,7 +111,7 @@ public class RingClosedCheckTest extends TestCleanUp { ...@@ -111,7 +111,7 @@ public class RingClosedCheckTest extends TestCleanUp {
@Test @Test
public void testClosedRing5() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testClosedRing5() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-LR-0003-T0002.gml", 1); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-LR-0003-T0002.gml", 1);
for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) {
LinearRing r = p.getExteriorRing(); LinearRing r = p.getExteriorRing();
CheckResult cr = r.getCheckResult(CheckId.C_GE_R_NOT_CLOSED); CheckResult cr = r.getCheckResult(CheckId.C_GE_R_NOT_CLOSED);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
......
...@@ -101,7 +101,7 @@ public class RingSelfIntersectionTest { ...@@ -101,7 +101,7 @@ public class RingSelfIntersectionTest {
@Test @Test
public void testSelfIntPoints4() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testSelfIntPoints4() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml");
for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) {
LinearRing r = p.getExteriorRing(); LinearRing r = p.getExteriorRing();
CheckResult cr = r.getCheckResult(CheckId.C_GE_R_SELF_INTERSECTION); CheckResult cr = r.getCheckResult(CheckId.C_GE_R_SELF_INTERSECTION);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
...@@ -131,7 +131,7 @@ public class RingSelfIntersectionTest { ...@@ -131,7 +131,7 @@ public class RingSelfIntersectionTest {
@Test @Test
public void testSelfIntPoints7() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testSelfIntPoints7() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-LR-0004-T0005.gml", 2); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-LR-0004-T0005.gml", 2);
for (Polygon p : c.getBuildings().get(0).getGeometries().get(0).getPolygons()) { for (Polygon p : c.getBuildings().toList().get(0).getGeometries().get(0).getPolygons()) {
LinearRing r = p.getExteriorRing(); LinearRing r = p.getExteriorRing();
CheckResult cr = r.getCheckResult(CheckId.C_GE_R_SELF_INTERSECTION); CheckResult cr = r.getCheckResult(CheckId.C_GE_R_SELF_INTERSECTION);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
......
...@@ -43,7 +43,7 @@ public class ShellNotClosedSystemTest { ...@@ -43,7 +43,7 @@ public class ShellNotClosedSystemTest {
@Test @Test
public void testShellNotClosed1() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testShellNotClosed1() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml");
Geometry g = c.getBuildings().get(0).getGeometries().get(0); Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0);
CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NOT_CLOSED); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NOT_CLOSED);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
} }
...@@ -51,7 +51,7 @@ public class ShellNotClosedSystemTest { ...@@ -51,7 +51,7 @@ public class ShellNotClosedSystemTest {
@Test @Test
public void testShellNotClosed2() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testShellNotClosed2() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0002-T0001.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0002-T0001.gml");
Geometry g = c.getBuildings().get(0).getGeometries().get(0); Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0);
CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NOT_CLOSED); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NOT_CLOSED);
assertEquals(ResultStatus.ERROR, cr.getResultStatus()); assertEquals(ResultStatus.ERROR, cr.getResultStatus());
assertEquals(ErrorId.GE_S_NOT_CLOSED, cr.getError().getErrorId()); assertEquals(ErrorId.GE_S_NOT_CLOSED, cr.getError().getErrorId());
......
...@@ -22,6 +22,7 @@ import java.io.IOException; ...@@ -22,6 +22,7 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional;
import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
...@@ -89,15 +90,16 @@ public class TestUtil { ...@@ -89,15 +90,16 @@ public class TestUtil {
if (gmlId == null) { if (gmlId == null) {
return null; return null;
} }
Polygon result = getPolygonById(gmlId, model.getBuildings()); Polygon result = getPolygonById(gmlId, model.getBuildings().toList());
if (result != null) { if (result != null) {
return result; return result;
} }
result = getPolygonById(gmlId, model.getVegetation()); result = getPolygonById(gmlId, model.getVegetation().toList());
if (result != null) { if (result != null) {
return result; return result;
} }
return getPolygonById(gmlId, model.getTransportation()); return getPolygonById(gmlId, model.getTransportation().toList());
} }
private static Polygon getPolygonById(String gmlId, List<? extends CityObject> features) { private static Polygon getPolygonById(String gmlId, List<? extends CityObject> features) {
...@@ -136,22 +138,17 @@ public class TestUtil { ...@@ -136,22 +138,17 @@ public class TestUtil {
if (featureId == null) { if (featureId == null) {
return null; return null;
} }
for (CityObject co : model.getBuildings()) { Optional<? extends CityObject> result = null;
if (featureId.equals(co.getGmlId().getGmlString())) { result = model.getBuildings().filter(b -> featureId.equals(b.getGmlId().toString())).findFirst();
return co; if (result.isPresent()){
} return result.get();
} }
for (CityObject co : model.getTransportation()) { result = model.getTransportation().filter(t-> featureId.equals(t.getGmlId().toString())).findFirst();
if (featureId.equals(co.getGmlId().getGmlString())) { if (result.isPresent()){
return co; return result.get();
}
} }
for (CityObject co : model.getVegetation()) { result = model.getVegetation().filter(t-> featureId.equals(t.getGmlId().toString())).findFirst();
if (featureId.equals(co.getGmlId().getGmlString())) { return result.orElse(null);
return co; }
}
}
return null;
}
} }
...@@ -43,7 +43,7 @@ public class TooFewPolygonsSystemTest { ...@@ -43,7 +43,7 @@ public class TooFewPolygonsSystemTest {
@Test @Test
public void testTooFewPolygons1() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testTooFewPolygons1() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS.gml");
Geometry g = c.getBuildings().get(0).getGeometries().get(0); Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0);
CheckResult cr = g.getCheckResult(CheckId.C_GE_S_TOO_FEW_POLYGONS); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_TOO_FEW_POLYGONS);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
} }
...@@ -51,7 +51,7 @@ public class TooFewPolygonsSystemTest { ...@@ -51,7 +51,7 @@ public class TooFewPolygonsSystemTest {
@Test @Test
public void testTooFewPolygons2() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testTooFewPolygons2() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0001-T0001.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0001-T0001.gml");
Geometry g = c.getBuildings().get(0).getGeometries().get(0); Geometry g = c.getBuildings().toList().get(0).getGeometries().get(0);
CheckResult cr = g.getCheckResult(CheckId.C_GE_S_TOO_FEW_POLYGONS); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_TOO_FEW_POLYGONS);
assertEquals(ResultStatus.ERROR, cr.getResultStatus()); assertEquals(ResultStatus.ERROR, cr.getResultStatus());
assertEquals(ErrorId.GE_S_TOO_FEW_POLYGONS, cr.getError().getErrorId()); assertEquals(ErrorId.GE_S_TOO_FEW_POLYGONS, cr.getError().getErrorId());
......
<?xml version="1.0" encoding="utf-8"?>
<core:CityModel xmlns:core="http://www.opengis.net/citygml/2.0" xmlns:gen="http://www.opengis.net/citygml/generics/2.0" xmlns:bldg="http://www.opengis.net/citygml/building/2.0" xmlns:app="http://www.opengis.net/citygml/appearance/2.0" xmlns:dem="http://www.opengis.net/citygml/relief/2.0" xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/citygml/building/2.0 http://schemas.opengis.net/citygml/building/2.0/building.xsd http://www.opengis.net/citygml/appearance/2.0 http://schemas.opengis.net/citygml/appearance/2.0/appearance.xsd http://www.opengis.net/citygml/relief/2.0 http://schemas.opengis.net/citygml/relief/2.0/relief.xsd http://www.opengis.net/citygml/2.0 http://schemas.opengis.net/citygml/2.0/cityGMLBase.xsd http://www.opengis.net/citygml/generics/2.0 http://schemas.opengis.net/citygml/generics/2.0/generics.xsd">
<!--
Einfaches Gebäude mit Grundriss 3m x 5m und Satteldach, Traufhöhe 3m, Firsthöhe 4,5m
Modelliert mit Begrenzungsflächen (eine Dachfläche, 4 Wandflächen, 1 Grundfläche),
die Gebäudegeometrie als Solid, der auf die Polygone der Begrenzungsflächen referenziert.
Fehler in der Reihenfolge der Attribute.
CityGML 2.0
Gebäudevolumen: 56,25 m3
12.12.2025
Author: V. Coors, HFT Stuttgart
Lizenz:
-->
<core:cityObjectMember>
<bldg:Building gml:id="_Simple_BD.1">
<gen:stringAttribute name="averageStoreyHeight">
<gen:value>2.5</gen:value>
</gen:stringAttribute>
<gml:name>falsche Reihenfolge</gml:name>
<bldg:lod2Solid>
<gml:Solid>
<gml:exterior>
<gml:CompositeSurface>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.1"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.2"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.3"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.4"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.5"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.6"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.7"/>
</gml:CompositeSurface>
</gml:exterior>
</gml:Solid>
</bldg:lod2Solid>
<bldg:boundedBy>
<bldg:WallSurface gml:id="_Simple_BD.1_WallSurface_1">
<bldg:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.2">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.2_LR.1">
<gml:posList srsDimension="3">
13.0 15.0 0.0
13.0 15.0 3.0
13.0 10.0 3.0
13.0 10.0 0.0
13.0 15.0 0.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="_Simple_BD.1_WallSurface_2">
<bldg:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.3">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.3_LR.1">
<gml:posList srsDimension="3">
10.0 15.0 0.0
10.0 15.0 3.0
11.5 15.0 4.5
13.0 15.0 3.0
13.0 15.0 0.0
10.0 15.0 0.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="_Simple_BD.1_WallSurface_3">
<bldg:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.4">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.4_LR.1">
<gml:posList srsDimension="3">
10.0 10.0 3.0
10.0 15.0 3.0
10.0 15.0 0.0
10.0 10.0 0.0
10.0 10.0 3.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="_Simple_BD.1_WallSurface_4">
<bldg:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.5">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.5_LR.1">
<gml:posList srsDimension="3">
13.0 10.0 0.0
13.0 10.0 3.0
11.5 10.0 4.5
10.0 10.0 3.0
10.0 10.0 0.0
13.0 10.0 0.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="_Simple_BD.1_RoofSurface_1">
<bldg:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.6">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.6_LR.1">
<gml:posList srsDimension="3">
10.0 10.0 3.0
11.5 10.0 4.5
11.5 15.0 4.5
10.0 15.0 3.0
10.0 10.0 3.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.7">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.7_LR.1">
<gml:posList srsDimension="3">
11.5 10.0 4.5
13.0 10.0 3.0
13.0 15.0 3.0
11.5 15.0 4.5
11.5 10.0 4.5
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:GroundSurface gml:id="_Simple_BD.1_GroundSurface_1">
<bldg:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.1">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.1_LR.1">
<gml:posList srsDimension="3">
10.0 10.0 0.0
10.0 15.0 0.0
13.0 15.0 0.0
13.0 10.0 0.0
10.0 10.0 0.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:GroundSurface>
</bldg:boundedBy>
</bldg:Building>
</core:cityObjectMember>
</core:CityModel>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<core:CityModel xmlns:xAL="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" xmlns:gml="http://www.opengis.net/gml" xmlns:wtr="http://www.opengis.net/citygml/waterbody/2.0" xmlns:app="http://www.opengis.net/citygml/appearance/2.0" xmlns:core="http://www.opengis.net/citygml/2.0" xmlns:veg="http://www.opengis.net/citygml/vegetation/2.0" xmlns:dem="http://www.opengis.net/citygml/relief/2.0" xmlns:tran="http://www.opengis.net/citygml/transportation/2.0" xmlns:bldg="http://www.opengis.net/citygml/building/2.0" xmlns:grp="http://www.opengis.net/citygml/cityobjectgroup/2.0" xmlns:tun="http://www.opengis.net/citygml/tunnel/2.0" xmlns:qual="https://transfer.hft-stuttgart.de/pages/citydoctor/qualityade/1.0.0" xmlns:frn="http://www.opengis.net/citygml/cityfurniture/2.0" xmlns:brid="http://www.opengis.net/citygml/bridge/2.0" xmlns:gen="http://www.opengis.net/citygml/generics/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:luse="http://www.opengis.net/citygml/landuse/2.0" xsi:schemaLocation="http://www.opengis.net/citygml/building/2.0 http://schemas.opengis.net/citygml/building/2.0/building.xsd http://www.opengis.net/citygml/cityobjectgroup/2.0 http://schemas.opengis.net/citygml/cityobjectgroup/2.0/cityObjectGroup.xsd http://www.opengis.net/citygml/tunnel/2.0 http://schemas.opengis.net/citygml/tunnel/2.0/tunnel.xsd http://www.opengis.net/citygml/waterbody/2.0 http://schemas.opengis.net/citygml/waterbody/2.0/waterBody.xsd http://www.opengis.net/citygml/appearance/2.0 http://schemas.opengis.net/citygml/appearance/2.0/appearance.xsd http://www.opengis.net/citygml/cityfurniture/2.0 http://schemas.opengis.net/citygml/cityfurniture/2.0/cityFurniture.xsd http://www.opengis.net/citygml/bridge/2.0 http://schemas.opengis.net/citygml/bridge/2.0/bridge.xsd http://www.opengis.net/citygml/generics/2.0 http://schemas.opengis.net/citygml/generics/2.0/generics.xsd http://www.opengis.net/citygml/vegetation/2.0 http://schemas.opengis.net/citygml/vegetation/2.0/vegetation.xsd http://www.opengis.net/citygml/relief/2.0 http://schemas.opengis.net/citygml/relief/2.0/relief.xsd http://www.opengis.net/citygml/transportation/2.0 http://schemas.opengis.net/citygml/transportation/2.0/transportation.xsd http://www.opengis.net/citygml/landuse/2.0 http://schemas.opengis.net/citygml/landuse/2.0/landUse.xsd">
<core:cityObjectMember>
<brid:Bridge gml:id="bridge">
<brid:lod1MultiSurface>
<gml:MultiSurface gml:id="CityDoctor_1737625875076_589">
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626144167_811">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626144167_810">
<gml:posList srsDimension="3">84816.978 447538.908 1.48 84816.938 447538.659 1.48 84817.016 447538.782 1.48 84816.978 447538.908 1.48</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</brid:lod1MultiSurface>
</brid:Bridge>
</core:cityObjectMember>
<core:cityObjectMember>
<wtr:WaterBody gml:id="water">
<wtr:lod1MultiSurface>
<gml:MultiSurface gml:id="CityDoctor_1737625875078_592">
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626152495_1043">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626152495_1042">
<gml:posList srsDimension="3">85012.343 447455.577 0.88 85010.804 447448.808 0.88 85013.832 447447.447 0.88 85012.343 447455.577 0.88</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</wtr:lod1MultiSurface>
</wtr:WaterBody>
</core:cityObjectMember>
<core:cityObjectMember>
<bldg:Building gml:id="building">
<bldg:lod1Solid>
<gml:Solid gml:id="CityDoctor_1737625875045_1">
<gml:exterior>
<gml:CompositeSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626132674_761">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626132674_760">
<gml:posList srsDimension="3">84967.573 447497.445 0.01 84967.538 447497.33 0.01 84964.343 447494.755 0.01 84962.909 447496.556 0.01 84964.913 447498.161 0.01 84964.969 447498.343 0.01 84967.602 447497.541 0.01 84967.573 447497.445 0.01</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626132674_763">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626132674_762">
<gml:posList srsDimension="3">84967.602 447497.541 2.36 84964.969 447498.343 2.36 84964.913 447498.161 2.36 84962.909 447496.556 2.36 84964.343 447494.755 2.36 84967.538 447497.33 2.36 84967.573 447497.445 2.36 84967.602 447497.541 2.36</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626132674_765">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626132674_764">
<gml:posList srsDimension="3">84967.538 447497.33 0.01 84967.573 447497.445 0.01 84967.573 447497.445 2.36 84967.538 447497.33 2.36 84967.538 447497.33 0.01</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626132674_767">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626132674_766">
<gml:posList srsDimension="3">84964.343 447494.755 0.01 84967.538 447497.33 0.01 84967.538 447497.33 2.36 84964.343 447494.755 2.36 84964.343 447494.755 0.01</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626132674_769">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626132674_768">
<gml:posList srsDimension="3">84962.909 447496.556 0.01 84964.343 447494.755 0.01 84964.343 447494.755 2.36 84962.909 447496.556 2.36 84962.909 447496.556 0.01</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626132674_771">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626132674_770">
<gml:posList srsDimension="3">84964.913 447498.161 0.01 84962.909 447496.556 0.01 84962.909 447496.556 2.36 84964.913 447498.161 2.36 84964.913 447498.161 0.01</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626132674_773">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626132674_772">
<gml:posList srsDimension="3">84964.969 447498.343 0.01 84964.913 447498.161 0.01 84964.913 447498.161 2.36 84964.969 447498.343 2.36 84964.969 447498.343 0.01</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626132674_775">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626132674_774">
<gml:posList srsDimension="3">84967.602 447497.541 0.01 84964.969 447498.343 0.01 84964.969 447498.343 2.36 84967.602 447497.541 2.36 84967.602 447497.541 0.01</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626132674_777">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626132674_776">
<gml:posList srsDimension="3">84967.573 447497.445 0.01 84967.602 447497.541 0.01 84967.602 447497.541 2.36 84967.573 447497.445 2.36 84967.573 447497.445 0.01</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:CompositeSurface>
</gml:exterior>
</gml:Solid>
</bldg:lod1Solid>
</bldg:Building>
</core:cityObjectMember>
<core:cityObjectMember>
<frn:CityFurniture gml:id="city_furniture">
<frn:lod3Geometry>
<gml:MultiSurface gml:id="CityDoctor_1737378219186_2805">
<gml:surfaceMember>
<gml:CompositeSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="PolyIDGeo19503615">
<gml:exterior>
<gml:LinearRing gml:id="ringID19503615">
<gml:posList srsDimension="3">358506.943 5655399.912 47.06 358507.161 5655401.568 47.06 358506.943 5655403.224 47.06 358506.304 5655404.768 47.06 358505.287 5655406.093 47.06 358503.961 5655407.111 47.06 358502.418 5655407.75 47.06 358500.761 5655407.968 47.06 358499.105 5655407.75 47.06 358497.561 5655407.111 47.06 358496.236 5655406.093 47.06 358495.219 5655404.768 47.06 358494.579 5655403.224 47.06 358494.361 5655401.568 47.06 358494.579 5655399.912 47.06 358495.219 5655398.368 47.06 358496.236 5655397.043 47.06 358497.561 5655396.025 47.06 358499.105 5655395.386 47.06 358500.761 5655395.168 47.06 358502.418 5655395.386 47.06 358503.961 5655396.025 47.06 358505.287 5655397.043 47.06 358506.304 5655398.368 47.06 358506.943 5655399.912 47.06</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:CompositeSurface>
</gml:surfaceMember>
</gml:MultiSurface>
</frn:lod3Geometry>
</frn:CityFurniture>
</core:cityObjectMember>
<core:cityObjectMember>
<tran:Road gml:id="transport">
<tran:lod1MultiSurface>
<gml:MultiSurface gml:id="CityDoctor_1737625875071_446">
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626138227_779">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626138227_778">
<gml:posList srsDimension="3">85034.582 447448.556 1.27 85034.916 447449.902 1.36 85030.3 447450.585 1.18 85034.582 447448.556 1.27</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</tran:lod1MultiSurface>
</tran:Road>
</core:cityObjectMember>
<core:cityObjectMember>
<veg:PlantCover gml:id="vegetation">
<veg:lod1MultiSurface>
<gml:MultiSurface gml:id="CityDoctor_1737625875066_320">
<gml:surfaceMember>
<gml:Polygon gml:id="CityDoctor_1737626125400_739">
<gml:exterior>
<gml:LinearRing gml:id="CityDoctor_1737626125400_738">
<gml:posList srsDimension="3">84930.097 447481.714 0.04 84930.561 447483.073 -0.07 84929.876 447482.352 0.045 84930.097 447481.714 0.04</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</veg:lod1MultiSurface>
</veg:PlantCover>
</core:cityObjectMember>
<core:cityObjectMember>
<gen:GenericCityObject gml:id="generic_object">
<gen:lod2Geometry>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="PolyIDGeo-5">
<gml:exterior>
<gml:LinearRing gml:id="ringID-5">
<gml:posList srsDimension="3">-0.049000 0.085000 1.162000 -0.098000 0.000000 1.162000 -0.049000 -0.085000 1.162000 0.049000 -0.085000 1.162000 0.098000 0.000000 1.162000 0.049000 0.085000 1.162000 -0.049000 0.085000 1.162000 </gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</gen:lod2Geometry>
</gen:GenericCityObject>
</core:cityObjectMember>
<core:cityObjectMember>
<tun:Tunnel gml:id="tunnel">
<tun:lod2Solid>
<gml:Solid>
<gml:exterior>
<gml:CompositeSurface>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.1"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.2"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.3"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.4"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.5"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.6"/>
<gml:surfaceMember xlink:href="#_Simple_BD.1_PG.7"/>
</gml:CompositeSurface>
</gml:exterior>
</gml:Solid>
</tun:lod2Solid>
<tun:boundedBy>
<tun:WallSurface gml:id="_Simple_BD.1_WallSurface_1">
<tun:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.2">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.2_LR.1">
<gml:posList srsDimension="3">
13.0 15.0 0.0
13.0 15.0 3.0
13.0 10.0 3.0
13.0 10.0 0.0
13.0 15.0 0.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</tun:lod2MultiSurface>
</tun:WallSurface>
</tun:boundedBy>
<tun:boundedBy>
<tun:WallSurface gml:id="_Simple_BD.1_WallSurface_2">
<tun:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.3">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.3_LR.1">
<gml:posList srsDimension="3">
10.0 15.0 0.0
10.0 15.0 3.0
11.5 15.0 4.5
13.0 15.0 3.0
13.0 15.0 0.0
10.0 15.0 0.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</tun:lod2MultiSurface>
</tun:WallSurface>
</tun:boundedBy>
<tun:boundedBy>
<tun:WallSurface gml:id="_Simple_BD.1_WallSurface_3">
<tun:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.4">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.4_LR.1">
<gml:posList srsDimension="3">
10.0 10.0 3.0
10.0 15.0 3.0
10.0 15.0 0.0
10.0 10.0 0.0
10.0 10.0 3.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</tun:lod2MultiSurface>
</tun:WallSurface>
</tun:boundedBy>
<tun:boundedBy>
<tun:WallSurface gml:id="_Simple_BD.1_WallSurface_4">
<tun:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.5">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.5_LR.1">
<gml:posList srsDimension="3">
13.0 10.0 0.0
13.0 10.0 3.0
11.5 10.0 4.5
10.0 10.0 3.0
10.0 10.0 0.0
13.0 10.0 0.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</tun:lod2MultiSurface>
</tun:WallSurface>
</tun:boundedBy>
<tun:boundedBy>
<tun:RoofSurface gml:id="_Simple_BD.1_RoofSurface_1">
<tun:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.6">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.6_LR.1">
<gml:posList srsDimension="3">
10.0 10.0 3.0
11.5 10.0 4.5
11.5 15.0 4.5
10.0 15.0 3.0
10.0 10.0 3.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.7">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.7_LR.1">
<gml:posList srsDimension="3">
11.5 10.0 4.5
13.0 10.0 3.0
13.0 15.0 3.0
11.5 15.0 4.5
11.5 10.0 4.5
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</tun:lod2MultiSurface>
</tun:RoofSurface>
</tun:boundedBy>
<tun:boundedBy>
<tun:GroundSurface gml:id="_Simple_BD.1_GroundSurface_1">
<tun:lod2MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon gml:id="_Simple_BD.1_PG.1">
<gml:exterior>
<gml:LinearRing gml:id="_Simple_BD.1_PG.1_LR.1">
<gml:posList srsDimension="3">
10.0 10.0 0.0
10.0 15.0 0.0
13.0 15.0 0.0
13.0 10.0 0.0
10.0 10.0 0.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</tun:lod2MultiSurface>
</tun:GroundSurface>
</tun:boundedBy>
</tun:Tunnel>
</core:cityObjectMember>
<core:cityObjectMember>
<luse:LandUse gml:id="terrain">
<luse:lod1MultiSurface>
<gml:MultiSurface>
<gml:surfaceMember>
<gml:Polygon>
<gml:exterior>
<gml:LinearRing>
<gml:pos>85042.215 447482.383 0.494</gml:pos>
<gml:pos>85042.439 447481.251 0.490</gml:pos>
<gml:pos>85044.905 447482.952 0.490</gml:pos>
<gml:pos>85042.215 447482.383 0.494</gml:pos>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</luse:lod1MultiSurface>
</luse:LandUse>
</core:cityObjectMember>
</core:CityModel>
\ No newline at end of file
minVertexDistance: 1.0E-4 globalParameters:
useStreaming: false numberOfRoundingPlaces: 8
xmlValidation: false schematronFilePath: src/test/resources/schematronTest.xml
checks: useStreaming: true
C_GE_R_TOO_FEW_POINTS: xmlValidation: true
requirements:
R_GE_R_TOO_FEW_POINTS:
enabled: true enabled: true
C_GE_R_NOT_CLOSED: R_GE_R_NOT_CLOSED:
enabled: true enabled: true
C_GE_R_DUPLICATE_POINT: R_GE_R_CONSECUTIVE_POINTS_SAME:
enabled: true enabled: true
C_GE_R_SELF_INTERSECTION: R_GE_R_SELF_INTERSECTION:
enabled: true enabled: true
C_GE_S_MULTIPLE_CONNECTED_COMPONENTS: R_GE_S_MULTIPLE_CONNECTED_COMPONENTS:
enabled: true enabled: true
C_GE_P_INTERIOR_DISCONNECTED: R_GE_P_INTERIOR_DISCONNECTED:
enabled: true enabled: true
C_GE_P_INTERSECTING_RINGS: R_GE_P_INTERSECTING_RINGS:
enabled: true enabled: true
C_GE_P_NON_PLANAR: R_GE_P_NON_PLANAR:
enabled: false enabled: false
parameters: parameters:
# one of ("distance", "angle", "both") # one of ("distance", "angle", "both")
type: distance type: distance
distanceTolerance: 0.01 distanceTolerance: 0.01
angleTolerance: 0.1 angleTolerance: 0.1
C_GE_P_HOLE_OUTSIDE: R_GE_P_HOLE_OUTSIDE:
enabled: true enabled: true
C_GE_P_ORIENTATION_RINGS_SAME: R_GE_P_ORIENTATION_RINGS_SAME:
enabled: true enabled: true
C_GE_P_INNER_RINGS_NESTED: R_GE_P_INNER_RINGS_NESTED:
enabled: true enabled: true
C_GE_S_TOO_FEW_POLYGONS: R_GE_S_TOO_FEW_POLYGONS:
enabled: true enabled: true
C_GE_S_NOT_CLOSED: R_GE_S_NOT_CLOSED:
enabled: true enabled: true
C_GE_S_NON_MANIFOLD_EDGE: R_GE_S_NON_MANIFOLD_EDGE:
enabled: true enabled: true
C_GE_S_POLYGON_WRONG_ORIENTATION: R_GE_S_POLYGON_WRONG_ORIENTATION:
enabled: true enabled: true
C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION: R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION:
enabled: true enabled: true
C_GE_S_NON_MANIFOLD_VERTEX: R_GE_S_NON_MANIFOLD_VERTEX:
enabled: true enabled: true
C_GE_S_SELF_INTERSECTION: R_GE_S_SELF_INTERSECTION:
enabled: true enabled: true
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>de.hft.stuttgart</groupId> <groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId> <artifactId>CityDoctorParent</artifactId>
<version>3.16.0</version> <version>3.18.2</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<artifactId>CityDoctorAutoPro</artifactId> <artifactId>CityDoctorAutoPro</artifactId>
......
...@@ -362,7 +362,7 @@ public class AutoProWindow extends JDialog implements ItemListener { ...@@ -362,7 +362,7 @@ public class AutoProWindow extends JDialog implements ItemListener {
*/ */
private void startCppAutoProMethods() throws FileNotFoundException { private void startCppAutoProMethods() throws FileNotFoundException {
List<Building> buildings = this.model.getBuildings(); List<Building> buildings = this.model.getBuildings().toList();
List<Geometry> geometryList = getAllGeometriesOfSelectedBuildings(buildings); List<Geometry> geometryList = getAllGeometriesOfSelectedBuildings(buildings);
setBuildingsToOrigin(geometryList); setBuildingsToOrigin(geometryList);
...@@ -525,12 +525,12 @@ public class AutoProWindow extends JDialog implements ItemListener { ...@@ -525,12 +525,12 @@ public class AutoProWindow extends JDialog implements ItemListener {
Building building = part.getParent(); Building building = part.getParent();
building.getBuildingParts().remove(part); building.getBuildingParts().remove(part);
if (building.getBuildingParts().isEmpty() && building.getGeometries().isEmpty()) { if (building.getBuildingParts().isEmpty() && building.getGeometries().isEmpty()) {
model.getBuildings().remove(building); model.getBuildings().toList().remove(building);
} }
} else if (parent instanceof Building && parent.getGeometries().isEmpty()) { } else if (parent instanceof Building && parent.getGeometries().isEmpty()) {
Building building = (Building) parent; Building building = (Building) parent;
if (building.getBuildingParts().isEmpty()) { if (building.getBuildingParts().isEmpty()) {
model.getBuildings().remove(building); model.getBuildings().toList().remove(building);
} }
} }
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>de.hft.stuttgart</groupId> <groupId>de.hft.stuttgart</groupId>
<artifactId>CityDoctorParent</artifactId> <artifactId>CityDoctorParent</artifactId>
<version>3.16.0</version> <version>3.18.3</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<artifactId>CityDoctorGUI</artifactId> <artifactId>CityDoctorGUI</artifactId>
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
<profile> <profile>
<id>create-binaries</id> <id>create-binaries</id>
<properties> <properties>
<win-jre>jre-${jre-version-short}-full</win-jre> <win-jre>jre-${jre-version-short}</win-jre>
<lin-jre>${win-jre}</lin-jre> <lin-jre>${win-jre}</lin-jre>
<mac-jre>${win-jre}.jre</mac-jre> <mac-jre>${win-jre}.jre</mac-jre>
</properties> </properties>
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
</goals> </goals>
<configuration> <configuration>
<uri> <uri>
https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-windows-amd64-full.zip</uri> https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-windows-amd64.zip</uri>
<unpack>false</unpack> <unpack>false</unpack>
<outputDirectory> <outputDirectory>
${project.build.directory}/jre/jre-win</outputDirectory> ${project.build.directory}/jre/jre-win</outputDirectory>
...@@ -105,7 +105,7 @@ ...@@ -105,7 +105,7 @@
</goals> </goals>
<configuration> <configuration>
<uri> <uri>
https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-linux-amd64-full.tar.gz</uri> https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-linux-amd64.tar.gz</uri>
<unpack>false</unpack> <unpack>false</unpack>
<outputDirectory> <outputDirectory>
${project.build.directory}/jre/jre-lin</outputDirectory> ${project.build.directory}/jre/jre-lin</outputDirectory>
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
</goals> </goals>
<configuration> <configuration>
<uri> <uri>
https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-macos-amd64-full.zip</uri> https://download.bell-sw.com/java/${jre-version}/bellsoft-jre${jre-version}-macos-amd64.zip</uri>
<unpack>false</unpack> <unpack>false</unpack>
<outputDirectory> <outputDirectory>
${project.build.directory}/jre/jre-mac</outputDirectory> ${project.build.directory}/jre/jre-mac</outputDirectory>
......
#!/bin/sh #! /usr/bin/env sh
./runtime/bin/java -classpath app/*:plugin/* de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter java -classpath "app/*:plugin/*" de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter
\ No newline at end of file
package de.hft.stuttgart.citydoctor2.gui; package de.hft.stuttgart.citydoctor2.gui;
import java.awt.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
...@@ -10,6 +13,12 @@ import java.util.List; ...@@ -10,6 +13,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Tooltip;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
...@@ -23,6 +32,7 @@ import de.hft.stuttgart.citydoctor2.check.Unit; ...@@ -23,6 +32,7 @@ import de.hft.stuttgart.citydoctor2.check.Unit;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.checks.Checks; import de.hft.stuttgart.citydoctor2.checks.Checks;
import de.hft.stuttgart.citydoctor2.gui.tree.TreeRequirement; import de.hft.stuttgart.citydoctor2.gui.tree.TreeRequirement;
import de.hft.stuttgart.citydoctor2.parser.ProgressListener;
import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.event.Event; import javafx.event.Event;
...@@ -30,6 +40,7 @@ import javafx.fxml.FXML; ...@@ -30,6 +40,7 @@ import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar; import javafx.scene.control.ProgressBar;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
...@@ -110,6 +121,18 @@ public class CheckDialog { ...@@ -110,6 +121,18 @@ public class CheckDialog {
@FXML @FXML
private Button selectBtn; private Button selectBtn;
@FXML
private Button selectAllGCBtn;
@FXML
private Button selectNoneGCBtn;
@FXML
private Button selectAllSCBtn;
@FXML
private Button selectNoneSCBtn;
@FXML @FXML
private TextField schematronField; private TextField schematronField;
...@@ -131,9 +154,33 @@ public class CheckDialog { ...@@ -131,9 +154,33 @@ public class CheckDialog {
@FXML @FXML
private Label schematronFileLabel; private Label schematronFileLabel;
@FXML
private Label checkDescriptionsLabel;
@FXML
private Hyperlink homepageHyperlink;
@FXML
private CheckBox streamCheckOption;
@FXML
private Label streamCheckLabel;
@FXML
private VBox streamCheckParametersVBox;
@FXML
private Label outputPathLabel;
@FXML
private TextField outputPathTextField;
@FXML
private Button outputPathBtn;
@FXML
private CheckBox pdfReportsOption;
@FXML
private CheckBox xmlReportsOption;
private CityDoctorController controller; private CityDoctorController controller;
private FilterPane filterPane; private FilterPane filterPane;
private MainWindow window; private MainWindow window;
private IndeterminateSpinnerDialog waitingDialog;
public CheckDialog(MainWindow window, Window parent, CityDoctorController controller) throws IOException { public CheckDialog(MainWindow window, Window parent, CityDoctorController controller) throws IOException {
this.window = window; this.window = window;
...@@ -153,6 +200,7 @@ public class CheckDialog { ...@@ -153,6 +200,7 @@ public class CheckDialog {
} }
public void initialize() { public void initialize() {
createEnableColumn(geometricTable); createEnableColumn(geometricTable);
createNameColumn(geometricTable); createNameColumn(geometricTable);
createValueColumn(geometricTable); createValueColumn(geometricTable);
...@@ -170,9 +218,15 @@ public class CheckDialog { ...@@ -170,9 +218,15 @@ public class CheckDialog {
geometricChecksLabel.setText(Localization.getText("CheckDialog.geometricChecksLabel")); geometricChecksLabel.setText(Localization.getText("CheckDialog.geometricChecksLabel"));
semanticChecksLabel.setText(Localization.getText("CheckDialog.semanticChecksLabel")); semanticChecksLabel.setText(Localization.getText("CheckDialog.semanticChecksLabel"));
schematronFileLabel.setText(Localization.getText("CheckDialog.schematronFileLabel")); schematronFileLabel.setText(Localization.getText("CheckDialog.schematronFileLabel"));
checkDescriptionsLabel.setText(Localization.getText("CheckDialog.checkDescriptionsLabel"));
homepageHyperlink.setText(Localization.getText("CheckDialog.clickHereToOpen"));
selectBtn.setText(Localization.getText("CheckDialog.selectBtn")); selectBtn.setText(Localization.getText("CheckDialog.selectBtn"));
checkBtn.setText(Localization.getText("CheckDialog.checkBtn")); checkBtn.setText(Localization.getText("CheckDialog.checkBtn"));
cancelBtn.setText(Localization.getText("CheckDialog.cancelBtn")); cancelBtn.setText(Localization.getText("CheckDialog.cancelBtn"));
selectAllGCBtn.setText(Localization.getText("CheckDialog.selectAllChecksBtn"));
selectNoneGCBtn.setText(Localization.getText("CheckDialog.unselectAllChecksBtn"));
selectAllSCBtn.setText(Localization.getText("CheckDialog.selectAllChecksBtn"));
selectNoneSCBtn.setText(Localization.getText("CheckDialog.unselectAllChecksBtn"));
List<TableColumn<GlobalParameter, ?>> columns = globalParametersTable.getColumns(); List<TableColumn<GlobalParameter, ?>> columns = globalParametersTable.getColumns();
TableColumn<GlobalParameter, String> nameCol = new TableColumn<>(NAME); TableColumn<GlobalParameter, String> nameCol = new TableColumn<>(NAME);
...@@ -229,7 +283,60 @@ public class CheckDialog { ...@@ -229,7 +283,60 @@ public class CheckDialog {
}); });
schematronField.setText(ValidationConfiguration.CHECK_FOR_SOLID_XML); schematronField.setText(ValidationConfiguration.CHECK_FOR_SOLID_XML);
}
String configString = Settings.get(Settings.CONFIG);
if (configString != null) {
try {
applyConfig(ValidationConfiguration.loadValidationConfigFromString(configString));
} catch (IOException e) {
// failed to read config, ignore as default values are still available
}
}
selectAllGCBtn.setOnAction(ae ->
geometricTable.getRoot().getChildren().forEach(item -> item.getValue().setEnabled(true)));
selectNoneGCBtn.setOnAction(ae ->
geometricTable.getRoot().getChildren().forEach(item -> item.getValue().setEnabled(false)));
selectAllSCBtn.setOnAction(ae ->
semanticTable.getRoot().getChildren().forEach(item -> item.getValue().setEnabled(true)));
selectNoneSCBtn.setOnAction(ae ->
semanticTable.getRoot().getChildren().forEach(item -> item.getValue().setEnabled(false)));
try {
homepageHyperlink.setTooltip(new Tooltip(Localization.getText("CheckDialog.homepageHyperlink")));
final URI hpUrl = new URI(Localization.getText("CheckDialog.homepageHyperlink"));
homepageHyperlink.setOnAction(event -> {
try {
Desktop.getDesktop().browse(hpUrl);
} catch (IOException e) {
homepageHyperlink.setText("[Hyperlink blocked]");
Alert alert = new Alert(Alert.AlertType.ERROR,
"Opening of hyperlink was blocked by the system, URL has been copied to Clipboard",
ButtonType.OK);
Clipboard cb = Clipboard.getSystemClipboard();
ClipboardContent content = new ClipboardContent();
content.putString(hpUrl.toString());
cb.setContent(content);
alert.showAndWait();
}
});
} catch (URISyntaxException e) {
//Should never actually occur
homepageHyperlink.setText("[Hyperlink missing/invalid]");
}
try{
waitingDialog = new IndeterminateSpinnerDialog(window.getMainStage(),"Schematron still running, please wait");
} catch (IOException e) {
logger.error("Failed to load IndeterminateSpinnerDialog");
}
}
private void setupValueColumns(List<TableColumn<GlobalParameter, ?>> columns) { private void setupValueColumns(List<TableColumn<GlobalParameter, ?>> columns) {
TableColumn<GlobalParameter, String> valueCol = new TableColumn<>(VALUE); TableColumn<GlobalParameter, String> valueCol = new TableColumn<>(VALUE);
...@@ -256,6 +363,7 @@ public class CheckDialog { ...@@ -256,6 +363,7 @@ public class CheckDialog {
}); });
} }
private void setupSelectButton() { private void setupSelectButton() {
selectBtn.setOnAction(ae -> { selectBtn.setOnAction(ae -> {
FileChooser chooser = new FileChooser(); FileChooser chooser = new FileChooser();
...@@ -291,7 +399,7 @@ public class CheckDialog { ...@@ -291,7 +399,7 @@ public class CheckDialog {
if (f != null) { if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent()); Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try { try {
ValidationConfiguration config = ValidationConfiguration.loadValidationConfig(f.getAbsolutePath()); ValidationConfiguration config = ValidationConfiguration.loadValidationConfigFile(f.getAbsolutePath());
applyConfig(config); applyConfig(config);
} catch (IOException e) { } catch (IOException e) {
window.showExceptionDialog(e); window.showExceptionDialog(e);
...@@ -412,17 +520,36 @@ public class CheckDialog { ...@@ -412,17 +520,36 @@ public class CheckDialog {
private void setupCheckButton() { private void setupCheckButton() {
checkBtn.setOnAction(ea -> { checkBtn.setOnAction(ea -> {
ValidationConfiguration config = createConfig();
checkBtn.setDisable(true); checkBtn.setDisable(true);
cancelBtn.setDisable(true); cancelBtn.setDisable(true);
ValidationConfiguration config = createConfig();
try {
String configString = config.saveAsString();
Settings.set(Settings.CONFIG, configString);
} catch (IOException e) {
// failed to save config, ignore
}
stage.setOnCloseRequest(Event::consume); stage.setOnCloseRequest(Event::consume);
Thread t = new Thread(() -> { Thread t = new Thread(() -> {
try { try {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
String startingChecks = Localization.getText("CheckDialog.startingChecks"); String startingChecks = Localization.getText("CheckDialog.startingChecks");
logger.info(startingChecks); logger.info(startingChecks);
} }
controller.startChecks(config, progress::setProgress); ProgressListener l = new ProgressListener() {
@Override
public void updateProgress(float state) {
Platform.runLater(() -> {
progress.setProgress(state);
if (state < 0 ){
waitingDialog.show();
}
});
}
};
controller.startChecks(config, l);
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
String checksDone = Localization.getText("CheckDialog.checksDone"); String checksDone = Localization.getText("CheckDialog.checksDone");
logger.info(checksDone); logger.info(checksDone);
...@@ -440,6 +567,7 @@ public class CheckDialog { ...@@ -440,6 +567,7 @@ public class CheckDialog {
Platform.runLater(() -> { Platform.runLater(() -> {
checkBtn.setDisable(false); checkBtn.setDisable(false);
cancelBtn.setDisable(false); cancelBtn.setDisable(false);
waitingDialog.hide();
stage.setOnCloseRequest(null); stage.setOnCloseRequest(null);
stage.close(); stage.close();
}); });
...@@ -574,6 +702,7 @@ public class CheckDialog { ...@@ -574,6 +702,7 @@ public class CheckDialog {
} }
} }
public void show() { public void show() {
Platform.runLater(() -> { Platform.runLater(() -> {
if (controller.getCurrentConfig() != null) { if (controller.getCurrentConfig() != null) {
......
package de.hft.stuttgart.citydoctor2.gui; package de.hft.stuttgart.citydoctor2.gui;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
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.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.CityModel;
import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.Checker; import de.hft.stuttgart.citydoctor2.check.Checker;
import de.hft.stuttgart.citydoctor2.check.ErrorId; import de.hft.stuttgart.citydoctor2.check.ErrorId;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.check.error.SchematronError; import de.hft.stuttgart.citydoctor2.database.UnconnectedCache;
import de.hft.stuttgart.citydoctor2.datastructure.*; import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractFurniture;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractRoom;
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.BuildingConstructiveElement;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingUnit;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityFurniture;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.FeatureType;
import de.hft.stuttgart.citydoctor2.datastructure.GenericCityObject;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.datastructure.Installation;
import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.datastructure.Storey;
import de.hft.stuttgart.citydoctor2.datastructure.TopLevelTransportFeature;
import de.hft.stuttgart.citydoctor2.datastructure.TrafficAreaObject;
import de.hft.stuttgart.citydoctor2.datastructure.TrafficSpaceObject;
import de.hft.stuttgart.citydoctor2.datastructure.TransportSection;
import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject;
import de.hft.stuttgart.citydoctor2.datastructure.TransportationSpace;
import de.hft.stuttgart.citydoctor2.datastructure.Tunnel;
import de.hft.stuttgart.citydoctor2.datastructure.TunnelConstructiveElement;
import de.hft.stuttgart.citydoctor2.datastructure.TunnelPart;
import de.hft.stuttgart.citydoctor2.datastructure.Vegetation;
import de.hft.stuttgart.citydoctor2.datastructure.WaterObject;
import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException;
import de.hft.stuttgart.citydoctor2.gui.table.ErrorStat; import de.hft.stuttgart.citydoctor2.gui.table.ErrorStat;
import de.hft.stuttgart.citydoctor2.gui.tree.*; import de.hft.stuttgart.citydoctor2.gui.tree.AddMoreButtonRunnable;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; import de.hft.stuttgart.citydoctor2.gui.tree.ButtonRenderable;
import de.hft.stuttgart.citydoctor2.parser.*; import de.hft.stuttgart.citydoctor2.gui.tree.Renderable;
import de.hft.stuttgart.citydoctor2.gui.tree.node.CityObjectGroupNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.CityObjectNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.GeometryNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.OpeningNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.TopLevelCityObjectNode;
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.utils.Localization; import de.hft.stuttgart.citydoctor2.utils.Localization;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.chart.XYChart.Data; import javafx.scene.chart.XYChart.Data;
...@@ -19,17 +78,9 @@ import javafx.scene.control.ButtonType; ...@@ -19,17 +78,9 @@ import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog; import javafx.scene.control.Dialog;
import javafx.scene.control.TreeItem; import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView; import javafx.scene.control.TreeView;
import javafx.scene.paint.Color;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter; 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 { public class CityDoctorController {
...@@ -57,7 +108,9 @@ public class CityDoctorController { ...@@ -57,7 +108,9 @@ public class CityDoctorController {
private AtomicInteger waterChunkNr = new AtomicInteger(0); private AtomicInteger waterChunkNr = new AtomicInteger(0);
private AtomicInteger landChunkNr = new AtomicInteger(0); private AtomicInteger landChunkNr = new AtomicInteger(0);
private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0); private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0);
private AtomicInteger otherObjectsChunkNr = new AtomicInteger(0); private AtomicInteger otherCityObjectsChunkNr = new AtomicInteger(0);
public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) { public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) {
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
...@@ -75,6 +128,7 @@ public class CityDoctorController { ...@@ -75,6 +128,7 @@ public class CityDoctorController {
try { try {
Platform.runLater(() -> { Platform.runLater(() -> {
mainWindow.getOpenBtn().setDisable(true); mainWindow.getOpenBtn().setDisable(true);
mainWindow.getWriteReportButton().setDisable(true); mainWindow.getWriteReportButton().setDisable(true);
mainWindow.getMeshGroup().getChildren().clear(); mainWindow.getMeshGroup().getChildren().clear();
...@@ -84,40 +138,14 @@ public class CityDoctorController { ...@@ -84,40 +138,14 @@ public class CityDoctorController {
}); });
currentChecker = null; currentChecker = null;
currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory); currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory);
GMLValidationHandler handler = null; model = CityGmlParser.parseCityGmlFile(path, currentConfig, l);
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); mainWindow.getClickHandler().setConfig(currentConfig);
sourceFile = path; sourceFile = path;
renderer.reset(); renderer.reset();
Platform.runLater(() -> { Platform.runLater(() -> {
// show errors that occured during parsing
updateGlobalErrors();
mainWindow.addFileNameToTitle(path); mainWindow.addFileNameToTitle(path);
mainWindow.getCheckButton().setDisable(false); mainWindow.getCheckButton().setDisable(false);
mainWindow.getLod1Btn().setDisable(false); mainWindow.getLod1Btn().setDisable(false);
...@@ -138,29 +166,34 @@ public class CityDoctorController { ...@@ -138,29 +166,34 @@ public class CityDoctorController {
} }
} }
public static int getMaxFeaturesPerChunk(){
return MAX_FEATURES_PER_CHUNK;
}
private void setupFeatureTabs() { private void setupFeatureTabs() {
mainWindow.setDisableOfFeatureTab(FeatureType.BUILDING, model.getBuildings().isEmpty()); mainWindow.setDisableOfFeatureTab(FeatureType.BUILDING, model.getBuildingCount() == 0);
mainWindow.setDisableOfFeatureTab(FeatureType.VEGETATION, model.getVegetation().isEmpty()); mainWindow.setDisableOfFeatureTab(FeatureType.VEGETATION, model.getVegetationCount() == 0);
mainWindow.setDisableOfFeatureTab(FeatureType.TRANSPORTATION, model.getTransportation().isEmpty()); mainWindow.setDisableOfFeatureTab(FeatureType.TRANSPORTATION, model.getTransportationCount() == 0);
mainWindow.setDisableOfFeatureTab(FeatureType.TUNNEL, model.getTunnels().isEmpty()); mainWindow.setDisableOfFeatureTab(FeatureType.TUNNEL, model.getTunnelCount() == 0);
mainWindow.setDisableOfFeatureTab(FeatureType.BRIDGE, model.getBridges().isEmpty()); mainWindow.setDisableOfFeatureTab(FeatureType.BRIDGE, model.getBridgeCount() == 0);
mainWindow.setDisableOfFeatureTab(FeatureType.WATER, model.getWater().isEmpty()); mainWindow.setDisableOfFeatureTab(FeatureType.WATER, model.getWaterCount() == 0);
mainWindow.setDisableOfFeatureTab(FeatureType.LAND, model.getLand().isEmpty()); mainWindow.setDisableOfFeatureTab(FeatureType.LAND, model.getLandCount() == 0);
mainWindow.setDisableOfFeatureTab(FeatureType.CITY_FURNITURE, model.getCityFurniture().isEmpty()); mainWindow.setDisableOfFeatureTab(FeatureType.CITY_FURNITURE, model.getCityFurnitureCount() == 0);
mainWindow.setDisableOfFeatureTab(FeatureType.GENERIC_CITY_OBJECT, model.getGenericCityObjects().isEmpty()); mainWindow.setDisableOfFeatureTab(FeatureType.OTHER_CITY_OBJECT, model.getOtherCityObjectsCount() == 0);
} }
public void buildTrees() { public void buildTrees() {
resetFeatureChunks(); resetFeatureChunks();
buildBuildings(model); buildBuildings(model);
buildVegetation(model.getVegetation()); buildVegetation(model);
buildTransportation(model.getTransportation()); buildTransportation(model);
buildTunnel(model);
buildBridges(model); buildBridges(model);
buildWater(model); buildWater(model);
buildLand(model); buildLand(model);
buildCityFurniture(model); buildCityFurniture(model);
buildOtherCityObjects(model); buildGenericCityObjects(model);
buildTunnel(model.getTunnels()); buildTunnel(model);
} }
private void resetFeatureChunks() { private void resetFeatureChunks() {
...@@ -172,143 +205,134 @@ public class CityDoctorController { ...@@ -172,143 +205,134 @@ public class CityDoctorController {
waterChunkNr.set(0); waterChunkNr.set(0);
landChunkNr.set(0); landChunkNr.set(0);
cityFurnitureChunkNr.set(0); cityFurnitureChunkNr.set(0);
otherObjectsChunkNr.set(0); otherCityObjectsChunkNr.set(0);
} }
private void buildLand(CityDoctorModel model) { private void buildLand(CityDoctorModel model) {
if (model.getLand().isEmpty()) { if (model.getLandCount() == 0) {
return; return;
} }
TreeView<Renderable> landView = mainWindow.getTerrainView(); TreeView<Renderable> landView = mainWindow.getTerrainView();
TreeItem<Renderable> landRoot = new TreeItem<>(new AllTerrainNode(model.getLand())); TreeItem<Renderable> landRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Land",
Color.BROWN));
landRoot.setExpanded(true); landRoot.setExpanded(true);
landView.setRoot(landRoot); landView.setRoot(landRoot);
buildLandFromList(model.getLand(), landView.getRoot()); Supplier<Stream<CityObject>> supply = model::getLand;
addMoreButtonIfNecessary(model.getLand(), landView, landRoot, landChunkNr); buildLandTreeFromList(supply, landView.getRoot());
addMoreButtonForLand(landView, landRoot, landChunkNr, supply, model.getLandCount());
} }
private void buildLandFromList(List<CityObject> list, TreeItem<Renderable> root) { private void buildLandTreeFromList(Supplier<Stream<CityObject>> supply, TreeItem<Renderable> root) {
int landChunk = landChunkNr.get(); int landChunk = landChunkNr.get();
for (int i = landChunk * MAX_FEATURES_PER_CHUNK; i < (landChunk + 1) * MAX_FEATURES_PER_CHUNK supply.get().skip((long) landChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(land ->{
&& i < list.size(); i++) { CityObjectNode node = new CityObjectNode(land);
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); TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true); item.setExpanded(true);
root.getChildren().add(item); root.getChildren().add(item);
createGeometryNodes(land, 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) { private void buildCityFurniture(CityDoctorModel model) {
if (model.getCityFurniture().isEmpty()) { if (model.getCityFurnitureCount() == 0) {
return; return;
} }
TreeView<Renderable> cityFurnitureView = mainWindow.getCityFurnitureView(); TreeView<Renderable> cityFurnitureView = mainWindow.getCityFurnitureView();
TreeItem<Renderable> cityFurnitureRoot = new TreeItem<>(new AllCityFurnitureNode(model.getCityFurniture())); TreeItem<Renderable> cityFurnitureRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(),
"City Furniture", Color.BLUEVIOLET));
cityFurnitureRoot.setExpanded(true); cityFurnitureRoot.setExpanded(true);
cityFurnitureView.setRoot(cityFurnitureRoot); cityFurnitureView.setRoot(cityFurnitureRoot);
buildTreeFromList(model.getCityFurniture(), cityFurnitureView.getRoot(), cityFurnitureChunkNr); Supplier<Stream<CityFurniture>> supply = model::getCityFurniture;
addMoreButtonIfNecessary(model.getCityFurniture(), cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr); buildCityFurnitureTreeFromList(supply, cityFurnitureView.getRoot());
addMoreButtonForCityFurniture(cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr, supply,
model.getCityFurnitureCount());
} }
private void buildOtherCityObjects(CityDoctorModel model) { private void buildGenericCityObjects(CityDoctorModel model) {
if (model.getGenericCityObjects().isEmpty()) { if (model.getOtherCityObjectsCount() == 0) {
return; return;
} }
TreeView<Renderable> otherObjectsView = mainWindow.getOtherObjectsView(); TreeView<Renderable> otherObjectsView = mainWindow.getOtherCityObjectView();
TreeItem<Renderable> otherObjectsRoot = new TreeItem<>(new AllOtherObjectsNode(model.getGenericCityObjects())); TreeItem<Renderable> otherObjectsRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(),
"Generic CityObjects", Color.DARKGRAY));
otherObjectsRoot.setExpanded(true); otherObjectsRoot.setExpanded(true);
otherObjectsView.setRoot(otherObjectsRoot); otherObjectsView.setRoot(otherObjectsRoot);
buildTreeFromList(model.getGenericCityObjects(), otherObjectsView.getRoot(), otherObjectsChunkNr); Supplier<Stream<CityObject>> supply = model::getOtherCityObjects;
addMoreButtonIfNecessary(model.getGenericCityObjects(), otherObjectsView, otherObjectsRoot, otherObjectsChunkNr); buildOtherCityObjectsTreeFromList(supply, otherObjectsView.getRoot());
addMoreButtonForOtherCityObjects( otherObjectsView, otherObjectsRoot, otherCityObjectsChunkNr, supply,
model.getOtherCityObjectsCount());
} }
private void buildWater(CityDoctorModel model) { private void buildWater(CityDoctorModel model) {
if (model.getWater().isEmpty()) { if (model.getWaterCount() == 0) {
return; return;
} }
TreeView<Renderable> waterView = mainWindow.getWaterView(); TreeView<Renderable> waterView = mainWindow.getWaterView();
TreeItem<Renderable> waterRoot = new TreeItem<>(new AllWaterNode(model.getWater())); TreeItem<Renderable> waterRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Water",
Color.LIGHTSKYBLUE));
waterRoot.setExpanded(true); waterRoot.setExpanded(true);
waterView.setRoot(waterRoot); waterView.setRoot(waterRoot);
buildTreeFromList(model.getWater(), waterView.getRoot(), waterChunkNr); Supplier<Stream<WaterObject>> supply = model::getWater;
addMoreButtonIfNecessary(model.getWater(), waterView, waterRoot, waterChunkNr); buildWaterTreeFromList(supply, waterRoot);
addMoreButtonForWater(waterView, waterRoot, waterChunkNr, supply, model.getWaterCount());
} }
private void buildBridges(CityDoctorModel model) { private void buildBridges(CityDoctorModel model) {
if (model.getBridges().isEmpty()) { if (model.getBridgeCount() == 0) {
return; return;
} }
TreeView<Renderable> bridgeView = mainWindow.getBridgeView(); TreeView<Renderable> bridgeView = mainWindow.getBridgeView();
TreeItem<Renderable> bridgeRoot = new TreeItem<>(new AllBridgesNode(model.getBridges())); TreeItem<Renderable> bridgeRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Bridges",
Color.CORAL));
bridgeRoot.setExpanded(true); bridgeRoot.setExpanded(true);
bridgeView.setRoot(bridgeRoot); bridgeView.setRoot(bridgeRoot);
buildBridgeTreeFromList(model.getBridges(), bridgeView.getRoot()); Supplier<Stream<BridgeObject>> supply = model::getBridges;
addMoreButtonIfNecessary(model.getBridges(), bridgeView, bridgeRoot, bridgeChunkNr); buildBridgeTreeFromList(supply, bridgeView.getRoot());
addMoreButtonForBridges(bridgeView, bridgeRoot, bridgeChunkNr, supply, model.getBridgeCount());
} }
private void buildTransportation(List<TransportationObject> trans) { private void buildTransportation(CityDoctorModel model) {
if (trans.isEmpty()) { if (model.getTransportationCount() == 0) {
return; return;
} }
TreeView<Renderable> transView = mainWindow.getTransportationView(); TreeView<Renderable> transView = mainWindow.getTransportationView();
TreeItem<Renderable> transRoot = new TreeItem<>(new AllTransportationNode(model.getTransportation())); TreeItem<Renderable> transRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(),
"Transportation", Color.YELLOW));
transRoot.setExpanded(true); transRoot.setExpanded(true);
transView.setRoot(transRoot); transView.setRoot(transRoot);
buildTreeFromList(trans, transView.getRoot(), transportationChunkNr); Supplier<Stream<TransportationObject>> supply = model::getTransportation;
addMoreButtonIfNecessary(trans, transView, transRoot, transportationChunkNr); buildTransportationTreeFromList(supply, transView.getRoot());
addMoreButtonForTransportation(transView, transRoot, transportationChunkNr, supply,
model.getTransportationCount());
} }
private void buildTunnel(List<Tunnel> tunnels) { private void buildTunnel(CityDoctorModel model) {
if (tunnels.isEmpty()) { if (model.getTunnelCount() == 0) {
return; return;
} }
TreeView<Renderable> transView = mainWindow.getTunnelView(); TreeView<Renderable> tunnelView = mainWindow.getTunnelView();
TreeItem<Renderable> transRoot = new TreeItem<>(new AllTunnelsNode(model.getTunnels())); TreeItem<Renderable> tunnelRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Tunnel",
transRoot.setExpanded(true); Color.SLATEGRAY));
transView.setRoot(transRoot); tunnelRoot.setExpanded(true);
buildTreeFromList(tunnels, transView.getRoot(), tunnelChunkNr); tunnelView.setRoot(tunnelRoot);
addMoreButtonIfNecessary(tunnels, transView, transRoot, tunnelChunkNr); Supplier<Stream<Tunnel>> supply = model::getTunnels;
buildTunnelTreeFromList(supply, tunnelRoot);
addMoreButtonForTunnel(tunnelView, tunnelRoot, tunnelChunkNr, supply, model.getTunnelCount());
} }
private void buildVegetation(List<Vegetation> veg) { private void buildVegetation(CityDoctorModel model) {
if (veg.isEmpty()) { if (model.getVegetationCount() == 0) {
return; return;
} }
TreeView<Renderable> vegetationsView = mainWindow.getVegetationView(); TreeView<Renderable> vegetationsView = mainWindow.getVegetationView();
TreeItem<Renderable> vegRoot = new TreeItem<>(new AllVegetationNode(model.getVegetation())); TreeItem<Renderable> vegRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(),
"Vegetation", Color.LIGHTGREEN));
vegRoot.setExpanded(true); vegRoot.setExpanded(true);
vegetationsView.setRoot(vegRoot); vegetationsView.setRoot(vegRoot);
buildTreeFromListWithProducer(veg, vegetationsView.getRoot(), vegetationChunkNr, VegetationNode::new); Supplier<Stream<Vegetation>> supply = model::getVegetation;
addMoreButtonIfNecessary(veg, vegetationsView, vegRoot, vegetationChunkNr); buildVegetationTreeFromList(supply, vegRoot);
addMoreButtonForVegetation(vegetationsView, vegRoot, vegetationChunkNr, supply, model.getVegetationCount());
} }
private void addMoreButtonIfNecessary(List<? extends CityObject> list, TreeView<Renderable> view, private void addMoreButtonIfNecessary(List<? extends CityObject> list, TreeView<Renderable> view,
...@@ -323,7 +347,8 @@ public class CityDoctorController { ...@@ -323,7 +347,8 @@ public class CityDoctorController {
root.getChildren().remove(root.getChildren().size() - 1); root.getChildren().remove(root.getChildren().size() - 1);
chunkCounter.incrementAndGet(); chunkCounter.incrementAndGet();
int visibleFeatures = countVisibleNodes(view); int visibleFeatures = countVisibleNodes(view);
buildTreeFromList(list, root, chunkCounter);
// buildTreeFromList(list, root, chunkCounter);
updateTree(root); updateTree(root);
if (selectedIndex >= 0) { if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex); view.getSelectionModel().select(selectedIndex);
...@@ -339,16 +364,35 @@ public class CityDoctorController { ...@@ -339,16 +364,35 @@ public class CityDoctorController {
} }
} }
private void moreButton(CityDoctorModel model, TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<CityObject>> streamSupplier){
int featureCount = model.getBuildingCount();
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildTreeFromList(streamSupplier, root, chunkCounter);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void buildBuildings(CityDoctorModel model) { private void buildBuildings(CityDoctorModel model) {
if (model.getBuildings().isEmpty()) { if (model.getBuildingCount() == 0) {
return; return;
} }
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView(); TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = new TreeItem<>(new AllBuildingsNode(model.getBuildings())); TreeItem<Renderable> root = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Buildings",
Color.WHITE));
root.setExpanded(true); root.setExpanded(true);
buildBuildingTreeFromList(model.getBuildings(), root);
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), buildingsView, root, buildingChunkNr);
buildingsView.setRoot(root); buildingsView.setRoot(root);
Supplier<Stream<Building>> supply = model::getBuildings;
buildBuildingTreeFromList(supply, root);
addMoreButtonForBuildings(buildingsView, root, buildingChunkNr, supply, model.getBuildingCount());
} }
private void addMoreButtonToBuildingsIfNecessary(List<Building> list, TreeView<Renderable> view, private void addMoreButtonToBuildingsIfNecessary(List<Building> list, TreeView<Renderable> view,
...@@ -363,7 +407,7 @@ public class CityDoctorController { ...@@ -363,7 +407,7 @@ public class CityDoctorController {
root.getChildren().remove(root.getChildren().size() - 1); root.getChildren().remove(root.getChildren().size() - 1);
chunkCounter.getAndIncrement(); chunkCounter.getAndIncrement();
int visibleFeatures = countVisibleNodes(view); int visibleFeatures = countVisibleNodes(view);
buildBuildingTreeFromList(list, root); // buildBuildingTreeFromList(list, root);
updateTree(root); updateTree(root);
if (selectedIndex >= 0) { if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex); view.getSelectionModel().select(selectedIndex);
...@@ -379,6 +423,141 @@ public class CityDoctorController { ...@@ -379,6 +423,141 @@ public class CityDoctorController {
} }
} }
private void addMoreButtonForBuildings(TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<Building>> supply, int featureCount) {
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildBuildingTreeFromList(supply, root);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void addMoreButtonForBridges(TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<BridgeObject>> supply, int featureCount){
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildBridgeTreeFromList(supply, root);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void addMoreButtonForVegetation(TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<Vegetation>> supply, int featureCount){
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildVegetationTreeFromList(supply, root);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void addMoreButtonForTunnel(TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<Tunnel>> supply, int featureCount){
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildTunnelTreeFromList(supply, root);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void addMoreButtonForTransportation(TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<TransportationObject>> supply, int featureCount){
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildTransportationTreeFromList(supply, root);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void addMoreButtonForLand(TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<CityObject>> supply, int featureCount){
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildLandTreeFromList(supply, root);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void addMoreButtonForWater(TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<WaterObject>> supply, int featureCount){
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildWaterTreeFromList(supply, root);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void addMoreButtonForCityFurniture(TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<CityFurniture>> supply, int featureCount){
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildCityFurnitureTreeFromList(supply, root);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void addMoreButtonForOtherCityObjects(TreeView<Renderable> view, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Supplier<Stream<CityObject>> supply, int featureCount){
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < featureCount) {
AddMoreButtonRunnable run = new AddMoreButtonRunnable(featureCount, view, root, chunkCounter) {
@Override
public void buildNodeTreeCall() {
buildOtherCityObjectsTreeFromList(supply, root);
updateTree(root);
}
};
//add button
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private static int countVisibleNodes(TreeView<Renderable> view) { private static int countVisibleNodes(TreeView<Renderable> view) {
return countVisibleNodes(view.getRoot()) + 1; return countVisibleNodes(view.getRoot()) + 1;
} }
...@@ -393,17 +572,16 @@ public class CityDoctorController { ...@@ -393,17 +572,16 @@ public class CityDoctorController {
return visibleFeatures; return visibleFeatures;
} }
private void buildTreeFromList(List<? extends CityObject> cos, TreeItem<Renderable> root, private void buildTreeFromList(Supplier<Stream<CityObject>> supply, TreeItem<Renderable> root,
AtomicInteger chunkCounter) { AtomicInteger chunkCounter) {
int chunk = chunkCounter.get(); int buildingChunk = buildingChunkNr.get();
for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) { supply.get().skip((long) buildingChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(co ->{
CityObject co = cos.get(i);
CityObjectNode node = new CityObjectNode(co); CityObjectNode node = new CityObjectNode(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node); TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true); nodeItem.setExpanded(true);
root.getChildren().add(nodeItem); root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem); createGeometryNodes(co, nodeItem);
} });
} }
private <T extends CityObject> void buildTreeFromListWithProducer(List<T> cos, TreeItem<Renderable> root, private <T extends CityObject> void buildTreeFromListWithProducer(List<T> cos, TreeItem<Renderable> root,
...@@ -427,7 +605,7 @@ public class CityDoctorController { ...@@ -427,7 +605,7 @@ public class CityDoctorController {
mainWindow.getWaterView().setRoot(null); mainWindow.getWaterView().setRoot(null);
mainWindow.getTerrainView().setRoot(null); mainWindow.getTerrainView().setRoot(null);
mainWindow.getCityFurnitureView().setRoot(null); mainWindow.getCityFurnitureView().setRoot(null);
mainWindow.getOtherObjectsView().setRoot(null); mainWindow.getOtherCityObjectView().setRoot(null);
mainWindow.getErrorTree().getRoot().getChildren().clear(); mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getAttributeView().getRoot().getChildren().clear(); mainWindow.getAttributeView().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear(); mainWindow.getGlobalErrorsView().getItems().clear();
...@@ -441,64 +619,261 @@ public class CityDoctorController { ...@@ -441,64 +619,261 @@ public class CityDoctorController {
mainWindow.getVertexView().getRoot().getChildren().clear(); mainWindow.getVertexView().getRoot().getChildren().clear();
} }
private void buildBuildingTreeFromList(List<Building> list, TreeItem<Renderable> root) { private TreeItem<Renderable> buildTopLevelCityObjectNode(CityObject co, TreeItem<Renderable> root) {
return buildTopLevelCityObjectNode(co, root, Collections.emptyList());
}
private TreeItem<Renderable> buildTopLevelCityObjectNode(CityObject co, TreeItem<Renderable> root,
List<BoundarySurface> surfaces) {
TopLevelCityObjectNode node = new TopLevelCityObjectNode(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
if (!surfaces.isEmpty()) {
createBoundarySurfaceNodes(surfaces, nodeItem);
}
return nodeItem;
}
private TreeItem<Renderable> buildCityObjectNode(CityObject co, TreeItem<Renderable> root) {
return buildCityObjectNode(co, root, Collections.emptyList());
}
private TreeItem<Renderable> buildCityObjectNode(CityObject co, TreeItem<Renderable> root, List<BoundarySurface> surfaces) {
CityObjectNode node = new CityObjectNode(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(false);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
if (!surfaces.isEmpty()) {
createBoundarySurfaceNodes(surfaces, nodeItem);
}
return nodeItem;
}
private void buildBuildingTreeFromList(Supplier<Stream<Building>> supply, TreeItem<Renderable> root) {
int buildingChunk = buildingChunkNr.get(); int buildingChunk = buildingChunkNr.get();
for (int i = buildingChunk * MAX_FEATURES_PER_CHUNK; i < (buildingChunk + 1) * MAX_FEATURES_PER_CHUNK supply.get().skip((long) buildingChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(b ->{
&& i < list.size(); i++) { TreeItem<Renderable> item = buildTopLevelCityObjectNode(b, root, b.getBoundarySurfaces());
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); createBuildingInstallationNodes(b, item);
createBuildingConstructiveElementsNodes(b, item);
createBuildingRoomNodes(b, item); createBuildingRoomNodes(b, item);
createBuildingFurnitureNodes(b, item); createBuildingFurnitureNodes(b, item);
createBuildingStoreyNodes(b, item); createBuildingStoreyNodes(b, item);
createBuildingUnitNodes(b, item); createBuildingUnitNodes(b, item);
createBuildingPartNodes(b, item); createBuildingPartNodes(b, item);
} });
} }
private void buildBridgeTreeFromList(List<BridgeObject> list, TreeItem<Renderable> root) { private void buildBridgeTreeFromList(Supplier<Stream<BridgeObject>> supply, TreeItem<Renderable> root) {
int bridgeChunk = bridgeChunkNr.get(); int bridgeChunk = bridgeChunkNr.get();
for (int i = bridgeChunk * MAX_FEATURES_PER_CHUNK; i < (bridgeChunk + 1) * MAX_FEATURES_PER_CHUNK supply.get().skip((long) bridgeChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(bridge ->{
&& i < list.size(); i++) { TreeItem<Renderable> item = buildTopLevelCityObjectNode(bridge, root, bridge.getBoundarySurfaces());
BridgeObject bridge = list.get(i);
BridgeNode node = new BridgeNode(bridge); createBridgeRoomNodes(bridge, item);
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(bridge, item);
createBoundarySurfaceNodes(bridge.getBoundarySurfaces(), item);
createBridgeInstallationNodes(bridge, item); createBridgeInstallationNodes(bridge, item);
createConstructiveElementsNodes(bridge, item); createBridgeFurnitureNodes(bridge, item);
createBridgeConstructiveElementsNodes(bridge, item);
createBridgePartNodes(bridge, item); createBridgePartNodes(bridge, item);
});
}
private void buildTransportationTreeFromList(Supplier<Stream<TransportationObject>> supply, TreeItem<Renderable> root) {
int transportChunk = transportationChunkNr.get();
supply.get().skip((long) transportChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(trans ->{
TreeItem<Renderable> item = buildTopLevelCityObjectNode(trans, root);
createTransportSectionNodes(trans, item);
createTrafficSpacesNodes(trans, item);
});
}
private void buildVegetationTreeFromList(Supplier<Stream<Vegetation>> supply, TreeItem<Renderable> root) {
int vegetationChunk = vegetationChunkNr.get();
supply.get().skip((long) vegetationChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(veg ->{
buildTopLevelCityObjectNode(veg, root);
});
}
private void buildWaterTreeFromList(Supplier<Stream<WaterObject>> supply, TreeItem<Renderable> root) {
int waterChunk = waterChunkNr.get();
supply.get().skip((long) waterChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(wat ->{
buildTopLevelCityObjectNode(wat, root);
});
}
private void buildCityFurnitureTreeFromList(Supplier<Stream<CityFurniture>> supply, TreeItem<Renderable> root) {
int cityFurnChunk = cityFurnitureChunkNr.get();
supply.get().skip((long) cityFurnChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(cf ->{
buildTopLevelCityObjectNode(cf, root);
});
}
private void buildOtherCityObjectsTreeFromList(Supplier<Stream<CityObject>> supply, TreeItem<Renderable> root) {
int genericObjectChunk = otherCityObjectsChunkNr.get();
supply.get().skip((long) genericObjectChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(gen ->{
buildTopLevelCityObjectNode(gen, root);
});
}
private void createTransportSectionNodes(TransportationObject t, TreeItem<Renderable> root) {
if (t instanceof TopLevelTransportFeature top) {
if (!top.getSections().isEmpty()) {
CityObjectGroupNode sectionsGroupNode = new CityObjectGroupNode(top.getSections(),
"Sections", Color.YELLOW);
TreeItem<Renderable> sectionsGroupItem = new TreeItem<>(sectionsGroupNode);
root.getChildren().add(sectionsGroupItem);
for (TransportSection ts : top.getSections()) {
TreeItem<Renderable> tsNodeItem = buildCityObjectNode(ts, sectionsGroupItem);
createTrafficSpacesNodes(ts, tsNodeItem);
}
}
if (!top.getIntersections().isEmpty()) {
CityObjectGroupNode intersectionsGroupNode = new CityObjectGroupNode(top.getIntersections(),
"Intersections", Color.YELLOW);
TreeItem<Renderable> intersectionsGroupItem = new TreeItem<>(intersectionsGroupNode);
root.getChildren().add(intersectionsGroupItem);
for (TransportSection ts : top.getIntersections()) {
TreeItem<Renderable> tsNodeItem = buildCityObjectNode(ts, intersectionsGroupItem);
createTrafficSpacesNodes(ts, tsNodeItem);
}
}
}
}
private void createTrafficSpacesNodes(TransportationObject t, TreeItem<Renderable> root) {
if (t instanceof TransportationSpace space) {
if (!space.getTrafficSpaces().isEmpty()) {
CityObjectGroupNode trafficSpacesGroupNode = new CityObjectGroupNode(space.getTrafficSpaces(),
"TRAFFIC_SPACE", Color.YELLOW);
TreeItem<Renderable> trafficSpacesGroupItem = new TreeItem<>(trafficSpacesGroupNode);
root.getChildren().add(trafficSpacesGroupItem);
trafficSpacesGroupItem.setExpanded(true);
for (TrafficSpaceObject tso : space.getTrafficSpaces()) {
TreeItem<Renderable> tsoNodeItem = buildCityObjectNode(tso, trafficSpacesGroupItem);
createTrafficAreaNodes(tso, tsoNodeItem, TrafficAreaObject.TrafficAreaType.TRAFFIC_AREA);
}
}
if (!space.getAuxTrafficSpaces().isEmpty()) {
CityObjectGroupNode auxTrafficSpacesGroupNode = new CityObjectGroupNode(space.getAuxTrafficSpaces(),
"AUXILIARY_TRAFFIC_SPACE", Color.YELLOW);
TreeItem<Renderable> auxTrafficSpacesGroupItem = new TreeItem<>(auxTrafficSpacesGroupNode);
root.getChildren().add(auxTrafficSpacesGroupItem);
auxTrafficSpacesGroupItem.setExpanded(true);
for (TrafficSpaceObject atso : space.getAuxTrafficSpaces()) {
TreeItem<Renderable> atsoNodeItem = buildCityObjectNode(atso, auxTrafficSpacesGroupItem);
createTrafficAreaNodes(atso, atsoNodeItem, TrafficAreaObject.TrafficAreaType.AUXILIARY_TRAFFIC_AREA);
}
}
}
}
public void createTrafficAreaNodes(TransportationObject t, TreeItem<Renderable> root, TrafficAreaObject.TrafficAreaType type) {
if (t instanceof TrafficSpaceObject space && !space.getTrafficAreas().isEmpty()) {
CityObjectGroupNode trafficAreaGroupNode = new CityObjectGroupNode(space.getTrafficAreas(), type.toString(), Color.YELLOW);
TreeItem<Renderable> trafficAreaGroupItem = new TreeItem<>(trafficAreaGroupNode);
root.getChildren().add(trafficAreaGroupItem);
trafficAreaGroupItem.setExpanded(true);
for (TrafficAreaObject tao : space.getTrafficAreas()) {
buildCityObjectNode(tao, trafficAreaGroupItem);
}
} }
} }
private void buildTunnelTreeFromList(Supplier<Stream<Tunnel>> supply, TreeItem<Renderable> root) {
int tunnelChunk = tunnelChunkNr.get();
supply.get().skip((long) tunnelChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(tunnel ->{
TreeItem<Renderable> item = buildTopLevelCityObjectNode(tunnel, root);
createTunnelHollowNodes(tunnel, item);
createTunnelInstallationNodes(tunnel, item);
createTunnelFurnitureNodes(tunnel, item);
createTunnelConstructiveElementsNodes(tunnel, item);
createTunnelPartNodes(tunnel, item);
});
}
private void createTunnelInstallationNodes(Tunnel tunnel, TreeItem<Renderable> root) {
createInstallationNodes(tunnel.getTunnelInstallations(), root);
}
private void createTunnelHollowNodes(Tunnel tunnel, TreeItem<Renderable> root) {
createRoomNodes(tunnel.getTunnelHollows(), root);
}
private void createTunnelFurnitureNodes(Tunnel tunnel, TreeItem<Renderable> root) {
createFurnitureNodes(tunnel.getTunnelFurniture(), root);
}
private void createTunnelConstructiveElementsNodes(Tunnel tunnel, TreeItem<Renderable> root){
if (tunnel.getTunnelConstructiveElements().isEmpty()){
return;
}
CityObjectGroupNode constructiveElementsGroupNode = new CityObjectGroupNode(tunnel.getTunnelConstructiveElements(),
"Constructive Elements", Color.DARKGRAY);
TreeItem<Renderable> constructiveElementsGroupItem = new TreeItem<>(constructiveElementsGroupNode);
root.getChildren().add(constructiveElementsGroupItem);
for (TunnelConstructiveElement tce : tunnel.getTunnelConstructiveElements()) {
buildCityObjectNode(tce, constructiveElementsGroupItem, tce.getBoundarySurfaces());
}
}
private void createTunnelPartNodes(Tunnel tunnel, TreeItem<Renderable> root) {
if (tunnel.getTunnelParts().isEmpty()) {
return;
}
CityObjectGroupNode tunnelPartsGroupNode = new CityObjectGroupNode(tunnel.getTunnelParts(), "Tunnel parts", Color.DARKGRAY);
TreeItem<Renderable> tunnelPartsGroupItem = new TreeItem<>(tunnelPartsGroupNode);
root.getChildren().add(tunnelPartsGroupItem);
for (TunnelPart tp : tunnel.getTunnelParts()) {
TreeItem<Renderable> tpNodeItem = buildCityObjectNode(tp, tunnelPartsGroupItem, tp.getBoundarySurfaces());
createTunnelHollowNodes(tunnel, tpNodeItem);
createTunnelInstallationNodes(tunnel, tpNodeItem);
createTunnelFurnitureNodes(tunnel, tpNodeItem);
createTunnelConstructiveElementsNodes(tunnel, tpNodeItem);
}
}
private void createBridgeRoomNodes(BridgeObject bridge, TreeItem<Renderable> root) {
createRoomNodes(bridge.getBridgeRooms(), root);
}
private void createBridgeInstallationNodes(BridgeObject bridge, TreeItem<Renderable> root) { private void createBridgeInstallationNodes(BridgeObject bridge, TreeItem<Renderable> root) {
createInstallationNodes(bridge.getBridgeInstallations(), root); createInstallationNodes(bridge.getBridgeInstallations(), root);
} }
private void createConstructiveElementsNodes(BridgeObject bridge, TreeItem<Renderable> item) { private void createBridgeFurnitureNodes(BridgeObject bridge, TreeItem<Renderable> root) {
createFurnitureNodes(bridge.getBridgeFurniture(), root);
}
private void createBridgeConstructiveElementsNodes(BridgeObject bridge, TreeItem<Renderable> item) {
if (bridge.getConstructiveElements().isEmpty()) { if (bridge.getConstructiveElements().isEmpty()) {
return; return;
} }
AllBridgeConstructiveElementsNode allBceNode = new AllBridgeConstructiveElementsNode(
bridge.getConstructiveElements()); CityObjectGroupNode constructiveElementsGroupNode = new CityObjectGroupNode(
TreeItem<Renderable> allBceNodeTextItem = new TreeItem<>(allBceNode); bridge.getConstructiveElements(), "Constructive Elements", Color.CORAL);
item.getChildren().add(allBceNodeTextItem); TreeItem<Renderable> constructiveElementsGroupItem = new TreeItem<>(constructiveElementsGroupNode);
item.getChildren().add(constructiveElementsGroupItem);
for (BridgeConstructiveElement bce : bridge.getConstructiveElements()) { for (BridgeConstructiveElement bce : bridge.getConstructiveElements()) {
BridgeConstructiveElementNode bceNode = new BridgeConstructiveElementNode(bce); buildCityObjectNode(bce, constructiveElementsGroupItem, bce.getBoundarySurfaces());
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bceNode);
bpNodeItem.setExpanded(true);
allBceNodeTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bce, bpNodeItem);
createBoundarySurfaceNodes(bce.getBoundarySurfaces(), bpNodeItem);
} }
} }
...@@ -506,21 +881,22 @@ public class CityDoctorController { ...@@ -506,21 +881,22 @@ public class CityDoctorController {
if (bridge.getParts().isEmpty()) { if (bridge.getParts().isEmpty()) {
return; return;
} }
AllBridgePartsNode allBpsText = new AllBridgePartsNode(bridge.getParts());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText); CityObjectGroupNode bridgePartsGroupNode = new CityObjectGroupNode(bridge.getParts(), "Bridge Parts", Color.CORAL);
item.getChildren().add(allBpsTextItem); TreeItem<Renderable> bridgePartsGroupItem = new TreeItem<>(bridgePartsGroupNode);
item.getChildren().add(bridgePartsGroupItem);
for (BridgeObject bp : bridge.getParts()) { for (BridgeObject bp : bridge.getParts()) {
BridgeNode bpNode = new BridgeNode(bp); TreeItem<Renderable> bpNodeItem = buildCityObjectNode(bp, bridgePartsGroupItem, bp.getBoundarySurfaces());
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true); createBridgeRoomNodes(bp, bpNodeItem);
allBpsTextItem.getChildren().add(bpNodeItem); createBridgeInstallationNodes(bp, bpNodeItem);
createGeometryNodes(bp, bpNodeItem); createBridgeFurnitureNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem); createBridgeConstructiveElementsNodes(bp, bpNodeItem);
} }
} }
private void createBuildingInstallationNodes(Building ab, TreeItem<Renderable> root) { private void createBuildingInstallationNodes(AbstractBuilding ab, TreeItem<Renderable> root) {
createInstallationNodes(ab.getBuildingInstallations(), root); createInstallationNodes(ab.getBuildingInstallations(), root);
} }
...@@ -529,80 +905,81 @@ public class CityDoctorController { ...@@ -529,80 +905,81 @@ public class CityDoctorController {
return; return;
} }
AllInstallationsNode allBiNode = new AllInstallationsNode(installations); CityObjectGroupNode installationsGroupNode = new CityObjectGroupNode(installations, "Installations", null);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBiNode); TreeItem<Renderable> installationsGroupItem = new TreeItem<>(installationsGroupNode);
root.getChildren().add(allBpsTextItem); root.getChildren().add(installationsGroupItem);
for (Installation bi : installations) { for (Installation bi : installations) {
InstallationNode biNode = new InstallationNode(bi); buildCityObjectNode(bi, installationsGroupItem, bi.getBoundarySurfaces());
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) { private void createBuildingRoomNodes(AbstractBuilding ab, TreeItem<Renderable> root) {
createRoomNodes(ab.getBuildingRooms(), root); createRoomNodes(ab.getBuildingRooms(), root);
} }
private void createBuildingConstructiveElementsNodes(AbstractBuilding ab, TreeItem<Renderable> root) {
if (ab.getConstructiveElements().isEmpty()) {
return;
}
CityObjectGroupNode constructiveElementsGroupNode = new CityObjectGroupNode(
ab.getConstructiveElements(), "Constructive Elements", Color.CORAL);
TreeItem<Renderable> constructiveElementsGroupItem = new TreeItem<>(constructiveElementsGroupNode);
root.getChildren().add(constructiveElementsGroupItem);
for (BuildingConstructiveElement bce : ab.getConstructiveElements()) {
buildCityObjectNode(bce, constructiveElementsGroupItem, bce.getBoundarySurfaces());
}
}
private void createRoomNodes(List<? extends AbstractRoom> rooms, TreeItem<Renderable> root) { private void createRoomNodes(List<? extends AbstractRoom> rooms, TreeItem<Renderable> root) {
if (rooms.isEmpty()) { if (rooms.isEmpty()) {
return; return;
} }
AllRoomsNode allRoNode = new AllRoomsNode(rooms); CityObjectGroupNode roomsGroupNode = new CityObjectGroupNode(rooms, "Rooms", null);
TreeItem<Renderable> allRoNodeTextItem = new TreeItem<>(allRoNode); TreeItem<Renderable> roomsGroupItem = new TreeItem<>(roomsGroupNode);
root.getChildren().add(allRoNodeTextItem); root.getChildren().add(roomsGroupItem);
for (AbstractRoom room : rooms) { for (AbstractRoom room : rooms) {
RoomNode roomNode = new RoomNode(room); buildCityObjectNode(room, roomsGroupItem, room.getBoundarySurfaces());
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) { private void createBuildingStoreyNodes(AbstractBuilding ab, TreeItem<Renderable> root) {
List<Storey> storeys = ab.getBuildingStoreys(); List<Storey> storeys = ab.getBuildingStoreys();
if (storeys.isEmpty()) { if (storeys.isEmpty()) {
return; return;
} }
AllStoreysNode allSNode = new AllStoreysNode(storeys);
TreeItem<Renderable> allSNodeTextItem = new TreeItem<>(allSNode); CityObjectGroupNode storeysGroupNode = new CityObjectGroupNode(storeys, "Storeys", null);
root.getChildren().add(allSNodeTextItem); TreeItem<Renderable> storeysGroupItem = new TreeItem<>(storeysGroupNode);
root.getChildren().add(storeysGroupItem);
for (Storey storey : storeys) { for (Storey storey : storeys) {
StoreyNode storeyNode = new StoreyNode(storey); buildCityObjectNode(storey, storeysGroupItem, storey.getBoundarySurfaces());
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) { public void createBuildingUnitNodes(AbstractBuilding ab, TreeItem<Renderable> root) {
List<BuildingUnit> buildingUnits = ab.getBuildingUnits(); List<BuildingUnit> buildingUnits = ab.getBuildingUnits();
if (buildingUnits.isEmpty()) { if (buildingUnits.isEmpty()) {
return; return;
} }
AllBuildingUnitsNode allSNode = new AllBuildingUnitsNode(buildingUnits);
TreeItem<Renderable> allSNodeTextItem = new TreeItem<>(allSNode); CityObjectGroupNode buildingUnitsGroupNode = new CityObjectGroupNode(buildingUnits, "Building Units", null);
root.getChildren().add(allSNodeTextItem); TreeItem<Renderable> buildingUnitsGroupItem = new TreeItem<>(buildingUnitsGroupNode);
root.getChildren().add(buildingUnitsGroupItem);
for (BuildingUnit buildingUnit : buildingUnits) { for (BuildingUnit buildingUnit : buildingUnits) {
BuildingUnitNode buildingUnitNode = new BuildingUnitNode(buildingUnit); buildCityObjectNode(buildingUnit, buildingUnitsGroupItem, buildingUnit.getBoundarySurfaces());
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) { private void createBuildingFurnitureNodes(AbstractBuilding ab, TreeItem<Renderable> root) {
createFurnitureNodes(ab.getBuildingRoomFurnitureList(), root); createFurnitureNodes(ab.getBuildingRoomFurnitureList(), root);
} }
...@@ -611,16 +988,12 @@ public class CityDoctorController { ...@@ -611,16 +988,12 @@ public class CityDoctorController {
return; return;
} }
AllFurnitureNode allFnNode = new AllFurnitureNode(furniture); CityObjectGroupNode furnitureGroupNode = new CityObjectGroupNode(furniture, "Furniture", null);
TreeItem<Renderable> allFnNodeTextItem = new TreeItem<>(allFnNode); TreeItem<Renderable> furnitureGroupItem = new TreeItem<>(furnitureGroupNode);
root.getChildren().add(allFnNodeTextItem); root.getChildren().add(furnitureGroupItem);
for (AbstractFurniture fn : furniture) { for (AbstractFurniture fn : furniture) {
FurnitureNode fnNode = new FurnitureNode(fn); buildCityObjectNode(fn, furnitureGroupItem, fn.getBoundarySurfaces());
TreeItem<Renderable> fnNodeTextItem = new TreeItem<>(fnNode);
fnNodeTextItem.setExpanded(true);
allFnNodeTextItem.getChildren().add(fnNodeTextItem);
createGeometryNodes(fn, fnNodeTextItem);
createBoundarySurfaceNodes(fn.getBoundarySurfaceList(), fnNodeTextItem);
} }
} }
...@@ -628,25 +1001,26 @@ public class CityDoctorController { ...@@ -628,25 +1001,26 @@ public class CityDoctorController {
if (bsList.isEmpty()) { if (bsList.isEmpty()) {
return; return;
} }
AllBoundarySurfacesNode allBsText = new AllBoundarySurfacesNode(bsList);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBsText);
root.getChildren().add(allBpsTextItem);
allBpsTextItem.getChildren().add(new TreeItem<>()); CityObjectGroupNode boundarySurfacesGroupNode = new CityObjectGroupNode(bsList, "Boundary Surfaces", null);
TreeItem<Renderable> boundarySurfacesGroupItem = new TreeItem<>(boundarySurfacesGroupNode);
root.getChildren().add(boundarySurfacesGroupItem);
allBpsTextItem.expandedProperty().addListener((obs, old, newV) -> { boundarySurfacesGroupItem.getChildren().add(new TreeItem<>());
if (Boolean.TRUE.equals(newV) && allBpsTextItem.getChildren().size() == 1) {
boundarySurfacesGroupItem.expandedProperty().addListener((obs, old, newV) -> {
if (Boolean.TRUE.equals(newV) && boundarySurfacesGroupItem.getChildren().size() == 1) {
Platform.runLater(() -> { Platform.runLater(() -> {
allBpsTextItem.getChildren().clear(); boundarySurfacesGroupItem.getChildren().clear();
for (BoundarySurface bs : bsList) { for (BoundarySurface bs : bsList) {
BoundarySurfaceNode bsText = new BoundarySurfaceNode(bs); CityObjectNode bsText = new CityObjectNode(bs);
TreeItem<Renderable> bsTextItem = new TreeItem<>(bsText); TreeItem<Renderable> bsTextItem = new TreeItem<>(bsText);
bsTextItem.setExpanded(true); bsTextItem.setExpanded(true);
allBpsTextItem.getChildren().add(bsTextItem); boundarySurfacesGroupItem.getChildren().add(bsTextItem);
createGeometryNodes(bs, bsTextItem); createGeometryNodes(bs, bsTextItem);
createOpeningNodes(bs.getOpenings(), bsTextItem); createOpeningNodes(bs.getOpenings(), bsTextItem);
} }
updateTree(allBpsTextItem); updateTree(boundarySurfacesGroupItem);
}); });
} }
}); });
...@@ -656,31 +1030,35 @@ public class CityDoctorController { ...@@ -656,31 +1030,35 @@ public class CityDoctorController {
if (openings == null || openings.isEmpty()) { if (openings == null || openings.isEmpty()) {
return; return;
} }
AllOpeningsNode allOpeningsNode = new AllOpeningsNode(openings);
TreeItem<Renderable> allOpeningsItem = new TreeItem<>(allOpeningsNode); CityObjectGroupNode openingsGroupNode = new CityObjectGroupNode(openings, "Openings", null);
TreeItem<Renderable> openingsGroupItem = new TreeItem<>(openingsGroupNode);
root.getChildren().add(openingsGroupItem);
for (Opening o : openings) { for (Opening o : openings) {
OpeningNode openingNode = new OpeningNode(o); OpeningNode openingNode = new OpeningNode(o);
TreeItem<Renderable> openingsItem = new TreeItem<>(openingNode); TreeItem<Renderable> openingItem = new TreeItem<>(openingNode);
allOpeningsItem.getChildren().add(openingsItem); openingsGroupItem.getChildren().add(openingItem);
} }
root.getChildren().add(allOpeningsItem);
} }
private void createBuildingPartNodes(Building ab, TreeItem<Renderable> item) { private void createBuildingPartNodes(Building ab, TreeItem<Renderable> item) {
if (ab.getBuildingParts().isEmpty()) { if (ab.getBuildingParts().isEmpty()) {
return; return;
} }
AllBuildingPartsNode allBpsText = new AllBuildingPartsNode(ab.getBuildingParts()); CityObjectGroupNode buildingPartsGroupNode = new CityObjectGroupNode(ab.getBuildingParts(), "Building Parts", null);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText); TreeItem<Renderable> buildingPartsGroupItem = new TreeItem<>(buildingPartsGroupNode);
item.getChildren().add(allBpsTextItem); item.getChildren().add(buildingPartsGroupItem);
for (BuildingPart bp : ab.getBuildingParts()) { for (BuildingPart bp : ab.getBuildingParts()) {
BuildingPartNode bpNode = new BuildingPartNode(bp); TreeItem<Renderable> bpNodeItem = buildCityObjectNode(bp, buildingPartsGroupItem, bp.getBoundarySurfaces());
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true); createBuildingInstallationNodes(bp, bpNodeItem);
allBpsTextItem.getChildren().add(bpNodeItem); createBuildingRoomNodes(bp, bpNodeItem);
createGeometryNodes(bp, bpNodeItem); createBuildingFurnitureNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem); createBuildingStoreyNodes(bp, bpNodeItem);
createBuildingUnitNodes(bp, bpNodeItem);
} }
} }
...@@ -694,11 +1072,15 @@ public class CityDoctorController { ...@@ -694,11 +1072,15 @@ public class CityDoctorController {
public void startChecks(ValidationConfiguration config, ProgressListener l) { public void startChecks(ValidationConfiguration config, ProgressListener l) {
if (model == null) { if (model == null) {
logger.warn(Localization.getText("CityDoctorController.noDatamodel")); if (logger.isWarnEnabled()) {
logger.warn(Localization.getText("CityDoctorController.noDatamodel"));
}
return; return;
} }
if (sourceFile == null) { if (sourceFile == null) {
logger.warn(Localization.getText("CityDoctorController.noSourceFile")); if (logger.isWarnEnabled()) {
logger.warn(Localization.getText("CityDoctorController.noSourceFile"));
}
return; return;
} }
try { try {
...@@ -714,13 +1096,10 @@ public class CityDoctorController { ...@@ -714,13 +1096,10 @@ public class CityDoctorController {
Platform.runLater(() -> { Platform.runLater(() -> {
// apply check results to tree views // apply check results to tree views
buildTrees();
updateFeatureTrees(); updateFeatureTrees();
updateTree(mainWindow.getPolygonsView().getRoot()); updateTree(mainWindow.getPolygonsView().getRoot());
for (CheckError e : model.getGlobalErrors()) { updateGlobalErrors();
if (e instanceof SchematronError se) {
mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString());
}
}
renderer.refresh(); renderer.refresh();
mainWindow.getWriteReportButton().setDisable(false); mainWindow.getWriteReportButton().setDisable(false);
}); });
...@@ -729,6 +1108,18 @@ public class CityDoctorController { ...@@ -729,6 +1108,18 @@ public class CityDoctorController {
} }
} }
private void updateGlobalErrors() {
GlobalErrorVisitor globErrVisitor = new GlobalErrorVisitor(mainWindow);
for (CheckError e : model.getGlobalErrors()) {
e.accept(globErrVisitor);
}
if (model.getGlobalErrors().isEmpty()) {
mainWindow.getGlobalErrorsLabel().setStyle("-fx-text-fill: black;");
} else {
mainWindow.getGlobalErrorsLabel().setStyle("-fx-text-fill: red;");
}
}
void updateFeatureTrees() { void updateFeatureTrees() {
updateTree(mainWindow.getBuildingsView().getRoot()); updateTree(mainWindow.getBuildingsView().getRoot());
updateTree(mainWindow.getVegetationView().getRoot()); updateTree(mainWindow.getVegetationView().getRoot());
...@@ -737,7 +1128,8 @@ public class CityDoctorController { ...@@ -737,7 +1128,8 @@ public class CityDoctorController {
updateTree(mainWindow.getCityFurnitureView().getRoot()); updateTree(mainWindow.getCityFurnitureView().getRoot());
updateTree(mainWindow.getTransportationView().getRoot()); updateTree(mainWindow.getTransportationView().getRoot());
updateTree(mainWindow.getWaterView().getRoot()); updateTree(mainWindow.getWaterView().getRoot());
updateTree(mainWindow.getOtherObjectsView().getRoot()); updateTree(mainWindow.getOtherCityObjectView().getRoot());
updateTree(mainWindow.getTunnelView().getRoot());
renderer.updateErrors(); renderer.updateErrors();
} }
...@@ -776,14 +1168,13 @@ public class CityDoctorController { ...@@ -776,14 +1168,13 @@ public class CityDoctorController {
private Map<ErrorId, ErrorStat> getErrorStats() { private Map<ErrorId, ErrorStat> getErrorStats() {
Map<ErrorId, ErrorStat> stats = new HashMap<>(); Map<ErrorId, ErrorStat> stats = new HashMap<>();
addErrorStats(stats, model.getBridges()); List<CheckError> errors = new ArrayList<>();
addErrorStats(stats, model.getBuildings()); model.createFeatureStream().forEach(co -> co.collectContainedErrors(errors));
addErrorStats(stats, model.getLand()); Set<CheckError> filteredErrors = new HashSet<>(errors);
addErrorStats(stats, model.getTransportation()); for (CheckError error : filteredErrors) {
addErrorStats(stats, model.getVegetation()); ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new);
addErrorStats(stats, model.getWater()); stat.incrementCount();
addErrorStats(stats, model.getCityFurniture()); }
addErrorStats(stats, model.getGenericCityObjects());
return stats; return stats;
} }
...@@ -797,15 +1188,19 @@ public class CityDoctorController { ...@@ -797,15 +1188,19 @@ public class CityDoctorController {
ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new); ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new);
stat.incrementCount(); stat.incrementCount();
} }
} }
public void export(Building b) { public void export(CityObject co) {
if (model == null) { if (model == null) {
return; return;
} }
CityDoctorModel newModel = new CityDoctorModel(model.getParserConfig(), model.getFile()); CityDoctorModel newModel = new CityDoctorModel(model.getParserConfig(), model.getFile(), new UnconnectedCache());
newModel.setCityModel(new CityModel()); newModel.setCityModel(new CityModel());
newModel.addBuilding(b); newModel.addCityObject(co);
newModel.setParsedCityGMLVersion(model.getCityGMLVersion());
newModel.getCityModel().setBoundedBy(model.getCityModel().getBoundedBy());
FileChooser fc = new FileChooser(); FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml")); fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml"));
...@@ -820,7 +1215,6 @@ public class CityDoctorController { ...@@ -820,7 +1215,6 @@ public class CityDoctorController {
if (f != null) { if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent()); Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try { try {
newModel.saveAs(f.getAbsolutePath(), true); newModel.saveAs(f.getAbsolutePath(), true);
} catch (CityDoctorWriteException e) { } catch (CityDoctorWriteException e) {
logger.error(e); logger.error(e);
...@@ -831,6 +1225,7 @@ public class CityDoctorController { ...@@ -831,6 +1225,7 @@ public class CityDoctorController {
public void showWorld() { public void showWorld() {
if (model != null) { if (model != null) {
renderer.render(model); renderer.render(model);
resetSearch(mainWindow.getSelectedTab());
} }
} }
...@@ -847,68 +1242,123 @@ public class CityDoctorController { ...@@ -847,68 +1242,123 @@ public class CityDoctorController {
} }
mainWindow.unselectEverything(); mainWindow.unselectEverything();
resetFeatureChunks(); resetFeatureChunks();
if (selectedTab == FeatureType.BUILDING) { switch (selectedTab) {
List<Building> foundBuildings = filterFeatures(searchString, model.getBuildings()); case BUILDING -> filterBuildingTree(searchString);
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView(); case BRIDGE -> filterBridgeTree(searchString);
TreeItem<Renderable> root = buildingsView.getRoot(); case TUNNEL -> filterTunnelTree(searchString);
root.getChildren().clear(); case VEGETATION -> filterVegetationTree(searchString);
buildBuildingTreeFromList(foundBuildings, root); case TRANSPORTATION -> filterTransportationTree(searchString);
updateTree(root); case WATER -> filterWaterTree(searchString);
addMoreButtonToBuildingsIfNecessary(foundBuildings, buildingsView, root, buildingChunkNr); case LAND -> filterLandTree(searchString);
} else { case CITY_FURNITURE -> filterCityFurnitureTree(searchString);
TreeView<Renderable> view; case OTHER_CITY_OBJECT -> filterOtherCityObjectsTree(searchString);
List<? extends CityObject> cos; default -> throw new IllegalArgumentException("selected tab is unknown: " + selectedTab);
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 void filterBuildingTree(String searchString) {
Supplier<Stream<Building>> foundBuildings = () -> model.getBuildings().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundBuildings.get().count();
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot();
root.getChildren().clear();
buildBuildingTreeFromList(foundBuildings, root);
updateTree(root);
addMoreButtonForBuildings(buildingsView, root, buildingChunkNr, foundBuildings, featureCount);
}
private void filterBridgeTree(String searchString) {
Supplier<Stream<BridgeObject>> foundBridges = () -> model.getBridges().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundBridges.get().count();
TreeView<Renderable> bridgeView = mainWindow.getBridgeView();
TreeItem<Renderable> root = bridgeView.getRoot();
root.getChildren().clear();
buildBridgeTreeFromList(foundBridges, root);
updateTree(root);
addMoreButtonForBridges(bridgeView, root, bridgeChunkNr, foundBridges, featureCount);
}
private void filterTunnelTree(String searchString) {
Supplier<Stream<Tunnel>> foundTunnel = () -> model.getTunnels().filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundTunnel.get().count();
TreeView<Renderable> tunnelView = mainWindow.getTunnelView();
TreeItem<Renderable> root = tunnelView.getRoot();
root.getChildren().clear();
buildTunnelTreeFromList(foundTunnel, root);
updateTree(root);
addMoreButtonForTunnel(tunnelView, root, tunnelChunkNr, foundTunnel, featureCount);
}
private void filterVegetationTree(String searchString) {
Supplier<Stream<Vegetation>> foundVegetation = () -> model.getVegetation().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundVegetation.get().count();
TreeView<Renderable> vegetationView = mainWindow.getVegetationView();
TreeItem<Renderable> root = vegetationView.getRoot();
root.getChildren().clear();
buildVegetationTreeFromList(foundVegetation, root);
updateTree(root);
addMoreButtonForVegetation(vegetationView, root, vegetationChunkNr, foundVegetation, featureCount);
}
private void filterTransportationTree(String searchString) {
Supplier<Stream<TransportationObject>> foundTransportation = () -> model.getTransportation().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundTransportation.get().count();
TreeView<Renderable> transportationView = mainWindow.getTransportationView();
TreeItem<Renderable> root = transportationView.getRoot();
root.getChildren().clear();
buildTransportationTreeFromList(foundTransportation, root);
updateTree(root);
addMoreButtonForTransportation(transportationView, root, transportationChunkNr,
foundTransportation, featureCount);
}
private void filterWaterTree(String searchString) {
Supplier<Stream<WaterObject>> foundWater = () -> model.getWater().filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundWater.get().count();
TreeView<Renderable> waterView = mainWindow.getWaterView();
TreeItem<Renderable> root = waterView.getRoot();
root.getChildren().clear();
buildWaterTreeFromList(foundWater, root);
addMoreButtonForWater(waterView, root, waterChunkNr, foundWater, featureCount);
}
private void filterLandTree(String searchString) {
Supplier<Stream<CityObject>> foundLand = () -> model.getLand().filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundLand.get().count();
TreeView<Renderable> landView = mainWindow.getTerrainView();
TreeItem<Renderable> root = landView.getRoot();
root.getChildren().clear();
buildLandTreeFromList(foundLand, root);
addMoreButtonForLand(landView, root, landChunkNr, foundLand, featureCount);
}
private void filterCityFurnitureTree(String searchString) {
Supplier<Stream<CityFurniture>> foundCityFurniture = () -> model.getCityFurniture().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundCityFurniture.get().count();
TreeView<Renderable> cityFurnitureView = mainWindow.getCityFurnitureView();
TreeItem<Renderable> root = cityFurnitureView.getRoot();
root.getChildren().clear();
buildCityFurnitureTreeFromList(foundCityFurniture, root);
addMoreButtonForCityFurniture(cityFurnitureView, root, cityFurnitureChunkNr, foundCityFurniture, featureCount);
}
private void filterOtherCityObjectsTree(String searchString) {
Supplier<Stream<CityObject>> foundGenericCityObject = () -> model.getOtherCityObjects().
filter(co ->featureFilter(co, searchString));
int featureCount = (int) foundGenericCityObject.get().count();
TreeView<Renderable> genericCityObjectView = mainWindow.getOtherCityObjectView();
TreeItem<Renderable> root = genericCityObjectView.getRoot();
root.getChildren().clear();
buildOtherCityObjectsTreeFromList(foundGenericCityObject, root);
addMoreButtonForOtherCityObjects(genericCityObjectView, root, otherCityObjectsChunkNr,
foundGenericCityObject, featureCount);
} }
private <T extends CityObject> List<T> filterFeatures(String searchString, List<T> features) { private <T extends CityObject> List<T> filterFeatures(String searchString, List<T> features) {
...@@ -921,76 +1371,17 @@ public class CityDoctorController { ...@@ -921,76 +1371,17 @@ public class CityDoctorController {
return foundFeatures; return foundFeatures;
} }
private boolean featureFilter(CityObject co, String searchString) {
return co.getGmlId().getGmlString().contains(searchString);
}
public void resetSearch(FeatureType selectedTab) { public void resetSearch(FeatureType selectedTab) {
if (model == null) { if (model == null) {
return; return;
} }
resetFeatureChunks(); mainWindow.rebuildErrorFilterTrees();
if (selectedTab == FeatureType.BUILDING) {
// buildings are handled differently, because of surface and building // buildTrees();
// 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() { public Series<String, Number> createErrorSeries() {
...@@ -1020,137 +1411,193 @@ public class CityDoctorController { ...@@ -1020,137 +1411,193 @@ public class CityDoctorController {
} }
mainWindow.resetSearchBar(); mainWindow.resetSearchBar();
root.getChildren().clear(); root.getChildren().clear();
List<Building> errorBuildings = new ArrayList<>();
for (Building b : model.getBuildings()) {
if (b.containsAnyError()) {
errorBuildings.add(b);
}
}
resetFeatureChunks(); resetFeatureChunks();
buildBuildingTreeFromList(errorBuildings, root); List<GmlId> foundBuildings = model.getCache().getErrorFeatureIds();
List<GmlId> idList = model.getBuildingIds();
foundBuildings.removeIf(gmlId -> (!idList.contains(gmlId)));
//Supplier<Stream<Building>> supply = () -> model.getCache().getStream(foundBuildings).map(co -> (Building) co);
Supplier<Stream<Building>> supply = () -> model.getCache().getAll(foundBuildings).stream().map(co -> (Building) co);
buildBuildingTreeFromList(supply, root);
updateTree(root); updateTree(root);
addMoreButtonToBuildingsIfNecessary(errorBuildings, buildingsView, root, buildingChunkNr); addMoreButtonForBuildings(buildingsView, root, buildingChunkNr, supply, foundBuildings.size());
} }
public void fillTreeViewWithErrorVegetation() { public void fillTreeViewWithErrorVegetation() {
TreeView<Renderable> vegetationView = mainWindow.getVegetationView();
TreeItem<Renderable> root = vegetationView.getRoot();
if (model == null) { if (model == null) {
return; return;
} }
fillTreeViewWithErrorCityObjects(mainWindow.getVegetationView(), model.getVegetation(), vegetationChunkNr); if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
resetFeatureChunks();
List<GmlId> foundVegetation = model.getCache().getErrorFeatureIds();
List<GmlId> idList = model.getVegetationIds();
foundVegetation.removeIf(gmlId -> (!idList.contains(gmlId)));
Supplier<Stream<Vegetation>> supply = () -> model.getCache().getStream(foundVegetation).map(co -> (Vegetation) co);
buildVegetationTreeFromList(supply, root);
updateTree(root);
addMoreButtonForVegetation(vegetationView, root, vegetationChunkNr, supply, foundVegetation.size());
} }
public void fillTreeViewWithErrorBridges() { public void fillTreeViewWithErrorBridges() {
TreeView<Renderable> bridgeView = mainWindow.getBridgeView();
TreeItem<Renderable> root = bridgeView.getRoot();
if (model == null) { if (model == null) {
return; return;
} }
fillTreeViewWithErrorCityObjects(mainWindow.getBridgeView(), model.getBridges(), bridgeChunkNr); if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
resetFeatureChunks();
List<GmlId> foundBridge = model.getCache().getErrorFeatureIds();
List<GmlId> idList = model.getBridgeIds();
foundBridge.removeIf(gmlId -> (!idList.contains(gmlId)));
Supplier<Stream<BridgeObject>> supply = () -> model.getCache().getStream(foundBridge).map(co -> (BridgeObject) co);
buildBridgeTreeFromList(supply, root);
updateTree(root);
addMoreButtonForBridges(bridgeView, root, bridgeChunkNr, supply, foundBridge.size());
} }
public void fillTreeViewWithErrorLand() { public void fillTreeViewWithErrorLand() {
TreeView<Renderable> landView = mainWindow.getTerrainView();
TreeItem<Renderable> root = landView.getRoot();
if (model == null) { if (model == null) {
return; return;
} }
fillTreeViewWithErrorCityObjects(mainWindow.getTerrainView(), model.getLand(), landChunkNr); if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
resetFeatureChunks();
List<GmlId> foundLand = model.getCache().getErrorFeatureIds();
List<GmlId> idList = model.getLandIds();
foundLand.removeIf(gmlId -> (!idList.contains(gmlId)));
Supplier<Stream<CityObject>> supply = () -> model.getCache().getStream(foundLand).map(co -> (CityObject) co);
buildLandTreeFromList(supply, root);
updateTree(root);
addMoreButtonForLand(landView, root, landChunkNr, supply, foundLand.size());
} }
public void fillTreeViewWithErrorCityFurniture() { public void fillTreeViewWithErrorCityFurniture() {
TreeView<Renderable> cityFurnitureView = mainWindow.getCityFurnitureView();
TreeItem<Renderable> root = cityFurnitureView.getRoot();
if (model == null) { if (model == null) {
return; return;
} }
fillTreeViewWithErrorCityObjects(mainWindow.getCityFurnitureView(), if (root == null) {
model.getCityFurniture(), cityFurnitureChunkNr); return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
resetFeatureChunks();
List<GmlId> foundCityFurniture = model.getCache().getErrorFeatureIds();
List<GmlId> idList = model.getCityFurnitureIds();
foundCityFurniture.removeIf(gmlId -> (!idList.contains(gmlId)));
Supplier<Stream<CityFurniture>> supply = () -> model.getCache().getStream(foundCityFurniture).map(co -> (CityFurniture) co);
buildCityFurnitureTreeFromList(supply, root);
updateTree(root);
addMoreButtonForCityFurniture(cityFurnitureView, root, cityFurnitureChunkNr, supply, foundCityFurniture.size());
} }
public void fillTreeViewWithErrorOtherObjects() { public void fillTreeViewWithErrorOtherCityObjects() {
TreeView<Renderable> genericCityObjectsView = mainWindow.getOtherCityObjectView();
TreeItem<Renderable> root = genericCityObjectsView.getRoot();
if (model == null) { if (model == null) {
return; return;
} }
fillTreeViewWithErrorCityObjects(mainWindow.getOtherObjectsView(), if (root == null) {
model.getGenericCityObjects(), otherObjectsChunkNr); return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
resetFeatureChunks();
List<GmlId> foundOtherCityObjects = model.getCache().getErrorFeatureIds();
List<GmlId> idList = model.getOtherCityObjectsIds();
foundOtherCityObjects.removeIf(gmlId -> (!idList.contains(gmlId)));
Supplier<Stream<CityObject>> supply = () -> model.getCache().getStream(foundOtherCityObjects).map(co -> (GenericCityObject) co);
buildOtherCityObjectsTreeFromList(supply, root);
updateTree(root);
addMoreButtonForOtherCityObjects(genericCityObjectsView, root, otherCityObjectsChunkNr, supply, foundOtherCityObjects.size());
} }
public void fillTreeViewWithErrorTransportation() { public void fillTreeViewWithErrorTransportation() {
TreeView<Renderable> transportationView = mainWindow.getTransportationView();
TreeItem<Renderable> root = transportationView.getRoot();
if (model == null) { if (model == null) {
return; return;
} }
fillTreeViewWithErrorCityObjects(mainWindow.getTransportationView(), model.getTransportation(), if (root == null) {
transportationChunkNr);
}
public void fillTreeViewWithErrorTunnel() {
if (model == null) {
return; return;
} }
fillTreeViewWithErrorCityObjects(mainWindow.getTunnelView(), model.getTunnels(), tunnelChunkNr); mainWindow.resetSearchBar();
root.getChildren().clear();
resetFeatureChunks();
List<GmlId> foundTransportation = model.getCache().getErrorFeatureIds();
List<GmlId> idList = model.getTransportationIds();
foundTransportation.removeIf(gmlId -> (!idList.contains(gmlId)));
Supplier<Stream<TransportationObject>> supply = () -> model.getCache().getStream(foundTransportation).map(co -> (TransportationObject) co);
buildTransportationTreeFromList(supply, root);
updateTree(root);
addMoreButtonForTransportation(transportationView, root, transportationChunkNr, supply, foundTransportation.size());
} }
public void fillTreeViewWithErrorWater() { public void fillTreeViewWithErrorTunnel() {
TreeView<Renderable> tunnelView = mainWindow.getTunnelView();
TreeItem<Renderable> root = tunnelView.getRoot();
if (model == null) { if (model == null) {
return; 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) { if (root == null) {
return; return;
} }
mainWindow.resetSearchBar(); mainWindow.resetSearchBar();
root.getChildren().clear(); root.getChildren().clear();
List<CityObject> errorObjects = new ArrayList<>(); resetFeatureChunks();
for (CityObject co : objects) { List<GmlId> foundTunnel = model.getCache().getErrorFeatureIds();
if (co.containsAnyError()) { List<GmlId> idList = model.getTunnelIds();
errorObjects.add(co); foundTunnel.removeIf(gmlId -> (!idList.contains(gmlId)));
}
} Supplier<Stream<Tunnel>> supply = () -> model.getCache().getStream(foundTunnel).map(co -> (Tunnel) co);
buildTreeFromList(errorObjects, root, chunkCounter); buildTunnelTreeFromList(supply, root);
updateTree(root); updateTree(root);
addMoreButtonIfNecessary(errorObjects, treeView, root, chunkCounter); addMoreButtonForTunnel(tunnelView, root, tunnelChunkNr, supply, foundTunnel.size());
} }
public void fillTreeViewFromFeatures(FeatureType selectedTab) { public void fillTreeViewWithErrorWater() {
TreeView<Renderable> waterView = mainWindow.getWaterView();
TreeItem<Renderable> root = waterView.getRoot();
if (model == null) { if (model == null) {
return; return;
} }
mainWindow.resetSearchBar(); if (root == null) {
switch (selectedTab) { return;
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();
} }
mainWindow.resetSearchBar();
root.getChildren().clear();
resetFeatureChunks();
List<GmlId> foundWater = model.getCache().getErrorFeatureIds();
List<GmlId> idList = model.getWaterIds();
foundWater.removeIf(gmlId -> (!idList.contains(gmlId)));
Supplier<Stream<WaterObject>> supply = () -> model.getCache().getStream(foundWater).map(co -> (WaterObject) co);
buildWaterTreeFromList(supply, root);
updateTree(root);
addMoreButtonForWater(waterView, root, tunnelChunkNr, supply, foundWater.size());
} }
public CityDoctorModel getModel() { public CityDoctorModel getModel() {
...@@ -1202,49 +1649,34 @@ public class CityDoctorController { ...@@ -1202,49 +1649,34 @@ public class CityDoctorController {
public void delete(TreeItem<Renderable> selectedItem) { public void delete(TreeItem<Renderable> selectedItem) {
if (model == null) { if (model == null) {
return; return;
} }/* Old debug function, would first need a refactor due to the RenderNode and GeoDB reworks
Renderable render = selectedItem.getValue(); Renderable render = selectedItem.getValue();
if (render instanceof BuildingNode node) { if (render instanceof BuildingNode node) {
model.getBuildings().remove(node.getBuilding()); model.getBuildings().remove(node.getBuilding());
mainWindow.getBuildingsView().getRoot().getChildren().remove(selectedItem); mainWindow.getBuildingsView().getRoot().getChildren().remove(selectedItem);
} }
*/
} }
public void errorFilterIndexChanged(Number newV) { public void errorFilterIndexChanged(Number newV) {
mainWindow.getMeshGroup().getChildren().clear(); mainWindow.getMeshGroup().getChildren().clear();
mainWindow.unselectEverything(); mainWindow.unselectEverything();
if (newV.intValue() == 0) { mainWindow.resetSearchBar();
fillTreeViewFromFeatures(mainWindow.getSelectedTab()); renderer.clearCurrentRender();
} else if (newV.intValue() == 1) { if (newV.intValue() == 0) { // Error filter was set to "All"
switch (mainWindow.getSelectedTab()) { buildTrees();
case BUILDING: updateFeatureTrees();
fillTreeViewWithErrorBuildings(); } else if (newV.intValue() == 1) { // Error filter was set to "With errors"
break; fillTreeViewWithErrorBuildings();
case VEGETATION: fillTreeViewWithErrorVegetation();
fillTreeViewWithErrorVegetation(); fillTreeViewWithErrorBridges();
break; fillTreeViewWithErrorLand();
case BRIDGE: fillTreeViewWithErrorTransportation();
fillTreeViewWithErrorBridges(); fillTreeViewWithErrorTunnel();
break; fillTreeViewWithErrorWater();
case LAND: fillTreeViewWithErrorCityFurniture();
fillTreeViewWithErrorLand(); fillTreeViewWithErrorOtherCityObjects();
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 { } else {
throw new IllegalStateException("Unknown filter index selected: " + newV); throw new IllegalStateException("Unknown filter index selected: " + newV);
} }
......
package de.hft.stuttgart.citydoctor2.gui;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.ErrorVisitor;
import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError;
import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError;
import de.hft.stuttgart.citydoctor2.check.error.DegeneratedRingError;
import de.hft.stuttgart.citydoctor2.check.error.DependenciesNotMetError;
import de.hft.stuttgart.citydoctor2.check.error.MultipleConnectedComponentsError;
import de.hft.stuttgart.citydoctor2.check.error.NestedRingError;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldEdgeError;
import de.hft.stuttgart.citydoctor2.check.error.NonManifoldVertexError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonDistancePlaneError;
import de.hft.stuttgart.citydoctor2.check.error.NonPlanarPolygonNormalsDeviation;
import de.hft.stuttgart.citydoctor2.check.error.NotCeilingError;
import de.hft.stuttgart.citydoctor2.check.error.NotFloorError;
import de.hft.stuttgart.citydoctor2.check.error.NotGroundError;
import de.hft.stuttgart.citydoctor2.check.error.NotWallError;
import de.hft.stuttgart.citydoctor2.check.error.NullAreaError;
import de.hft.stuttgart.citydoctor2.check.error.PlanarityError;
import de.hft.stuttgart.citydoctor2.check.error.PointTouchesEdgeError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonHoleOutsideError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonInteriorDisconnectedError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonIntersectingRingsError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonSameOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWithoutSurfaceError;
import de.hft.stuttgart.citydoctor2.check.error.PolygonWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.RingDuplicatePointError;
import de.hft.stuttgart.citydoctor2.check.error.RingEdgeIntersectionError;
import de.hft.stuttgart.citydoctor2.check.error.RingError;
import de.hft.stuttgart.citydoctor2.check.error.RingNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.RingSelfIntersectionError;
import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError;
import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import de.hft.stuttgart.citydoctor2.check.error.SolidError;
import de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError;
import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceFragmentedError;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError;
import de.hft.stuttgart.citydoctor2.check.error.XMLValidationError;
public class GlobalErrorVisitor implements ErrorVisitor {
private MainWindow mainWindow;
public GlobalErrorVisitor(MainWindow mainWindow) {
this.mainWindow = mainWindow;
}
@Override
public void visit(RingError err) {
}
@Override
public void visit(PolygonError err) {
}
@Override
public void visit(RingSelfIntersectionError err) {
}
@Override
public void visit(PlanarityError err) {
}
@Override
public void visit(PolygonHoleOutsideError err) {
}
@Override
public void visit(NonManifoldEdgeError err) {
}
@Override
public void visit(MultipleConnectedComponentsError err) {
}
@Override
public void visit(NestedRingError err) {
}
@Override
public void visit(NonManifoldVertexError err) {
}
@Override
public void visit(PolygonWrongOrientationError err) {
}
@Override
public void visit(PolygonSameOrientationError err) {
}
@Override
public void visit(SolidNotClosedError err) {
}
@Override
public void visit(DependenciesNotMetError err) {
}
@Override
public void visit(UnknownCheckError err) {
}
@Override
public void visit(RingNotClosedError err) {
}
@Override
public void visit(ConsecutivePointSameError err) {
}
@Override
public void visit(AllPolygonsWrongOrientationError err) {
}
@Override
public void visit(PolygonInteriorDisconnectedError err) {
}
@Override
public void visit(NullAreaError err) {
}
@Override
public void visit(RingTooFewPointsError err) {
}
@Override
public void visit(NonPlanarPolygonNormalsDeviation err) {
}
@Override
public void visit(NonPlanarPolygonDistancePlaneError err) {
}
@Override
public void visit(PolygonIntersectingRingsError err) {
}
@Override
public void visit(SolidSelfIntError err) {
}
@Override
public void visit(TooFewPolygonsError err) {
}
@Override
public void visit(RingDuplicatePointError err) {
}
@Override
public void visit(RingEdgeIntersectionError err) {
}
@Override
public void visit(PointTouchesEdgeError err) {
}
@Override
public void visit(NotCeilingError err) {
}
@Override
public void visit(NotFloorError err) {
}
@Override
public void visit(NotWallError err) {
}
@Override
public void visit(NotGroundError err) {
}
@Override
public void visit(SchematronError se) {
mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString() + " - " + se.getNameOfAttribute());
}
@Override
public void visit(SurfaceFragmentedError err) {
}
@Override
public void visit(DegeneratedRingError err) {
}
@Override
public void visit(AttributeMissingError err) {
mainWindow.getGlobalErrorsView().getItems().add(err.getErrorId().getIdString()
+ " - " + err.getNameOfAttribute());
}
@Override
public void visit(AttributeValueWrongError err) {
mainWindow.getGlobalErrorsView().getItems().add(err.getErrorId().getIdString()
+ " - " + err.getNameOfAttribute());
}
@Override
public void visit(AttributeInvalidError err) {
mainWindow.getGlobalErrorsView().getItems().add(err.getErrorId().getIdString()
+ " - " + err.getNameOfAttribute());
}
@Override
public void visit(PolygonWithoutSurfaceError err) {
}
@Override
public void visit(CheckError err) {
}
@Override
public void visit(SolidError err) {
}
@Override
public void visit(XMLValidationError err) {
mainWindow.getGlobalErrorsView().getItems().add(err.getErrorId().getIdString()
+ " - " + err.getMessage());
}
}
package de.hft.stuttgart.citydoctor2.gui;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
import java.io.IOException;
public class IndeterminateSpinnerDialog {
private Stage stage;
@FXML
private Label messageLbl;
public IndeterminateSpinnerDialog(Window parent, String message) throws IOException {
FXMLLoader loader = new FXMLLoader(LoadingInfoDialog.class.getResource("IndeterminateSpinnerDialog.fxml"));
loader.setController(this);
VBox box = loader.load();
stage = new Stage(StageStyle.UNDECORATED);
Scene scene = new Scene(box);
scene.setFill(null);
stage.setScene(scene);
stage.initOwner(parent);
stage.initModality(Modality.APPLICATION_MODAL);
messageLbl.setText(message);
}
public void changeMessage(String message){
messageLbl.setText(message);
}
public void show() {
stage.show();
}
public void hide() {
stage.hide();
}
}
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