source: josm/trunk/src/org/openstreetmap/josm/io/GpxWriter.java@ 13795

Last change on this file since 13795 was 12156, checked in by michael2402, 7 years ago

See #14120: Don't make gpx tracks depend on the isChanged method, use a listener based approach instead.

  • Property svn:eol-style set to native
File size: 10.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.BufferedWriter;
7import java.io.OutputStream;
8import java.io.OutputStreamWriter;
9import java.io.PrintWriter;
10import java.nio.charset.StandardCharsets;
11import java.util.Collection;
12import java.util.List;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import javax.xml.XMLConstants;
17
18import org.openstreetmap.josm.data.Bounds;
19import org.openstreetmap.josm.data.coor.LatLon;
20import org.openstreetmap.josm.data.gpx.Extensions;
21import org.openstreetmap.josm.data.gpx.GpxConstants;
22import org.openstreetmap.josm.data.gpx.GpxData;
23import org.openstreetmap.josm.data.gpx.GpxLink;
24import org.openstreetmap.josm.data.gpx.GpxRoute;
25import org.openstreetmap.josm.data.gpx.GpxTrack;
26import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
27import org.openstreetmap.josm.data.gpx.IWithAttributes;
28import org.openstreetmap.josm.data.gpx.WayPoint;
29import org.openstreetmap.josm.tools.JosmRuntimeException;
30
31/**
32 * Writes GPX files from GPX data or OSM data.
33 */
34public class GpxWriter extends XmlWriter implements GpxConstants {
35
36 /**
37 * Constructs a new {@code GpxWriter}.
38 * @param out The output writer
39 */
40 public GpxWriter(PrintWriter out) {
41 super(out);
42 }
43
44 /**
45 * Constructs a new {@code GpxWriter}.
46 * @param out The output stream
47 */
48 public GpxWriter(OutputStream out) {
49 super(new PrintWriter(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8))));
50 }
51
52 private GpxData data;
53 private String indent = "";
54
55 private static final int WAY_POINT = 0;
56 private static final int ROUTE_POINT = 1;
57 private static final int TRACK_POINT = 2;
58
59 /**
60 * Writes the given GPX data.
61 * @param data The data to write
62 */
63 public void write(GpxData data) {
64 this.data = data;
65 // We write JOSM specific meta information into gpx 'extensions' elements.
66 // In particular it is noted whether the gpx data is from the OSM server
67 // (so the rendering of clouds of anonymous TrackPoints can be improved)
68 // and some extra synchronization info for export of AudioMarkers.
69 // It is checked in advance, if any extensions are used, so we know whether
70 // a namespace declaration is necessary.
71 boolean hasExtensions = data.fromServer;
72 if (!hasExtensions) {
73 for (WayPoint wpt : data.waypoints) {
74 Extensions extensions = (Extensions) wpt.get(META_EXTENSIONS);
75 if (extensions != null && !extensions.isEmpty()) {
76 hasExtensions = true;
77 break;
78 }
79 }
80 }
81
82 out.println("<?xml version='1.0' encoding='UTF-8'?>");
83 out.println("<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
84 (hasExtensions ? String.format(" xmlns:josm=\"%s\"%n", JOSM_EXTENSIONS_NAMESPACE_URI) : "") +
85 " xmlns:xsi=\""+XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI+"\" \n" +
86 " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">");
87 indent = " ";
88 writeMetaData();
89 writeWayPoints();
90 writeRoutes();
91 writeTracks();
92 out.print("</gpx>");
93 out.flush();
94 }
95
96 private void writeAttr(IWithAttributes obj, List<String> keys) {
97 for (String key : keys) {
98 if (META_LINKS.equals(key)) {
99 Collection<GpxLink> lValue = obj.<GpxLink>getCollection(key);
100 if (lValue != null) {
101 for (GpxLink link : lValue) {
102 gpxLink(link);
103 }
104 }
105 } else if (META_EXTENSIONS.equals(key)) {
106 Extensions extensions = (Extensions) obj.get(key);
107 if (extensions != null) {
108 gpxExtensions(extensions);
109 }
110 } else {
111 String value = obj.getString(key);
112 if (value != null) {
113 simpleTag(key, value);
114 }
115 }
116 }
117 }
118
119 private void writeMetaData() {
120 Map<String, Object> attr = data.attr;
121 openln("metadata");
122
123 // write the description
124 if (attr.containsKey(META_DESC)) {
125 simpleTag("desc", data.getString(META_DESC));
126 }
127
128 // write the author details
129 if (attr.containsKey(META_AUTHOR_NAME)
130 || attr.containsKey(META_AUTHOR_EMAIL)) {
131 openln("author");
132 // write the name
133 simpleTag("name", data.getString(META_AUTHOR_NAME));
134 // write the email address
135 if (attr.containsKey(META_AUTHOR_EMAIL)) {
136 String[] tmp = data.getString(META_AUTHOR_EMAIL).split("@");
137 if (tmp.length == 2) {
138 inline("email", "id=\"" + tmp[0] + "\" domain=\""+tmp[1]+'\"');
139 }
140 }
141 // write the author link
142 gpxLink((GpxLink) data.get(META_AUTHOR_LINK));
143 closeln("author");
144 }
145
146 // write the copyright details
147 if (attr.containsKey(META_COPYRIGHT_LICENSE)
148 || attr.containsKey(META_COPYRIGHT_YEAR)) {
149 openAtt("copyright", "author=\""+ data.get(META_COPYRIGHT_AUTHOR) +'\"');
150 if (attr.containsKey(META_COPYRIGHT_YEAR)) {
151 simpleTag("year", (String) data.get(META_COPYRIGHT_YEAR));
152 }
153 if (attr.containsKey(META_COPYRIGHT_LICENSE)) {
154 simpleTag("license", encode((String) data.get(META_COPYRIGHT_LICENSE)));
155 }
156 closeln("copyright");
157 }
158
159 // write links
160 if (attr.containsKey(META_LINKS)) {
161 for (GpxLink link : data.<GpxLink>getCollection(META_LINKS)) {
162 gpxLink(link);
163 }
164 }
165
166 // write keywords
167 if (attr.containsKey(META_KEYWORDS)) {
168 simpleTag("keywords", data.getString(META_KEYWORDS));
169 }
170
171 Bounds bounds = data.recalculateBounds();
172 if (bounds != null) {
173 String b = "minlat=\"" + bounds.getMinLat() + "\" minlon=\"" + bounds.getMinLon() +
174 "\" maxlat=\"" + bounds.getMaxLat() + "\" maxlon=\"" + bounds.getMaxLon() + '\"';
175 inline("bounds", b);
176 }
177
178 if (data.fromServer) {
179 openln("extensions");
180 simpleTag("josm:from-server", "true");
181 closeln("extensions");
182 }
183
184 closeln("metadata");
185 }
186
187 private void writeWayPoints() {
188 for (WayPoint pnt : data.getWaypoints()) {
189 wayPoint(pnt, WAY_POINT);
190 }
191 }
192
193 private void writeRoutes() {
194 for (GpxRoute rte : data.getRoutes()) {
195 openln("rte");
196 writeAttr(rte, RTE_TRK_KEYS);
197 for (WayPoint pnt : rte.routePoints) {
198 wayPoint(pnt, ROUTE_POINT);
199 }
200 closeln("rte");
201 }
202 }
203
204 private void writeTracks() {
205 for (GpxTrack trk : data.getTracks()) {
206 openln("trk");
207 writeAttr(trk, RTE_TRK_KEYS);
208 for (GpxTrackSegment seg : trk.getSegments()) {
209 openln("trkseg");
210 for (WayPoint pnt : seg.getWayPoints()) {
211 wayPoint(pnt, TRACK_POINT);
212 }
213 closeln("trkseg");
214 }
215 closeln("trk");
216 }
217 }
218
219 private void openln(String tag) {
220 open(tag);
221 out.println();
222 }
223
224 private void open(String tag) {
225 out.print(indent + '<' + tag + '>');
226 indent += " ";
227 }
228
229 private void openAtt(String tag, String attributes) {
230 out.println(indent + '<' + tag + ' ' + attributes + '>');
231 indent += " ";
232 }
233
234 private void inline(String tag, String attributes) {
235 out.println(indent + '<' + tag + ' ' + attributes + "/>");
236 }
237
238 private void close(String tag) {
239 indent = indent.substring(2);
240 out.print(indent + "</" + tag + '>');
241 }
242
243 private void closeln(String tag) {
244 close(tag);
245 out.println();
246 }
247
248 /**
249 * if content not null, open tag, write encoded content, and close tag
250 * else do nothing.
251 * @param tag GPX tag
252 * @param content content
253 */
254 private void simpleTag(String tag, String content) {
255 if (content != null && !content.isEmpty()) {
256 open(tag);
257 out.print(encode(content));
258 out.println("</" + tag + '>');
259 indent = indent.substring(2);
260 }
261 }
262
263 /**
264 * output link
265 * @param link link
266 */
267 private void gpxLink(GpxLink link) {
268 if (link != null) {
269 openAtt("link", "href=\"" + link.uri + '\"');
270 simpleTag("text", link.text);
271 simpleTag("type", link.type);
272 closeln("link");
273 }
274 }
275
276 /**
277 * output a point
278 * @param pnt waypoint
279 * @param mode {@code WAY_POINT} for {@code wpt}, {@code ROUTE_POINT} for {@code rtept}, {@code TRACK_POINT} for {@code trkpt}
280 */
281 private void wayPoint(WayPoint pnt, int mode) {
282 String type;
283 switch(mode) {
284 case WAY_POINT:
285 type = "wpt";
286 break;
287 case ROUTE_POINT:
288 type = "rtept";
289 break;
290 case TRACK_POINT:
291 type = "trkpt";
292 break;
293 default:
294 throw new JosmRuntimeException(tr("Unknown mode {0}.", mode));
295 }
296 if (pnt != null) {
297 LatLon c = pnt.getCoor();
298 String coordAttr = "lat=\"" + c.lat() + "\" lon=\"" + c.lon() + '\"';
299 if (pnt.attr.isEmpty()) {
300 inline(type, coordAttr);
301 } else {
302 openAtt(type, coordAttr);
303 writeAttr(pnt, WPT_KEYS);
304 closeln(type);
305 }
306 }
307 }
308
309 private void gpxExtensions(Extensions extensions) {
310 if (extensions != null && !extensions.isEmpty()) {
311 openln("extensions");
312 for (Entry<String, String> e : extensions.entrySet()) {
313 simpleTag("josm:" + e.getKey(), e.getValue());
314 }
315 closeln("extensions");
316 }
317 }
318}
Note: See TracBrowser for help on using the repository browser.