source: josm/trunk/src/org/openstreetmap/josm/io/GpxReader.java@ 1574

Last change on this file since 1574 was 1574, checked in by stoecker, 15 years ago

close #2214 - patch by Henrik Niehaus - GPX export metadata handling

  • Property svn:eol-style set to native
File size: 13.9 KB
Line 
1//License: GPL. Copyright 2007 by Immanuel Scholz and others
2
3//TODO: this is far from complete, but can emulate old RawGps behaviour
4package org.openstreetmap.josm.io;
5
6import static org.openstreetmap.josm.tools.I18n.tr;
7
8import java.io.File;
9import java.io.IOException;
10import java.io.InputStream;
11import java.io.InputStreamReader;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.LinkedList;
15import java.util.Map;
16import java.util.Stack;
17
18import javax.xml.parsers.ParserConfigurationException;
19import javax.xml.parsers.SAXParserFactory;
20
21import org.openstreetmap.josm.data.coor.LatLon;
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.WayPoint;
27import org.xml.sax.Attributes;
28import org.xml.sax.InputSource;
29import org.xml.sax.SAXException;
30import org.xml.sax.helpers.DefaultHandler;
31
32/**
33 * Read a gpx file. Bounds are not read, as we caluclate them. @see GpxData.recalculateBounds()
34 * @author imi, ramack
35 */
36public class GpxReader {
37 // TODO: implement GPX 1.0 parsing
38
39 /**
40 * The resulting gpx data
41 */
42 public GpxData data;
43 public enum state { init, metadata, wpt, rte, trk, ext, author, link, trkseg, copyright}
44
45 private class Parser extends DefaultHandler {
46
47 private GpxData currentData;
48 private GpxTrack currentTrack;
49 private Collection<WayPoint> currentTrackSeg;
50 private GpxRoute currentRoute;
51 private WayPoint currentWayPoint;
52
53 private state currentState = state.init;
54
55 private GpxLink currentLink;
56 private Stack<state> states;
57
58 private StringBuffer accumulator = new StringBuffer();
59
60 @Override public void startDocument() {
61 accumulator = new StringBuffer();
62 states = new Stack<state>();
63 currentData = new GpxData();
64 }
65
66 private double parseCoord(String s) {
67 try {
68 return Double.parseDouble(s);
69 } catch (NumberFormatException ex) {
70 return Double.NaN;
71 }
72 }
73
74 private LatLon parseLatLon(Attributes atts) {
75 return new LatLon(
76 parseCoord(atts.getValue("lat")),
77 parseCoord(atts.getValue("lon")));
78 }
79
80 @Override public void startElement(String namespaceURI, String qName, String rqName, Attributes atts) throws SAXException {
81 switch(currentState) {
82 case init:
83 if (qName.equals("metadata")) {
84 states.push(currentState);
85 currentState = state.metadata;
86 } else if (qName.equals("wpt")) {
87 states.push(currentState);
88 currentState = state.wpt;
89 currentWayPoint = new WayPoint(parseLatLon(atts));
90 } else if (qName.equals("rte")) {
91 states.push(currentState);
92 currentState = state.rte;
93 currentRoute = new GpxRoute();
94 } else if (qName.equals("trk")) {
95 states.push(currentState);
96 currentState = state.trk;
97 currentTrack = new GpxTrack();
98 } else if (qName.equals("extensions")) {
99 states.push(currentState);
100 currentState = state.ext;
101 }
102 break;
103 case author:
104 if (qName.equals("link")) {
105 states.push(currentState);
106 currentState = state.link;
107 currentLink = new GpxLink(atts.getValue("href"));
108 } else if (qName.equals("email")) {
109 currentData.attr.put(GpxData.META_AUTHOR_EMAIL, atts.getValue("id") + "@" + atts.getValue("domain"));
110 }
111 break;
112 case trk:
113 if (qName.equals("trkseg")) {
114 states.push(currentState);
115 currentState = state.trkseg;
116 currentTrackSeg = new ArrayList<WayPoint>();
117 } else if (qName.equals("link")) {
118 states.push(currentState);
119 currentState = state.link;
120 currentLink = new GpxLink(atts.getValue("href"));
121 } else if (qName.equals("extensions")) {
122 states.push(currentState);
123 currentState = state.ext;
124 }
125 break;
126 case metadata:
127 if (qName.equals("author")) {
128 states.push(currentState);
129 currentState = state.author;
130 } else if (qName.equals("extensions")) {
131 states.push(currentState);
132 currentState = state.ext;
133 } else if (qName.equals("copyright")) {
134 states.push(currentState);
135 currentState = state.copyright;
136 currentData.attr.put(GpxData.META_COPYRIGHT_AUTHOR, atts.getValue("author"));
137 } else if (qName.equals("link")) {
138 states.push(currentState);
139 currentState = state.link;
140 currentLink = new GpxLink(atts.getValue("href"));
141 }
142 break;
143 case trkseg:
144 if (qName.equals("trkpt")) {
145 states.push(currentState);
146 currentState = state.wpt;
147 currentWayPoint = new WayPoint(parseLatLon(atts));
148 }
149 break;
150 case wpt:
151 if (qName.equals("link")) {
152 states.push(currentState);
153 currentState = state.link;
154 currentLink = new GpxLink(atts.getValue("href"));
155 } else if (qName.equals("extensions")) {
156 states.push(currentState);
157 currentState = state.ext;
158 }
159 break;
160 case rte:
161 if (qName.equals("link")) {
162 states.push(currentState);
163 currentState = state.link;
164 currentLink = new GpxLink(atts.getValue("href"));
165 } else if (qName.equals("rtept")) {
166 states.push(currentState);
167 currentState = state.wpt;
168 currentWayPoint = new WayPoint(parseLatLon(atts));
169 } else if (qName.equals("extensions")) {
170 states.push(currentState);
171 currentState = state.ext;
172 }
173 break;
174 default:
175 }
176 accumulator.setLength(0);
177 }
178
179 @Override public void characters(char[] ch, int start, int length) {
180 accumulator.append(ch, start, length);
181 }
182
183 private Map<String, Object> getAttr() {
184 switch (currentState) {
185 case rte: return currentRoute.attr;
186 case metadata: return currentData.attr;
187 case wpt: return currentWayPoint.attr;
188 case trk: return currentTrack.attr;
189 default: return null;
190 }
191 }
192
193 @Override public void endElement(String namespaceURI, String qName, String rqName) {
194 switch (currentState) {
195 case metadata:
196 if (qName.equals("name")) {
197 currentData.attr.put(GpxData.META_NAME, accumulator.toString());
198 } else if (qName.equals("desc")) {
199 currentData.attr.put(GpxData.META_DESC, accumulator.toString());
200 } else if (qName.equals("time")) {
201 currentData.attr.put(GpxData.META_TIME, accumulator.toString());
202 } else if (qName.equals("keywords")) {
203 currentData.attr.put(GpxData.META_KEYWORDS, accumulator.toString());
204 } else if (qName.equals("metadata")) {
205 currentState = states.pop();
206 }
207 //TODO: parse bounds, extensions
208 break;
209 case author:
210 if (qName.equals("author")) {
211 currentState = states.pop();
212 } else if (qName.equals("name")) {
213 currentData.attr.put(GpxData.META_AUTHOR_NAME, accumulator.toString());
214 } else if (qName.equals("email")) {
215 // do nothing, has been parsed on startElement
216 } else if (qName.equals("link")) {
217 currentData.attr.put(GpxData.META_AUTHOR_LINK, currentLink);
218 }
219 break;
220 case copyright:
221 if (qName.equals("copyright")) {
222 currentState = states.pop();
223 } else if (qName.equals("year")) {
224 currentData.attr.put(GpxData.META_COPYRIGHT_YEAR, accumulator.toString());
225 } else if (qName.equals("license")) {
226 currentData.attr.put(GpxData.META_COPYRIGHT_LICENSE, accumulator.toString());
227 }
228 break;
229 case link:
230 if (qName.equals("text")) {
231 currentLink.text = accumulator.toString();
232 } else if (qName.equals("type")) {
233 currentLink.type = accumulator.toString();
234 } else if (qName.equals("link")) {
235 currentState = states.pop();
236 }
237 if (currentState == state.author) {
238 currentData.attr.put(GpxData.META_AUTHOR_LINK, currentLink);
239 } else if (currentState == state.metadata) {
240 Map<String, Object> attr = getAttr();
241 if (!attr.containsKey(GpxData.META_LINKS)) {
242 attr.put(GpxData.META_LINKS, new LinkedList<GpxLink>());
243 }
244 ((Collection<GpxLink>) attr.get(GpxData.META_LINKS)).add(currentLink);
245 }
246 break;
247 case wpt:
248 if ( qName.equals("ele") || qName.equals("magvar")
249 || qName.equals("name") || qName.equals("geoidheight")
250 || qName.equals("type") || qName.equals("sym")) {
251 currentWayPoint.attr.put(qName, accumulator.toString());
252 } else if(qName.equals("hdop") /*|| qName.equals("vdop") ||
253 qName.equals("pdop")*/) {
254 try {
255 currentWayPoint.attr.put(qName, Float.parseFloat(accumulator.toString()));
256 } catch(Exception e) {
257 currentWayPoint.attr.put(qName, new Float(0));
258 }
259 } else if (qName.equals("time")) {
260 currentWayPoint.attr.put(qName, accumulator.toString());
261 currentWayPoint.setTime();
262 } else if (qName.equals("cmt") || qName.equals("desc")) {
263 currentWayPoint.attr.put(qName, accumulator.toString());
264 currentWayPoint.setGarminCommentTime(qName);
265 } else if (qName.equals("rtept")) {
266 currentState = states.pop();
267 currentRoute.routePoints.add(currentWayPoint);
268 } else if (qName.equals("trkpt")) {
269 currentState = states.pop();
270 currentTrackSeg.add(currentWayPoint);
271 } else if (qName.equals("wpt")) {
272 currentState = states.pop();
273 currentData.waypoints.add(currentWayPoint);
274 }
275 break;
276 case trkseg:
277 if (qName.equals("trkseg")) {
278 currentState = states.pop();
279 currentTrack.trackSegs.add(currentTrackSeg);
280 }
281 break;
282 case trk:
283 if (qName.equals("trk")) {
284 currentState = states.pop();
285 currentData.tracks.add(currentTrack);
286 } else if (qName.equals("name") || qName.equals("cmt")
287 || qName.equals("desc") || qName.equals("src")
288 || qName.equals("type") || qName.equals("number")) {
289 currentTrack.attr.put(qName, accumulator.toString());
290 }
291 break;
292 case ext:
293 if (qName.equals("extensions")) {
294 currentState = states.pop();
295 }
296 break;
297 default:
298 if (qName.equals("wpt")) {
299 currentState = states.pop();
300 } else if (qName.equals("rte")) {
301 currentState = states.pop();
302 currentData.routes.add(currentRoute);
303 }
304 }
305 }
306
307 @Override public void endDocument() throws SAXException {
308 if (!states.empty()) {
309 throw new SAXException(tr("Parse error: invalid document structure for gpx document"));
310 }
311 data = currentData;
312 }
313 }
314
315 /**
316 * Parse the input stream and store the result in trackData and markerData
317 *
318 * @param relativeMarkerPath The directory to use as relative path for all &lt;wpt&gt;
319 * marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers.
320 */
321 public GpxReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException {
322
323 Parser parser = new Parser();
324 InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
325 try {
326 SAXParserFactory factory = SAXParserFactory.newInstance();
327 factory.setNamespaceAware(true);
328 factory.newSAXParser().parse(inputSource, parser);
329 } catch (ParserConfigurationException e) {
330 e.printStackTrace(); // broken SAXException chaining
331 throw new SAXException(e);
332 }
333 }
334}
Note: See TracBrowser for help on using the repository browser.