JobFileBuilderImpl.java 15 KB
Newer Older
bruse's avatar
bruse committed
1
package eu.simstadt.nf4j.async;
2

3
4
5
6
7
8
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
9
10
11
12
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
13
14
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
15
16
17
18
19
20
21
22
23
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;
24
25
26
27
import org.osgeo.proj4j.BasicCoordinateTransform;
import org.osgeo.proj4j.CRSFactory;
import org.osgeo.proj4j.CoordinateReferenceSystem;
import org.osgeo.proj4j.ProjCoordinate;
28
29
import org.w3c.dom.Document;
import org.w3c.dom.Element;
bruse's avatar
bruse committed
30
31
import eu.simstadt.nf4j.InvalidJobDescriptorException;

duminil's avatar
duminil committed
32

33
/**
duminil's avatar
duminil committed
34
35
36
 * 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.
 * 
37
38
 * @author Marcel Bruse
 */
duminil's avatar
duminil committed
39
40
41
public class JobFileBuilderImpl implements JobFileBuilder<ImportJobDescription, ExportJobDescription>
{

42
43
	/** Supported version of the novaFACTORY. */
	public static final String NOVA_FACTORY_VERSION = "6.3.1.1";
duminil's avatar
duminil committed
44

45
46
	/** The version of the XML export job format. */
	public static final String EXPORT_JOB_VERSION = "1.0.0";
duminil's avatar
duminil committed
47

bruse's avatar
bruse committed
48
49
50
	/**
	 * @return Returns the supported novaFACTORY version.
	 */
51
52
53
54
55
	@Override
	public String supportsNFVersion() {
		return NOVA_FACTORY_VERSION;
	}

bruse's avatar
bruse committed
56
57
58
	/**
	 * @return Returns the supported XML export job version.
	 */
59
60
61
62
63
	@Override
	public String supportsExportJobVersion() {
		return EXPORT_JOB_VERSION;
	}

bruse's avatar
bruse committed
64
65
66
	/**
	 * @return Returns the supported XML import job version.
	 */
67
68
69
70
	@Override
	public String supportsImportJobVersion() {
		return null;
	}
duminil's avatar
duminil committed
71

72
73
74
	/**
	 * This is an intermediate prototype.
	 * 
duminil's avatar
duminil committed
75
76
	 * @param jobDescriptor A job descriptor which describes the export job with all its attributes according to a valid
	 *           nF export job DTD.
77
	 * @return Returns a string representation of the nF export job.
duminil's avatar
duminil committed
78
	 * @throws FailedJobTransmissionException
79
80
	 */
	@Override
bruse's avatar
bruse committed
81
	public File buildExportJobFile(ExportJobDescription jobDescriptor) throws InvalidJobDescriptorException {
82
		File result = null;
83
84
85
86
		if (Objects.nonNull(jobDescriptor) && jobDescriptor.isValid()) {
			try {
				DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
				DocumentBuilder builder = factory.newDocumentBuilder();
duminil's avatar
duminil committed
87

88
89
90
91
				Document doc = builder.newDocument();
				Element root = doc.createElement("EXPORT_JOB");
				root.setAttribute("version", supportsExportJobVersion());
				doc.appendChild(root);
duminil's avatar
duminil committed
92

93
94
				Element job = doc.createElement("job");
				root.appendChild(job);
duminil's avatar
duminil committed
95

96
97
98
				Element initiator = doc.createElement("initiator");
				initiator.appendChild(doc.createTextNode(jobDescriptor.getInitiator()));
				job.appendChild(initiator);
duminil's avatar
duminil committed
99

100
101
102
				Element jobnumber = doc.createElement("jobnumber");
				jobnumber.appendChild(doc.createTextNode(jobDescriptor.getJobnumber()));
				job.appendChild(jobnumber);
duminil's avatar
duminil committed
103

104
				Element account = doc.createElement("account");
duminil's avatar
duminil committed
105
				account.appendChild(doc.createTextNode(System.getProperty("user.name")));
106
				job.appendChild(account);
duminil's avatar
duminil committed
107

108
109
110
				Element product = doc.createElement("product");
				product.appendChild(doc.createTextNode(jobDescriptor.getProduct()));
				root.appendChild(product);
duminil's avatar
duminil committed
111

112
113
114
115
116
117
118
				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);
duminil's avatar
duminil committed
119

120
				appendLayers(doc, layers, jobDescriptor.getLayerList());
duminil's avatar
duminil committed
121

122
123
124
				Element srs = doc.createElement("srs");
				srs.appendChild(doc.createTextNode(jobDescriptor.getSrs()));
				root.appendChild(srs);
duminil's avatar
duminil committed
125

126
127
128
				Element extent = doc.createElement("extent");
				extent.setAttribute("merge_mapsheets", jobDescriptor.getMergeMapsheets());
				root.appendChild(extent);
duminil's avatar
duminil committed
129

130
				if (!jobDescriptor.getUnitList().isEmpty()) {
131
					extent.setAttribute("tile1asgn", jobDescriptor.getTile1asgn());
132
133
134
135
136
137
138
					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()));
duminil's avatar
duminil committed
139
						extent.appendChild(unitElement);
140
					}
141
				} else {
142
					Element polygon = createRegionPolygonElement(doc, jobDescriptor.regionPolygon);
duminil's avatar
duminil committed
143
					extent.appendChild(polygon);
144
				}
