// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.io; import static org.openstreetmap.josm.tools.I18n.tr; import java.io.BufferedWriter; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.xml.XMLConstants; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.gpx.Extensions; import org.openstreetmap.josm.data.gpx.GpxConstants; import org.openstreetmap.josm.data.gpx.GpxData; import org.openstreetmap.josm.data.gpx.GpxLink; import org.openstreetmap.josm.data.gpx.GpxRoute; import org.openstreetmap.josm.data.gpx.GpxTrack; import org.openstreetmap.josm.data.gpx.GpxTrackSegment; import org.openstreetmap.josm.data.gpx.IWithAttributes; import org.openstreetmap.josm.data.gpx.WayPoint; /** * Writes GPX files from GPX data or OSM data. */ public class GpxWriter extends XmlWriter implements GpxConstants { /** * Constructs a new {@code GpxWriter}. * @param out The output writer */ public GpxWriter(PrintWriter out) { super(out); } /** * Constructs a new {@code GpxWriter}. * @param out The output stream */ public GpxWriter(OutputStream out) { super(new PrintWriter(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))); } private GpxData data; private String indent = ""; private static final int WAY_POINT = 0; private static final int ROUTE_POINT = 1; private static final int TRACK_POINT = 2; /** * Writes the given GPX data. * @param data The data to write */ public void write(GpxData data) { this.data = data; // We write JOSM specific meta information into gpx 'extensions' elements. // In particular it is noted whether the gpx data is from the OSM server // (so the rendering of clouds of anonymous TrackPoints can be improved) // and some extra synchronization info for export of AudioMarkers. // It is checked in advance, if any extensions are used, so we know whether // a namespace declaration is necessary. boolean hasExtensions = data.fromServer; if (!hasExtensions) { for (WayPoint wpt : data.waypoints) { Extensions extensions = (Extensions) wpt.get(META_EXTENSIONS); if (extensions != null && !extensions.isEmpty()) { hasExtensions = true; break; } } } out.println(""); out.println(""); indent = " "; writeMetaData(); writeWayPoints(); writeRoutes(); writeTracks(); out.print(""); out.flush(); } private void writeAttr(IWithAttributes obj, List keys) { for (String key : keys) { if (key.equals(META_LINKS)) { Collection lValue = obj.getCollection(key); if (lValue != null) { for (GpxLink link : lValue) { gpxLink(link); } } } else if (key.equals(META_EXTENSIONS)) { Extensions extensions = (Extensions) obj.get(key); if (extensions != null) { gpxExtensions(extensions); } } else { String value = obj.getString(key); if (value != null) { simpleTag(key, value); } } } } private void writeMetaData() { Map attr = data.attr; openln("metadata"); // write the description if (attr.containsKey(META_DESC)) { simpleTag("desc", data.getString(META_DESC)); } // write the author details if (attr.containsKey(META_AUTHOR_NAME) || attr.containsKey(META_AUTHOR_EMAIL)) { openln("author"); // write the name simpleTag("name", data.getString(META_AUTHOR_NAME)); // write the email address if (attr.containsKey(META_AUTHOR_EMAIL)) { String[] tmp = data.getString(META_AUTHOR_EMAIL).split("@"); if (tmp.length == 2) { inline("email", "id=\"" + tmp[0] + "\" domain=\""+tmp[1]+"\""); } } // write the author link gpxLink((GpxLink) data.get(META_AUTHOR_LINK)); closeln("author"); } // write the copyright details if (attr.containsKey(META_COPYRIGHT_LICENSE) || attr.containsKey(META_COPYRIGHT_YEAR)) { openAtt("copyright", "author=\""+ data.get(META_COPYRIGHT_AUTHOR) +"\""); if (attr.containsKey(META_COPYRIGHT_YEAR)) { simpleTag("year", (String) data.get(META_COPYRIGHT_YEAR)); } if (attr.containsKey(META_COPYRIGHT_LICENSE)) { simpleTag("license", encode((String) data.get(META_COPYRIGHT_LICENSE))); } closeln("copyright"); } // write links if (attr.containsKey(META_LINKS)) { for (GpxLink link : data.getCollection(META_LINKS)) { gpxLink(link); } } // write keywords if (attr.containsKey(META_KEYWORDS)) { simpleTag("keywords", data.getString(META_KEYWORDS)); } Bounds bounds = data.recalculateBounds(); if (bounds != null) { String b = "minlat=\"" + bounds.getMinLat() + "\" minlon=\"" + bounds.getMinLon() + "\" maxlat=\"" + bounds.getMaxLat() + "\" maxlon=\"" + bounds.getMaxLon() + "\"" ; inline("bounds", b); } if (data.fromServer) { openln("extensions"); simpleTag("josm:from-server", "true"); closeln("extensions"); } closeln("metadata"); } private void writeWayPoints() { for (WayPoint pnt : data.waypoints) { wayPoint(pnt, WAY_POINT); } } private void writeRoutes() { for (GpxRoute rte : data.routes) { openln("rte"); writeAttr(rte, RTE_TRK_KEYS); for (WayPoint pnt : rte.routePoints) { wayPoint(pnt, ROUTE_POINT); } closeln("rte"); } } private void writeTracks() { for (GpxTrack trk : data.tracks) { openln("trk"); writeAttr(trk, RTE_TRK_KEYS); for (GpxTrackSegment seg : trk.getSegments()) { openln("trkseg"); for (WayPoint pnt : seg.getWayPoints()) { wayPoint(pnt, TRACK_POINT); } closeln("trkseg"); } closeln("trk"); } } private void openln(String tag) { open(tag); out.println(); } private void open(String tag) { out.print(indent + "<" + tag + ">"); indent += " "; } private void openAtt(String tag, String attributes) { out.println(indent + "<" + tag + " " + attributes + ">"); indent += " "; } private void inline(String tag, String attributes) { out.println(indent + "<" + tag + " " + attributes + "/>"); } private void close(String tag) { indent = indent.substring(2); out.print(indent + ""); } private void closeln(String tag) { close(tag); out.println(); } /** * if content not null, open tag, write encoded content, and close tag * else do nothing. */ private void simpleTag(String tag, String content) { if (content != null && content.length() > 0) { open(tag); out.print(encode(content)); out.println(""); indent = indent.substring(2); } } /** * output link */ private void gpxLink(GpxLink link) { if (link != null) { openAtt("link", "href=\"" + link.uri + "\""); simpleTag("text", link.text); simpleTag("type", link.type); closeln("link"); } } /** * output a point */ private void wayPoint(WayPoint pnt, int mode) { String type; switch(mode) { case WAY_POINT: type = "wpt"; break; case ROUTE_POINT: type = "rtept"; break; case TRACK_POINT: type = "trkpt"; break; default: throw new RuntimeException(tr("Unknown mode {0}.", mode)); } if (pnt != null) { LatLon c = pnt.getCoor(); String coordAttr = "lat=\"" + c.lat() + "\" lon=\"" + c.lon() + "\""; if (pnt.attr.isEmpty()) { inline(type, coordAttr); } else { openAtt(type, coordAttr); writeAttr(pnt, WPT_KEYS); closeln(type); } } } private void gpxExtensions(Extensions extensions) { if (extensions != null && !extensions.isEmpty()) { openln("extensions"); for (Entry e : extensions.entrySet()) { simpleTag("josm:" + e.getKey(), e.getValue()); } closeln("extensions"); } } }