diff --git a/src/eu/simstadt/regionchooser/RegionChooserBrowser.java b/src/eu/simstadt/regionchooser/RegionChooserBrowser.java index b8869ff16228258e52ff6202bc3cabe526f0a1a2..5e3ee12c749375f5854e926338a967adc149e8e4 100644 --- a/src/eu/simstadt/regionchooser/RegionChooserBrowser.java +++ b/src/eu/simstadt/regionchooser/RegionChooserBrowser.java @@ -37,6 +37,9 @@ public class RegionChooserBrowser extends Region { + /** + * JavaFX Backend for RegionChooser. Inside simstadt_openlayers.js frontend, this class is available as `fxapp`. + */ public class JavaScriptFXBridge { private Path repo; @@ -95,10 +98,11 @@ public void extractZIPtoGML(String zipFilename) throws IOException { zipFile.close(); } - public void downloadRegionFromCityGML(String wktPolygon, String project, String citygml) + public void downloadRegionFromCityGML(String wktPolygon, String project, String citygml, String srsName) throws IOException, ParseException, SAXParseException, XMLStreamException, NumberFormatException, XPathParseException, NavException, XPathEvalException { - StringBuffer sb = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath(project, citygml), wktPolygon); + StringBuffer sb = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath(project, citygml), wktPolygon, + srsName); File buildingIdsFile = selectSaveFileWithDialog(project, citygml, "selected_region"); if (buildingIdsFile != null) { diff --git a/src/eu/simstadt/regionchooser/RegionChooserFX.java b/src/eu/simstadt/regionchooser/RegionChooserFX.java index aa48a55164add2a46bfb18320607c4931a74cc47..5bedc65b00a1efe58310ef6eec0f4f6c000c7cd5 100644 --- a/src/eu/simstadt/regionchooser/RegionChooserFX.java +++ b/src/eu/simstadt/regionchooser/RegionChooserFX.java @@ -13,7 +13,7 @@ public class RegionChooserFX extends Application protected final static Logger LOGGER = Logger.getLogger(RegionChooserFX.class.getName()); /** - * Start point of RegionChooser application. Either "Run as Java" from Eclipse or run + * Starting point of RegionChooser application. Either "Run as Java" from Eclipse or run * "RegionChooser.bat/.sh/.command" from deployed SimStadt folder. * * This application is basically just a scene and a browser for the RegionChooser website (HTML + Javascript frontend diff --git a/src/eu/simstadt/regionchooser/RegionExtractor.java b/src/eu/simstadt/regionchooser/RegionExtractor.java index 4afd38fd899041b5471fdfcafab59bff4af04169..7175ff4b0ff84b27d8b23eb31e3570bae3632a4c 100644 --- a/src/eu/simstadt/regionchooser/RegionExtractor.java +++ b/src/eu/simstadt/regionchooser/RegionExtractor.java @@ -6,6 +6,7 @@ import javax.xml.stream.XMLStreamException; import org.xml.sax.SAXParseException; import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; @@ -34,10 +35,11 @@ * * @param citygmlPath * @param wktPolygon - * @return + * @param string + * @return a StringBuffer, full with the extracted Citygml, including header, buildings and footer. * @throws Exception */ - static public StringBuffer selectRegionDirectlyFromCityGML(Path citygmlPath, String wktPolygon) + static public StringBuffer selectRegionDirectlyFromCityGML(Path citygmlPath, String wktPolygon, String srsName) throws SAXParseException, XMLStreamException, ParseException, XPathParseException, NavException, NumberFormatException, XPathEvalException, IOException { @@ -49,10 +51,7 @@ static public StringBuffer selectRegionDirectlyFromCityGML(Path citygmlPath, Str CityGmlIterator citygml = new CityGmlIterator(citygmlPath); for (BuildingXmlNode buildingXmlNode : citygml) { if (buildingsCount == 0) { - //TODO: Replace original CityGML envelope with a smaller one, corresponding to wktPolygon - // Envelope env = poly.getEnvelopeInternal(); - // System.out.println(env); - sb.append(citygml.getHeader()); + sb.append(replaceEnvelopeInHeader(citygml.getHeader(), poly.getEnvelopeInternal(), srsName)); } buildingsCount += 1; Coordinate coord = new Coordinate(buildingXmlNode.x, buildingXmlNode.y); @@ -68,7 +67,30 @@ static public StringBuffer selectRegionDirectlyFromCityGML(Path citygmlPath, Str LOGGER.info("Buildings found in selected region " + foundBuildingsCount); sb.append(citygml.getFooter()); - return sb; } + + /** + * Some Citygml files include an envelope (bounding box), defined at the very beginning of the file. If the extracted + * region comes from a huge file (e.g. from NYC), it might inherit this header with a huge envelope. Some methods + * might get confused by this wrong envelope, so this method replaces the original envelope with the bounding box + * from the extracting polygon. The real envelope might be even smaller, but it could only be known at the end of the + * parsing, after having analyzed every building. The envelope should be written in the header. + * + * @param header + * @param envelope + * @param srsName + * @return CityGML Header with an updated envelope + */ + private static String replaceEnvelopeInHeader(String header, Envelope envelope, String srsName) { + //NOTE: Sorry for using a regex to parse XML. The header in itself isn't correct, so this looked like the easiest solution. + String headerWithoutEnvelope = header.replaceFirst("(?is).*?", ""); + String newEnvelope = "\r\n" + + " \r\n" + //NOTE: Would srsDimension="2" be better? Should the original Z get extracted? + " " + envelope.getMinX() + " " + envelope.getMinY() + " 0\r\n" + + " " + envelope.getMaxX() + " " + envelope.getMaxY() + " 0\r\n" + + " \r\n" + + "\r\n"; + return headerWithoutEnvelope + newEnvelope; + } } diff --git a/src/eu/simstadt/regionchooser/website/script/simstadt_openlayers.js b/src/eu/simstadt/regionchooser/website/script/simstadt_openlayers.js index 28ba9766fa7a65a85fc34785984c9d8a52230085..4616e0069d45b84d551151775e8af14dc22b2846 100644 --- a/src/eu/simstadt/regionchooser/website/script/simstadt_openlayers.js +++ b/src/eu/simstadt/regionchooser/website/script/simstadt_openlayers.js @@ -229,7 +229,7 @@ function downloadRegionFromCityGML(i) { if (proj4.defs(srsName)){ $("html").addClass("wait"); console.log("Selected region is written in " + srsName + " coordinate system."); - fxapp.downloadRegionFromCityGML(sketchAsWKT(srsName), feature.get("project"), feature.get("name")); + fxapp.downloadRegionFromCityGML(sketchAsWKT(srsName), feature.get("project"), feature.get("name"), srsName); var end = new Date().getTime(); var time = end - start; console.log('DL Execution time: ' + time); diff --git a/test/eu/simstadt/regionchooser/RegionExtractorTests.java b/test/eu/simstadt/regionchooser/RegionExtractorTests.java index ef414762f84c22588ae364143e9913cca4670b05..fa0fdfae5f6be88f38a6dce5ff9ab7fcd17b34e7 100644 --- a/test/eu/simstadt/regionchooser/RegionExtractorTests.java +++ b/test/eu/simstadt/regionchooser/RegionExtractorTests.java @@ -25,11 +25,11 @@ public static int countRegexMatches(String str, String subStr) { @Test public void testExtract3BuildingsFromGSK3Model() throws Throwable { //NOTE: Small region around Martinskirche in Grünbühl - // "EPSG:31467" 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 repo = Paths.get("../TestRepository"); Path citygmlPath = repo.resolve("Gruenbuehl.proj/20140218_Gruenbuehl_LOD2.gml"); - String churchGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon).toString(); + String churchGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, "EPSG:31467") + .toString(); assertEquals(countRegexMatches(churchGMLString, "<(core:)?cityObjectMember"), 3); assertTrue(churchGMLString.contains("Donaustr")); assertTrue(churchGMLString.contains("DEBW_LOD2_203056")); @@ -37,24 +37,33 @@ public void testExtract3BuildingsFromGSK3Model() throws Throwable { assertTrue(churchGMLString.contains("DEBW_LOD2_2909")); assertTrue(churchGMLString.contains("")); + assertTrue("The exported CityGML should contain a new envelope", churchGMLString + .contains("3515829.0815150724 5415766.204935087 ")); + assertTrue("The exported CityGML should contain a new envelope", churchGMLString + .contains("3515876.489172751 5415823.108586172 ")); } @Test public void testExtractBuildingsWithoutCommentsInBetween() throws Throwable { //NOTE: Small region around WashingtonSquare - // "EPSG:32118" 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 repo = Paths.get("../TestRepository"); Path citygmlPath = repo.resolve("NewYork.proj/ManhattanSmall.gml"); - String archGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon).toString(); + String archGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, "EPSG:32118") + .toString(); assertEquals(countRegexMatches(archGMLString, "<(core:)?cityObjectMember"), 2); assertTrue(archGMLString.contains("WASHINGTON SQUARE")); assertTrue(archGMLString.contains("uuid_c0980a6e-05ea-4d09-bc83-efab226945a1")); assertTrue(archGMLString.contains("uuid_0985cebb-922d-4b3e-95e5-15dc6089cd28")); assertTrue(archGMLString.contains("298393.46959639067 59277.34021543693 -11.892070104139751")); assertFalse("The exported CityGML shouldn't contain the original envelope", familyCourtBuilding .contains("305641.79529639013 67101.44881543722 547.7591871983744")); + assertTrue("The exported CityGML should contain a new envelope", familyCourtBuilding + .contains("299721.46983062755 61021.99295737501 ")); + assertTrue("The exported CityGML should contain a new envelope", familyCourtBuilding + .contains("299823.9079725632 61122.68126771413 ")); + assertTrue("The exported CityGML should contain a new envelope with the correct EPSG", familyCourtBuilding + .contains("")); } @Test public void testExtract0BuildingsWithWrongCoordinates() throws Throwable { //NOTE: Small region, far away from NYC - // "EPSG:32118" String wktPolygon = "POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))"; Path repo = Paths.get("../TestRepository"); Path citygmlPath = repo.resolve("NewYork.proj/ManhattanSmall.gml"); - String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon).toString(); + String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, "EPSG:32118") + .toString(); assertEquals(countRegexMatches(emptyGMLString, "<(core:)?cityObjectMember"), 0); assertTrue(emptyGMLString.contains("