Nasa's Space Shuttle Atlantis Transiting the SunCreamy Swiss-style (Vegan) Chicken and Mushroom Pepper StewGarmin Edge 205 GPS Cycle Training ToolA Simple 2D Terrain ModelSpooky Case of the Wem GhostTank Wars in G3D
Guest6:17 pm, Sunday, 15 February 09 |
|
this sounds great, can you put an example code for me? thx! | |
compton10:28 pm, Tuesday, 17 February 09 |
|
Here is the gpxParser class which parses the source XML using an event-driven SAX model. To implement a SAX parser like this, you create a class which extends org.xml.sax.helpers.DefaultHandler, and in this class you override functions of DefaultHandler in order to handle various events which are fired as the XML document is read element-by-element: import java.io.*;
import java.text.ParseException;
import java.util.ArrayList;
import org.xml.sax.*;
public class gpxParser extends org.xml.sax.helpers.DefaultHandler {
private stringStack elementNames;
private float minLat, maxLat, minLon, maxLon;
private int totalPoints;
private long totalSeconds;
private ArrayList<journeyPoint> plots = new ArrayList<journeyPoint>(50);
private StringBuilder contentBuffer;
private double currentDistance;
public gpxParser() {
clear();
}
public void clear() {
totalPoints = 0;
currentDistance = 0;
totalSeconds = 0;
elementNames = new stringStack();
plots.clear();
contentBuffer = new StringBuilder();
}
/*
* READ GPX DATA FILE
*/
public int read(String filename) {
clear();
try {
FileInputStream in = new FileInputStream(new File(filename));
InputSource source = new InputSource(in);
XMLReader parser = org.xml.sax.helpers.XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
parser.setContentHandler(this);
parser.parse(source);
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 0;
}
public void load(ArrayList<journeyPoint> arrayList, journeyPoint topLeft, journeyPoint bottomRight) {
clear();
plots.addAll(arrayList);
totalPoints = plots.size();
}
/*
* DefaultHandler::startElement() fires whenever an XML start tag is encountered
* @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
*/
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// the <bounds> element has attributes which specify min & max latitude and longitude
if (localName.compareToIgnoreCase("bounds") == 0) {
minLat = new Float(attributes.getValue("minlat")).floatValue();
maxLat = new Float(attributes.getValue("maxlat")).floatValue();
minLon = new Float(attributes.getValue("minlon")).floatValue();
maxLon = new Float(attributes.getValue("maxlon")).floatValue();
} else {
// the <trkpt> element has attributes which specify latitude and longitude (it has child elements that specify the time and elevation)
if (localName.compareToIgnoreCase("trkpt") == 0) {
totalPoints++;
plots.add(new journeyPoint(Double.parseDouble(attributes.getValue("lon")), Double.parseDouble(attributes.getValue("lat"))));
}
}
// Clear content buffer
contentBuffer.delete(0, contentBuffer.length());
// Store name of current element in stack
elementNames.push(qName);
}
/*
* the DefaultHandler::characters() function fires 1 or more times for each text node encountered
*
*/
public void characters(char[] ch, int start, int length) throws SAXException {
contentBuffer.append(String.copyValueOf(ch, start, length));
}
/*
* the DefaultHandler::endElement() function fires for each end tag
*
*/
public void endElement(String uri, String localName, String qName) throws SAXException {
String currentElement = elementNames.pop();
if (totalPoints > 0 && currentElement != null) {
if (currentElement.compareToIgnoreCase("ele") == 0) {
plots.get(totalPoints-1).setElevation(Float.parseFloat(contentBuffer.toString()));
} else {
if (currentElement.compareToIgnoreCase("time") == 0) {
try {
plots.get(totalPoints-1).setTime(contentBuffer.toString());
} catch (ParseException e) {
System.out.println("Bad date: " + contentBuffer.toString());
}
} else if (currentElement.compareToIgnoreCase("trkpt") == 0) {
if (totalPoints > 1) {
currentDistance += plots.get(totalPoints-1).metresTo(plots.get(totalPoints-2));
plots.get(totalPoints-1).setDistance(currentDistance);
totalSeconds += plots.get(totalPoints-1).secondsTo(plots.get(totalPoints-2));
plots.get(totalPoints-1).setDuration(totalSeconds);
}
}
}
}
}
public int getTotalPoints() {
return totalPoints;
}
public ArrayList<journeyPoint> getPlots() {
return plots;
}
public journeyPoint getTopLeft() {
return new journeyPoint(minLon, minLat);
}
public journeyPoint getBottomRight() {
return new journeyPoint(maxLon, maxLat);
}
}
The SAX model is effectively a stateless XML parser. The XML document is read one time, and is not stored in memory. If we want to know where we are in the document tree, we need to record our position ourselves. This is what makes it so lightweight and fast, however it also means it isn't always intuitive to work with. Take a look at the GPS files which we're parsing here. They record track data as a series of <trkpt> elements:<trkpt lat="52.446738677" lon="-1.880564885">
<ele>155.390259</ele>
<time>2008-12-15T15:56:02Z</time>
</trkpt>
Each of these elements records a GPS position using attributes specifying the latitude and longitude, and two child elements, <ele> and <time>, which each contain a string (in XML parlance a "child text node") representing the data value recorded at that point. The <ele> element records the elevation in metres, and the other is obvious.So in order for us to record all the data for a point, we first need to read the attributes of the <trkpt> element, and then the text data of its two child elements. Each of these requires a different approach in our Java code. Reading attributes is straightforward - we can do it in the startElement() function which SAX fires each time the start of an XML element is encountered. Reading the two text nodes is not so simple. Text nodes can be handled by overriding the characters() method, however a single text node may call this multiple times, once for each chunk of text. This means that we won't know that we have read all the text until we hit the end of the XML element - which we deal with in the endElement() method. So our characters() method just has to concatenate all the chunks of text, and the endElement() method will then process that text. I use a simple stack to record the element we're currently reading, and the points of the GPX route are saved as an ArrayList of journeyPoint instances. This ArrayList forms the internal model that our Java application uses to represent a recorded route. Once the gpsParser class has built it, it is passed to the chartPanel class for display. |
|
Juan8:02 pm, Wednesday, 23 December 09 |
|
Hi, I wonder if you could show me the code of the journeyPoint class...that would be very useful...Thanxs a lot! | |
compton2:35 pm, Monday, 28 December 09 |
|
Hi Juan, I've uploaded all the code here. Give me a shout if you have any problems getting it to run OK. | |
Quique6:19 pm, Tuesday, 6 April 10 |
|
Hi, I want to use your gpx parser (probably modified) in a personal project. Can I do that? What license or conditions will apply? Thanks. |
|
compton1:01 am, Thursday, 8 April 10 |
|
Hi Quique, please feel free to make use of the code here. I haven't done any work on it for a while, so I'm glad to hear it could be useful to someone. No restrictions, but I'd be interested to hear how it goes! |
|
Guest4:58 pm, Wednesday, 21 September 11 |
|
To read GPX files easily in Java see: http://sourceforge.net/p/gpsanalysis/home/Home | |
Leave a comment |