package eu.simstadt.nf4j; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.osgeo.proj4j.BasicCoordinateTransform; import org.osgeo.proj4j.CRSFactory; import org.osgeo.proj4j.CoordinateReferenceSystem; import org.osgeo.proj4j.ProjCoordinate; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Builds nF import and export jobs using nF's XML job format. Please read the nF manual if you want more * details about the numerous job attributes listed below. * * @author Marcel Bruse */ public class JobBuilderImpl implements JobBuilder { /** Supported version of the novaFACTORY. */ public static final String NOVA_FACTORY_VERSION = "6.3.1.1"; /** The version of the XML export job format. */ public static final String EXPORT_JOB_VERSION = "1.0.0"; @Override public String supportsNFVersion() { return NOVA_FACTORY_VERSION; } @Override public String supportsExportJobVersion() { return EXPORT_JOB_VERSION; } @Override public String supportsImportJobVersion() { return null; } /** * This is an intermediate prototype. * * @param jobDescriptor A job descriptor which describes the export job with all its attributes according to * a valid nF export job DTD. * @return Returns a string representation of the nF export job. */ @Override public File buildExportJobFile(ExportJobDescriptor jobDescriptor) { File result = null; if (Objects.nonNull(jobDescriptor) && jobDescriptor.isValid()) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.newDocument(); Element root = doc.createElement("EXPORT_JOB"); root.setAttribute("version", supportsExportJobVersion()); doc.appendChild(root); Element job = doc.createElement("job"); root.appendChild(job); Element initiator = doc.createElement("initiator"); initiator.appendChild(doc.createTextNode(jobDescriptor.getInitiator())); job.appendChild(initiator); Element jobnumber = doc.createElement("jobnumber"); jobnumber.appendChild(doc.createTextNode(jobDescriptor.getJobnumber())); job.appendChild(jobnumber); Element account = doc.createElement("account"); account.appendChild(doc.createTextNode("Habib")); job.appendChild(account); Element product = doc.createElement("product"); product.appendChild(doc.createTextNode(jobDescriptor.getProduct())); root.appendChild(product); Element layers = doc.createElement("layers"); layers.setAttribute("color", jobDescriptor.getColor()); layers.setAttribute("mono", jobDescriptor.getMono()); layers.setAttribute("plotLabelSrs", jobDescriptor.getPlotLabelSrs()); layers.setAttribute("plotframe", jobDescriptor.getPlotframe()); layers.setAttribute("single", jobDescriptor.getSingle()); root.appendChild(layers); appendLayers(doc, layers, jobDescriptor.getLayerList()); Element srs = doc.createElement("srs"); srs.appendChild(doc.createTextNode(jobDescriptor.getSrs())); root.appendChild(srs); Element extent = doc.createElement("extent"); extent.setAttribute("merge_mapsheets", jobDescriptor.getMergeMapsheets()); root.appendChild(extent); Element polygon = appendRegionPolygon(doc, jobDescriptor.regionPolygon); extent.appendChild(polygon); Element resolution = doc.createElement("resolution"); resolution.appendChild(doc.createTextNode(jobDescriptor.getResolution())); root.appendChild(resolution); Element scale = doc.createElement("scale"); scale.appendChild(doc.createTextNode(jobDescriptor.getScale())); root.appendChild(scale); Element format = doc.createElement("format"); // TODO format.setAttribute("alphalinscale", "0.0"); format.setAttribute("alphascale", "1.0"); format.setAttribute("citygml_actfunc", "tolod1"); format.setAttribute("citygml_apptheme", ""); format.setAttribute("citygml_elemclasses", "true"); format.setAttribute("citygml_lodmode", "all"); format.setAttribute("citygml_lods", "1"); format.setAttribute("citygml_metadata", "true"); format.setAttribute("citygml_outmode", "normal"); format.setAttribute("dtm", "false"); format.setAttribute("foredit", "false"); format.setAttribute("materialcopymode", "none"); format.setAttribute("polyopts_reverse", "false"); format.setAttribute("relcoords", "false"); format.setAttribute("rooftxr", "false"); format.setAttribute("roundcoords", "3"); format.setAttribute("schemetxr", "false"); format.setAttribute("solar", "false"); format.setAttribute("solargeoplex", "false"); format.setAttribute("tex", "false"); format.setAttribute("tolod1", "true"); format.setAttribute("tolod1_add", "false"); format.setAttribute("tolod1_delrest", "false"); format.setAttribute("tolod1_dstattrabsgrnd", ""); format.setAttribute("tolod1_dstattrabsheight", ""); format.setAttribute("tolod1_dstattrrelheight", ""); format.setAttribute("tolod1_dstattrzmode", ""); format.setAttribute("tolod1_geommode", "lod1solid"); format.setAttribute("tolod1_lodsrcmode", "all"); format.setAttribute("tolod1_triangulate", "false"); format.setAttribute("tolod1_zroofmode", "mid"); format.setAttribute("tolod1_zusegenattrs", ""); format.setAttribute("tolod1_zusemeasuredheight", "false"); format.setAttribute("xyz", "false"); format.appendChild(doc.createTextNode("CityGML")); root.appendChild(format); Element exportmetadata = doc.createElement("exportmetadata"); exportmetadata.setAttribute("calibration", jobDescriptor.getCalibration()); exportmetadata.setAttribute("xmetadata", jobDescriptor.getXmetadata()); exportmetadata.appendChild(doc.createTextNode(jobDescriptor.getExportmetadata())); root.appendChild(exportmetadata); Element addfile = doc.createElement("addfile"); addfile.setAttribute("col", jobDescriptor.getCol()); addfile.setAttribute("eck", jobDescriptor.getEck()); root.appendChild(addfile); Element usenodatamask = doc.createElement("usenodatamask"); usenodatamask.appendChild(doc.createTextNode(jobDescriptor.getUsenodatamask())); root.appendChild(usenodatamask); Element usepdctborderpoly = doc.createElement("usepdctborderpoly"); usepdctborderpoly.appendChild(doc.createTextNode(jobDescriptor.getUsepdctborderpoly())); root.appendChild(usepdctborderpoly); Element dhkresolvereferences = doc.createElement("dhkresolvereferences"); dhkresolvereferences.appendChild(doc.createTextNode(jobDescriptor.getDhkresolvereferences())); root.appendChild(dhkresolvereferences); Element zipresult = doc.createElement("zipresult"); zipresult.appendChild(doc.createTextNode(jobDescriptor.getZipresult())); root.appendChild(zipresult); Element userdescription = doc.createElement("userdescription"); root.appendChild(userdescription); Element namingpattern = doc.createElement("namingpattern"); root.appendChild(namingpattern); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); StringWriter writer = new StringWriter(); StreamResult streamResult = new StreamResult(writer); transformer.transform(new DOMSource(doc), streamResult); File tempfile = File.createTempFile(jobDescriptor.getProduct() + "_", ".xml"); PrintWriter printWriter = new PrintWriter(tempfile); printWriter.print(writer.toString()); printWriter.close(); return tempfile; } catch (ParserConfigurationException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } catch (TransformerConfigurationException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } catch (TransformerException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } catch (FileNotFoundException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } catch (IOException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } } return result; } private void appendLayers(Document doc, Element layers, ArrayList layerList) { for (Layer layer : layerList) { Element layerElement = doc.createElement("layer"); layerElement.setAttribute("name", layer.getName()); String product = layer.getProduct(); if (Objects.nonNull(product) && !product.isEmpty()) { layerElement.setAttribute("product", product); } String style = layer.getStyle(); if (Objects.nonNull(style) && !style.isEmpty()) { layerElement.setAttribute("style", style); } layers.appendChild(layerElement); } } /** * Transforms a global WGS 84 position into a coordinate of the given target SRS. * * @param wgs84Position The WGS 84 position to be transformed to a position within the target SRS. * @param targetCRS The target SRS for the transformation. * @return The transformed target position within the target SRS. */ public static ProjCoordinate transformCoordinate(ProjCoordinate wgs84Position, CoordinateReferenceSystem targetCRS) { ProjCoordinate result = new ProjCoordinate(); CRSFactory f = new CRSFactory(); CoordinateReferenceSystem sourceCRS = f.createFromName(CRSWKT.EPSG_4326.wkt); // WGS 84 (used by Google Maps / OpenStreetMap) BasicCoordinateTransform transform = new BasicCoordinateTransform(sourceCRS, targetCRS); transform.transform(wgs84Position, result); return result; } /** * Appends the region polygon to the XML export job document. In order to do this, the given WGS 84 region * polygon will be transformed into a DHDN Gauss-Kruger zone 3 polygon. * * @param doc The XML export job document. * @param regionPolygon The polygon of the region which has been selected to be exported. * @return The w3c.dom.Element of the XML export job which describes the region polygon. */ private static Element appendRegionPolygon(Document doc, List regionPolygon) { Element polygon = doc.createElement("polygon"); polygon.setAttribute("srs", "31467"); CRSFactory f = new CRSFactory(); CoordinateReferenceSystem targetCRS = f.createFromName(CRSWKT.EPSG_31467.wkt); // DHDN Gauss-Kruger zone 3 for (Coord coord : regionPolygon) { ProjCoordinate sourcePosition = new ProjCoordinate(coord.longitude, coord.latitude); ProjCoordinate targetPosition = transformCoordinate(sourcePosition, targetCRS); Element vertex = doc.createElement("vertex"); vertex.setAttribute("x", String.valueOf(targetPosition.x)); vertex.setAttribute("y", String.valueOf(targetPosition.y)); polygon.appendChild(vertex); } return polygon; } /** * Builds a zipped import job file. This file can be sent to a nF server instance by the caller afterwards. * * @param jobDescriptor A job descriptor which describes the import job with all its attributes according to * a valid nF import job DTD. * @return Returns a XML import job document. */ @Override public File buildImportJobFile(ImportJobDescriptor jobDescriptor) { try { // Write the nF start file which triggers and controls the processing of the CityGML file. String startFilename = jobDescriptor.getProduct() + "_" + jobDescriptor.getLeaf() + ".start"; File startfile = new File(System.getProperty("java.io.tmpdir"), startFilename); PrintWriter writer = new PrintWriter(startfile); writer.print(jobDescriptor.getLevel()); writer.close(); // Zip start file and CityGML file File zippedCityGMLFile = File.createTempFile("nF_Import_", ".zip"); ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zippedCityGMLFile)); File cityGMLFile = jobDescriptor.getCityGMLFile(); writeBytesToZipFile(new FileInputStream(cityGMLFile), zos, jobDescriptor.getProduct() + "_" + jobDescriptor.getLeaf() + "_" + jobDescriptor.getLevel() + "_" + jobDescriptor.getOperation() + ".gml"); writeBytesToZipFile(new FileInputStream(startfile), zos, startFilename); zos.close(); return zippedCityGMLFile; } catch (FileNotFoundException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } catch (IOException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } return null; } /** * Writes a file to the given ZipOutputStream which compresses the file. * * @param fis The file input stream to be compressed. * @param zos The zip output stream. * @param zipEntry The new zip entry for the file to be compressed. * @throws IOException You will get some of this, if your streams point to nirvana. */ private void writeBytesToZipFile(FileInputStream fis, ZipOutputStream zos, String zipEntry) throws IOException { zos.putNextEntry(new ZipEntry(zipEntry)); byte[] b = new byte[1024]; int chunkSize; while ((chunkSize = fis.read(b)) > 0) { zos.write(b, 0, chunkSize); } fis.close(); } }