[10] | 1 | package org.openstreetmap.josm.io;
|
---|
| 2 |
|
---|
| 3 | import java.io.IOException;
|
---|
| 4 | import java.io.Writer;
|
---|
| 5 | import java.util.HashMap;
|
---|
| 6 | import java.util.LinkedList;
|
---|
| 7 | import java.util.Map;
|
---|
| 8 | import java.util.StringTokenizer;
|
---|
| 9 | import java.util.Map.Entry;
|
---|
| 10 |
|
---|
| 11 | import org.jdom.Document;
|
---|
| 12 | import org.jdom.Element;
|
---|
| 13 | import org.jdom.Namespace;
|
---|
| 14 | import org.jdom.output.Format;
|
---|
| 15 | import org.jdom.output.XMLOutputter;
|
---|
[22] | 16 | import org.openstreetmap.josm.Main;
|
---|
[10] | 17 | import org.openstreetmap.josm.data.osm.Key;
|
---|
| 18 | import org.openstreetmap.josm.data.osm.LineSegment;
|
---|
| 19 | import org.openstreetmap.josm.data.osm.Node;
|
---|
[33] | 20 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
[10] | 21 | import org.openstreetmap.josm.data.osm.Track;
|
---|
| 22 |
|
---|
| 23 | /**
|
---|
| 24 | * Exports a dataset to GPX data. All information available are tried to store in
|
---|
| 25 | * the gpx. If no corresponding tag is available in GPX, use
|
---|
| 26 | * <code><extensions></code> instead.
|
---|
| 27 | *
|
---|
| 28 | * GPX-Track segments are stored as 2-node-pairs, so no <trkseg> with more
|
---|
| 29 | * or less than 2 <trkpt> are exported.
|
---|
| 30 | *
|
---|
| 31 | * @author imi
|
---|
| 32 | */
|
---|
| 33 | public class GpxWriter {
|
---|
| 34 |
|
---|
| 35 | /**
|
---|
| 36 | * The GPX namespace used.
|
---|
| 37 | */
|
---|
[33] | 38 | public static final Namespace GPX = Namespace.getNamespace("http://www.topografix.com/GPX/1/0");
|
---|
[10] | 39 | /**
|
---|
| 40 | * The OSM namespace used (for extensions).
|
---|
| 41 | */
|
---|
[33] | 42 | public static final Namespace OSM = Namespace.getNamespace("osm", "http://www.openstreetmap.org");
|
---|
| 43 | /**
|
---|
| 44 | * The JOSM namespace (for JOSM-extensions).
|
---|
| 45 | */
|
---|
| 46 | public static final Namespace JOSM = Namespace.getNamespace("josm", "http://wiki.eigenheimstrasse.de/wiki/JOSM");
|
---|
[10] | 47 |
|
---|
| 48 | /**
|
---|
| 49 | * This is the output writer to store the resulting data in.
|
---|
| 50 | */
|
---|
| 51 | private Writer out;
|
---|
| 52 |
|
---|
| 53 | /**
|
---|
| 54 | * Create a GpxWrite from an output writer. As example to write in a file,
|
---|
| 55 | * use FileWriter.
|
---|
| 56 | *
|
---|
| 57 | * @param out The Writer to store the result data in.
|
---|
| 58 | */
|
---|
[22] | 59 | public GpxWriter(Writer out) {
|
---|
[10] | 60 | this.out = out;
|
---|
| 61 | }
|
---|
| 62 |
|
---|
| 63 |
|
---|
| 64 | /**
|
---|
| 65 | * Do the output in the former set writer.
|
---|
| 66 | * @exception IOException In case of IO errors, throw this exception.
|
---|
| 67 | */
|
---|
| 68 | public void output() throws IOException {
|
---|
| 69 | Element root = parseDataSet();
|
---|
| 70 | root.addNamespaceDeclaration(OSM);
|
---|
[33] | 71 | root.addNamespaceDeclaration(JOSM);
|
---|
[10] | 72 | Document d = new Document(root);
|
---|
| 73 | XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat());
|
---|
| 74 | xmlOut.output(d, out);
|
---|
| 75 | }
|
---|
| 76 |
|
---|
| 77 |
|
---|
| 78 | /**
|
---|
| 79 | * Write the whole DataSet in an JDom-Element and return the new element.
|
---|
| 80 | * @return The created element, out of the dataset.
|
---|
| 81 | */
|
---|
| 82 | @SuppressWarnings("unchecked")
|
---|
| 83 | private Element parseDataSet() {
|
---|
| 84 | Element e = new Element("gpx", GPX);
|
---|
| 85 | e.setAttribute("version", "1.0");
|
---|
[32] | 86 | e.setAttribute("creator", "JOSM");
|
---|
[10] | 87 | // for getting all unreferenced waypoints in the wpt-list
|
---|
[32] | 88 | LinkedList<Node> unrefNodes = new LinkedList<Node>(Main.main.ds.nodes);
|
---|
| 89 | // for getting all unreferenced line segments
|
---|
| 90 | LinkedList<LineSegment> unrefLs = new LinkedList<LineSegment>(Main.main.ds.lineSegments);
|
---|
[10] | 91 |
|
---|
| 92 | // tracks
|
---|
[23] | 93 | for (Track t : Main.main.ds.tracks) {
|
---|
[10] | 94 | Element tElem = new Element("trk", GPX);
|
---|
[33] | 95 | HashMap<Key, String> keys = null;
|
---|
[10] | 96 | if (t.keys != null) {
|
---|
[33] | 97 | keys = new HashMap<Key, String>(t.keys);
|
---|
[10] | 98 | addAndRemovePropertyTag("name", tElem, keys);
|
---|
| 99 | addAndRemovePropertyTag("cmt", tElem, keys);
|
---|
| 100 | addAndRemovePropertyTag("desc", tElem, keys);
|
---|
| 101 | addAndRemovePropertyTag("src", tElem, keys);
|
---|
| 102 | addAndRemovePropertyLinkTag(tElem, keys);
|
---|
| 103 | addAndRemovePropertyTag("number", tElem, keys);
|
---|
| 104 | addAndRemovePropertyTag("type", tElem, keys);
|
---|
| 105 | }
|
---|
[33] | 106 | addPropertyExtensions(tElem, keys, t);
|
---|
| 107 |
|
---|
[10] | 108 | // line segments
|
---|
[23] | 109 | for (LineSegment ls : t.segments) {
|
---|
[29] | 110 | tElem.getChildren().add(parseLineSegment(ls));
|
---|
[32] | 111 | unrefNodes.remove(ls.start);
|
---|
| 112 | unrefNodes.remove(ls.end);
|
---|
| 113 | unrefLs.remove(ls);
|
---|
[10] | 114 | }
|
---|
[29] | 115 |
|
---|
[10] | 116 | e.getChildren().add(tElem);
|
---|
| 117 | }
|
---|
| 118 |
|
---|
[29] | 119 | // encode pending line segments as tracks
|
---|
[32] | 120 | for (LineSegment ls : unrefLs) {
|
---|
[29] | 121 | Element t = new Element("trk", GPX);
|
---|
| 122 | t.getChildren().add(parseLineSegment(ls));
|
---|
[32] | 123 | unrefNodes.remove(ls.start);
|
---|
| 124 | unrefNodes.remove(ls.end);
|
---|
[29] | 125 | Element ext = new Element("extensions", GPX);
|
---|
[33] | 126 | ext.getChildren().add(new Element("segment", JOSM));
|
---|
[29] | 127 | t.getChildren().add(ext);
|
---|
| 128 | e.getChildren().add(t);
|
---|
| 129 | }
|
---|
| 130 |
|
---|
[10] | 131 | // waypoints (missing nodes)
|
---|
[32] | 132 | for (Node n : unrefNodes)
|
---|
[10] | 133 | e.getChildren().add(parseWaypoint(n, "wpt"));
|
---|
| 134 |
|
---|
| 135 | return e;
|
---|
| 136 | }
|
---|
| 137 |
|
---|
[29] | 138 |
|
---|
[10] | 139 | /**
|
---|
[29] | 140 | * Parse a line segment and store it into a JDOM-Element. Return that element.
|
---|
| 141 | */
|
---|
| 142 | @SuppressWarnings("unchecked")
|
---|
| 143 | private Element parseLineSegment(LineSegment ls) {
|
---|
| 144 | Element lsElem = new Element("trkseg", GPX);
|
---|
[33] | 145 | addPropertyExtensions(lsElem, ls.keys, ls);
|
---|
[29] | 146 | lsElem.getChildren().add(parseWaypoint(ls.start, "trkpt"));
|
---|
| 147 | lsElem.getChildren().add(parseWaypoint(ls.end, "trkpt"));
|
---|
| 148 | return lsElem;
|
---|
| 149 | }
|
---|
| 150 |
|
---|
| 151 | /**
|
---|
[10] | 152 | * Parse a waypoint (node) and store it into an JDOM-Element. Return that
|
---|
| 153 | * element.
|
---|
| 154 | *
|
---|
| 155 | * @param n The Node to parse and store
|
---|
| 156 | * @param name The name of the tag (different names for nodes in GPX)
|
---|
| 157 | * @return The resulting GPX-Element
|
---|
| 158 | */
|
---|
| 159 | private Element parseWaypoint(Node n, String name) {
|
---|
| 160 | Element e = new Element(name, GPX);
|
---|
| 161 | e.setAttribute("lat", Double.toString(n.coor.lat));
|
---|
| 162 | e.setAttribute("lon", Double.toString(n.coor.lon));
|
---|
[33] | 163 | HashMap<Key, String> keys = null;
|
---|
[10] | 164 | if (n.keys != null) {
|
---|
[33] | 165 | keys = new HashMap<Key, String>(n.keys);
|
---|
[10] | 166 | addAndRemovePropertyTag("ele", e, keys);
|
---|
| 167 | addAndRemovePropertyTag("time", e, keys);
|
---|
| 168 | addAndRemovePropertyTag("magvar", e, keys);
|
---|
| 169 | addAndRemovePropertyTag("geoidheight", e, keys);
|
---|
| 170 | addAndRemovePropertyTag("name", e, keys);
|
---|
| 171 | addAndRemovePropertyTag("cmt", e, keys);
|
---|
| 172 | addAndRemovePropertyTag("desc", e, keys);
|
---|
| 173 | addAndRemovePropertyTag("src", e, keys);
|
---|
| 174 | addAndRemovePropertyLinkTag(e, keys);
|
---|
| 175 | addAndRemovePropertyTag("sym", e, keys);
|
---|
| 176 | addAndRemovePropertyTag("type", e, keys);
|
---|
| 177 | addAndRemovePropertyTag("fix", e, keys);
|
---|
| 178 | addAndRemovePropertyTag("sat", e, keys);
|
---|
| 179 | addAndRemovePropertyTag("hdop", e, keys);
|
---|
| 180 | addAndRemovePropertyTag("vdop", e, keys);
|
---|
| 181 | addAndRemovePropertyTag("pdop", e, keys);
|
---|
| 182 | addAndRemovePropertyTag("ageofdgpsdata", e, keys);
|
---|
| 183 | addAndRemovePropertyTag("dgpsid", e, keys);
|
---|
| 184 | }
|
---|
[33] | 185 | addPropertyExtensions(e, keys, n);
|
---|
[10] | 186 | return e;
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 |
|
---|
| 190 | /**
|
---|
| 191 | * Add a link-tag to the element, if the property list contain a value named
|
---|
| 192 | * "link". The property is removed from the map afterwards.
|
---|
| 193 | *
|
---|
| 194 | * For the format, @see GpxReader#parseKeyValueLink(OsmPrimitive, Element).
|
---|
| 195 | * @param e The element to add the link to.
|
---|
| 196 | * @param keys The map containing the link property.
|
---|
| 197 | */
|
---|
| 198 | @SuppressWarnings("unchecked")
|
---|
| 199 | private void addAndRemovePropertyLinkTag(Element e, Map<Key, String> keys) {
|
---|
| 200 | Key key = Key.get("link");
|
---|
| 201 | String value = keys.get(key);
|
---|
| 202 | if (value != null) {
|
---|
| 203 | StringTokenizer st = new StringTokenizer(value, ";");
|
---|
| 204 | if (st.countTokens() != 2)
|
---|
| 205 | return;
|
---|
| 206 | Element link = new Element("link", GPX);
|
---|
| 207 | link.getChildren().add(new Element("type", GPX).setText(st.nextToken()));
|
---|
| 208 | link.getChildren().add(0,new Element("text", GPX).setText(st.nextToken()));
|
---|
| 209 | e.getChildren().add(link);
|
---|
| 210 | keys.remove(key);
|
---|
| 211 | }
|
---|
| 212 | }
|
---|
| 213 |
|
---|
| 214 |
|
---|
| 215 | /**
|
---|
| 216 | * Helper to add a property with a given name as tag to the element. This
|
---|
| 217 | * will look like <name><i>keys.get(name)</i></name>
|
---|
| 218 | *
|
---|
| 219 | * After adding, the property is removed from the map.
|
---|
| 220 | *
|
---|
| 221 | * If the property does not exist, nothing is done.
|
---|
| 222 | *
|
---|
| 223 | * @param name The properties name
|
---|
| 224 | * @param e The element to add the tag to.
|
---|
| 225 | * @param osm The data to get the property from.
|
---|
| 226 | */
|
---|
| 227 | @SuppressWarnings("unchecked")
|
---|
| 228 | private void addAndRemovePropertyTag(String name, Element e, Map<Key, String> keys) {
|
---|
| 229 | Key key = Key.get(name);
|
---|
| 230 | String value = keys.get(key);
|
---|
| 231 | if (value != null) {
|
---|
| 232 | e.getChildren().add(new Element(name, GPX).setText(value));
|
---|
| 233 | keys.remove(key);
|
---|
| 234 | }
|
---|
| 235 | }
|
---|
| 236 |
|
---|
| 237 | /**
|
---|
| 238 | * Add the property in the entry as <extensions> to the element
|
---|
| 239 | * @param e The element to add the property to.
|
---|
| 240 | * @param prop The property to add.
|
---|
| 241 | */
|
---|
| 242 | @SuppressWarnings("unchecked")
|
---|
[33] | 243 | private void addPropertyExtensions(Element e, Map<Key, String> keys, OsmPrimitive osm) {
|
---|
[35] | 244 | if ((keys == null || keys.isEmpty()) && osm.id == 0 && !osm.modified && !osm.modifiedProperties)
|
---|
[10] | 245 | return;
|
---|
[29] | 246 | Element extensions = e.getChild("extensions", GPX);
|
---|
[10] | 247 | if (extensions == null)
|
---|
| 248 | e.getChildren().add(extensions = new Element("extensions", GPX));
|
---|
[33] | 249 | if (keys != null && !keys.isEmpty()) {
|
---|
| 250 | for (Entry<Key, String> prop : keys.entrySet()) {
|
---|
| 251 | Element propElement = new Element("property", OSM);
|
---|
| 252 | propElement.setAttribute("key", prop.getKey().name);
|
---|
| 253 | propElement.setAttribute("value", prop.getValue());
|
---|
| 254 | extensions.getChildren().add(propElement);
|
---|
| 255 | }
|
---|
| 256 | }
|
---|
| 257 | if (osm.id != 0) {
|
---|
| 258 | Element propElement = new Element("uid", JOSM);
|
---|
| 259 | propElement.setText(""+osm.id);
|
---|
[10] | 260 | extensions.getChildren().add(propElement);
|
---|
| 261 | }
|
---|
[33] | 262 | if (osm.modified) {
|
---|
| 263 | Element modElement = new Element("modified", JOSM);
|
---|
| 264 | extensions.getChildren().add(modElement);
|
---|
| 265 | }
|
---|
[35] | 266 | if (osm.modifiedProperties) {
|
---|
| 267 | Element modElement = new Element("modifiedProperties", JOSM);
|
---|
| 268 | extensions.getChildren().add(modElement);
|
---|
| 269 | }
|
---|
[10] | 270 | }
|
---|
| 271 | }
|
---|