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

Merge branch 'dev_embedded_geodb' into 'dev'

!Geodatabase implementation

See merge request !36
parents 2fe401b3 e2cce980
Pipeline #12243 passed with stage
in 2 minutes and 14 seconds
...@@ -46,7 +46,7 @@ public class NonManifoldEdgeSystemTest { ...@@ -46,7 +46,7 @@ public class NonManifoldEdgeSystemTest {
@Test @Test
public void testNonManifoldEdge1() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testNonManifoldEdge1() 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_NON_MANIFOLD_EDGE); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NON_MANIFOLD_EDGE);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
} }
...@@ -59,7 +59,7 @@ public class NonManifoldEdgeSystemTest { ...@@ -59,7 +59,7 @@ public class NonManifoldEdgeSystemTest {
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-SO-0004-T0001.gml", CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0004-T0001.gml",
paramMap); paramMap);
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_NON_MANIFOLD_EDGE); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NON_MANIFOLD_EDGE);
assertEquals(ResultStatus.ERROR, cr.getResultStatus()); assertEquals(ResultStatus.ERROR, cr.getResultStatus());
assertEquals(ErrorId.GE_S_NON_MANIFOLD_EDGE, cr.getError().getErrorId()); assertEquals(ErrorId.GE_S_NON_MANIFOLD_EDGE, cr.getError().getErrorId());
......
...@@ -43,7 +43,7 @@ public class NonManifoldVertexSystemTest { ...@@ -43,7 +43,7 @@ public class NonManifoldVertexSystemTest {
@Test @Test
public void testNonManifoldVertex1() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testNonManifoldVertex1() 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_NON_MANIFOLD_VERTEX); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NON_MANIFOLD_VERTEX);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
} }
...@@ -51,7 +51,7 @@ public class NonManifoldVertexSystemTest { ...@@ -51,7 +51,7 @@ public class NonManifoldVertexSystemTest {
@Test @Test
public void testNonManifoldVertex2() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testNonManifoldVertex2() throws CityGmlParseException, IOException, InvalidGmlFileException {
CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0003-T0001.gml"); CityDoctorModel c = TestUtil.loadAndCheckCityModel("src/test/resources/SimpleSolid_SrefBS-GE-gml-SO-0003-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_NON_MANIFOLD_VERTEX); CheckResult cr = g.getCheckResult(CheckId.C_GE_S_NON_MANIFOLD_VERTEX);
assertEquals(ResultStatus.ERROR, cr.getResultStatus()); assertEquals(ResultStatus.ERROR, cr.getResultStatus());
assertEquals(ErrorId.GE_S_NON_MANIFOLD_VERTEX, cr.getError().getErrorId()); assertEquals(ErrorId.GE_S_NON_MANIFOLD_VERTEX, cr.getError().getErrorId());
......
...@@ -80,7 +80,7 @@ public class NumPointsCheckTest { ...@@ -80,7 +80,7 @@ public class NumPointsCheckTest {
public void testNumPoints3() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testNumPoints3() 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();
assertEquals(ResultStatus.OK, r.getCheckResult(CheckId.C_GE_R_TOO_FEW_POINTS).getResultStatus()); assertEquals(ResultStatus.OK, r.getCheckResult(CheckId.C_GE_R_TOO_FEW_POINTS).getResultStatus());
} }
......
...@@ -43,7 +43,7 @@ public class OrientationSameSystemTest { ...@@ -43,7 +43,7 @@ public class OrientationSameSystemTest {
@Test @Test
public void testOrientationSame1() throws CityGmlParseException, IOException, InvalidGmlFileException { public void testOrientationSame1() 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_ORIENTATION_RINGS_SAME); CheckResult cr = p.getCheckResult(CheckId.C_GE_P_ORIENTATION_RINGS_SAME);
assertEquals(ResultStatus.OK, cr.getResultStatus()); assertEquals(ResultStatus.OK, cr.getResultStatus());
} }
......
...@@ -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());
} }
......
...@@ -107,7 +107,7 @@ public class PlanarTest { ...@@ -107,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());
} }
......
...@@ -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());
......
...@@ -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.17.3</version> <version>3.18.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<artifactId>CityDoctorAutoPro</artifactId> <artifactId>CityDoctorAutoPro</artifactId>
......
...@@ -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.17.3</version> <version>3.18.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<artifactId>CityDoctorGUI</artifactId> <artifactId>CityDoctorGUI</artifactId>
......
...@@ -11,6 +11,7 @@ import java.util.List; ...@@ -11,6 +11,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import de.hft.stuttgart.citydoctor2.parser.ProgressListener;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
...@@ -139,8 +140,6 @@ public class CheckDialog { ...@@ -139,8 +140,6 @@ public class CheckDialog {
@FXML @FXML
private Label schematronFileLabel; private Label schematronFileLabel;
@FXML
private VBox zipArchiveOptionsVBox;
@FXML @FXML
private CheckBox streamCheckOption; private CheckBox streamCheckOption;
@FXML @FXML
...@@ -161,7 +160,6 @@ public class CheckDialog { ...@@ -161,7 +160,6 @@ public class CheckDialog {
private CityDoctorController controller; private CityDoctorController controller;
private FilterPane filterPane; private FilterPane filterPane;
private MainWindow window; private MainWindow window;
private boolean zipArchiveMode;
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;
...@@ -181,7 +179,6 @@ public class CheckDialog { ...@@ -181,7 +179,6 @@ public class CheckDialog {
} }
public void initialize() { public void initialize() {
setupZipArchiveOptions();
createEnableColumn(geometricTable); createEnableColumn(geometricTable);
createNameColumn(geometricTable); createNameColumn(geometricTable);
...@@ -286,46 +283,6 @@ public class CheckDialog { ...@@ -286,46 +283,6 @@ public class CheckDialog {
}); });
} }
private void setupZipArchiveOptions() {
streamCheckOption.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
streamCheckParametersVBox.setDisable(!streamCheckOption.isSelected());
}
});
outputPathBtn.setOnAction(e -> {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setInitialDirectory(Path.of(Settings.get(Settings.LAST_OPEN_FOLDER)).getParent().toFile());
File f = directoryChooser.showDialog(stage.getOwner());
if (f != null) {
outputPathTextField.setText(f.getAbsolutePath());
Settings.set(Settings.LAST_OPEN_FOLDER, f.getAbsolutePath());
}
});
streamCheckOption.setText(Localization.getText("CheckDialog.streamCheckOption"));
streamCheckLabel.setTooltip(new Tooltip(Localization.getText("CheckDialog.streamCheckLabel")));
outputPathLabel.setText(Localization.getText("CheckDialog.outputPathLabel"));
outputPathBtn.setText(Localization.getText("CheckDialog.outputPathBtn"));
pdfReportsOption.setText(Localization.getText("CheckDialog.pdfReportsOption"));
xmlReportsOption.setText(Localization.getText("CheckDialog.xmlReportsOption"));
showZipArchiveOptions(false);
}
/**
* Shows the option panel for ZIP-Archive checking and enables the ZIP-Archive checking mode for this dialog.
*
* @param enabled
*/
public void showZipArchiveOptions(boolean enabled) {
zipArchiveOptionsVBox.setDisable(!enabled);
zipArchiveOptionsVBox.setVisible(enabled);
zipArchiveMode = enabled;
}
private void setupSelectButton() { private void setupSelectButton() {
selectBtn.setOnAction(ae -> { selectBtn.setOnAction(ae -> {
...@@ -494,25 +451,13 @@ public class CheckDialog { ...@@ -494,25 +451,13 @@ public class CheckDialog {
String startingChecks = Localization.getText("CheckDialog.startingChecks"); String startingChecks = Localization.getText("CheckDialog.startingChecks");
logger.info(startingChecks); logger.info(startingChecks);
} }
if (zipArchiveMode) { ProgressListener l = new ProgressListener() {
Path outputPath = null; @Override
if (streamCheckOption.isSelected()) { public void updateProgress(float state) {
if (outputPathTextField.getText().isEmpty()) { Platform.runLater(() -> progress.setProgress(state));
Alert warning = new Alert(Alert.AlertType.ERROR);
warning.setTitle("Error");
warning.setContentText(Localization.getText("CheckDialog.noPathError"));
warning.showAndWait();
return;
}
outputPath = Path.of(outputPathTextField.getText());
Platform.runLater(() -> progress.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS));
} }
controller.startZipFileChecks(config, progress::setProgress, streamCheckOption.isSelected(), };
outputPath, pdfReportsOption.isSelected(), controller.startChecks(config, l);
xmlReportsOption.isSelected());
} else {
controller.startChecks(config, progress::setProgress);
}
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
String checksDone = Localization.getText("CheckDialog.checksDone"); String checksDone = Localization.getText("CheckDialog.checksDone");
......
...@@ -5,6 +5,7 @@ import de.hft.stuttgart.citydoctor2.check.Checker; ...@@ -5,6 +5,7 @@ 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.check.error.SchematronError;
import de.hft.stuttgart.citydoctor2.database.UnconnectedCache;
import de.hft.stuttgart.citydoctor2.datastructure.*; import de.hft.stuttgart.citydoctor2.datastructure.*;
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;
...@@ -16,16 +17,11 @@ import de.hft.stuttgart.citydoctor2.gui.tree.node.OpeningNode; ...@@ -16,16 +17,11 @@ import de.hft.stuttgart.citydoctor2.gui.tree.node.OpeningNode;
import de.hft.stuttgart.citydoctor2.gui.tree.node.TopLevelCityObjectNode; import de.hft.stuttgart.citydoctor2.gui.tree.node.TopLevelCityObjectNode;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler;
import de.hft.stuttgart.citydoctor2.parser.*; import de.hft.stuttgart.citydoctor2.parser.*;
import de.hft.stuttgart.citydoctor2.utils.ArchivePacker;
import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import de.hft.stuttgart.citydoctor2.zip.MalformedZipFileException;
import de.hft.stuttgart.quality.adapter.types.ErrorAdapter; import de.hft.stuttgart.quality.adapter.types.ErrorAdapter;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.chart.XYChart.Data; import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series; import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType; 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;
...@@ -33,22 +29,18 @@ import javafx.scene.control.TreeView; ...@@ -33,22 +29,18 @@ import javafx.scene.control.TreeView;
import javafx.scene.paint.Color; 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.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.core.CityModel; import org.citygml4j.core.model.core.CityModel;
import org.xml.sax.SAXParseException; import org.xml.sax.SAXParseException;
import java.awt.*;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class CityDoctorController { public class CityDoctorController {
...@@ -61,9 +53,7 @@ public class CityDoctorController { ...@@ -61,9 +53,7 @@ public class CityDoctorController {
private CityDoctorModel model; private CityDoctorModel model;
private ParserConfiguration currentConfig; private ParserConfiguration currentConfig;
private String sourceFile; private String sourceFile;
private CityGmlZipArchive zipArchive;
private ZipEntryManager currentZipEntryManager;
private HashMap<CityDoctorModel, Checker> previousCheckers = null;
private Checker currentChecker; private Checker currentChecker;
private HighlightController highlightController; private HighlightController highlightController;
...@@ -78,7 +68,9 @@ public class CityDoctorController { ...@@ -78,7 +68,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 genericCityObjectsChunkNr = 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;
...@@ -96,7 +88,7 @@ public class CityDoctorController { ...@@ -96,7 +88,7 @@ public class CityDoctorController {
try { try {
Platform.runLater(() -> { Platform.runLater(() -> {
mainWindow.getOpenBtn().setDisable(true); mainWindow.getOpenBtn().setDisable(true);
mainWindow.getZipManagerBtn().setDisable(true);
mainWindow.getWriteReportButton().setDisable(true); mainWindow.getWriteReportButton().setDisable(true);
mainWindow.getMeshGroup().getChildren().clear(); mainWindow.getMeshGroup().getChildren().clear();
...@@ -104,12 +96,6 @@ public class CityDoctorController { ...@@ -104,12 +96,6 @@ public class CityDoctorController {
mainWindow.resetSearchBar(); mainWindow.resetSearchBar();
mainWindow.resetFilterComboBox(); mainWindow.resetFilterComboBox();
}); });
if (path.endsWith(".zip")) {
loadCityGmlZipFile(path, numberOfRoundingPlaces, useValidation, lowMemory);
return;
}
currentZipEntryManager = null;
previousCheckers = null;
currentChecker = null; currentChecker = null;
currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory); currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory);
GMLValidationHandler handler = null; GMLValidationHandler handler = null;
...@@ -136,7 +122,7 @@ public class CityDoctorController { ...@@ -136,7 +122,7 @@ public class CityDoctorController {
} }
}; };
} }
model = CityGmlParser.parseCityGmlFile(path, currentConfig, l, handler, true); model = CityGmlParser.parseCityGmlFile(path, currentConfig, l, handler);
if (!validationIssues.isEmpty()) { if (!validationIssues.isEmpty()) {
StringJoiner sj = new StringJoiner("\n"); StringJoiner sj = new StringJoiner("\n");
validationIssues.stream().forEach(sj::add); validationIssues.stream().forEach(sj::add);
...@@ -156,7 +142,6 @@ public class CityDoctorController { ...@@ -156,7 +142,6 @@ public class CityDoctorController {
mainWindow.getSaveBtn().setDisable(false); mainWindow.getSaveBtn().setDisable(false);
mainWindow.getResetCameraBtn().setDisable(false); mainWindow.getResetCameraBtn().setDisable(false);
mainWindow.getHideRoofBtn().setDisable(false); mainWindow.getHideRoofBtn().setDisable(false);
mainWindow.getZipManagerBtn().setDisable(true);
mainWindow.getNorthArrow().setVisible(true); mainWindow.getNorthArrow().setVisible(true);
mainWindow.alignNorthArrow(); mainWindow.alignNorthArrow();
setupFeatureTabs(); setupFeatureTabs();
...@@ -167,109 +152,20 @@ public class CityDoctorController { ...@@ -167,109 +152,20 @@ public class CityDoctorController {
} }
} }
private void loadCityGmlZipFile(String path, int numberOfRoundingPlaces, boolean useValidation, public static int getMaxFeaturesPerChunk(){
boolean lowMemory) { return MAX_FEATURES_PER_CHUNK;
currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory);
try {
zipArchive = CityGmlZipArchive.register(path);
} catch (MalformedZipFileException e) {
Platform.runLater(() -> {
Alert alert = new Alert(Alert.AlertType.ERROR, e.getMessage());
alert.showAndWait();
});
} catch (Exception e) {
Platform.runLater(() -> {
ExceptionDialog exDialog = new ExceptionDialog();
exDialog.show(e);
});
}
if (zipArchive != null) {
previousCheckers = new HashMap<>();
Platform.runLater(() -> {
try {
currentZipEntryManager = new ZipEntryManager(mainWindow.getMainStage(), this);
currentZipEntryManager.show();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}
public void decompressZipEntry(CityGmlZipEntry entry, ProgressListener l) {
entry.loadEntry(currentConfig, l);
}
public CityGmlZipArchive getZipArchive() {
return zipArchive;
}
public void loadZipEntry(CityGmlZipEntry entry) {
if (!entry.isLoaded()) {
entry.loadEntry(currentConfig);
}
if (entry.getErrorType() != null) {
throw new IllegalArgumentException("Attempted to load erroneous zip entry");
}
try {
Platform.runLater(() -> {
mainWindow.getOpenBtn().setDisable(true);
mainWindow.getZipManagerBtn().setDisable(true);
mainWindow.getWriteReportButton().setDisable(true);
mainWindow.getMeshGroup().getChildren().clear();
clearTrees();
mainWindow.resetSearchBar();
mainWindow.resetFilterComboBox();
});
model = entry.getModel();
currentChecker = previousCheckers.getOrDefault(model, null);
mainWindow.getClickHandler().setConfig(currentConfig);
sourceFile = entry.getArchive().getArchivePath().toString();
renderer.reset();
Platform.runLater(() -> {
mainWindow.addFileNameToTitle(entry.getArchive().getArchivePath().toString());
mainWindow.getCheckButton().setDisable(false);
mainWindow.getLod1Btn().setDisable(false);
mainWindow.getLod2Btn().setDisable(false);
mainWindow.getLod3Btn().setDisable(false);
mainWindow.getLod4Btn().setDisable(false);
mainWindow.getWorldBtn().setDisable(false);
mainWindow.getSaveBtn().setDisable(false);
mainWindow.getResetCameraBtn().setDisable(false);
mainWindow.getHideRoofBtn().setDisable(false);
mainWindow.getZipManagerBtn().setDisable(false);
mainWindow.getNorthArrow().setVisible(true);
mainWindow.alignNorthArrow();
setupFeatureTabs();
buildTrees();
updateFeatureTrees();
mainWindow.getWriteReportButton().setDisable(currentChecker == null);
});
} finally {
Platform.runLater(() -> mainWindow.getOpenBtn().setDisable(false));
}
}
public void showZipManager() {
Platform.runLater(() -> currentZipEntryManager.show());
} }
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() {
...@@ -282,7 +178,7 @@ public class CityDoctorController { ...@@ -282,7 +178,7 @@ public class CityDoctorController {
buildWater(model); buildWater(model);
buildLand(model); buildLand(model);
buildCityFurniture(model); buildCityFurniture(model);
buildOtherCityObjects(model); buildGenericCityObjects(model);
buildTunnel(model); buildTunnel(model);
} }
...@@ -295,126 +191,134 @@ public class CityDoctorController { ...@@ -295,126 +191,134 @@ public class CityDoctorController {
waterChunkNr.set(0); waterChunkNr.set(0);
landChunkNr.set(0); landChunkNr.set(0);
cityFurnitureChunkNr.set(0); cityFurnitureChunkNr.set(0);
genericCityObjectsChunkNr.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 CityObjectGroupNode(model.getLand(), "Land", TreeItem<Renderable> landRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Land",
Color.BROWN)); 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++) {
CityObject land = list.get(i);
CityObjectNode node = new CityObjectNode(land); CityObjectNode node = new CityObjectNode(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);
});
}
} }
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 CityObjectGroupNode(model.getCityFurniture(), TreeItem<Renderable> cityFurnitureRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(),
"City Furniture", Color.BLUEVIOLET)); "City Furniture", Color.BLUEVIOLET));
cityFurnitureRoot.setExpanded(true); cityFurnitureRoot.setExpanded(true);
cityFurnitureView.setRoot(cityFurnitureRoot); cityFurnitureView.setRoot(cityFurnitureRoot);
buildCityFurnitureTreeFromList(model.getCityFurniture(), cityFurnitureView.getRoot()); 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.getGenericCityObjectView(); TreeView<Renderable> otherObjectsView = mainWindow.getOtherCityObjectView();
TreeItem<Renderable> otherObjectsRoot = new TreeItem<>(new CityObjectGroupNode(model.getGenericCityObjects(), TreeItem<Renderable> otherObjectsRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(),
"Generic CityObjects", Color.DARKGRAY)); "Generic CityObjects", Color.DARKGRAY));
otherObjectsRoot.setExpanded(true); otherObjectsRoot.setExpanded(true);
otherObjectsView.setRoot(otherObjectsRoot); otherObjectsView.setRoot(otherObjectsRoot);
buildGenericCityObjectTreeFromList(model.getGenericCityObjects(), otherObjectsView.getRoot()); Supplier<Stream<CityObject>> supply = model::getOtherCityObjects;
addMoreButtonIfNecessary(model.getGenericCityObjects(), otherObjectsView, otherObjectsRoot, genericCityObjectsChunkNr); 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 CityObjectGroupNode(model.getWater(), "Water", TreeItem<Renderable> waterRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Water",
Color.LIGHTSKYBLUE)); Color.LIGHTSKYBLUE));
waterRoot.setExpanded(true); waterRoot.setExpanded(true);
waterView.setRoot(waterRoot); waterView.setRoot(waterRoot);
buildWaterTreeFromList(model.getWater(), waterRoot); 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 CityObjectGroupNode(model.getBridges(), "Bridges", TreeItem<Renderable> bridgeRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Bridges",
Color.CORAL)); 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(CityDoctorModel model) { private void buildTransportation(CityDoctorModel model) {
if (model.getTransportation().isEmpty()) { if (model.getTransportationCount() == 0) {
return; return;
} }
TreeView<Renderable> transView = mainWindow.getTransportationView(); TreeView<Renderable> transView = mainWindow.getTransportationView();
TreeItem<Renderable> transRoot = new TreeItem<>(new CityObjectGroupNode(model.getTransportation(), TreeItem<Renderable> transRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(),
"Transportation", Color.YELLOW)); "Transportation", Color.YELLOW));
transRoot.setExpanded(true); transRoot.setExpanded(true);
transView.setRoot(transRoot); transView.setRoot(transRoot);
buildTransportationTreeFromList(model.getTransportation(), transView.getRoot()); Supplier<Stream<TransportationObject>> supply = model::getTransportation;
addMoreButtonIfNecessary(model.getTransportation(), transView, transRoot, transportationChunkNr); buildTransportationTreeFromList(supply, transView.getRoot());
addMoreButtonForTransportation(transView, transRoot, transportationChunkNr, supply,
model.getTransportationCount());
} }
private void buildTunnel(CityDoctorModel model) { private void buildTunnel(CityDoctorModel model) {
if (model.getTunnels().isEmpty()) { if (model.getTunnelCount() == 0) {
return; return;
} }
TreeView<Renderable> tunnelView = mainWindow.getTunnelView(); TreeView<Renderable> tunnelView = mainWindow.getTunnelView();
TreeItem<Renderable> tunnelRoot = new TreeItem<>(new CityObjectGroupNode(model.getTunnels(), "Tunnel", TreeItem<Renderable> tunnelRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Tunnel",
Color.SLATEGRAY)); Color.SLATEGRAY));
tunnelRoot.setExpanded(true); tunnelRoot.setExpanded(true);
tunnelView.setRoot(tunnelRoot); tunnelView.setRoot(tunnelRoot);
buildTunnelTreeFromList(model.getTunnels(), tunnelRoot); Supplier<Stream<Tunnel>> supply = model::getTunnels;
addMoreButtonIfNecessary(model.getTunnels(), tunnelView, tunnelRoot, tunnelChunkNr); buildTunnelTreeFromList(supply, tunnelRoot);
addMoreButtonForTunnel(tunnelView, tunnelRoot, tunnelChunkNr, supply, model.getTunnelCount());
} }
private void buildVegetation(CityDoctorModel model) { private void buildVegetation(CityDoctorModel model) {
if (model.getVegetation().isEmpty()) { if (model.getVegetationCount() == 0) {
return; return;
} }
TreeView<Renderable> vegetationsView = mainWindow.getVegetationView(); TreeView<Renderable> vegetationsView = mainWindow.getVegetationView();
TreeItem<Renderable> vegRoot = new TreeItem<>(new CityObjectGroupNode(model.getVegetation(), TreeItem<Renderable> vegRoot = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(),
"Vegetation", Color.LIGHTGREEN)); "Vegetation", Color.LIGHTGREEN));
vegRoot.setExpanded(true); vegRoot.setExpanded(true);
vegetationsView.setRoot(vegRoot); vegetationsView.setRoot(vegRoot);
buildVegetationTreeFromList(model.getVegetation(), vegRoot); Supplier<Stream<Vegetation>> supply = model::getVegetation;
addMoreButtonIfNecessary(model.getVegetation(), 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,
...@@ -430,7 +334,7 @@ public class CityDoctorController { ...@@ -430,7 +334,7 @@ public class CityDoctorController {
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);
...@@ -446,17 +350,34 @@ public class CityDoctorController { ...@@ -446,17 +350,34 @@ 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 CityObjectGroupNode(model.getBuildings(), "Buildings", TreeItem<Renderable> root = new TreeItem<>(new CityObjectGroupNode(Collections.emptyList(), "Buildings",
Color.WHITE)); Color.WHITE));
root.setExpanded(true); root.setExpanded(true);
buildingsView.setRoot(root); buildingsView.setRoot(root);
buildBuildingTreeFromList(model.getBuildings(), root); Supplier<Stream<Building>> supply = model::getBuildings;
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), buildingsView, root, buildingChunkNr); buildBuildingTreeFromList(supply, root);
addMoreButtonForBuildings(buildingsView, root, buildingChunkNr, supply, model.getBuildingCount());
} }
...@@ -472,7 +393,7 @@ public class CityDoctorController { ...@@ -472,7 +393,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);
...@@ -488,6 +409,141 @@ public class CityDoctorController { ...@@ -488,6 +409,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;
} }
...@@ -502,17 +558,16 @@ public class CityDoctorController { ...@@ -502,17 +558,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,
...@@ -536,7 +591,7 @@ public class CityDoctorController { ...@@ -536,7 +591,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.getGenericCityObjectView().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();
...@@ -583,11 +638,9 @@ public class CityDoctorController { ...@@ -583,11 +638,9 @@ public class CityDoctorController {
return nodeItem; return nodeItem;
} }
private void buildBuildingTreeFromList(List<Building> list, TreeItem<Renderable> root) { 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++) {
Building b = list.get(i);
TreeItem<Renderable> item = buildTopLevelCityObjectNode(b, root, b.getBoundarySurfaces()); TreeItem<Renderable> item = buildTopLevelCityObjectNode(b, root, b.getBoundarySurfaces());
createBuildingInstallationNodes(b, item); createBuildingInstallationNodes(b, item);
...@@ -597,14 +650,12 @@ public class CityDoctorController { ...@@ -597,14 +650,12 @@ public class CityDoctorController {
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++) {
BridgeObject bridge = list.get(i);
TreeItem<Renderable> item = buildTopLevelCityObjectNode(bridge, root, bridge.getBoundarySurfaces()); TreeItem<Renderable> item = buildTopLevelCityObjectNode(bridge, root, bridge.getBoundarySurfaces());
createBridgeRoomNodes(bridge, item); createBridgeRoomNodes(bridge, item);
...@@ -612,55 +663,46 @@ public class CityDoctorController { ...@@ -612,55 +663,46 @@ public class CityDoctorController {
createBridgeFurnitureNodes(bridge, item); createBridgeFurnitureNodes(bridge, item);
createBridgeConstructiveElementsNodes(bridge, item); createBridgeConstructiveElementsNodes(bridge, item);
createBridgePartNodes(bridge, item); createBridgePartNodes(bridge, item);
} });
} }
private void buildTransportationTreeFromList(List<TransportationObject> list, TreeItem<Renderable> root) { private void buildTransportationTreeFromList(Supplier<Stream<TransportationObject>> supply, TreeItem<Renderable> root) {
int transportChunk = transportationChunkNr.get(); int transportChunk = transportationChunkNr.get();
for (int i = transportChunk * MAX_FEATURES_PER_CHUNK; i < (transportChunk + 1) * MAX_FEATURES_PER_CHUNK supply.get().skip((long) transportChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(trans ->{
&& i < list.size(); i++) { TreeItem<Renderable> item = buildTopLevelCityObjectNode(trans, root);
TransportationObject t = list.get(i); createTransportSectionNodes(trans, item);
TreeItem<Renderable> item = buildTopLevelCityObjectNode(t, root); createTrafficSpacesNodes(trans, item);
createTransportSectionNodes(t, item); });
createTrafficSpacesNodes(t, item);
}
} }
private void buildVegetationTreeFromList(List<Vegetation> list, TreeItem<Renderable> root) { private void buildVegetationTreeFromList(Supplier<Stream<Vegetation>> supply, TreeItem<Renderable> root) {
int vegetationChunk = vegetationChunkNr.get(); int vegetationChunk = vegetationChunkNr.get();
for (int i = vegetationChunk * MAX_FEATURES_PER_CHUNK; i < (vegetationChunk + 1) * MAX_FEATURES_PER_CHUNK supply.get().skip((long) vegetationChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(veg ->{
&& i < list.size(); i++) { buildTopLevelCityObjectNode(veg, root);
Vegetation v = list.get(i); });
buildTopLevelCityObjectNode(v, root);
}
} }
private void buildWaterTreeFromList(List<WaterObject> list, TreeItem<Renderable> root) { private void buildWaterTreeFromList(Supplier<Stream<WaterObject>> supply, TreeItem<Renderable> root) {
int waterChunk = waterChunkNr.get(); int waterChunk = waterChunkNr.get();
for (int i = waterChunk * MAX_FEATURES_PER_CHUNK; i < (waterChunk + 1) * MAX_FEATURES_PER_CHUNK supply.get().skip((long) waterChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(wat ->{
&& i < list.size(); i++) { buildTopLevelCityObjectNode(wat, root);
WaterObject w = list.get(i); });
buildTopLevelCityObjectNode(w, root);
}
} }
private void buildCityFurnitureTreeFromList(List<CityFurniture> list, TreeItem<Renderable> root) { private void buildCityFurnitureTreeFromList(Supplier<Stream<CityFurniture>> supply, TreeItem<Renderable> root) {
int cityFurnChunk = cityFurnitureChunkNr.get(); int cityFurnChunk = cityFurnitureChunkNr.get();
for (int i = cityFurnChunk * MAX_FEATURES_PER_CHUNK; i < (cityFurnChunk + 1) * MAX_FEATURES_PER_CHUNK supply.get().skip((long) cityFurnChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(cf ->{
&& i < list.size(); i++) {
CityFurniture cf = list.get(i);
buildTopLevelCityObjectNode(cf, root); buildTopLevelCityObjectNode(cf, root);
} });
} }
private void buildGenericCityObjectTreeFromList(List<GenericCityObject> list, TreeItem<Renderable> root) { private void buildOtherCityObjectsTreeFromList(Supplier<Stream<CityObject>> supply, TreeItem<Renderable> root) {
int genericObjectChunk = genericCityObjectsChunkNr.get(); int genericObjectChunk = otherCityObjectsChunkNr.get();
for (int i = genericObjectChunk * MAX_FEATURES_PER_CHUNK; i < (genericObjectChunk + 1) * MAX_FEATURES_PER_CHUNK supply.get().skip((long) genericObjectChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(gen ->{
&& i < list.size(); i++) { buildTopLevelCityObjectNode(gen, root);
GenericCityObject goc = list.get(i); });
buildTopLevelCityObjectNode(goc, root);
}
} }
private void createTransportSectionNodes(TransportationObject t, TreeItem<Renderable> root) { private void createTransportSectionNodes(TransportationObject t, TreeItem<Renderable> root) {
...@@ -734,10 +776,9 @@ public class CityDoctorController { ...@@ -734,10 +776,9 @@ public class CityDoctorController {
} }
private void buildTunnelTreeFromList(List<Tunnel> list, TreeItem<Renderable> root) { private void buildTunnelTreeFromList(Supplier<Stream<Tunnel>> supply, TreeItem<Renderable> root) {
int tunnelChunk = tunnelChunkNr.get(); int tunnelChunk = tunnelChunkNr.get();
for (int i = tunnelChunk * MAX_FEATURES_PER_CHUNK; i < (tunnelChunk + 1) * MAX_FEATURES_PER_CHUNK && i < list.size(); i++) { supply.get().skip((long) tunnelChunk * MAX_FEATURES_PER_CHUNK).limit(MAX_FEATURES_PER_CHUNK).forEach(tunnel ->{
Tunnel tunnel = list.get(i);
TreeItem<Renderable> item = buildTopLevelCityObjectNode(tunnel, root); TreeItem<Renderable> item = buildTopLevelCityObjectNode(tunnel, root);
createTunnelHollowNodes(tunnel, item); createTunnelHollowNodes(tunnel, item);
...@@ -745,7 +786,8 @@ public class CityDoctorController { ...@@ -745,7 +786,8 @@ public class CityDoctorController {
createTunnelFurnitureNodes(tunnel, item); createTunnelFurnitureNodes(tunnel, item);
createTunnelConstructiveElementsNodes(tunnel, item); createTunnelConstructiveElementsNodes(tunnel, item);
createTunnelPartNodes(tunnel, item); createTunnelPartNodes(tunnel, item);
} });
} }
private void createTunnelInstallationNodes(Tunnel tunnel, TreeItem<Renderable> root) { private void createTunnelInstallationNodes(Tunnel tunnel, TreeItem<Renderable> root) {
...@@ -1036,13 +1078,11 @@ public class CityDoctorController { ...@@ -1036,13 +1078,11 @@ public class CityDoctorController {
config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces()); config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces());
config.setParserConfig(currentConfig); config.setParserConfig(currentConfig);
currentChecker = new Checker(config, model); currentChecker = new Checker(config, model);
if (previousCheckers != null) {
previousCheckers.put(model, currentChecker);
}
currentChecker.runChecks(l); currentChecker.runChecks(l);
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());
updateGlobalErrors(); updateGlobalErrors();
...@@ -1054,137 +1094,10 @@ public class CityDoctorController { ...@@ -1054,137 +1094,10 @@ public class CityDoctorController {
} }
} }
private void updateGlobalErrors() { private void updateGlobalErrors() {
GlobalErrorVisitor globErrVisitor = new GlobalErrorVisitor(mainWindow); GlobalErrorVisitor globErrVisitor = new GlobalErrorVisitor(mainWindow);
for (CheckError e : model.getGlobalErrors()) { for (CheckError e : model.getGlobalErrors()) {
e.accept(globErrVisitor); e.accept(globErrVisitor);
}
}
public void startZipFileChecks(ValidationConfiguration config, ProgressListener l, boolean lowMemoryMode,
Path outputPath, boolean pdfReports, boolean xmlReports) {
if (zipArchive == null && logger.isWarnEnabled()) {
logger.warn(Localization.getText("CityDoctorController.noZipFile"));
}
if (lowMemoryMode) {
startZipFileCheckStream(config, outputPath, pdfReports, xmlReports);
return;
}
try {
Platform.runLater(() -> {
mainWindow.getCheckButton().setDisable(true);
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear();
});
previousCheckers = new HashMap<>();
zipArchive.mountArchive(currentConfig);
config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces());
config.setParserConfig(currentConfig);
int totalEntries = zipArchive.getEntries().size();
int checkedEntries = 0;
for (CityGmlZipEntry entry : zipArchive.getEntries()) {
if (entry.getErrorType() != null && entry.getModel() == null) {
continue;
}
model = entry.getModel();
currentChecker = new Checker(config, model);
currentChecker.runChecks();
previousCheckers.put(model, currentChecker);
checkedEntries++;
if (l != null) {
l.updateProgress((float) checkedEntries / totalEntries);
}
}
Platform.runLater(() -> {
// apply check results to tree views
updateFeatureTrees();
updateTree(mainWindow.getPolygonsView().getRoot());
updateGlobalErrors();
renderer.refresh();
mainWindow.getWriteReportButton().setDisable(false);
currentZipEntryManager.refresh();
});
} finally {
Platform.runLater(() -> mainWindow.getCheckButton().setDisable(false));
}
}
private void startZipFileCheckStream(ValidationConfiguration valConfig, Path outputPath, boolean pdfReports,
boolean xmlReports) {
try {
Path outputZipDirectory = null;
if (outputPath.toFile().isDirectory()) {
String filename = FilenameUtils.removeExtension(zipArchive.getArchivePath().getFileName().toString());
filename = filename + "_validated";
outputZipDirectory = outputPath.resolve(filename);
Files.createDirectories(outputZipDirectory);
} else {
String filename = FilenameUtils.removeExtension(outputPath.toAbsolutePath().getFileName().toString());
outputZipDirectory = Path.of(filename);
}
String xmlReportsDirectory = null;
String pdfReportsDirectory = null;
if (xmlReports) {
xmlReportsDirectory = Files.createDirectories(
outputZipDirectory.resolve("xml_reports/")).toAbsolutePath().toString();
}
if (pdfReports) {
pdfReportsDirectory = Files.createDirectories(
outputZipDirectory.resolve("pdf_reports/")).toAbsolutePath().toString();
}
Checker.streamCheck(zipArchive, xmlReportsDirectory, pdfReportsDirectory, valConfig, outputZipDirectory.toString());
Desktop desktop = Desktop.getDesktop();
desktop.open(outputPath.toFile());
} catch (CityGmlParseException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void saveZipArchiveReports(File outputZipDirectory) {
if (zipArchive == null) {
return;
}
String directoryName = FilenameUtils.removeExtension(outputZipDirectory.getAbsolutePath());
Path tempOutput = Path.of(directoryName + File.separator);
File temp = null;
try {
temp = tempOutput.toFile();
for (CityGmlZipEntry entry : zipArchive.getEntries()) {
if (entry.getErrorType() != null || entry.getModel() == null) {
continue;
}
model = entry.getModel();
if (model.isValidated()) {
Checker checker = previousCheckers.get(model);
Files.createDirectories(tempOutput.resolve(entry.getEntrySubPath()).getParent());
checker.writeXmlReport(tempOutput.resolve(entry.getDisplayName() + "_report.xml").toString());
checker.writePdfReport(tempOutput.resolve(entry.getDisplayName() + "_report.pdf").toString());
}
}
ArchivePacker.packAndDeleteDirectory(tempOutput.toString());
temp = null;
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Export Report");
alert.setContentText(Localization.getText("CityDoctorController.saveZipArchiveReports"));
alert.showAndWait();
} catch (IOException e) {
logger.error(e);
} finally {
try {
if (temp != null) {
FileUtils.deleteDirectory(temp);
}
} catch (IOException e) {
logger.error(e);
}
} }
} }
...@@ -1196,7 +1109,7 @@ public class CityDoctorController { ...@@ -1196,7 +1109,7 @@ 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.getGenericCityObjectView().getRoot()); updateTree(mainWindow.getOtherCityObjectView().getRoot());
updateTree(mainWindow.getTunnelView().getRoot()); updateTree(mainWindow.getTunnelView().getRoot());
renderer.updateErrors(); renderer.updateErrors();
} }
...@@ -1236,15 +1149,13 @@ public class CityDoctorController { ...@@ -1236,15 +1149,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.getTunnels()); for (CheckError error : filteredErrors) {
addErrorStats(stats, model.getTransportation()); ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new);
addErrorStats(stats, model.getVegetation()); stat.incrementCount();
addErrorStats(stats, model.getWater()); }
addErrorStats(stats, model.getCityFurniture());
addErrorStats(stats, model.getGenericCityObjects());
return stats; return stats;
} }
...@@ -1258,13 +1169,15 @@ public class CityDoctorController { ...@@ -1258,13 +1169,15 @@ 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(CityObject co) { 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.addCityObject(co); newModel.addCityObject(co);
newModel.setParsedCityGMLVersion(model.getCityGMLVersion()); newModel.setParsedCityGMLVersion(model.getCityGMLVersion());
...@@ -1319,96 +1232,113 @@ public class CityDoctorController { ...@@ -1319,96 +1232,113 @@ public class CityDoctorController {
case WATER -> filterWaterTree(searchString); case WATER -> filterWaterTree(searchString);
case LAND -> filterLandTree(searchString); case LAND -> filterLandTree(searchString);
case CITY_FURNITURE -> filterCityFurnitureTree(searchString); case CITY_FURNITURE -> filterCityFurnitureTree(searchString);
case GENERIC_CITY_OBJECT -> filterGenericCityObjectTree(searchString); case OTHER_CITY_OBJECT -> filterOtherCityObjectsTree(searchString);
} }
} }
private void filterBuildingTree(String searchString) { private void filterBuildingTree(String searchString) {
List<Building> foundBuildings = filterFeatures(searchString, model.getBuildings()); Supplier<Stream<Building>> foundBuildings = () -> model.getBuildings().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundBuildings.get().count();
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView(); TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot(); TreeItem<Renderable> root = buildingsView.getRoot();
root.getChildren().clear(); root.getChildren().clear();
buildBuildingTreeFromList(foundBuildings, root); buildBuildingTreeFromList(foundBuildings, root);
updateTree(root); updateTree(root);
addMoreButtonToBuildingsIfNecessary(foundBuildings, buildingsView, root, buildingChunkNr); addMoreButtonForBuildings(buildingsView, root, buildingChunkNr, foundBuildings, featureCount);
} }
private void filterBridgeTree(String searchString) { private void filterBridgeTree(String searchString) {
List<BridgeObject> foundBridges = filterFeatures(searchString, model.getBridges()); Supplier<Stream<BridgeObject>> foundBridges = () -> model.getBridges().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundBridges.get().count();
TreeView<Renderable> bridgeView = mainWindow.getBridgeView(); TreeView<Renderable> bridgeView = mainWindow.getBridgeView();
TreeItem<Renderable> root = bridgeView.getRoot(); TreeItem<Renderable> root = bridgeView.getRoot();
root.getChildren().clear(); root.getChildren().clear();
buildBridgeTreeFromList(foundBridges, root); buildBridgeTreeFromList(foundBridges, root);
updateTree(root); updateTree(root);
addMoreButtonIfNecessary(foundBridges, bridgeView, root, bridgeChunkNr); addMoreButtonForBridges(bridgeView, root, bridgeChunkNr, foundBridges, featureCount);
} }
private void filterTunnelTree(String searchString) { private void filterTunnelTree(String searchString) {
List<Tunnel> foundTunnel = filterFeatures(searchString, model.getTunnels()); Supplier<Stream<Tunnel>> foundTunnel = () -> model.getTunnels().filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundTunnel.get().count();
TreeView<Renderable> tunnelView = mainWindow.getTunnelView(); TreeView<Renderable> tunnelView = mainWindow.getTunnelView();
TreeItem<Renderable> root = tunnelView.getRoot(); TreeItem<Renderable> root = tunnelView.getRoot();
root.getChildren().clear(); root.getChildren().clear();
buildTunnelTreeFromList(foundTunnel, root); buildTunnelTreeFromList(foundTunnel, root);
updateTree(root); updateTree(root);
addMoreButtonIfNecessary(foundTunnel, tunnelView, root, tunnelChunkNr); addMoreButtonForTunnel(tunnelView, root, tunnelChunkNr, foundTunnel, featureCount);
} }
private void filterVegetationTree(String searchString) { private void filterVegetationTree(String searchString) {
List<Vegetation> foundVegetation = filterFeatures(searchString, model.getVegetation()); Supplier<Stream<Vegetation>> foundVegetation = () -> model.getVegetation().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundVegetation.get().count();
TreeView<Renderable> vegetationView = mainWindow.getVegetationView(); TreeView<Renderable> vegetationView = mainWindow.getVegetationView();
TreeItem<Renderable> root = vegetationView.getRoot(); TreeItem<Renderable> root = vegetationView.getRoot();
root.getChildren().clear(); root.getChildren().clear();
buildVegetationTreeFromList(foundVegetation, root); buildVegetationTreeFromList(foundVegetation, root);
updateTree(root); updateTree(root);
addMoreButtonIfNecessary(foundVegetation, vegetationView, root, vegetationChunkNr); addMoreButtonForVegetation(vegetationView, root, vegetationChunkNr, foundVegetation, featureCount);
} }
private void filterTransportationTree(String searchString) { private void filterTransportationTree(String searchString) {
List<TransportationObject> foundTransportation = filterFeatures(searchString, model.getTransportation()); Supplier<Stream<TransportationObject>> foundTransportation = () -> model.getTransportation().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundTransportation.get().count();
TreeView<Renderable> transportationView = mainWindow.getTransportationView(); TreeView<Renderable> transportationView = mainWindow.getTransportationView();
TreeItem<Renderable> root = transportationView.getRoot(); TreeItem<Renderable> root = transportationView.getRoot();
root.getChildren().clear(); root.getChildren().clear();
buildTransportationTreeFromList(foundTransportation, root); buildTransportationTreeFromList(foundTransportation, root);
updateTree(root); updateTree(root);
addMoreButtonIfNecessary(foundTransportation, transportationView, root, transportationChunkNr); addMoreButtonForTransportation(transportationView, root, transportationChunkNr,
foundTransportation, featureCount);
} }
private void filterWaterTree(String searchString) { private void filterWaterTree(String searchString) {
List<WaterObject> foundWater = filterFeatures(searchString, model.getWater()); Supplier<Stream<WaterObject>> foundWater = () -> model.getWater().filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundWater.get().count();
TreeView<Renderable> waterView = mainWindow.getWaterView(); TreeView<Renderable> waterView = mainWindow.getWaterView();
TreeItem<Renderable> root = waterView.getRoot(); TreeItem<Renderable> root = waterView.getRoot();
root.getChildren().clear(); root.getChildren().clear();
buildWaterTreeFromList(foundWater, root); buildWaterTreeFromList(foundWater, root);
addMoreButtonIfNecessary(foundWater, waterView, root, waterChunkNr); addMoreButtonForWater(waterView, root, waterChunkNr, foundWater, featureCount);
} }
private void filterLandTree(String searchString) { private void filterLandTree(String searchString) {
List<CityObject> foundLand = filterFeatures(searchString, model.getLand()); Supplier<Stream<CityObject>> foundLand = () -> model.getLand().filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundLand.get().count();
TreeView<Renderable> landView = mainWindow.getTerrainView(); TreeView<Renderable> landView = mainWindow.getTerrainView();
TreeItem<Renderable> root = landView.getRoot(); TreeItem<Renderable> root = landView.getRoot();
root.getChildren().clear(); root.getChildren().clear();
buildLandFromList(foundLand, root); buildLandTreeFromList(foundLand, root);
addMoreButtonIfNecessary(foundLand, landView, root, landChunkNr); addMoreButtonForLand(landView, root, landChunkNr, foundLand, featureCount);
} }
private void filterCityFurnitureTree(String searchString) { private void filterCityFurnitureTree(String searchString) {
List<CityFurniture> foundCityFurniture = filterFeatures(searchString, model.getCityFurniture()); Supplier<Stream<CityFurniture>> foundCityFurniture = () -> model.getCityFurniture().
filter(co -> featureFilter(co, searchString));
int featureCount = (int) foundCityFurniture.get().count();
TreeView<Renderable> cityFurnitureView = mainWindow.getCityFurnitureView(); TreeView<Renderable> cityFurnitureView = mainWindow.getCityFurnitureView();
TreeItem<Renderable> root = cityFurnitureView.getRoot(); TreeItem<Renderable> root = cityFurnitureView.getRoot();
root.getChildren().clear(); root.getChildren().clear();
buildCityFurnitureTreeFromList(foundCityFurniture, root); buildCityFurnitureTreeFromList(foundCityFurniture, root);
addMoreButtonIfNecessary(foundCityFurniture, cityFurnitureView, root, cityFurnitureChunkNr); addMoreButtonForCityFurniture(cityFurnitureView, root, cityFurnitureChunkNr, foundCityFurniture, featureCount);
} }
private void filterGenericCityObjectTree(String searchString) { private void filterOtherCityObjectsTree(String searchString) {
List<GenericCityObject> foundGenericCityObject = filterFeatures(searchString, model.getGenericCityObjects()); Supplier<Stream<CityObject>> foundGenericCityObject = () -> model.getOtherCityObjects().
TreeView<Renderable> genericCityObjectView = mainWindow.getGenericCityObjectView(); filter(co ->featureFilter(co, searchString));
int featureCount = (int) foundGenericCityObject.get().count();
TreeView<Renderable> genericCityObjectView = mainWindow.getOtherCityObjectView();
TreeItem<Renderable> root = genericCityObjectView.getRoot(); TreeItem<Renderable> root = genericCityObjectView.getRoot();
root.getChildren().clear(); root.getChildren().clear();
buildGenericCityObjectTreeFromList(foundGenericCityObject, root); buildOtherCityObjectsTreeFromList(foundGenericCityObject, root);
addMoreButtonIfNecessary(foundGenericCityObject, genericCityObjectView, root, genericCityObjectsChunkNr); 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) {
...@@ -1421,6 +1351,10 @@ public class CityDoctorController { ...@@ -1421,6 +1351,10 @@ 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;
...@@ -1455,141 +1389,192 @@ public class CityDoctorController { ...@@ -1455,141 +1389,192 @@ 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);
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 fillTreeViewWithErrorGenericCityObjects() { public void fillTreeViewWithErrorOtherCityObjects() {
TreeView<Renderable> genericCityObjectsView = mainWindow.getOtherCityObjectView();
TreeItem<Renderable> root = genericCityObjectsView.getRoot();
if (model == null) { if (model == null) {
return; return;
} }
fillTreeViewWithErrorCityObjects(mainWindow.getGenericCityObjectView(), if (root == null) {
model.getGenericCityObjects(), genericCityObjectsChunkNr); 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);
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);
updateTree(mainWindow.getTransportationView().getRoot());
break;
case TUNNEL:
buildTunnel(model);
updateTree(mainWindow.getTunnelView().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.getGenericCityObjectView().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() {
...@@ -1641,7 +1626,7 @@ public class CityDoctorController { ...@@ -1641,7 +1626,7 @@ public class CityDoctorController {
public void delete(TreeItem<Renderable> selectedItem) { public void delete(TreeItem<Renderable> selectedItem) {
if (model == null) { if (model == null) {
return; return;
}/* TODO: Rework this properly via Renderable implementation }/* 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());
...@@ -1654,37 +1639,19 @@ public class CityDoctorController { ...@@ -1654,37 +1639,19 @@ public class CityDoctorController {
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) { if (newV.intValue() == 0) { // Error filter was set to "All"
fillTreeViewFromFeatures(mainWindow.getSelectedTab()); buildTrees();
} else if (newV.intValue() == 1) { updateFeatureTrees();
switch (mainWindow.getSelectedTab()) { } else if (newV.intValue() == 1) { // Error filter was set to "With errors"
case BUILDING: fillTreeViewWithErrorBuildings();
fillTreeViewWithErrorBuildings(); fillTreeViewWithErrorVegetation();
break; fillTreeViewWithErrorBridges();
case VEGETATION: fillTreeViewWithErrorLand();
fillTreeViewWithErrorVegetation(); fillTreeViewWithErrorTransportation();
break; fillTreeViewWithErrorTunnel();
case BRIDGE: fillTreeViewWithErrorWater();
fillTreeViewWithErrorBridges(); fillTreeViewWithErrorCityFurniture();
break; fillTreeViewWithErrorOtherCityObjects();
case LAND:
fillTreeViewWithErrorLand();
break;
case TRANSPORTATION:
fillTreeViewWithErrorTransportation();
break;
case WATER:
fillTreeViewWithErrorWater();
break;
case CITY_FURNITURE:
fillTreeViewWithErrorCityFurniture();
break;
case GENERIC_CITY_OBJECT:
fillTreeViewWithErrorGenericCityObjects();
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);
} }
......
...@@ -85,12 +85,6 @@ public class MainToolBar { ...@@ -85,12 +85,6 @@ public class MainToolBar {
@FXML @FXML
private ImageView lod4View; private ImageView lod4View;
@FXML
private Button zipManagerBtn;
@FXML
private ImageView zipManagerView;
@FXML @FXML
private Button aboutBtn; private Button aboutBtn;
...@@ -153,7 +147,6 @@ public class MainToolBar { ...@@ -153,7 +147,6 @@ public class MainToolBar {
setupReportButton(); setupReportButton();
setupResetCameraButton(); setupResetCameraButton();
setupHideRoofButton(); setupHideRoofButton();
setupZipManagerButton();
loadImages(); loadImages();
gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected())); gridButton.setOnAction(ae -> renderer.showWireFrame(gridButton.isSelected()));
...@@ -232,10 +225,6 @@ public class MainToolBar { ...@@ -232,10 +225,6 @@ public class MainToolBar {
Image img = new Image(inStream); Image img = new Image(inStream);
hideRoofView.setImage(img); hideRoofView.setImage(img);
} }
try (InputStream inStream = MainWindow.class.getResourceAsStream("icons/zip.png")) {
Image img = new Image(inStream);
zipManagerView.setImage(img);
}
} catch (IOException e) { } catch (IOException e) {
// ignore close exception // ignore close exception
} }
...@@ -347,14 +336,6 @@ public class MainToolBar { ...@@ -347,14 +336,6 @@ public class MainToolBar {
} }
private void setupZipManagerButton() {
zipManagerBtn.setDisable(true);
zipManagerBtn.setTooltip(new Tooltip(Localization.getText("MainToolBar.zip")));
zipManagerBtn.setOnAction(ae -> {
mainWindow.showZipManager();
});
}
public Button getCheckButton() { public Button getCheckButton() {
return checkBtn; return checkBtn;
} }
...@@ -407,10 +388,6 @@ public class MainToolBar { ...@@ -407,10 +388,6 @@ public class MainToolBar {
return resetCameraBtn; return resetCameraBtn;
} }
public Button getZipManagerBtn() {
return zipManagerBtn;
}
public HBox getToolBar() { public HBox getToolBar() {
return toolBar; return toolBar;
} }
......
...@@ -453,6 +453,7 @@ public class MainWindow extends Application { ...@@ -453,6 +453,7 @@ public class MainWindow extends Application {
}); });
} }
@SuppressWarnings("java:S2629")
private void checkForStartupLoading() { private void checkForStartupLoading() {
if (loadFileAtStartup) { if (loadFileAtStartup) {
logger.info(Localization.getText("MainWindow.loadGivenFile")); logger.info(Localization.getText("MainWindow.loadGivenFile"));
...@@ -638,7 +639,7 @@ public class MainWindow extends Application { ...@@ -638,7 +639,7 @@ public class MainWindow extends Application {
selectedTab = FeatureType.CITY_FURNITURE; selectedTab = FeatureType.CITY_FURNITURE;
break; break;
case 8: case 8:
selectedTab = FeatureType.GENERIC_CITY_OBJECT; selectedTab = FeatureType.OTHER_CITY_OBJECT;
break; break;
default: default:
throw new IllegalStateException("Unknown tab index: " + index); throw new IllegalStateException("Unknown tab index: " + index);
...@@ -672,7 +673,7 @@ public class MainWindow extends Application { ...@@ -672,7 +673,7 @@ public class MainWindow extends Application {
case CITY_FURNITURE: case CITY_FURNITURE:
cityFurnitureTab.setDisable(value); cityFurnitureTab.setDisable(value);
break; break;
case GENERIC_CITY_OBJECT: case OTHER_CITY_OBJECT:
otherObjectsTab.setDisable(value); otherObjectsTab.setDisable(value);
break; break;
default: default:
...@@ -801,10 +802,13 @@ public class MainWindow extends Application { ...@@ -801,10 +802,13 @@ public class MainWindow extends Application {
}); });
menu.getItems().add(copyToClipboardOption); menu.getItems().add(copyToClipboardOption);
/* Was used for debugging the repair module, functionless since the RenderNode rework.
* Commented out for now, might be needed later on again
MenuItem deleteOption = new MenuItem(Localization.getText("MainWindow.delete")); MenuItem deleteOption = new MenuItem(Localization.getText("MainWindow.delete"));
deleteOption.setOnAction(ae -> controller.delete(featureView.getSelectionModel().getSelectedItem())); deleteOption.setOnAction(ae -> controller.delete(featureView.getSelectionModel().getSelectedItem()));
menu.getItems().add(deleteOption); menu.getItems().add(deleteOption);
featureView.setContextMenu(menu); featureView.setContextMenu(menu);
*/
} }
public void fetchCopyAction(Renderable node) { public void fetchCopyAction(Renderable node) {
...@@ -1011,7 +1015,7 @@ public class MainWindow extends Application { ...@@ -1011,7 +1015,7 @@ public class MainWindow extends Application {
return attributeView; return attributeView;
} }
public TreeView<Renderable> getGenericCityObjectView() { public TreeView<Renderable> getOtherCityObjectView() {
return otherObjectsView; return otherObjectsView;
} }
...@@ -1157,7 +1161,7 @@ public class MainWindow extends Application { ...@@ -1157,7 +1161,7 @@ public class MainWindow extends Application {
case WATER -> new Pair<>(waterTab, waterView); case WATER -> new Pair<>(waterTab, waterView);
case LAND -> new Pair<>(terrainTab, terrainView); case LAND -> new Pair<>(terrainTab, terrainView);
case CITY_FURNITURE -> new Pair<>(cityFurnitureTab, cityFurnitureView); case CITY_FURNITURE -> new Pair<>(cityFurnitureTab, cityFurnitureView);
case GENERIC_CITY_OBJECT -> new Pair<>(otherObjectsTab, otherObjectsView); case OTHER_CITY_OBJECT -> new Pair<>(otherObjectsTab, otherObjectsView);
default -> throw new IllegalArgumentException("unknown feature type: " + coType); default -> throw new IllegalArgumentException("unknown feature type: " + coType);
}; };
} }
...@@ -1178,13 +1182,6 @@ public class MainWindow extends Application { ...@@ -1178,13 +1182,6 @@ public class MainWindow extends Application {
return mainToolBar.getOpenBtn(); return mainToolBar.getOpenBtn();
} }
public void showZipManager() {
controller.showZipManager();
}
public Button getZipManagerBtn() {
return mainToolBar.getZipManagerBtn();
}
public VertexClickHandler getClickHandler() { public VertexClickHandler getClickHandler() {
return clickHandler; return clickHandler;
......
...@@ -120,7 +120,7 @@ public class OpenFileDialog { ...@@ -120,7 +120,7 @@ public class OpenFileDialog {
if (fc == null) { if (fc == null) {
fc = new FileChooser(); fc = new FileChooser();
fc.setTitle(Localization.getText("OpenFileDialog.select")); fc.setTitle(Localization.getText("OpenFileDialog.select"));
fc.getExtensionFilters().add(new ExtensionFilter("GML/XML/ZIP", "*.gml", "*.xml", "*.zip")); fc.getExtensionFilters().add(new ExtensionFilter("GML/XML", "*.gml", "*.xml"));
fc.getExtensionFilters().add(new ExtensionFilter(Localization.getText("MainWindow.all"), "*.*")); fc.getExtensionFilters().add(new ExtensionFilter(Localization.getText("MainWindow.all"), "*.*"));
} }
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, "")); File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
...@@ -153,9 +153,6 @@ public class OpenFileDialog { ...@@ -153,9 +153,6 @@ public class OpenFileDialog {
Thread t = new Thread(() -> { Thread t = new Thread(() -> {
try { try {
if (path.endsWith(".zip")) {
Platform.runLater(() -> progress.setProgress(-1));
}
controller.loadCityGml(path, numberOfRoundingPlaces, progress::setProgress, useValidation, controller.loadCityGml(path, numberOfRoundingPlaces, progress::setProgress, useValidation,
lowMemory); lowMemory);
Platform.runLater(() -> stage.close()); Platform.runLater(() -> stage.close());
......
...@@ -13,6 +13,7 @@ import javafx.scene.paint.PhongMaterial; ...@@ -13,6 +13,7 @@ import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.*; import javafx.scene.shape.*;
import java.util.*; import java.util.*;
import java.util.stream.Stream;
public class TriangulatedGeometry { public class TriangulatedGeometry {
...@@ -91,15 +92,15 @@ public class TriangulatedGeometry { ...@@ -91,15 +92,15 @@ public class TriangulatedGeometry {
addPolygonDataFromCityObjects(model.getVegetation(), triGeom, Color.LIGHTGREEN, filters); addPolygonDataFromCityObjects(model.getVegetation(), triGeom, Color.LIGHTGREEN, filters);
addPolygonDataFromCityObjects(model.getWater(), triGeom, Color.LIGHTSKYBLUE, filters); addPolygonDataFromCityObjects(model.getWater(), triGeom, Color.LIGHTSKYBLUE, filters);
addPolygonDataFromCityObjects(model.getCityFurniture(), triGeom, Color.BLUEVIOLET, filters); addPolygonDataFromCityObjects(model.getCityFurniture(), triGeom, Color.BLUEVIOLET, filters);
addPolygonDataFromCityObjects(model.getGenericCityObjects(), triGeom, Color.BLACK, filters); addPolygonDataFromCityObjects(model.getOtherCityObjects(), triGeom, Color.BLACK, filters);
return triGeom; return triGeom;
} }
private static void addPolygonDataFromCityObjects(List<? extends CityObject> cos, private static void addPolygonDataFromCityObjects(Stream<? extends CityObject> cos,
TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) { TriangulatedGeometry triGeom, Color color, List<ViewFilter> filters) {
CityObjectCollector collector = new CityObjectCollector(); CityObjectCollector collector = new CityObjectCollector();
cos.forEach(cityObject -> cityObject.accept(collector)); cos.forEach(cityObject -> cityObject.accept(collector));
collector.getCityObjects().forEach(cityObject -> addPolygonData(cityObject, triGeom, color, filters)); collector.getCityObjects().forEach(cityObject -> addPolygonData(cityObject, triGeom, color, filters));
......
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