]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/load/xml/KmlHandler.java
Version 19.2, December 2018
[GpsPrune.git] / src / tim / prune / load / xml / KmlHandler.java
1 package tim.prune.load.xml;
2
3 import java.util.ArrayList;
4 import org.xml.sax.Attributes;
5 import org.xml.sax.SAXException;
6
7 import tim.prune.data.Field;
8
9
10 /**
11  * Class for handling specifics of parsing Kml files
12  */
13 public class KmlHandler extends XmlHandler
14 {
15         private boolean _insideCoordinates = false;
16         private boolean _insideGxTrack = false;
17         private String _value = null;
18         private String _name = null, _desc = null;
19         private String _timestamp = null, _imgLink = null;
20         private StringBuffer _coordinates = null;
21         private ArrayList<String> _coordinateList = null;
22         private ArrayList<String[]> _pointList = new ArrayList<String[]>();
23         private ArrayList<String> _linkList = new ArrayList<String>();
24         // variables for gx extensions
25         private ArrayList<String> _whenList = new ArrayList<String>();
26         private ArrayList<String> _whereList = new ArrayList<String>();
27
28
29         /**
30          * Receive the start of a tag
31          * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
32          */
33         public void startElement(String uri, String localName, String qName,
34                 Attributes attributes) throws SAXException
35         {
36                 String tagName = localName;
37                 if (tagName == null || tagName.equals("")) {tagName = qName;}
38                 tagName = tagName.toLowerCase();
39
40                 if (tagName.equals("placemark"))
41                 {
42                         _coordinateList = new ArrayList<String>();
43                 }
44                 else if (tagName.equals("coordinates"))
45                 {
46                         _insideCoordinates = true;
47                         _coordinates = null;
48                 }
49                 else if (tagName.equals("gx:track"))
50                 {
51                         _insideGxTrack = true;
52                 }
53                 _value = null;
54                 super.startElement(uri, localName, qName, attributes);
55         }
56
57
58         /**
59          * Process end tag
60          * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
61          */
62         public void endElement(String uri, String localName, String qName)
63         throws SAXException
64         {
65                 String tagName = localName;
66                 if (tagName == null || tagName.equals("")) {tagName = qName;}
67                 tagName = tagName.toLowerCase();
68
69                 if (tagName.equals("placemark"))
70                 {
71                         processPlacemark();
72                         _name = _desc = _imgLink = _timestamp = null;
73                 }
74                 else if (tagName.equals("coordinates"))
75                 {
76                         _insideCoordinates = false;
77                         if (_coordinates != null) _coordinateList.add(_coordinates.toString().trim());
78                 }
79                 else if (tagName.equals("name"))
80                 {
81                         _name = _value;
82                 }
83                 else if (tagName.equals("description"))
84                 {
85                         _desc = _value;
86                         _imgLink = getImgLink(_desc);
87                 }
88                 else if (tagName.equals("when"))
89                 {
90                         if (!_insideGxTrack)
91                                 _timestamp = _value;
92                         else
93                                 _whenList.add(_value);
94                 }
95                 else if (tagName.equals("gx:coord"))
96                 {
97                         if (_insideGxTrack) {
98                                 _whereList.add(_value);
99                         }
100                 }
101                 else if (tagName.equals("gx:track"))
102                 {
103                         processGxTrack();
104                         _insideGxTrack = false;
105                 }
106                 super.endElement(uri, localName, qName);
107         }
108
109
110         /**
111          * Process character text (inside tags or between them)
112          * @see org.xml.sax.ContentHandler#characters(char[], int, int)
113          */
114         public void characters(char[] ch, int start, int length)
115                         throws SAXException
116         {
117                 String val = new String(ch, start, length);
118                 if (_insideCoordinates)
119                 {
120                         if (_coordinates == null) {
121                                 _coordinates = new StringBuffer();
122                         }
123                         _coordinates.append(val);
124                 }
125                 else
126                 {
127                         // Store string in _value
128                         if (_value == null) _value = val;
129                         else _value = _value + val;
130                 }
131                 super.characters(ch, start, length);
132         }
133
134
135         /**
136          * Process a placemark entry, either a single waypoint or a whole track
137          */
138         private void processPlacemark()
139         {
140                 if (_coordinateList == null || _coordinateList.isEmpty()) return;
141                 final boolean isSingleSelection = (_coordinateList.size() == 1);
142                 // Loop over coordinate sets in list (may have multiple <coordinates> tags within single placemark)
143                 for (String coords : _coordinateList)
144                 {
145                         String[] coordArray = coords.split("[ \n]");
146                         int numPoints = coordArray.length;
147                         if (numPoints == 1)
148                         {
149                                 // Add single point to list
150                                 final String name = (isSingleSelection ? _name : null);
151                                 _pointList.add(makeStringArray(coords, name, _desc, _timestamp));
152                                 _linkList.add(_imgLink);
153                         }
154                         else if (numPoints > 1)
155                         {
156                                 // Add each of the unnamed track points to list
157                                 boolean firstPoint = true;
158                                 for (int p=0; p<numPoints; p++)
159                                 {
160                                         if (coordArray[p] != null && coordArray[p].trim().length()>3)
161                                         {
162                                                 String[] pointArray = makeStringArray(coordArray[p], null, null, null);
163                                                 if (firstPoint) {pointArray[5] = "1";} // start of segment flag
164                                                 firstPoint = false;
165                                                 _pointList.add(pointArray);
166                                         }
167                                         _linkList.add(null);
168                                 }
169                         }
170                 }
171         }
172
173         /**
174          * Process a Gx track including timestamps
175          */
176         private void processGxTrack()
177         {
178                 if (!_whereList.isEmpty())
179                 {
180                         // If the whens don't match, then throw them all away
181                         if (_whenList.size() != _whereList.size()) {System.out.println("clearing!"); _whenList.clear();}
182
183                         // Add each of the unnamed track points to list
184                         boolean firstPoint = true;
185                         final int numPoints = _whenList.size();
186                         for (int p=0; p < numPoints; p++)
187                         {
188                                 String when  = (_whenList.isEmpty() ? null : _whenList.get(p));
189                                 String where = _whereList.get(p);
190                                 if (where != null)
191                                 {
192                                         String[] coords = where.split(" ");
193                                         if (coords.length == 3)
194                                         {
195                                                 String[] pointArray = new String[7];
196                                                 pointArray[0] = coords[0];
197                                                 pointArray[1] = coords[1];
198                                                 pointArray[2] = coords[2];
199                                                 // leave name and description empty
200                                                 if (firstPoint) {pointArray[5] = "1";} // start of segment flag
201                                                 firstPoint = false;
202                                                 pointArray[6] = when; // timestamp
203                                                 _pointList.add(pointArray);
204                                         }
205                                 }
206                                 _linkList.add(null);
207                         }
208                 }
209                 _whenList.clear();
210                 _whereList.clear();
211         }
212
213         /**
214          * Extract an image link from the point description
215          * @param inDesc description tag contents
216          * @return image link if found or null
217          */
218         private static String getImgLink(String inDesc)
219         {
220                 if (inDesc == null || inDesc.equals("")) {return null;}
221                 // Pull out <img tag from description (if any)
222                 int spos = inDesc.indexOf("<img");
223                 int epos = inDesc.indexOf('>', spos + 10);
224                 if (spos < 0 || epos < 0) return null;
225                 String imgtag = inDesc.substring(spos + 4, epos);
226                 // Find the src attribute from img tag
227                 int quotepos = imgtag.toLowerCase().indexOf("src=");
228                 if (quotepos < 0) return null;
229                 // source may be quoted with single or double quotes
230                 char quotechar = imgtag.charAt(quotepos + 4);
231                 int equotepos = imgtag.indexOf(quotechar, quotepos + 7);
232                 if (equotepos < 0) return null;
233                 return imgtag.substring(quotepos + 5, equotepos);
234         }
235
236         /**
237          * Construct the String array for the given coordinates and name
238          * @param inCoordinates coordinate string in Kml format
239          * @param inName name of waypoint, or null if track point
240          * @param inDesc description of waypoint, if any
241          * @param inDesc timestamp of waypoint, if any
242          * @return String array for point
243          */
244         private static String[] makeStringArray(String inCoordinates,
245                 String inName, String inDesc, String inTimestamp)
246         {
247                 String[] result = new String[7];
248                 String[] values = inCoordinates.split(",");
249                 final int numValues = values.length;
250                 if (numValues == 3 || numValues == 2) {
251                         System.arraycopy(values, 0, result, 0, numValues);
252                 }
253                 result[3] = inName;
254                 result[4] = inDesc;
255                 result[6] = inTimestamp;
256                 return result;
257         }
258
259
260         /**
261          * @see tim.prune.load.xml.XmlHandler#getFieldArray()
262          */
263         public Field[] getFieldArray()
264         {
265                 final Field[] fields = {Field.LONGITUDE, Field.LATITUDE, Field.ALTITUDE,
266                         Field.WAYPT_NAME, Field.DESCRIPTION, Field.NEW_SEGMENT, Field.TIMESTAMP};
267                 return fields;
268         }
269
270
271         /**
272          * Return the parsed information as a 2d array
273          * @see tim.prune.load.xml.XmlHandler#getDataArray()
274          */
275         public String[][] getDataArray()
276         {
277                 int numPoints = _pointList.size();
278                 // construct data array
279                 String[][] result = new String[numPoints][];
280                 for (int i=0; i<numPoints; i++) {
281                         result[i] = _pointList.get(i);
282                 }
283                 return result;
284         }
285
286         /**
287          * @return array of links, or null if none
288          */
289         public String[] getLinkArray()
290         {
291                 int numPoints = _linkList.size();
292                 boolean hasLink = false;
293                 String[] result = new String[numPoints];
294                 for (int i=0; i<numPoints; i++)
295                 {
296                         result[i] = _linkList.get(i);
297                         if (result[i] != null) {hasLink = true;}
298                 }
299                 if (!hasLink) {result = null;}
300                 return result;
301         }
302 }