| 1 | // License: GPL. Copyright 2007 by Immanuel Scholz and others
|
|---|
| 2 | package org.openstreetmap.josm.io;
|
|---|
| 3 |
|
|---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 5 |
|
|---|
| 6 | import java.io.File;
|
|---|
| 7 | import java.io.IOException;
|
|---|
| 8 | import java.io.InputStream;
|
|---|
| 9 | import java.io.InputStreamReader;
|
|---|
| 10 | import java.util.ArrayList;
|
|---|
| 11 | import java.util.Collection;
|
|---|
| 12 | import java.util.HashMap;
|
|---|
| 13 | import java.util.LinkedList;
|
|---|
| 14 | import java.util.Stack;
|
|---|
| 15 |
|
|---|
| 16 | import org.openstreetmap.josm.data.coor.LatLon;
|
|---|
| 17 | import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
|
|---|
| 18 | import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
|
|---|
| 19 | import org.openstreetmap.josm.gui.layer.markerlayer.MarkerProducers;
|
|---|
| 20 | import org.xml.sax.Attributes;
|
|---|
| 21 | import org.xml.sax.SAXException;
|
|---|
| 22 |
|
|---|
| 23 | import uk.co.wilson.xml.MinML2;
|
|---|
| 24 |
|
|---|
| 25 | /**
|
|---|
| 26 | * Read raw gps data from a gpx file. Only way points with their ways segments
|
|---|
| 27 | * and waypoints are imported.
|
|---|
| 28 | * @author imi
|
|---|
| 29 | */
|
|---|
| 30 | public class RawGpsReader {
|
|---|
| 31 |
|
|---|
| 32 | /**
|
|---|
| 33 | * The relative path when constructing markers from wpt-tags. Passed to
|
|---|
| 34 | * {@link MarkerProducers#createMarker(LatLon, java.util.Map, String)}
|
|---|
| 35 | */
|
|---|
| 36 | private File relativeMarkerPath;
|
|---|
| 37 |
|
|---|
| 38 | /**
|
|---|
| 39 | * Hold the resulting gps data (tracks and their track points)
|
|---|
| 40 | */
|
|---|
| 41 | public Collection<Collection<GpsPoint>> trackData = new LinkedList<Collection<GpsPoint>>();
|
|---|
| 42 |
|
|---|
| 43 | /**
|
|---|
| 44 | * Hold the waypoints of the gps data.
|
|---|
| 45 | */
|
|---|
| 46 | public Collection<Marker> markerData = new ArrayList<Marker>();
|
|---|
| 47 |
|
|---|
| 48 | private class Parser extends MinML2 {
|
|---|
| 49 | /**
|
|---|
| 50 | * Current track to be read. The last entry is the current trkpt.
|
|---|
| 51 | * If in wpt-mode, it contain only one GpsPoint.
|
|---|
| 52 | */
|
|---|
| 53 | private Collection<GpsPoint> current = new LinkedList<GpsPoint>();
|
|---|
| 54 | private LatLon currentLatLon;
|
|---|
| 55 | private HashMap<String, String> currentTagValues = new HashMap<String, String>();
|
|---|
| 56 | private Stack<String> tags = new Stack<String>();
|
|---|
| 57 |
|
|---|
| 58 | @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
|
|---|
| 59 | if (qName.equals("wpt") || qName.equals("trkpt")) {
|
|---|
| 60 | try {
|
|---|
| 61 | double lat = Double.parseDouble(atts.getValue("lat"));
|
|---|
| 62 | double lon = Double.parseDouble(atts.getValue("lon"));
|
|---|
| 63 | if (Math.abs(lat) > 90)
|
|---|
| 64 | throw new SAXException(tr("Data error: lat value \"{0}\" is out of bounds.", lat));
|
|---|
| 65 | if (Math.abs(lon) > 180)
|
|---|
| 66 | throw new SAXException(tr("Data error: lon value \"{0}\" is out of bounds.", lon));
|
|---|
| 67 | currentLatLon = new LatLon(lat, lon);
|
|---|
| 68 | } catch (NumberFormatException e) {
|
|---|
| 69 | e.printStackTrace();
|
|---|
| 70 | throw new SAXException(e);
|
|---|
| 71 | }
|
|---|
| 72 | currentTagValues.clear();
|
|---|
| 73 | }
|
|---|
| 74 | tags.push(qName);
|
|---|
| 75 | }
|
|---|
| 76 |
|
|---|
| 77 | @Override public void characters(char[] ch, int start, int length) {
|
|---|
| 78 | String peek = tags.peek();
|
|---|
| 79 | if (peek.equals("time") || peek.equals("name") || peek.equals("link") || peek.equals("symbol")) {
|
|---|
| 80 | String tag = tags.pop();
|
|---|
| 81 | if (tags.empty() || (!tags.peek().equals("wpt") && !tags.peek().equals("trkpt"))) {
|
|---|
| 82 | tags.push(tag);
|
|---|
| 83 | return;
|
|---|
| 84 | }
|
|---|
| 85 | String contents = new String(ch, start, length);
|
|---|
| 86 | String oldContents = currentTagValues.get(peek);
|
|---|
| 87 | if (oldContents == null) {
|
|---|
| 88 | currentTagValues.put(peek, contents);
|
|---|
| 89 | } else {
|
|---|
| 90 | currentTagValues.put(peek, oldContents + contents);
|
|---|
| 91 | }
|
|---|
| 92 | tags.push(tag);
|
|---|
| 93 | }
|
|---|
| 94 | }
|
|---|
| 95 |
|
|---|
| 96 | @Override public void endElement(String namespaceURI, String localName, String qName) {
|
|---|
| 97 | if (qName.equals("trkpt")) {
|
|---|
| 98 | current.add(new GpsPoint(currentLatLon, currentTagValues.get("time")));
|
|---|
| 99 | currentTagValues.clear();
|
|---|
| 100 | } else if (qName.equals("wpt")) {
|
|---|
| 101 | Marker m = Marker.createMarker(currentLatLon, currentTagValues, relativeMarkerPath);
|
|---|
| 102 | if (m != null)
|
|---|
| 103 | markerData.add(m);
|
|---|
| 104 | currentTagValues.clear();
|
|---|
| 105 | } else if (qName.equals("trkseg") || qName.equals("trk") || qName.equals("gpx")) {
|
|---|
| 106 | newTrack();
|
|---|
| 107 | currentTagValues.clear();
|
|---|
| 108 | }
|
|---|
| 109 | tags.pop();
|
|---|
| 110 | }
|
|---|
| 111 |
|
|---|
| 112 | private void newTrack() {
|
|---|
| 113 | if (!current.isEmpty()) {
|
|---|
| 114 | trackData.add(current);
|
|---|
| 115 | current = new LinkedList<GpsPoint>();
|
|---|
| 116 | }
|
|---|
| 117 | }
|
|---|
| 118 | }
|
|---|
| 119 |
|
|---|
| 120 | /**
|
|---|
| 121 | * Parse the input stream and store the result in trackData and markerData
|
|---|
| 122 | *
|
|---|
| 123 | * @param relativeMarkerPath The directory to use as relative path for all <wpt>
|
|---|
| 124 | * marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers.
|
|---|
| 125 | */
|
|---|
| 126 | public RawGpsReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException {
|
|---|
| 127 | this.relativeMarkerPath = relativeMarkerPath;
|
|---|
| 128 | Parser parser = new Parser();
|
|---|
| 129 | parser.parse(new InputStreamReader(source, "UTF-8"));
|
|---|
| 130 | }
|
|---|
| 131 | }
|
|---|