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

Last change on this file since 572 was 572, checked in by david, 16 years ago

Reorganise audio interface in light of recent usability comments

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.Collection;
13import java.util.HashMap;
14import java.util.LinkedList;
15import java.util.ArrayList;
16import java.util.Stack;
17import java.util.Map;
18
19import javax.xml.parsers.ParserConfigurationException;
20import javax.xml.parsers.SAXParserFactory;
21
22import org.openstreetmap.josm.Main;
23import org.openstreetmap.josm.data.coor.LatLon;
24import org.openstreetmap.josm.data.gpx.GpxData;
25import org.openstreetmap.josm.data.gpx.GpxLink;
26import org.openstreetmap.josm.data.gpx.GpxTrack;
27import org.openstreetmap.josm.data.gpx.WayPoint;
28import org.openstreetmap.josm.data.gpx.GpxRoute;
29import org.openstreetmap.josm.gui.layer.markerlayer.MarkerProducers;
30import org.xml.sax.Attributes;
31import org.xml.sax.InputSource;
32import org.xml.sax.SAXException;
33import org.xml.sax.helpers.DefaultHandler;
34
35/**
36 * Read a gpx file. Bounds are not read, as we caluclate them. @see GpxData.recalculateBounds()
37 * @author imi, ramack
38 */
39public class GpxReader {
40 // TODO: implement GPX 1.0 parsing
41
42 /**
43 * The resulting gpx data
44 */
45 public GpxData data;
46 public enum state { init, metadata, wpt, rte, trk, ext, author, link, trkseg }
47
48 private class Parser extends DefaultHandler {
49
50 private GpxData currentData;
51 private GpxTrack currentTrack;
52 private Collection<WayPoint> currentTrackSeg;
53 private GpxRoute currentRoute;
54 private WayPoint currentWayPoint;
55
56 private state currentState = state.init;
57
58 private GpxLink currentLink;
59 private Stack<state> states;
60
61 private StringBuffer accumulator = new StringBuffer();
62
63 @Override public void startDocument() {
64 accumulator = new StringBuffer();
65 states = new Stack<state>();
66 currentData = new GpxData();
67 }
68
69 private double parseCoord(String s) {
70 try {
71 return Double.parseDouble(s);
72 } catch (NumberFormatException ex) {
73 return Double.NaN;
74 }
75 }
76
77 private LatLon parseLatLon(Attributes atts) {
78 return new LatLon(
79 parseCoord(atts.getValue("lat")),
80 parseCoord(atts.getValue("lon")));
81 }
82
83 @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
84 switch(currentState) {
85 case init:
86 if (qName.equals("metadata")) {
87 states.push(currentState);
88 currentState = state.metadata;
89 } else if (qName.equals("wpt")) {
90 states.push(currentState);
91 currentState = state.wpt;
92 currentWayPoint = new WayPoint(parseLatLon(atts));
93 } else if (qName.equals("rte")) {
94 states.push(currentState);
95 currentState = state.rte;
96 currentRoute = new GpxRoute();
97 } else if (qName.equals("trk")) {
98 states.push(currentState);
99 currentState = state.trk;
100 currentTrack = new GpxTrack();
101 } else if (qName.equals("extensions")) {
102 states.push(currentState);
103 currentState = state.ext;
104 }
105 break;
106 case author:
107 if (qName.equals("link")) {
108 states.push(currentState);
109 currentState = state.link;
110 currentLink = new GpxLink(atts.getValue("href"));
111 }
112 break;
113 case trk:
114 if (qName.equals("trkseg")) {
115 states.push(currentState);
116 currentState = state.trkseg;
117 currentTrackSeg = new ArrayList<WayPoint>();
118 } else if (qName.equals("link")) {
119 states.push(currentState);
120 currentState = state.link;
121 currentLink = new GpxLink(atts.getValue("href"));
122 } else if (qName.equals("extensions")) {
123 states.push(currentState);
124 currentState = state.ext;
125 }
126 break;
127 case metadata:
128 if (qName.equals("author")) {
129 states.push(currentState);
130 currentState = state.author;
131 } else if (qName.equals("extensions")) {
132 states.push(currentState);
133 currentState = state.ext;
134 }
135 break;
136 case trkseg:
137 if (qName.equals("trkpt")) {
138 states.push(currentState);
139 currentState = state.wpt;
140 currentWayPoint = new WayPoint(parseLatLon(atts));
141 }
142 break;
143 case wpt:
144 if (qName.equals("link")) {
145 states.push(currentState);
146 currentState = state.link;
147 currentLink = new GpxLink(atts.getValue("href"));
148 } else if (qName.equals("extensions")) {
149 states.push(currentState);
150 currentState = state.ext;
151 }
152 break;
153 case rte:
154 if (qName.equals("link")) {
155 states.push(currentState);
156 currentState = state.link;
157 currentLink = new GpxLink(atts.getValue("href"));
158 } else if (qName.equals("rtept")) {
159 states.push(currentState);
160 currentState = state.wpt;
161 currentWayPoint = new WayPoint(parseLatLon(atts));
162 } else if (qName.equals("extensions")) {
163 states.push(currentState);
164 currentState = state.ext;
165 }
166 break;
167 default:
168 }
169 accumulator.setLength(0);
170 }
171
172 @Override public void characters(char[] ch, int start, int length) {
173 accumulator.append(ch, start, length);
174 }
175
176 private Map<String, Object> getAttr() {
177 switch (currentState) {
178 case rte: return currentRoute.attr;
179 case metadata: return currentData.attr;
180 case wpt: return currentWayPoint.attr;
181 case trk: return currentTrack.attr;
182 default: return null;
183 }
184 }
185
186 @Override public void endElement(String namespaceURI, String localName, String qName) {
187 switch (currentState) {
188 case metadata:
189 if (qName.equals("name") || qName.equals("desc") ||
190 qName.equals("time") || qName.equals("keywords")) {
191 currentData.attr.put(qName, accumulator.toString());
192 } else if (qName.equals("metadata")) {
193 currentState = states.pop();
194 }
195 //TODO: parse copyright, bounds, extensions
196 break;
197 case author:
198 if (qName.equals("author")) {
199 currentState = states.pop();
200 } else if (qName.equals("name") || qName.equals("email")) {
201 currentData.attr.put("author" + qName, accumulator.toString());
202 } else if (qName.equals("link")) {
203 currentData.attr.put("authorlink", currentLink);
204 }
205 break;
206 case link:
207 if (qName.equals("text")) {
208 currentLink.text = accumulator.toString();
209 } else if (qName.equals("type")) {
210 currentLink.type = accumulator.toString();
211 } else if (qName.equals("link")) {
212 // <link>URL</link>
213 if (currentLink.uri == null)
214 currentLink.uri = accumulator.toString();
215
216 currentState = states.pop();
217 }
218 if (currentState == state.author) {
219 currentData.attr.put("authorlink", currentLink);
220 } else if (currentState != state.link) {
221 Map<String, Object> attr = getAttr();
222 if (!attr.containsKey("link")) {
223 attr.put("link", new LinkedList<GpxLink>());
224 }
225 ((Collection<GpxLink>) attr.get("link")).add(currentLink);
226 }
227 break;
228 case wpt:
229 if (qName.equals("ele") || qName.equals("desc")
230 || qName.equals("magvar") || qName.equals("geoidheight")
231 || qName.equals("name") || qName.equals("sym")
232 || qName.equals("cmt") || qName.equals("type")) {
233 currentWayPoint.attr.put(qName, accumulator.toString());
234 } else if (qName.equals("time")) {
235 currentWayPoint.attr.put(qName, accumulator.toString());
236 currentWayPoint.setTime();
237 } else if (qName.equals("rtept")) {
238 currentState = states.pop();
239 currentRoute.routePoints.add(currentWayPoint);
240 } else if (qName.equals("trkpt")) {
241 currentState = states.pop();
242 currentTrackSeg.add(currentWayPoint);
243 } else if (qName.equals("wpt")) {
244 currentState = states.pop();
245 currentData.waypoints.add(currentWayPoint);
246 }
247 break;
248 case trkseg:
249 if (qName.equals("trkseg")) {
250 currentState = states.pop();
251 currentTrack.trackSegs.add(currentTrackSeg);
252 }
253 break;
254 case trk:
255 if (qName.equals("trk")) {
256 currentState = states.pop();
257 currentData.tracks.add(currentTrack);
258 } else if (qName.equals("name") || qName.equals("cmt")
259 || qName.equals("desc") || qName.equals("src")
260 || qName.equals("type") || qName.equals("number")) {
261 currentTrack.attr.put(qName, accumulator.toString());
262 }
263 break;
264 case ext:
265 if (qName.equals("extensions")) {
266 currentState = states.pop();
267 }
268 break;
269 default:
270 if (qName.equals("wpt")) {
271 currentState = states.pop();
272 } else if (qName.equals("rte")) {
273 currentState = states.pop();
274 currentData.routes.add(currentRoute);
275 }
276 }
277 }
278
279 @Override public void endDocument() throws SAXException {
280 if (!states.empty()) {
281 throw new SAXException(tr("Parse error: invalid document structure for gpx document"));
282 }
283 data = currentData;
284 }
285 }
286
287 /**
288 * Parse the input stream and store the result in trackData and markerData
289 *
290 * @param relativeMarkerPath The directory to use as relative path for all &lt;wpt&gt;
291 * marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers.
292 */
293 public GpxReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException {
294
295 Parser parser = new Parser();
296 InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
297 try {
298 SAXParserFactory.newInstance().newSAXParser().parse(inputSource, parser);
299 data.storageFile = relativeMarkerPath;
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.