duminil's avatar
duminil committed
145

146
147
148
				Element resolution = doc.createElement("resolution");
				resolution.appendChild(doc.createTextNode(jobDescriptor.getResolution()));
				root.appendChild(resolution);
duminil's avatar
duminil committed
149

150
151
152
				Element scale = doc.createElement("scale");
				scale.appendChild(doc.createTextNode(jobDescriptor.getScale()));
				root.appendChild(scale);
duminil's avatar
duminil committed
153

154
				Element format = doc.createElement("format");
duminil's avatar
duminil committed
155

156
157
				format.setAttribute("alphalinscale", "0.0");
				format.setAttribute("alphascale", "1.0");
158
				format.setAttribute("citygml_actfunc", "undef");
159
160
161
				format.setAttribute("citygml_apptheme", "");
				format.setAttribute("citygml_elemclasses", "true");
				format.setAttribute("citygml_lodmode", "all");
162
				format.setAttribute("citygml_lods", "01234");
163
164
165
166
167
168
169
170
171
172
173
174
175
				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");
176
				format.setAttribute("tolod1", "false");
177
178
179
				format.setAttribute("xyz", "false");
				format.appendChild(doc.createTextNode("CityGML"));
				root.appendChild(format);
duminil's avatar
duminil committed
180

181
				Element exportmetadata = doc.createElement("exportmetadata");
duminil's avatar
duminil committed
182

183
184
185
186
				exportmetadata.setAttribute("calibration", jobDescriptor.getCalibration());
				exportmetadata.setAttribute("xmetadata", jobDescriptor.getXmetadata());
				exportmetadata.appendChild(doc.createTextNode(jobDescriptor.getExportmetadata()));
				root.appendChild(exportmetadata);
duminil's avatar
duminil committed
187

188
189
190
191
				Element addfile = doc.createElement("addfile");
				addfile.setAttribute("col", jobDescriptor.getCol());
				addfile.setAttribute("eck", jobDescriptor.getEck());
				root.appendChild(addfile);
duminil's avatar
duminil committed
192

193
194
195
				Element usenodatamask = doc.createElement("usenodatamask");
				usenodatamask.appendChild(doc.createTextNode(jobDescriptor.getUsenodatamask()));
				root.appendChild(usenodatamask);
duminil's avatar
duminil committed
196

197
198
199
				Element usepdctborderpoly = doc.createElement("usepdctborderpoly");
				usepdctborderpoly.appendChild(doc.createTextNode(jobDescriptor.getUsepdctborderpoly()));
				root.appendChild(usepdctborderpoly);
duminil's avatar
duminil committed
200

201
202
203
				Element dhkresolvereferences = doc.createElement("dhkresolvereferences");
				dhkresolvereferences.appendChild(doc.createTextNode(jobDescriptor.getDhkresolvereferences()));
				root.appendChild(dhkresolvereferences);
duminil's avatar
duminil committed
204

205
206
207
				Element zipresult = doc.createElement("zipresult");
				zipresult.appendChild(doc.createTextNode(jobDescriptor.getZipresult()));
				root.appendChild(zipresult);
duminil's avatar
duminil committed
208

209
210
				Element userdescription = doc.createElement("userdescription");
				root.appendChild(userdescription);
duminil's avatar
duminil committed
211

212
213
				Element namingpattern = doc.createElement("namingpattern");
				root.appendChild(namingpattern);
duminil's avatar
duminil committed
214

215
216
217
218
219
				TransformerFactory transformerFactory = TransformerFactory.newInstance();
				Transformer transformer = transformerFactory.newTransformer();
				StringWriter writer = new StringWriter();
				StreamResult streamResult = new StreamResult(writer);
				transformer.transform(new DOMSource(doc), streamResult);
duminil's avatar
duminil committed
220

221
222
223
224
				File tempfile = File.createTempFile(jobDescriptor.getProduct() + "_", ".xml");
				PrintWriter printWriter = new PrintWriter(tempfile);
				printWriter.print(writer.toString());
				printWriter.close();
duminil's avatar
duminil committed
225

226
				return tempfile;
