package eu.simstadt.regionchooser; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Enumeration; import java.util.prefs.Preferences; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.xml.stream.XMLStreamException; import org.xml.sax.SAXParseException; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; import com.ximpleware.NavException; import com.ximpleware.XPathEvalException; import com.ximpleware.XPathParseException; import eu.simstadt.geo.fast_xml_parser.ConvexHullCalculator; import eu.simstadt.nf4j.ExportJobFromJavaFXRegionChooser; import javafx.application.Platform; import javafx.beans.value.ObservableValue; import javafx.concurrent.Task; import javafx.concurrent.Worker.State; import javafx.geometry.HPos; import javafx.geometry.VPos; import javafx.scene.layout.Region; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javafx.stage.FileChooser; import javafx.stage.Stage; import netscape.javascript.JSObject; 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; private WKTReader wktReader = new WKTReader(); public JavaScriptFXBridge() { Preferences userPrefs = Preferences.userRoot().node("/eu/simstadt/desktop"); String repoString = userPrefs.get("RECENT_REPOSITORY", null); if (repoString == null) { //TODO: Add a FileDialog? repo = Paths.get("../TestRepository"); } else { repo = Paths.get(repoString); } } /** * Launches a background thread in which the hull gets extracted for every CityGML file. The hull gets sent back * to the JS app in order to be displayed. * */ public void refreshHulls() { //NOTE: Could add progress bar? Task task = new Task() { @Override public Void call() throws IOException { ConvexHullCalculator.extractHullsForEveryCityGML(repo, (hullKML -> { Platform.runLater(new Runnable() { @Override public void run() { jsApp.call("addCityGmlHull", hullKML); } }); })); return null; } }; task.setOnRunning(e -> { jsApp.call("display", "Importing citgyml. Please wait."); }); task.setOnSucceeded(e -> { jsApp.call("ready"); }); new Thread(task).start(); } public void downloadRegion(String wktPolygon, String productName, JSObject novaFactoryLayer) throws InterruptedException { //TODO: Ask nf Server about available regions Task task = new Task() { @Override protected Integer call() throws Exception { ExportJobFromJavaFXRegionChooser nfJob = new ExportJobFromJavaFXRegionChooser(); Geometry poly = wktReader.read(wktPolygon); nfJob.processJob(poly, productName, novaFactoryLayer); return 0; } }; new Thread(task).start(); } /* * Download a small region from Gruenbuehl. Useful to test inside js console (with 'fxapp.test();'). */ public void test() { try { downloadRegionFromCityGML( "POLYGON((3515896.6132767177 5415942.563662692,3516013.1135652466 5415930.341095623,3516035.1608944996 5415925.696283888,3516052.531667652 5415905.3452489935,3516053.640043498 5415793.1428597355,3516092.996199113 5415790.117097386,3516086.9957373445 5415687.30812527,3515953.2106800284 5415687.710348818,3515893.4419519473 5415673.416324939,3515876.73573549 5415736.92758554,3515896.6132767177 5415942.563662692))", "Gruenbuehl", "Gruenbuehl_LOD2_validated+ADE.gml", "EPSG:34567"); } catch (Exception ex) { ex.printStackTrace(); } } public void extractZIPtoGML(String zipFilename) throws IOException { ZipFile zipFile = new ZipFile(zipFilename); Enumeration entries = zipFile.entries(); String userName = System.getProperty("user.name"); while (entries.hasMoreElements()) { ZipEntry ze = entries.nextElement(); String zeName = ze.getName(); if (zeName.toLowerCase().contains("gml")) { File extractedCityGML = selectSaveFileWithDialog(null, zeName.replace("_GML.", ".").replace(userName, "novaFACTORY"), ""); if (extractedCityGML != null) { InputStream cityGMLInputStream = zipFile.getInputStream(ze); BufferedReader cityGMLZipReader = new BufferedReader(new InputStreamReader(cityGMLInputStream)); BufferedWriter cityGMLOutput = Files.newBufferedWriter(extractedCityGML.toPath()); String buf = null; while ((buf = cityGMLZipReader.readLine()) != null) { cityGMLOutput.write(buf.replace("srsName=\"\"", "srsName=\"EPSG:31467\"")); //TODO: Get EPSG:id from NovaFactory Server? } cityGMLZipReader.close(); cityGMLInputStream.close(); cityGMLOutput.close(); System.out.println("Extracted"); } } } zipFile.close(); } 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, srsName); File buildingIdsFile = selectSaveFileWithDialog(project, citygml, "selected_region"); if (buildingIdsFile != null) { BufferedWriter writer = Files.newBufferedWriter(buildingIdsFile.toPath()); writer.write(sb.toString()); writer.close(); } } private File selectSaveFileWithDialog(String project, String citygml, String suffix) { Stage mainStage = (Stage) RegionChooserBrowser.this.getScene().getWindow(); FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Save CITYGML ids"); if (project != null) { fileChooser.setInitialDirectory(repo.resolve(project + ".proj").toFile()); } else { fileChooser.setInitialDirectory(repo.toFile()); } if (suffix.isEmpty()) { fileChooser.setInitialFileName(citygml); } else { fileChooser.setInitialFileName(citygml.replace(".", "_" + suffix + ".")); } FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("GML files (*.gml)", "*.gml"); fileChooser.getExtensionFilters().add(extFilter); return fileChooser.showSaveDialog(mainStage); } public boolean checkIfCityGMLSAreAvailable(String project, String citygml) { Path p = citygmlPath(project, citygml); return Files.isReadable(p); } public void log(String text) { System.out.println(text); } private Path citygmlPath(String project, String citygml) { return repo.resolve(project + ".proj").resolve(citygml); } public void importNovaFactoryBoundingBoxes() throws IOException { BufferedReader nf_csv = new BufferedReader(new InputStreamReader( RegionChooserFX.class.getResourceAsStream("website/data/novafactory_products.csv"))); nf_csv.readLine(); String sCurrentLine; while ((sCurrentLine = nf_csv.readLine()) != null) { String[] values = sCurrentLine.trim().split(","); String product = values[1]; // String description = values[2]; String[] srs = values[3].split(" "); String epsgId = srs[srs.length - 1]; // System.out.println(product); jsApp.call("addNovaFactoryProduct", values[8], values[9], values[10], values[11], product, epsgId); } nf_csv.close(); } } final WebView browser = new WebView(); final WebEngine webEngine = browser.getEngine(); final JavaScriptFXBridge fxapp = new JavaScriptFXBridge(); private JSObject jsApp; public RegionChooserBrowser() { //apply the styles getStyleClass().add("browser"); String url = RegionChooserFX.class.getResource("website/index.html").toExternalForm(); webEngine.load(url); // load the web page // process page loading webEngine.getLoadWorker().stateProperty().addListener( (ObservableValue ov, State oldState, State newState) -> { if (newState == State.SUCCEEDED) { jsApp = (JSObject) webEngine.executeScript("regionChooser"); jsApp.call("setFxApp", fxapp); fxapp.refreshHulls(); try { fxapp.importNovaFactoryBoundingBoxes(); } catch (Exception ex) { RegionChooserFX.LOGGER.warning("NovaFactory CSV not found or corrupt"); ex.printStackTrace(); } // fxapp.test(); // System.exit(0); } }); //add the web view to the scene getChildren().add(browser); } @Override protected void layoutChildren() { double w = getWidth(); double h = getHeight(); layoutInArea(browser, 0, 0, w, h, 0, HPos.CENTER, VPos.CENTER); } @Override protected double computePrefWidth(double height) { return 900; } @Override protected double computePrefHeight(double width) { return 600; } }