1 package tim.prune.data;
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
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;
25 * @param inValueArray array of String values
26 * @param inFieldList list of fields
27 * @param inAltFormat altitude format
29 public DataPoint(String[] inValueArray, FieldList inFieldList, int inAltFormat)
32 _fieldValues = inValueArray;
33 // save list of fields
34 _fieldList = inFieldList;
35 // parse fields into objects
36 parseFields(inAltFormat);
41 * Parse the string values into objects eg Coordinates
42 * @param inAltFormat altitude format
44 private void parseFields(int inAltFormat)
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);
55 * Constructor for additional points (eg interpolated, photos)
56 * @param inLatitude latitude
57 * @param inLongitude longitude
58 * @param inAltitude altitude
60 public DataPoint(Coordinate inLatitude, Coordinate inLongitude, Altitude inAltitude)
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);
73 * Get the value for the given field
74 * @param inField field to interrogate
75 * @return value of field
77 public String getFieldValue(Field inField)
79 return getFieldValue(_fieldList.getFieldIndex(inField));
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
88 public String getFieldValue(int inIndex)
90 if (_fieldValues == null || inIndex < 0 || inIndex >= _fieldValues.length)
92 return _fieldValues[inIndex];
97 * Set (or edit) the specified field value
98 * @param inField Field to set
99 * @param inValue value to set
101 public void setFieldValue(Field inField, String inValue)
103 // See if this data point already has this field
104 int fieldIndex = _fieldList.getFieldIndex(inField);
105 // Add to field list if necessary
108 // If value is empty & field doesn't exist then do nothing
109 if (inValue == null || inValue.equals(""))
113 // value isn't empty so extend field list
114 fieldIndex = _fieldList.extendList(inField);
116 // Extend array of field values if necessary
117 if (fieldIndex >= _fieldValues.length)
119 resizeValueArray(fieldIndex);
121 // Set field value in array
122 _fieldValues[fieldIndex] = inValue;
123 // Change Coordinate, Altitude, Name or Timestamp fields after edit
124 if (_altitude != null)
126 parseFields(_altitude.getFormat());
130 // use default altitude format of metres
131 parseFields(Altitude.FORMAT_METRES);
136 public Coordinate getLatitude()
140 public Coordinate getLongitude()
144 public boolean hasAltitude()
146 return _altitude.isValid();
148 public Altitude getAltitude()
152 public boolean hasTimestamp()
154 return _timestamp.isValid();
156 public Timestamp getTimestamp()
160 public String getWaypointName()
162 return _waypointName;
166 * @return true if point has a waypoint name
168 public boolean isWaypoint()
170 return (_waypointName != null && !_waypointName.equals(""));
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
179 public boolean isDuplicate(DataPoint inOther)
181 if (inOther == null) return false;
182 if (_longitude == null || _latitude == null
183 || inOther._longitude == null || inOther._latitude == null)
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))
194 // Note that conversion from decimal to dms can make non-identical points into duplicates
195 // Compare waypoint name (if any)
198 return !inOther.isWaypoint();
202 return (inOther._waypointName != null && inOther._waypointName.equals(_waypointName));
208 * Set the photo for this data point
209 * @param inPhoto Photo object
211 public void setPhoto(Photo inPhoto)
218 * @return associated Photo object
220 public Photo getPhoto()
227 * @return true if the point is valid
229 public boolean isValid()
231 return _latitude.isValid() && _longitude.isValid();
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
241 public DataPoint[] interpolate(DataPoint inEndPoint, int inNumPoints)
243 DataPoint[] range = new DataPoint[inNumPoints];
244 Coordinate endLatitude = inEndPoint.getLatitude();
245 Coordinate endLongitude = inEndPoint.getLongitude();
246 Altitude endAltitude = inEndPoint.getAltitude();
249 for (int i=0; i<inNumPoints; i++)
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);
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
266 public static double calculateRadiansBetween(DataPoint inPoint1, DataPoint inPoint2)
268 if (inPoint1 == null || inPoint2 == null)
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));
288 * Resize the value array
289 * @param inNewIndex new index to allow
291 private void resizeValueArray(int inNewIndex)
293 int newSize = inNewIndex + 1;
294 if (newSize > _fieldValues.length)
296 String[] newArray = new String[newSize];
297 System.arraycopy(_fieldValues, 0, newArray, 0, _fieldValues.length);
298 _fieldValues = newArray;
304 * @return a clone object with copied data
306 public DataPoint clonePoint()
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());
318 * Restore the contents from another point
319 * @param inOther point containing contents to copy
320 * @return true if successful
322 public boolean restoreContents(DataPoint inOther)
326 // Copy string values across
327 _fieldValues = inOther._fieldValues;
328 if (_altitude != null)
330 parseFields(_altitude.getFormat());
334 // use default altitude format of metres
335 parseFields(Altitude.FORMAT_METRES);