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 _coordinateList = null; private ArrayList _pointList = new ArrayList(); private ArrayList _linkList = new ArrayList(); // variables for gx extensions private ArrayList _whenList = new ArrayList(); private ArrayList _whereList = new ArrayList(); /** * 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(); } 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 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; p3) { 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 ', 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