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

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

close bug #1702

  • Property svn:eol-style set to native
File size: 9.3 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 }
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 }
109 break;
110 case trk:
111 if (qName.equals("trkseg")) {
112 states.push(currentState);
113 currentState = state.trkseg;
114 currentTrackSeg = new ArrayList<WayPoint>();
115 } else if (qName.equals("link")) {
116 states.push(currentState);
117 currentState = state.link;
118 currentLink = new GpxLink(atts.getValue("href"));
119 } else if (qName.equals("extensions")) {
120 states.push(currentState);
121 currentState = state.ext;
122 }
123 break;
124 case metadata:
125 if (qName.equals("author")) {
126 states.push(currentState);
127 currentState = state.author;
128 } else if (qName.equals("extensions")) {
129 states.push(currentState);
130 currentState = state.ext;
131 }
132 break;
133 case trkseg:
134 if (qName.equals("trkpt")) {
135 states.push(currentState);
136 currentState = state.wpt;
137 currentWayPoint = new WayPoint(parseLatLon(atts));
138 }
139 break;
140 case wpt:
141 if (qName.equals("link")) {
142 states.push(currentState);
143 currentState = state.link;
144 currentLink = new GpxLink(atts.getValue("href"));
145 } else if (qName.equals("extensions")) {
146 states.push(currentState);
147 currentState = state.ext;
148 }
149 break;
150 case rte:
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("rtept")) {
156 states.push(currentState);
157 currentState = state.wpt;
158 currentWayPoint = new WayPoint(parseLatLon(atts));
159 } else if (qName.equals("extensions")) {
160 states.push(currentState);
161 currentState = state.ext;
162 }
163 break;
164 default:
165 }
166 accumulator.setLength(0);
167 }
168
169 @Override public void characters(char[] ch, int start, int length) {
170 accumulator.append(ch, start, length);
171 }
172
173 private Map<String, Object> getAttr() {
174 switch (currentState) {
175 case rte: return currentRoute.attr;
176 case metadata: return currentData.attr;
177 case wpt: return currentWayPoint.attr;
178 case trk: return currentTrack.attr;
179 default: return null;
180 }
181 }
182
183 @Override public void endElement(String namespaceURI, String qName, String rqName) {
184 switch (currentState) {
185 case metadata:
186 if (qName.equals("name") || qName.equals("desc") ||
187 qName.equals("time") || qName.equals("keywords")) {
188 currentData.attr.put(qName, accumulator.toString());
189 } else if (qName.equals("metadata")) {
190 currentState = states.pop();
191 }
192 //TODO: parse copyright, bounds, extensions
193 break;
194 case author:
195 if (qName.equals("author")) {
196 currentState = states.pop();
197 } else if (qName.equals("name") || qName.equals("email")) {
198 currentData.attr.put("author" + qName, accumulator.toString());
199 } else if (qName.equals("link")) {
200 currentData.attr.put("authorlink", currentLink);
201 }
202 break;
203 case link:
204 if (qName.equals("text")) {
205 currentLink.text = accumulator.toString();
206 } else if (qName.equals("type")) {
207 currentLink.type = accumulator.toString();
208 } else if (qName.equals("link")) {
209 // <link>URL</link>
210 if (currentLink.uri == null)
211 currentLink.uri = accumulator.toString();
212
213 currentState = states.pop();
214 }
215 if (currentState == state.author) {
216 currentData.attr.put("authorlink", currentLink);
217 } else if (currentState != state.link) {
218 Map<String, Object> attr = getAttr();
219 if (!attr.containsKey("link")) {
220 attr.put("link", new LinkedList<GpxLink>());
221 }
222 ((Collection<GpxLink>) attr.get("link")).add(currentLink);
223 }
224 break;
225 case wpt:
226 if (qName.equals("ele") || qName.equals("magvar")
227 || qName.equals("geoidheight") || qName.equals("name")
228 || qName.equals("sym") || qName.equals("type")) {
229 currentWayPoint.attr.put(qName, accumulator.toString());
230 } else if (qName.equals("time")) {
231 currentWayPoint.attr.put(qName, accumulator.toString());
232 currentWayPoint.setTime();
233 } else if (qName.equals("cmt") || qName.equals("desc")) {
234 currentWayPoint.attr.put(qName, accumulator.toString());
235 currentWayPoint.setGarminCommentTime(qName);
236 } else if (qName.equals("rtept")) {
237 currentState = states.pop();
238 currentRoute.routePoints.add(currentWayPoint);
239 } else if (qName.equals("trkpt")) {
240 currentState = states.pop();
241 currentTrackSeg.add(currentWayPoint);
242 } else if (qName.equals("wpt")) {
243 currentState = states.pop();
244 currentData.waypoints.add(currentWayPoint);
245 }
246 break;
247 case trkseg:
248 if (qName.equals("trkseg")) {
249 currentState = states.pop();
250 currentTrack.trackSegs.add(currentTrackSeg);
251 }
252 break;
253 case trk:
254 if (qName.equals("trk")) {
255 currentState = states.pop();
256 currentData.tracks.add(currentTrack);
257 } else if (qName.equals("name") || qName.equals("cmt")
258 || qName.equals("desc") || qName.equals("src")
259 || qName.equals("type") || qName.equals("number")) {
260 currentTrack.attr.put(qName, accumulator.toString());
261 }
262 break;
263 case ext:
264 if (qName.equals("extensions")) {
265 currentState = states.pop();
266 }
267 break;
268 default:
269 if (qName.equals("wpt")) {
270 currentState = states.pop();
271 } else if (qName.equals("rte")) {
272 currentState = states.pop();
273 currentData.routes.add(currentRoute);
274 }
275 }
276 }
277
278 @Override public void endDocument() throws SAXException {
279 if (!states.empty()) {
280 throw new SAXException(tr("Parse error: invalid document structure for gpx document"));
281 }
282 data = currentData;
283 }
284 }
285
286 /**
287 * Parse the input stream and store the result in trackData and markerData
288 *
289 * @param relativeMarkerPath The directory to use as relative path for all &lt;wpt&gt;
290 * marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers.
291 */
292 public GpxReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException {
293
294 Parser parser = new Parser();
295 InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
296 try {
297 SAXParserFactory factory = SAXParserFactory.newInstance();
298 factory.setNamespaceAware(true);
299 factory.newSAXParser().parse(inputSource, parser);
300 } catch (ParserConfigurationException e) {
301 e.printStackTrace(); // broken SAXException chaining
302 throw new SAXException(e);
303 }
304 }
305}
Note: See TracBrowser for help on using the repository browser.