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

Last change on this file since 447 was 447, checked in by gebner, 17 years ago

Reduce GPX data memory usage.

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