]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - src/tim/prune/load/xml/KmlHandler.java
Moved source into separate src directory due to popular request
[GpsPrune.git] / src / tim / prune / load / xml / KmlHandler.java
diff --git a/src/tim/prune/load/xml/KmlHandler.java b/src/tim/prune/load/xml/KmlHandler.java
new file mode 100644 (file)
index 0000000..e8f2c6a
--- /dev/null
@@ -0,0 +1,302 @@
+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;
+       }
+}