diff --git a/src/main/java/eu/simstadt/regionchooser/RegionChooserBrowser.java b/src/main/java/eu/simstadt/regionchooser/RegionChooserBrowser.java index 1c323d7e3c29a91d16c2d61486803004827ad50e..efcaf6676dae0e8cbc97c9a62ad3c4c65da3714b 100644 --- a/src/main/java/eu/simstadt/regionchooser/RegionChooserBrowser.java +++ b/src/main/java/eu/simstadt/regionchooser/RegionChooserBrowser.java @@ -1,7 +1,9 @@ package eu.simstadt.regionchooser; +import java.io.BufferedWriter; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.logging.Logger; @@ -76,16 +78,22 @@ public Void call() throws IOException { /** * This method is called from Javascript, with a prepared wktPolygon written in local coordinates. */ - public void downloadRegionFromCityGMLs(String wktPolygon, String project, String csvCitygmls, String srsName) + public int downloadRegionFromCityGMLs(String wktPolygon, String project, String csvCitygmls, String srsName) throws IOException, ParseException, XPathParseException, NavException { // It doesn't seem possible to pass arrays or list from JS to Java. So csvCitygmls contains names separated by ; Path[] paths = Stream.of(csvCitygmls.split(";")).map(s -> citygmlPath(project, s)).toArray(Path[]::new); - StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, srsName, paths); - File buildingIdsFile = selectSaveFileWithDialog(project, + File outputFile = selectSaveFileWithDialog(project, csvCitygmls.replace(";", "_").replace(".gml", ""), "selected_region"); - RegionChooserUtils.writeStringBuilderToFile(sb, buildingIdsFile.toPath()); + + int count; + + try (BufferedWriter gmlWriter = Files.newBufferedWriter(outputFile.toPath())) { + count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, srsName, gmlWriter, paths); + } + + return count; } diff --git a/src/main/java/eu/simstadt/regionchooser/RegionChooserCLI.java b/src/main/java/eu/simstadt/regionchooser/RegionChooserCLI.java index 20d863136aec128d190ece7203eb4da0b248b957..c3d88e2f23680b91dd08cf7093c5d1921da7d5ea 100644 --- a/src/main/java/eu/simstadt/regionchooser/RegionChooserCLI.java +++ b/src/main/java/eu/simstadt/regionchooser/RegionChooserCLI.java @@ -1,5 +1,6 @@ package eu.simstadt.regionchooser; +import java.io.BufferedWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -101,9 +102,10 @@ public Integer call() throws Exception { logInfo("WKT Polygon expressed in local coordinates: " + wktPolygon); - StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, localCRS.toString(), citygmls); - - RegionChooserUtils.writeStringBuilderToFile(sb, outputCityGML); + try (BufferedWriter gmlWriter = Files.newBufferedWriter(outputCityGML)) { + int count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, localCRS.toString(), gmlWriter, citygmls); + logInfo("Found buildings : " + count); + } return 0; } diff --git a/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java b/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java index 8adcfcb7e7ccabc6a18543208fd4de3e154fd87a..316d378bd2c5c92365adeebd7f5d539122746a65 100644 --- a/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java +++ b/src/main/java/eu/simstadt/regionchooser/RegionExtractor.java @@ -1,6 +1,7 @@ package eu.simstadt.regionchooser; import java.io.IOException; +import java.io.Writer; import java.nio.file.Path; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -34,10 +35,12 @@ * * @param wktPolygon * @param srsName + * @param output * @param citygmlPaths * * - * @return a StringBuffer, full with the extracted Citygml, including header, buildings and footer. + * @writes the extracted Citygml, including header, buildings and footer to output + * @return counts of found building. * @throws ParseException * @throws IOException * @throws XPathEvalException @@ -45,15 +48,11 @@ * @throws XPathParseException * @throws NumberFormatException */ - static StringBuilder selectRegionDirectlyFromCityGML(String wktPolygon, String srsName, Path... citygmlPaths) - throws ParseException, XPathParseException, NavException, IOException { - - //TODO: Should actually write directly to a bufferedwriter - //TODO: Should return the number of found buildings. + static int selectRegionDirectlyFromCityGML(String wktPolygon, String srsName, Writer sb, + Path... citygmlPaths) throws ParseException, XPathParseException, NavException, IOException { int buildingsCount = 0; int foundBuildingsCount = 0; - StringBuilder sb = new StringBuilder(); Geometry poly = WKT_READER.read(wktPolygon); CityGmlIterator citygml = null; @@ -90,7 +89,7 @@ static StringBuilder selectRegionDirectlyFromCityGML(String wktPolygon, String s LOGGER.info("Buildings found in selected region " + foundBuildingsCount); sb.append(citygml.getFooter()); - return sb; + return foundBuildingsCount; } /** 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 b165ffac0bd9dd1e5dbd3121d243536c0b05a938..90af5396ef7a9763a5cfea3ea2ec9d496fe3234d 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 @@ -97,7 +97,7 @@ public String getHeader() throws NavException { * @return Citygml footer * @throws NavException */ - public Object getFooter() throws IOException, NavException { + public String getFooter() throws IOException, NavException { int footerOffset = buildingOffset + buildingLength; int footerLength = (int) (Files.size(citygmlPath) - footerOffset); return navigator.toRawString(footerOffset, footerLength); diff --git a/src/main/resources/eu/simstadt/regionchooser/website/script/simstadt_openlayers.js b/src/main/resources/eu/simstadt/regionchooser/website/script/simstadt_openlayers.js index 2ffc45710def0e9e71016aa00212d8d1bfd742f8..1b0573582608e56cede95e0e3f03c7fd7c83acb7 100644 --- a/src/main/resources/eu/simstadt/regionchooser/website/script/simstadt_openlayers.js +++ b/src/main/resources/eu/simstadt/regionchooser/website/script/simstadt_openlayers.js @@ -243,8 +243,9 @@ var regionChooser = (function(){ if (proj4.defs(srsName)){ console.log("Selected region is written in " + srsName + " coordinate system."); try { - fxapp.downloadRegionFromCityGMLs(sketchAsWKT(srsName), project, citygmlNames.join(";"), srsName); - dataPanel.prepend("<h2 class='ok'>Done!</h2><br/>\n"); + var count = fxapp.downloadRegionFromCityGMLs(sketchAsWKT(srsName), project, citygmlNames.join(";"), srsName); + //FIXME: count looks wrong. too high. + dataPanel.prepend("<h2 class='ok'>Done! (" + count + " buildings found) </h2><br/>\n"); } catch (e) { console.warn("ERROR : " + e); dataPanel.prepend("<h2 class='error'>Some problem occured!</h2><br/>\n"); diff --git a/src/test/java/eu/simstadt/regionchooser/RegionChooserTests.java b/src/test/java/eu/simstadt/regionchooser/RegionChooserTests.java index 92d585247e744a04dcc85840d173d6fa1d262076..a0701caa24be30973b65a386b76b4ee1ba425961 100644 --- a/src/test/java/eu/simstadt/regionchooser/RegionChooserTests.java +++ b/src/test/java/eu/simstadt/regionchooser/RegionChooserTests.java @@ -94,10 +94,6 @@ void testExtractRegionFromTwoCitygmlsInWGS84() throws IOException { assertEquals(22, countBuildings(outGML)); } - private long countBuildings(Path outGML) throws IOException { - return Files.readAllLines(outGML).stream().filter(line -> line.contains("bldg:Building gml:id=")).count(); - } - @Test void testExtractRegionWithStandardInput() throws IOException { String wktPolygon = "POLYGON((-73.9959209576448 40.73286384885367, -73.996317924579 40.732359794090684, -73.9947515145143 40.7315061442504, -73.99422580154739 40.73214841515045, -73.9959209576448 40.73286384885367))"; @@ -129,5 +125,9 @@ void testExtractRegionWithMissingInput() throws IOException { assertTrue(err.toString().contains(expectedLog), err.toString() + " should contain " + expectedLog); assertFalse(Files.exists(outGML)); } + + private long countBuildings(Path outGML) throws IOException { + return Files.readAllLines(outGML).stream().filter(line -> line.contains("bldg:Building gml:id=")).count(); + } } diff --git a/src/test/java/eu/simstadt/regionchooser/RegionExtractorTests.java b/src/test/java/eu/simstadt/regionchooser/RegionExtractorTests.java index 9009c68c7a28454b430d0b21ad1a556d231d8f03..ada5e84ca5d0ef745fa5251640ad0a9c4aa31cc5 100644 --- a/src/test/java/eu/simstadt/regionchooser/RegionExtractorTests.java +++ b/src/test/java/eu/simstadt/regionchooser/RegionExtractorTests.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.StringWriter; import java.nio.file.Path; import java.nio.file.Paths; import java.util.regex.Matcher; @@ -35,8 +36,10 @@ void testExtract3BuildingsFromGSK3Model() throws Throwable { //NOTE: Small region around Martinskirche in Grünbühl String wktPolygon = "POLYGON((3515848.896028535 5415823.108586172,3515848.9512289143 5415803.590347393,3515829.0815150724 5415803.338023346,3515830.9784850604 5415793.437034622,3515842.0946056456 5415793.272282251,3515843.3515515197 5415766.204935087,3515864.1064344468 5415766.557899496,3515876.489172751 5415805.433782301,3515876.343844858 5415822.009293416,3515848.896028535 5415823.108586172))"; Path citygmlPath = TEST_REPOSITORY.resolve("Gruenbuehl.proj/20140218_Gruenbuehl_LOD2.gml"); - String churchGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, "EPSG:31467", citygmlPath) - .toString(); + StringWriter gmlWriter = new StringWriter(); + int count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, "EPSG:31467", gmlWriter, citygmlPath); + String churchGMLString = gmlWriter.toString(); + assertEquals(3, count); assertEquals(3, countRegexMatches(churchGMLString, CITY_OBJECT_MEMBER_REGEX)); assertTrue(churchGMLString.contains("Donaustr")); assertTrue(churchGMLString.contains("DEBW_LOD2_203056")); @@ -56,9 +59,11 @@ void testExtract3BuildingsFromGSK3Model() throws Throwable { void testExtractBuildingsWithCommentsInBetween() throws Throwable { String wktPolygon = "POLYGON((300259.78663489706 62835.835907766595,300230.33294975647 62792.0482567884,300213.5667431851 62770.83143720031,300183.6592861123 62730.20347659383,300252.9947486632 62676.938468840905,300273.3862256562 62701.767105345614,300257.5250407747 62715.760413539596,300308.2754543957 62805.14198211394,300259.78663489706 62835.835907766595))"; Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/ManhattanSmall.gml"); - String archGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath) - .toString(); - assertEquals(countRegexMatches(archGMLString, CITY_OBJECT_MEMBER_REGEX), 2); + StringWriter gmlWriter = new StringWriter(); + int count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, gmlWriter, citygmlPath); + String archGMLString = gmlWriter.toString(); + assertEquals(2, count); + assertEquals(2, countRegexMatches(archGMLString, CITY_OBJECT_MEMBER_REGEX)); assertTrue(archGMLString.contains("WASHINGTON SQUARE")); assertTrue(archGMLString.contains("uuid_c0980a6e-05ea-4d09-bc83-efab226945a1")); assertTrue(archGMLString.contains("uuid_0985cebb-922d-4b3e-95e5-15dc6089cd28")); @@ -70,8 +75,10 @@ void testExtractBuildingsWithCommentsInBetween() throws Throwable { void testExtractBuildingsAndChangeEnvelope() throws Throwable { String wktPolygon = "POLYGON((299761.8123557725 61122.68126771413,299721.46983062755 61058.11626595352,299780.84627343423 61021.99295737501,299823.9079725632 61083.3979344517,299761.8123557725 61122.68126771413))"; Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/FamilyCourt_LOD2_with_PLUTO_attributes.gml"); - String familyCourtBuilding = RegionExtractor - .selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath).toString(); + StringWriter gmlWriter = new StringWriter(); + int count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, gmlWriter, citygmlPath); + String familyCourtBuilding = gmlWriter.toString(); + assertEquals(1, count); assertEquals(1, countRegexMatches(familyCourtBuilding, CITY_OBJECT_MEMBER_REGEX)); assertTrue(familyCourtBuilding.contains("Bldg_12210021066")); assertFalse( @@ -95,8 +102,10 @@ void testExtract0BuildingsWithWrongCoordinates() throws Throwable { //NOTE: Small region, far away from NYC String wktPolygon = "POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))"; Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/ManhattanSmall.gml"); - String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath) - .toString(); + StringWriter gmlWriter = new StringWriter(); + int count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, gmlWriter, citygmlPath); + String emptyGMLString = gmlWriter.toString(); + assertEquals(0, count); assertEquals(0, countRegexMatches(emptyGMLString, CITY_OBJECT_MEMBER_REGEX)); assertTrue(emptyGMLString.contains(CITY_MODEL_HEADER)); assertTrue(emptyGMLString.contains(CITY_MODEL_FOOTER)); @@ -107,8 +116,10 @@ void testExtract0BuildingsFromEmptyGML() throws Throwable { //NOTE: Small region, with too many spaces between coordinates String wktPolygon = "POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))"; Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/empty_model.gml"); - String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath) - .toString(); + StringWriter gmlWriter = new StringWriter(); + int count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, gmlWriter, citygmlPath); + String emptyGMLString = gmlWriter.toString(); + assertEquals(0, count); assertEquals(0, countRegexMatches(emptyGMLString, CITY_OBJECT_MEMBER_REGEX)); assertTrue(emptyGMLString.contains(CORE_CITY_MODEL_HEADER)); assertTrue(emptyGMLString.contains(CORE_CITY_MODEL_FOOTER)); @@ -119,8 +130,10 @@ void testExtract0BuildingsFromWeirdGML() throws Throwable { //NOTE: Small region, with too many spaces between coordinates String wktPolygon = "POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))"; Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/broken_nyc_lod2.gml"); - String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath) - .toString(); + StringWriter gmlWriter = new StringWriter(); + int count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, gmlWriter, citygmlPath); + String emptyGMLString = gmlWriter.toString(); + assertEquals(0, count); assertEquals(0, countRegexMatches(emptyGMLString, CITY_OBJECT_MEMBER_REGEX)); assertTrue(emptyGMLString.contains(CORE_CITY_MODEL_HEADER)); assertTrue(emptyGMLString.contains(CORE_CITY_MODEL_FOOTER)); @@ -130,9 +143,11 @@ void testExtract0BuildingsFromWeirdGML() throws Throwable { void testExtractBuildingsFromCitygmlWithoutZinEnvelope() throws Throwable { String wktPolygon = "POLYGON((3512683.1280912133 5404783.732132129,3512719.1608604863 5404714.627650777,3512831.40076119 5404768.344155442,3512790.239106708 5404838.614891164,3512683.1280912133 5404783.732132129))"; Path citygmlPath = TEST_REPOSITORY.resolve("Stuttgart.proj/Stuttgart_LOD0_LOD1_small.gml"); - String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, "EPSG:31463", citygmlPath) - .toString(); - assertEquals(2, countRegexMatches(emptyGMLString, "<bldg:Building gml:id")); + StringWriter gmlWriter = new StringWriter(); + int count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, "EPSG:31463", gmlWriter, citygmlPath); + String twoBuildings = gmlWriter.toString(); + assertEquals(2, count); + assertEquals(2, countRegexMatches(twoBuildings, "<bldg:Building gml:id")); } @Test @@ -141,9 +156,12 @@ void testExtractBuildingsFrom2Citygmls() throws Throwable { Path citygml1 = TEST_REPOSITORY.resolve("Stuttgart.proj/Stuttgart_LOD0_LOD1_small.gml"); Path citygml2 = TEST_REPOSITORY.resolve("Stuttgart.proj/Stöckach_überarbeitete GML-NoBuildingPart.gml"); - String emptyGMLString = RegionExtractor - .selectRegionDirectlyFromCityGML(wktPolygon, "EPSG:31463", citygml1, citygml2).toString(); - assertEquals(17 + 3, countRegexMatches(emptyGMLString, "<bldg:Building gml:id")); + StringWriter gmlWriter = new StringWriter(); + int count = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, "EPSG:31463", gmlWriter, citygml1, + citygml2); + String gmlFromTwoGMLs = gmlWriter.toString(); + assertEquals(17 + 3, count); + assertEquals(17 + 3, countRegexMatches(gmlFromTwoGMLs, "<bldg:Building gml:id")); } diff --git a/src/test/java/eu/simstadt/regionchooser/RegionExtractorWithDifferentInputTests.java b/src/test/java/eu/simstadt/regionchooser/RegionExtractorWithDifferentInputTests.java index 81aff9400bd79cd3fb06b92b59284f5e4316f66f..9f9b8c3eb6aa87d02b4701cad731f9f4a186107b 100644 --- a/src/test/java/eu/simstadt/regionchooser/RegionExtractorWithDifferentInputTests.java +++ b/src/test/java/eu/simstadt/regionchooser/RegionExtractorWithDifferentInputTests.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; +import java.io.StringWriter; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; @@ -28,8 +29,9 @@ void testExtractRegionWithLocalCRS() Path citygmlPath = project.resolve(citygml); CoordinateReferenceSystem localCRS = RegionChooserUtils.crsFromCityGMLHeader(citygmlPath); - StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, localCRS.getName(), citygmlPath); - assertTrue(sb.toString().contains("gml_ZVHMQQ6BZGRT0O3Q6RGXF12BDOV49QIZ58XB"), + StringWriter gmlWriter = new StringWriter(); + RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, localCRS.getName(), gmlWriter, citygmlPath); + assertTrue(gmlWriter.toString().contains("gml_ZVHMQQ6BZGRT0O3Q6RGXF12BDOV49QIZ58XB"), "One weird shaped roof should be inside the region"); } @@ -44,9 +46,9 @@ void testExtractRegionWithWGS84() CoordinateReferenceSystem localCRS = RegionChooserUtils.crsFromCityGMLHeader(citygmlPath); String localWktPolygon = RegionChooserUtils.wktPolygonToLocalCRS(wgs84WktPolygon, localCRS); - StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(localWktPolygon, localCRS.getName(), - citygmlPath); - assertTrue(sb.toString().contains("gml_ZVHMQQ6BZGRT0O3Q6RGXF12BDOV49QIZ58XB"), + StringWriter gmlWriter = new StringWriter(); + RegionExtractor.selectRegionDirectlyFromCityGML(localWktPolygon, localCRS.getName(), gmlWriter, citygmlPath); + assertTrue(gmlWriter.toString().contains("gml_ZVHMQQ6BZGRT0O3Q6RGXF12BDOV49QIZ58XB"), "One weird shaped roof should be inside the region"); } @@ -90,10 +92,10 @@ void testExtractRegionWithOldCoordinates() CoordinateReferenceSystem localCRS = RegionChooserUtils.crsFromCityGMLHeader(citygmlPath); String localWktPolygon = RegionChooserUtils.wktPolygonToLocalCRS(wgs84WktPolygon, localCRS); - StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(localWktPolygon, localCRS.getName(), - citygmlPath); + StringWriter gmlWriter = new StringWriter(); + RegionExtractor.selectRegionDirectlyFromCityGML(localWktPolygon, localCRS.getName(), gmlWriter, citygmlPath); - assertTrue(sb.toString().contains("gml_ZVHMQQ6BZGRT0O3Q6RGXF12BDOV49QIZ58XB"), + assertTrue(gmlWriter.toString().contains("gml_ZVHMQQ6BZGRT0O3Q6RGXF12BDOV49QIZ58XB"), "One weird shaped roof should be inside the region"); } }