]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/data/Track.java
Version 14, October 2012
[GpsPrune.git] / tim / prune / data / Track.java
index df51e8f27e3c8963189b664adefc7534861188ea..b28fd3642091cdc485e1a9130a72c3f2ecd14dac 100644 (file)
@@ -2,7 +2,6 @@ package tim.prune.data;
 
 import java.util.List;
 
-import tim.prune.Config;
 import tim.prune.UpdateMessageBroker;
 import tim.prune.function.edit.FieldEdit;
 import tim.prune.function.edit.FieldEditList;
@@ -22,11 +21,11 @@ public class Track
        private double[] _yValues = null;
        private boolean _scaled = false;
        private int _numPoints = 0;
-       private boolean _mixedData = false;
+       private boolean _hasTrackpoint = false;
+       private boolean _hasWaypoint = false;
        // Master field list
        private FieldList _masterFieldList = null;
        // variable ranges
-       private AltitudeRange _altitudeRange = null;
        private DoubleRange _latRange = null, _longRange = null;
        private DoubleRange _xRange = null, _yRange = null;
 
@@ -45,6 +44,19 @@ public class Track
                _scaled = false;
        }
 
+       /**
+        * Constructor using fields and points from another Track
+        * @param inFieldList Field list from another Track object
+        * @param inPoints (edited) point array
+        */
+       public Track(FieldList inFieldList, DataPoint[] inPoints)
+       {
+               _masterFieldList = inFieldList;
+               _dataPoints = inPoints;
+               if (_dataPoints == null) _dataPoints = new DataPoint[0];
+               _numPoints = _dataPoints.length;
+               _scaled = false;
+       }
 
        /**
         * Load method, for initialising and reinitialising data
@@ -100,6 +112,23 @@ public class Track
                _scaled = false;
        }
 
+       /**
+        * Request that a rescale be done to recalculate derived values
+        */
+       public void requestRescale()
+       {
+               _scaled = false;
+       }
+
+       /**
+        * Extend the track's field list with the given additional fields
+        * @param inFieldList list of fields to be added
+        */
+       public void extendFieldList(FieldList inFieldList)
+       {
+               _masterFieldList = _masterFieldList.merge(inFieldList);
+       }
+
        ////////////////// Modification methods //////////////////////
 
 
