]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/data/DataPoint.java
Version 5, May 2008
[GpsPrune.git] / tim / prune / data / DataPoint.java
1 package tim.prune.data;
2
3 /**
4  * Class to represent a single data point in the series
5  * including all its fields
6  * Can be either a track point or a waypoint
7  */
8 public class DataPoint
9 {
10         /** Array of Strings holding raw values */
11         private String[] _fieldValues = null;
12         /** list of field definitions */
13         private FieldList _fieldList = null;
14         /** Special fields for coordinates */
15         private Coordinate _latitude = null, _longitude = null;
16         private Altitude _altitude;
17         private Timestamp _timestamp = null;
18         private Photo _photo = null;
19         private String _waypointName = null;
20         private boolean _startOfSegment = false;
21
22
23         /**
24          * Constructor
25          * @param inValueArray array of String values
26          * @param inFieldList list of fields
27          * @param inAltFormat altitude format
28          */
29         public DataPoint(String[] inValueArray, FieldList inFieldList, int inAltFormat)
30         {
31                 // save data
32                 _fieldValues = inValueArray;
33                 // save list of fields
34                 _fieldList = inFieldList;
35                 // parse fields into objects
36                 parseFields(inAltFormat);
37         }
38
39
40         /**
41          * Parse the string values into objects eg Coordinates
42          * @param inAltFormat altitude format
43          */
44         private void parseFields(int inAltFormat)
45         {
46                 _latitude = new Latitude(getFieldValue(Field.LATITUDE));
47                 _longitude = new Longitude(getFieldValue(Field.LONGITUDE));
48                 _altitude = new Altitude(getFieldValue(Field.ALTITUDE), inAltFormat);
49                 _timestamp = new Timestamp(getFieldValue(Field.TIMESTAMP));
50                 _waypointName = getFieldValue(Field.WAYPT_NAME);
51                 String segmentStr = getFieldValue(Field.NEW_SEGMENT);
52                 if (segmentStr != null) {segmentStr = segmentStr.trim();}
53                 _startOfSegment = (segmentStr != null && (segmentStr.equals("1") || segmentStr.toUpperCase().equals("Y")));
54         }
55
56
57         /**
58          * Constructor for additional points (eg interpolated, photos)
59          * @param inLatitude latitude
60          * @param inLongitude longitude
61          * @param inAltitude altitude
62          */
63         public DataPoint(Coordinate inLatitude, Coordinate inLongitude, Altitude inAltitude)
64         {
65                 // Only these three fields are available
66                 _fieldValues = new String[3];
67                 Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE};
68                 _fieldList = new FieldList(fields);
69                 _latitude = inLatitude;
70                 _fieldValues[0] = inLatitude.output(Coordinate.FORMAT_DEG_MIN_SEC);
71                 _longitude = inLongitude;
72                 _fieldValues[1] = inLongitude.output(Coordinate.FORMAT_DEG_MIN_SEC);
73                 _altitude = inAltitude;
74                 if (inAltitude != null) {_fieldValues[2] = "" + inAltitude.getValue(Altitude.FORMAT_METRES);}
75                 _timestamp = new Timestamp(null);
76         }
77
78
79         /**
80          * Get the value for the given field
81          * @param inField field to interrogate
82          * @return value of field
83          */
84         public String getFieldValue(Field inField)
85         {
86                 return getFieldValue(_fieldList.getFieldIndex(inField));
87         }
88
89
90         /**
91          * Get the value at the given index
92          * @param inIndex index number starting at zero
93          * @return field value, or null if not found
94          */
95         public String getFieldValue(int inIndex)
96         {
97                 if (_fieldValues == null || inIndex < 0 || inIndex >= _fieldValues.length)
98                         return null;
99                 return _fieldValues[inIndex];
100         }
101
102
103         /**
104          * Set (or edit) the specified field value
105          * @param inField Field to set
106          * @param inValue value to set
107          */
108         public void setFieldValue(Field inField, String inValue)
109         {
110                 // See if this data point already has this field
111                 int fieldIndex = _fieldList.getFieldIndex(inField);
112                 // Add to field list if necessary
113                 if (fieldIndex < 0)
114                 {
115                         // If value is empty & field doesn't exist then do nothing
116                         if (inValue == null || inValue.equals(""))
117                         {
118                                 return;
119                         }
120                         // value isn't empty so extend field list
121                         fieldIndex = _fieldList.extendList(inField);
122                 }
123                 // Extend array of field values if necessary
124                 if (fieldIndex >= _fieldValues.length)
125                 {
126                         resizeValueArray(fieldIndex);
127                 }
128                 // Set field value in array
129                 _fieldValues[fieldIndex] = inValue;
130                 // Change Coordinate, Altitude, Name or Timestamp fields after edit
131                 if (_altitude != null)
132                 {
133                         parseFields(_altitude.getFormat());
134                 }
135                 else
136                 {
137                         // use default altitude format of metres
138                         parseFields(Altitude.FORMAT_METRES);
139                 }
140         }
141
142         /** @param inFlag true for start of track segment */
143         public void setSegmentStart(boolean inFlag)
144         {
145                 setFieldValue(Field.NEW_SEGMENT, inFlag?"1":null);
146         }
147
148         /** @return latitude */
149         public Coordinate getLatitude()
150         {
151                 return _latitude;
152         }
153         /** @return longitude */
154         public Coordinate getLongitude()
155         {
156                 return _longitude;
157         }
158         /** @return true if point has altitude */
159         public boolean hasAltitude()
160         {
161                 return _altitude.isValid();
162         }
163         /** @return altitude */
164         public Altitude getAltitude()
165         {
166                 return _altitude;
167         }
168         /** @return true if point has timestamp */
169         public boolean hasTimestamp()
170         {
171                 return _timestamp.isValid();
172         }
173         /** @return timestamp */
174         public Timestamp getTimestamp()
175         {
176                 return _timestamp;
177         }
178         /** @return waypoint name, if any */
179         public String getWaypointName()
180         {
181                 return _waypointName;
182         }
183
184         /** @return true if start of new track segment */
185         public boolean getSegmentStart()
186         {
187                 return _startOfSegment;
188         }
189
190         /**
191          * @return true if point has a waypoint name
192          */
193         public boolean isWaypoint()
194         {
195                 return (_waypointName != null && !_waypointName.equals(""));
196         }
197
198
199         /**
200          * Compare two DataPoint objects to see if they are duplicates
201          * @param inOther other object to compare
202          * @return true if the points are equivalent
203          */
204         public boolean isDuplicate(DataPoint inOther)
205         {
206                 if (inOther == null) return false;
207                 if (_longitude == null || _latitude == null
208                         || inOther._longitude == null || inOther._latitude == null)
209                 {
210                         return false;
211                 }
212                 // Make sure photo points aren't specified as duplicates
213                 if (_photo != null) return false;
214                 // Compare latitude and longitude
215                 if (!_longitude.equals(inOther._longitude) || !_latitude.equals(inOther._latitude))
216                 {
217                         return false;
218                 }
219                 // Note that conversion from decimal to dms can make non-identical points into duplicates
220                 // Compare waypoint name (if any)
221                 if (!isWaypoint())
222                 {
223                         return !inOther.isWaypoint();
224                 }
225                 return (inOther._waypointName != null && inOther._waypointName.equals(_waypointName));
226         }
227
228
229         /**
230          * Set the photo for this data point
231          * @param inPhoto Photo object
232          */
233         public void setPhoto(Photo inPhoto)
234         {
235                 _photo = inPhoto;
236         }
237
238
239         /**
240          * @return associated Photo object
241          */
242         public Photo getPhoto()
243         {
244                 return _photo;
245         }
246
247
248         /**
249          * @return true if the point is valid
250          */
251         public boolean isValid()
252         {
253                 return _latitude.isValid() && _longitude.isValid();
254         }
255
256
257         /**
258          * Interpolate a set of points between this one and the given one
259          * @param inEndPoint end point of interpolation
260          * @param inNumPoints number of points to generate
261          * @return the DataPoint array
262          */
263         public DataPoint[] interpolate(DataPoint inEndPoint, int inNumPoints)
264         {
265                 DataPoint[] range = new DataPoint[inNumPoints];
266                 // Loop over points
267                 for (int i=0; i<inNumPoints; i++)
268                 {
269                         Coordinate latitude = Coordinate.interpolate(_latitude, inEndPoint.getLatitude(), i, inNumPoints);
270                         Coordinate longitude = Coordinate.interpolate(_longitude, inEndPoint.getLongitude(), i, inNumPoints);
271                         Altitude altitude = Altitude.interpolate(_altitude, inEndPoint.getAltitude(), i, inNumPoints);
272                         range[i] = new DataPoint(latitude, longitude, altitude);
273                 }
274                 return range;
275         }
276
277         /**
278          * Interpolate between the two given points
279          * @param inStartPoint start point
280          * @param inEndPoint end point
281          * @param inFrac fractional distance from first point (0.0 to 1.0)
282          * @return new DataPoint object between two given ones
283          */
284         public static DataPoint interpolate(DataPoint inStartPoint, DataPoint inEndPoint, double inFrac)
285         {
286                 if (inStartPoint == null || inEndPoint == null) {return null;}
287                 return new DataPoint(
288                         Coordinate.interpolate(inStartPoint.getLatitude(), inEndPoint.getLatitude(), inFrac),
289                         Coordinate.interpolate(inStartPoint.getLongitude(), inEndPoint.getLongitude(), inFrac),
290                         Altitude.interpolate(inStartPoint.getAltitude(), inEndPoint.getAltitude(), inFrac)
291                 );
292         }
293
294         /**
295          * Calculate the number of radians between two points (for distance calculation)
296          * @param inPoint1 first point
297          * @param inPoint2 second point
298          * @return angular distance between points in radians
299          */
300         public static double calculateRadiansBetween(DataPoint inPoint1, DataPoint inPoint2)
301         {
302                 if (inPoint1 == null || inPoint2 == null)
303                         return 0.0;
304                 final double TO_RADIANS = Math.PI / 180.0;
305                 // Get lat and long from points
306                 double lat1 = inPoint1.getLatitude().getDouble() * TO_RADIANS;
307                 double lat2 = inPoint2.getLatitude().getDouble() * TO_RADIANS;
308                 double lon1 = inPoint1.getLongitude().getDouble() * TO_RADIANS;
309                 double lon2 = inPoint2.getLongitude().getDouble() * TO_RADIANS;
310                 // Formula given by Wikipedia:Great-circle_distance as follows:
311                 // angle = 2 arcsin( sqrt( (sin ((lat2-lat1)/2))^^2 + cos(lat1)cos(lat2)(sin((lon2-lon1)/2))^^2))
312                 double firstSine = Math.sin((lat2-lat1) / 2.0);
313                 double secondSine = Math.sin((lon2-lon1) / 2.0);
314                 double term2 = Math.cos(lat1) * Math.cos(lat2) * secondSine * secondSine;
315                 double answer = 2 * Math.asin(Math.sqrt(firstSine*firstSine + term2));
316                 // phew
317                 return answer;
318         }
319
320
321         /**
322          * Resize the value array
323          * @param inNewIndex new index to allow
324          */
325         private void resizeValueArray(int inNewIndex)
326         {
327                 int newSize = inNewIndex + 1;
328                 if (newSize > _fieldValues.length)
329                 {
330                         String[] newArray = new String[newSize];
331                         System.arraycopy(_fieldValues, 0, newArray, 0, _fieldValues.length);
332                         _fieldValues = newArray;
333                 }
334         }
335
336
337         /**
338          * @return a clone object with copied data
339          */
340         public DataPoint clonePoint()
341         {
342                 // Copy all values
343                 String[] valuesCopy = new String[_fieldValues.length];
344                 System.arraycopy(_fieldValues, 0, valuesCopy, 0, _fieldValues.length);
345                 // Make new object to hold cloned data
346                 DataPoint point = new DataPoint(valuesCopy, _fieldList, _altitude.getFormat());
347                 return point;
348         }
349
350
351         /**
352          * Restore the contents from another point
353          * @param inOther point containing contents to copy
354          * @return true if successful
355          */
356         public boolean restoreContents(DataPoint inOther)
357         {
358                 if (inOther != null)
359                 {
360                         // Copy string values across
361                         _fieldValues = inOther._fieldValues;
362                         if (_altitude != null)
363                         {
364                                 parseFields(_altitude.getFormat());
365                         }
366                         else
367                         {
368                                 // use default altitude format of metres
369                                 parseFields(Altitude.FORMAT_METRES);
370                         }
371                         return true;
372                 }
373                 return false;
374         }
375
376
377         /**
378          * Get string for debug
379          * @see java.lang.Object#toString()
380          */
381         public String toString()
382         {
383                 return "[Lat=" + getLatitude().toString() + ", Lon=" + getLongitude().toString() + "]";
384         }
385 }