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

Last change on this file since 14216 was 14081, checked in by Don-vip, 6 years ago

see #16010 - Ignore tests using JMockit on Java 11+, workaround to https://github.com/jmockit/jmockit1/issues/534

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