Commit 5a0bec3d authored by eric.duminil's avatar eric.duminil
Browse files

Removing CRLF from repository.

parent 3f54801c
package eu.simstadt.nf4j.async;
import java.io.File;
import java.util.ArrayList;
import java.util.Objects;
import eu.simstadt.nf4j.ImportJobDescriptor;
/**
* Every instance of this class describes an import job for the novaFACTORY. Instances of NFConnector and
* JobBuilder take JobDescriptions and build XML import job files out of it.
*
* @author Marcel Bruse
*/
public class ImportJobDescription implements ImportJobDescriptor {
/** The version of the novaFACTORY XML export job format. */
public static final String IMPORT_JOB_VERSION = "1.0.0";
/** The default level on which your CityGML will be stored within the nF. */
public static final String DEFAULT_LEVEL = "GML";
/** List of ADE XML schemata which describe additional elements within the CityGML file. */
private ArrayList<File> adeSchemaFileList = new ArrayList<>();
/** The nF product (Produkt) which will keep our CityGML. */
private String product;
/** The nF leaf (Blatt) of the nF product. */
private String leaf;
/** The nF level (Ebene) of the nF product. */
private String level;
/** The operation to be performed for the feature objects of the CityGML file. */
private Operation operation;
/** The CityGML file to be imported to the nF. */
private File cityGMLFile;
/**
* Sets the CityGML file which should be imported by nF.
*
* @param file The CityGML file to be uploaded to the nF.
*/
@Override
public void setCityGMLFile(File cityGMLFile) {
this.cityGMLFile = cityGMLFile;
}
/**
* @return Returns the CityGML file which should be uploaded to the nF.
*/
@Override
public File getCityGMLFile() {
return cityGMLFile;
}
/**
* @return Returns the nF product which will keep our CityGML.
*/
public String getProduct() {
return product;
}
/**
* Sets the nF product for this import job.
*
* @param product The product of our import job.
*/
public void setProduct(String product) {
this.product = product;
}
/**
* @return Returns the nF leaf of the nF product.
*/
public String getLeaf() {
return leaf;
}
/**
* Sets the nF leaf for the nF product.
*
* @param leaf The leaf for the nF product.
*/
public void setLeaf(String leaf) {
this.leaf = leaf;
}
/**
* @return Returns the level of the product.
*/
public String getLevel() {
return level;
}
/**
* Sets the nF level for the nF product.
*
* @param level The level for the nF product.
*/
public void setLevel(String level) {
this.level = level;
}
/**
* If your CityGML file encodes ADE specific elements then you have to add the corresponding schema
* definition file of the used ADE here.
*
* @param adeSchemaFile The schema definition of the used ADE.
*/
public void addADESchemaFile(File adeSchemaFile) {
adeSchemaFileList.add(adeSchemaFile);
}
/**
* @return Returns the list of ADE schemata which are used within your CityGML file.
*/
public ArrayList<File> getADESchemaFileList() {
return adeSchemaFileList;
}
/**
* @return Returns the operation which should be conducted for the features of the CityGML file.
*/
public Operation getOperation() {
return operation;
}
/**
* Sets the operation which should be conducted for the feature objects of the CityGML file.
*
* @param operation The operation which should be conducted for the feature object of the CityGML file.
*/
public void setOperation(Operation operation) {
this.operation = operation;
}
/**
* @return Returns the supported nF job version. This enables your job builder instance to check if
* the job version is compatible with itself.
*/
@Override
public String supportsJobVersion() {
return IMPORT_JOB_VERSION;
}
/**
* This is just a prototype for presentation purposes.
*/
public static ImportJobDescription getDefaultDescriptor() {
ImportJobDescription descriptor = new ImportJobDescription();
descriptor.setLevel(DEFAULT_LEVEL);
return descriptor;
}
/**
* @return Returns true, if product, leaf, level and CityGML file are present.
*/
public boolean isValid() {
if (product.isEmpty()
|| leaf.isEmpty()
|| level.isEmpty()
|| Objects.isNull(cityGMLFile)
|| !cityGMLFile.canRead()) {
return false;
} else {
return true;
}
}
package eu.simstadt.nf4j.async;
import java.io.File;
import java.util.ArrayList;
import java.util.Objects;
import eu.simstadt.nf4j.ImportJobDescriptor;
/**
* Every instance of this class describes an import job for the novaFACTORY. Instances of NFConnector and
* JobBuilder take JobDescriptions and build XML import job files out of it.
*
* @author Marcel Bruse
*/
public class ImportJobDescription implements ImportJobDescriptor {
/** The version of the novaFACTORY XML export job format. */
public static final String IMPORT_JOB_VERSION = "1.0.0";
/** The default level on which your CityGML will be stored within the nF. */
public static final String DEFAULT_LEVEL = "GML";
/** List of ADE XML schemata which describe additional elements within the CityGML file. */
private ArrayList<File> adeSchemaFileList = new ArrayList<>();
/** The nF product (Produkt) which will keep our CityGML. */
private String product;
/** The nF leaf (Blatt) of the nF product. */
private String leaf;
/** The nF level (Ebene) of the nF product. */
private String level;
/** The operation to be performed for the feature objects of the CityGML file. */
private Operation operation;
/** The CityGML file to be imported to the nF. */
private File cityGMLFile;
/**
* Sets the CityGML file which should be imported by nF.
*
* @param file The CityGML file to be uploaded to the nF.
*/
@Override
public void setCityGMLFile(File cityGMLFile) {
this.cityGMLFile = cityGMLFile;
}
/**
* @return Returns the CityGML file which should be uploaded to the nF.
*/
@Override
public File getCityGMLFile() {
return cityGMLFile;
}
/**
* @return Returns the nF product which will keep our CityGML.
*/
public String getProduct() {
return product;
}
/**
* Sets the nF product for this import job.
*
* @param product The product of our import job.
*/
public void setProduct(String product) {
this.product = product;
}
/**
* @return Returns the nF leaf of the nF product.
*/
public String getLeaf() {
return leaf;
}
/**
* Sets the nF leaf for the nF product.
*
* @param leaf The leaf for the nF product.
*/
public void setLeaf(String leaf) {
this.leaf = leaf;
}
/**
* @return Returns the level of the product.
*/
public String getLevel() {
return level;
}
/**
* Sets the nF level for the nF product.
*
* @param level The level for the nF product.
*/
public void setLevel(String level) {
this.level = level;
}
/**
* If your CityGML file encodes ADE specific elements then you have to add the corresponding schema
* definition file of the used ADE here.
*
* @param adeSchemaFile The schema definition of the used ADE.
*/
public void addADESchemaFile(File adeSchemaFile) {
adeSchemaFileList.add(adeSchemaFile);
}
/**
* @return Returns the list of ADE schemata which are used within your CityGML file.
*/
public ArrayList<File> getADESchemaFileList() {
return adeSchemaFileList;
}
/**
* @return Returns the operation which should be conducted for the features of the CityGML file.
*/
public Operation getOperation() {
return operation;
}
/**
* Sets the operation which should be conducted for the feature objects of the CityGML file.
*
* @param operation The operation which should be conducted for the feature object of the CityGML file.
*/
public void setOperation(Operation operation) {
this.operation = operation;
}
/**
* @return Returns the supported nF job version. This enables your job builder instance to check if
* the job version is compatible with itself.
*/
@Override
public String supportsJobVersion() {
return IMPORT_JOB_VERSION;
}
/**
* This is just a prototype for presentation purposes.
*/
public static ImportJobDescription getDefaultDescriptor() {
ImportJobDescription descriptor = new ImportJobDescription();
descriptor.setLevel(DEFAULT_LEVEL);
return descriptor;
}
/**
* @return Returns true, if product, leaf, level and CityGML file are present.
*/
public boolean isValid() {
if (product.isEmpty()
|| leaf.isEmpty()
|| level.isEmpty()
|| Objects.isNull(cityGMLFile)
|| !cityGMLFile.canRead()) {
return false;
} else {
return true;
}
}
}
\ No newline at end of file
package eu.simstadt.nf4j.async;
import java.io.File;
import eu.simstadt.nf4j.ExportJobDescriptor;
import eu.simstadt.nf4j.ImportJobDescriptor;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
/**
* Implementations of JobBuilder build nF import and export jobs using nF's XML job format. There should be
* one implementation for each version of novaFACTORY. The supported version should be returned by
* supportsNFVersion().
*
* @param <I> The import job descriptor implementation for this builder.
* @param <E> The export job descriptor implementation for this builder.
*
* @author Marcel Bruse
*/
public interface JobFileBuilder<I extends ImportJobDescriptor, E extends ExportJobDescriptor> {
/**
* @return Tells the caller the supported version of novaFACTORY.
*/
public String supportsNFVersion();
/**
* @return The supported version of the XML export job format.
*/
public String supportsExportJobVersion();
/**
* @return The supported version of the XML import job format.
*/
public String supportsImportJobVersion();
/**
* Builds a XML export job document. This file can be sent to a nF server instance by the caller afterwards.
*
* @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 XML export job document.
*/
public File buildExportJobFile(E exportJobDescriptor) throws InvalidJobDescriptorException;
/**
* 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
* the nF manual.
* @return Returns a zipped import job file.
*/
public File buildImportJobFile(I importJobDescriptor) throws InvalidJobDescriptorException;
}
package eu.simstadt.nf4j.async;
import java.io.File;
import eu.simstadt.nf4j.ExportJobDescriptor;
import eu.simstadt.nf4j.ImportJobDescriptor;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
/**
* Implementations of JobBuilder build nF import and export jobs using nF's XML job format. There should be
* one implementation for each version of novaFACTORY. The supported version should be returned by
* supportsNFVersion().
*
* @param <I> The import job descriptor implementation for this builder.
* @param <E> The export job descriptor implementation for this builder.
*
* @author Marcel Bruse
*/
public interface JobFileBuilder<I extends ImportJobDescriptor, E extends ExportJobDescriptor> {
/**
* @return Tells the caller the supported version of novaFACTORY.
*/
public String supportsNFVersion();
/**
* @return The supported version of the XML export job format.
*/
public String supportsExportJobVersion();
/**
* @return The supported version of the XML import job format.
*/
public String supportsImportJobVersion();
/**
* Builds a XML export job document. This file can be sent to a nF server instance by the caller afterwards.
*
* @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 XML export job document.
*/
public File buildExportJobFile(E exportJobDescriptor) throws InvalidJobDescriptorException;
/**
* 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
* the nF manual.
* @return Returns a zipped import job file.
*/
public File buildImportJobFile(I importJobDescriptor) throws InvalidJobDescriptorException;
}
package eu.simstadt.nf4j.async;
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;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
/**
* 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 JobFileBuilderImpl implements JobFileBuilder<ImportJobDescription, ExportJobDescription>
{
/** 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";
/**
* @return Returns the supported novaFACTORY version.
*/
@Override
public String supportsNFVersion() {
return NOVA_FACTORY_VERSION;
}
/**
* @return Returns the supported XML export job version.
*/
@Override
public String supportsExportJobVersion() {
return EXPORT_JOB_VERSION;
}
/**
* @return Returns the supported XML import 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.
* @throws FailedJobTransmissionException
*/
@Override
public File buildExportJobFile(ExportJobDescription jobDescriptor) throws InvalidJobDescriptorException {
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(System.getProperty("user.name")));
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);
if (!jobDescriptor.getUnitList().isEmpty()) {
extent.setAttribute("tile1asgn", jobDescriptor.getTile1asgn());
for (Unit unit : jobDescriptor.getUnitList()) {
Element unitElement = doc.createElement("unit");
unitElement.setAttribute("exterior", unit.getExterior());
unitElement.setAttribute("frame", unit.getFrame());
unitElement.setAttribute("select_mapsheets", unit.getSelectMapsheets());
unitElement.setAttribute("subdivision", unit.getSubdivision());
unitElement.appendChild(doc.createTextNode(unit.getValue()));
extent.appendChild(unitElement);
}
} else {
Element polygon = createRegionPolygonElement(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");
format.setAttribute("alphalinscale", "0.0");
format.setAttribute("alphascale", "1.0");
format.setAttribute("citygml_actfunc", "undef");
format.setAttribute("citygml_apptheme", "");
format.setAttribute("citygml_elemclasses", "true");
format.setAttribute("citygml_lodmode", "all");
format.setAttribute("citygml_lods", jobDescriptor.getLODs());
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", "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();
}
} else {
throw new InvalidJobDescriptorException();
}
return result;
}
/**
* Appends layers to the XML job document.
*
* @param doc The XML document.
* @param layers The XML layers element where new layers should be appended.
* @param layerList The user defined list of layers.
*/
private void appendLayers(Document doc, Element layers,
ArrayList<Layer> 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 createRegionPolygonElement(Document doc, List<Coord> 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(ImportJobDescription jobDescriptor)
throws InvalidJobDescriptorException {
if (Objects.nonNull(jobDescriptor) && jobDescriptor.isValid()) {
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, CityGML file and ADE schemata
File zippedCityGMLFile = File.createTempFile("nF_Import_", ".zip");
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zippedCityGMLFile));
String zipFileName = jobDescriptor.getProduct() + "_" + jobDescriptor.getLeaf() + "_"
+ jobDescriptor.getLevel();
if (Objects.nonNull(jobDescriptor.getOperation())) {
zipFileName += "_" + jobDescriptor.getOperation();
}
zipFileName += ".gml";
File cityGMLFile = jobDescriptor.getCityGMLFile();
writeBytesToZipFile(new FileInputStream(cityGMLFile), zos, zipFileName);
writeBytesToZipFile(new FileInputStream(startfile), zos, startFilename);
for (File adeSchemaFile : jobDescriptor.getADESchemaFileList()) {
writeBytesToZipFile(new FileInputStream(adeSchemaFile), zos, adeSchemaFile.getName());
}
zos.close();
return zippedCityGMLFile;
} catch (FileNotFoundException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
} catch (IOException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
} else {
throw new InvalidJobDescriptorException();
}
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();
}
}
package eu.simstadt.nf4j.async;
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;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
/**
* 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 JobFileBuilderImpl implements JobFileBuilder<ImportJobDescription, ExportJobDescription>
{
/** 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";
/**
* @return Returns the supported novaFACTORY version.
*/
@Override
public String supportsNFVersion() {
return NOVA_FACTORY_VERSION;
}
/**
* @return Returns the supported XML export job version.
*/
@Override
public String supportsExportJobVersion() {
return EXPORT_JOB_VERSION;
}
/**
* @return Returns the supported XML import 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.
* @throws FailedJobTransmissionException
*/
@Override
public File buildExportJobFile(ExportJobDescription jobDescriptor) throws InvalidJobDescriptorException {
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(System.getProperty("user.name")));
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);
if (!jobDescriptor.getUnitList().isEmpty()) {
extent.setAttribute("tile1asgn", jobDescriptor.getTile1asgn());
for (Unit unit : jobDescriptor.getUnitList()) {
Element unitElement = doc.createElement("unit");
unitElement.setAttribute("exterior", unit.getExterior());
unitElement.setAttribute("frame", unit.getFrame());
unitElement.setAttribute("select_mapsheets", unit.getSelectMapsheets());
unitElement.setAttribute("subdivision", unit.getSubdivision());
unitElement.appendChild(doc.createTextNode(unit.getValue()));
extent.appendChild(unitElement);
}
} else {
Element polygon = createRegionPolygonElement(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");
format.setAttribute("alphalinscale", "0.0");
format.setAttribute("alphascale", "1.0");
format.setAttribute("citygml_actfunc", "undef");
format.setAttribute("citygml_apptheme", "");
format.setAttribute("citygml_elemclasses", "true");
format.setAttribute("citygml_lodmode", "all");
format.setAttribute("citygml_lods", jobDescriptor.getLODs());
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", "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();
}
} else {
throw new InvalidJobDescriptorException();
}
return result;
}
/**
* Appends layers to the XML job document.
*
* @param doc The XML document.
* @param layers The XML layers element where new layers should be appended.
* @param layerList The user defined list of layers.
*/
private void appendLayers(Document doc, Element layers,
ArrayList<Layer> 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 createRegionPolygonElement(Document doc, List<Coord> 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(ImportJobDescription jobDescriptor)
throws InvalidJobDescriptorException {
if (Objects.nonNull(jobDescriptor) && jobDescriptor.isValid()) {
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, CityGML file and ADE schemata
File zippedCityGMLFile = File.createTempFile("nF_Import_", ".zip");
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zippedCityGMLFile));
String zipFileName = jobDescriptor.getProduct() + "_" + jobDescriptor.getLeaf() + "_"
+ jobDescriptor.getLevel();
if (Objects.nonNull(jobDescriptor.getOperation())) {
zipFileName += "_" + jobDescriptor.getOperation();
}
zipFileName += ".gml";
File cityGMLFile = jobDescriptor.getCityGMLFile();
writeBytesToZipFile(new FileInputStream(cityGMLFile), zos, zipFileName);
writeBytesToZipFile(new FileInputStream(startfile), zos, startFilename);
for (File adeSchemaFile : jobDescriptor.getADESchemaFileList()) {
writeBytesToZipFile(new FileInputStream(adeSchemaFile), zos, adeSchemaFile.getName());
}
zos.close();
return zippedCityGMLFile;
} catch (FileNotFoundException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
} catch (IOException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
} else {
throw new InvalidJobDescriptorException();
}
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();
}
}
package eu.simstadt.nf4j.async;
import java.util.EventObject;
import java.util.Optional;
import eu.simstadt.nf4j.Job;
import eu.simstadt.nf4j.JobStatus;
/**
* Every time when the status of a job progresses, one of this events will be created and sent
* to all of the job status listeners registered at the job. Job status listeners implement
* the jobStatusChanged() method which takes this JobStatusEvent as its argument. This event will
* contain the job status as the event source and listener can read the source and decide how to
* deal with the new status of its observed job.
*
* Note, the job status, job and additional event messages will be referenced in extra object members of this event,
* because the status of referenced job may change during the notification of the listeners. Therefore, obtaining
* the job status directly from the job is not reliable, if the listener wants to know the actual source of this
* event.
*
* @author Marcel Bruse
*/
public class JobStatusEvent extends EventObject {
private static final long serialVersionUID = -1800246486543538087L;
/** The job for which this event will be sent to the job status listeners. */
private Job job;
/** There might be an additional (error) message provided with the new job status. */
private Optional<String> message;
/**
* Constructor with job status as event source. The source can be read by the job status listeners.
*
* @param source The new job status, which triggers this event.
*/
public JobStatusEvent(JobStatus source, Job job) {
this(source, job, null);
}
/**
* Constructor with job status as event source and an additional (error) message. The source can be read
* by the job status listeners.
*
* @param source The new job status, which triggers this event.
* @param message an additional (error) message for this event and job status.
*/
public JobStatusEvent(JobStatus source, Job job, Optional<String> message) {
super(source);
this.job = job;
this.message = message;
}
/**
* @return Returns the job for which this event will be sent to the job status listeners.
*/
public Job getJob() {
return job;
}
/**
* @return Returns an additional (error) message, if present.
*/
public Optional<String> getMessage() {
return message;
}
}
package eu.simstadt.nf4j.async;
import java.util.EventObject;
import java.util.Optional;
import eu.simstadt.nf4j.Job;
import eu.simstadt.nf4j.JobStatus;
/**
* Every time when the status of a job progresses, one of this events will be created and sent
* to all of the job status listeners registered at the job. Job status listeners implement
* the jobStatusChanged() method which takes this JobStatusEvent as its argument. This event will
* contain the job status as the event source and listener can read the source and decide how to
* deal with the new status of its observed job.
*
* Note, the job status, job and additional event messages will be referenced in extra object members of this event,
* because the status of referenced job may change during the notification of the listeners. Therefore, obtaining
* the job status directly from the job is not reliable, if the listener wants to know the actual source of this
* event.
*
* @author Marcel Bruse
*/
public class JobStatusEvent extends EventObject {
private static final long serialVersionUID = -1800246486543538087L;
/** The job for which this event will be sent to the job status listeners. */
private Job job;
/** There might be an additional (error) message provided with the new job status. */
private Optional<String> message;
/**
* Constructor with job status as event source. The source can be read by the job status listeners.
*
* @param source The new job status, which triggers this event.
*/
public JobStatusEvent(JobStatus source, Job job) {
this(source, job, null);
}
/**
* Constructor with job status as event source and an additional (error) message. The source can be read
* by the job status listeners.
*
* @param source The new job status, which triggers this event.
* @param message an additional (error) message for this event and job status.
*/
public JobStatusEvent(JobStatus source, Job job, Optional<String> message) {
super(source);
this.job = job;
this.message = message;
}
/**
* @return Returns the job for which this event will be sent to the job status listeners.
*/
public Job getJob() {
return job;
}
/**
* @return Returns an additional (error) message, if present.
*/
public Optional<String> getMessage() {
return message;
}
}
package eu.simstadt.nf4j.async;
import java.util.EventListener;
/**
* Your main application may become a job status listener in order to get updates about the status changes of its
* ongoing jobs. Job listeners of asynchronous export and import jobs will receive event objects for most of the
* job status' listed in the job status enumeration.
*
* @author Marcel Bruse
*/
public interface JobStatusListener extends EventListener {
/**
* This callback method will be called by your asynchronous export and import jobs during their send, poll
* and download operations in order to keep you updated about job status changes.
*
* @param event The latest job status event for one of your export or import jobs.
*/
public void jobStatusChanged(JobStatusEvent event);
}
package eu.simstadt.nf4j.async;
import java.util.EventListener;
/**
* Your main application may become a job status listener in order to get updates about the status changes of its
* ongoing jobs. Job listeners of asynchronous export and import jobs will receive event objects for most of the
* job status' listed in the job status enumeration.
*
* @author Marcel Bruse
*/
public interface JobStatusListener extends EventListener {
/**
* This callback method will be called by your asynchronous export and import jobs during their send, poll
* and download operations in order to keep you updated about job status changes.
*
* @param event The latest job status event for one of your export or import jobs.
*/
public void jobStatusChanged(JobStatusEvent event);
}
package eu.simstadt.nf4j.async;
/**
* A layer describes an aspect of a nF product and the type of its data. For instance, a layer could contain
* all house numbers of all buildings of the product.
*
* @author Marcel Bruse
*/
public class Layer {
private static final String DEFAULT_NAME = "GML";
private static final String DEFAULT_PRODUCT = "WU3";
private static final String DEFAULT_STYLE = "#000000";
/** The name of the layer. */
private String name;
/** The name of the product to which this layer belongs. */
private String product;
/** The style of this layer. Should be a color code (?). */
private String style;
/**
* The standard constructor.
*/
public Layer() {}
/**
* A convenience constructor for layers.
*
* @param name The name of the layer. This layer has to exist within the product.
* @param product The name of the product. This product has to exist in the database.
* @param style The purpose of this field is unknown.
*/
public Layer(String name, String product, String style) {
this.name = name;
this.product = product;
this.style = style;
}
/**
* @return Returns the name of the layer.
*/
public String getName() {
return name;
}
/**
* Sets the name of the layer.
*
* @param name The name of the layer.
*/
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the name of the layer's product.
*/
public String getProduct() {
return product;
}
/**
* Sets the product of this layer.
*
* @param product The product of this layer.
*/
public void setProduct(String product) {
this.product = product;
}
/**
* @return Returns the style of the layer.
*/
public String getStyle() {
return style;
}
/**
* Sets the style of this layer. Should be a color code (?).
*
* @param style The style of this layer.
*/
public void setStyle(String style) {
this.style = style;
}
public static Layer getDefaultLayer() {
Layer layer = new Layer();
layer.setName(DEFAULT_NAME);
layer.setProduct(DEFAULT_PRODUCT);
layer.setStyle(DEFAULT_STYLE);
return layer;
}
}
package eu.simstadt.nf4j.async;
/**
* A layer describes an aspect of a nF product and the type of its data. For instance, a layer could contain
* all house numbers of all buildings of the product.
*
* @author Marcel Bruse
*/
public class Layer {
private static final String DEFAULT_NAME = "GML";
private static final String DEFAULT_PRODUCT = "WU3";
private static final String DEFAULT_STYLE = "#000000";
/** The name of the layer. */
private String name;
/** The name of the product to which this layer belongs. */
private String product;
/** The style of this layer. Should be a color code (?). */
private String style;
/**
* The standard constructor.
*/
public Layer() {}
/**
* A convenience constructor for layers.
*
* @param name The name of the layer. This layer has to exist within the product.
* @param product The name of the product. This product has to exist in the database.
* @param style The purpose of this field is unknown.
*/
public Layer(String name, String product, String style) {
this.name = name;
this.product = product;
this.style = style;
}
/**
* @return Returns the name of the layer.
*/
public String getName() {
return name;
}
/**
* Sets the name of the layer.
*
* @param name The name of the layer.
*/
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the name of the layer's product.
*/
public String getProduct() {
return product;
}
/**
* Sets the product of this layer.
*
* @param product The product of this layer.
*/
public void setProduct(String product) {
this.product = product;
}
/**
* @return Returns the style of the layer.
*/
public String getStyle() {
return style;
}
/**
* Sets the style of this layer. Should be a color code (?).
*
* @param style The style of this layer.
*/
public void setStyle(String style) {
this.style = style;
}
public static Layer getDefaultLayer() {
Layer layer = new Layer();
layer.setName(DEFAULT_NAME);
layer.setProduct(DEFAULT_PRODUCT);
layer.setStyle(DEFAULT_STYLE);
return layer;
}
}
package eu.simstadt.nf4j.async;
/**
* These are the known operation modes of the nF import servlet.
*
* @author Marcel Bruse
*/
public enum Operation {
REP, // Replaces whole existing buildings,
REPUPD, // Replaces whole existing buildings and adds new buildings,
UPD, // Update, same as REP,
CHG, // Change, same as REPUPD,
DEL, // Deletes the geometry of a particular LOD,
DELALL // Deletes a whole building
}
package eu.simstadt.nf4j.async;
/**
* These are the known operation modes of the nF import servlet.
*
* @author Marcel Bruse
*/
public enum Operation {
REP, // Replaces whole existing buildings,
REPUPD, // Replaces whole existing buildings and adds new buildings,
UPD, // Update, same as REP,
CHG, // Change, same as REPUPD,
DEL, // Deletes the geometry of a particular LOD,
DELALL // Deletes a whole building
}
package eu.simstadt.nf4j.async;
import eu.simstadt.nf4j.FailedTransmissionException;
/**
* This task frequently polls the status of an asynchronous job within a separate poll thread. Changes of the
* jobs status will be signaled to all of the job status listeners registered at the job.
* You can cancel this task by calling job.cancel().
*
* @author Marcel Bruse
*/
public class PollJobStatusTask implements Runnable {
/** The job for which you want to poll status changes for. */
private AsyncJob job;
/**
* Don't flood your nF server with status request. This interval ensures that your server will receive
* a status request within every time interval.
*/
private int interval;
/**
* Constructor with asynchronous job and the poll interval.
*
* @param job The job to update frequently.
* @param interval The time interval for one request.
*/
public PollJobStatusTask(AsyncJob job, int interval) {
this.job = job;
this.interval = interval;
}
/**
* This method performs the poll operation asynchronously in the jobs separate poll thread.
* Job status listeners will be notified upon status changes.
*/
@Override
public void run() {
try {
while (!job.hasFinished() && !job.hasFailed() && job.keepPolling()) {
job.triggerStatusUpdate();
Thread.sleep(interval * 1000l);
}
// At this line the job may have finished or failed before the job listeners could be notified.
// Therefore, we have to ensure that all listeners know the current status.
job.notifyJobStatusListeners();
} catch (FailedTransmissionException ex) {
job.cancel();
} catch (InterruptedException ex) {
// Canceled by the main thread
}
}
package eu.simstadt.nf4j.async;
import eu.simstadt.nf4j.FailedTransmissionException;
/**
* This task frequently polls the status of an asynchronous job within a separate poll thread. Changes of the
* jobs status will be signaled to all of the job status listeners registered at the job.
* You can cancel this task by calling job.cancel().
*
* @author Marcel Bruse
*/
public class PollJobStatusTask implements Runnable {
/** The job for which you want to poll status changes for. */
private AsyncJob job;
/**
* Don't flood your nF server with status request. This interval ensures that your server will receive
* a status request within every time interval.
*/
private int interval;
/**
* Constructor with asynchronous job and the poll interval.
*
* @param job The job to update frequently.
* @param interval The time interval for one request.
*/
public PollJobStatusTask(AsyncJob job, int interval) {
this.job = job;
this.interval = interval;
}
/**
* This method performs the poll operation asynchronously in the jobs separate poll thread.
* Job status listeners will be notified upon status changes.
*/
@Override
public void run() {
try {
while (!job.hasFinished() && !job.hasFailed() && job.keepPolling()) {
job.triggerStatusUpdate();
Thread.sleep(interval * 1000l);
}
// At this line the job may have finished or failed before the job listeners could be notified.
// Therefore, we have to ensure that all listeners know the current status.
job.notifyJobStatusListeners();
} catch (FailedTransmissionException ex) {
job.cancel();
} catch (InterruptedException ex) {
// Canceled by the main thread
}
}
}
\ No newline at end of file
package eu.simstadt.nf4j.async;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* This SAX handler scans nF XML status reports and exception reports and searches for the nF job id, the status of a
* nF job and service exception messages.
*
* @author Marcel Bruse
*/
public class ReportHandler extends DefaultHandler {
/** The XML tag which tells you the status of a nF export job. */
public static final String STATUS_TAG = "status";
/** The XML tag which tells you the status of a nF import job. */
public static final String RESULT_STATUS_TAG = "ResultStatus";
/** The XML attribute which tells you the status of a nF import job. */
public static final String STATUS_ATTRIBUTE = "status";
/** The XML tag which holds the id of the nF job. */
public static final String JOB_ID = "jobId";
/** If there was a problem on the nF server, then this XML tag gives you some hints. */
public static final String SERVICE_EXCEPTION_TAG = "ServiceException";
/** The id of the status of the nF job. */
public Integer statusId = null;
/** The id of the nF job. */
public Integer jobId = null;
/** If there was any problem, then you will find an exception message here. */
public String serviceException = null;
/** Scanned string will be stored here temporarily. */
private String currentString;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
if (qName.equalsIgnoreCase(RESULT_STATUS_TAG)) {
statusId = Integer.valueOf(attributes.getValue(STATUS_ATTRIBUTE));
}
}
/**
* If a tag has been read, its contents will be tested here. If it contains either a status id, job id or
* service exception message, then the contents will be stored in the appropriate member variable.
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equalsIgnoreCase(STATUS_TAG)) {
statusId = Integer.valueOf(currentString);
} else if (qName.equalsIgnoreCase(SERVICE_EXCEPTION_TAG)) {
serviceException = currentString;
} else if (qName.equalsIgnoreCase(JOB_ID)) {
jobId = Integer.valueOf(currentString);
}
}
/**
* The scanner of the XML document.
*
* @see DefaultHandler
*/
@Override
public void characters(char[] ch, int start, int length) {
currentString = new String(ch, start, length);
}
}
package eu.simstadt.nf4j.async;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* This SAX handler scans nF XML status reports and exception reports and searches for the nF job id, the status of a
* nF job and service exception messages.
*
* @author Marcel Bruse
*/
public class ReportHandler extends DefaultHandler {
/** The XML tag which tells you the status of a nF export job. */
public static final String STATUS_TAG = "status";
/** The XML tag which tells you the status of a nF import job. */
public static final String RESULT_STATUS_TAG = "ResultStatus";
/** The XML attribute which tells you the status of a nF import job. */
public static final String STATUS_ATTRIBUTE = "status";
/** The XML tag which holds the id of the nF job. */
public static final String JOB_ID = "jobId";
/** If there was a problem on the nF server, then this XML tag gives you some hints. */
public static final String SERVICE_EXCEPTION_TAG = "ServiceException";
/** The id of the status of the nF job. */
public Integer statusId = null;
/** The id of the nF job. */
public Integer jobId = null;
/** If there was any problem, then you will find an exception message here. */
public String serviceException = null;
/** Scanned string will be stored here temporarily. */
private String currentString;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
if (qName.equalsIgnoreCase(RESULT_STATUS_TAG)) {
statusId = Integer.valueOf(attributes.getValue(STATUS_ATTRIBUTE));
}
}
/**
* If a tag has been read, its contents will be tested here. If it contains either a status id, job id or
* service exception message, then the contents will be stored in the appropriate member variable.
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equalsIgnoreCase(STATUS_TAG)) {
statusId = Integer.valueOf(currentString);
} else if (qName.equalsIgnoreCase(SERVICE_EXCEPTION_TAG)) {
serviceException = currentString;
} else if (qName.equalsIgnoreCase(JOB_ID)) {
jobId = Integer.valueOf(currentString);
}
}
/**
* The scanner of the XML document.
*
* @see DefaultHandler
*/
@Override
public void characters(char[] ch, int start, int length) {
currentString = new String(ch, start, length);
}
}
package eu.simstadt.nf4j.async;
import java.util.Optional;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
/**
* This task sends an export job to your nF server asynchronously within a separate send thread. Once the send
* operation has finished all of the jobs status listeners will be notified. You can cancel this task by calling
* job.cancel().
*
* @author Marcel Bruse
*/
public class SendExportJobTask implements Runnable {
/** The job to be sent to your nF server. */
private AsyncExportJob job;
/**
* Constructor with the export job to be sent.
*
* @param job The export job to be sent.
*/
public SendExportJobTask(AsyncExportJob job) {
this.job = job;
}
/**
* This methods performs the actual send operation asynchronously in a separate send thread.
* Job status listeners will be notified once the operation finishes or fails.
*/
@Override
public void run() {
try {
HTTPConnection connector = (HTTPConnection) job.getConnector();
connector.sendAndUpdateExportJob(job);
job.poll();
} catch (InvalidJobDescriptorException ex) {
signalError("Job cancel because of an invalid job description!");
} catch (FailedTransmissionException ex) {
signalError("The job transmission failed. There seams to be a problem with the connector!");
}
}
/**
* This method is superfluous I guess? TODO: Please check and refactor it.
*/
private void signalError(String errorMessage) {
job.setStatus(JobStatus.UNKNOWN, Optional.of(errorMessage));
}
package eu.simstadt.nf4j.async;
import java.util.Optional;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
/**
* This task sends an export job to your nF server asynchronously within a separate send thread. Once the send
* operation has finished all of the jobs status listeners will be notified. You can cancel this task by calling
* job.cancel().
*
* @author Marcel Bruse
*/
public class SendExportJobTask implements Runnable {
/** The job to be sent to your nF server. */
private AsyncExportJob job;
/**
* Constructor with the export job to be sent.
*
* @param job The export job to be sent.
*/
public SendExportJobTask(AsyncExportJob job) {
this.job = job;
}
/**
* This methods performs the actual send operation asynchronously in a separate send thread.
* Job status listeners will be notified once the operation finishes or fails.
*/
@Override
public void run() {
try {
HTTPConnection connector = (HTTPConnection) job.getConnector();
connector.sendAndUpdateExportJob(job);
job.poll();
} catch (InvalidJobDescriptorException ex) {
signalError("Job cancel because of an invalid job description!");
} catch (FailedTransmissionException ex) {
signalError("The job transmission failed. There seams to be a problem with the connector!");
}
}
/**
* This method is superfluous I guess? TODO: Please check and refactor it.
*/
private void signalError(String errorMessage) {
job.setStatus(JobStatus.UNKNOWN, Optional.of(errorMessage));
}
}
\ No newline at end of file
package eu.simstadt.nf4j.async;
import java.util.Optional;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
/**
* This task sends an import job to your nF server asynchronously within a separate send thread. Once the send
* operation has finished all of the jobs status listeners will be notified. You can cancel this task by calling
* job.cancel().
*
* @author Marcel Bruse
*/
public class SendImportJobTask implements Runnable {
/** The job to be sent to your nF server. */
private AsyncImportJob job;
/**
* Constructor with the import job to be sent.
*
* @param job The import job to be sent.
*/
public SendImportJobTask(AsyncImportJob job) {
this.job = job;
}
/**
* This methods performs the actual send operation asynchronously in a separate send thread.
* Job status listeners will be notified once the operation finishes or fails.
*/
@Override
public void run() {
try {
HTTPConnection connector = (HTTPConnection) job.getConnector();
connector.sendAndUpdateImportJob(job);
job.poll();
} catch (InvalidJobDescriptorException ex) {
signalError("Job cancel because of an invalid job description!");
} catch (FailedTransmissionException ex) {
signalError("The job transmission failed. There seams to be a problem with the connector!");
}
}
/**
* This method is superfluous I guess? TODO: Please check and refactor it.
*/
private void signalError(String errorMessage) {
job.setStatus(JobStatus.UNKNOWN, Optional.of(errorMessage));
}
package eu.simstadt.nf4j.async;
import java.util.Optional;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
/**
* This task sends an import job to your nF server asynchronously within a separate send thread. Once the send
* operation has finished all of the jobs status listeners will be notified. You can cancel this task by calling
* job.cancel().
*
* @author Marcel Bruse
*/
public class SendImportJobTask implements Runnable {
/** The job to be sent to your nF server. */
private AsyncImportJob job;
/**
* Constructor with the import job to be sent.
*
* @param job The import job to be sent.
*/
public SendImportJobTask(AsyncImportJob job) {
this.job = job;
}
/**
* This methods performs the actual send operation asynchronously in a separate send thread.
* Job status listeners will be notified once the operation finishes or fails.
*/
@Override
public void run() {
try {
HTTPConnection connector = (HTTPConnection) job.getConnector();
connector.sendAndUpdateImportJob(job);
job.poll();
} catch (InvalidJobDescriptorException ex) {
signalError("Job cancel because of an invalid job description!");
} catch (FailedTransmissionException ex) {
signalError("The job transmission failed. There seams to be a problem with the connector!");
}
}
/**
* This method is superfluous I guess? TODO: Please check and refactor it.
*/
private void signalError(String errorMessage) {
job.setStatus(JobStatus.UNKNOWN, Optional.of(errorMessage));
}
}
\ No newline at end of file
package eu.simstadt.nf4j.async;
/**
* Units (Blattschnitte) divide regions into sections. For instance, the city of Stuttgart could have the units
* "Stg-Mitte", "Stg-West", "Bad Cannstatt", "Heslach", etc.
*
* @author Marcel Bruse
*
*/
public class Unit {
private static final String DEFAULT_EXTERIOR = "0";
private static final String DEFAULT_FRAME = "0";
private static final String DEFAULT_SELECT_MAP_SHEETS = "0";
private static final String DEFAULT_SUBDIVISION = "4";
/** The exterior attribute of the unit tag. */
private String exterior;
/** The frame attribute of the unit tag. */
private String frame;
/** The select mapsheet attribute of the unit tag. */
private String selectMapsheets;
/** The subdivision attribute of the unit tag. */
private String subdivision;
/** The actual value of the unit tag. */
private String value;
public String getExterior() {
return exterior;
}
public void setExterior(String exterior) {
this.exterior = exterior;
}
public String getFrame() {
return frame;
}
public void setFrame(String frame) {
this.frame = frame;
}
public String getSelectMapsheets() {
return selectMapsheets;
}
public void setSelectMapsheets(String selectMapsheets) {
this.selectMapsheets = selectMapsheets;
}
public String getSubdivision() {
return subdivision;
}
public void setSubdivision(String subdivision) {
this.subdivision = subdivision;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
/**
* @return Returns true, if the unit is valid.
*/
public boolean isValid() {
return !(exterior.isEmpty()
|| frame.isEmpty()
|| selectMapsheets.isEmpty()
|| subdivision.isEmpty());
}
public static Unit getDefaultUnit() {
Unit unit = new Unit();
unit.setExterior(DEFAULT_EXTERIOR);
unit.setFrame(DEFAULT_FRAME);
unit.setSelectMapsheets(DEFAULT_SELECT_MAP_SHEETS);
unit.setSubdivision(DEFAULT_SUBDIVISION);
return unit;
}
}
package eu.simstadt.nf4j.async;
/**
* Units (Blattschnitte) divide regions into sections. For instance, the city of Stuttgart could have the units
* "Stg-Mitte", "Stg-West", "Bad Cannstatt", "Heslach", etc.
*
* @author Marcel Bruse
*
*/
public class Unit {
private static final String DEFAULT_EXTERIOR = "0";
private static final String DEFAULT_FRAME = "0";
private static final String DEFAULT_SELECT_MAP_SHEETS = "0";
private static final String DEFAULT_SUBDIVISION = "4";
/** The exterior attribute of the unit tag. */
private String exterior;
/** The frame attribute of the unit tag. */
private String frame;
/** The select mapsheet attribute of the unit tag. */
private String selectMapsheets;
/** The subdivision attribute of the unit tag. */
private String subdivision;
/** The actual value of the unit tag. */
private String value;
public String getExterior() {
return exterior;
}
public void setExterior(String exterior) {
this.exterior = exterior;
}
public String getFrame() {
return frame;
}
public void setFrame(String frame) {
this.frame = frame;
}
public String getSelectMapsheets() {
return selectMapsheets;
}
public void setSelectMapsheets(String selectMapsheets) {
this.selectMapsheets = selectMapsheets;
}
public String getSubdivision() {
return subdivision;
}
public void setSubdivision(String subdivision) {
this.subdivision = subdivision;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
/**
* @return Returns true, if the unit is valid.
*/
public boolean isValid() {
return !(exterior.isEmpty()
|| frame.isEmpty()
|| selectMapsheets.isEmpty()
|| subdivision.isEmpty());
}
public static Unit getDefaultUnit() {
Unit unit = new Unit();
unit.setExterior(DEFAULT_EXTERIOR);
unit.setFrame(DEFAULT_FRAME);
unit.setSelectMapsheets(DEFAULT_SELECT_MAP_SHEETS);
unit.setSubdivision(DEFAULT_SUBDIVISION);
return unit;
}
}
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.nf4j.ExportJobFromJavaFXRegionChooser;
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) {
repo = Paths.get("../TestRepository");
} else {
repo = Paths.get(repoString);
}
}
public void downloadRegion(String wktPolygon, String productName, JSObject novaFactoryLayer)
throws InterruptedException {
//TODO: Ask nf Server about available regions
Task<Integer> task = new Task<Integer>() {
@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();
}
public void extractZIPtoGML(String zipFilename) throws IOException {
ZipFile zipFile = new ZipFile(zipFilename);
Enumeration<? extends ZipEntry> 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 {
JSObject novafactoryVectors = (JSObject) webEngine.executeScript("novafactory_vectors");
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);
novafactoryVectors.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();
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<? extends State> ov, State oldState, State newState) -> {
if (newState == State.SUCCEEDED) {
JSObject win = (JSObject) webEngine.executeScript("window");
JavaScriptFXBridge fxapp = new JavaScriptFXBridge();
win.setMember("fxapp", fxapp);
webEngine.executeScript("console.log = function(message)\n" +
"{\n" +
" fxapp.log(message);\n" +
"};");
try {
fxapp.importNovaFactoryBoundingBoxes();
} catch (Exception ex) {
RegionChooserFX.LOGGER.warning("NovaFactory CSV not found or corrupt");
ex.printStackTrace();
}
// try {
// fxapp.selectRegionDirectlyFromCityGML(
// Paths.get("../TestRepository").resolve("Gruenbuehl.proj")
// .resolve("Gruenbuehl_LOD2_validated+ADE.gml"),
// "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))"
// );
// } catch (Exception ex) {
// ex.printStackTrace();
//
// }
// 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;
}
}
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.nf4j.ExportJobFromJavaFXRegionChooser;
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) {
repo = Paths.get("../TestRepository");
} else {
repo = Paths.get(repoString);
}
}
public void downloadRegion(String wktPolygon, String productName, JSObject novaFactoryLayer)
throws InterruptedException {
//TODO: Ask nf Server about available regions
Task<Integer> task = new Task<Integer>() {
@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();
}
public void extractZIPtoGML(String zipFilename) throws IOException {
ZipFile zipFile = new ZipFile(zipFilename);
Enumeration<? extends ZipEntry> 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 {
JSObject novafactoryVectors = (JSObject) webEngine.executeScript("novafactory_vectors");
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);
novafactoryVectors.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();
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<? extends State> ov, State oldState, State newState) -> {
if (newState == State.SUCCEEDED) {
JSObject win = (JSObject) webEngine.executeScript("window");
JavaScriptFXBridge fxapp = new JavaScriptFXBridge();
win.setMember("fxapp", fxapp);
webEngine.executeScript("console.log = function(message)\n" +
"{\n" +
" fxapp.log(message);\n" +
"};");
try {
fxapp.importNovaFactoryBoundingBoxes();
} catch (Exception ex) {
RegionChooserFX.LOGGER.warning("NovaFactory CSV not found or corrupt");
ex.printStackTrace();
}
// try {
// fxapp.selectRegionDirectlyFromCityGML(
// Paths.get("../TestRepository").resolve("Gruenbuehl.proj")
// .resolve("Gruenbuehl_LOD2_validated+ADE.gml"),
// "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))"
// );
// } catch (Exception ex) {
// ex.printStackTrace();
//
// }
// 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;
}
}
package eu.simstadt.regionchooser;
import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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;
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.BuildingXmlNode;
import eu.simstadt.geo.fast_xml_parser.CityGmlIterator;
public class RegionExtractor
{
private static final WKTReader wktReader = new WKTReader();
private static final Logger LOGGER = Logger.getLogger(RegionExtractor.class.getName());
private static final GeometryFactory gf = new GeometryFactory();
/**
* Main method behind RegionChooser. Given a CityGML (as Path) and a geometry (as Well-known text POLYGON, in the
* same coordinate system as the CityGML), it iterates over each Building and checks if the building is inside the
* geometry. It only works with CityGML files smaller than 2GB. It uses VTD-XML parser instead of a whole
* Simstadt/Citydoctor/Citygml model.
*
*
* @param citygmlPath
* @param wktPolygon
* @param string
* @return a StringBuffer, full with the extracted Citygml, including header, buildings and footer.
* @throws ParseException
* @throws IOException
* @throws XPathEvalException
* @throws NavException
* @throws XPathParseException
* @throws NumberFormatException
*/
static public StringBuffer selectRegionDirectlyFromCityGML(Path citygmlPath, String wktPolygon, String srsName)
throws ParseException, NumberFormatException, XPathParseException, NavException, XPathEvalException,
IOException {
int buildingsCount = 0;
int foundBuildingsCount = 0;
StringBuffer sb = new StringBuffer();
Geometry poly = wktReader.read(wktPolygon);
CityGmlIterator citygml = new CityGmlIterator(citygmlPath);
for (BuildingXmlNode buildingXmlNode : citygml) {
if (buildingsCount == 0) {
sb.append(replaceEnvelopeInHeader(citygml.getHeader(), poly.getEnvelopeInternal(), srsName));
}
buildingsCount += 1;
Coordinate coord = new Coordinate(buildingXmlNode.x, buildingXmlNode.y);
Point point = gf.createPoint(coord);
if (point.within(poly)) {
foundBuildingsCount++;
sb.append(buildingXmlNode.toString());
}
if (buildingsCount % 1000 == 0) {
LOGGER.info("1000 buildings parsed");
}
}
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. If present, min and
* max values for Z are kept.
*
* @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 a valid XML, so this looked like the easiest solution.
double zMin = 0;
double zMax = 0;
Pattern boundedByPattern = Pattern.compile(
"(?is)<gml:boundedBy>.*?<gml:lowerCorner>(.*?)</gml:lowerCorner>\\s*<gml:upperCorner>(.*?)</gml:upperCorner>.*?</gml:boundedBy>");
Matcher matcher = boundedByPattern.matcher(header);
String headerWithoutEnvelope = header;
if (matcher.find()) {
headerWithoutEnvelope = matcher.replaceFirst("");
zMin = Double.valueOf(matcher.group(1).split("\\s+")[2]);
zMax = Double.valueOf(matcher.group(2).split("\\s+")[2]);
}
String newEnvelope = "<gml:boundedBy>\r\n" +
" <gml:Envelope srsName=\"" + srsName + "\" srsDimension=\"3\">\r\n" + //NOTE: Would srsDimension="2" be better? Should the original Z get extracted?
" <gml:lowerCorner>" + envelope.getMinX() + " " + envelope.getMinY() + " " + zMin
+ "</gml:lowerCorner>\r\n" +
" <gml:upperCorner>" + envelope.getMaxX() + " " + envelope.getMaxY() + " " + zMax
+ "</gml:upperCorner>\r\n" +
" </gml:Envelope>\r\n" +
"</gml:boundedBy>\r\n";
return headerWithoutEnvelope + newEnvelope;
}
}
package eu.simstadt.regionchooser;
import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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;
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.BuildingXmlNode;
import eu.simstadt.geo.fast_xml_parser.CityGmlIterator;
public class RegionExtractor
{
private static final WKTReader wktReader = new WKTReader();
private static final Logger LOGGER = Logger.getLogger(RegionExtractor.class.getName());
private static final GeometryFactory gf = new GeometryFactory();
/**
* Main method behind RegionChooser. Given a CityGML (as Path) and a geometry (as Well-known text POLYGON, in the
* same coordinate system as the CityGML), it iterates over each Building and checks if the building is inside the
* geometry. It only works with CityGML files smaller than 2GB. It uses VTD-XML parser instead of a whole
* Simstadt/Citydoctor/Citygml model.
*
*
* @param citygmlPath
* @param wktPolygon
* @param string
* @return a StringBuffer, full with the extracted Citygml, including header, buildings and footer.
* @throws ParseException
* @throws IOException
* @throws XPathEvalException
* @throws NavException
* @throws XPathParseException
* @throws NumberFormatException
*/
static public StringBuffer selectRegionDirectlyFromCityGML(Path citygmlPath, String wktPolygon, String srsName)
throws ParseException, NumberFormatException, XPathParseException, NavException, XPathEvalException,
IOException {
int buildingsCount = 0;
int foundBuildingsCount = 0;
StringBuffer sb = new StringBuffer();
Geometry poly = wktReader.read(wktPolygon);
CityGmlIterator citygml = new CityGmlIterator(citygmlPath);
for (BuildingXmlNode buildingXmlNode : citygml) {
if (buildingsCount == 0) {
sb.append(replaceEnvelopeInHeader(citygml.getHeader(), poly.getEnvelopeInternal(), srsName));
}
buildingsCount += 1;
Coordinate coord = new Coordinate(buildingXmlNode.x, buildingXmlNode.y);
Point point = gf.createPoint(coord);
if (point.within(poly)) {
foundBuildingsCount++;
sb.append(buildingXmlNode.toString());
}
if (buildingsCount % 1000 == 0) {
LOGGER.info("1000 buildings parsed");
}
}
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. If present, min and
* max values for Z are kept.
*
* @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 a valid XML, so this looked like the easiest solution.
double zMin = 0;
double zMax = 0;
Pattern boundedByPattern = Pattern.compile(
"(?is)<gml:boundedBy>.*?<gml:lowerCorner>(.*?)</gml:lowerCorner>\\s*<gml:upperCorner>(.*?)</gml:upperCorner>.*?</gml:boundedBy>");
Matcher matcher = boundedByPattern.matcher(header);
String headerWithoutEnvelope = header;
if (matcher.find()) {
headerWithoutEnvelope = matcher.replaceFirst("");
zMin = Double.valueOf(matcher.group(1).split("\\s+")[2]);
zMax = Double.valueOf(matcher.group(2).split("\\s+")[2]);
}
String newEnvelope = "<gml:boundedBy>\r\n" +
" <gml:Envelope srsName=\"" + srsName + "\" srsDimension=\"3\">\r\n" + //NOTE: Would srsDimension="2" be better? Should the original Z get extracted?
" <gml:lowerCorner>" + envelope.getMinX() + " " + envelope.getMinY() + " " + zMin
+ "</gml:lowerCorner>\r\n" +
" <gml:upperCorner>" + envelope.getMaxX() + " " + envelope.getMaxY() + " " + zMax
+ "</gml:upperCorner>\r\n" +
" </gml:Envelope>\r\n" +
"</gml:boundedBy>\r\n";
return headerWithoutEnvelope + newEnvelope;
}
}
//TODO: Clean up code and don't leave so many global variables
var reset_btn = $('#reset')[0];
var dataPanel = $('#dataPanel');
var wgs84Sphere = new ol.Sphere(6378137);
proj4.defs("EPSG:3068", "+proj=cass +lat_0=52.41864827777778 +lon_0=13.62720366666667 +x_0=40000 +y_0=10000 +ellps=bessel +datum=potsdam +units=m +no_defs"); // http://spatialreference.org/ref/epsg/3068/proj4js/
proj4.defs("EPSG:32632", "+proj=utm +zone=32 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"); // http://spatialreference.org/ref/epsg/32632/proj4js/
proj4.defs("EPSG:31463", "+proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0" + " +ellps=bessel +datum=potsdam +units=m +no_defs"); // http://spatialreference.org/ref/epsg/31463/proj4js/
proj4.defs("EPSG:31467", "+proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0" + " +ellps=bessel +datum=potsdam +units=m +no_defs"); // http://spatialreference.org/ref/epsg/31467/proj4js/
proj4.defs("EPSG:32118", "+proj=lcc +lat_1=41.03333333333333 +lat_2=40.66666666666666 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs"); // http://spatialreference.org/ref/epsg/32118/proj4js/
proj4.defs("EPSG:2263", "+proj=lcc +lat_1=41.03333333333333 +lat_2=40.66666666666666 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000.0000000001 +y_0=0 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs "); // http://www.spatialreference.org/ref/epsg/nad83-new-york-long-island-ftus/proj4/
//NOTE: Proj4 string for 28992 is wrong at http://spatialreference.org/ref/epsg/amersfoort-rd-new/
//NOTE: Corrected version from https://oegeo.wordpress.com/2008/05/20/note-to-self-the-one-and-only-rd-projection-string/
proj4.defs("EPSG:28992", "+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.999908 +x_0=155000 +y_0=463000 +ellps=bessel +units=m +towgs84=565.2369,50.0087,465.658,-0.406857330322398,0.350732676542563,-1.8703473836068,4.0812 +no_defs <>"); //
var osm_layer = new ol.layer.Tile({
source: new ol.source.OSM()
});
var kml_source = new ol.source.KML({
projection : ol.proj.get('EPSG:3857'),
url : 'data/citygml_hulls.kml',
extractAttributes : false,
extractStyles : false
});
function polygon_style(color, alpha) {
return new ol.style.Style({
fill : new ol.style.Fill({
color : 'rgba(255, 255, 255,' + alpha + ')'
}),
stroke : new ol.style.Stroke({
color : color,
width : 2,
lineDash : [ 5, 10 ]
}),
});
}
var kml_layer = new ol.layer.Vector({
source : kml_source,
style : polygon_style('#777777', 0.2)
});
var intersections = new ol.source.Vector();
var intersections_layer = new ol.layer.Vector({
source : intersections,
style : new ol.style.Style({
fill : new ol.style.Fill({
color : 'rgba(255, 155, 51, 0.2)'
})
})
});
var novafactory_vectors = new ol.source.Vector({
features : []
});
novafactory_vectors.addNovaFactoryProduct = function(xmin, ymin, xmax, ymax, name, epsgId) {
var box = new ol.geom.Polygon(
[ [ [ xmin, ymin ], [ xmin, ymax ], [ xmax, ymax ], [ xmax, ymin ], [ xmin, ymin ] ] ]);
box.transform('EPSG:' + epsgId, 'EPSG:3857');
var feature = new ol.Feature({
geometry : box,
name : name,
});
feature["geoJSON"] = geoJSONformat.writeFeatureObject(feature);
feature["area"] = feature.getGeometry().getArea();
feature["description"] = "novaFACTORY>" + name;
feature["available"] = true;
feature["source"] = "NovaFACTORY";
this.addFeature(feature);
};
var novafactory_layer = new ol.layer.Vector({
source : novafactory_vectors,
style : polygon_style('#ff7700', 0.1)
});
var map = new ol.Map({
target : 'map',
layers : [ osm_layer, kml_layer, novafactory_layer, intersections_layer ],
interactions : ol.interaction.defaults({
keyboard : true
})
});
var geoJSONformat = new ol.format.GeoJSON();
kml_layer.addEventListener("change", function() {
map.getView().fitExtent(kml_source.getExtent(), (map.getSize()));
});
function updateGMLPolygons() {
kml_source.forEachFeature(function(feature) {
feature["geoJSON"] = geoJSONformat.writeFeatureObject(feature);
feature["area"] = feature.getGeometry().getArea();
var project = feature.get("project");
var name = feature.get("name");
feature["description"] = project + ">" + name;
feature["source"] = "CityGML";
var citygmlHere;
if (fromJavaFX) {
citygmlHere = fxapp.checkIfCityGMLSAreAvailable(project, name);
}
feature["available"] = citygmlHere;
});
}
// The features are not added to a regular vector layer/source,
// but to a feature overlay which holds a collection of features.
// This collection is passed to the modify and also the draw
// interaction, so that both can add or modify features.
var featureOverlay = new ol.FeatureOverlay({
style : new ol.style.Style({
fill : new ol.style.Fill({
color : 'rgba(255, 155, 51, 0.5)'
}),
stroke : new ol.style.Stroke({
color : '#ffcc33',
width : 4
}),
image : new ol.style.Circle({
radius : 5,
fill : new ol.style.Fill({
color : '#ffcc33'
})
})
})
});
featureOverlay.setMap(map);
var selected_features = featureOverlay.getFeatures();
selected_features.on('add', function(event) {
var feature = event.element;
feature.on("change", function() {
displayInfo();
});
});
var modify = new ol.interaction.Modify({
features : featureOverlay.getFeatures(),
// the SHIFT key must be pressed to delete vertices, so
// that new vertices can be drawn at the same position
// of existing vertices
deleteCondition : function(event) {
return ol.events.condition.shiftKeyOnly(event) && ol.events.condition.singleClick(event);
}
});
map.addInteraction(modify);
var draw = new ol.interaction.Draw({
features : featureOverlay.getFeatures(),
type : 'Polygon'
});
map.addInteraction(draw);
var sketch;
var fromJavaFX;
draw.on('drawstart', function(evt) {
fromJavaFX = (typeof fxapp !== 'undefined');
sketch = evt.feature;
reset_btn.disabled = false;
updateGMLPolygons();
});
var sourceProj = map.getView().getProjection();
function findIntersections() {
var sketch_area = sketch.getGeometry().getArea();
var poly1 = geoJSONformat.writeFeatureObject(sketch);
var intersection_found = false;
intersections.clear();
function findIntersection(feature) {
try {
var jsonIntersection = turf.intersect(poly1, feature["geoJSON"]);
if (undefined !== jsonIntersection) {
if (!intersection_found) {
dataPanel.append("Intersection found with :<br/>\n");
intersection_found = true;
}
var intersection = geoJSONformat.readFeature(jsonIntersection);
var intersectionArea = intersection.getGeometry().getArea();
var citygml_percentage = Math.round(intersectionArea / feature["area"] * 100);
var sketch_percentage = Math.round(intersectionArea / sketch_area * 100);
intersections.addFeature(intersection);
var description;
if (feature["available"]) {
description = "<a href=\"#\" onclick=\"downloadRegionFrom" + feature["source"] + "(" + i
+ ");return false;\">" + feature["description"] + "</a>";
// console.log(description);
} else {
description = feature['description'];
}
dataPanel.append(description + " (" + citygml_percentage + "%");
if (sketch_percentage == 100) {
dataPanel.append(", all inside");
}
dataPanel.append(")<br/>\n");
}
} catch (err) {
console.log(feature.get('description') + " - " + err);
}
i++;
}
var i = 0;
novafactory_vectors.forEachFeature(findIntersection);
i = 0;
kml_source.forEachFeature(findIntersection);
if (!intersection_found) {
dataPanel.append("No intersection found with any CityGML or NovaFactory product<br/>\n");
}
}
function downloadRegionFromCityGML(i) {
// TODO: Disable all links
// TODO: DRY
var feature = kml_source.getFeatures()[i];
// Waiting 100ms in order to let the cursor change
setTimeout(function() {
var start = new Date().getTime();
var srsName = feature.get("srsName") || "EPSG:31467";
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"), srsName);
var end = new Date().getTime();
var time = end - start;
console.log('DL Execution time: ' + time);
setTimeout(function() {
$("html").removeClass("wait");
dataPanel.append("Done<br/>\n");
}, 100);
} else {
var msg = "ERROR : Unknown coordinate system : \"" + srsName + "\". Cannot extract any region";
console.log(msg);
dataPanel.append(msg + "<br/>\n");
}
}, 100);
}
function displayInfo() {
// var start = new Date().getTime();
dataPanel.empty();
var geom = /** @type {ol.geom.Polygon} */
(sketch.getGeometry().clone().transform(sourceProj, 'EPSG:4326'));
var coordinates = geom.getLinearRing(0).getCoordinates();
var area = Math.abs(wgs84Sphere.geodesicArea(coordinates));
var coords = geom.getLinearRing(0).getCoordinates();
if (!fromJavaFX) {
var wgs84_coords = "";
var n = coords.length;
for (var i = 0; i < n; i++) {
var wgs84_coord = coords[i];
// wgs84_coords += "regionPolygon.add(new Coord(" + wgs84_coord[1] +
// "," + wgs84_coord[0] + "));<br/>";
wgs84_coords += "(" + wgs84_coord[1] + "," + wgs84_coord[0] + ")<br/>";
}
dataPanel.append("WGS84 Coordinates<br/>");
dataPanel.append(wgs84_coords + "<br/>\n");
}
dataPanel.append("Area" + "<br/>\n");
dataPanel.append((Math.round(area / 1000) / 10).toString() + " ha<br/><br/>\n");
findIntersections();
// var end = new Date().getTime();
// var time = end - start;
// console.log('Execution time: ' + time);
}
draw.on('drawend', function() {
displayInfo();
draw.setActive(false);
});
$('#reset').click(function() {
try {
draw.finishDrawing();
} finally {
dataPanel.empty();
$("html").removeClass("wait");
draw.setActive(true);
featureOverlay.getFeatures().clear();
intersections.clear();
reset_btn.disabled = true;
focusOnMap();
}
});
novafactory_layer.downloadFinished = function() {
// FIXME: Weird <br>s are inserted between lines
// FIXME: Doesn't stop waiting cursor
$("html").removeClass("wait");
setTimeout(function() {
dataPanel.append("NovaFactory : DONE <br/>\n");
}, 100);
};
novafactory_layer.updateStatus = function(status) {
dataPanel.append("NovaFactory : " + status + "<br/>\n");
};
novafactory_layer.selectSaveFile = function(zipFilename) {
fxapp.extractZIPtoGML(zipFilename);
};
function downloadRegionFromNovaFACTORY(i) {
$("html").addClass("wait");
var feature = novafactory_vectors.getFeatures()[i];
// Waiting 100ms in order to let the cursor change
setTimeout(function() {
fxapp.downloadRegion(sketchAsWKT(), feature.get('name'), novafactory_layer);
}, 100);
}
function sketchAsWKT(srsName) {
srsName = (typeof srsName === 'undefined') ? 'EPSG:4326' : srsName;
var wktFormat = new ol.format.WKT();
return wktFormat.writeFeature(sketch, {
dataProjection : ol.proj.get(srsName),
featureProjection : ol.proj.get('EPSG:3857')
});
}
function focusOnMap() {
$('#map').focus();
// $('#map').scrollIntoView();
}
//TODO: Clean up code and don't leave so many global variables
var reset_btn = $('#reset')[0];
var dataPanel = $('#dataPanel');
var wgs84Sphere = new ol.Sphere(6378137);
proj4.defs("EPSG:3068", "+proj=cass +lat_0=52.41864827777778 +lon_0=13.62720366666667 +x_0=40000 +y_0=10000 +ellps=bessel +datum=potsdam +units=m +no_defs"); // http://spatialreference.org/ref/epsg/3068/proj4js/
proj4.defs("EPSG:32632", "+proj=utm +zone=32 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"); // http://spatialreference.org/ref/epsg/32632/proj4js/
proj4.defs("EPSG:31463", "+proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0" + " +ellps=bessel +datum=potsdam +units=m +no_defs"); // http://spatialreference.org/ref/epsg/31463/proj4js/
proj4.defs("EPSG:31467", "+proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0" + " +ellps=bessel +datum=potsdam +units=m +no_defs"); // http://spatialreference.org/ref/epsg/31467/proj4js/
proj4.defs("EPSG:32118", "+proj=lcc +lat_1=41.03333333333333 +lat_2=40.66666666666666 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs"); // http://spatialreference.org/ref/epsg/32118/proj4js/
proj4.defs("EPSG:2263", "+proj=lcc +lat_1=41.03333333333333 +lat_2=40.66666666666666 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000.0000000001 +y_0=0 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs "); // http://www.spatialreference.org/ref/epsg/nad83-new-york-long-island-ftus/proj4/
//NOTE: Proj4 string for 28992 is wrong at http://spatialreference.org/ref/epsg/amersfoort-rd-new/
//NOTE: Corrected version from https://oegeo.wordpress.com/2008/05/20/note-to-self-the-one-and-only-rd-projection-string/
proj4.defs("EPSG:28992", "+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.999908 +x_0=155000 +y_0=463000 +ellps=bessel +units=m +towgs84=565.2369,50.0087,465.658,-0.406857330322398,0.350732676542563,-1.8703473836068,4.0812 +no_defs <>"); //
var osm_layer = new ol.layer.Tile({
source: new ol.source.OSM()
});
var kml_source = new ol.source.KML({
projection : ol.proj.get('EPSG:3857'),
url : 'data/citygml_hulls.kml',
extractAttributes : false,
extractStyles : false
});
function polygon_style(color, alpha) {
return new ol.style.Style({
fill : new ol.style.Fill({
color : 'rgba(255, 255, 255,' + alpha + ')'
}),
stroke : new ol.style.Stroke({
color : color,
width : 2,
lineDash : [ 5, 10 ]
}),
});
}
var kml_layer = new ol.layer.Vector({
source : kml_source,
style : polygon_style('#777777', 0.2)
});
var intersections = new ol.source.Vector();
var intersections_layer = new ol.layer.Vector({
source : intersections,
style : new ol.style.Style({
fill : new ol.style.Fill({
color : 'rgba(255, 155, 51, 0.2)'
})
})
});
var novafactory_vectors = new ol.source.Vector({
features : []
});
novafactory_vectors.addNovaFactoryProduct = function(xmin, ymin, xmax, ymax, name, epsgId) {
var box = new ol.geom.Polygon(
[ [ [ xmin, ymin ], [ xmin, ymax ], [ xmax, ymax ], [ xmax, ymin ], [ xmin, ymin ] ] ]);
box.transform('EPSG:' + epsgId, 'EPSG:3857');
var feature = new ol.Feature({
geometry : box,
name : name,
});
feature["geoJSON"] = geoJSONformat.writeFeatureObject(feature);
feature["area"] = feature.getGeometry().getArea();
feature["description"] = "novaFACTORY>" + name;
feature["available"] = true;
feature["source"] = "NovaFACTORY";
this.addFeature(feature);
};
var novafactory_layer = new ol.layer.Vector({
source : novafactory_vectors,
style : polygon_style('#ff7700', 0.1)
});
var map = new ol.Map({
target : 'map',
layers : [ osm_layer, kml_layer, novafactory_layer, intersections_layer ],
interactions : ol.interaction.defaults({
keyboard : true
})
});
var geoJSONformat = new ol.format.GeoJSON();
kml_layer.addEventListener("change", function() {
map.getView().fitExtent(kml_source.getExtent(), (map.getSize()));
});
function updateGMLPolygons() {
kml_source.forEachFeature(function(feature) {
feature["geoJSON"] = geoJSONformat.writeFeatureObject(feature);
feature["area"] = feature.getGeometry().getArea();
var project = feature.get("project");
var name = feature.get("name");
feature["description"] = project + ">" + name;
feature["source"] = "CityGML";
var citygmlHere;
if (fromJavaFX) {
citygmlHere = fxapp.checkIfCityGMLSAreAvailable(project, name);
}
feature["available"] = citygmlHere;
});
}
// The features are not added to a regular vector layer/source,
// but to a feature overlay which holds a collection of features.
// This collection is passed to the modify and also the draw
// interaction, so that both can add or modify features.
var featureOverlay = new ol.FeatureOverlay({
style : new ol.style.Style({
fill : new ol.style.Fill({
color : 'rgba(255, 155, 51, 0.5)'
}),
stroke : new ol.style.Stroke({
color : '#ffcc33',
width : 4
}),
image : new ol.style.Circle({
radius : 5,
fill : new ol.style.Fill({
color : '#ffcc33'
})
})
})
});
featureOverlay.setMap(map);
var selected_features = featureOverlay.getFeatures();
selected_features.on('add', function(event) {
var feature = event.element;
feature.on("change", function() {
displayInfo();
});
});
var modify = new ol.interaction.Modify({
features : featureOverlay.getFeatures(),
// the SHIFT key must be pressed to delete vertices, so
// that new vertices can be drawn at the same position
// of existing vertices
deleteCondition : function(event) {
return ol.events.condition.shiftKeyOnly(event) && ol.events.condition.singleClick(event);
}
});
map.addInteraction(modify);
var draw = new ol.interaction.Draw({
features : featureOverlay.getFeatures(),
type : 'Polygon'
});
map.addInteraction(draw);
var sketch;
var fromJavaFX;
draw.on('drawstart', function(evt) {
fromJavaFX = (typeof fxapp !== 'undefined');
sketch = evt.feature;
reset_btn.disabled = false;
updateGMLPolygons();
});
var sourceProj = map.getView().getProjection();
function findIntersections() {
var sketch_area = sketch.getGeometry().getArea();
var poly1 = geoJSONformat.writeFeatureObject(sketch);
var intersection_found = false;
intersections.clear();
function findIntersection(feature) {
try {
var jsonIntersection = turf.intersect(poly1, feature["geoJSON"]);
if (undefined !== jsonIntersection) {
if (!intersection_found) {
dataPanel.append("Intersection found with :<br/>\n");
intersection_found = true;
}
var intersection = geoJSONformat.readFeature(jsonIntersection);
var intersectionArea = intersection.getGeometry().getArea();
var citygml_percentage = Math.round(intersectionArea / feature["area"] * 100);
var sketch_percentage = Math.round(intersectionArea / sketch_area * 100);
intersections.addFeature(intersection);
var description;
if (feature["available"]) {
description = "<a href=\"#\" onclick=\"downloadRegionFrom" + feature["source"] + "(" + i
+ ");return false;\">" + feature["description"] + "</a>";
// console.log(description);
} else {
description = feature['description'];
}
dataPanel.append(description + " (" + citygml_percentage + "%");
if (sketch_percentage == 100) {
dataPanel.append(", all inside");
}
dataPanel.append(")<br/>\n");
}
} catch (err) {
console.log(feature.get('description') + " - " + err);
}
i++;
}
var i = 0;
novafactory_vectors.forEachFeature(findIntersection);
i = 0;
kml_source.forEachFeature(findIntersection);
if (!intersection_found) {
dataPanel.append("No intersection found with any CityGML or NovaFactory product<br/>\n");
}
}
function downloadRegionFromCityGML(i) {
// TODO: Disable all links
// TODO: DRY
var feature = kml_source.getFeatures()[i];
// Waiting 100ms in order to let the cursor change
setTimeout(function() {
var start = new Date().getTime();
var srsName = feature.get("srsName") || "EPSG:31467";
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"), srsName);
var end = new Date().getTime();
var time = end - start;
console.log('DL Execution time: ' + time);
setTimeout(function() {
$("html").removeClass("wait");
dataPanel.append("Done<br/>\n");
}, 100);
} else {
var msg = "ERROR : Unknown coordinate system : \"" + srsName + "\". Cannot extract any region";
console.log(msg);
dataPanel.append(msg + "<br/>\n");
}
}, 100);
}
function displayInfo() {
// var start = new Date().getTime();
dataPanel.empty();
var geom = /** @type {ol.geom.Polygon} */
(sketch.getGeometry().clone().transform(sourceProj, 'EPSG:4326'));
var coordinates = geom.getLinearRing(0).getCoordinates();
var area = Math.abs(wgs84Sphere.geodesicArea(coordinates));
var coords = geom.getLinearRing(0).getCoordinates();
if (!fromJavaFX) {
var wgs84_coords = "";
var n = coords.length;
for (var i = 0; i < n; i++) {
var wgs84_coord = coords[i];
// wgs84_coords += "regionPolygon.add(new Coord(" + wgs84_coord[1] +
// "," + wgs84_coord[0] + "));<br/>";
wgs84_coords += "(" + wgs84_coord[1] + "," + wgs84_coord[0] + ")<br/>";
}
dataPanel.append("WGS84 Coordinates<br/>");
dataPanel.append(wgs84_coords + "<br/>\n");
}
dataPanel.append("Area" + "<br/>\n");
dataPanel.append((Math.round(area / 1000) / 10).toString() + " ha<br/><br/>\n");
findIntersections();
// var end = new Date().getTime();
// var time = end - start;
// console.log('Execution time: ' + time);
}
draw.on('drawend', function() {
displayInfo();
draw.setActive(false);
});
$('#reset').click(function() {
try {
draw.finishDrawing();
} finally {
dataPanel.empty();
$("html").removeClass("wait");
draw.setActive(true);
featureOverlay.getFeatures().clear();
intersections.clear();
reset_btn.disabled = true;
focusOnMap();
}
});
novafactory_layer.downloadFinished = function() {
// FIXME: Weird <br>s are inserted between lines
// FIXME: Doesn't stop waiting cursor
$("html").removeClass("wait");
setTimeout(function() {
dataPanel.append("NovaFactory : DONE <br/>\n");
}, 100);
};
novafactory_layer.updateStatus = function(status) {
dataPanel.append("NovaFactory : " + status + "<br/>\n");
};
novafactory_layer.selectSaveFile = function(zipFilename) {
fxapp.extractZIPtoGML(zipFilename);
};
function downloadRegionFromNovaFACTORY(i) {
$("html").addClass("wait");
var feature = novafactory_vectors.getFeatures()[i];
// Waiting 100ms in order to let the cursor change
setTimeout(function() {
fxapp.downloadRegion(sketchAsWKT(), feature.get('name'), novafactory_layer);
}, 100);
}
function sketchAsWKT(srsName) {
srsName = (typeof srsName === 'undefined') ? 'EPSG:4326' : srsName;
var wktFormat = new ol.format.WKT();
return wktFormat.writeFeature(sketch, {
dataProjection : ol.proj.get(srsName),
featureProjection : ol.proj.get('EPSG:3857')
});
}
function focusOnMap() {
$('#map').focus();
// $('#map').scrollIntoView();
}
focusOnMap();
\ No newline at end of file
package eu.simstadt.nf4j.async;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Scanner;
import java.util.zip.ZipInputStream;
import org.junit.Test;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
import eu.simstadt.nf4j.async.AsyncExportJob;
import eu.simstadt.nf4j.async.ExportJobDescription;
import eu.simstadt.nf4j.async.HTTPConnection;
import eu.simstadt.nf4j.async.JobStatusEvent;
import eu.simstadt.nf4j.async.JobStatusListener;
import eu.simstadt.nf4j.async.Layer;
import eu.simstadt.nf4j.async.Unit;
/**
* This class contains client oriented export job tests. It will send an export job and listens to status updates. Every
* of the subsequent status' LOCAL, SENT, PENDING, RUNNING, FINISHED and DOWNLOAD have to be signaled to this test
* class.
*
* @author Marcel Bruse
*/
public class SuccessfulExportJob implements JobStatusListener
{
public AsyncExportJob job;
@Test
public void processJob() throws InterruptedException {
ExportJobDescription description = ExportJobDescription.getDefaultDescriptor();
description.setInitiator(String.valueOf((int) (Math.random() * 9999)));
String userName = System.getProperty("user.name");
description.setAccount(userName);
description.setProduct("WU3");
description.setJobnumber(userName);
description.setLODs("2");
//FIXME: Zipped GMLs coming from nF don't have any defined srsName
//FIXME: Save files somewhere else
//NOTE: Unit is a predefined Map Region.
// Some of those units are empty. 821 (Finsterrot) and 824 (Neulautern) are available on HFT nF Server.
// <designation>
// 820
// </designation><name>
// Wuestenrot
// </name></mapsheet><mapsheet nr="127"><designation>
// 821
// </designation><name>
// Finsterrot
// </name></mapsheet><mapsheet nr="128"><designation>
// 822
// </designation><name>
// Maienfels
// </name></mapsheet><mapsheet nr="129"><designation>
// 823
// </designation><name>
// Neuhütten
// </name></mapsheet><mapsheet nr="130"><designation>
// 824
// </designation><name>
// Neulautern
// </name></mapsheet><mapsheet nr="131"><designation>
// 824-1
// </designation><name>
// Neulautern (1)
// </name>
Unit unit = Unit.getDefaultUnit();
unit.setValue("824");
description.addUnit(unit);
//NOTE: Polygon selection. This would be for a small part of Neulautern
// ArrayList<Coord> regionPolygon = new ArrayList<>();
// regionPolygon.add(new Coord(49.06020829807264, 9.432239985562616));
// regionPolygon.add(new Coord(49.05989193639516, 9.432497477628047));
// regionPolygon.add(new Coord(49.05968102749148, 9.432883715726192));
// regionPolygon.add(new Coord(49.05935060174289, 9.433001732922845));
// regionPolygon.add(new Coord(49.058422585764504, 9.433066105939206));
// regionPolygon.add(new Coord(49.05806402949591, 9.433248496152215));
// regionPolygon.add(new Coord(49.05748752183746, 9.434353566266353));
// regionPolygon.add(new Coord(49.05788826567445, 9.435544467068967));
// regionPolygon.add(new Coord(49.06072150273306, 9.435233330823237));
// regionPolygon.add(new Coord(49.06133312328379, 9.43515822897082));
// regionPolygon.add(new Coord(49.06143154427858, 9.43440721044665));
// regionPolygon.add(new Coord(49.06020829807264, 9.432239985562616));
//
// description.setRegionPolygon(regionPolygon);
Layer layer = Layer.getDefaultLayer();
layer.setProduct("WU3");
layer.setName("GML");
description.addLayer(layer);
job = new AsyncExportJob(description, new HTTPConnection("193.196.136.164"));
job.addJobStatusListener(this);
try {
job.send();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
} catch (InvalidJobDescriptorException ex) {
ex.printStackTrace();
}
// Wait for timeout, failure or that all tests pass
long timeout = 1000 * 60 * 3l; // 3 minutes maximum
long interval = 10000l;
while (!job.hasFinished() && !job.hasFailed() && timeout > 0) {
Thread.sleep(interval);
timeout -= interval;
System.out.println("+");
}
}
@Override
public void jobStatusChanged(JobStatusEvent event) {
JobStatus status = (JobStatus) event.getSource();
System.out.println(status);
if (status == JobStatus.LOCAL) {
assertTrue(true);
} else if (status == JobStatus.SENT) {
assertTrue(true);
} else if (status == JobStatus.PENDING) {
assertTrue(true);
} else if (status == JobStatus.RUNNING) {
assertTrue(true);
} else if (status == JobStatus.FINISHED) {
try {
assertTrue(true);
job.downloadResult();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
} else if (status == JobStatus.DOWNLOAD) {
try {
File file = job.getResult();
assertTrue(file.canRead());
testForExistingLOD2AndMissingLOD1(file);
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
}
}
/**
* These asserts ensure that only LOD2 has been loaded as specified in the job descriptor above.
*/
private void testForExistingLOD2AndMissingLOD1(File file) {
FileInputStream fileStream;
try {
fileStream = new FileInputStream(file);
ZipInputStream unzipStream = new ZipInputStream(fileStream);
unzipStream.getNextEntry(); // Skip the first entry, its the job description
unzipStream.getNextEntry();
File handle = Files.createTempDirectory("nfDownload").resolve("test.gml").toFile();
BufferedOutputStream bos =
new BufferedOutputStream(new FileOutputStream(handle));
byte[] in = new byte[4096];
int read = 0;
while ((read = unzipStream.read(in)) != -1) {
bos.write(in, 0, read);
}
unzipStream.closeEntry();
bos.close();
unzipStream.close();
Scanner scanner = new Scanner(handle);
boolean lod1Missing = true;
boolean lod2Found = false;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.contains("lod2")) {
lod2Found = true;
}
if (line.contains("lod1")) {
lod1Missing = false;
}
}
scanner.close();
assertTrue(lod1Missing);
assertTrue(lod2Found);
} catch (IOException ex) {
fail();
}
}
package eu.simstadt.nf4j.async;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Scanner;
import java.util.zip.ZipInputStream;
import org.junit.Test;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
import eu.simstadt.nf4j.async.AsyncExportJob;
import eu.simstadt.nf4j.async.ExportJobDescription;
import eu.simstadt.nf4j.async.HTTPConnection;
import eu.simstadt.nf4j.async.JobStatusEvent;
import eu.simstadt.nf4j.async.JobStatusListener;
import eu.simstadt.nf4j.async.Layer;
import eu.simstadt.nf4j.async.Unit;
/**
* This class contains client oriented export job tests. It will send an export job and listens to status updates. Every
* of the subsequent status' LOCAL, SENT, PENDING, RUNNING, FINISHED and DOWNLOAD have to be signaled to this test
* class.
*
* @author Marcel Bruse
*/
public class SuccessfulExportJob implements JobStatusListener
{
public AsyncExportJob job;
@Test
public void processJob() throws InterruptedException {
ExportJobDescription description = ExportJobDescription.getDefaultDescriptor();
description.setInitiator(String.valueOf((int) (Math.random() * 9999)));
String userName = System.getProperty("user.name");
description.setAccount(userName);
description.setProduct("WU3");
description.setJobnumber(userName);
description.setLODs("2");
//FIXME: Zipped GMLs coming from nF don't have any defined srsName
//FIXME: Save files somewhere else
//NOTE: Unit is a predefined Map Region.
// Some of those units are empty. 821 (Finsterrot) and 824 (Neulautern) are available on HFT nF Server.
// <designation>
// 820
// </designation><name>
// Wuestenrot
// </name></mapsheet><mapsheet nr="127"><designation>
// 821
// </designation><name>
// Finsterrot
// </name></mapsheet><mapsheet nr="128"><designation>
// 822
// </designation><name>
// Maienfels
// </name></mapsheet><mapsheet nr="129"><designation>
// 823
// </designation><name>
// Neuhütten
// </name></mapsheet><mapsheet nr="130"><designation>
// 824
// </designation><name>
// Neulautern
// </name></mapsheet><mapsheet nr="131"><designation>
// 824-1
// </designation><name>
// Neulautern (1)
// </name>
Unit unit = Unit.getDefaultUnit();
unit.setValue("824");
description.addUnit(unit);
//NOTE: Polygon selection. This would be for a small part of Neulautern
// ArrayList<Coord> regionPolygon = new ArrayList<>();
// regionPolygon.add(new Coord(49.06020829807264, 9.432239985562616));
// regionPolygon.add(new Coord(49.05989193639516, 9.432497477628047));
// regionPolygon.add(new Coord(49.05968102749148, 9.432883715726192));
// regionPolygon.add(new Coord(49.05935060174289, 9.433001732922845));
// regionPolygon.add(new Coord(49.058422585764504, 9.433066105939206));
// regionPolygon.add(new Coord(49.05806402949591, 9.433248496152215));
// regionPolygon.add(new Coord(49.05748752183746, 9.434353566266353));
// regionPolygon.add(new Coord(49.05788826567445, 9.435544467068967));
// regionPolygon.add(new Coord(49.06072150273306, 9.435233330823237));
// regionPolygon.add(new Coord(49.06133312328379, 9.43515822897082));
// regionPolygon.add(new Coord(49.06143154427858, 9.43440721044665));
// regionPolygon.add(new Coord(49.06020829807264, 9.432239985562616));
//
// description.setRegionPolygon(regionPolygon);
Layer layer = Layer.getDefaultLayer();
layer.setProduct("WU3");
layer.setName("GML");
description.addLayer(layer);
job = new AsyncExportJob(description, new HTTPConnection("193.196.136.164"));
job.addJobStatusListener(this);
try {
job.send();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
} catch (InvalidJobDescriptorException ex) {
ex.printStackTrace();
}
// Wait for timeout, failure or that all tests pass
long timeout = 1000 * 60 * 3l; // 3 minutes maximum
long interval = 10000l;
while (!job.hasFinished() && !job.hasFailed() && timeout > 0) {
Thread.sleep(interval);
timeout -= interval;
System.out.println("+");
}
}
@Override
public void jobStatusChanged(JobStatusEvent event) {
JobStatus status = (JobStatus) event.getSource();
System.out.println(status);
if (status == JobStatus.LOCAL) {
assertTrue(true);
} else if (status == JobStatus.SENT) {
assertTrue(true);
} else if (status == JobStatus.PENDING) {
assertTrue(true);
} else if (status == JobStatus.RUNNING) {
assertTrue(true);
} else if (status == JobStatus.FINISHED) {
try {
assertTrue(true);
job.downloadResult();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
} else if (status == JobStatus.DOWNLOAD) {
try {
File file = job.getResult();
assertTrue(file.canRead());
testForExistingLOD2AndMissingLOD1(file);
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
}
}
/**
* These asserts ensure that only LOD2 has been loaded as specified in the job descriptor above.
*/
private void testForExistingLOD2AndMissingLOD1(File file) {
FileInputStream fileStream;
try {
fileStream = new FileInputStream(file);
ZipInputStream unzipStream = new ZipInputStream(fileStream);
unzipStream.getNextEntry(); // Skip the first entry, its the job description
unzipStream.getNextEntry();
File handle = Files.createTempDirectory("nfDownload").resolve("test.gml").toFile();
BufferedOutputStream bos =
new BufferedOutputStream(new FileOutputStream(handle));
byte[] in = new byte[4096];
int read = 0;
while ((read = unzipStream.read(in)) != -1) {
bos.write(in, 0, read);
}
unzipStream.closeEntry();
bos.close();
unzipStream.close();
Scanner scanner = new Scanner(handle);
boolean lod1Missing = true;
boolean lod2Found = false;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.contains("lod2")) {
lod2Found = true;
}
if (line.contains("lod1")) {
lod1Missing = false;
}
}
scanner.close();
assertTrue(lod1Missing);
assertTrue(lod2Found);
} catch (IOException ex) {
fail();
}
}
}
\ No newline at end of file
package eu.simstadt.nf4j.async;
import static org.junit.Assert.assertTrue;
import java.io.File;
import org.junit.Test;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
import eu.simstadt.nf4j.async.AsyncImportJob;
import eu.simstadt.nf4j.async.HTTPConnection;
import eu.simstadt.nf4j.async.ImportJobDescription;
import eu.simstadt.nf4j.async.JobStatusEvent;
import eu.simstadt.nf4j.async.JobStatusListener;
/**
* This class contains client oriented import job tests. It will send an import job and listens to
* status updates. Every of the subsequent status' LOCAL, SENT, PENDING, RUNNING, FINISHED
* have to be signaled to this test class.
*
* @author Marcel Bruse
*/
public class SuccessfulImportJob implements JobStatusListener {
public AsyncImportJob job;
@Test
public void processJob() throws InterruptedException {
ImportJobDescription desc = ImportJobDescription.getDefaultDescriptor();
desc.setProduct("LBTEST");
desc.setLeaf("GR");
desc.setCityGMLFile(new File("SomeBuildings.gml"));
HTTPConnection connector = new HTTPConnection("193.196.136.164");
job = new AsyncImportJob(desc, connector);
job.addJobStatusListener(this);
try {
job.send();
} catch (InvalidJobDescriptorException ex) {
ex.printStackTrace();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
// Wait for timeout, failure or that all tests pass
long timeout = 1000 * 60 * 5l; // 5 minutes maximum
long interval = 10000l;
while (!job.hasFinished() && !job.hasFailed() && timeout > 0) {
Thread.sleep(interval);
timeout -= interval;
}
}
@Override
public void jobStatusChanged(JobStatusEvent event) {
JobStatus status = (JobStatus) event.getSource();
System.out.println(status);
if (status == JobStatus.LOCAL) {
assertTrue(true);
} else if (status == JobStatus.SENT) {
assertTrue(true);
} else if (status == JobStatus.PENDING) {
assertTrue(true);
} else if (status == JobStatus.RUNNING) {
assertTrue(true);
} else if (status == JobStatus.FINISHED) {
assertTrue(true);
}
}
package eu.simstadt.nf4j.async;
import static org.junit.Assert.assertTrue;
import java.io.File;
import org.junit.Test;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
import eu.simstadt.nf4j.async.AsyncImportJob;
import eu.simstadt.nf4j.async.HTTPConnection;
import eu.simstadt.nf4j.async.ImportJobDescription;
import eu.simstadt.nf4j.async.JobStatusEvent;
import eu.simstadt.nf4j.async.JobStatusListener;
/**
* This class contains client oriented import job tests. It will send an import job and listens to
* status updates. Every of the subsequent status' LOCAL, SENT, PENDING, RUNNING, FINISHED
* have to be signaled to this test class.
*
* @author Marcel Bruse
*/
public class SuccessfulImportJob implements JobStatusListener {
public AsyncImportJob job;
@Test
public void processJob() throws InterruptedException {
ImportJobDescription desc = ImportJobDescription.getDefaultDescriptor();
desc.setProduct("LBTEST");
desc.setLeaf("GR");
desc.setCityGMLFile(new File("SomeBuildings.gml"));
HTTPConnection connector = new HTTPConnection("193.196.136.164");
job = new AsyncImportJob(desc, connector);
job.addJobStatusListener(this);
try {
job.send();
} catch (InvalidJobDescriptorException ex) {
ex.printStackTrace();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
// Wait for timeout, failure or that all tests pass
long timeout = 1000 * 60 * 5l; // 5 minutes maximum
long interval = 10000l;
while (!job.hasFinished() && !job.hasFailed() && timeout > 0) {
Thread.sleep(interval);
timeout -= interval;
}
}
@Override
public void jobStatusChanged(JobStatusEvent event) {
JobStatus status = (JobStatus) event.getSource();
System.out.println(status);
if (status == JobStatus.LOCAL) {
assertTrue(true);
} else if (status == JobStatus.SENT) {
assertTrue(true);
} else if (status == JobStatus.PENDING) {
assertTrue(true);
} else if (status == JobStatus.RUNNING) {
assertTrue(true);
} else if (status == JobStatus.FINISHED) {
assertTrue(true);
}
}
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment