]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/data/DataPoint.java
Version 14, October 2012
[GpsPrune.git] / tim / prune / data / DataPoint.java
index 5d66ec55783a596d7bef1f1cd671dbe75a8c5b13..34d7bd72055275f81339e1fe23c434d2008603c6 100644 (file)
@@ -1,5 +1,7 @@
 package tim.prune.data;
 
+import tim.prune.config.Config;
+
 /**
  * Class to represent a single data point in the series
  * including all its fields
@@ -7,18 +9,22 @@ package tim.prune.data;
  */
 public class DataPoint
 {
-       // Hold these as Strings?  Or FieldValue objects?
+       /** Array of Strings holding raw values */
        private String[] _fieldValues = null;
-       // list of fields
+       /** list of field definitions */
        private FieldList _fieldList = null;
-       // Special fields for coordinates
+       /** Special fields for coordinates */
        private Coordinate _latitude = null, _longitude = null;
        private Altitude _altitude;
        private Timestamp _timestamp = null;
-       private boolean _pointValid = false;
-
-
-       // TODO: Make it possible to turn track point into waypoint - may need to alter FieldList
+       /** Attached photo */
+       private Photo _photo = null;
+       /** Attached audio clip */
+       private AudioClip _audio = null;
+       private String _waypointName = null;
+       private boolean _startOfSegment = false;
+       private boolean _markedForDeletion = false;
+       private int _modifyCount = 0;
 
        /**
         * Constructor
@@ -26,35 +32,73 @@ public class DataPoint
         * @param inFieldList list of fields
         * @param inAltFormat altitude format
         */
-       public DataPoint(String[] inValueArray, FieldList inFieldList, int inAltFormat)
+       public DataPoint(String[] inValueArray, FieldList inFieldList, Altitude.Format inAltFormat)
        {
                // save data
                _fieldValues = inValueArray;
                // save list of fields
                _fieldList = inFieldList;
+               // Remove double quotes around values
+               removeQuotes(_fieldValues);
+               // parse fields into objects
+               parseFields(null, inAltFormat);
+       }
+
 
-               // parse fields
-               _latitude = new Latitude(getFieldValue(Field.LATITUDE));
-               _longitude = new Longitude(getFieldValue(Field.LONGITUDE));
-               _altitude = new Altitude(getFieldValue(Field.ALTITUDE), inAltFormat);
-               _timestamp = new Timestamp(getFieldValue(Field.TIMESTAMP));
+       /**
+        * Parse the string values into objects eg Coordinates
+        * @param inField field which has changed, or null for all
+        * @param inAltFormat altitude format
+        */
+       private void parseFields(Field inField, Altitude.Format inAltFormat)
+       {
+               if (inField == null || inField == Field.LATITUDE) {
+                       _latitude = new Latitude(getFieldValue(Field.LATITUDE));
+               }
+               if (inField == null || inField == Field.LONGITUDE) {
+                       _longitude = new Longitude(getFieldValue(Field.LONGITUDE));
+               }
+               if (inField == null || inField == Field.ALTITUDE) {
+                       _altitude = new Altitude(getFieldValue(Field.ALTITUDE), inAltFormat);
+               }
+               if (inField == null || inField == Field.TIMESTAMP) {
+                       _timestamp = new Timestamp(getFieldValue(Field.TIMESTAMP));
+               }
+               if (inField == null || inField == Field.WAYPT_NAME) {
+                       _waypointName = getFieldValue(Field.WAYPT_NAME);
+               }
+               if (inField == null || inField == Field.NEW_SEGMENT)
+               {
+                       String segmentStr = getFieldValue(Field.NEW_SEGMENT);
+                       if (segmentStr != null) {segmentStr = segmentStr.trim();}
+                       _startOfSegment = (segmentStr != null && (segmentStr.equals("1") || segmentStr.toUpperCase().equals("Y")));
+               }
        }
 
 
        /**
-        * Private constructor for artificially generated points (eg interpolated)
+        * Constructor for additional points (eg interpolated, photos)
         * @param inLatitude latitude
         * @param inLongitude longitude
         * @param inAltitude altitude
         */
-       private DataPoint(Coordinate inLatitude, Coordinate inLongitude, Altitude inAltitude)
+       public DataPoint(Coordinate inLatitude, Coordinate inLongitude, Altitude inAltitude)
        {
                // Only these three fields are available
-               _fieldValues = new String[0];
-               _fieldList = new FieldList();
+               _fieldValues = new String[3];
+               Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE};
+               _fieldList = new FieldList(fields);
                _latitude = inLatitude;
+               _fieldValues[0] = inLatitude.output(Coordinate.FORMAT_NONE);
                _longitude = inLongitude;
-               _altitude = inAltitude;
+               _fieldValues[1] = inLongitude.output(Coordinate.FORMAT_NONE);
+               if (inAltitude == null) {
+                       _altitude = Altitude.NONE;
+               }
+               else {
+                       _altitude = inAltitude;
+                       _fieldValues[2] = "" + inAltitude.getValue();
+               }
                _timestamp = new Timestamp(null);
        }
 
@@ -83,43 +127,151 @@ public class DataPoint
        }
 
 
+       /**
+        * Set (or edit) the specified field value
+        * @param inField Field to set
+        * @param inValue value to set
+        * @param inUndo true if undo operation, false otherwise
+        */
+       public void setFieldValue(Field inField, String inValue, boolean inUndo)
+       {
+               // See if this data point already has this field
+               int fieldIndex = _fieldList.getFieldIndex(inField);
+               // Add to field list if necessary
+               if (fieldIndex < 0)
+               {
+                       // If value is empty & field doesn't exist then do nothing
+                       if (inValue == null || inValue.equals(""))
+                       {
+                               return;
+                       }
+                       // value isn't empty so extend field list
+                       fieldIndex = _fieldList.extendList(inField);
+               }
+               // Extend array of field values if necessary
+               if (fieldIndex >= _fieldValues.length)
+               {
+                       resizeValueArray(fieldIndex);
+               }
+               // Set field value in array
+               _fieldValues[fieldIndex] = inValue;
+               // Increment edit count on all field edits except segment
+               if (inField != Field.NEW_SEGMENT) {
+                       setModified(inUndo);
+               }
+               // Change Coordinate, Altitude, Name or Timestamp fields after edit
+               if (_altitude != null && _altitude.getFormat() != Altitude.Format.NO_FORMAT) {
+                       // Altitude already present so reuse format
+                       parseFields(inField, _altitude.getFormat());
+               }
+               else {
+                       // use default altitude format from config
+                       parseFields(inField, Config.getUnitSet().getDefaultAltitudeFormat());
+               }
+       }
+
+       /**
+        * Either increment or decrement the modify count, depending on whether it's an undo or not
+        * @param inUndo true for undo, false otherwise
+        */
+       public void setModified(boolean inUndo)
+       {
+               if (!inUndo) {
+                       _modifyCount++;
+               }
+               else {
+                       _modifyCount--;
+               }
+       }
+
+       /**
+        * @return field list for this point
+        */
+       public FieldList getFieldList()
+       {
+               return _fieldList;
+       }
+
+       /** @param inFlag true for start of track segment */
+       public void setSegmentStart(boolean inFlag)
+       {
+               setFieldValue(Field.NEW_SEGMENT, inFlag?"1":null, false);
+       }
+
+       /**
+        * Mark the point for deletion
+        * @param inFlag true to delete, false to keep
+        */
+       public void setMarkedForDeletion(boolean inFlag) {
+               _markedForDeletion = inFlag;
+       }
+
+       /** @return latitude */
        public Coordinate getLatitude()
        {
                return _latitude;
        }
+       /** @return longitude */
        public Coordinate getLongitude()
        {
                return _longitude;
        }
+       /** @return true if point has altitude */
        public boolean hasAltitude()
        {
                return _altitude.isValid();
        }
+       /** @return altitude */
        public Altitude getAltitude()
        {
                return _altitude;
        }
+       /** @return true if point has timestamp */
        public boolean hasTimestamp()
        {
                return _timestamp.isValid();
        }
+       /** @return timestamp */
        public Timestamp getTimestamp()
        {
                return _timestamp;
        }
+       /** @return waypoint name, if any */
+       public String getWaypointName()
+       {
+               return _waypointName;
+       }
+
+       /** @return true if start of new track segment */
+       public boolean getSegmentStart()
+       {
+               return _startOfSegment;
+       }
+
+       /** @return true if point marked for deletion */
+       public boolean getDeleteFlag()
+       {
+               return _markedForDeletion;
+       }
 
        /**
         * @return true if point has a waypoint name
         */
        public boolean isWaypoint()
        {
-               String name = getFieldValue(Field.WAYPT_NAME);
-               return (name != null && !name.equals(""));
+               return (_waypointName != null && !_waypointName.equals(""));
        }
 
        /**
-        * Compare two DataPoint objects to see if they
-        * are duplicates
+        * @return true if point has been modified since loading
+        */
+       public boolean isModified()
+       {
+               return _modifyCount > 0;
+       }
+
+       /**
+        * Compare two DataPoint objects to see if they are duplicates
         * @param inOther other object to compare
         * @return true if the points are equivalent
         */
@@ -131,26 +283,73 @@ public class DataPoint
                {
                        return false;
                }
+               // Make sure photo points aren't specified as duplicates
+               if (_photo != null) return false;
                // Compare latitude and longitude
                if (!_longitude.equals(inOther._longitude) || !_latitude.equals(inOther._latitude))
                {
                        return false;
                }
                // Note that conversion from decimal to dms can make non-identical points into duplicates
-               // Compare description (if any)
-               String name1 = getFieldValue(Field.WAYPT_NAME);
-               String name2 = inOther.getFieldValue(Field.WAYPT_NAME);
-               if (name1 == null || name1.equals(""))
-               {
-                       return (name2 == null || name2.equals(""));
-               }
-               else
+               // Compare waypoint name (if any)
+               if (!isWaypoint())
                {
-                       return (name2 != null && name2.equals(name1));
+                       return !inOther.isWaypoint();
                }
+               return (inOther._waypointName != null && inOther._waypointName.equals(_waypointName));
        }
 
 
+       /**
+        * Set the photo for this data point
+        * @param inPhoto Photo object
+        */
+       public void setPhoto(Photo inPhoto) {
+               _photo = inPhoto;
+               _modifyCount++;
+       }
+
+       /**
+        * @return associated Photo object
+        */
+       public Photo getPhoto() {
+               return _photo;
+       }
+
+       /**
+        * Set the audio clip for this point
+        * @param inAudio audio object
+        */
+       public void setAudio(AudioClip inAudio) {
+               _audio = inAudio;
+               _modifyCount++;
+       }
+
+       /**
+        * @return associated audio object
+        */
+       public AudioClip getAudio() {
+               return _audio;
+       }
+
+       /**
+        * Attach the given media object according to type
+        * @param inMedia either a photo or an audio clip
+        */
+       public void attachMedia(MediaObject inMedia)
+       {
+               if (inMedia != null) {
+                       if (inMedia instanceof Photo) {
+                               setPhoto((Photo) inMedia);
+                               inMedia.setDataPoint(this);
+                       }
+                       else if (inMedia instanceof AudioClip) {
+                               setAudio((AudioClip) inMedia);
+                               inMedia.setDataPoint(this);
+                       }
+               }
+       }
+
        /**
         * @return true if the point is valid
         */
@@ -159,6 +358,31 @@ public class DataPoint
                return _latitude.isValid() && _longitude.isValid();
        }
 
+       /**
+        * @return true if the point has either a photo or audio attached
+        */
+       public boolean hasMedia() {
+               return _photo != null || _audio != null;
+       }
+
+       /**
+        * @return name of attached photo and/or audio
+        */
+       public String getMediaName()
+       {
+               String mediaName = null;
+               if (_photo != null) mediaName = _photo.getName();
+               if (_audio != null)
+               {
+                       if (mediaName == null) {
+                               mediaName = _audio.getName();
+                       }
+                       else {
+                               mediaName = mediaName + ", " + _audio.getName();
+                       }
+               }
+               return mediaName;
+       }
 
        /**
         * Interpolate a set of points between this one and the given one
@@ -169,21 +393,33 @@ public class DataPoint
        public DataPoint[] interpolate(DataPoint inEndPoint, int inNumPoints)
        {
                DataPoint[] range = new DataPoint[inNumPoints];
-               Coordinate endLatitude = inEndPoint.getLatitude();
-               Coordinate endLongitude = inEndPoint.getLongitude();
-               Altitude endAltitude = inEndPoint.getAltitude();
-
                // Loop over points
                for (int i=0; i<inNumPoints; i++)
                {
-                       Coordinate latitude = Coordinate.interpolate(_latitude, endLatitude, i, inNumPoints);
-                       Coordinate longitude = Coordinate.interpolate(_longitude, endLongitude, i, inNumPoints);
-                       Altitude altitude = Altitude.interpolate(_altitude, endAltitude, i, inNumPoints);
+                       Coordinate latitude = Coordinate.interpolate(_latitude, inEndPoint.getLatitude(), i, inNumPoints);
+                       Coordinate longitude = Coordinate.interpolate(_longitude, inEndPoint.getLongitude(), i, inNumPoints);
+                       Altitude altitude = Altitude.interpolate(_altitude, inEndPoint.getAltitude(), i, inNumPoints);
                        range[i] = new DataPoint(latitude, longitude, altitude);
                }
                return range;
        }
 
+       /**
+        * Interpolate between the two given points
+        * @param inStartPoint start point
+        * @param inEndPoint end point
+        * @param inFrac fractional distance from first point (0.0 to 1.0)
+        * @return new DataPoint object between two given ones
+        */
+       public static DataPoint interpolate(DataPoint inStartPoint, DataPoint inEndPoint, double inFrac)
+       {
+               if (inStartPoint == null || inEndPoint == null) {return null;}
+               return new DataPoint(
+                       Coordinate.interpolate(inStartPoint.getLatitude(), inEndPoint.getLatitude(), inFrac),
+                       Coordinate.interpolate(inStartPoint.getLongitude(), inEndPoint.getLongitude(), inFrac),
+                       Altitude.interpolate(inStartPoint.getAltitude(), inEndPoint.getAltitude(), inFrac)
+               );
+       }
 
        /**
         * Calculate the number of radians between two points (for distance calculation)
@@ -210,4 +446,79 @@ public class DataPoint
                // phew
                return answer;
        }
+
+
+       /**
+        * Resize the value array
+        * @param inNewIndex new index to allow
+        */
+       private void resizeValueArray(int inNewIndex)
+       {
+               int newSize = inNewIndex + 1;
+               if (newSize > _fieldValues.length)
+               {
+                       String[] newArray = new String[newSize];
+                       System.arraycopy(_fieldValues, 0, newArray, 0, _fieldValues.length);
+                       _fieldValues = newArray;
+               }
+       }
+
+
+       /**
+        * @return a clone object with copied data
+        */
+       public DataPoint clonePoint()
+       {
+               // Copy all values (note that photo not copied)
+               String[] valuesCopy = new String[_fieldValues.length];
+               System.arraycopy(_fieldValues, 0, valuesCopy, 0, _fieldValues.length);
+               // Make new object to hold cloned data
+               DataPoint point = new DataPoint(valuesCopy, _fieldList, _altitude.getFormat());
+               return point;
+       }
+
+
+       /**
+        * Remove all single and double quotes surrounding each value
+        * @param inValues array of values
+        */
+       private static void removeQuotes(String[] inValues)
+       {
+               if (inValues == null) {return;}
+               for (int i=0; i<inValues.length; i++)
+               {
+                       inValues[i] = removeQuotes(inValues[i]);
+               }
+       }
+
+       /**
+        * Remove any single or double quotes surrounding a value
+        * @param inValue value to modify
+        * @return modified String
+        */
+       private static String removeQuotes(String inValue)
+       {
+               if (inValue == null) {return inValue;}
+               final int len = inValue.length();
+               if (len <= 1) {return inValue;}
+               // get the first and last characters
+               final char firstChar = inValue.charAt(0);
+               final char lastChar  = inValue.charAt(len-1);
+               if (firstChar == lastChar)
+               {
+                       if (firstChar == '\"' || firstChar == '\'') {
+                               return inValue.substring(1, len-1);
+                       }
+               }
+               return inValue;
+       }
+
+       /**
+        * Get string for debug
+        * @see java.lang.Object#toString()
+        */
+       public String toString()
+       {
+               return "[Lat=" + getLatitude().toString() + ", Lon=" + getLongitude().toString() + "]";
+       }
 }