]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/data/DataPoint.java
cd285d66790a83f263b78a44e0ef11ce1b7517cc
[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 _pointValid = 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         }
52
53
54         /**
55          * Constructor for additional points (eg interpolated, photos)
56          * @param inLatitude latitude
57          * @param inLongitude longitude
58          * @param inAltitude altitude
59          */
60         public DataPoint(Coordinate inLatitude, Coordinate inLongitude, Altitude inAltitude)
61         {
62                 // Only these three fields are available
63                 _fieldValues = new String[0];
64                 _fieldList = new FieldList();
65                 _latitude = inLatitude;
66                 _longitude = inLongitude;
67                 _altitude = inAltitude;
68                 _timestamp = new Timestamp(null);
69         }
70
71
72         /**
73          * Get the value for the given field
74          * @param inField field to interrogate
75          * @return value of field
76          */
77         public String getFieldValue(Field inField)
78         {
79                 return getFieldValue(_fieldList.getFieldIndex(inField));
80         }
81
82
83         /**
84          * Get the value at the given index
85          * @param inIndex index number starting at zero
86          * @return field value, or null if not found
87          */
88         public String getFieldValue(int inIndex)
89         {
90                 if (_fieldValues == null || inIndex < 0 || inIndex >= _fieldValues.length)
91                         return null;
92                 return _fieldValues[inIndex];
93         }
94
95
96         /**
97          * Set (or edit) the specified field value
98          * @param inField Field to set
99          * @param inValue value to set
100          */
101         public void setFieldValue(Field inField, String inValue)
102         {
103                 // See if this data point already has this field
104                 int fieldIndex = _fieldList.getFieldIndex(inField);
105                 // Add to field list if necessary
106                 if (fieldIndex < 0)
107                 {
108                         // If value is empty & field doesn't exist then do nothing
109                         if (inValue == null || inValue.equals(""))
110                         {
111                                 return;
112                         }
113                         // value isn't empty so extend field list
114                         fieldIndex = _fieldList.extendList(inField);
115                 }
116                 // Extend array of field values if necessary
117                 if (fieldIndex >= _fieldValues.length)
118                 {
119                         resizeValueArray(fieldIndex);
120                 }
121                 // Set field value in array
122                 _fieldValues[fieldIndex] = inValue;
123                 // Change Coordinate, Altitude, Name or Timestamp fields after edit
124                 if (_altitude != null)
125                 {
126                         parseFields(_altitude.getFormat());
127                 }
128                 else
129                 {
130                         // use default altitude format of metres
131                         parseFields(Altitude.FORMAT_METRES);
132                 }
133         }
134
135
136         public Coordinate getLatitude()
137         {
138                 return _latitude;
139         }
140         public Coordinate getLongitude()
141         {
142                 return _longitude;
143         }
144         public boolean hasAltitude()
145         {
146                 return _altitude.isValid();
147         }
148         public Altitude getAltitude()
149         {
150                 return _altitude;
151         }
152         public boolean hasTimestamp()
153         {
154                 return _timestamp.isValid();
155         }
156         public Timestamp getTimestamp()
157         {
158                 return _timestamp;
159         }
160         public String getWaypointName()
161         {
162                 return _waypointName;
163         }
164
165         /**
166          * @return true if point has a waypoint name
167          */
168         public boolean isWaypoint()
169         {
170                 return (_waypointName != null && !_waypointName.equals(""));
171         }
172
173
174         /**
175          * Compare two DataPoint objects to see if they are duplicates
176          * @param inOther other object to compare
177          * @return true if the points are equivalent
178          */
179         public boolean isDuplicate(DataPoint inOther)
180         {
181                 if (inOther == null) return false;
182                 if (_longitude == null || _latitude == null
183                         || inOther._longitude == null || inOther._latitude == null)
184                 {
185                         return false;
186                 }
187                 // Make sure photo points aren't specified as duplicates
188                 if (_photo != null) return false;
189                 // Compare latitude and longitude
190                 if (!_longitude.equals(inOther._longitude) || !_latitude.equals(inOther._latitude))
191                 {
192                         return false;
193                 }
194                 // Note that conversion from decimal to dms can make non-identical points into duplicates
195                 // Compare waypoint name (if any)
196                 if (!isWaypoint())
197                 {
198                         return !inOther.isWaypoint();
199                 }
200                 else
201                 {
202                         return (inOther._waypointName != null && inOther._waypointName.equals(_waypointName));
203                 }
204         }
205
206
207         /**
208          * Set the photo for this data point
209          * @param inPhoto Photo object
210          */
211         public void setPhoto(Photo inPhoto)
212         {
213                 _photo = inPhoto;
214         }
215
216
217         /**
218          * @return associated Photo object
219          */
220         public Photo getPhoto()
221         {
222                 return _photo;
223         }
224
225
226         /**
227          * @return true if the point is valid
228          */
229         public boolean isValid()
230         {
231                 return _latitude.isValid() && _longitude.isValid();
232         }
233
234
235         /**
236          * Interpolate a set of points between this one and the given one
237          * @param inEndPoint end point of interpolation
238          * @param inNumPoints number of points to generate
239          * @return the DataPoint array
240          */
241         public DataPoint[] interpolate(DataPoint inEndPoint, int inNumPoints)
242         {
243                 DataPoint[] range = new DataPoint[inNumPoints];
244                 Coordinate endLatitude = inEndPoint.getLatitude();
245                 Coordinate endLongitude = inEndPoint.getLongitude();
246                 Altitude endAltitude = inEndPoint.getAltitude();
247
248                 // Loop over points
249                 for (int i=0; i<inNumPoints; i++)
250                 {
251                         Coordinate latitude = Coordinate.interpolate(_latitude, endLatitude, i, inNumPoints);
252                         Coordinate longitude = Coordinate.interpolate(_longitude, endLongitude, i, inNumPoints);
253                         Altitude altitude = Altitude.interpolate(_altitude, endAltitude, i, inNumPoints);
254                         range[i] = new DataPoint(latitude, longitude, altitude);
255                 }
256                 return range;
257         }
258
259
260         /**
261          * Calculate the number of radians between two points (for distance calculation)
262          * @param inPoint1 first point
263          * @param inPoint2 second point
264          * @return angular distance between points in radians
265          */
266         public static double calculateRadiansBetween(DataPoint inPoint1, DataPoint inPoint2)
267         {
268                 if (inPoint1 == null || inPoint2 == null)
269                         return 0.0;
270                 final double TO_RADIANS = Math.PI / 180.0;
271                 // Get lat and long from points
272                 double lat1 = inPoint1.getLatitude().getDouble() * TO_RADIANS;
273                 double lat2 = inPoint2.getLatitude().getDouble() * TO_RADIANS;
274                 double lon1 = inPoint1.getLongitude().getDouble() * TO_RADIANS;
275                 double lon2 = inPoint2.getLongitude().getDouble() * TO_RADIANS;
276                 // Formula given by Wikipedia:Great-circle_distance as follows:
277                 // angle = 2 arcsin( sqrt( (sin ((lat2-lat1)/2))^^2 + cos(lat1)cos(lat2)(sin((lon2-lon1)/2))^^2))
278                 double firstSine = Math.sin((lat2-lat1) / 2.0);
279                 double secondSine = Math.sin((lon2-lon1) / 2.0);
280                 double term2 = Math.cos(lat1) * Math.cos(lat2) * secondSine * secondSine;
281                 double answer = 2 * Math.asin(Math.sqrt(firstSine*firstSine + term2));
282                 // phew
283                 return answer;
284         }
285
286
287         /**
288          * Resize the value array
289          * @param inNewIndex new index to allow
290          */
291         private void resizeValueArray(int inNewIndex)
292         {
293                 int newSize = inNewIndex + 1;
294                 if (newSize > _fieldValues.length)
295                 {
296                         String[] newArray = new String[newSize];
297                         System.arraycopy(_fieldValues, 0, newArray, 0, _fieldValues.length);
298                         _fieldValues = newArray;
299                 }
300         }
301
302
303         /**
304          * @return a clone object with copied data
305          */
306         public DataPoint clonePoint()
307         {
308                 // Copy all values
309                 String[] valuesCopy = new String[_fieldValues.length];
310                 System.arraycopy(_fieldValues, 0, valuesCopy, 0, _fieldValues.length);
311                 // Make new object to hold cloned data
312                 DataPoint point = new DataPoint(valuesCopy, _fieldList, _altitude.getFormat());
313                 return point;
314         }
315
316
317         /**
318          * Restore the contents from another point
319          * @param inOther point containing contents to copy
320          * @return true if successful
321          */
322         public boolean restoreContents(DataPoint inOther)
323         {
324                 if (inOther != null)
325                 {
326                         // Copy string values across
327                         _fieldValues = inOther._fieldValues;
328                         if (_altitude != null)
329                         {
330                                 parseFields(_altitude.getFormat());
331                         }
332                         else
333                         {
334                                 // use default altitude format of metres
335                                 parseFields(Altitude.FORMAT_METRES);
336                         }
337                         return true;
338                 }
339                 return false;
340         }
341 }