| 1 | // License: GPL. Copyright 2007 by Immanuel Scholz and others |
|---|
| 2 | package org.openstreetmap.josm.io; |
|---|
| 3 | |
|---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr; |
|---|
| 5 | |
|---|
| 6 | import java.io.BufferedWriter; |
|---|
| 7 | import java.io.OutputStream; |
|---|
| 8 | import java.io.OutputStreamWriter; |
|---|
| 9 | import java.io.PrintWriter; |
|---|
| 10 | import java.io.UnsupportedEncodingException; |
|---|
| 11 | import java.util.Collection; |
|---|
| 12 | import java.util.Map; |
|---|
| 13 | |
|---|
| 14 | import org.openstreetmap.josm.data.Bounds; |
|---|
| 15 | import org.openstreetmap.josm.data.coor.LatLon; |
|---|
| 16 | import org.openstreetmap.josm.data.gpx.GpxData; |
|---|
| 17 | import org.openstreetmap.josm.data.gpx.GpxLink; |
|---|
| 18 | import org.openstreetmap.josm.data.gpx.GpxRoute; |
|---|
| 19 | import org.openstreetmap.josm.data.gpx.GpxTrack; |
|---|
| 20 | import org.openstreetmap.josm.data.gpx.GpxTrackSegment; |
|---|
| 21 | import org.openstreetmap.josm.data.gpx.WayPoint; |
|---|
| 22 | |
|---|
| 23 | /** |
|---|
| 24 | * Writes GPX files from GPX data or OSM data. |
|---|
| 25 | */ |
|---|
| 26 | public class GpxWriter extends XmlWriter { |
|---|
| 27 | |
|---|
| 28 | public GpxWriter(PrintWriter out) { |
|---|
| 29 | super(out); |
|---|
| 30 | } |
|---|
| 31 | |
|---|
| 32 | public GpxWriter(OutputStream out) throws UnsupportedEncodingException { |
|---|
| 33 | super(new PrintWriter(new BufferedWriter(new OutputStreamWriter(out, "UTF-8")))); |
|---|
| 34 | } |
|---|
| 35 | |
|---|
| 36 | public GpxWriter() { |
|---|
| 37 | super(null); |
|---|
| 38 | //sorry for this one here, this will be cleaned up once the new scheme works |
|---|
| 39 | } |
|---|
| 40 | |
|---|
| 41 | private GpxData data; |
|---|
| 42 | private String indent = ""; |
|---|
| 43 | |
|---|
| 44 | private final static int WAY_POINT = 0; |
|---|
| 45 | private final static int ROUTE_POINT = 1; |
|---|
| 46 | private final static int TRACK_POINT = 2; |
|---|
| 47 | |
|---|
| 48 | public void write(GpxData data) { |
|---|
| 49 | this.data = data; |
|---|
| 50 | out.println("<?xml version='1.0' encoding='UTF-8'?>"); |
|---|
| 51 | out.println("<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" + |
|---|
| 52 | " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n" + |
|---|
| 53 | " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">"); |
|---|
| 54 | indent = " "; |
|---|
| 55 | writeMetaData(); |
|---|
| 56 | writeWayPoints(); |
|---|
| 57 | writeRoutes(); |
|---|
| 58 | writeTracks(); |
|---|
| 59 | out.print("</gpx>"); |
|---|
| 60 | out.flush(); |
|---|
| 61 | } |
|---|
| 62 | |
|---|
| 63 | @SuppressWarnings("unchecked") |
|---|
| 64 | private void writeAttr(Map<String, Object> attr) { |
|---|
| 65 | // FIXME this loop is evil, because it does not assure the |
|---|
| 66 | // correct element order specified by the xml schema. |
|---|
| 67 | // for now it works, but future extension could get very complex and unmaintainable |
|---|
| 68 | for (Map.Entry<String, Object> ent : attr.entrySet()) { |
|---|
| 69 | String k = ent.getKey(); |
|---|
| 70 | if (k.equals(GpxData.META_LINKS)) { |
|---|
| 71 | for (GpxLink link : (Collection<GpxLink>) ent.getValue()) { |
|---|
| 72 | gpxLink(link); |
|---|
| 73 | } |
|---|
| 74 | } else { |
|---|
| 75 | simpleTag(k, ent.getValue().toString()); |
|---|
| 76 | } |
|---|
| 77 | } |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | @SuppressWarnings("unchecked") |
|---|
| 81 | private void writeMetaData() { |
|---|
| 82 | Map<String, Object> attr = data.attr; |
|---|
| 83 | openln("metadata"); |
|---|
| 84 | |
|---|
| 85 | // write the description |
|---|
| 86 | if (attr.containsKey(GpxData.META_DESC)) { |
|---|
| 87 | simpleTag("desc", (String)attr.get(GpxData.META_DESC)); |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | // write the author details |
|---|
| 91 | if (attr.containsKey(GpxData.META_AUTHOR_NAME) |
|---|
| 92 | || attr.containsKey(GpxData.META_AUTHOR_EMAIL)) { |
|---|
| 93 | openln("author"); |
|---|
| 94 | // write the name |
|---|
| 95 | simpleTag("name", (String) attr.get(GpxData.META_AUTHOR_NAME)); |
|---|
| 96 | // write the email address |
|---|
| 97 | if(attr.containsKey(GpxData.META_AUTHOR_EMAIL)) { |
|---|
| 98 | String[] tmp = ((String)attr.get(GpxData.META_AUTHOR_EMAIL)).split("@"); |
|---|
| 99 | if(tmp.length == 2) { |
|---|
| 100 | inline("email", "id=\"" + tmp[0] + "\" domain=\""+tmp[1]+"\""); |
|---|
| 101 | } |
|---|
| 102 | } |
|---|
| 103 | // write the author link |
|---|
| 104 | gpxLink((GpxLink) attr.get(GpxData.META_AUTHOR_LINK)); |
|---|
| 105 | closeln("author"); |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | // write the copyright details |
|---|
| 109 | if(attr.containsKey(GpxData.META_COPYRIGHT_LICENSE) |
|---|
| 110 | || attr.containsKey(GpxData.META_COPYRIGHT_YEAR)) { |
|---|
| 111 | openAtt("copyright", "author=\""+ attr.get(GpxData.META_COPYRIGHT_AUTHOR) +"\""); |
|---|
| 112 | if(attr.containsKey(GpxData.META_COPYRIGHT_YEAR)) { |
|---|
| 113 | simpleTag("year", (String) attr.get(GpxData.META_COPYRIGHT_YEAR)); |
|---|
| 114 | } |
|---|
| 115 | if(attr.containsKey(GpxData.META_COPYRIGHT_LICENSE)) { |
|---|
| 116 | simpleTag("license", encode((String) attr.get(GpxData.META_COPYRIGHT_LICENSE))); |
|---|
| 117 | } |
|---|
| 118 | closeln("copyright"); |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | // write links |
|---|
| 122 | if(attr.containsKey(GpxData.META_LINKS)) { |
|---|
| 123 | for (GpxLink link : (Collection<GpxLink>) attr.get(GpxData.META_LINKS)) { |
|---|
| 124 | gpxLink(link); |
|---|
| 125 | } |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | // write keywords |
|---|
| 129 | if (attr.containsKey(GpxData.META_KEYWORDS)) { |
|---|
| 130 | simpleTag("keywords", (String)attr.get(GpxData.META_KEYWORDS)); |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | Bounds bounds = data.recalculateBounds(); |
|---|
| 134 | if(bounds != null) |
|---|
| 135 | { |
|---|
| 136 | String b = "minlat=\"" + bounds.getMin().lat() + "\" minlon=\"" + bounds.getMin().lon() + |
|---|
| 137 | "\" maxlat=\"" + bounds.getMax().lat() + "\" maxlon=\"" + bounds.getMax().lon() + "\"" ; |
|---|
| 138 | inline("bounds", b); |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | closeln("metadata"); |
|---|
| 142 | } |
|---|
| 143 | |
|---|
| 144 | private void writeWayPoints() { |
|---|
| 145 | for (WayPoint pnt : data.waypoints) { |
|---|
| 146 | wayPoint(pnt, WAY_POINT); |
|---|
| 147 | } |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | private void writeRoutes() { |
|---|
| 151 | for (GpxRoute rte : data.routes) { |
|---|
| 152 | openln("rte"); |
|---|
| 153 | writeAttr(rte.attr); |
|---|
| 154 | for (WayPoint pnt : rte.routePoints) { |
|---|
| 155 | wayPoint(pnt, ROUTE_POINT); |
|---|
| 156 | } |
|---|
| 157 | closeln("rte"); |
|---|
| 158 | } |
|---|
| 159 | } |
|---|
| 160 | |
|---|
| 161 | private void writeTracks() { |
|---|
| 162 | for (GpxTrack trk : data.tracks) { |
|---|
| 163 | open("trk"); |
|---|
| 164 | writeAttr(trk.getAttributes()); |
|---|
| 165 | for (GpxTrackSegment seg : trk.getSegments()) { |
|---|
| 166 | openln("trkseg"); |
|---|
| 167 | for (WayPoint pnt : seg.getWayPoints()) { |
|---|
| 168 | wayPoint(pnt, TRACK_POINT); |
|---|
| 169 | } |
|---|
| 170 | closeln("trkseg"); |
|---|
| 171 | } |
|---|
| 172 | closeln("trk"); |
|---|
| 173 | } |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | private void openln(String tag) { |
|---|
| 177 | open(tag); |
|---|
| 178 | out.println(); |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | private void open(String tag) { |
|---|
| 182 | out.print(indent + "<" + tag + ">"); |
|---|
| 183 | indent += " "; |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | private void openAtt(String tag, String attributes) { |
|---|
| 187 | out.println(indent + "<" + tag + " " + attributes + ">"); |
|---|
| 188 | indent += " "; |
|---|
| 189 | } |
|---|
| 190 | |
|---|
| 191 | private void inline(String tag, String attributes) { |
|---|
| 192 | out.println(indent + "<" + tag + " " + attributes + " />"); |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | private void close(String tag) { |
|---|
| 196 | indent = indent.substring(2); |
|---|
| 197 | out.print(indent + "</" + tag + ">"); |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | private void closeln(String tag) { |
|---|
| 201 | close(tag); |
|---|
| 202 | out.println(); |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | /** |
|---|
| 206 | * if content not null, open tag, write encoded content, and close tag |
|---|
| 207 | * else do nothing. |
|---|
| 208 | */ |
|---|
| 209 | private void simpleTag(String tag, String content) { |
|---|
| 210 | if (content != null && content.length() > 0) { |
|---|
| 211 | open(tag); |
|---|
| 212 | out.print(encode(content)); |
|---|
| 213 | out.println("</" + tag + ">"); |
|---|
| 214 | indent = indent.substring(2); |
|---|
| 215 | } |
|---|
| 216 | } |
|---|
| 217 | |
|---|
| 218 | /** |
|---|
| 219 | * output link |
|---|
| 220 | */ |
|---|
| 221 | private void gpxLink(GpxLink link) { |
|---|
| 222 | if (link != null) { |
|---|
| 223 | openAtt("link", "href=\"" + link.uri + "\""); |
|---|
| 224 | simpleTag("text", link.text); |
|---|
| 225 | simpleTag("type", link.type); |
|---|
| 226 | closeln("link"); |
|---|
| 227 | } |
|---|
| 228 | } |
|---|
| 229 | |
|---|
| 230 | /** |
|---|
| 231 | * output a point |
|---|
| 232 | */ |
|---|
| 233 | private void wayPoint(WayPoint pnt, int mode) { |
|---|
| 234 | String type; |
|---|
| 235 | switch(mode) { |
|---|
| 236 | case WAY_POINT: |
|---|
| 237 | type = "wpt"; |
|---|
| 238 | break; |
|---|
| 239 | case ROUTE_POINT: |
|---|
| 240 | type = "rtept"; |
|---|
| 241 | break; |
|---|
| 242 | case TRACK_POINT: |
|---|
| 243 | type = "trkpt"; |
|---|
| 244 | break; |
|---|
| 245 | default: |
|---|
| 246 | throw new RuntimeException(tr("Unknown mode {0}.", mode)); |
|---|
| 247 | } |
|---|
| 248 | if (pnt != null) { |
|---|
| 249 | LatLon c = pnt.getCoor(); |
|---|
| 250 | openAtt(type, "lat=\"" + c.lat() + "\" lon=\"" + c.lon() + "\""); |
|---|
| 251 | writeAttr(pnt.attr); |
|---|
| 252 | closeln(type); |
|---|
| 253 | } |
|---|
| 254 | } |
|---|
| 255 | } |
|---|