From c49391c1060727f8deb39208ddd9eea7b87d99ca Mon Sep 17 00:00:00 2001 From: Eric Duminil <eric.duminil@gmail.com> Date: Tue, 28 Nov 2023 11:12:34 +0100 Subject: [PATCH] Allow Vegetation to be extracted too. --- .../regionchooser/RegionExtractor.java | 13 +++++----- .../fast_xml_parser/CityGmlIterator.java | 12 ++++----- ...dingXmlNode.java => CityObjectMember.java} | 25 +++++++++++++------ .../fast_xml_parser/ConvexHullCalculator.java | 2 +- .../fast_xml_parser/CitygmlParserTests.java | 13 +++++----- 5 files changed, 38 insertions(+), 27 deletions(-) rename src/main/java/eu/simstadt/regionchooser/fast_xml_parser/{BuildingXmlNode.java => CityObjectMember.java} (76%) diff --git a/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java b/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java index bb3d0bc..ababc25 100644 --- a/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java +++ b/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java @@ -16,8 +16,8 @@ import com.ximpleware.NavException; import com.ximpleware.XPathEvalException; import com.ximpleware.XPathParseException; -import eu.simstadt.regionchooser.fast_xml_parser.BuildingXmlNode; import eu.simstadt.regionchooser.fast_xml_parser.CityGmlIterator; +import eu.simstadt.regionchooser.fast_xml_parser.CityObjectMember; public class RegionExtractor @@ -51,8 +51,9 @@ static int selectRegionDirectlyFromCityGML(String wktPolygon, String srsName, Writer sb, Path... citygmlPaths) throws ParseException, XPathParseException, NavException, IOException { - int buildingsCount = 0; + int cityObjectsCount = 0; int foundBuildingsCount = 0; + // int foundVegetationCount = 0; Geometry poly = WKT_READER.read(wktPolygon); CityGmlIterator citygml = null; @@ -61,11 +62,11 @@ static int selectRegionDirectlyFromCityGML(String wktPolygon, String srsName, Wr LOGGER.info("Parsing " + citygmlPath); //TODO: Allow citygmlPath for folders too, and iterate over gmls? citygml = new CityGmlIterator(citygmlPath); - for (BuildingXmlNode buildingXmlNode : citygml) { - if (buildingsCount == 0) { + for (CityObjectMember buildingXmlNode : citygml) { + if (cityObjectsCount == 0) { sb.append(replaceEnvelopeInHeader(citygml.getHeader(), poly.getEnvelopeInternal(), srsName)); } - buildingsCount += 1; + cityObjectsCount += 1; if (buildingXmlNode.hasCoordinates()) { Coordinate coord = new Coordinate(buildingXmlNode.x, buildingXmlNode.y); Point point = GEOMETRY_FACTORY.createPoint(coord); @@ -74,7 +75,7 @@ static int selectRegionDirectlyFromCityGML(String wktPolygon, String srsName, Wr sb.append(buildingXmlNode.toString()); } } - if (buildingsCount % 1000 == 0) { + if (cityObjectsCount % 1000 == 0) { LOGGER.info("1000 buildings parsed"); } } diff --git a/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/CityGmlIterator.java b/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/CityGmlIterator.java index 90af539..8c283d9 100644 --- a/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/CityGmlIterator.java +++ b/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/CityGmlIterator.java @@ -13,7 +13,7 @@ import com.ximpleware.XPathParseException; -public class CityGmlIterator implements Iterable<BuildingXmlNode> +public class CityGmlIterator implements Iterable<CityObjectMember> { private static final Logger LOGGER = Logger.getLogger(CityGmlIterator.class.getName()); @@ -42,12 +42,12 @@ public CityGmlIterator(Path citygmlPath) throws XPathParseException { parser.parseFile(citygmlPath.toString(), false); this.navigator = parser.getNav(); this.buildingsFinder = new AutoPilot(navigator); - buildingsFinder.selectXPath("/CityModel/cityObjectMember[Building]"); //TODO: Check it's the only correct possibility. //FIXME: BuildingPart too! + buildingsFinder.selectXPath(CityObjectMember.XPATH_PATTERN); } @Override - public Iterator<BuildingXmlNode> iterator() { - return new Iterator<BuildingXmlNode>() { + public Iterator<CityObjectMember> iterator() { + return new Iterator<CityObjectMember>() { @Override public boolean hasNext() { @@ -60,12 +60,12 @@ public boolean hasNext() { } @Override - public BuildingXmlNode next() { + public CityObjectMember next() { try { long offsetAndLength = navigator.getElementFragment(); buildingOffset = (int) offsetAndLength; buildingLength = (int) (offsetAndLength >> 32); - return new BuildingXmlNode(navigator, buildingOffset, buildingLength); + return new CityObjectMember(navigator, buildingOffset, buildingLength); } catch (NavException | NumberFormatException | XPathParseException | XPathEvalException ex) { LOGGER.warning("Error while parsing " + citygmlPath); } diff --git a/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/BuildingXmlNode.java b/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/CityObjectMember.java similarity index 76% rename from src/main/java/eu/simstadt/regionchooser/fast_xml_parser/BuildingXmlNode.java rename to src/main/java/eu/simstadt/regionchooser/fast_xml_parser/CityObjectMember.java index 1c20985..f51252e 100644 --- a/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/BuildingXmlNode.java +++ b/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/CityObjectMember.java @@ -7,11 +7,19 @@ import com.ximpleware.XPathParseException; -public class BuildingXmlNode +/** + * XML Node representing a CityObjectMember + * + */ + +public class CityObjectMember { + //TODO: Check it's the only correct possibility. + //FIXME: BuildingPart too! + static final String XPATH_PATTERN = "/CityModel/cityObjectMember[Building or SolitaryVegetationObject or PlantCover]"; - private int buildingOffset; - private int buildingLength; + private int nodeOffset; + private int nodeLength; private VTDNav navigator; private AutoPilot coordinatesFinder; public Double x; @@ -22,14 +30,15 @@ public Double y; private int coordinatesCount = 0; - public BuildingXmlNode(VTDNav navigator, int buildingOffset, int buildingLength) + public CityObjectMember(VTDNav navigator, int nodeOffset, int nodeLength) throws XPathParseException, XPathEvalException, NavException { this.navigator = navigator; this.coordinatesFinder = new AutoPilot(navigator); - this.buildingLength = buildingLength; - this.buildingOffset = buildingOffset; + this.nodeLength = nodeLength; + this.nodeOffset = nodeOffset; extractCoordinates(); - //TODO: Get Building ID too, in order to avoid duplicates? + //TODO: Get Node ID too, in order to avoid duplicates? + //TODO: Now that "Building" can be Vegetation too, define a method to check class? } public boolean hasCoordinates() { @@ -84,7 +93,7 @@ private void extractCoordinates() public String toString() { try { - return navigator.toRawString(buildingOffset, buildingLength); + return navigator.toRawString(nodeOffset, nodeLength); } catch (NavException ex) { return ""; } diff --git a/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/ConvexHullCalculator.java b/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/ConvexHullCalculator.java index e10492a..ff23cf1 100644 --- a/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/ConvexHullCalculator.java +++ b/src/main/java/eu/simstadt/regionchooser/fast_xml_parser/ConvexHullCalculator.java @@ -55,7 +55,7 @@ public static Geometry calculateFromCityGML(Path citygmlPath) throws XPathParseE GeometryFactory geometryFactory = new GeometryFactory(); ArrayList<Coordinate> allPoints = new ArrayList<>(); CityGmlIterator citygml = new CityGmlIterator(citygmlPath); - for (BuildingXmlNode buildingXmlNode : citygml) { + for (CityObjectMember buildingXmlNode : citygml) { if (buildingXmlNode.hasCoordinates()) { allPoints.add(new Coordinate(buildingXmlNode.xMin, buildingXmlNode.yMin)); allPoints.add(new Coordinate(buildingXmlNode.xMin, buildingXmlNode.yMax)); diff --git a/src/test/java/eu/simstadt/regionchooser/fast_xml_parser/CitygmlParserTests.java b/src/test/java/eu/simstadt/regionchooser/fast_xml_parser/CitygmlParserTests.java index 5e99c90..cebd94b 100644 --- a/src/test/java/eu/simstadt/regionchooser/fast_xml_parser/CitygmlParserTests.java +++ b/src/test/java/eu/simstadt/regionchooser/fast_xml_parser/CitygmlParserTests.java @@ -17,7 +17,7 @@ private void testNoNanInCoordinates(Path citygmlPath) throws XPathParseException { CityGmlIterator buildingXmlNodes = new CityGmlIterator(citygmlPath); - for (BuildingXmlNode buildingXmlNode : buildingXmlNodes) { + for (CityObjectMember buildingXmlNode : buildingXmlNodes) { assertTrue(buildingXmlNode.hasCoordinates(), "Buildings should have coordinates"); assertFalse(Double.isNaN(buildingXmlNode.x), COORDINATE_SHOULD_BE_A_DOUBLE); assertFalse(Double.isNaN(buildingXmlNode.y), COORDINATE_SHOULD_BE_A_DOUBLE); @@ -25,10 +25,11 @@ private void testNoNanInCoordinates(Path citygmlPath) throws XPathParseException assertFalse(Double.isNaN(buildingXmlNode.yMax), COORDINATE_SHOULD_BE_A_DOUBLE); assertFalse(Double.isNaN(buildingXmlNode.xMin), COORDINATE_SHOULD_BE_A_DOUBLE); assertFalse(Double.isNaN(buildingXmlNode.yMin), COORDINATE_SHOULD_BE_A_DOUBLE); - assertTrue(buildingXmlNode.xMax > buildingXmlNode.x, COORDINATES_SHOULD_BE_PLAUSIBLE); - assertTrue(buildingXmlNode.yMax > buildingXmlNode.y, COORDINATES_SHOULD_BE_PLAUSIBLE); - assertTrue(buildingXmlNode.xMin < buildingXmlNode.x, COORDINATES_SHOULD_BE_PLAUSIBLE); - assertTrue(buildingXmlNode.yMin < buildingXmlNode.y, COORDINATES_SHOULD_BE_PLAUSIBLE); + // Some SolitaryVegetationObjects are defined with a single point. + assertTrue(buildingXmlNode.xMax >= buildingXmlNode.x, COORDINATES_SHOULD_BE_PLAUSIBLE); + assertTrue(buildingXmlNode.yMax >= buildingXmlNode.y, COORDINATES_SHOULD_BE_PLAUSIBLE); + assertTrue(buildingXmlNode.xMin <= buildingXmlNode.x, COORDINATES_SHOULD_BE_PLAUSIBLE); + assertTrue(buildingXmlNode.yMin <= buildingXmlNode.y, COORDINATES_SHOULD_BE_PLAUSIBLE); } } @@ -66,7 +67,7 @@ public void testExtractNoCoordsFromEmptyBuilding() throws XPathParseException { Path citygmlPath = repo.resolve("Stöckach_empty_buildings.gml"); CityGmlIterator buildingXmlNodes = new CityGmlIterator(citygmlPath); int counter = 0; - for (BuildingXmlNode buildingXmlNode : buildingXmlNodes) { + for (CityObjectMember buildingXmlNode : buildingXmlNodes) { assertFalse(buildingXmlNode.hasCoordinates(), "Empty Buildings shouldn't have coordinates"); assertTrue(Double.isNaN(buildingXmlNode.x), "Coordinate should be a Nan"); assertTrue(Double.isNaN(buildingXmlNode.y), "Coordinate should be a Nan"); -- GitLab