package org.osm2world.core.osm.creation;
import static java.lang.Double.parseDouble;
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* variant of {@link StrictOSMFileReader} with the necessary robustness to
* ignore small standard incompabilities in .osm files written by JOSM.
*/
public class JOSMFileReader extends StrictOSMFileReader {
public JOSMFileReader(File file) throws IOException,
ParserConfigurationException, SAXException,
TransformerException {
super(createTempOSMFile(file));
}
/**
* creates a temporary file in the .osm format. This removes some
* JOSM-specific attributes present in the original file,
* sets fake versions for unversioned elements,
* and merges multiple bound elements.
*
* The generated file should <em>not</em> be used for anything except
* feeding it to OSM2World.
*/
private static final File createTempOSMFile(File josmFile) throws
IOException, ParserConfigurationException, SAXException,
TransformerException {
/* parse original file */
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(josmFile);
/* modify DOM */
NodeList nodes = doc.getDocumentElement().getChildNodes();
List<Node> nodesToDelete = new ArrayList<Node>();
List<Element> boundsElements = new ArrayList<Element>();
for (int i = 0; i < nodes.getLength(); i++) {
if (nodes.item(i) instanceof Element) {
Element element = (Element) nodes.item(i);
if ("node".equals(element.getNodeName())
|| "way".equals(element.getNodeName())
|| "relation".equals(element.getNodeName())) {
if ("delete".equals(element.getAttribute("action"))) {
nodesToDelete.add(element);
} else if (!element.hasAttribute("version")) {
element.setAttribute("version", "424242");
}
} else if ("bounds".equals(element.getNodeName())) {
boundsElements.add(element);
}
}
}
if (boundsElements.size() > 1) {
double minLat = Double.POSITIVE_INFINITY;
double minLon = Double.POSITIVE_INFINITY;
double maxLat = Double.NEGATIVE_INFINITY;
double maxLon = Double.NEGATIVE_INFINITY;
for (Element bounds : boundsElements) {
minLat = min(minLat, parseDouble(bounds.getAttribute("minlat")));
minLon = min(minLon, parseDouble(bounds.getAttribute("minlon")));
maxLat = max(maxLat, parseDouble(bounds.getAttribute("maxlat")));
maxLon = max(maxLon, parseDouble(bounds.getAttribute("maxlon")));
}
Element firstBounds = boundsElements.remove(0);
firstBounds.setAttribute("minlat", Double.toString(minLat));
firstBounds.setAttribute("minlon", Double.toString(minLon));
firstBounds.setAttribute("maxlat", Double.toString(maxLat));
firstBounds.setAttribute("maxlon", Double.toString(maxLon));
nodesToDelete.addAll(boundsElements);
System.out.println("WARNING: input file contains multiple <bounds>." +
" This can lead to wrong coastlines and other issues."); //TODO proper logging
}
for (Node node : nodesToDelete) {
doc.getDocumentElement().removeChild(node);
}
/* write result */
File tempFile = File.createTempFile("workaround", ".osm", null);
tempFile.deleteOnExit();
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new FileOutputStream(tempFile));
transformer.transform(source, result);
return tempFile;
}
}