--- /dev/null
+package tim.prune.load.xml;
+
+import java.util.ArrayList;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import tim.prune.data.Field;
+
+
+/**
+ * Class for handling specifics of parsing Kml files
+ */
+public class KmlHandler extends XmlHandler
+{
+ private boolean _insideCoordinates = false;
+ private boolean _insideGxTrack = false;
+ private String _value = null;
+ private String _name = null, _desc = null;
+ private String _timestamp = null, _imgLink = null;
+ private StringBuffer _coordinates = null;
+ private ArrayList<String> _coordinateList = null;
+ private ArrayList<String[]> _pointList = new ArrayList<String[]>();
+ private ArrayList<String> _linkList = new ArrayList<String>();
+ // variables for gx extensions
+ private ArrayList<String> _whenList = new ArrayList<String>();
+ private ArrayList<String> _whereList = new ArrayList<String>();
+
+
+ /**
+ * Receive the start of a tag
+ * @see org.xml.sax.ContentHandler#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
+ {
+ String tagName = localName;
+ if (tagName == null || tagName.equals("")) {tagName = qName;}
+ tagName = tagName.toLowerCase();
+
+ if (tagName.equals("placemark"))
+ {
+ _coordinateList = new ArrayList<String>();
+ }
+ else if (tagName.equals("coordinates"))
+ {
+ _insideCoordinates = true;
+ _coordinates = null;
+ }
+ else if (tagName.equals("gx:track"))
+ {
+ _insideGxTrack = true;
+ }
+ _value = null;
+ super.startElement(uri, localName, qName, attributes);
+ }
+
+
+ /**
+ * Process end tag
+ * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException
+ {
+ String tagName = localName;
+ if (tagName == null || tagName.equals("")) {tagName = qName;}
+ tagName = tagName.toLowerCase();
+
+ if (tagName.equals("placemark"))
+ {
+ processPlacemark();
+ _name = _desc = _imgLink = _timestamp = null;
+ }
+ else if (tagName.equals("coordinates"))
+ {
+ _insideCoordinates = false;
+ if (_coordinates != null) _coordinateList.add(_coordinates.toString().trim());
+ }
+ else if (tagName.equals("name"))
+ {
+ _name = _value;
+ }
+ else if (tagName.equals("description"))
+ {
+ _desc = _value;
+ _imgLink = getImgLink(_desc);
+ }
+ else if (tagName.equals("when"))
+ {
+ if (!_insideGxTrack)
+ _timestamp = _value;
+ else
+ _whenList.add(_value);
+ }
+ else if (tagName.equals("gx:coord"))
+ {
+ if (_insideGxTrack) {
+ _whereList.add(_value);
+ }
+ }
+ else if (tagName.equals("gx:track"))
+ {
+ processGxTrack();
+ _insideGxTrack = false;
+ }
+ super.endElement(uri, localName, qName);
+ }
+
+
+ /**
+ * Process character text (inside tags or between them)
+ * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+ */
+ public void characters(char[] ch, int start, int length)
+ throws SAXException
+ {
+ String val = new String(ch, start, length);
+ if (_insideCoordinates)
+ {
+ if (_coordinates == null) {
+ _coordinates = new StringBuffer();
+ }
+ _coordinates.append(val);
+ }
+ else
+ {
+ // Store string in _value
+ if (_value == null) _value = val;
+ else _value = _value + val;
+ }
+ super.characters(ch, start, length);
+ }
+
+
+ /**
+ * Process a placemark entry, either a single waypoint or a whole track
+ */
+ private void processPlacemark()
+ {
+ if (_coordinateList == null || _coordinateList.isEmpty()) return;
+ final boolean isSingleSelection = (_coordinateList.size() == 1);
+ // Loop over coordinate sets in list (may have multiple <coordinates> tags within single placemark)
+ for (String coords : _coordinateList)
+ {
+ String[] coordArray = coords.split("[ \n]");
+ int numPoints = coordArray.length;
+ if (numPoints == 1)
+ {
+ // Add single point to list
+ final String name = (isSingleSelection ? _name : null);
+ _pointList.add(makeStringArray(coords, name, _desc, _timestamp));
+ _linkList.add(_imgLink);
+ }
+ else if (numPoints > 1)
+ {
+ // Add each of the unnamed track points to list
+ boolean firstPoint = true;
+ for (int p=0; p<numPoints; p++)
+ {
+ if (coordArray[p] != null && coordArray[p].trim().length()>3)
+ {
+ String[] pointArray = makeStringArray(coordArray[p], null, null, null);
+ if (firstPoint) {pointArray[5] = "1";} // start of segment flag
+ firstPoint = false;
+ _pointList.add(pointArray);
+ }
+ _linkList.add(null);
+ }
+ }
+ }
+ }
+
+ /**
+ * Process a Gx track including timestamps
+ */
+ private void processGxTrack()
+ {
+ if (!_whereList.isEmpty())
+ {
+ // If the whens don't match, then throw them all away
+ if (_whenList.size() != _whereList.size()) {System.out.println("clearing!"); _whenList.clear();}
+
+ // Add each of the unnamed track points to list
+ boolean firstPoint = true;
+ final int numPoints = _whenList.size();
+ for (int p=0; p < numPoints; p++)
+ {
+ String when = (_whenList.isEmpty() ? null : _whenList.get(p));
+ String where = _whereList.get(p);
+ if (where != null)
+ {
+ String[] coords = where.split(" ");
+ if (coords.length == 3)
+ {
+ String[] pointArray = new String[7];
+ pointArray[0] = coords[0];
+ pointArray[1] = coords[1];
+ pointArray[2] = coords[2];
+ // leave name and description empty
+ if (firstPoint) {pointArray[5] = "1";} // start of segment flag
+ firstPoint = false;
+ pointArray[6] = when; // timestamp
+ _pointList.add(pointArray);
+ }
+ }
+ _linkList.add(null);
+ }
+ }
+ _whenList.clear();
+ _whereList.clear();
+ }
+
+ /**
+ * Extract an image link from the point description
+ * @param inDesc description tag contents
+ * @return image link if found or null
+ */
+ private static String getImgLink(String inDesc)
+ {
+ if (inDesc == null || inDesc.equals("")) {return null;}
+ // Pull out <img tag from description (if any)
+ int spos = inDesc.indexOf("<img");
+ int epos = inDesc.indexOf('>', spos + 10);
+ if (spos < 0 || epos < 0) return null;
+ String imgtag = inDesc.substring(spos + 4, epos);
+ // Find the src attribute from img tag
+ int quotepos = imgtag.toLowerCase().indexOf("src=");
+ if (quotepos < 0) return null;
+ // source may be quoted with single or double quotes
+ char quotechar = imgtag.charAt(quotepos + 4);
+ int equotepos = imgtag.indexOf(quotechar, quotepos + 7);
+ if (equotepos < 0) return null;
+ return imgtag.substring(quotepos + 5, equotepos);
+ }
+
+ /**
+ * Construct the String array for the given coordinates and name
+ * @param inCoordinates coordinate string in Kml format
+ * @param inName name of waypoint, or null if track point
+ * @param inDesc description of waypoint, if any
+ * @param inDesc timestamp of waypoint, if any
+ * @return String array for point
+ */
+ private static String[] makeStringArray(String inCoordinates,
+ String inName, String inDesc, String inTimestamp)
+ {
+ String[] result = new String[7];
+ String[] values = inCoordinates.split(",");
+ final int numValues = values.length;
+ if (numValues == 3 || numValues == 2) {
+ System.arraycopy(values, 0, result, 0, numValues);
+ }
+ result[3] = inName;
+ result[4] = inDesc;
+ result[6] = inTimestamp;
+ return result;
+ }
+
+
+ /**
+ * @see tim.prune.load.xml.XmlHandler#getFieldArray()
+ */
+ public Field[] getFieldArray()
+ {
+ final Field[] fields = {Field.LONGITUDE, Field.LATITUDE, Field.ALTITUDE,
+ Field.WAYPT_NAME, Field.DESCRIPTION, Field.NEW_SEGMENT, Field.TIMESTAMP};
+ return fields;
+ }
+
+
+ /**
+ * Return the parsed information as a 2d array
+ * @see tim.prune.load.xml.XmlHandler#getDataArray()
+ */
+ public String[][] getDataArray()
+ {
+ int numPoints = _pointList.size();
+ // construct data array
+ String[][] result = new String[numPoints][];
+ for (int i=0; i<numPoints; i++) {
+ result[i] = _pointList.get(i);
+ }
+ return result;
+ }
+
+ /**
+ * @return array of links, or null if none
+ */
+ public String[] getLinkArray()
+ {
+ int numPoints = _linkList.size();
+ boolean hasLink = false;
+ String[] result = new String[numPoints];
+ for (int i=0; i<numPoints; i++)
+ {
+ result[i] = _linkList.get(i);
+ if (result[i] != null) {hasLink = true;}
+ }
+ if (!hasLink) {result = null;}
+ return result;
+ }
+}