@@ -154,8 +183,8 @@ public class Track
                for (int i=0; i<_numPoints; i++)
                {
                        DataPoint point = _dataPoints[i];
-                       // Don't delete waypoints or photo points
-                       if (point.isWaypoint() || point.getPhoto() != null || !point.getDeleteFlag())
+                       // Don't delete photo points
+                       if (point.hasMedia() || !point.getDeleteFlag())
                        {
                                newPointArray[numCopied] = point;
                                numCopied++;
@@ -274,9 +303,10 @@ public class Track
         * @param inStart start of range
         * @param inEnd end of range
         * @param inOffset offset to add (-ve to subtract)
+        * @param inUndo true for undo operation
         * @return true on success
         */
-       public boolean addTimeOffset(int inStart, int inEnd, long inOffset)
+       public boolean addTimeOffset(int inStart, int inEnd, long inOffset, boolean inUndo)
        {
                // sanity check
                if (inStart < 0 || inEnd < 0 || inStart >= inEnd || inEnd >= _numPoints) {
@@ -292,37 +322,47 @@ public class Track
                                // This point has a timestamp so add the offset to it
                                foundTimestamp = true;
                                timestamp.addOffset(inOffset);
+                               _dataPoints[i].setModified(inUndo);
                        }
                }
                return foundTimestamp;
        }
 
-
        /**
-        * Merge the track segments within the given range
-        * @param inStart start index
-        * @param inEnd end index
-        * @return true if successful
+        * Add the given altitude offset to the specified range
+        * @param inStart start of range
+        * @param inEnd end of range
+        * @param inOffset offset to add (-ve to subtract)
+        * @param inFormat altitude format of offset
+        * @param inDecimals number of decimal places in offset
+        * @return true on success
         */
-       public boolean mergeTrackSegments(int inStart, int inEnd)
+       public boolean addAltitudeOffset(int inStart, int inEnd, double inOffset,
+        Altitude.Format inFormat, int inDecimals)
        {
-               boolean firstTrackPoint = true;
-               // Loop between start and end
-               for (int i=inStart; i<=inEnd; i++) {
-                       DataPoint point = getPoint(i);
-                       // Set all segments to false apart from first track point
-                       if (point != null && !point.isWaypoint()) {
-                               point.setSegmentStart(firstTrackPoint);
-                               firstTrackPoint = false;
+               // sanity check
+               if (inStart < 0 || inEnd < 0 || inStart >= inEnd || inEnd >= _numPoints) {
+                       return false;
+               }
+               boolean foundAlt = false;
+               // Loop over all points within range
+               for (int i=inStart; i<=inEnd; i++)
+               {
+                       Altitude alt = _dataPoints[i].getAltitude();
+                       if (alt != null && alt.isValid())
+                       {
+                               // This point has an altitude so add the offset to it
+                               foundAlt = true;
+                               alt.addOffset(inOffset, inFormat, inDecimals);
+                               _dataPoints[i].setModified(false);
                        }
                }
-               // Find following track point, if any
-               DataPoint nextPoint = getNextTrackPoint(inEnd+1);
-               if (nextPoint != null) {nextPoint.setSegmentStart(true);}
-               UpdateMessageBroker.informSubscribers();
-               return true;
+               // needs to be scaled again
+               _scaled = false;
+               return foundAlt;
        }
 
+
        /**
         * Collect all waypoints to the start or end of the track
         * @param inAtStart true to collect at start, false for end
@@ -445,8 +485,9 @@ public class Track
         */
        public boolean cutAndMoveSection(int inSectionStart, int inSectionEnd, int inMoveTo)
        {
+               // TODO: Move cut/move into separate function?
                // Check that indices make sense
-               if (inSectionStart > 0 && inSectionEnd > inSectionStart && inMoveTo > 0
+               if (inSectionStart >= 0 && inSectionEnd > inSectionStart && inMoveTo >= 0
                        && (inMoveTo < inSectionStart || inMoveTo > (inSectionEnd+1)))
                {
                        // do the cut and move
@@ -457,30 +498,34 @@ public class Track
                        {
                                int sectionLength = inSectionEnd - inSectionStart + 1;
                                // move section to earlier point
-                               if (inMoveTo > 0)
+                               if (inMoveTo > 0) {
                                        System.arraycopy(_dataPoints, 0, newPointArray, 0, inMoveTo); // unchanged points before
+                               }
                                System.arraycopy(_dataPoints, inSectionStart, newPointArray, inMoveTo, sectionLength); // moved bit
                                // after insertion point, before moved bit
-                               if (inSectionStart > (inMoveTo + 1))
-                                       System.arraycopy(_dataPoints, inMoveTo, newPointArray, inMoveTo + sectionLength, inSectionStart - inMoveTo);
+                               System.arraycopy(_dataPoints, inMoveTo, newPointArray, inMoveTo + sectionLength, inSectionStart - inMoveTo);
                                // after moved bit
-                               if (inSectionEnd < (_numPoints - 1))
+                               if (inSectionEnd < (_numPoints - 1)) {
                                        System.arraycopy(_dataPoints, inSectionEnd+1, newPointArray, inSectionEnd+1, _numPoints - inSectionEnd - 1);
+                               }
                        }
                        else
                        {
                                // Move section to later point
-                               if (inSectionStart > 0)
+                               if (inSectionStart > 0) {
                                        System.arraycopy(_dataPoints, 0, newPointArray, 0, inSectionStart); // unchanged points before
+                               }
                                // from end of section to move to point
-                               if (inMoveTo > (inSectionEnd + 1))
+                               if (inMoveTo > (inSectionEnd + 1)) {
                                        System.arraycopy(_dataPoints, inSectionEnd+1, newPointArray, inSectionStart, inMoveTo - inSectionEnd - 1);
+                               }
                                // moved bit
                                System.arraycopy(_dataPoints, inSectionStart, newPointArray, inSectionStart + inMoveTo - inSectionEnd - 1,
                                        inSectionEnd - inSectionStart + 1);
                                // unchanged bit after
-                               if (inSectionEnd < (_numPoints - 1))
+                               if (inSectionEnd < (_numPoints - 1)) {
                                        System.arraycopy(_dataPoints, inMoveTo, newPointArray, inMoveTo, _numPoints - inMoveTo);
+                               }
                        }
                        // Copy array references
                        _dataPoints = newPointArray;
@@ -533,7 +578,7 @@ public class Track
                double latitudeDiff = 0.0, longitudeDiff = 0.0;
                double totalAltitude = 0;
                int numAltitudes = 0;
-               Altitude.Format altFormat = Config.getUseMetricUnits()?Altitude.Format.METRES:Altitude.Format.FEET;
+               Altitude.Format altFormat = Altitude.Format.NO_FORMAT;
                // loop between start and end points
                for (int i=inStartIndex; i<= inEndIndex; i++)
                {
@@ -542,6 +587,9 @@ public class Track
                        longitudeDiff += (currPoint.getLongitude().getDouble() - firstLongitude);
                        if (currPoint.hasAltitude()) {
                                totalAltitude += currPoint.getAltitude().getValue(altFormat);
+                               // Use altitude format of first valid altitude
+                               if (altFormat == Altitude.Format.NO_FORMAT)
+                                       altFormat = currPoint.getAltitude().getFormat();
                                numAltitudes++;
                        }
                }
@@ -596,15 +644,6 @@ public class Track
                return null;
        }
 
-
-       /**
-        * @return altitude range of points as AltitudeRange object
-        */
-       public AltitudeRange getAltitudeRange()
-       {
-               if (!_scaled) scalePoints();
-               return _altitudeRange;
-       }
        /**
         * @return the number of (valid) points in the track
         */
@@ -684,6 +723,8 @@ public class Track
         */
        public boolean hasData(Field inField)
        {
+               // Don't use this method for altitudes
+               if (inField.equals(Field.ALTITUDE)) {return hasAltitudeData();}
                return hasData(inField, 0, _numPoints-1);
        }
 
@@ -713,14 +754,33 @@ public class Track
                return false;
        }
 
+       /**
+        * @return true if track has altitude data
+        */
+       public boolean hasAltitudeData()
+       {
+               for (int i=0; i<_numPoints; i++) {
+                       if (_dataPoints[i].hasAltitude()) {return true;}
+               }
+               return false;
+       }
 
        /**
-        * @return true if track contains waypoints and trackpoints
+        * @return true if track contains at least one trackpoint
         */
-       public boolean hasMixedData()
+       public boolean hasTrackPoints()
        {
                if (!_scaled) scalePoints();
-               return _mixedData;
+               return _hasTrackpoint;
+       }
+
+       /**
+        * @return true if track contains waypoints
+        */
+       public boolean hasWaypoints()
+       {
+               if (!_scaled) scalePoints();
+               return _hasWaypoint;
        }
 
        /**
@@ -804,12 +864,11 @@ public class Track
         */
        private void scalePoints()
        {
-               // Loop through all points in track, to see limits of lat, long and altitude
+               // Loop through all points in track, to see limits of lat, long
                _longRange = new DoubleRange();
                _latRange = new DoubleRange();
-               _altitudeRange = new AltitudeRange();
                int p;
-               boolean hasWaypoint = false, hasTrackpoint = false;
+               _hasWaypoint = false; _hasTrackpoint = false;
                for (p=0; p < getNumPoints(); p++)
                {
                        DataPoint point = getPoint(p);
@@ -817,17 +876,12 @@ public class Track
                        {
                                _longRange.addValue(point.getLongitude().getDouble());
                                _latRange.addValue(point.getLatitude().getDouble());
-                               if (point.getAltitude().isValid())
-                               {
-                                       _altitudeRange.addValue(point.getAltitude());
-                               }
                                if (point.isWaypoint())
-                                       hasWaypoint = true;
+                                       _hasWaypoint = true;
                                else
-                                       hasTrackpoint = true;
+                                       _hasTrackpoint = true;
                        }
                }
-               _mixedData = hasWaypoint && hasTrackpoint;
 
                // Loop over points and calculate scales
                _xValues = new double[getNumPoints()];
@@ -862,16 +916,21 @@ public class Track
        {
                int nearestPoint = 0;
                double nearestDist = -1.0;
-               double currDist;
+               double mDist, yDist;
                for (int i=0; i < getNumPoints(); i++)
                {
                        if (!inJustTrackPoints || !_dataPoints[i].isWaypoint())
                        {
-                               currDist = Math.abs(_xValues[i] - inX) + Math.abs(_yValues[i] - inY);
-                               if (currDist < nearestDist || nearestDist < 0.0)
+                               yDist = Math.abs(_yValues[i] - inY);
+                               if (yDist < nearestDist || nearestDist < 0.0)
                                {
-                                       nearestPoint = i;
-                                       nearestDist = currDist;
+                                       // y dist is within range, so check x too
+                                       mDist = yDist + getMinXDist(_xValues[i] - inX);
+                                       if (mDist < nearestDist || nearestDist < 0.0)
+                                       {
+                                               nearestPoint = i;
+                                               nearestDist = mDist;
+                                       }
                                }
                        }
                }
@@ -883,6 +942,16 @@ public class Track
                return nearestPoint;
        }
 
+       /**
+        * @param inX x value of point
+        * @return minimum wrapped value
+        */
+       private static final double getMinXDist(double inX)
+       {
+               // TODO: Can use some kind of floor here?
+               return Math.min(Math.min(Math.abs(inX), Math.abs(inX-1.0)), Math.abs(inX+1.0));
+       }
+
        /**
         * Get the next track point starting from the given index
         * @param inStartIndex index to start looking from
@@ -911,6 +980,7 @@ public class Track
         */
        public DataPoint getPreviousTrackPoint(int inStartIndex)
        {
+               // end index is given as _numPoints but actually it just counts down to -1
                return getNextTrackPoint(inStartIndex, _numPoints, false);
        }
 
@@ -1086,9 +1156,10 @@ public class Track
         * Edit the specified point
         * @param inPoint point to edit
         * @param inEditList list of edits to make
+        * @param inUndo true if undo operation, false otherwise
         * @return true if successful
         */
-       public boolean editPoint(DataPoint inPoint, FieldEditList inEditList)
+       public boolean editPoint(DataPoint inPoint, FieldEditList inEditList, boolean inUndo)
        {
                if (inPoint != null && inEditList != null && inEditList.getNumEdits() > 0)
                {
@@ -1099,10 +1170,15 @@ public class Track
                        for (int i=0; i<numEdits; i++)
                        {
                                FieldEdit edit = inEditList.getEdit(i);
-                               inPoint.setFieldValue(edit.getField(), edit.getValue());
+                               Field editField = edit.getField();
+                               inPoint.setFieldValue(editField, edit.getValue(), inUndo);
+                               // Check that master field list has this field already (maybe point name has been added)
+                               if (!_masterFieldList.contains(editField)) {
+                                       _masterFieldList.extendList(editField);
+                               }
                                // check coordinates
-                               coordsChanged |= (edit.getField().equals(Field.LATITUDE)
-                                       || edit.getField().equals(Field.LONGITUDE) || edit.getField().equals(Field.ALTITUDE));
+                               coordsChanged |= (editField.equals(Field.LATITUDE)
+                                       || editField.equals(Field.LONGITUDE) || editField.equals(Field.ALTITUDE));
                        }
                        // set photo status if coordinates have changed
                        if (inPoint.getPhoto() != null && coordsChanged)
@@ -1117,4 +1193,18 @@ public class Track
                }
                return false;
        }
+
+       /**
+        * @param inPoint point to check
+        * @return true if this track contains the given point
+        */
+       public boolean containsPoint(DataPoint inPoint)
+       {
+               if (inPoint == null) return false;
+               for (int i=0; i < getNumPoints(); i++)
+               {
+                       if (getPoint(i) == inPoint) return true;
+               }
+               return false; // not found
+       }
 }