diff --git a/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java b/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java index bb3d0bc71daf3d6efd3acbafcb09740bc1d9ca3f..ababc25ff60cece255a1e07e922ef79b9cf161d3 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 90af5396ef7a9763a5cfea3ea2ec9d496fe3234d..8c283d94349e35459f67ab52528d8fe4e15fe6dd 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 1c2098534fe5b3d61387a006b25fbc75696607c2..f51252e82b50eb0550a51702779bbd20847fa851 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 e10492a136ea98b8ed84bd47d1e240abeb83b50a..ff23cf17780675bb483dd436b3f29608f48f1ad6 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 5e99c908bfd5b1e08a4a236f606b765bf58e4a2a..cebd94b6e1c4e6a90d6334c52f24fa218f38eb53 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");