227
228
229
230
231
232
233
234
235
			} 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();
236
237
238
239
240
241
			} catch (FileNotFoundException ex) {
				// TODO Auto-generated catch block
				ex.printStackTrace();
			} catch (IOException ex) {
				// TODO Auto-generated catch block
				ex.printStackTrace();
242
			}
243
244
		} else {
			throw new InvalidJobDescriptorException();
245
246
247
		}
		return result;
	}
duminil's avatar
duminil committed
248

bruse's avatar
bruse committed
249
250
251
252
253
254
255
	/**
	 * 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.
	 */
256
257
258
259
260
261
262
	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()) {
duminil's avatar
duminil committed
263
				layerElement.setAttribute("product", product);
264
265
266
			}
			String style = layer.getStyle();
			if (Objects.nonNull(style) && !style.isEmpty()) {
duminil's avatar
duminil committed
267
				layerElement.setAttribute("style", style);
268
			}
duminil's avatar
duminil committed
269
			layers.appendChild(layerElement);
270
271
272
273
274
		}
	}

	/**
	 * Transforms a global WGS 84 position into a coordinate of the given target SRS.
duminil's avatar
duminil committed
275
	 * 
276
277
278
279
	 * @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.
	 */
duminil's avatar
duminil committed
280
	public static ProjCoordinate transformCoordinate(ProjCoordinate wgs84Position,
281
			CoordinateReferenceSystem targetCRS) {
282
283
		ProjCoordinate result = new ProjCoordinate();
		CRSFactory f = new CRSFactory();
284
		CoordinateReferenceSystem sourceCRS = f.createFromName(CRSWKT.EPSG_4326.wkt); // WGS 84 (used by Google Maps / OpenStreetMap)
285
286
		BasicCoordinateTransform transform = new BasicCoordinateTransform(sourceCRS, targetCRS);
		transform.transform(wgs84Position, result);
287
288
		return result;
	}
duminil's avatar
duminil committed
289

290
	/**
duminil's avatar
duminil committed
291
292
	 * 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.
293
294
295
296
297
	 * 
	 * @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.
	 */
298
	private static Element createRegionPolygonElement(Document doc, List<Coord> regionPolygon) {
299
300
		Element polygon = doc.createElement("polygon");
		polygon.setAttribute("srs", "31467");
301
		CRSFactory f = new CRSFactory();
302
		CoordinateReferenceSystem targetCRS = f.createFromName(CRSWKT.EPSG_31467.wkt); // DHDN Gauss-Kruger zone 3
303
304
305
306
307
308
309
		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);
310
311
312
313
		}
		return polygon;
	}

314
315
316
	/**
	 * Builds a zipped import job file. This file can be sent to a nF server instance by the caller afterwards.
	 * 
duminil's avatar
duminil committed
317
318
	 * @param jobDescriptor A job descriptor which describes the import job with all its attributes according to a valid
	 *           nF import job DTD.
319
320
	 * @return Returns a XML import job document.
	 */
321
	@Override
duminil's avatar
duminil committed
322
	public File buildImportJobFile(ImportJobDescription jobDescriptor)
323
324
325
326
			throws InvalidJobDescriptorException {
		if (Objects.nonNull(jobDescriptor) && jobDescriptor.isValid()) {
			try {
				// Write the nF start file which triggers and controls the processing of the CityGML file.
duminil's avatar
duminil committed
327
				String startFilename = jobDescriptor.getProduct() + "_" + jobDescriptor.getLeaf() + ".start";
328
329
330
331
				File startfile = new File(System.getProperty("java.io.tmpdir"), startFilename);
				PrintWriter writer = new PrintWriter(startfile);
				writer.print(jobDescriptor.getLevel());
				writer.close();
duminil's avatar
duminil committed
332

333
334
335
336
337
338
339
340
341
342
343
344
345
				// 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()) {
duminil's avatar
duminil committed
346
					writeBytesToZipFile(new FileInputStream(adeSchemaFile), zos, adeSchemaFile.getName());
347
348
				}
				zos.close();
duminil's avatar
duminil committed
349

350
351
352
353
354
355
356
357
358
359
				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();
360
		}
361
362
		return null;
	}
duminil's avatar
duminil committed
363

364
365
	/**
	 * Writes a file to the given ZipOutputStream which compresses the file.
duminil's avatar
duminil committed
366
	 * 
367
	 * @param fis The file input stream to be compressed.
duminil's avatar
duminil committed
368
	 * @param zos The zip output stream.
369
370
371
	 * @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.
	 */
duminil's avatar
duminil committed
372
	private void writeBytesToZipFile(FileInputStream fis, ZipOutputStream zos, String zipEntry)
373
374
375
376
377
378
379
380
381
			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();
	}
382
383

}