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

Last change on this file since 729 was 627, checked in by framm, 16 years ago
  • Property svn:eol-style set to native
File size: 9.2 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 localName, String qName, 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 localName, String qName) {
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("desc")
227 || qName.equals("magvar") || qName.equals("geoidheight")
228 || qName.equals("name") || qName.equals("sym")
229 || qName.equals("cmt") || qName.equals("type")) {
230 currentWayPoint.attr.put(qName, accumulator.toString());
231 } else if (qName.equals("time")) {
232 currentWayPoint.attr.put(qName, accumulator.toString());
233 currentWayPoint.setTime();
234 } else if (qName.equals("rtept")) {
235 currentState = states.pop();
236 currentRoute.routePoints.add(currentWayPoint);
237 } else if (qName.equals("trkpt")) {
238 currentState = states.pop();
239 currentTrackSeg.add(currentWayPoint);
240 } else if (qName.equals("wpt")) {
241 currentState = states.pop();
242 currentData.waypoints.add(currentWayPoint);
243 }
244 break;
245 case trkseg:
246 if (qName.equals("trkseg")) {
247 currentState = states.pop();
248 currentTrack.trackSegs.add(currentTrackSeg);
249 }
250 break;
251 case trk:
252 if (qName.equals("trk")) {
253 currentState = states.pop();
254 currentData.tracks.add(currentTrack);
255 } else if (qName.equals("name") || qName.equals("cmt")
256 || qName.equals("desc") || qName.equals("src")
257 || qName.equals("type") || qName.equals("number")) {
258 currentTrack.attr.put(qName, accumulator.toString());
259 }
260 break;
261 case ext:
262 if (qName.equals("extensions")) {
263 currentState = states.pop();
264 }
265 break;
266 default:
267 if (qName.equals("wpt")) {
268 currentState = states.pop();
269 } else if (qName.equals("rte")) {
270 currentState = states.pop();
271 currentData.routes.add(currentRoute);
272 }
273 }
274 }
275
276 @Override public void endDocument() throws SAXException {
277 if (!states.empty()) {
278 throw new SAXException(tr("Parse error: invalid document structure for gpx document"));
279 }
280 data = currentData;
281 }
282 }
283
284 /**
285 * Parse the input stream and store the result in trackData and markerData
286 *
287 * @param relativeMarkerPath The directory to use as relative path for all &lt;wpt&gt;
288 * marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers.
289 */
290 public GpxReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException {
291
292 Parser parser = new Parser();
293 InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
294 try {
295 SAXParserFactory.newInstance().newSAXParser().parse(inputSource, parser);
296 data.storageFile = relativeMarkerPath;
297 } catch (ParserConfigurationException e) {
298 e.printStackTrace(); // broken SAXException chaining
299 throw new SAXException(e);
300 }
301 }
302}
Note: See TracBrowser for help on using the repository browser.